Repository: Jguer/yay
Branch: next
Commit: 90ae08862a52
Files: 214
Total size: 1.7 MB
Directory structure:
gitextract_b9cisaon/
├── .dockerignore
├── .github/
│ ├── CODEOWNERS
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ └── feature_request.md
│ ├── dependabot.yml
│ └── workflows/
│ ├── builder-image.yml
│ ├── multiarch-build.yml
│ ├── testing-git.yml
│ └── testing.yml
├── .gitignore
├── .golangci.yml
├── .pre-commit-config.yaml
├── CONTRIBUTING.md
├── Dockerfile
├── LICENSE
├── Makefile
├── README.md
├── SECURITY.md
├── ci.Dockerfile
├── clean.go
├── clean_test.go
├── cmd.go
├── cmd_test.go
├── completions/
│ ├── bash
│ ├── fish
│ └── zsh
├── doc/
│ └── yay.8
├── errors.go
├── get.go
├── go.mod
├── go.sum
├── local_install.go
├── local_install_test.go
├── main.go
├── pkg/
│ ├── cmd/
│ │ └── graph/
│ │ └── main.go
│ ├── completion/
│ │ ├── completion.go
│ │ └── completion_test.go
│ ├── db/
│ │ ├── executor.go
│ │ ├── ialpm/
│ │ │ ├── alpm.go
│ │ │ ├── alpm_test.go
│ │ │ └── high_level.go
│ │ ├── mock/
│ │ │ ├── executor.go
│ │ │ └── repo.go
│ │ └── types.go
│ ├── dep/
│ │ ├── dep.go
│ │ ├── dep_graph.go
│ │ ├── dep_graph_bench_test.go
│ │ ├── dep_graph_rpc_test.go
│ │ ├── dep_graph_test.go
│ │ ├── mock/
│ │ │ └── aur.go
│ │ ├── target_handler.go
│ │ ├── testdata/
│ │ │ ├── android-sdk.json
│ │ │ ├── aws-cli-git.json
│ │ │ ├── clion.json
│ │ │ ├── gstreamer-git.json
│ │ │ ├── jellyfin-server.json
│ │ │ ├── jellyfin-web.json
│ │ │ ├── jellyfin.json
│ │ │ ├── liri-desktop-git.json
│ │ │ ├── mesa-git.json
│ │ │ ├── nx.json
│ │ │ ├── python-pydantic.json
│ │ │ └── samsung-unified-driver.json
│ │ └── topo/
│ │ ├── dep.go
│ │ ├── dep_test.go
│ │ └── errors.go
│ ├── download/
│ │ ├── abs.go
│ │ ├── abs_test.go
│ │ ├── aur.go
│ │ ├── aur_test.go
│ │ ├── errors.go
│ │ ├── unified.go
│ │ ├── unified_integration_test.go
│ │ ├── unified_test.go
│ │ └── utils_test.go
│ ├── intrange/
│ │ ├── intrange.go
│ │ └── intrange_test.go
│ ├── menus/
│ │ ├── clean_menu.go
│ │ ├── diff_menu.go
│ │ ├── edit_menu.go
│ │ └── menu.go
│ ├── multierror/
│ │ └── multierror.go
│ ├── news/
│ │ ├── .snapshots/
│ │ │ ├── TestPrintNewsFeed-all-quiet
│ │ │ ├── TestPrintNewsFeed-all-verbose
│ │ │ ├── TestPrintNewsFeed-latest-quiet
│ │ │ ├── TestPrintNewsFeed-latest-quiet-topdown
│ │ │ └── TestPrintNewsFeedSameDay
│ │ ├── news.go
│ │ └── news_test.go
│ ├── query/
│ │ ├── aur_warnings.go
│ │ ├── errors.go
│ │ ├── filter.go
│ │ ├── metric.go
│ │ ├── metric_test.go
│ │ ├── query_builder.go
│ │ ├── query_builder_test.go
│ │ ├── source.go
│ │ ├── types.go
│ │ ├── version_diff.go
│ │ └── version_diff_test.go
│ ├── runtime/
│ │ ├── pacman.go
│ │ ├── pacman_test.go
│ │ ├── runtime.go
│ │ └── runtime_test.go
│ ├── settings/
│ │ ├── args.go
│ │ ├── config.go
│ │ ├── config_test.go
│ │ ├── dirs.go
│ │ ├── dirs_test.go
│ │ ├── errors.go
│ │ ├── exe/
│ │ │ ├── cmd_builder.go
│ │ │ ├── exec.go
│ │ │ └── mock.go
│ │ ├── ini.go
│ │ ├── ini_test.go
│ │ ├── parser/
│ │ │ ├── parser.go
│ │ │ ├── parser_test.go
│ │ │ ├── rebuild_mode.go
│ │ │ └── target_mode.go
│ │ └── yay.conf
│ ├── sync/
│ │ ├── build/
│ │ │ ├── errors.go
│ │ │ ├── installer.go
│ │ │ ├── installer_test.go
│ │ │ ├── pkg_archive.go
│ │ │ └── pkg_archive_test.go
│ │ ├── srcinfo/
│ │ │ ├── pgp/
│ │ │ │ ├── keys.go
│ │ │ │ ├── keys_test.go
│ │ │ │ └── testdata/
│ │ │ │ ├── 11E521D646982372EB577A1F8F0871F202119294
│ │ │ │ ├── 487EACC08557AD082088DABA1EB2638FF56C0C53
│ │ │ │ ├── 647F28654894E3BD457199BE38DBBDC86092693E
│ │ │ │ ├── A314827C4E4250A204CE6E13284FC34C8E4B1A25
│ │ │ │ ├── ABAF11C65A2970B130ABE3C479BE3E4300411886
│ │ │ │ ├── B6C8F98282B944E3B0D5C2530FC3042E345AD05D
│ │ │ │ └── C52048C0C0748FEE227D47A2702353E0F7E48EDB
│ │ │ ├── service.go
│ │ │ └── service_test.go
│ │ ├── sync.go
│ │ └── workdir/
│ │ ├── aur_source.go
│ │ ├── aur_source_test.go
│ │ ├── clean.go
│ │ ├── merge.go
│ │ ├── preparer.go
│ │ └── preparer_test.go
│ ├── text/
│ │ ├── color.go
│ │ ├── convert.go
│ │ ├── errors.go
│ │ ├── input.go
│ │ ├── service.go
│ │ ├── text.go
│ │ ├── text_test.go
│ │ └── time.go
│ ├── upgrade/
│ │ ├── .snapshots/
│ │ │ ├── Test_upAUR-No_Updates
│ │ │ ├── Test_upAUR-Simple_Update
│ │ │ └── Test_upAUR-Time_Update
│ │ ├── service.go
│ │ ├── service_test.go
│ │ ├── sources.go
│ │ ├── sources_test.go
│ │ ├── upgrade.go
│ │ └── upgrade_test.go
│ └── vcs/
│ ├── .snapshots/
│ │ └── TestInfoStore_Update-simple
│ ├── mock.go
│ ├── vcs.go
│ └── vcs_test.go
├── po/
│ ├── ca.po
│ ├── ca_ES.po
│ ├── cs.po
│ ├── de.po
│ ├── en.po
│ ├── es.po
│ ├── eu.po
│ ├── fi.po
│ ├── fr.po
│ ├── fr_FR.po
│ ├── he.po
│ ├── he_IL.po
│ ├── hu.po
│ ├── id.po
│ ├── it_IT.po
│ ├── ja.po
│ ├── ko.po
│ ├── nl.po
│ ├── pl.po
│ ├── pl_PL.po
│ ├── pt.po
│ ├── pt_BR.po
│ ├── ru.po
│ ├── ru_RU.po
│ ├── sk.po
│ ├── sv.po
│ ├── tr.po
│ ├── uk.po
│ ├── vi.po
│ ├── vi_VN.po
│ ├── zh_CN.po
│ └── zh_TW.po
├── print.go
├── print_test.go
├── query.go
├── query_test.go
├── sync.go
├── sync_test.go
├── testdata/
│ ├── cephbin/
│ │ ├── .SRCINFO
│ │ └── PKGBUILD
│ ├── gourou/
│ │ ├── .SRCINFO
│ │ └── PKGBUILD
│ ├── jfin/
│ │ ├── .SRCINFO
│ │ └── PKGBUILD
│ ├── libzip-git/
│ │ ├── .SRCINFO
│ │ └── PKGBUILD
│ └── pacman.conf
├── vcs.go
└── vote.go
================================================
FILE CONTENTS
================================================
================================================
FILE: .dockerignore
================================================
*
!*.go
!pkg
!go.mod
!go.sum
!Makefile
!po
!doc
!completions
================================================
FILE: .github/CODEOWNERS
================================================
* @Jguer
================================================
FILE: .github/FUNDING.yml
================================================
github: [Jguer]
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Report a malfunction to help us improve
title: ""
labels: "Status: Triage, Type: Bug"
assignees: ""
---
### Affected Version
### Describe the bug
### Reproduction Steps
1.
2.
3.
### Expected behavior
### Output
```sh
```
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
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.
### Describe the solution you'd like
### Describe alternatives you've considered
### Additional context
================================================
FILE: .github/dependabot.yml
================================================
# 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:
- '*'
================================================
FILE: .github/workflows/builder-image.yml
================================================
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 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: |
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=ghcr.io/${{ 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 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: |
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 GitHub Container Registry tags
GHCR_TAGS=$(echo '${{ steps.meta.outputs.tags }}' | xargs -I {} echo "-t {}")
# Create a manifest list using the image digests from /tmp/digests/*
DIGESTS=$(for file in /tmp/digests/*; do
echo -n "ghcr.io/${{ env.REGISTRY_IMAGE }}@$(cat $file) "
done)
# Create the manifest list for GitHub Container Registry
docker buildx imagetools create $GHCR_TAGS $DIGESTS
- name: Inspect image
run: |
docker buildx imagetools inspect ghcr.io/${{ env.REGISTRY_IMAGE }}:latest
================================================
FILE: .github/workflows/multiarch-build.yml
================================================
name: Build Release
on:
push:
tags:
- v*
jobs:
build-releases:
strategy:
matrix:
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@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
run: |
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
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@v4
with:
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
run: |
echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
echo "TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
- uses: actions/download-artifact@v4
with:
pattern: yay_*
merge-multiple: true
- name: Create Release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
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 }}
================================================
FILE: .github/workflows/testing-git.yml
================================================
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 -si --nocheck'
- name: Run Build and Tests with pacman-git
run: |
make test
================================================
FILE: .github/workflows/testing.yml
================================================
name: Test against pacman
on:
pull_request:
jobs:
build:
name: Lint and test yay
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-
- 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: |
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
================================================
FILE: .gitignore
================================================
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
.vscode
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
*.exe
*.test
*.prof
yay
yay_*/
*.tar.gz
qemu-*
.go
# Locale
*.mo
*.pot
*.po~
*.pprof
node_modules/
xgotext
.devcontainer/
================================================
FILE: .golangci.yml
================================================
version: "2"
run:
go: "1.26"
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:
lines: 100
statements: 50
goconst:
min-len: 3
min-occurrences: 4
gocritic:
enabled-tags:
- diagnostic
- experimental
- opinionated
- performance
- style
gocyclo:
min-complexity: 15
lll:
line-length: 140
misspell:
locale: US
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
- gosec
- govet
- lll
- revive
- staticcheck
- wsl
path: (.+)_test.go
- path: (.+)\.go$
text: G204
- path: (.+)\.go$
text: G115
- path: (.+)\.go$
text: G703
- path: (.+)\.go$
text: G705
- path: (.+)\.go$
text: QF1012
paths:
- third_party$
- builtin$
- examples$
formatters:
enable:
- gofmt
- goimports
settings:
goimports:
local-prefixes:
- github.com/Jguer/yay/v12
exclusions:
generated: lax
paths:
- third_party$
- builtin$
- examples$
================================================
FILE: .pre-commit-config.yaml
================================================
default_stages: [commit]
repos:
- repo: https://github.com/dnephin/pre-commit-golang
rev: v0.5.1
hooks:
- id: go-fmt
- 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]
================================================
FILE: CONTRIBUTING.md
================================================
# 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!
If you plan to make any large changes or changes that may not be 100% agreed
on, we suggest opening an issue detailing your ideas first.
Otherwise send us a pull request and we will be happy to review it.
### 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:
- go (make only)
- git
- base-devel
- pacman
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.
#### Docker Release
`make docker-release` will build the release packages for `aarch64` and for `x86_64`.
For `aarch64` to run on a `x86_64` platform `qemu-user-static(-bin)` must be
installed.
```
docker run --rm --privileged multiarch/qemu-user-static:register --reset
```
will register QEMU in the build agent. ARM builds tend to crash sometimes but
repeated runs tend to succeed.
### Code Style
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
formatted correctly, run the code through `go vet`, and run unit tests.
================================================
FILE: Dockerfile
================================================
FROM ghcr.io/jguer/yay-builder:latest
LABEL maintainer="Jguer,docker@jguer.space"
ARG VERSION
ARG PREFIX
ARG ARCH
WORKDIR /app
COPY . .
RUN pacman -Syu --overwrite=* --noconfirm
RUN make release VERSION=${VERSION} PREFIX=${PREFIX} ARCH=${ARCH}
================================================
FILE: LICENSE
================================================
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
{one line to give the program's name and a brief idea of what it does.}
Copyright (C) {year} {name of author}
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
{project} Copyright (C) {year} {fullname}
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
.
================================================
FILE: Makefile
================================================
export GO111MODULE=on
GOPROXY ?= https://proxy.golang.org,direct
export GOPROXY
BUILD_TAG = devel
ARCH ?= $(shell uname -m)
BIN := yay
DESTDIR :=
GO ?= go
PKGNAME := yay
PREFIX := /usr/local
MAJORVERSION := 13
MINORVERSION := 0
PATCHVERSION := 0
VERSION ?= ${MAJORVERSION}.${MINORVERSION}.${PATCHVERSION}
LOCALEDIR := po
SYSTEMLOCALEPATH := $(PREFIX)/share/locale/
# ls -1 po | sed -e 's/\.po$//' | paste -sd " "
LANGS := ca cs de en es eu fr_FR he hu id it_IT ja ko nl pl_PL pt_BR pt ru_RU ru sk sv tr uk vi vi_VN zh_CN zh_TW
POTFILE := default.pot
POFILES := $(addprefix $(LOCALEDIR)/,$(addsuffix .po,$(LANGS)))
MOFILES := $(POFILES:.po=.mo)
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
SOURCES ?= $(shell find . -name "*.go" -type f)
.PRECIOUS: ${LOCALEDIR}/%.po
.PHONY: default
default: build
.PHONY: all
all: | clean release
.PHONY: clean
clean:
$(GO) clean $(FLAGS) -i ./...
rm -rf $(BIN) $(PKGNAME)_*
.PHONY: test_lint
test_lint: test lint
.PHONY: test
test:
$(GO) test -race -covermode=atomic $(FLAGS) ./...
.PHONY: test-integration
test-integration:
$(GO) test -tags=integration $(FLAGS) ./...
.PHONY: build
build: $(BIN)
.PHONY: release
release: $(PACKAGE)
.PHONY: docker-release-all
docker-release-all:
make docker-release-armv7h ARCH=armv7h
make docker-release-x86_64 ARCH=x86_64
make docker-release-aarch64 ARCH=aarch64
docker-release:
docker create --name yay-$(ARCH) yay:${ARCH} /bin/sh
docker cp yay-$(ARCH):/app/${PACKAGE} $(PACKAGE)
docker container rm yay-$(ARCH)
.PHONY: docker-build
docker-build:
docker build -t yay-$(ARCH):${VERSION} .
docker run -e="ARCH=$(ARCH)" --name yay-$(ARCH) yay-$(ARCH):${VERSION} make build VERSION=${VERSION} PREFIX=${PREFIX}
docker cp yay-$(ARCH):/app/${BIN} $(BIN)
docker container rm yay-$(ARCH)
.PHONY: lint
lint:
GOFLAGS="$(FLAGS)" golangci-lint run ./...
.PHONY: fmt
fmt:
go fmt ./...
.PHONY: install
install: build ${MOFILES}
install -Dm755 ${BIN} $(DESTDIR)$(PREFIX)/bin/${BIN}
install -Dm644 doc/${PKGNAME}.8 $(DESTDIR)$(PREFIX)/share/man/man8/${PKGNAME}.8
install -Dm644 completions/bash $(DESTDIR)$(PREFIX)/share/bash-completion/completions/${PKGNAME}
install -Dm644 completions/zsh $(DESTDIR)$(PREFIX)/share/zsh/site-functions/_${PKGNAME}
install -Dm644 completions/fish $(DESTDIR)$(PREFIX)/share/fish/vendor_completions.d/${PKGNAME}.fish
for lang in ${LANGS}; do \
install -Dm644 ${LOCALEDIR}/$${lang}.mo $(DESTDIR)$(PREFIX)/share/locale/$$lang/LC_MESSAGES/${PKGNAME}.mo; \
done
.PHONY: uninstall
uninstall:
rm -f $(DESTDIR)$(PREFIX)/bin/${BIN}
rm -f $(DESTDIR)$(PREFIX)/share/man/man8/${PKGNAME}.8
rm -f $(DESTDIR)$(PREFIX)/share/bash-completion/completions/${PKGNAME}
rm -f $(DESTDIR)$(PREFIX)/share/zsh/site-functions/_${PKGNAME}
rm -f $(DESTDIR)$(PREFIX)/share/fish/vendor_completions.d/${PKGNAME}.fish
for lang in ${LANGS}; do \
rm -f $(DESTDIR)$(PREFIX)/share/locale/$$lang/LC_MESSAGES/${PKGNAME}.mo; \
done
$(BIN): $(SOURCES)
$(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
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 $@ $<
================================================
FILE: README.md
================================================
[](https://aur.archlinux.org/packages/yay/)
[](https://aur.archlinux.org/packages/yay-bin/)
[](https://aur.archlinux.org/packages/yay-git/)

[](https://github.com/Jguer/yay/blob/master/LICENSE)
# yay
Yet Another Yogurt - An AUR Helper Written in Go
### Help translate yay: [Transifex](https://app.transifex.com/yay-1/yay/)
## Features
- 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 (`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
- Build local PKGBUILDs with AUR dependencies
- Un/Vote for packages
[](https://asciinema.org/a/399431)
[](https://asciinema.org/a/399433)
## Installation
If you are migrating from another AUR helper, you can simply install yay with that helper.
> [!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 ` | Present package-installation selection menu. |
| `yay -Bi ` | Install dependencies and build a local PKGBUILD. |
| `yay -G ` | Download PKGBUILD from ABS or AUR. (yay v12.0+) |
| `yay -Gp ` | 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 -Wu ` | Unvote for package (Requires setting `AUR_USERNAME` and `AUR_PASSWORD` environment variables) (yay v11.3+) |
| `yay -Wv ` | 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
officially supported by Arch Linux, support should not be sought out on the
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.
## Images
### Other AUR helpers/tools
- [paru](https://github.com/morganamilo/paru)
- [aurutils](https://github.com/AladW/aurutils)
- [pikaur](https://github.com/actionless/pikaur)
================================================
FILE: SECURITY.md
================================================
# 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.
================================================
FILE: ci.Dockerfile
================================================
FROM quay.io/gmanka/archlinuxarm:base-devel
LABEL maintainer="Jguer,docker@jguer.space"
ENV GO111MODULE=on
WORKDIR /app
COPY go.mod .
# asciidoc, doxygen, meson needed for pacman-git
RUN set -eux; \
pacman-key --init; \
sed -i 's/^#DisableSandboxFilesystem/DisableSandboxFilesystem/' /etc/pacman.conf; \
sed -i 's/^#DisableSandboxSyscalls/DisableSandboxSyscalls/' /etc/pacman.conf; \
pacman -Syu --noconfirm --needed archlinux-keyring pacman go git gcc make base-devel sudo asciidoc doxygen meson; \
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s v2.10.1; \
go mod download; \
rm -rf /var/lib/pacman/sync/* /var/cache/pacman/* /tmp/* /var/tmp/*; \
rm -rf /usr/share/man/* /usr/share/doc/* || true; \
yes | pacman -Scc >/dev/null 2>&1 || true
================================================
FILE: clean.go
================================================
package main
import (
"context"
"os"
"path/filepath"
"github.com/Jguer/aur"
mapset "github.com/deckarep/golang-set/v2"
"github.com/leonelquinteros/gotext"
"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"
)
// 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(ctx, cfg, cmdBuilder, cmdArgs, hanging)
}
return nil
}
// 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()
if err := arguments.AddArg("R", "s", "u"); err != nil {
return err
}
arguments.AddTarget(pkgNames...)
return cmdBuilder.Show(
cmdBuilder.BuildPacmanCmd(ctx,
arguments, cfg.Mode, settings.NoConfirm))
}
func syncClean(ctx context.Context, run *runtime.Runtime, cmdArgs *parser.Arguments, dbExecutor db.Executor) error {
keepInstalled := false
keepCurrent := false
_, removeAll, _ := cmdArgs.GetArg("c", "clean")
for _, v := range run.PacmanConf.CleanMethod {
switch v {
case "KeepInstalled":
keepInstalled = true
case "KeepCurrent":
keepCurrent = true
}
}
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 !run.Cfg.Mode.AtLeastAUR() {
return nil
}
var question string
if removeAll {
question = gotext.Get("Do you want to remove ALL AUR packages from cache?")
} else {
question = gotext.Get("Do you want to remove all other AUR packages from cache?")
}
run.Logger.Println(gotext.Get("\nBuild directory:"), run.Cfg.BuildDir)
if run.Logger.ContinueTask(question, true, settings.NoConfirm) {
if err := cleanAUR(ctx, run, keepInstalled, keepCurrent, removeAll, dbExecutor); err != nil {
return err
}
}
if removeAll {
return nil
}
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(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 := mapset.NewThreadUnsafeSet[string]()
inAURBases := mapset.NewThreadUnsafeSet[string]()
remotePackages := dbExecutor.InstalledRemotePackages()
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
}
cachedPackages = append(cachedPackages, file.Name())
}
// Most people probably don't use keep current and that is the only
// case where this is needed.
// Querying the AUR is slow and needs internet so don't do it if we
// don't need to.
if keepCurrent {
info, errInfo := run.AURClient.Get(ctx, &aur.Query{
Needles: cachedPackages,
})
if errInfo != nil {
return errInfo
}
for i := range info {
inAURBases.Add(info[i].PackageBase)
}
}
for _, pkg := range remotePackages {
if pkg.Base() != "" {
installedBases.Add(pkg.Base())
} else {
installedBases.Add(pkg.Name())
}
}
for _, file := range files {
if !file.IsDir() {
continue
}
if !removeAll {
if keepInstalled && installedBases.Contains(file.Name()) {
continue
}
if keepCurrent && inAURBases.Contains(file.Name()) {
continue
}
}
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(ctx context.Context, run *runtime.Runtime) error {
run.Logger.Println(gotext.Get("removing untracked AUR files from cache..."))
files, err := os.ReadDir(run.Cfg.BuildDir)
if err != nil {
return err
}
for _, file := range files {
if !file.IsDir() {
continue
}
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 isGitRepository(dir string) bool {
_, err := os.Stat(filepath.Join(dir, ".git"))
return !os.IsNotExist(err)
}
================================================
FILE: clean_test.go
================================================
//go:build !integration
// +build !integration
package main
import (
"context"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
alpm "github.com/Jguer/dyalpm"
pacmanconf "github.com/Morganamilo/go-pacmanconf"
"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.Package) []alpm.Depend {
if i.Name() == "linux" {
return []alpm.Depend{
{
Name: "linux-headers",
},
}
}
return []alpm.Depend{}
},
PackageProvidesFn: func(p alpm.Package) []alpm.Depend { return []alpm.Depend{} },
PackageDependsFn: func(p alpm.Package) []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))
}
})
}
}
func TestIntegrationCleanAUR(t *testing.T) {
buildDir := filepath.Join(t.TempDir(), "build")
yayGitDir := filepath.Join(buildDir, "yay-git")
zoomDir := filepath.Join(buildDir, "zoom")
t.Parallel()
testCases := []struct {
name string
args []string
wantDirs []string
}{
{
name: "Sync clean AUR",
args: []string{"S", "c", "a"},
wantDirs: []string{"zoom"},
},
{
name: "Sync clean double AUR",
args: []string{"S", "c", "c", "a"},
wantDirs: []string{},
},
}
dbExc := &mock.DBExecutor{
PackageOptionalDependsFn: func(i alpm.Package) []alpm.Depend {
if i.Name() == "linux" {
return []alpm.Depend{
{
Name: "linux-headers",
},
}
}
return []alpm.Depend{}
},
PackageProvidesFn: func(p alpm.Package) []alpm.Depend { return []alpm.Depend{} },
PackageDependsFn: func(p alpm.Package) []alpm.Depend { return []alpm.Depend{} },
InstalledRemotePackagesFn: func() map[string]alpm.Package {
return map[string]alpm.Package{
"zoom": &mock.Package{
PName: "zoom",
PVersion: "6.5.8-1",
PBase: "zoom",
PReason: alpm.PkgReasonExplicit,
},
}
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
mockRunner := &exe.MockRunner{}
cfg := &settings.Configuration{
BuildDir: buildDir,
Mode: parser.ModeAUR,
}
pacmanConf := &pacmanconf.Config{
// Only testing the keep installed clean method right now
CleanMethod: []string{"KeepInstalled"},
}
run := &runtime.Runtime{
Cfg: cfg,
PacmanConf: pacmanConf,
Logger: newTestLogger(),
}
cmdArgs := parser.MakeArguments()
cmdArgs.AddArg(tc.args...)
// Create the package directories to be cleaned
err := os.MkdirAll(yayGitDir, 0o755)
require.NoError(t, err)
err = os.MkdirAll(zoomDir, 0o755)
require.NoError(t, err)
err = handleCmd(context.Background(),
run, cmdArgs, dbExc,
)
require.NoError(t, err)
// This should only test AUR cleaning, so no calls to an external command should be made
assert.Len(t, mockRunner.ShowCalls, 0)
// Make sure the directories left after cleaning are the only ones we expect
packageDirs, err := os.ReadDir(buildDir)
require.NoError(t, err)
var packageDirNames []string
for _, dir := range packageDirs {
packageDirNames = append(packageDirNames, dir.Name())
}
assert.ElementsMatch(t, tc.wantDirs, packageDirNames)
})
}
}
================================================
FILE: cmd.go
================================================
package main
import (
"context"
"errors"
"fmt"
"net/http"
"strings"
alpm "github.com/Jguer/dyalpm"
"github.com/leonelquinteros/gotext"
"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"
)
func usage(logger *text.Logger) {
logger.Println(`Usage:
yay
yay [...]
yay
operations:
yay {-h --help}
yay {-V --version}
yay {-D --database}
yay {-F --files} [options] [package(s)]
yay {-Q --query} [options] [package(s)]
yay {-R --remove} [options]
yay {-S --sync} [options] [package(s)]
yay {-T --deptest} [options] [package(s)]
yay {-U --upgrade} [options]
New operations:
yay {-B --build} [options] [dir]
yay {-G --getpkgbuild} [options] [package(s)]
yay {-P --show} [options]
yay {-W --web} [options] [package(s)]
yay {-Y --yay} [options] [package(s)]
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:
-N --repo Assume targets are from the repositories
-a --aur Assume targets are from the AUR
Permanent configuration options:
--save Causes the following options to be saved back to the
config file when used
--aururl Set an alternative AUR URL
--aurrpcurl Set an alternative URL for the AUR /rpc endpoint
--builddir Directory used to download and run PKGBUILDS
--editor Editor to use when editing PKGBUILDs
--editorflags Pass arguments to editor
--makepkg makepkg command to use
--mflags Pass arguments to makepkg
--pacman pacman command to use
--git git command to use
--gitflags Pass arguments to git
--gpg gpg command to use
--gpgflags Pass arguments to gpg
--config pacman.conf file to use
--makepkgconf makepkg.conf file to use
--nomakepkgconf Use the default makepkg.conf
--requestsplitn Max amount of packages to query per AUR request
--completioninterval Time in days to refresh completion cache
--sortby Sort AUR results by a specific field during search
--searchby Search for packages using a specified field
--answerclean Set a predetermined answer for the clean build menu
--answerdiff Set a predetermined answer for the diff menu
--answeredit Set a predetermined answer for the edit pkgbuild menu
--answerupgrade Set a predetermined answer for the upgrade menu
--noanswerclean Unset the answer for the clean build menu
--noanswerdiff Unset the answer for the edit diff menu
--noansweredit Unset the answer for the edit pkgbuild menu
--noanswerupgrade Unset the answer for the upgrade menu
--cleanmenu Give the option to clean build PKGBUILDS
--diffmenu Give the option to show diffs for build files
--editmenu Give the option to edit/view PKGBUILDS
--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
--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
--rebuild Always build target packages
--rebuildall Always build all AUR packages
--norebuild Skip package build if in cache and up to date
--rebuildtree Always build all AUR packages even if installed
--redownload Always download pkgbuilds of targets
--noredownload Skip pkgbuild download if in cache and up to date
--redownloadall Always download pkgbuilds of all AUR packages
--provides Look for matching providers when searching for packages
--pgpfetch Prompt to import PGP keys from PKGBUILDs
--useask Automatically resolve conflicts using pacman's ask flag
--sudo sudo command to use
--sudoflags Pass arguments to sudo
--sudoloop Loop sudo calls in the background to avoid timeout
show specific options (used with -P):
-c --complete Used for completions
-d --defaultconfig Print default yay configuration
-g --currentconfig Print current yay configuration
-s --stats Display system package statistics
-w --news Print arch news
yay specific options (used with -Y):
-c --clean Remove unneeded dependencies (-cc to ignore optdepends)
--gendb Generates development package DB used for updating
getpkgbuild specific options (used with -G):
-f --force Force download for existing ABS packages
-p --print Print pkgbuild of packages`)
}
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 run.Cfg.SudoLoop && cmdArgs.NeedRoot(run.Cfg.Mode) {
run.CmdBuilder.SudoLoop()
}
switch cmdArgs.Op {
case "V", "version":
handleVersion(run.Logger)
return nil
case "D", "database":
return run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx,
cmdArgs, run.Cfg.Mode, settings.NoConfirm))
case "F", "files":
return run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx,
cmdArgs, run.Cfg.Mode, settings.NoConfirm))
case "Q", "query":
return handleQuery(ctx, run, cmdArgs, dbExecutor)
case "R", "remove":
return handleRemove(ctx, run, cmdArgs, run.VCSStore)
case "S", "sync":
return handleSync(ctx, run, cmdArgs, dbExecutor)
case "T", "deptest":
return run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx,
cmdArgs, run.Cfg.Mode, settings.NoConfirm))
case "U", "upgrade":
return handleUpgrade(ctx, run, cmdArgs)
case "B", "build":
return handleBuild(ctx, run, dbExecutor, cmdArgs)
case "G", "getpkgbuild":
return handleGetpkgbuild(ctx, run, cmdArgs, dbExecutor)
case "P", "show":
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 errors.New(gotext.Get("unhandled operation"))
}
// 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")
switch {
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
}
return printUpdateList(ctx, run, cmdArgs, dbExecutor,
cmdArgs.ExistsDouble("u", "sysupgrade"), filter)
}
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("")
}
return err
}
return nil
}
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
}
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"), run.Logger)
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 {
return installLocalPKGBUILD(ctx, run, cmdArgs, dbExecutor)
}
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 {
localCache.RemovePackages(cmdArgs.Targets)
}
return err
}
// NumberMenu presents a CLI for selecting packages to install.
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)
if err := queryBuilder.Results(dbExecutor, query.NumberMenu); err != nil {
return err
}
if queryBuilder.Len() == 0 {
// no results were found
return nil
}
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
}
include, exclude, _, otherExclude := intrange.ParseNumberMenu(numberBuf)
targets, err := queryBuilder.GetTargets(include, exclude, otherExclude)
if err != nil {
return err
}
// 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(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 run.Cfg.Mode.AtLeastAUR() && (len(cmdArgs.Targets) == 0 || aur) {
scanner, err := download.GetPackageScanner(ctx, httpClient, run.Cfg.AURURL, run.Logger)
if err != nil {
return err
}
defer scanner.Close()
for scanner.Scan() {
name := scanner.Text()
if cmdArgs.ExistsArg("q", "quiet") {
run.Logger.Println(name)
} else {
run.Logger.Printf("%s %s %s", text.Magenta("aur"), text.Bold(name), text.Bold(text.Green(gotext.Get("unknown-version"))))
if dbExecutor.LocalPackage(name) != nil {
run.Logger.Print(text.Bold(text.Blue(gotext.Get(" [Installed]"))))
}
run.Logger.Println()
}
}
}
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
}
================================================
FILE: cmd_test.go
================================================
//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, "git")
// 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))
}
}
================================================
FILE: completions/bash
================================================
# This file is in the public domain.
_arch_compgen() {
local i r
COMPREPLY=($(compgen -W '$*' -- "$cur"))
for ((i = 1; i < ${#COMP_WORDS[@]} - 1; i++)); do
for r in ${!COMPREPLY[@]}; do
if [[ ${COMP_WORDS[i]} == ${COMPREPLY[r]} ]]; then
unset 'COMPREPLY[r]'
break
fi
done
done
}
_arch_ptr2comp() {
local list= x y
for x; do
for y in '0 --' '1 -'; do
eval 'set -- ${'$x'[${y% *}]}'
list+=\ ${@/#/${y#* }}
done
done
_arch_compgen $list
}
_arch_incomp() {
local r="[[:space:]]-(-${1#* }[[:space:]]|[[:alnum:]_]*${1% *})"
[[ $COMP_LINE =~ $r ]]
}
_pacman_pkg() {
_arch_compgen "$(
if [[ $2 ]]; then
\pacman -$1 2>/dev/null | \cut -d' ' -f1 | \sort -u
else
\pacman -$1 2>/dev/null
fi
)"
}
_yay_pkg() {
[ -z "$cur" ] && _pacman_pkg Slq && return
_arch_compgen "$(yay -Pc)"
}
_pacman_repo_list() {
_arch_compgen "$(pacman-conf --repo-list)"
}
_yay() {
compopt -o default
local common core cur database files prev query remove sync upgrade o
local yays show getpkgbuild web
local cur prev words cword
_init_completion || return
database=('asdeps asexplicit')
files=('list machinereadable refresh regex' 'l x y')
query=('changelog check deps explicit file foreign groups info list native owns
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 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
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 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' 'W web'; do
_arch_incomp "$o" && break
done
if [[ $? != 0 ]]; then
_arch_ptr2comp core
elif [[ ! $prev =~ ^-[[:alnum:]_]*[Vbhr] && ! $prev == --@(cachedir|color|config|dbpath|help|hookdir|gpgdir|logfile|root|version) ]]; then
[[ $cur == -* ]] && _arch_ptr2comp ${o#* } common ||
case ${o% *} in
D | R)
_pacman_pkg Qq
;;
F)
{ _arch_incomp 'l list' && _pacman_pkg Slq; } ||
_arch_incomp 'o owns' ||
compopt +o default
;;
Q)
{ _arch_incomp 'g groups' && _pacman_pkg Qg sort; } ||
{ _arch_incomp 'p file' && _pacman_file; } ||
{ _arch_incomp 's search' && compopt +o default; } ||
{ _arch_incomp 'u upgrades' && compopt +o default; } ||
_arch_incomp 'o owns' ||
_pacman_pkg Qq
;;
S)
{ _arch_incomp 'g groups' && _pacman_pkg Sg; } ||
{ _arch_incomp 'l list' && _pacman_repo_list; } ||
{ _arch_incomp 's search' && compopt +o default; } ||
_yay_pkg
;;
U)
_pacman_file
;;
G)
_yay_pkg
;;
W)
_yay_pkg
;;
esac
fi
true
}
_pacman_file() {
compopt -o filenames
_filedir 'pkg.*'
}
complete -F _yay yay
# ex:et ts=2 sw=2 ft=sh
================================================
FILE: completions/fish
================================================
# vim:fdm=marker foldlevel=0 tabstop=2 shiftwidth=2 filetype=fish
# Original Author for pacman: Giorgio Lando
# Updated for yay by jguer
set -l progname yay
# Yay constants
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'
# Pacman constants
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'
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
complete -c $progname -n "$noopt" -a "-D" -d "Modify the package database"
complete -c $progname -n "$noopt" -a "-Q" -d "Query the package database"
# Primary operations
complete -c $progname -s D -f -l database -n "$noopt" -d 'Modify the package database'
complete -c $progname -s Q -f -l query -n "$noopt" -d 'Query the package database'
complete -c $progname -s 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 -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 -d 'Display version and exit'
complete -c $progname -s h -f -l help -d 'Display help'
# General options
# Only offer these once a command has been given so they get prominent display
complete -c $progname -n "not $noopt" -s b -l dbpath -d 'Alternate database location' -xa "(__fish_complete_directories)"
complete -c $progname -n "not $noopt" -s r -l root -d 'Alternate installation root' -xa "(__fish_complete_directories)"
complete -c $progname -n "not $noopt" -s v -l verbose -d 'Output more status messages' -f
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' -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
complete -c $progname -n "not $noopt" -l gpgdir -d 'Alternate home directory for GnuPG' -xa "(__fish_complete_directories)"
complete -c $progname -n "not $noopt" -l hookdir -d 'Alternate hook location' -xa "(__fish_complete_directories)"
complete -c $progname -n "not $noopt" -l logfile -d 'Alternate log file'
complete -c $progname -n "not $noopt" -l noconfirm -d 'Bypass any confirmation' -f
complete -c $progname -n "not $noopt" -l sysroot -d 'Operate on a mounted guest system (root-only)' -xa "(__fish_complete_directories)"
# File, query, sync options (files, query, sync)
for condition in files query sync
complete -c $progname -n "$$condition" -s q -l quiet -d 'Show less information' -f
end
# Transaction options (sync, remove, upgrade)
for condition in sync remove upgrade
complete -c $progname -n "$$condition" -s d -l nodeps -d 'Skip [all] dependency checks' -f
complete -c $progname -n "$$condition" -s p -l print -d 'Dry run, only print targets' -f
complete -c $progname -n "$$condition" -l assume-installed -d 'Add a virtual package to satisfy dependencies' -f
complete -c $progname -n "$$condition" -l dbonly -d 'Modify database entry only' -f
complete -c $progname -n "$$condition" -l noprogressbar -d 'Do not display progress bar' -f
complete -c $progname -n "$$condition" -l noscriptlet -d 'Do not execute install script' -f
complete -c $progname -n "$$condition" -l print-format -d 'Specify printf-like format' -x
end
# File and query options (files, query)
for condition in files query
complete -c $progname -n "$$condition" -s l -l list -d 'List the files owned by PACKAGE' -f
end
# File and sync options (files, sync)
for condition in files sync
complete -c $progname -n "$$condition" -s y -l refresh -d 'Download fresh package databases [force]' -f
end
# Query and sync options (query, sync)
for condition in query sync
complete -c $progname -n "$$condition" -s g -l groups -d 'Display members of [all] package GROUP' -xa "$listgroups"
end
# Sync and upgrade options (sync, upgrade)
for condition in sync upgrade
complete -c $progname -n "$$condition" -l asdeps -d 'Install packages as non-explicitly installed' -f
complete -c $progname -n "$$condition" -l asexplicit -d 'Install packages as explicitly installed' -f
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)' -rF
end
# Database options
set -l has_db_opt '__fish_contains_opt asdeps asexplicit check -s k'
complete -c $progname -n "$database; and not $has_db_opt" -s k -l check -d 'Check database validity'
complete -c $progname -n "$database" -s q -l quite -d 'Suppress output of success messages' -f
complete -c $progname -n "$database; and not $has_db_opt" -l asdeps -d 'Mark PACKAGE as dependency' -x
complete -c $progname -n "$database; and not $has_db_opt" -l asexplicit -d 'Mark PACKAGE as explicitly installed' -x
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"
# Query options
complete -c $progname -n "$query" -s c -l changelog -d 'View the change log of PACKAGE' -f
complete -c $progname -n "$query" -s d -l deps -d 'List only non-explicit packages (dependencies)' -f
complete -c $progname -n "$query" -s e -l explicit -d 'List only explicitly installed packages' -f
complete -c $progname -n "$query" -s i -l info -d 'View PACKAGE [backup files] information' -f
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' -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
complete -c $progname -n "$query" -d 'Installed package' -xa "$listinstalled"
# Remove options
complete -c $progname -n "$remove" -s c -l cascade -d 'Also remove packages depending on PACKAGE' -f
complete -c $progname -n "$remove" -s n -l nosave -d 'Ignore file backup designations' -f
complete -c $progname -n "$remove" -s s -l recursive -d 'Also remove dependencies of PACKAGE' -f
complete -c $progname -n "$remove" -s u -l unneeded -d 'Only remove targets not required by PACKAGE' -f
complete -c $progname -n "$remove" -d 'Installed package' -xa "$listinstalled"
# Sync options
complete -c $progname -n "$sync" -s c -l clean -d 'Remove [all] packages from cache' -f
complete -c $progname -n "$sync" -s i -l info -d 'View PACKAGE [extended] information' -f
complete -c $progname -n "$sync" -s l -l list -d 'List all packages in REPOSITORY' -xa "$listrepos"
complete -c $progname -n "$sync" -s s -l search -d 'Search remote repositories for regexp' -f
complete -c $progname -n "$sync" -s u -l sysupgrade -d 'Upgrade all packages that are out of date'
complete -c $progname -n "$sync" -s w -l downloadonly -d 'Only download the target packages'
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, 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" -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
complete -c $progname -n "$yayspecific" -l gendb -d 'Generate development package DB' -f
# Show options
complete -c $progname -n "$show" -s c -l complete -d 'Print a list of all AUR and repo packages' -f
#complete -c $progname -n "$show" -s f -l fish -d 'During complete adjust the output for the fish shell' -f
complete -c $progname -n "$show" -s d -l defaultconfig -d 'Print default yay configuration' -f
complete -c $progname -n "$show" -s g -l currentconfig -d 'Print current yay configuration' -f
complete -c $progname -n "$show" -s s -l stats -d 'Display system package statistics' -f
complete -c $progname -n "$show" -s w -l news -d 'Print arch news' -f
complete -c $progname -n "$show" -s q -l quiet -d 'Do not print news description' -f
# 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
# 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 editor -d 'Editor to use' -f
complete -c $progname -n "not $noopt" -l editorflags -d 'Editor flags to use' -f
complete -c $progname -n "not $noopt" -l makepkg -d 'Makepkg command to use' -f
complete -c $progname -n "not $noopt" -l pacman -d 'Pacman command to use' -f
complete -c $progname -n "not $noopt" -l tar -d 'Tar command to use' -f
complete -c $progname -n "not $noopt" -l git -d 'Git command to use' -f
complete -c $progname -n "not $noopt" -l gpg -d 'Gpg command to use' -f
complete -c $progname -n "not $noopt" -l config -d 'The pacman config file to use' -r
complete -c $progname -n "not $noopt" -l makepkgconf -d 'Use custom makepkg.conf location' -r
complete -c $progname -n "not $noopt" -l nomakepkgconf -d 'Use default makepkg.conf' -f
complete -c $progname -n "not $noopt" -l requestsplitn -d 'Max amount of packages to query per AUR request' -f
complete -c $progname -n "not $noopt" -l completioninterval -d 'Refresh interval for completion cache' -f
complete -c $progname -n "not $noopt" -l sortby -d 'Sort AUR results by a specific field during search' -xa "{votes,popularity,name,base,submitted,modified}"
complete -c $progname -n "not $noopt" -l searchby -d 'Search for AUR packages by querying the specified field' -xa "{name,name-desc,maintainer,depends,checkdepends,makedepends,optdepends}"
complete -c $progname -n "not $noopt" -l answerclean -d 'Set a predetermined answer for the clean build menu' -xa "{All,None,Installed,NotInstalled}"
complete -c $progname -n "not $noopt" -l answerdiff -d 'Set a predetermined answer for the edit diff menu' -xa "{All,None,Installed,NotInstalled}"
complete -c $progname -n "not $noopt" -l answeredit -d 'Set a predetermined answer for the edit pkgbuild menu' -xa "{All,None,Installed,NotInstalled}"
complete -c $progname -n "not $noopt" -l answerupgrade -d 'Set a predetermined answer for the upgrade menu' -f
complete -c $progname -n "not $noopt" -l noanswerclean -d 'Unset the answer for the clean build menu' -f
complete -c $progname -n "not $noopt" -l noanswerdiff -d 'Unset the answer for the diff menu' -f
complete -c $progname -n "not $noopt" -l noansweredit -d 'Unset the answer for the edit pkgbuild menu' -f
complete -c $progname -n "not $noopt" -l noanswerupgrade -d 'Unset the answer for the upgrade menu' -f
complete -c $progname -n "not $noopt" -l cleanmenu -d 'Give the option to clean build PKGBUILDS' -f
complete -c $progname -n "not $noopt" -l diffmenu -d 'Give the option to show diffs for build files' -f
complete -c $progname -n "not $noopt" -l editmenu -d 'Give the option to edit/view PKGBUILDS' -f
complete -c $progname -n "not $noopt" -l 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 cleanafter -d 'Clean package sources after successful build' -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 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 pgpfetch -d 'Prompt to import PGP keys from PKGBUILDs' -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 combinedupgrade -d 'Refresh then perform the repo and AUR upgrade together' -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 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
complete -c $progname -n "not $noopt" -l norebuild -d 'Skip package build if in cache and up to date' -f
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
================================================
FILE: completions/zsh
================================================
#compdef yay
# vim:noexpandtab tabstop=2 shiftwidth=2 filetype=zsh
typeset -A opt_args
setopt extendedglob
# options for passing to _arguments: main pacman commands
_pacman_opts_commands=(
{-D,--database}'[Modify database]'
{-F,--files}'[Query the files database]'
{-G,--getpkgbuild}'[Get PKGBUILD from ABS or AUR]'
{-Q,--query}'[Query the package database]'
{-R,--remove}'[Remove a package from the system]'
{-P,--show}'[Print yay information]'
{-S,--sync}'[Synchronize packages]'
{-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=(
'(-N --repo)'{-N,--repo}'[Assume targets are from the repositories]'
'(-a --aur)'{-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)'{-b,--dbpath}'[Alternate database location]:database_location:_files -/'
'--color[colorize the output]:color options:(always never auto)'
'(- *)'{-h,--help}'[Display syntax for the given operation]'
'(-r --root)'{-r,--root}'[Set alternate installation root]:installation root:_files -/'
'(-v --verbose)'{-v,--verbose}'[Be more verbose]'
'--cachedir[Alternate package cache location]:cache_location:_files -/'
'--config[An alternate configuration file]:config file:_files'
'(--nomakepkgconf)--makepkgconf[makepkg.conf file to use]:config file:_files'
'(--makepkgconf)--nomakepkgconf[Use the default makepkg.conf]'
'--requestsplitn[Max amount of packages to query per AUR request]:number'
'--completioninterval[Time in days to refresh completion cache]:number'
'--confirm[Always ask for confirmation]'
'--debug[Display debug messages]'
'--gpgdir[Set an alternate directory for GnuPG (instead of /etc/pacman.d/gnupg)]: :_files -/'
'--hookdir[Set an alternate hook location]: :_files -/'
'--logfile[An alternate log file]:config file:_files'
'--noconfirm[Do not ask for confirmation]'
'--noprogressbar[Do not show a progress bar when downloading files]'
'--noscriptlet[Do not execute the install scriptlet if one exists]'
'--save[Causes config options to be saved back to the config file]'
'--builddir[Directory to use for building AUR Packages]:build dir:_files -/'
'--editor[Editor to use when editing PKGBUILDs]:editor:_files'
'--editorflags[Flags to pass to editor]'
'--makepkg[makepkg command to use]:makepkg:_files'
'--pacman[pacman command to use]:pacman:_files'
'--git[git command to use]:git:_files'
'--gpg[gpg command to use]:gpg:_files'
'--sortby[Sort AUR results by a specific field during search]:sortby options:(votes popularity name base submitted modified)'
'--searchby[Search for packages using a specified field]:query'
'(--noanswerclean)--answerclean[Set a predetermined answer for the clean build menu]:answer'
'(--noanswerdiff)--answerdiff[Set a predetermined answer for the diff menu]:answer'
'(--noansweredit)--answeredit[Set a predetermined answer for the edit pkgbuild menu]:answer'
'(--noanswerupgrade)--answerupgrade[Set a predetermined answer for the upgrade menu]:answer'
'(--answerclean)--noanswerclean[Unset the answer for the clean build menu]'
'(--answerdiff)--noanswerdiff[Unset the answer for the diff menu]'
'(--answeredit)--noansweredit[Unset the answer for the edit pkgbuild menu]'
'(--answerupgrade)--noanswerupgrade[Unset the answer for the upgrade menu]'
'--cleanmenu[Give the option to clean build PKGBUILDS]'
'--diffmenu[Give the option to show diffs for build files]'
'--editmenu[Give the option to edit/view PKGBUILDS]'
'(--askyesremovemake --removemake --noremovemake)--askremovemake[Ask to remove makedepends after install]'
'(--askremovemake --removemake --noremovemake)--askyesremovemake[Ask to remove makedepends after install(with "Y" as default)]'
'(--askremovemake --askyesremovemake --noremovemake)--removemake[Remove makedepends after install]'
"(--askremovemake --askyesremovemake --removemake)--noremovemake[Don't remove makedepends after install]"
'(--bottomup)--topdown[Show repository packages first]'
'(--topdown)--bottomup[Show AUR packages first]'
'(--doublelineresults)--singlelineresults[List each search result on its own line]'
'(--singlelineresults)--doublelineresults[List each search result on two lines, like pacman]'
'--devel[Check -git/-svn/-hg development version]'
'--cleanafter[Clean package sources after successful build]'
'--keepsrc[Keep pkg/ and src/ after building packages]'
'--separatesources[Separate query results by source, AUR and sync]'
'(--redownloadall --noredownload)--redownload[Always download pkgbuilds of targets]'
'(--redownload --noredownload)--redownloadall[Always download pkgbuilds of all AUR packages]'
'(--redownload --redownloadall)--noredownload[Skip pkgbuild download if in cache and up to date]'
'--provides[Look for matching providers when searching for packages]'
'--pgpfetch[Prompt to import PGP keys from PKGBUILDs]'
"--useask[Automatically resolve conflicts using pacman's ask flag]"
'--combinedupgrade[Refresh then perform the repo and AUR upgrade together]'
'--batchinstall[Build multiple AUR packages then install them together]'
'(--rebuildall --rebuildtree --norebuild)--rebuild[Always build target packages]'
'(--rebuild --rebuildtree --norebuild)--rebuildall[Always build all AUR packages]'
'(--rebuild --rebuildall --norebuild)--rebuildtree[Always build all AUR packages even if installed]'
'(--rebuild --rebuildall --rebuildtree)--norebuild[Skip package build if in cache and up to date]'
'--mflags[Pass arguments to makepkg]:mflags'
'--gpgflags[Pass arguments to gpg]:gpgflags'
'--sudo[The command to use for sudo calls]:command'
'--sudoflags[Passes arguments to sudo]:flag'
'--sudoloop[Loop sudo calls in the background to avoid timeout]'
)
# options for passing to _arguments: options for --upgrade commands
_pacman_opts_pkgfile=(
'*-d[Skip dependency checks]'
'*--nodeps[Skip dependency checks]'
'*--assume-installed[Add virtual package to satisfy dependencies]'
'--dbonly[Only remove database entry, do not remove files]'
'--overwrite[Overwrite conflicting files]:file:_files -g "*"'
'--needed[Do not reinstall up to date packages]'
'(--asexplicit)--asdeps[mark packages as non-explicitly installed]'
'(--asdeps)--asexplicit[mark packages as explicitly installed]'
'(-p --print)'{-p,--print}'[Only print the targets instead of performing the operation]'
'*--ignore[Ignore a package upgrade]:package: _pacman_completions_all_packages'
'*--ignoregroup[Ignore a group upgrade]:package group:_pacman_completions_all_groups'
'--print-format[Specify how the targets should be printed]'
'*:package file:_files -g "*.pkg.tar*~*.sig(.,@)"'
)
# options for passing to _arguments: subactions for --query command
_pacman_opts_query_actions=(
'(-Q --query)'{-Q,--query}
{-g,--groups}'[View all members of a package group]:*:package groups:->query_group'
{-o,--owns}'[Query the package that owns a file]:file:_files'
{-p,--file}'[Package file to query]:*:package file:->query_file'
{-s,--search}'[Search package names and descriptions]:*:search text:->query_search'
)
# options for passing to _arguments: options for --query and subcommands
_pacman_opts_query_modifiers=(
'(-c --changelog)'{-c,--changelog}'[List package changelog]'
'(-d --deps)'{-d,--deps}'[List packages installed as dependencies]'
'(-e --explicit)'{-e,--explicit}'[List packages explicitly installed]'
{\*-i,\*--info}'[View package information]'
{\*-k,\*--check}'[Check package files]'
'(-l --list)'{-l,--list}'[List package contents]'
'(-m --foreign)'{-m,--foreign}'[List installed packages not found in sync db(s)]'
'(-n --native)'{-n,--native}'[List installed packages found in sync db(s)]'
'(-q --quiet)'{-q,--quiet}'[Show less information for query and search]'
'(-t --unrequired)'{-t,--unrequired}'[List packages not required by any package]'
'(-u --upgrades)'{-u,--upgrades}'[List packages that can be upgraded]'
)
# -Y
_pacman_opts_yay_modifiers=(
'(-c --clean)'{-c,--clean}'[Remove unneeded dependencies]'
'--gendb[Generates development package DB used for updating]'
)
# -G
_pacman_opts_getpkgbuild_modifiers=(
'(-f --force)'{-f,--force}'[Force download for existing ABS packages]'
'(-p --print)'{-p,--print}'[Print PKGBUILDs]:package:_pacman_completions_all_packages'
)
# -W
_pacman_opts_web_modifiers=(
'(-u --unvote)'{-u,--unvote}'[Unvote AUR package]:package:_pacman_completions_all_packages'
'(-v --vote)'{-v,--vote}'[Vote AUR package]:package:_pacman_completions_all_packages'
)
# -P
_pacman_opts_print_modifiers=(
'(-c --complete)'{-c,--complete}'[Used for completions]'
'(-d --defaultconfig)'{-d,--defaultconfig}'[Print default yay configuration]'
'(-g --config)'{-g,--config}'[Print current yay configuration]'
'(-n --numberupgrades)'{-n,--numberupgrades}'[Print number of updates]'
'(-s --stats)'{-s,--stats}'[Display system package statistics]'
'(-u --upgrades)'{-u,--upgrades}'[Print update list]'
'(-w --news)'{-w,--news}'[Print arch news]'
)
# options for passing to _arguments: options for --remove command
_pacman_opts_remove=(
'(-c --cascade)'{-c,--cascade}'[Remove all dependent packages]'
'(-d --nodeps)'{-d,--nodeps}'[Skip dependency checks]'
'*--assume-installed[Add virtual package to satisfy dependencies]'
'(-n --nosave)'{-n,--nosave}'[Remove protected configuration files]'
'(-p --print)'{-p,--print}'[Only print the targets instead of performing the operation]'
{\*-s,\*--recursive}'[Remove dependencies not required by other packages]'
'(-u --unneeded)'{-u,--unneeded}'[Remove unneeded packages]'
'--dbonly[Only remove database entry, do not remove files]'
'--print-format[Specify how the targets should be printed]'
'*:installed package:_pacman_completions_installed_packages'
)
_pacman_opts_database=(
'(--asexplicit)--asdeps[mark packages as non-explicitly installed]'
'(--asdeps)--asexplicit[mark packages as explicitly installed]'
'*:installed package:_pacman_completions_installed_packages'
)
_pacman_opts_files=(
'(-l --list)'{-l,--list}'[List the files owned by the queried package]:package:_pacman_completions_all_packages'
'(-x --regex)'{-x,--regex}'[Enable searching using regular expressions]:regex:'
'(-y --refresh)'{-y,--refresh}'[Download fresh files databases from the server]'
'--machinereadable[Produce machine-readable output]'
'(-q --quiet)'{-q,--quiet}'[Show less information for query and search]'
)
# options for passing to _arguments: options for --sync command
_pacman_opts_sync_actions=(
'(-S --sync)'{-S,--sync}
{\*-c,\*--clean}'[Remove old packages from cache]:\*:clean:->sync_clean'
{-g,--groups}'[View all members of a package group]:*:package groups:->sync_group'
{-s,--search}'[Search package names and descriptions]:*:search text:->sync_search'
'--dbonly[Only remove database entry, do not remove files]'
'--needed[Do not reinstall up to date packages]'
'--recursive[Reinstall all dependencies of target packages]'
)
# options for passing to _arguments: options for --sync command
_pacman_opts_sync_modifiers=(
{\*-d,\*--nodeps}'[Skip dependency checks]'
'*--assume-installed[Add virtual package to satisfy dependencies]'
{\*-i,\*--info}'[View package information]'
'(-l --list)'{-l,--list}'[List all packages in a repository]'
'(-p --print)'{-p,--print}'[Print download URIs for each package to be installed]'
'(-q --quiet)'{-q,--quiet}'[Show less information for query and search]'
{\*-u,\*--sysupgrade}'[Upgrade all out-of-date packages]'
'(-w --downloadonly)'{-w,--downloadonly}'[Download packages only]'
{\*-y,\*--refresh}'[Download fresh package databases]'
'*--ignore[Ignore a package upgrade]:package: _pacman_completions_all_packages'
'*--ignoregroup[Ignore a group upgrade]:package group:_pacman_completions_all_groups'
'(--asexplicit)--asdeps[Install packages as non-explicitly installed]'
'(--asdeps)--asexplicit[Install packages as explicitly installed]'
'--overwrite[Overwrite conflicting files]:files:_files'
'--print-format[Specify how the targets should be printed]'
)
# handles --help subcommand
_pacman_action_help() {
_arguments -s : \
"$_pacman_opts_commands[@]"
}
# handles cases where no subcommand has yet been given
_pacman_action_none() {
_arguments -s : \
"$_pacman_opts_commands[@]"
}
# handles --query subcommand
_pacman_action_query() {
local context state line
typeset -A opt_args
case $state in
query_file)
_arguments -s : \
"$_pacman_opts_common[@]" \
"$_pacman_opts_query_modifiers[@]" \
'*:package file:_files -g "*.pkg.tar*~*.sig(.,@)"'
;;
query_group)
_arguments -s : \
"$_pacman_opts_common[@]" \
"$_pacman_opts_query_modifiers[@]" \
'*:groups:_pacman_completions_installed_groups'
;;
query_owner)
_arguments -s : \
"$_pacman_opts_common[@]" \
"$_pacman_opts_query_modifiers[@]" \
'*:file:_files'
;;
query_search)
_arguments -s : \
"$_pacman_opts_common[@]" \
"$_pacman_opts_query_modifiers[@]" \
'*:search text: '
;;
*)
_arguments -s : \
"$_pacman_opts_common[@]" \
"$_pacman_opts_query_actions[@]" \
"$_pacman_opts_query_modifiers[@]" \
'*:package:_pacman_completions_installed_packages'
;;
esac
}
# handles --remove subcommand
_pacman_action_remove() {
_arguments -s : \
'(--remove -R)'{-R,--remove} \
"$_pacman_opts_common[@]" \
"$_pacman_opts_remove[@]"
}
# handles --database subcommand
_pacman_action_database() {
_arguments -s : \
'(--database -D)'{-D,--database} \
"$_pacman_opts_common[@]" \
"$_pacman_opts_database[@]"
}
# handles --files subcommand
_pacman_action_files() {
_arguments -s : \
'(--files -F)'{-F,--files} \
"$_pacman_opts_common[@]" \
"$_pacman_opts_files[@]"
}
_pacman_action_deptest () {
_arguments -s : \
'(--deptest)-T' \
"$_pacman_opts_common[@]" \
":packages:_pacman_all_packages"
}
# handles --sync subcommand
_pacman_action_sync() {
local context state line
typeset -A opt_args
if (( $+words[(r)--clean] )); then
state=sync_clean
elif (( $+words[(r)--groups] )); then
state=sync_group
elif (( $+words[(r)--search] )); then
state=sync_search
fi
case $state in
sync_clean)
_arguments -s : \
{\*-c,\*--clean}'[Remove old packages from cache]' \
"$_pacman_opts_common[@]" \
"$_pacman_opts_sync_modifiers[@]"
;;
sync_group)
_arguments -s : \
"$_pacman_opts_common[@]" \
"$_pacman_opts_sync_modifiers[@]" \
'(-g --group)'{-g,--groups} \
'*:package group:_pacman_completions_all_groups'
;;
sync_search)
_arguments -s : \
"$_pacman_opts_common[@]" \
"$_pacman_opts_sync_modifiers[@]" \
'*:search text: '
;;
*)
_arguments -s : \
"$_pacman_opts_common[@]" \
"$_pacman_opts_sync_actions[@]" \
"$_pacman_opts_sync_modifiers[@]" \
'*:package:_pacman_completions_all_packages'
;;
esac
}
# handles --upgrade subcommand
_pacman_action_upgrade() {
_arguments -s : \
'(-U --upgrade)'{-U,--upgrade} \
"$_pacman_opts_common[@]" \
"$_pacman_opts_pkgfile[@]"
}
# handles --version subcommand
_pacman_action_version() {
# no further arguments
return 0
}
# provides completions for package groups
_pacman_completions_all_groups() {
local -a cmd groups
_pacman_get_command
groups=( $(_call_program groups $cmd[@] -Sg) )
typeset -U groups
if [[ ${words[CURRENT-1]} == '--ignoregroup' ]]; then
_sequence compadd -S ',' "$@" -a groups
else
compadd "$@" -a groups
fi
}
# provides completions for packages available from repositories
# these can be specified as either 'package' or 'repository/package'
_pacman_completions_all_packages() {
local -a seq sep cmd packages repositories packages_long
if [[ ${words[CURRENT-1]} == '--ignore' ]]; then
seq='_sequence'
sep=(-S ',')
else
seq=
sep=()
fi
if compset -P1 '*/*'; then
packages=( $(_call_program packages yay -Pc ${words[CURRENT]%/*}) )
typeset -U packages
${seq} _wanted repo_packages expl "repository/package" compadd ${sep[@]} ${(@)packages}
else
packages=( $(_call_program packages yay -Pc ) )
typeset -U packages
${seq} _wanted packages expl "packages" compadd ${sep[@]} - "${(@)packages}"
repositories=($(pacman-conf --repo-list))
typeset -U repositories
_wanted repo_packages expl "repository/package" compadd -S "/" $repositories
fi
}
# provides completions for package groups
_pacman_completions_installed_groups() {
local -a cmd groups
_pacman_get_command
groups=(${(o)${(f)"$(_call_program groups $cmd[@] -Qg)"}% *})
typeset -U groups
compadd "$@" -a groups
}
# provides completions for installed packages
_pacman_completions_installed_packages() {
local -a cmd packages packages_long
packages_long=(/var/lib/pacman/local/*(/))
packages=( ${${packages_long#/var/lib/pacman/local/}%-*-*} )
compadd "$@" -a packages
}
_pacman_all_packages() {
_alternative : \
'localpkgs:local packages:_pacman_completions_installed_packages' \
'repopkgs:repository packages:_pacman_completions_all_packages'
}
# provides completions for repository names
_pacman_completions_repositories() {
local -a cmd repositories
repositories=($(pacman-conf --repo-list))
# Uniq the array
typeset -U repositories
compadd "$@" -a repositories
}
# builds command for invoking pacman in a _call_program command - extracts
# relevant options already specified (config file, etc)
# $cmd must be declared by calling function
_pacman_get_command() {
# this is mostly nicked from _perforce
cmd=( "pacman" "2>/dev/null")
integer i
for (( i = 2; i < CURRENT - 1; i++ )); do
if [[ ${words[i]} = "--config" || ${words[i]} = "--root" ]]; then
cmd+=( ${words[i,i+1]} )
fi
done
}
# main dispatcher
_pacman_zsh_comp() {
local -a args cmds;
local tmp
args=( ${${${(M)words:#-*}#-}:#-*} )
for tmp in $words; do
cmds+=("${${_pacman_opts_commands[(r)*$tmp\[*]%%\[*}#*\)}")
done
case $args in #$words[2] in
h*)
if (( ${(c)#args} <= 1 && ${(w)#cmds} <= 1 )); then
_pacman_action_help
else
_message "no more arguments"
fi
;;
*h*)
_message "no more arguments"
;;
D*)
_pacman_action_database
;;
F*)
_pacman_action_files
;;
Q*g*) # ipkg groups
_arguments -s : \
"$_pacman_opts_common[@]" \
"$_pacman_opts_query_modifiers[@]" \
'*:groups:_pacman_completions_installed_groups'
;;
Q*o*) # file
_arguments -s : \
"$_pacman_opts_common[@]" \
"$_pacman_opts_query_modifiers[@]" \
'*:package file:_files'
;;
Q*p*) # file *.pkg.tar*
_arguments -s : \
"$_pacman_opts_common[@]" \
"$_pacman_opts_query_modifiers[@]" \
'*:package file:_files -g "*.pkg.tar*~*.sig(.,@)"'
;;
Q*)
_pacman_action_query
;;
P*)
_arguments -s : \
'-P' \
"$_pacman_opts_print_modifiers[@]"
;;
W*)
_arguments -s : \
'-W' \
"$_pacman_opts_web_modifiers[@]"
;;
R*)
_pacman_action_remove
;;
S*c*) # no completion
_arguments -s : \
'(-c --clean)'{\*-c,\*--clean}'[Remove all files from the cache]' \
"$_pacman_opts_common[@]"
;;
S*l*) # repos
_arguments -s : \
"$_pacman_opts_common[@]" \
"$_pacman_opts_sync_modifiers[@]" \
'*:package repo:_pacman_completions_repositories' \
;;
S*g*) # pkg groups
_arguments -s : \
"$_pacman_opts_common[@]" \
"$_pacman_opts_sync_modifiers[@]" \
'*:package group:_pacman_completions_all_groups'
;;
S*s*)
_arguments -s : \
"$_pacman_opts_common[@]" \
"$_pacman_opts_sync_modifiers[@]" \
'*:search text: '
;;
S*)
_pacman_action_sync
;;
T*)
_pacman_action_deptest
;;
U*)
_pacman_action_upgrade
;;
V*)
_pacman_action_version
;;
Y*)
_arguments -s : \
'-Y' \
"$_pacman_opts_yay_modifiers[@]"
;;
G*)
_arguments -s : \
'-G' \
"$_pacman_opts_getpkgbuild_modifiers[@]"
;;
*)
case ${(M)words:#--*} in
*--help*)
if (( ${(w)#cmds} == 1 )); then
_pacman_action_help
else
return 0;
fi
;;
*--sync*)
_pacman_action_sync
;;
*--query*)
_pacman_action_query
;;
*--remove*)
_pacman_action_remove
;;
*--deptest*)
_pacman_action_deptest
;;
*--database*)
_pacman_action_database
;;
*--files*)
_pacman_action_files
;;
*--version*)
_pacman_action_version
;;
*--upgrade*)
_pacman_action_upgrade
;;
*)
_pacman_action_none
;;
esac
;;
esac
}
_pacman_comp() {
case "$service" in
yay)
_pacman_zsh_comp "$@"
;;
*)
_message "Error"
;;
esac
}
_pacman_comp "$@"
================================================
FILE: doc/yay.8
================================================
.TH "YAY" "8" "2019\-10\-21" "Yay v12.0+" "Yay Manual"
.nh
.ad l
.SH NAME
yay \- AUR Helper written in go
.SH SYNOPSIS
\fIyay\fR [options] [targets]
.sp
\fIyay\fR
.sp
\fIyay\fR
.SH DESCRIPTION
Yay is a Pacman wrapper with AUR support. It passes options to Makepkg and
Pacman after resolving packages to install/upgrade.
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 and targets are defined.
.TP
.B \-B, \-\-build
Build a PKGBUILD in a given directory.
.TP
.B \-P, \-\-show
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.
.TP
.B \-W, \-\-web
Web related operations such as voting for AUR packages.
.RE
If no operation is specified 'yay \-Syu' will be performed
If no operation is specified and targets are provided \-Y will be assumed
.SH EXTENDED PACMAN OPERATIONS
.TP
.B \-S, \-Si, \-Sl, \-Ss, \-Su, \-Sc, \-Qu
These operations are extended to support both AUR and repo packages.
.TP
.B \-Sc
Yay will also clean cached AUR package and any untracked Files in the
cache. Cleaning untracked files will wipe any downloaded sources or
built packages but will keep already downloaded vcs sources.
.TP
.B \-R
Yay will also remove cached data about devel packages.
.SH NEW OPTIONS
.TP
.B \-N, \-\-repo
Assume all targets are from the repositories. Additionally Actions such as
sysupgrade will only act on repository packages.
.TP
.B \-a, \-\-aur
Assume all targets are from the AUR. Additionally Actions such as
sysupgrade will only act on AUR packages.
Note that dependency resolving will still act normally and include repository
packages.
.SH YAY OPTIONS (APPLY TO \-Y AND \-\-YAY)
.TP
.B
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
development package, when there is a new commit Yay will know to update. This
is done per package whenever a package is synced. This option should only be
used when migrating to Yay from another AUR helper.
.TP
.B \-c, \-\-clean
Remove unneeded dependencies.
.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 \-d, \-\-defaultconfig
Print default yay configuration.
.TP
.B \-g, \-\-currentconfig
Print current yay configuration.
.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 \-w, \-\-news
Print new news from the Archlinux homepage. News is considered new if it is
newer than the build date of all native packages. Pass this twice to show all
available news.
.TP
.B \-q, \-\-quiet
Only show titles when printing news.
.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
Causes the following options to be saved back to the config file. This
provides an easy way to change config options without directly editing the
file.
.TP
.B \-\-aururl
Set an alternative AUR URL.
.TP
.B \-\-aurrpcurl
Set an alternative URL for the AUR /rpc endpoint.
.TP
.B \-\-builddir
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 \-\-editor
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
.B \-\-editorflags
Passes arguments to the editor. These flags get passed to every instance where
the editor is called by Yay. Arguments are split on whitespace before being
passed to the editor. Multiple arguments may be passed by supplying a space
separated list that is quoted by the shell.
.TP
.B \-\-makepkg
The command to use for \fBmakepkg\fR calls. This can be a command in
\fBPATH\fR or an absolute path to the file.
.TP
.B \-\-pacman
The command to use for \fBpacman\fR calls. This can be a command in
\fBPATH\fR or an absolute path to the file.
.TP
.B \-\-git
The command to use for \fBgit\fR calls. This can be a command in
\fBPATH\fR or an absolute path to the file.
.TP
.B \-\-gitflags
Passes arguments to git. These flags get passed to every instance where
git is called by yay. Arguments are split on whitespace before being
passed to git. Multiple arguments may be passed by supplying a space
separated list that is quoted by the shell.
.TP
.B \-\-gpg
The command to use for \fBgpg\fR calls. This can be a command in
\fBPATH\fR or an absolute path to the file.
.TP
.B \-\-config
The pacman config file to use.
.TP
.B \-\-makepkgconf
The config file for makepkg to use\%. If this is not set then the default
config file will be used.
.TP
.B \-\-nomakepkgconf
Reset the makepkg config file back to its default.
.TP
.B \-\-requestsplitn
The maximum amount of packages to request per AUR query. The higher the
number the faster AUR requests will be. Requesting too many packages in one
AUR query will cause an error. This should only make a noticeable difference
with very large requests (>500) packages.
.TP
.B \-\-completioninterval
Time in days to refresh the completion cache. Setting this to 0 will cause
the cache to be refreshed every time, while setting this to -1 will cause the
cache to never be refreshed.
.TP
.B \-\-sortby
Sort AUR results by a specific field during search.
.TP
.B \-\-searchby
Search for AUR packages by querying the specified field.
.TP
.B \-\-answerclean
Set a predetermined answer for the clean build menu question. This answer
will be used instead of reading from standard input but will be parsed exactly
the same.
.TP
.B \-\-answerdiff
Set a predetermined answer for the edit diff menu question. This answer
will be used instead of reading from standard input but will be parsed exactly
the same.
.TP
.B \-\-answeredit
Set a predetermined answer for the edit pkgbuild menu question. This answer
will be used instead of reading from standard input but will be parsed exactly
the same.
.TP
.B \-\-answerupgrade
Set a predetermined answer for the upgrade menu question. Selects which package
ranges or repos to omit for updates. This answer will be used instead of
reading from standard input but will be treated exactly the same.
.TP
.B \-\-noanswerclean
Unset the answer for the clean build menu.
.TP
.B \-\-noanswerdiff
Unset the answer for the diff menu.
.TP
.B \-\-noansweredit
Unset the answer for the edit pkgbuild menu.
.TP
.B \-\-noanswerupgrade
Unset the answer for the upgrade menu.
.TP
.B \-\-cleanmenu
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
build files before building.
Diffs are shown via \fBgit diff\fR which uses
less by default. This behaviour can be changed via git's config, the
\fB$GIT_PAGER\fR or \fB$PAGER\fR environment variables.
.TP
.B \-\-editmenu
Show the edit menu. This menu gives you the option to edit or view PKGBUILDs
before building.
\fBWarning\fR: Yay resolves dependencies ahead of time via the RPC. It is not
recommended to edit pkgbuild variables unless you know what you are doing.
.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.
.TP
.B \-\-noremovemake
Do not remove makedepends after installing packages.
.TP
.B \-\-topdown
Display repository packages first and then AUR packages.
.TP
.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
only Git packages are supported.
Devel checking is done using \fBgit ls-remote\fR. The newest commit hash is
compared against the hash at install time. This allows devel updates to be
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).
If 'devel' is enabled in the configuration file, you can temporarily disable it by
using '--devel=false' on the command line
.TP
.B \-\-cleanafter
Remove untracked files after installation.
Untracked files are removed with the exception of directories.
This allows VCS packages to easily pull an update
instead of having to reclone the entire repo.
.TP
.B \-\-keepsrc
Keep pkg/ and src/ after building packages
.TP
.B \-\-separatesources
Separate query results by source, AUR and sync
.TP
.B \-\-redownload
Always download pkgbuilds of targets even when a copy is available in cache.
.TP
.B \-\-redownloadall
Always download pkgbuilds of all AUR packages even when a copy is available
in cache.
.TP
.B \-\-noredownload
When downloading pkgbuilds if the pkgbuild is found in cache and is equal or
newer than the AUR's version use that instead of downloading a new one.
.TP
.B \-\-provides
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 \-\-pgpfetch
Prompt to import unknown PGP keys from the \fBvalidpgpkeys\fR field of each
PKGBUILD.
.TP
.B \-\-useask
Use pacman's --ask flag to automatically confirm package conflicts. Yay lists
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 \-\-combinedupgrade
During sysupgrade, Yay will first perform a refresh, then show
its combined menu of repo and AUR packages that will be upgraded. Then after
reviewing the pkgbuilds, the repo and AUR upgrade will start with no need
for manual intervention.
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 \-\-batchinstall
When building and installing AUR packages instead of installing each package
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 \-\-rebuild
Always build target packages even when a copy is available in cache.
.TP
.B \-\-rebuildall
Always build all AUR packages even when a copy is available
in cache.
.TP
.B \-\-rebuildtree
When installing an AUR package rebuild and reinstall all of its AUR
dependencies recursively, even the ones already installed. This flag allows
you to easily rebuild packages against your current system's libraries if they
have become incompatible.
.TP
.B \-\-norebuild
When building packages if the package is found in cache and is an equal version
to the one wanted skip the package build and use the existing package.
.TP
.B \-\-mflags
Passes arguments to makepkg. These flags get passed to every instance where
makepkg is called by Yay. Arguments are split on whitespace before being
passed to makepkg. Multiple arguments may be passed by supplying a space
separated list that is quoted by the shell.
.TP
.B \-\-gpgflags
Passes arguments to gpg. These flags get passed to every instance where
gpg is called by Yay. Arguments are split on whitespace before being
passed to gpg. Multiple arguments may be passed by supplying a space
separated list that is quoted by the shell.
.TP
.B \-\-sudo
The command to use for \fBsudo\fR calls. This can be a command in
\fBPATH\fR or an absolute path to the file.
The sudoloop is not guaranteed to work with a custom \fBsudo\fR command.
.TP
.B \-\-sudoflags
Passes arguments to sudo. These flags get passed to every instance where
sudo is called by Yay. Arguments are split on whitespace before being
passed to sudo. Multiple arguments may be passed by supplying a space
separated list that is quoted by the shell.
.TP
.B \-\-sudoloop
Loop sudo calls in the background to prevent sudo from timing out during long
builds.
.SH EXAMPLES
.TP
yay \fIfoo\fR
Search and install from the repos and the \fBAUR\fR\ using yogurt mode.
.TP
yay \-Syu
Update package list and upgrade all currently installed repo and \fBAUR\fR.
.TP
yay \-Sua
Update all currently installed \fBAUR\fR packages.
.TP
yay \-S \fIfoo\fR
Installs package \fIfoo\fR from the repos or the \fBAUR\fR.
.TP
yay \-Ss \fIfoo\fR
Searches for package \fIfoo\fR on the repos or the \fBAUR\fR.
.TP
yay \-Si \fIfoo\fR
Gets information about package \fIfoo\fR from the repos or the \fBAUR\fR.
.TP
yay \-S \fIfoo\fR \-\-mflags "\-\-skipchecksums \-\-skippgpcheck"
Installs \fIfoo\fR while skipping checksums and pgp checks.
.TP
yay \-\-devel \-\-save
Sets devel to true in the config.
.TP
yay \-P \-\-stats
Shows statistics for installed packages and system health.
.TP
pacman -Qmq | grep -Ee '-(cvs|svn|git|hg|bzr|darcs)$' | yay -S --needed -
pacaur-like devel check.
.SH ENVIRONMENT VARIABLES
.TP
.B AURDEST
Can be set to configure the build directory.
Overridden by \-\-builddir.
.TP
.B VISUAL, EDITOR
When editor is not configured, use these variables to pick what editor
to use when editing PKGBUILDS.
.SH FILES
.TP
.B CONFIG DIRECTORY
The config directory is \fI$XDG_CONFIG_HOME/yay/\fR. If
\fB$XDG_CONFIG_HOME\fR is unset, the config directory will fall back to
\fI$HOME/.config/yay\fR.
\fIconfig.json\fR Is used to store all of Yay's config options. Editing
this file should be done through Yay, using the options
mentioned in \fBPERMANENT CONFIGURATION SETTINGS\fR.
.TP
.B CACHE DIRECTORY
The cache directory is \fI$XDG_CACHE_HOME/yay/\fR. If
\fB$XDG_CACHE_HOME\fR is unset, the cache directory will fall back to
\fI$HOME/.cache/yay\fR.
\fIcompletion.cache\fR holds a list of of all packages, including the AUR,
for shell completion. By default the completion files are refreshed every
7 days.
\fIvcs.json\fR tracks VCS packages and the latest commit of each source. If
any of these commits change the package will be upgraded during a devel update.
.TP
.B BUILD DIRECTORY
Unless otherwise set this should be the same as \fBCACHE DIRECTORY\fR. This
directory is used to store downloaded AUR Packages as well as any source files
and built packages from those packages.
.TP
.B PACMAN.CONF
Yay uses Pacman's config file to set certain pacman options either through
go\-alpm or Yay itself. Options inherited include most libalpm options and
pacman options.
Notably: \fBDatabases\fR, \fBColor\fR and \fB*Path/*Dir\fR options are used.
.SH SEE ALSO
.BR makepkg (8),
.BR makepkg.conf (5),
.BR PKGBUILD (5),
.BR pacman (8),
.BR pacman.conf (5)
See the arch wiki at https://wiki.archlinux.org/index.php/Arch_User_Repository for more info on the \fBAUR\fR.
.SH BUGS
Please report bugs to our GitHub page https://github.com/Jguer/yay
.SH AUTHORS
Jguer
.br
Morgan
================================================
FILE: errors.go
================================================
package main
import (
"errors"
"github.com/leonelquinteros/gotext"
)
var ErrPackagesNotFound = errors.New(gotext.Get("could not find all required packages"))
================================================
FILE: get.go
================================================
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
}
================================================
FILE: go.mod
================================================
module github.com/Jguer/yay/v12
require (
github.com/Jguer/aur v1.3.0
github.com/Jguer/dyalpm v0.1.1
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/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.11.1
golang.org/x/net v0.52.0
golang.org/x/sys v0.42.0
golang.org/x/term v0.41.0
gopkg.in/h2non/gock.v1 v1.1.2
gopkg.in/ini.v1 v1.67.1
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/ebitengine/purego v0.9.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.18 // indirect
github.com/itchyny/timefmt-go v0.1.7 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/ohler55/ojg v1.28.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
go 1.26
================================================
FILE: go.sum
================================================
github.com/Jguer/aur v1.3.0 h1:skdjp/P9kB75TBaJmn9PKK/kCeA9QsgjdUrORZ3gldU=
github.com/Jguer/aur v1.3.0/go.mod h1:F8Awo+WKzTxlXtNOO4pDQjMkePLZ+oMSbu+1fKLTTLo=
github.com/Jguer/dyalpm v0.1.1 h1:38JkmJuHIGXVZedXIDGz/nhVcn8DtMk4zM+GRGipC7w=
github.com/Jguer/dyalpm v0.1.1/go.mod h1:eUPJQ/zSclJKTxOPihpspulI+S8WQNsxHJoIBiBgogw=
github.com/Jguer/votar v1.0.0 h1:drPYpV5Py5BeAQS8xezmT6uCEfLzotNjLf5yfmlHKTg=
github.com/Jguer/votar v1.0.0/go.mod h1:rc6vgVlTqNjI4nAnPbDTbdxw/N7kXkbB8BcUDjeFbYQ=
github.com/Morganamilo/go-pacmanconf v0.0.0-20210502114700-cff030e927a5 h1:TMscPjkb1ThXN32LuFY5bEYIcXZx3YlwzhS1GxNpn/c=
github.com/Morganamilo/go-pacmanconf v0.0.0-20210502114700-cff030e927a5/go.mod h1:Hk55m330jNiwxRodIlMCvw5iEyoRUCIY64W1p9D+tHc=
github.com/Morganamilo/go-srcinfo v1.0.0 h1:Wh4nEF+HJWo+29hnxM18Q2hi+DUf0GejS13+Wg+dzmI=
github.com/Morganamilo/go-srcinfo v1.0.0/go.mod h1:MP6VGY1NNpVUmYIEgoM9acix95KQqIRyqQ0hCLsyYUY=
github.com/adrg/strutil v0.3.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/ebitengine/purego v0.9.1 h1:a/k2f2HQU3Pi399RPW1MOaZyhKJL9w/xFpKAg4q1s0A=
github.com/ebitengine/purego v0.9.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
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.18 h1:gFGHyt/MLbG9n6dqnvlliiya2TaMMh6FFaR2b1H6Drc=
github.com/itchyny/gojq v0.12.18/go.mod h1:4hPoZ/3lN9fDL1D+aK7DY1f39XZpY9+1Xpjz8atrEkg=
github.com/itchyny/timefmt-go v0.1.7 h1:xyftit9Tbw+Dc/huSSPJaEmX1TVL8lw5vxjJLK4GMMA=
github.com/itchyny/timefmt-go v0.1.7/go.mod h1:5E46Q+zj7vbTgWY8o5YkMeYb4I6GeWLFnetPy5oBrAI=
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.28.0 h1:8xClBgMIRRJGDUC9xNe7NprP4kD2C3mQMeon3wY4KXA=
github.com/ohler55/ojg v1.28.0/go.mod h1:/Y5dGWkekv9ocnUixuETqiL58f+5pAsUfg5P8e7Pa2o=
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/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4=
github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0=
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.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU=
golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A=
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/ini.v1 v1.67.1 h1:tVBILHy0R6e4wkYOn3XmiITt/hEVH4TFMYvAX2Ytz6k=
gopkg.in/ini.v1 v1.67.1/go.mod h1:x/cyOwCgZqOkJoDIJ3c1KNHMo10+nLGAhh+kn3Zizss=
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=
================================================
FILE: local_install.go
================================================
// 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.TopoSortedLayers(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{})
}
================================================
FILE: local_install_test.go
================================================
//go:build !integration
// +build !integration
package main
import (
"context"
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
"slices"
"strings"
"sync"
"testing"
aur "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/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 newTestLogger() *text.Logger {
return text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), true, "test")
}
func TestIntegrationLocalInstall(t *testing.T) {
makepkgBin := t.TempDir() + "/makepkg"
pacmanBin := t.TempDir() + "/pacman"
gitBin := t.TempDir() + "/git"
tmpDir := t.TempDir()
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())
tars := []string{
tmpDir + "/jellyfin-10.8.4-1-x86_64.pkg.tar.zst",
tmpDir + "/jellyfin-web-10.8.4-1-x86_64.pkg.tar.zst",
tmpDir + "/jellyfin-server-10.8.4-1-x86_64.pkg.tar.zst",
}
wantShow := []string{
"makepkg --verifysource --skippgpcheck -f -Cc",
"pacman -S --config /etc/pacman.conf -- community/dotnet-sdk-6.0 community/dotnet-runtime-6.0",
"pacman -D -q --asdeps --config /etc/pacman.conf -- dotnet-runtime-6.0 dotnet-sdk-6.0",
"makepkg --nobuild -f -C --ignorearch",
"makepkg -c --nobuild --noextract --ignorearch",
"pacman -U --config /etc/pacman.conf -- /testdir/jellyfin-server-10.8.4-1-x86_64.pkg.tar.zst /testdir/jellyfin-web-10.8.4-1-x86_64.pkg.tar.zst",
"pacman -D -q --asexplicit --config /etc/pacman.conf -- jellyfin-server jellyfin-web",
"makepkg --nobuild -f -C --ignorearch",
"makepkg -c --nobuild --noextract --ignorearch",
"pacman -U --config /etc/pacman.conf -- /testdir/jellyfin-10.8.4-1-x86_64.pkg.tar.zst",
"pacman -D -q --asexplicit --config /etc/pacman.conf -- jellyfin",
}
wantCapture := []string{
"makepkg --packagelist",
"git -C testdata/jfin git reset --hard HEAD",
"git -C testdata/jfin git merge --no-edit --ff",
"makepkg --packagelist",
}
captureOverride := func(cmd *exec.Cmd) (stdout string, stderr string, err error) {
return strings.Join(tars, "\n"), "", nil
}
once := sync.Once{}
showOverride := func(cmd *exec.Cmd) error {
once.Do(func() {
for _, tar := range tars {
f, err := os.OpenFile(tar, os.O_RDONLY|os.O_CREATE, 0o666)
require.NoError(t, err)
require.NoError(t, f.Close())
}
})
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("B")
cmdArgs.AddArg("i")
cmdArgs.AddTarget("testdata/jfin")
settings.NoConfirm = true
defer func() { settings.NoConfirm = false }()
db := &mock.DBExecutor{
AlpmArchitecturesFn: func() ([]string, error) {
return []string{"x86_64"}, nil
},
LocalSatisfierExistsFn: func(s string) bool {
switch s {
case "dotnet-sdk>=6", "dotnet-sdk<7", "dotnet-runtime>=6", "dotnet-runtime<7", "jellyfin-server=10.8.4", "jellyfin-web=10.8.4":
return false
}
return true
},
SyncSatisfierFn: func(s string) mock.IPackage {
switch s {
case "dotnet-runtime>=6", "dotnet-runtime<7":
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", "dotnet-sdk<7":
return &mock.Package{
PName: "dotnet-sdk-6.0",
PBase: "dotnet-sdk-6.0",
PVersion: "6.0.100-1",
PDB: mock.NewDB("community"),
}
}
return nil
},
LocalPackageFn: func(s string) mock.IPackage { return nil },
InstalledRemotePackageNamesFn: func() []string { return []string{} },
}
run := &runtime.Runtime{
Cfg: &settings.Configuration{
RemoveMake: "no",
},
Logger: newTestLogger(),
CmdBuilder: cmdBuilder,
VCSStore: &vcs.Mock{},
AURClient: &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return []aur.Pkg{}, nil
},
},
}
err = handleCmd(context.Background(), run, cmdArgs, db)
require.NoError(t, err)
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, tmpDir, "/testdir") // replace the temp dir with a static path
show = strings.ReplaceAll(show, makepkgBin, "makepkg")
show = strings.ReplaceAll(show, pacmanBin, "pacman")
show = strings.ReplaceAll(show, gitBin, "git")
// 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))
}
}
func TestIntegrationLocalBuildOnly(t *testing.T) {
makepkgBin := t.TempDir() + "/makepkg"
pacmanBin := t.TempDir() + "/pacman"
gitBin := t.TempDir() + "/git"
tmpDir := t.TempDir()
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())
tars := []string{
tmpDir + "/jellyfin-10.8.4-1-x86_64.pkg.tar.zst",
tmpDir + "/jellyfin-web-10.8.4-1-x86_64.pkg.tar.zst",
tmpDir + "/jellyfin-server-10.8.4-1-x86_64.pkg.tar.zst",
}
wantShow := []string{
"makepkg --verifysource --skippgpcheck -f -Cc",
"pacman -S --config /etc/pacman.conf -- community/dotnet-sdk-6.0 community/dotnet-runtime-6.0",
"pacman -D -q --asdeps --config /etc/pacman.conf -- dotnet-runtime-6.0 dotnet-sdk-6.0",
"makepkg --nobuild -f -C --ignorearch",
"makepkg -c --nobuild --noextract --ignorearch",
"makepkg --nobuild -f -C --ignorearch",
"makepkg -c --nobuild --noextract --ignorearch",
}
wantCapture := []string{
"makepkg --packagelist",
"git -C testdata/jfin git reset --hard HEAD",
"git -C testdata/jfin git merge --no-edit --ff",
"makepkg --packagelist",
}
captureOverride := func(cmd *exec.Cmd) (stdout string, stderr string, err error) {
return strings.Join(tars, "\n"), "", nil
}
once := sync.Once{}
showOverride := func(cmd *exec.Cmd) error {
once.Do(func() {
for _, tar := range tars {
f, err := os.OpenFile(tar, os.O_RDONLY|os.O_CREATE, 0o666)
require.NoError(t, err)
require.NoError(t, f.Close())
}
})
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("B")
cmdArgs.AddTarget("testdata/jfin")
settings.NoConfirm = true
defer func() { settings.NoConfirm = false }()
db := &mock.DBExecutor{
AlpmArchitecturesFn: func() ([]string, error) {
return []string{"x86_64"}, nil
},
LocalSatisfierExistsFn: func(s string) bool {
switch s {
case "dotnet-sdk>=6", "dotnet-sdk<7", "dotnet-runtime>=6", "dotnet-runtime<7", "jellyfin-server=10.8.4", "jellyfin-web=10.8.4":
return false
}
return true
},
SyncSatisfierFn: func(s string) mock.IPackage {
switch s {
case "dotnet-runtime>=6", "dotnet-runtime<7":
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", "dotnet-sdk<7":
return &mock.Package{
PName: "dotnet-sdk-6.0",
PBase: "dotnet-sdk-6.0",
PVersion: "6.0.100-1",
PDB: mock.NewDB("community"),
}
}
return nil
},
LocalPackageFn: func(s string) mock.IPackage { return nil },
InstalledRemotePackageNamesFn: func() []string { return []string{} },
}
run := &runtime.Runtime{
Cfg: &settings.Configuration{
RemoveMake: "no",
},
Logger: newTestLogger(),
CmdBuilder: cmdBuilder,
VCSStore: &vcs.Mock{},
AURClient: &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return []aur.Pkg{}, nil
},
},
}
err = handleCmd(context.Background(), run, cmdArgs, db)
require.NoError(t, err)
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, tmpDir, "/testdir")
show = strings.ReplaceAll(show, makepkgBin, "makepkg")
show = strings.ReplaceAll(show, pacmanBin, "pacman")
show = strings.ReplaceAll(show, gitBin, "git")
assert.Subset(t, strings.Split(show, " "), strings.Split(wantShow[i], " "), fmt.Sprintf("%d - %s", i, show))
}
}
func TestIntegrationLocalInstallMissingDep(t *testing.T) {
wantErr := ErrPackagesNotFound
makepkgBin := t.TempDir() + "/makepkg"
pacmanBin := t.TempDir() + "/pacman"
gitBin := t.TempDir() + "/git"
tmpDir := t.TempDir()
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())
tars := []string{
tmpDir + "/jellyfin-10.8.4-1-x86_64.pkg.tar.zst",
tmpDir + "/jellyfin-web-10.8.4-1-x86_64.pkg.tar.zst",
tmpDir + "/jellyfin-server-10.8.4-1-x86_64.pkg.tar.zst",
}
wantShow := []string{}
wantCapture := []string{}
captureOverride := func(cmd *exec.Cmd) (stdout string, stderr string, err error) {
return strings.Join(tars, "\n"), "", nil
}
once := sync.Once{}
showOverride := func(cmd *exec.Cmd) error {
once.Do(func() {
for _, tar := range tars {
f, err := os.OpenFile(tar, os.O_RDONLY|os.O_CREATE, 0o666)
require.NoError(t, err)
require.NoError(t, f.Close())
}
})
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("B")
cmdArgs.AddArg("i")
cmdArgs.AddTarget("testdata/jfin")
settings.NoConfirm = true
defer func() { settings.NoConfirm = false }()
db := &mock.DBExecutor{
AlpmArchitecturesFn: func() ([]string, error) {
return []string{"x86_64"}, nil
},
LocalSatisfierExistsFn: func(s string) bool {
switch s {
case "dotnet-sdk>=6", "dotnet-sdk<7", "dotnet-runtime>=6", "dotnet-runtime<7", "jellyfin-server=10.8.4", "jellyfin-web=10.8.4":
return false
}
return true
},
SyncSatisfierFn: func(s string) mock.IPackage {
switch s {
case "dotnet-runtime>=6", "dotnet-runtime<7":
return &mock.Package{
PName: "dotnet-runtime-6.0",
PBase: "dotnet-runtime-6.0",
PVersion: "6.0.100-1",
PDB: mock.NewDB("community"),
}
}
return nil
},
LocalPackageFn: func(string) mock.IPackage { return nil },
}
run := &runtime.Runtime{
Cfg: &settings.Configuration{},
Logger: newTestLogger(),
CmdBuilder: cmdBuilder,
VCSStore: &vcs.Mock{},
AURClient: &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return []aur.Pkg{}, nil
},
},
}
err = handleCmd(context.Background(), run, cmdArgs, db)
require.ErrorContains(t, err, wantErr.Error())
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, tmpDir, "/testdir") // replace the temp dir with a static path
show = strings.ReplaceAll(show, makepkgBin, "makepkg")
show = strings.ReplaceAll(show, pacmanBin, "pacman")
show = strings.ReplaceAll(show, gitBin, "git")
// 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))
}
}
func TestIntegrationLocalInstallNeeded(t *testing.T) {
makepkgBin := t.TempDir() + "/makepkg"
pacmanBin := t.TempDir() + "/pacman"
gitBin := t.TempDir() + "/git"
tmpDir := t.TempDir()
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())
tars := []string{
tmpDir + "/jellyfin-10.8.4-1-x86_64.pkg.tar.zst",
tmpDir + "/jellyfin-web-10.8.4-1-x86_64.pkg.tar.zst",
tmpDir + "/jellyfin-server-10.8.4-1-x86_64.pkg.tar.zst",
}
wantShow := []string{
"makepkg --verifysource --skippgpcheck -f -Cc",
"pacman -S --config /etc/pacman.conf -- community/dotnet-sdk-6.0 community/dotnet-runtime-6.0",
"pacman -D -q --asdeps --config /etc/pacman.conf -- dotnet-runtime-6.0 dotnet-sdk-6.0",
"makepkg --nobuild -f -C --ignorearch",
"makepkg -c --nobuild --noextract --ignorearch",
"makepkg --nobuild -f -C --ignorearch",
"makepkg -c --nobuild --noextract --ignorearch",
}
wantCapture := []string{
"makepkg --packagelist",
"git -C testdata/jfin git reset --hard HEAD",
"git -C testdata/jfin git merge --no-edit --ff",
"makepkg --packagelist",
}
captureOverride := func(cmd *exec.Cmd) (stdout string, stderr string, err error) {
return strings.Join(tars, "\n"), "", nil
}
once := sync.Once{}
showOverride := func(cmd *exec.Cmd) error {
once.Do(func() {
for _, tar := range tars {
f, err := os.OpenFile(tar, os.O_RDONLY|os.O_CREATE, 0o666)
require.NoError(t, err)
require.NoError(t, f.Close())
}
})
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("B")
cmdArgs.AddArg("i")
cmdArgs.AddArg("needed")
cmdArgs.AddTarget("testdata/jfin")
settings.NoConfirm = true
defer func() { settings.NoConfirm = false }()
db := &mock.DBExecutor{
AlpmArchitecturesFn: func() ([]string, error) {
return []string{"x86_64"}, nil
},
IsCorrectVersionInstalledFn: func(s1, s2 string) bool {
return true
},
LocalPackageFn: func(s string) mock.IPackage {
if s == "jellyfin-server" {
return &mock.Package{
PName: "jellyfin-server",
PBase: "jellyfin-server",
PVersion: "10.8.4-1",
PDB: mock.NewDB("community"),
}
}
return nil
},
LocalSatisfierExistsFn: func(s string) bool {
switch s {
case "dotnet-sdk>=6", "dotnet-sdk<7", "dotnet-runtime>=6", "dotnet-runtime<7", "jellyfin-server=10.8.4", "jellyfin-web=10.8.4":
return false
}
return true
},
SyncSatisfierFn: func(s string) mock.IPackage {
switch s {
case "dotnet-runtime>=6", "dotnet-runtime<7":
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", "dotnet-sdk<7":
return &mock.Package{
PName: "dotnet-sdk-6.0",
PBase: "dotnet-sdk-6.0",
PVersion: "6.0.100-1",
PDB: mock.NewDB("community"),
}
}
return nil
},
InstalledRemotePackageNamesFn: func() []string { return []string{} },
}
run := &runtime.Runtime{
Cfg: &settings.Configuration{
RemoveMake: "no",
},
Logger: newTestLogger(),
CmdBuilder: cmdBuilder,
VCSStore: &vcs.Mock{},
AURClient: &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return []aur.Pkg{}, nil
},
},
}
err = handleCmd(context.Background(), run, cmdArgs, db)
require.NoError(t, err)
require.Len(t, mockRunner.ShowCalls, len(wantShow), "show calls: %v", mockRunner.ShowCalls)
require.Len(t, mockRunner.CaptureCalls, len(wantCapture))
for i, call := range mockRunner.ShowCalls {
show := call.Args[0].(*exec.Cmd).String()
show = strings.ReplaceAll(show, tmpDir, "/testdir") // replace the temp dir with a static path
show = strings.ReplaceAll(show, makepkgBin, "makepkg")
show = strings.ReplaceAll(show, pacmanBin, "pacman")
show = strings.ReplaceAll(show, gitBin, "git")
// 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))
}
}
func TestIntegrationLocalInstallGenerateSRCINFO(t *testing.T) {
makepkgBin := t.TempDir() + "/makepkg"
pacmanBin := t.TempDir() + "/pacman"
gitBin := t.TempDir() + "/git"
tmpDir := t.TempDir()
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())
srcinfo, err := os.ReadFile("testdata/jfin/.SRCINFO")
require.NoError(t, err)
assert.True(t, strings.HasPrefix(string(srcinfo), "pkgbase = jellyfin"), string(srcinfo))
targetDir := t.TempDir()
f, err = os.OpenFile(filepath.Join(targetDir, "PKGBUILD"), os.O_RDONLY|os.O_CREATE, 0o755)
require.NoError(t, err)
require.NoError(t, f.Close())
tars := []string{
tmpDir + "/jellyfin-10.8.4-1-x86_64.pkg.tar.zst",
tmpDir + "/jellyfin-web-10.8.4-1-x86_64.pkg.tar.zst",
tmpDir + "/jellyfin-server-10.8.4-1-x86_64.pkg.tar.zst",
}
wantShow := []string{
"makepkg --verifysource --skippgpcheck -f -Cc",
"pacman -S --config /etc/pacman.conf -- community/dotnet-sdk-6.0 community/dotnet-runtime-6.0",
"pacman -D -q --asdeps --config /etc/pacman.conf -- dotnet-runtime-6.0 dotnet-sdk-6.0",
"makepkg --nobuild -f -C --ignorearch",
"makepkg -c --nobuild --noextract --ignorearch",
"pacman -U --config /etc/pacman.conf -- /testdir/jellyfin-server-10.8.4-1-x86_64.pkg.tar.zst /testdir/jellyfin-web-10.8.4-1-x86_64.pkg.tar.zst",
"pacman -D -q --asexplicit --config /etc/pacman.conf -- jellyfin-server jellyfin-web",
"makepkg --nobuild -f -C --ignorearch",
"makepkg -c --nobuild --noextract --ignorearch",
"pacman -U --config /etc/pacman.conf -- /testdir/jellyfin-10.8.4-1-x86_64.pkg.tar.zst",
"pacman -D -q --asexplicit --config /etc/pacman.conf -- jellyfin",
}
wantCapture := []string{
"makepkg --printsrcinfo",
"makepkg --packagelist",
"git -C testdata/jfin git reset --hard HEAD",
"git -C testdata/jfin git merge --no-edit --ff",
"makepkg --packagelist",
}
captureOverride := func(cmd *exec.Cmd) (stdout string, stderr string, err error) {
if slices.Contains(cmd.Args, "--printsrcinfo") {
return string(srcinfo), "", nil
}
return strings.Join(tars, "\n"), "", nil
}
once := sync.Once{}
showOverride := func(cmd *exec.Cmd) error {
once.Do(func() {
for _, tar := range tars {
f, err := os.OpenFile(tar, os.O_RDONLY|os.O_CREATE, 0o666)
require.NoError(t, err)
require.NoError(t, f.Close())
}
})
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("B")
cmdArgs.AddArg("i")
cmdArgs.AddTarget(targetDir)
settings.NoConfirm = true
defer func() { settings.NoConfirm = false }()
db := &mock.DBExecutor{
AlpmArchitecturesFn: func() ([]string, error) {
return []string{"x86_64"}, nil
},
LocalSatisfierExistsFn: func(s string) bool {
switch s {
case "dotnet-sdk>=6", "dotnet-sdk<7", "dotnet-runtime>=6", "dotnet-runtime<7", "jellyfin-server=10.8.4", "jellyfin-web=10.8.4":
return false
}
return true
},
LocalPackageFn: func(string) mock.IPackage { return nil },
SyncSatisfierFn: func(s string) mock.IPackage {
switch s {
case "dotnet-runtime>=6", "dotnet-runtime<7":
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", "dotnet-sdk<7":
return &mock.Package{
PName: "dotnet-sdk-6.0",
PBase: "dotnet-sdk-6.0",
PVersion: "6.0.100-1",
PDB: mock.NewDB("community"),
}
}
return nil
},
InstalledRemotePackageNamesFn: func() []string { return []string{} },
}
run := &runtime.Runtime{
Cfg: &settings.Configuration{
RemoveMake: "no",
Debug: false,
},
Logger: newTestLogger(),
CmdBuilder: cmdBuilder,
VCSStore: &vcs.Mock{},
AURClient: &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return []aur.Pkg{}, nil
},
},
}
err = handleCmd(context.Background(), run, cmdArgs, db)
require.NoError(t, err)
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, tmpDir, "/testdir") // replace the temp dir with a static path
show = strings.ReplaceAll(show, makepkgBin, "makepkg")
show = strings.ReplaceAll(show, pacmanBin, "pacman")
show = strings.ReplaceAll(show, gitBin, "git")
// 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))
}
}
func TestIntegrationLocalInstallMissingFiles(t *testing.T) {
makepkgBin := t.TempDir() + "/makepkg"
pacmanBin := t.TempDir() + "/pacman"
gitBin := t.TempDir() + "/git"
tmpDir := t.TempDir()
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())
srcinfo, err := os.ReadFile("testdata/jfin/.SRCINFO")
require.NoError(t, err)
targetDir := t.TempDir()
tars := []string{
tmpDir + "/jellyfin-10.8.4-1-x86_64.pkg.tar.zst",
tmpDir + "/jellyfin-web-10.8.4-1-x86_64.pkg.tar.zst",
tmpDir + "/jellyfin-server-10.8.4-1-x86_64.pkg.tar.zst",
}
wantShow := []string{}
wantCapture := []string{}
captureOverride := func(cmd *exec.Cmd) (stdout string, stderr string, err error) {
if cmd.Args[1] == "--printsrcinfo" {
return string(srcinfo), "", nil
}
return strings.Join(tars, "\n"), "", nil
}
once := sync.Once{}
showOverride := func(cmd *exec.Cmd) error {
once.Do(func() {
for _, tar := range tars {
f, err := os.OpenFile(tar, os.O_RDONLY|os.O_CREATE, 0o666)
require.NoError(t, err)
require.NoError(t, f.Close())
}
})
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("B")
cmdArgs.AddArg("i")
cmdArgs.AddTarget(targetDir)
settings.NoConfirm = true
defer func() { settings.NoConfirm = false }()
db := &mock.DBExecutor{
AlpmArchitecturesFn: func() ([]string, error) {
return []string{"x86_64"}, nil
},
LocalSatisfierExistsFn: func(s string) bool {
switch s {
case "dotnet-sdk>=6", "dotnet-sdk<7", "dotnet-runtime>=6", "dotnet-runtime<7", "jellyfin-server=10.8.4", "jellyfin-web=10.8.4":
return false
}
return true
},
SyncSatisfierFn: func(s string) mock.IPackage {
switch s {
case "dotnet-runtime>=6", "dotnet-runtime<7":
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", "dotnet-sdk<7":
return &mock.Package{
PName: "dotnet-sdk-6.0",
PBase: "dotnet-sdk-6.0",
PVersion: "6.0.100-1",
PDB: mock.NewDB("community"),
}
}
return nil
},
}
config := &runtime.Runtime{
Cfg: &settings.Configuration{
RemoveMake: "no",
Debug: false,
},
Logger: newTestLogger(),
CmdBuilder: cmdBuilder,
VCSStore: &vcs.Mock{},
AURClient: &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return []aur.Pkg{}, nil
},
},
}
err = handleCmd(context.Background(), config, cmdArgs, db)
require.ErrorIs(t, err, ErrNoBuildFiles)
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, tmpDir, "/testdir") // replace the temp dir with a static path
show = strings.ReplaceAll(show, makepkgBin, "makepkg")
show = strings.ReplaceAll(show, pacmanBin, "pacman")
show = strings.ReplaceAll(show, gitBin, "git")
// 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))
}
}
func TestIntegrationLocalInstallWithDepsProvides(t *testing.T) {
makepkgBin := t.TempDir() + "/makepkg"
pacmanBin := t.TempDir() + "/pacman"
gitBin := t.TempDir() + "/git"
tmpDir := t.TempDir()
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())
tars := []string{
tmpDir + "/ceph-bin-17.2.6-2-x86_64.pkg.tar.zst",
tmpDir + "/ceph-libs-bin-17.2.6-2-x86_64.pkg.tar.zst",
}
wantShow := []string{
"makepkg --verifysource --skippgpcheck -f -Cc",
"makepkg --nobuild -f -C --ignorearch",
"makepkg -c --nobuild --noextract --ignorearch",
"pacman -U --config /etc/pacman.conf -- /testdir/ceph-libs-bin-17.2.6-2-x86_64.pkg.tar.zst",
"pacman -D -q --asexplicit --config /etc/pacman.conf -- ceph-libs-bin",
"makepkg --nobuild -f -C --ignorearch",
"makepkg -c --nobuild --noextract --ignorearch",
"pacman -U --config /etc/pacman.conf -- /testdir/ceph-bin-17.2.6-2-x86_64.pkg.tar.zst",
"pacman -D -q --asexplicit --config /etc/pacman.conf -- ceph-bin",
}
wantCapture := []string{
"git -C testdata/cephbin git reset --hard HEAD",
"git -C testdata/cephbin git merge --no-edit --ff",
"makepkg --packagelist",
"makepkg --packagelist",
}
captureOverride := func(cmd *exec.Cmd) (stdout string, stderr string, err error) {
return strings.Join(tars, "\n"), "", nil
}
once := sync.Once{}
showOverride := func(cmd *exec.Cmd) error {
once.Do(func() {
for _, tar := range tars {
f, err := os.OpenFile(tar, os.O_RDONLY|os.O_CREATE, 0o666)
require.NoError(t, err)
require.NoError(t, f.Close())
}
})
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("B")
cmdArgs.AddArg("i")
cmdArgs.AddTarget("testdata/cephbin")
settings.NoConfirm = true
defer func() { settings.NoConfirm = false }()
db := &mock.DBExecutor{
AlpmArchitecturesFn: func() ([]string, error) {
return []string{"x86_64"}, nil
},
LocalSatisfierExistsFn: func(s string) bool {
switch s {
case "ceph=17.2.6-2", "ceph-libs=17.2.6-2":
return false
}
return true
},
SyncSatisfierFn: func(s string) mock.IPackage {
return nil
},
LocalPackageFn: func(s string) mock.IPackage { return nil },
InstalledRemotePackageNamesFn: func() []string { return []string{} },
}
config := &runtime.Runtime{
Cfg: &settings.Configuration{
RemoveMake: "no",
},
Logger: newTestLogger(),
CmdBuilder: cmdBuilder,
VCSStore: &vcs.Mock{},
AURClient: &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return []aur.Pkg{}, nil
},
},
}
err = handleCmd(context.Background(), config, cmdArgs, db)
require.NoError(t, err)
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, tmpDir, "/testdir") // replace the temp dir with a static path
show = strings.ReplaceAll(show, makepkgBin, "makepkg")
show = strings.ReplaceAll(show, pacmanBin, "pacman")
show = strings.ReplaceAll(show, gitBin, "git")
// 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))
}
}
func TestIntegrationLocalInstallTwoSrcInfosWithDeps(t *testing.T) {
makepkgBin := t.TempDir() + "/makepkg"
pacmanBin := t.TempDir() + "/pacman"
gitBin := t.TempDir() + "/git"
tmpDir1 := t.TempDir()
tmpDir2 := t.TempDir()
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())
pkgsTars := []string{
tmpDir1 + "/libzip-git-1.9.2.r166.gd2c47d0f-1-x86_64.pkg.tar.zst",
tmpDir2 + "/gourou-0.8.1-4-x86_64.pkg.tar.zst",
}
wantShow := []string{
"makepkg --verifysource --skippgpcheck -f -Cc",
"makepkg --verifysource --skippgpcheck -f -Cc",
"makepkg --nobuild -f -C --ignorearch",
"makepkg -c --nobuild --noextract --ignorearch",
"pacman -U --config /etc/pacman.conf -- /testdir1/libzip-git-1.9.2.r166.gd2c47d0f-1-x86_64.pkg.tar.zst",
"pacman -D -q --asexplicit --config /etc/pacman.conf -- libzip-git",
"makepkg --nobuild -f -C --ignorearch",
"makepkg -c --nobuild --noextract --ignorearch",
"pacman -U --config /etc/pacman.conf -- /testdir2/gourou-0.8.1-4-x86_64.pkg.tar.zst",
"pacman -D -q --asexplicit --config /etc/pacman.conf -- gourou",
}
wantCapture := []string{
"git -C testdata/gourou git reset --hard HEAD",
"git -C testdata/gourou git merge --no-edit --ff",
"git -C testdata/libzip-git git reset --hard HEAD",
"git -C testdata/libzip-git git merge --no-edit --ff",
"makepkg --packagelist",
"makepkg --packagelist",
}
captureCounter := 0
captureOverride := func(cmd *exec.Cmd) (stdout string, stderr string, err error) {
captureCounter++
switch captureCounter {
case 5:
return pkgsTars[0] + "\n", "", nil
case 6:
return pkgsTars[1] + "\n", "", nil
default:
return "", "", nil
}
}
once := sync.Once{}
showOverride := func(cmd *exec.Cmd) error {
once.Do(func() {
for _, tar := range pkgsTars {
f, err := os.OpenFile(tar, os.O_RDONLY|os.O_CREATE, 0o666)
require.NoError(t, err)
require.NoError(t, f.Close())
}
})
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("B")
cmdArgs.AddArg("i")
cmdArgs.AddTarget("testdata/gourou")
cmdArgs.AddTarget("testdata/libzip-git")
settings.NoConfirm = true
defer func() { settings.NoConfirm = false }()
db := &mock.DBExecutor{
AlpmArchitecturesFn: func() ([]string, error) {
return []string{"x86_64"}, nil
},
LocalSatisfierExistsFn: func(s string) bool {
switch s {
case "gourou", "libzip", "libzip-git":
return false
}
return true
},
SyncSatisfierFn: func(s string) mock.IPackage {
return nil
},
LocalPackageFn: func(s string) mock.IPackage { return nil },
InstalledRemotePackageNamesFn: func() []string { return []string{} },
}
run := &runtime.Runtime{
Cfg: &settings.Configuration{
RemoveMake: "no",
},
Logger: newTestLogger(),
CmdBuilder: cmdBuilder,
VCSStore: &vcs.Mock{},
AURClient: &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return []aur.Pkg{}, nil
},
},
}
err = handleCmd(context.Background(), run, cmdArgs, db)
require.NoError(t, err)
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, tmpDir1, "/testdir1") // replace the temp dir with a static path
show = strings.ReplaceAll(show, tmpDir2, "/testdir2") // replace the temp dir with a static path
show = strings.ReplaceAll(show, makepkgBin, "makepkg")
show = strings.ReplaceAll(show, pacmanBin, "pacman")
show = strings.ReplaceAll(show, gitBin, "git")
// 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))
}
}
================================================
FILE: main.go
================================================
package main // import "github.com/Jguer/yay"
import (
"context"
"errors"
"os"
"os/exec"
"runtime/debug"
"strings"
"github.com/leonelquinteros/gotext"
"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"
)
var (
yayVersion = "13.0.0" // 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 main() {
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
}
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 {
if str := err.Error(); str != "" {
fallbackLog.Errorln(str)
}
ret = 1
return
}
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)
}
}
// 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
}
}
================================================
FILE: pkg/cmd/graph/main.go
================================================
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.TopoSortedLayers(nil))
return nil
}
================================================
FILE: pkg/completion/completion.go
================================================
package completion
import (
"context"
"io"
"os"
"path/filepath"
"strings"
"time"
"github.com/Jguer/yay/v12/pkg/db"
"github.com/Jguer/yay/v12/pkg/download"
"github.com/Jguer/yay/v12/pkg/text"
)
// NeedsUpdate checks if the completion cache needs to be regenerated.
// Returns true if the file doesn't exist, is older than interval days, is empty, or force is true.
func NeedsUpdate(completionPath string, interval int, force bool) bool {
if force {
return true
}
info, err := os.Stat(completionPath)
if os.IsNotExist(err) {
return true
}
// If the file is empty or invalid, regenerate it
if info.Size() == 0 {
return true
}
if interval != -1 && time.Since(info.ModTime()).Hours() >= float64(interval*24) {
return true
}
return false
}
type PkgSynchronizer interface {
SyncPackages(...string) []db.IPackage
}
// Show provides completion info for shells.
func Show(ctx context.Context, httpClient download.HTTPRequestDoer,
dbExecutor PkgSynchronizer, aurURL, completionPath string, interval int, force bool, logger *text.Logger,
) error {
if NeedsUpdate(completionPath, interval, force) {
if err := UpdateCache(ctx, httpClient, dbExecutor, aurURL, completionPath, logger); err != nil {
return err
}
}
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
}
// UpdateCache regenerates the completion cache file unconditionally.
func UpdateCache(ctx context.Context, httpClient download.HTTPRequestDoer,
dbExecutor PkgSynchronizer, aurURL, completionPath string, logger *text.Logger,
) error {
if err := os.MkdirAll(filepath.Dir(completionPath), 0o755); err != nil {
return err
}
out, err := os.Create(completionPath)
if err != nil {
return err
}
defer out.Close()
if err := createAURList(ctx, httpClient, aurURL, out, logger); err != nil {
os.Remove(completionPath)
return err
}
return createRepoList(dbExecutor, out)
}
// createAURList creates a new completion file.
func createAURList(ctx context.Context, client download.HTTPRequestDoer, aurURL string, out io.Writer, logger *text.Logger) error {
scanner, err := download.GetPackageScanner(ctx, client, aurURL, logger)
if err != nil {
return err
}
defer scanner.Close()
scanner.Scan()
for scanner.Scan() {
pkgName := scanner.Text()
if strings.HasPrefix(pkgName, "#") {
continue
}
if _, err := io.WriteString(out, pkgName+"\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
}
}
return nil
}
================================================
FILE: pkg/completion/completion_test.go
================================================
//go:build !integration
// +build !integration
package completion
import (
"bytes"
"compress/gzip"
"context"
"errors"
"io"
"net/http"
"os"
"path/filepath"
"testing"
"time"
"github.com/Jguer/yay/v12/pkg/db"
"github.com/Jguer/yay/v12/pkg/db/mock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
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 []byte
returnStatusCode int
returnErr error
wantURL string
}
func (m *mockDoer) Get(url string) (*http.Response, error) {
assert.Equal(m.t, m.wantURL, url)
return &http.Response{
StatusCode: m.returnStatusCode,
Body: io.NopCloser(bytes.NewReader(m.returnBody)),
}, m.returnErr
}
func gzipString(s string) []byte {
var buf bytes.Buffer
gz := gzip.NewWriter(&buf)
gz.Write([]byte(s))
gz.Close()
return buf.Bytes()
}
func Test_createAURList(t *testing.T) {
t.Parallel()
doer := &mockDoer{
t: t,
wantURL: "https://aur.archlinux.org/packages.gz",
returnStatusCode: 200,
returnBody: []byte(samplePackageResp),
returnErr: nil,
}
out := &bytes.Buffer{}
err := createAURList(context.Background(), doer, "https://aur.archlinux.org", out, nil)
assert.NoError(t, err)
gotOut := out.String()
assert.Equal(t, expectPackageCompletion, gotOut)
}
func Test_createAURListGzip(t *testing.T) {
t.Parallel()
doer := &mockDoer{
t: t,
wantURL: "https://aur.archlinux.org/packages.gz",
returnStatusCode: 200,
returnBody: gzipString(samplePackageResp),
returnErr: nil,
}
out := &bytes.Buffer{}
err := createAURList(context.Background(), doer, "https://aur.archlinux.org", out, nil)
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: []byte(samplePackageResp),
returnErr: errors.New("Not available"),
}
out := &bytes.Buffer{}
err := createAURList(context.Background(), doer, "https://aur.archlinux.org", out, nil)
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: []byte(samplePackageResp),
returnErr: nil,
}
out := &bytes.Buffer{}
err := createAURList(context.Background(), doer, "https://aur.archlinux.org", out, nil)
assert.EqualError(t, err, "invalid status code: 503")
}
func TestNeedsUpdate(t *testing.T) {
t.Parallel()
tests := []struct {
name string
setupFile func(t *testing.T, path string)
interval int
force bool
expectedResult bool
}{
{
name: "force returns true",
setupFile: nil,
interval: 7,
force: true,
expectedResult: true,
},
{
name: "file does not exist returns true",
setupFile: nil,
interval: 7,
force: false,
expectedResult: true,
},
{
name: "fresh file returns false",
setupFile: func(t *testing.T, path string) {
t.Helper()
err := os.WriteFile(path, []byte("test"), 0o600)
require.NoError(t, err)
},
interval: 7,
force: false,
expectedResult: false,
},
{
name: "interval -1 never updates",
setupFile: func(t *testing.T, path string) {
t.Helper()
err := os.WriteFile(path, []byte("test"), 0o600)
require.NoError(t, err)
// Set file time to 30 days ago
oldTime := time.Now().Add(-30 * 24 * time.Hour)
err = os.Chtimes(path, oldTime, oldTime)
require.NoError(t, err)
},
interval: -1,
force: false,
expectedResult: false,
},
{
name: "old file returns true",
setupFile: func(t *testing.T, path string) {
t.Helper()
err := os.WriteFile(path, []byte("test"), 0o600)
require.NoError(t, err)
// Set file time to 10 days ago
oldTime := time.Now().Add(-10 * 24 * time.Hour)
err = os.Chtimes(path, oldTime, oldTime)
require.NoError(t, err)
},
interval: 7,
force: false,
expectedResult: true,
},
{
name: "file within interval returns false",
setupFile: func(t *testing.T, path string) {
t.Helper()
err := os.WriteFile(path, []byte("test"), 0o600)
require.NoError(t, err)
// Set file time to 3 days ago
oldTime := time.Now().Add(-3 * 24 * time.Hour)
err = os.Chtimes(path, oldTime, oldTime)
require.NoError(t, err)
},
interval: 7,
force: false,
expectedResult: false,
},
{
name: "empty file returns true",
setupFile: func(t *testing.T, path string) {
t.Helper()
// Create an empty file
err := os.WriteFile(path, []byte(""), 0o600)
require.NoError(t, err)
},
interval: 7,
force: false,
expectedResult: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
tmpDir := t.TempDir()
completionPath := filepath.Join(tmpDir, "completion")
if tt.setupFile != nil {
tt.setupFile(t, completionPath)
}
result := NeedsUpdate(completionPath, tt.interval, tt.force)
assert.Equal(t, tt.expectedResult, result)
})
}
}
// mockPkgSynchronizer implements PkgSynchronizer for testing.
type mockPkgSynchronizer struct {
packages []db.IPackage
}
func (m *mockPkgSynchronizer) SyncPackages(...string) []db.IPackage {
return m.packages
}
func Test_createRepoList(t *testing.T) {
t.Parallel()
tests := []struct {
name string
packages []db.IPackage
expectedOutput string
expectedError error
}{
{
name: "empty package list",
packages: []db.IPackage{},
expectedOutput: "",
expectedError: nil,
},
{
name: "single package",
packages: []db.IPackage{
&mock.Package{PName: "vim", PDB: mock.NewDB("extra")},
},
expectedOutput: "vim\textra\n",
expectedError: nil,
},
{
name: "multiple packages",
packages: []db.IPackage{
&mock.Package{PName: "vim", PDB: mock.NewDB("extra")},
&mock.Package{PName: "git", PDB: mock.NewDB("extra")},
&mock.Package{PName: "linux", PDB: mock.NewDB("core")},
},
expectedOutput: "vim\textra\ngit\textra\nlinux\tcore\n",
expectedError: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
dbExecutor := &mockPkgSynchronizer{packages: tt.packages}
out := &bytes.Buffer{}
err := createRepoList(dbExecutor, out)
if tt.expectedError != nil {
assert.EqualError(t, err, tt.expectedError.Error())
} else {
assert.NoError(t, err)
}
assert.Equal(t, tt.expectedOutput, out.String())
})
}
}
// errorWriter is a writer that always returns an error.
type errorWriter struct{}
func (e *errorWriter) Write(p []byte) (n int, err error) {
return 0, errors.New("write error")
}
func Test_createRepoListWriteError(t *testing.T) {
t.Parallel()
dbExecutor := &mockPkgSynchronizer{
packages: []db.IPackage{
&mock.Package{PName: "vim", PDB: mock.NewDB("extra")},
},
}
err := createRepoList(dbExecutor, &errorWriter{})
assert.EqualError(t, err, "write error")
}
func TestUpdateCache(t *testing.T) {
t.Parallel()
tests := []struct {
name string
doer *mockDoer
packages []db.IPackage
expectedOutput string
expectError bool
}{
{
name: "successful update",
doer: &mockDoer{
returnStatusCode: 200,
returnBody: []byte("# Comment\npkg1\npkg2\n"),
returnErr: nil,
},
packages: []db.IPackage{
&mock.Package{PName: "vim", PDB: mock.NewDB("extra")},
},
expectedOutput: "pkg1\tAUR\npkg2\tAUR\nvim\textra\n",
expectError: false,
},
{
name: "AUR fetch error removes file",
doer: &mockDoer{
returnStatusCode: 500,
returnBody: []byte{},
returnErr: nil,
},
packages: []db.IPackage{},
expectError: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
tmpDir := t.TempDir()
completionPath := filepath.Join(tmpDir, "subdir", "completion")
tt.doer.t = t
tt.doer.wantURL = "https://aur.archlinux.org/packages.gz"
dbExecutor := &mockPkgSynchronizer{packages: tt.packages}
err := UpdateCache(context.Background(), tt.doer, dbExecutor, "https://aur.archlinux.org", completionPath, nil)
if tt.expectError {
assert.Error(t, err)
// File should be removed on error
_, statErr := os.Stat(completionPath)
assert.True(t, os.IsNotExist(statErr))
} else {
require.NoError(t, err)
content, readErr := os.ReadFile(completionPath)
require.NoError(t, readErr)
assert.Equal(t, tt.expectedOutput, string(content))
}
})
}
}
func TestShow(t *testing.T) {
// Note: Not running in parallel because we need to capture os.Stdout
tests := []struct {
name string
setupFile func(t *testing.T, path string)
doer *mockDoer
packages []db.IPackage
interval int
force bool
expectError bool
}{
{
name: "existing fresh file",
setupFile: func(t *testing.T, path string) {
t.Helper()
err := os.WriteFile(path, []byte("cached\tdata\n"), 0o600)
require.NoError(t, err)
},
doer: nil, // Should not be called
packages: nil,
interval: 7,
force: false,
expectError: false,
},
{
name: "file needs update",
setupFile: nil,
doer: &mockDoer{
returnStatusCode: 200,
returnBody: []byte("# Comment\naur-pkg\n"),
returnErr: nil,
},
packages: []db.IPackage{
&mock.Package{PName: "repo-pkg", PDB: mock.NewDB("core")},
},
interval: 7,
force: false,
expectError: false,
},
{
name: "force update",
setupFile: nil,
doer: &mockDoer{
returnStatusCode: 200,
returnBody: []byte("# Comment\nforced-pkg\n"),
returnErr: nil,
},
packages: []db.IPackage{},
interval: 7,
force: true,
expectError: false,
},
{
name: "update cache error",
setupFile: nil,
doer: &mockDoer{
returnStatusCode: 500,
returnBody: []byte{},
returnErr: nil,
},
packages: []db.IPackage{},
interval: 7,
force: false,
expectError: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Not running in parallel because we capture os.Stdout
tmpDir := t.TempDir()
completionPath := filepath.Join(tmpDir, "completion")
if tt.setupFile != nil {
tt.setupFile(t, completionPath)
}
if tt.doer != nil {
tt.doer.t = t
tt.doer.wantURL = "https://aur.archlinux.org/packages.gz"
}
dbExecutor := &mockPkgSynchronizer{packages: tt.packages}
// Capture stdout using a pipe
oldStdout := os.Stdout
r, w, pipeErr := os.Pipe()
require.NoError(t, pipeErr)
os.Stdout = w
err := Show(context.Background(), tt.doer, dbExecutor, "https://aur.archlinux.org", completionPath, tt.interval, tt.force, nil)
// Close writer first, then restore stdout, then read
w.Close()
os.Stdout = oldStdout
var buf bytes.Buffer
_, copyErr := io.Copy(&buf, r)
r.Close()
if tt.expectError {
assert.Error(t, err)
} else {
require.NoError(t, err)
require.NoError(t, copyErr)
// Verify file exists and has content
content, readErr := os.ReadFile(completionPath)
require.NoError(t, readErr)
assert.NotEmpty(t, content)
}
})
}
}
func TestShowFileOpenError(t *testing.T) {
t.Parallel()
tmpDir := t.TempDir()
// Use a path that can't be created (directory as file)
completionPath := filepath.Join(tmpDir, "completion")
// Create a directory where we expect a file - this will cause OpenFile to fail
err := os.MkdirAll(completionPath, 0o755)
require.NoError(t, err)
doer := &mockDoer{
t: t,
wantURL: "https://aur.archlinux.org/packages.gz",
returnStatusCode: 200,
returnBody: []byte("# Comment\npkg\n"),
returnErr: nil,
}
dbExecutor := &mockPkgSynchronizer{packages: []db.IPackage{}}
err = Show(context.Background(), doer, dbExecutor, "https://aur.archlinux.org", completionPath, 7, true, nil)
assert.Error(t, err)
}
func TestUpdateCacheMkdirError(t *testing.T) {
t.Parallel()
// Create a file where we expect a directory - this will cause MkdirAll to fail
tmpDir := t.TempDir()
blockingFile := filepath.Join(tmpDir, "blocking")
err := os.WriteFile(blockingFile, []byte("block"), 0o600)
require.NoError(t, err)
completionPath := filepath.Join(blockingFile, "subdir", "completion")
doer := &mockDoer{
t: t,
wantURL: "https://aur.archlinux.org/packages.gz",
returnStatusCode: 200,
returnBody: []byte("# Comment\npkg\n"),
returnErr: nil,
}
dbExecutor := &mockPkgSynchronizer{packages: []db.IPackage{}}
err = UpdateCache(context.Background(), doer, dbExecutor, "https://aur.archlinux.org", completionPath, nil)
assert.Error(t, err)
}
func Test_createAURListWriteError(t *testing.T) {
t.Parallel()
doer := &mockDoer{
t: t,
wantURL: "https://aur.archlinux.org/packages.gz",
returnStatusCode: 200,
returnBody: []byte("# Comment\npkg1\npkg2\n"),
returnErr: nil,
}
err := createAURList(context.Background(), doer, "https://aur.archlinux.org", &errorWriter{}, nil)
assert.EqualError(t, err, "write error")
}
================================================
FILE: pkg/db/executor.go
================================================
package db
import (
"time"
alpm "github.com/Jguer/dyalpm"
"github.com/Jguer/yay/v12/pkg/text"
)
type (
IPackage = alpm.Package
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.Package
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)
}
================================================
FILE: pkg/db/ialpm/alpm.go
================================================
package ialpm
import (
"errors"
"fmt"
"os"
"strconv"
"time"
alpm "github.com/Jguer/dyalpm"
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.Database
syncDB []alpm.Database
syncDBsCache []alpm.Database
conf *pacmanconf.Config
log *text.Logger
installedRemotePkgNames []string
installedRemotePkgMap map[string]alpm.Package
installedSyncPkgNames []string
}
func NewExecutor(pacmanConf *pacmanconf.Config, logger *text.Logger) (*AlpmExecutor, error) {
ae := &AlpmExecutor{
handle: nil,
localDB: nil,
syncDB: nil,
syncDBsCache: []alpm.Database{},
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
}
if err := alpmDB.SetServers(repo.Servers); err != nil {
return err
}
if err := alpmDB.SetUsage(int(toUsage(repo.Usage))); err != nil {
return err
}
}
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.Package) 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.Package) 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
}
if err := alpmSetQuestionCallback(alpmHandle, ae.questionCallback()); err != nil {
return err
}
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 {
return ae.SyncSatisfier(pkgName) != nil
}
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.Package {
dbs := ae.syncDBs()
if len(dbs) == 0 {
return nil
}
// Use FindDBSatisfier across sync databases
dbSlice := make([]alpm.Database, len(dbs))
copy(dbSlice, dbs)
return ae.handle.FindDBSatisfier(dbSlice, pkgName)
}
func (ae *AlpmExecutor) PackagesFromGroup(groupName string) []alpm.Package {
pkgs, err := ae.handle.FindGroupPkgs(ae.syncDBs(), groupName)
if err != nil {
return nil
}
return pkgs
}
func (ae *AlpmExecutor) PackagesFromGroupAndDB(groupName, dbName string) ([]alpm.Package, error) {
singleDBs, err := ae.handle.SyncDBListByDBName(dbName)
if err != nil {
return nil, err
}
return ae.handle.FindGroupPkgs(singleDBs, groupName)
}
func (ae *AlpmExecutor) LocalPackages() []alpm.Package {
localPackages := []alpm.Package{}
_ = ae.localDB.PkgCache().ForEach(func(pkg alpm.Package) 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.Package {
repoPackages := []alpm.Package{}
for _, alpmDB := range ae.syncDBs() {
if len(pkgNames) == 0 {
_ = alpmDB.PkgCache().ForEach(func(pkg alpm.Package) error {
repoPackages = append(repoPackages, pkg)
return nil
})
continue
}
_ = alpmDB.Search(pkgNames).ForEach(func(pkg alpm.Package) error {
repoPackages = append(repoPackages, pkg)
return nil
})
}
return repoPackages
}
func (ae *AlpmExecutor) LocalPackage(pkgName string) alpm.Package {
pkg := ae.localDB.Pkg(pkgName)
if pkg == nil {
return nil
}
return pkg
}
func (ae *AlpmExecutor) syncDBs() []alpm.Database {
if ae.syncDBsCache == nil {
ae.syncDBsCache = ae.syncDB
}
return ae.syncDBsCache
}
func (ae *AlpmExecutor) SyncPackage(pkgName string) alpm.Package {
for _, db := range ae.syncDBs() {
if dbPkg := db.Pkg(pkgName); dbPkg != nil {
return dbPkg
}
}
return nil
}
func (ae *AlpmExecutor) SyncPackageFromDB(pkgName, dbName string) alpm.Package {
singleDB, err := ae.handle.SyncDBByName(dbName)
if err != nil {
return nil
}
return singleDB.Pkg(pkgName)
}
func (ae *AlpmExecutor) SatisfierFromDB(pkgName, dbName string) (alpm.Package, error) {
singleDBs, err := ae.handle.SyncDBListByDBName(dbName)
if err != nil {
return nil, err
}
foundPkg := ae.handle.FindDBSatisfier(singleDBs, pkgName)
if foundPkg == nil {
return nil, nil
}
return foundPkg, nil
}
func (ae *AlpmExecutor) PackageDepends(pkg alpm.Package) []alpm.Depend {
return pkg.Depends()
}
func (ae *AlpmExecutor) PackageOptionalDepends(pkg alpm.Package) []alpm.Depend {
return pkg.OptionalDepends()
}
func (ae *AlpmExecutor) PackageProvides(pkg alpm.Package) []alpm.Depend {
return pkg.Provides()
}
func (ae *AlpmExecutor) PackageGroups(pkg alpm.Package) []string {
return pkg.Groups()
}
// 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.Package) 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.Package {
return append([]alpm.Package{}, ae.localDB.PkgCache().SortBySize()...)
}
func (ae *AlpmExecutor) LastBuildTime() time.Time {
var lastTime time.Time
for _, db := range ae.syncDBs() {
_ = db.PkgCache().ForEach(func(pkg alpm.Package) error {
thisTime := pkg.BuildDate()
if thisTime.After(lastTime) {
lastTime = thisTime
}
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) {
for _, db := range ae.syncDBs() {
repos = append(repos, db.Name())
}
return
}
func alpmSetArchitecture(alpmHandle alpm.Handle, arch []string) error {
return alpmHandle.SetArchitectures(arch)
}
func (ae *AlpmExecutor) AlpmArchitectures() ([]string, error) {
architectures, err := ae.handle.Architectures()
return architectures, err
}
func alpmSetLogCallback(alpmHandle alpm.Handle, cb func(alpm.LogLevel, string)) {
// dyalpm uses a different callback mechanism - log callback not easily supported
// due to va_list in libalpm. Skip setting log callback.
_ = alpmHandle
_ = cb
}
func alpmSetQuestionCallback(alpmHandle alpm.Handle, cb func(alpm.QuestionAny)) error {
return alpmHandle.SetQuestionCallbackFunc(func(q alpm.Question) {
cb(alpm.QuestionAny{Question: q})
})
}
================================================
FILE: pkg/db/ialpm/alpm_test.go
================================================
//go:build !integration
// +build !integration
package ialpm
import (
"io"
"strings"
"testing"
alpm "github.com/Jguer/dyalpm"
"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 := h.Root()
assert.Nil(t, err)
assert.Equal(t, "/", root)
dbPath := 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)
log := h.LogFile()
assert.Nil(t, err)
assert.Equal(t, "/logfile", log)
gpg := 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)
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)
ignoreGroup, err := h.IgnoreGroups()
assert.Nil(t, err)
assert.Equal(t, []string{"ignore", "this", "group"}, ignoreGroup)
noUp, err := h.NoUpgrades()
assert.Nil(t, err)
assert.Equal(t, []string{"noupgrade"}, noUp)
noEx, err := h.NoExtracts()
assert.Nil(t, err)
assert.Equal(t, []string{"noextract"}, noEx)
check := h.CheckSpace()
assert.Nil(t, err)
assert.Equal(t, true, check)
}
func alpmTestGetArch(h alpm.Handle) ([]string, error) {
architectures, err := h.Architectures()
return architectures, err
}
================================================
FILE: pkg/db/ialpm/high_level.go
================================================
package ialpm
import (
alpm "github.com/Jguer/dyalpm"
"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.Package{}
}
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.Package {
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
}
================================================
FILE: pkg/db/mock/executor.go
================================================
package mock
import (
"time"
"github.com/Jguer/yay/v12/pkg/db"
"github.com/Jguer/yay/v12/pkg/text"
alpm "github.com/Jguer/dyalpm"
)
type (
IPackage = alpm.Package
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.Package) []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()
}
// Tests that don't care about repo ordering shouldn't need to stub this out.
return nil
}
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")
}
================================================
FILE: pkg/db/mock/repo.go
================================================
package mock
import (
"io"
"time"
alpm "github.com/Jguer/dyalpm"
)
// DependList is a lightweight helper for test fixtures.
type DependList struct {
Depends []alpm.Depend
}
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 DependList
PProvides DependList
PArchitecture string
}
var _ alpm.Package = (*Package)(nil)
func (p *Package) Base() string {
return p.PBase
}
func (p *Package) BuildDate() time.Time {
return p.PBuildDate
}
func (p *Package) DB() alpm.Database {
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 {
return p.PArchitecture
}
// Backup returns a list of package backups.
func (p *Package) Backup() []alpm.Backup {
panic("not implemented")
}
// Conflicts returns the conflicts of the package as a DependList.
func (p *Package) Conflicts() []alpm.Depend {
panic("not implemented")
}
// Depends returns the package's dependency list.
func (p *Package) Depends() []alpm.Depend {
return p.PDepends.Depends
}
// Depends returns the package's optional dependency list.
func (p *Package) OptionalDepends() []alpm.Depend {
panic("not implemented")
}
// Depends returns the package's check dependency list.
func (p *Package) CheckDepends() []alpm.Depend {
panic("not implemented")
}
// Depends returns the package's make dependency list.
func (p *Package) MakeDepends() []alpm.Depend {
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() []string {
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() []string {
panic("not implemented")
}
// SHA256Sum returns package SHA256Sum.
func (p *Package) SHA256Sum() 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.Depend {
return p.PProvides.Depends
}
// 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.Depend {
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, error) {
panic("not implemented")
}
// ComputeOptionalFor returns the names of packages that optionally
// require the given package.
func (p *Package) ComputeOptionalFor() ([]string, error) {
panic("not implemented")
}
// SyncNewVersion checks if there is a new version of the
// package in a given DBlist.
func (p *Package) SyncNewVersion(dbs []alpm.Database) alpm.Package {
panic("not implemented")
}
func (p *Package) Type() string {
panic("not implemented")
}
func (p *Package) CheckMD5Sum() error {
panic("not implemented")
}
func (p *Package) CheckPGPSignature() (alpm.SigList, error) {
panic("not implemented")
}
func (p *Package) Contains(path string) bool {
panic("not implemented")
}
func (p *Package) Free() error {
return nil
}
// New methods required by dyalpm refactoring
func (p *Package) HasScriptlet() bool {
return false
}
func (p *Package) DownloadSize() int64 {
return 0
}
func (p *Package) NativeHandle() alpm.Handle {
return nil
}
func (p *Package) Sig() string {
return ""
}
func (p *Package) PkgValidation() alpm.PkgValidation {
return alpm.PkgValidationUnknown
}
func (p *Package) XData() string {
return ""
}
func (p *Package) Changelog() (io.ReadCloser, error) {
return nil, nil
}
func (p *Package) SyncGetNewVersion(dbsSync []alpm.Database) alpm.Package {
return nil
}
type DB struct {
alpm.Database
name string
}
func NewDB(name string) *DB {
return &DB{name: name}
}
func (d *DB) Name() string {
return d.name
}
func (d *DB) Pkg(name string) alpm.Package {
return nil
}
func (d *DB) PkgCache() alpm.PackageIterator {
return alpm.PackageIterator{}
}
func (d *DB) Search(needles []string) alpm.PackageIterator {
return alpm.PackageIterator{}
}
func (d *DB) SetServers(servers []string) error {
return nil
}
func (d *DB) SetUsage(usage int) error {
return nil
}
================================================
FILE: pkg/db/types.go
================================================
package db
import "slices"
func ArchIsSupported(alpmArch []string, arch string) bool {
if arch == "any" {
return true
}
return slices.Contains(alpmArch, arch)
}
================================================
FILE: pkg/dep/dep.go
================================================
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
}
================================================
FILE: pkg/dep/dep_graph.go
================================================
package dep
import (
"context"
"fmt"
"strconv"
"strings"
aurc "github.com/Jguer/aur"
alpm "github.com/Jguer/dyalpm"
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 int
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.AddProvides(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 {
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 {
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.Package, upgradeInfo *db.SyncUpgrade,
) *topo.Graph[string, *InstallInfo] {
if graph == nil {
graph = NewGraph()
}
graph.AddNode(pkg.Name())
provides := pkg.Provides()
for i := range provides {
p := &provides[i]
g.logger.Debugln(pkg.Name() + " provides: " + p.String())
graph.AddProvides(p.Name, p, pkg.Name())
}
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{}
var packagesNotFound int
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)
packagesNotFound++
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)
if packagesNotFound == len(targets) {
return graph, &aur.ErrTargetNotFound{}
}
return graph, nil
}
// Removes found deps from the deps mapset and returns the found deps.
func (g *Grapher) findDepsFromAUR(ctx context.Context,
graph *topo.Graph[string, *InstallInfo],
parentPkgName string,
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]
// Cache by the full depString (including version) for each dep whose name matches
for _, depString := range deps.ToSlice() {
depName, _, _ := splitDep(depString)
if depName == pkg.Name {
g.providerCache[depString] = append(g.providerCache[depString], *pkg)
}
}
for _, val := range pkg.Provides {
if val == pkg.Name {
continue
}
// Also check provides against versioned deps
provideName, _, _ := splitDep(val)
for _, depString := range deps.ToSlice() {
depName, _, _ := splitDep(depString)
if depName == provideName {
g.providerCache[depString] = append(g.providerCache[depString], *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 {
// set of packages that require this dependency
requiredBySet := mapset.NewThreadUnsafeSet[string]()
// add current parent
requiredBySet.Add(parentPkgName)
// if dependency is already in graph, get all packages that require it
if graph.Exists(depName) {
if deps := graph.Dependents(depName); deps != nil {
for parent := range deps {
requiredBySet.Add(parent)
}
}
}
requiredBySlice := requiredBySet.ToSlice()
requiredByStr := strings.Join(requiredBySlice, ", ")
g.logger.Errorln(gotext.Get("No AUR package found for"), " ", depString, " (", gotext.Get("required by"), ": ", requiredByStr, ")")
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.HasProvides(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.GetProviderInfo(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(); 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, graph, parentPkgName, 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
}
================================================
FILE: pkg/dep/dep_graph_bench_test.go
================================================
//go:build !integration
// +build !integration
package dep
import (
"context"
"io"
"os"
"testing"
aurc "github.com/Jguer/aur"
alpm "github.com/Jguer/dyalpm"
"github.com/stretchr/testify/require"
"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"
)
// benchCase represents a single benchmark scenario with expected results for validation.
type benchCase struct {
name string
targets []string
expectedLayers []map[string]*InstallInfo
noDeps bool
noCheckDeps bool
}
// newBenchMockDB creates a mock DB executor for benchmarking gstreamer-git scenarios.
func newBenchMockDB() *mock.DBExecutor {
return &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 "gstreamer-git", "gst-plugins-base-libs-git", "gst-plugins-good-git",
"gstreamer-git=1.24.0.r37-1", "gst-plugins-base-libs-git=1.24.0.r37-1":
return nil
case "libxml2":
return &mock.Package{
PName: "libxml2",
PVersion: "2.12.0-1",
PDB: mock.NewDB("core"),
}
case "glib2":
return &mock.Package{
PName: "glib2",
PVersion: "2.78.0-1",
PDB: mock.NewDB("core"),
}
case "orc":
return &mock.Package{
PName: "orc",
PVersion: "0.4.34-1",
PDB: mock.NewDB("extra"),
}
case "libxv":
return &mock.Package{
PName: "libxv",
PVersion: "1.0.12-1",
PDB: mock.NewDB("extra"),
}
case "iso-codes":
return &mock.Package{
PName: "iso-codes",
PVersion: "4.15.0-1",
PDB: mock.NewDB("extra"),
}
case "libpulse":
return &mock.Package{
PName: "libpulse",
PVersion: "16.1-1",
PDB: mock.NewDB("extra"),
}
case "wavpack":
return &mock.Package{
PName: "wavpack",
PVersion: "5.6.0-1",
PDB: mock.NewDB("extra"),
}
}
return nil
},
LocalSatisfierExistsFn: func(s string) bool {
switch s {
case "gstreamer-git", "gstreamer-git=1.24.0.r37-1",
"gst-plugins-base-libs-git", "gst-plugins-base-libs-git=1.24.0.r37-1",
"gst-plugins-good-git":
return false
case "libxml2", "glib2", "orc", "libxv", "iso-codes", "libpulse", "wavpack",
"git", "meson", "ninja", "llvm", "clang":
return true
}
return true
},
LocalPackageFn: func(string) mock.IPackage { return nil },
}
}
// newBenchMockAUR creates a mock AUR client for benchmarking gstreamer-git scenarios.
func newBenchMockAUR(t testing.TB) *mockaur.MockAUR {
return &mockaur.MockAUR{GetFn: func(ctx context.Context, query *aurc.Query) ([]aur.Pkg, error) {
if len(query.Needles) > 0 {
for _, needle := range query.Needles {
if needle == "gstreamer-git" || needle == "gst-plugins-base-libs-git" || needle == "gst-plugins-good-git" {
gstFn := getFromFile(t, "testdata/gstreamer-git.json")
return gstFn(ctx, query)
}
}
}
return []aur.Pkg{}, nil
}}
}
// newJellyfinMockDB creates a mock DB for jellyfin scenarios.
func newJellyfinMockDB() *mock.DBExecutor {
return &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 },
}
}
// newJellyfinMockAUR creates a mock AUR for jellyfin scenarios.
func newJellyfinMockAUR(t testing.TB) *mockaur.MockAUR {
return &mockaur.MockAUR{GetFn: func(ctx context.Context, query *aurc.Query) ([]aur.Pkg, error) {
if len(query.Needles) == 0 {
return []aur.Pkg{}, nil
}
switch query.Needles[0] {
case "jellyfin":
return getFromFile(t, "testdata/jellyfin.json")(ctx, query)
case "jellyfin-web":
return getFromFile(t, "testdata/jellyfin-web.json")(ctx, query)
case "jellyfin-server":
return getFromFile(t, "testdata/jellyfin-server.json")(ctx, query)
}
return []aur.Pkg{}, nil
}}
}
// newCephMockDB creates a mock DB for ceph scenarios with providers.
func newCephMockDB() *mock.DBExecutor {
return &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", "ceph", "ceph-libs", "ceph-libs=17.2.6-2":
return nil
}
return nil
},
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
}
return true
},
LocalPackageFn: func(string) mock.IPackage { return nil },
}
}
// newCephMockAUR creates a mock AUR for ceph scenarios.
func newCephMockAUR() *mockaur.MockAUR {
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"},
},
}
return &mockaur.MockAUR{GetFn: func(ctx context.Context, query *aurc.Query) ([]aur.Pkg, error) {
pkgs := []aur.Pkg{}
for _, needle := range query.Needles {
if pkg, ok := mockPkgs[needle]; ok {
pkgs = append(pkgs, pkg)
}
}
return pkgs, nil
}}
}
// newAndroidSDKMockDB creates a mock DB for android-sdk scenarios.
func newAndroidSDKMockDB() *mock.DBExecutor {
return &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},
},
},
}
}
return nil
},
PackagesFromGroupFn: func(string) []mock.IPackage { return nil },
LocalSatisfierExistsFn: func(s string) bool {
switch s {
case "java-environment":
return false
case "libxtst", "fontconfig", "freetype2", "lib32-gcc-libs", "lib32-glibc",
"libx11", "libxext", "libxrender", "zlib", "gcc-libs":
return true
}
return true
},
LocalPackageFn: func(string) mock.IPackage { return nil },
}
}
// newAndroidSDKMockAUR creates a mock AUR for android-sdk scenarios.
func newAndroidSDKMockAUR(t testing.TB) *mockaur.MockAUR {
return &mockaur.MockAUR{GetFn: func(ctx context.Context, query *aurc.Query) ([]aur.Pkg, error) {
if len(query.Needles) > 0 && query.Needles[0] == "android-sdk" {
return getFromFile(t, "testdata/android-sdk.json")(ctx, query)
}
return []aur.Pkg{}, nil
}}
}
// verifyLayers checks that the actual layers match expected layers.
func verifyLayers(t testing.TB, expected, actual []map[string]*InstallInfo) {
t.Helper()
require.Equal(t, len(expected), len(actual), "layer count mismatch")
for i := range expected {
require.Equal(t, len(expected[i]), len(actual[i]), "layer %d package count mismatch", i)
for name, expectedInfo := range expected[i] {
actualInfo, ok := actual[i][name]
require.True(t, ok, "missing package %s in layer %d", name, i)
require.Equal(t, expectedInfo.Source, actualInfo.Source, "source mismatch for %s", name)
require.Equal(t, expectedInfo.Reason, actualInfo.Reason, "reason mismatch for %s", name)
require.Equal(t, expectedInfo.Version, actualInfo.Version, "version mismatch for %s", name)
if expectedInfo.AURBase != nil {
require.NotNil(t, actualInfo.AURBase, "AURBase should not be nil for %s", name)
require.Equal(t, *expectedInfo.AURBase, *actualInfo.AURBase, "AURBase mismatch for %s", name)
}
if expectedInfo.SyncDBName != nil {
require.NotNil(t, actualInfo.SyncDBName, "SyncDBName should not be nil for %s", name)
require.Equal(t, *expectedInfo.SyncDBName, *actualInfo.SyncDBName, "SyncDBName mismatch for %s", name)
}
}
}
}
// BenchmarkGraphFromTargets_GstreamerGit benchmarks dependency graph construction
// for the gstreamer-git split package scenario.
func BenchmarkGraphFromTargets_GstreamerGit(b *testing.B) {
cases := []benchCase{
{
name: "SingleTarget",
targets: []string{"gst-plugins-good-git"},
expectedLayers: []map[string]*InstallInfo{
{"gst-plugins-good-git": {Source: AUR, Reason: Explicit, Version: "1.24.0.r37-1", AURBase: ptrString("gstreamer-git")}},
{"gst-plugins-base-libs-git": {Source: AUR, Reason: Dep, Version: "1.24.0.r37-1", AURBase: ptrString("gstreamer-git")}},
{"gstreamer-git": {Source: AUR, Reason: Dep, Version: "1.24.0.r37-1", AURBase: ptrString("gstreamer-git")}},
},
},
{
name: "TwoTargets",
targets: []string{"gstreamer-git", "gst-plugins-good-git"},
expectedLayers: []map[string]*InstallInfo{
{"gst-plugins-good-git": {Source: AUR, Reason: Explicit, Version: "1.24.0.r37-1", AURBase: ptrString("gstreamer-git")}},
{"gst-plugins-base-libs-git": {Source: AUR, Reason: Dep, Version: "1.24.0.r37-1", AURBase: ptrString("gstreamer-git")}},
{"gstreamer-git": {Source: AUR, Reason: Explicit, Version: "1.24.0.r37-1", AURBase: ptrString("gstreamer-git")}},
},
},
{
name: "AllThreeExplicit",
targets: []string{"gstreamer-git", "gst-plugins-base-libs-git", "gst-plugins-good-git"},
expectedLayers: []map[string]*InstallInfo{
{"gst-plugins-good-git": {Source: AUR, Reason: Explicit, Version: "1.24.0.r37-1", AURBase: ptrString("gstreamer-git")}},
{"gst-plugins-base-libs-git": {Source: AUR, Reason: Explicit, Version: "1.24.0.r37-1", AURBase: ptrString("gstreamer-git")}},
{"gstreamer-git": {Source: AUR, Reason: Explicit, Version: "1.24.0.r37-1", AURBase: ptrString("gstreamer-git")}},
},
},
}
for _, tc := range cases {
b.Run(tc.name, func(b *testing.B) {
mockDB := newBenchMockDB()
mockAUR := newBenchMockAUR(b)
logger := text.NewLogger(io.Discard, io.Discard, &os.File{}, true, "test")
g := NewGrapher(mockDB, mockAUR, false, true, tc.noDeps, tc.noCheckDeps, false, logger)
// Verify correctness once before benchmarking
graph, err := g.GraphFromTargets(context.Background(), nil, tc.targets)
require.NoError(b, err)
layers := graph.TopoSortedLayers(nil)
verifyLayers(b, tc.expectedLayers, layers)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = g.GraphFromTargets(context.Background(), nil, tc.targets)
}
})
}
}
// BenchmarkGraphFromTargets_Jellyfin benchmarks dependency graph construction
// for the jellyfin package with mixed AUR/sync dependencies.
func BenchmarkGraphFromTargets_Jellyfin(b *testing.B) {
cases := []benchCase{
{
name: "NoDeps",
targets: []string{"jellyfin"},
noDeps: true,
noCheckDeps: false,
expectedLayers: []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")}},
},
},
{
name: "WithDeps",
targets: []string{"jellyfin"},
noDeps: false,
noCheckDeps: false,
expectedLayers: []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")},
},
},
},
}
for _, tc := range cases {
b.Run(tc.name, func(b *testing.B) {
mockDB := newJellyfinMockDB()
mockAUR := newJellyfinMockAUR(b)
logger := text.NewLogger(io.Discard, io.Discard, &os.File{}, true, "test")
g := NewGrapher(mockDB, mockAUR, false, true, tc.noDeps, tc.noCheckDeps, false, logger)
// Verify correctness once before benchmarking
graph, err := g.GraphFromTargets(context.Background(), nil, tc.targets)
require.NoError(b, err)
layers := graph.TopoSortedLayers(nil)
verifyLayers(b, tc.expectedLayers, layers)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = g.GraphFromTargets(context.Background(), nil, tc.targets)
}
})
}
}
// BenchmarkGraphFromTargets_CephProvides benchmarks dependency graph construction
// for packages with virtual provides (ceph-bin provides ceph-libs).
func BenchmarkGraphFromTargets_CephProvides(b *testing.B) {
cases := []benchCase{
{
name: "CephBinWithLibsBin",
targets: []string{"ceph-bin", "ceph-libs-bin"},
expectedLayers: []map[string]*InstallInfo{
{"ceph-bin": {Source: AUR, Reason: Explicit, Version: "17.2.6-2", AURBase: ptrString("ceph-bin")}},
{"ceph-libs-bin": {Source: AUR, Reason: Explicit, Version: "17.2.6-2", AURBase: ptrString("ceph-bin")}},
},
},
{
name: "CephOnly",
targets: []string{"ceph"},
expectedLayers: []map[string]*InstallInfo{
{"ceph": {Source: AUR, Reason: Explicit, Version: "17.2.6-2", AURBase: ptrString("ceph")}},
{"ceph-libs": {Source: AUR, Reason: Dep, Version: "17.2.6-2", AURBase: ptrString("ceph")}},
},
},
{
name: "CephBinOnly",
targets: []string{"ceph-bin"},
expectedLayers: []map[string]*InstallInfo{
{"ceph-bin": {Source: AUR, Reason: Explicit, Version: "17.2.6-2", AURBase: ptrString("ceph-bin")}},
{"ceph-libs": {Source: AUR, Reason: Dep, Version: "17.2.6-2", AURBase: ptrString("ceph")}},
},
},
}
for _, tc := range cases {
b.Run(tc.name, func(b *testing.B) {
mockDB := newCephMockDB()
mockAUR := newCephMockAUR()
logger := text.NewLogger(io.Discard, io.Discard, &os.File{}, true, "test")
g := NewGrapher(mockDB, mockAUR, false, true, tc.noDeps, tc.noCheckDeps, false, logger)
// Verify correctness once before benchmarking
graph, err := g.GraphFromTargets(context.Background(), nil, tc.targets)
require.NoError(b, err)
layers := graph.TopoSortedLayers(nil)
verifyLayers(b, tc.expectedLayers, layers)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = g.GraphFromTargets(context.Background(), nil, tc.targets)
}
})
}
}
// BenchmarkGraphFromTargets_AndroidSDK benchmarks dependency graph construction
// for packages with explicit sync dependencies providing virtual packages.
func BenchmarkGraphFromTargets_AndroidSDK(b *testing.B) {
tc := benchCase{
name: "WithJDK",
targets: []string{"android-sdk", "jdk11-openjdk"},
expectedLayers: []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")}},
},
}
mockDB := newAndroidSDKMockDB()
mockAUR := newAndroidSDKMockAUR(b)
logger := text.NewLogger(io.Discard, io.Discard, &os.File{}, true, "test")
g := NewGrapher(mockDB, mockAUR, false, true, tc.noDeps, tc.noCheckDeps, false, logger)
// Verify correctness once before benchmarking
graph, err := g.GraphFromTargets(context.Background(), nil, tc.targets)
require.NoError(b, err)
layers := graph.TopoSortedLayers(nil)
verifyLayers(b, tc.expectedLayers, layers)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = g.GraphFromTargets(context.Background(), nil, tc.targets)
}
}
// BenchmarkTopoSortedLayers benchmarks the topological sort operation on pre-built graphs.
func BenchmarkTopoSortedLayers(b *testing.B) {
b.Run("GstreamerGit", func(b *testing.B) {
mockDB := newBenchMockDB()
mockAUR := newBenchMockAUR(b)
logger := text.NewLogger(io.Discard, io.Discard, &os.File{}, true, "test")
g := NewGrapher(mockDB, mockAUR, false, true, false, false, false, logger)
graph, err := g.GraphFromTargets(context.Background(), nil, []string{"gst-plugins-good-git"})
require.NoError(b, err)
// Verify correctness
layers := graph.TopoSortedLayers(nil)
require.Len(b, layers, 3)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = graph.TopoSortedLayers(nil)
}
})
b.Run("Jellyfin", func(b *testing.B) {
mockDB := newJellyfinMockDB()
mockAUR := newJellyfinMockAUR(b)
logger := text.NewLogger(io.Discard, io.Discard, &os.File{}, true, "test")
g := NewGrapher(mockDB, mockAUR, false, true, false, false, false, logger)
graph, err := g.GraphFromTargets(context.Background(), nil, []string{"jellyfin"})
require.NoError(b, err)
// Verify correctness
layers := graph.TopoSortedLayers(nil)
require.Len(b, layers, 3)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = graph.TopoSortedLayers(nil)
}
})
}
// BenchmarkNewGraph benchmarks graph creation overhead.
func BenchmarkNewGraph(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = NewGraph()
}
}
// BenchmarkGraphDependOn benchmarks adding dependency edges.
func BenchmarkGraphDependOn(b *testing.B) {
b.Run("SmallGraph", func(b *testing.B) {
for i := 0; i < b.N; i++ {
graph := NewGraph()
_ = graph.DependOn("pkg1", "pkg2")
_ = graph.DependOn("pkg2", "pkg3")
_ = graph.DependOn("pkg3", "pkg4")
}
})
b.Run("MediumGraph", func(b *testing.B) {
for i := 0; i < b.N; i++ {
graph := NewGraph()
// Create a chain of 20 dependencies
for j := 0; j < 20; j++ {
_ = graph.DependOn(
"pkg"+string(rune('A'+j)),
"pkg"+string(rune('A'+j+1)),
)
}
}
})
}
================================================
FILE: pkg/dep/dep_graph_rpc_test.go
================================================
//go:build !integration
// +build !integration
// Package dep provides tests for tree resolution and parsing using RPC/.SRCINFO metadata
// instead of PKGBUILD parsing. These tests validate:
// - Reliable parser: Ability to handle complex packages using provided metadata
// - Reliable solver: Ability to correctly solve and build complex dependency chains
// - Split packages: Ability to correctly build and install split packages
package dep
import (
"context"
"fmt"
"io"
"os"
"testing"
aurc "github.com/Jguer/aur"
"github.com/stretchr/testify/require"
"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"
)
// TestGrapher_ReliableParser_AWSCliGit tests the reliable parsing capability
// for complex packages like aws-cli-git that have many dependencies.
// This validates that the RPC metadata is correctly parsed and dependencies
// are properly resolved without needing PKGBUILD parsing.
func TestGrapher_ReliableParser_AWSCliGit(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 "aws-cli-git":
return nil
case "python":
return &mock.Package{PName: "python", PVersion: "3.11.0-1", PDB: mock.NewDB("core")}
case "python-botocore>=1.19.35", "python-botocore":
return &mock.Package{PName: "python-botocore", PVersion: "1.29.0-1", PDB: mock.NewDB("extra")}
case "python-docutils>=0.10", "python-docutils":
return &mock.Package{PName: "python-docutils", PVersion: "0.19-1", PDB: mock.NewDB("extra")}
case "python-rsa>=3.1.2", "python-rsa":
return &mock.Package{PName: "python-rsa", PVersion: "4.9-1", PDB: mock.NewDB("extra")}
case "python-s3transfer>=0.3.0", "python-s3transfer":
return &mock.Package{PName: "python-s3transfer", PVersion: "0.6.0-1", PDB: mock.NewDB("extra")}
case "python-yaml>=3.10", "python-yaml":
return &mock.Package{PName: "python-yaml", PVersion: "6.0-1", PDB: mock.NewDB("extra")}
case "python-colorama>=0.2.5", "python-colorama":
return &mock.Package{PName: "python-colorama", PVersion: "0.4.6-1", PDB: mock.NewDB("extra")}
case "python-tox>=2.3.1", "python-tox":
return &mock.Package{PName: "python-tox", PVersion: "4.0.0-1", PDB: mock.NewDB("extra")}
case "python-nose>=1.3.7", "python-nose":
return &mock.Package{PName: "python-nose", PVersion: "1.3.7-1", PDB: mock.NewDB("extra")}
case "python-mock>=1.3.0", "python-mock":
return &mock.Package{PName: "python-mock", PVersion: "5.0.0-1", PDB: mock.NewDB("extra")}
case "python-wheel>=0.24.0", "python-wheel":
return &mock.Package{PName: "python-wheel", PVersion: "0.38.0-1", PDB: mock.NewDB("extra")}
case "python-dateutil>=2.1", "python-dateutil":
return &mock.Package{PName: "python-dateutil", PVersion: "2.8.2-1", PDB: mock.NewDB("extra")}
case "python-sphinx>=1.1.3", "python-sphinx":
return &mock.Package{PName: "python-sphinx", PVersion: "6.0.0-1", PDB: mock.NewDB("extra")}
case "python-distribute":
return &mock.Package{PName: "python-distribute", PVersion: "0.7.3-1", PDB: mock.NewDB("extra")}
case "git":
return &mock.Package{PName: "git", PVersion: "2.39.0-1", PDB: mock.NewDB("extra")}
}
panic("implement me " + s)
},
LocalSatisfierExistsFn: func(s string) bool {
switch s {
case "aws-cli-git":
return false
case "python", "python-botocore>=1.19.35", "python-docutils>=0.10",
"python-rsa>=3.1.2", "python-s3transfer>=0.3.0", "python-yaml>=3.10",
"python-colorama>=0.2.5", "python-tox>=2.3.1", "python-nose>=1.3.7",
"python-mock>=1.3.0", "python-wheel>=0.24.0", "python-dateutil>=2.1",
"python-sphinx>=1.1.3", "python-distribute", "git":
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 len(query.Needles) > 0 && query.Needles[0] == "aws-cli-git" {
awsFn := getFromFile(t, "testdata/aws-cli-git.json")
return awsFn(ctx, query)
}
return []aur.Pkg{}, nil
}}
t.Run("parses aws-cli-git with all its dependencies", 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, []string{"aws-cli-git"})
require.NoError(t, err)
layers := got.TopoSortedLayers(nil)
require.NotEmpty(t, layers)
require.Contains(t, layers[0], "aws-cli-git")
require.Equal(t, "1.27.145.r11217.g5885ee4dc-1", layers[0]["aws-cli-git"].Version)
require.Equal(t, "aws-cli-git", *layers[0]["aws-cli-git"].AURBase)
require.Equal(t, Explicit, layers[0]["aws-cli-git"].Reason)
require.Equal(t, AUR, layers[0]["aws-cli-git"].Source)
})
t.Run("validates provides field for aws-cli", 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, []string{"aws-cli-git"})
require.NoError(t, err)
layers := got.TopoSortedLayers(nil)
require.NotEmpty(t, layers)
require.Contains(t, layers[0], "aws-cli-git")
})
}
// TestGrapher_ReliableSolver_LiriDesktopGit tests the dependency solver
// with complex dependency chains like liri-desktop-git metapackage.
func TestGrapher_ReliableSolver_LiriDesktopGit(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 "liri-desktop-git", "liri-shell-git", "liri-settings-git",
"libliri-git", "fluid-git", "liri-cmake-shared-git":
return nil
case "qt5-declarative":
return &mock.Package{PName: "qt5-declarative", PVersion: "5.15.10-1", PDB: mock.NewDB("extra")}
case "qt5-quickcontrols2":
return &mock.Package{PName: "qt5-quickcontrols2", PVersion: "5.15.10-1", PDB: mock.NewDB("extra")}
case "qt5-svg":
return &mock.Package{PName: "qt5-svg", PVersion: "5.15.10-1", PDB: mock.NewDB("extra")}
case "qt5-graphicaleffects":
return &mock.Package{PName: "qt5-graphicaleffects", PVersion: "5.15.10-1", PDB: mock.NewDB("extra")}
case "qt5-wayland":
return &mock.Package{PName: "qt5-wayland", PVersion: "5.15.10-1", PDB: mock.NewDB("extra")}
case "wayland":
return &mock.Package{PName: "wayland", PVersion: "1.22.0-1", PDB: mock.NewDB("extra")}
case "cmake":
return &mock.Package{PName: "cmake", PVersion: "3.28.0-1", PDB: mock.NewDB("extra")}
case "qt5-tools":
return &mock.Package{PName: "qt5-tools", PVersion: "5.15.10-1", PDB: mock.NewDB("extra")}
case "git":
return &mock.Package{PName: "git", PVersion: "2.43.0-1", PDB: mock.NewDB("extra")}
}
panic("implement me " + s)
},
LocalSatisfierExistsFn: func(s string) bool {
switch s {
case "liri-desktop-git", "liri-shell-git", "liri-settings-git",
"libliri-git", "fluid-git":
return false
case "liri-cmake-shared-git": // makedepend from AUR
return false
case "qt5-declarative", "qt5-quickcontrols2", "qt5-svg",
"qt5-graphicaleffects", "qt5-wayland", "wayland",
"cmake", "qt5-tools", "git":
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) {
for _, needle := range query.Needles {
switch needle {
case "liri-desktop-git", "liri-shell-git", "liri-settings-git",
"libliri-git", "fluid-git", "liri-cmake-shared-git":
liriFn := getFromFile(t, "testdata/liri-desktop-git.json")
return liriFn(ctx, query)
}
}
return []aur.Pkg{}, nil
}}
t.Run("liri-desktop-git pulls all dependencies", 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, []string{"liri-desktop-git"})
require.NoError(t, err)
layers := got.TopoSortedLayers(nil)
totalPkgs := 0
for _, layer := range layers {
totalPkgs += len(layer)
}
// 6 packages: liri-desktop-git + 4 deps + liri-cmake-shared-git (makedep)
require.Equal(t, 6, totalPkgs)
require.Contains(t, layers[0], "liri-desktop-git")
require.Equal(t, Explicit, layers[0]["liri-desktop-git"].Reason)
})
t.Run("complex dependency chain resolves in correct order", 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, []string{"liri-desktop-git"})
require.NoError(t, err)
layers := got.TopoSortedLayers(nil)
require.Contains(t, layers[0], "liri-desktop-git")
allPkgs := make(map[string]bool)
for _, layer := range layers {
for pkg := range layer {
allPkgs[pkg] = true
}
}
require.True(t, allPkgs["liri-desktop-git"])
require.True(t, allPkgs["liri-shell-git"])
require.True(t, allPkgs["liri-settings-git"])
require.True(t, allPkgs["fluid-git"])
require.True(t, allPkgs["libliri-git"])
require.True(t, allPkgs["liri-cmake-shared-git"]) // makedepend
})
}
// TestGrapher_SplitPackages_Clion tests split packages where multiple packages
// come from the same package base, ensuring no rebuilding or reinstalling multiple times.
func TestGrapher_SplitPackages_Clion(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 "clion", "clion-jre", "clion-cmake", "clion-gdb", "clion-lldb":
return nil
case "libdbusmenu-glib":
return &mock.Package{PName: "libdbusmenu-glib", PVersion: "16.04.0-5", PDB: mock.NewDB("extra")}
case "rsync":
return &mock.Package{PName: "rsync", PVersion: "3.2.7-1", PDB: mock.NewDB("extra")}
case "glibc":
return &mock.Package{PName: "glibc", PVersion: "2.38-1", PDB: mock.NewDB("core")}
case "gcc-libs":
return &mock.Package{PName: "gcc-libs", PVersion: "13.2.1-1", PDB: mock.NewDB("core")}
case "python":
return &mock.Package{PName: "python", PVersion: "3.11.0-1", PDB: mock.NewDB("core")}
}
panic("implement me " + s)
},
LocalSatisfierExistsFn: func(s string) bool {
switch s {
case "clion", "clion-jre", "clion-cmake", "clion-gdb", "clion-lldb":
return false
case "libdbusmenu-glib", "rsync", "glibc", "gcc-libs", "python":
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) {
for _, needle := range query.Needles {
switch needle {
case "clion", "clion-jre", "clion-cmake", "clion-gdb", "clion-lldb":
clionFn := getFromFile(t, "testdata/clion.json")
return clionFn(ctx, query)
}
}
return []aur.Pkg{}, nil
}}
installInfos := map[string]*InstallInfo{
"clion exp": {Source: AUR, Reason: Explicit, Version: "2025.3.1.1-1", AURBase: ptrString("clion")},
"clion-jre exp": {Source: AUR, Reason: Explicit, Version: "2025.3.1.1-1", AURBase: ptrString("clion")},
"clion-cmake exp": {Source: AUR, Reason: Explicit, Version: "2025.3.1.1-1", AURBase: ptrString("clion")},
"clion-gdb exp": {Source: AUR, Reason: Explicit, Version: "2025.3.1.1-1", AURBase: ptrString("clion")},
"clion-lldb exp": {Source: AUR, Reason: Explicit, Version: "2025.3.1.1-1", AURBase: ptrString("clion")},
}
tests := []struct {
name string
targets []string
wantLayers []map[string]*InstallInfo
}{
{
name: "single clion package",
targets: []string{"clion"},
wantLayers: []map[string]*InstallInfo{
{"clion": installInfos["clion exp"]},
},
},
{
name: "clion with clion-jre",
targets: []string{"clion", "clion-jre"},
wantLayers: []map[string]*InstallInfo{
{"clion": installInfos["clion exp"], "clion-jre": installInfos["clion-jre exp"]},
},
},
{
name: "all clion packages from same base",
targets: []string{"clion", "clion-jre", "clion-cmake", "clion-gdb", "clion-lldb"},
wantLayers: []map[string]*InstallInfo{
{
"clion": installInfos["clion exp"],
"clion-jre": installInfos["clion-jre exp"],
"clion-cmake": installInfos["clion-cmake exp"],
"clion-gdb": installInfos["clion-gdb exp"],
"clion-lldb": installInfos["clion-lldb exp"],
},
},
},
}
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.TopoSortedLayers(nil)
require.EqualValues(t, tt.wantLayers, layers, layers)
})
}
t.Run("packages from same base share AURBase", 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,
[]string{"clion", "clion-jre", "clion-cmake"})
require.NoError(t, err)
layers := got.TopoSortedLayers(nil)
require.Len(t, layers, 1)
for _, info := range layers[0] {
require.NotNil(t, info.AURBase)
require.Equal(t, "clion", *info.AURBase)
}
})
}
// TestGrapher_SplitPackages_SamsungUnifiedDriver tests split packages where
// packages depend on another package from the same package base.
func TestGrapher_SplitPackages_SamsungUnifiedDriver(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 "samsung-unified-driver", "samsung-unified-driver-common",
"samsung-unified-driver-printer", "samsung-unified-driver-scanner":
return nil
case "cups":
return &mock.Package{PName: "cups", PVersion: "2.4.2-1", PDB: mock.NewDB("extra")}
case "ghostscript":
return &mock.Package{PName: "ghostscript", PVersion: "10.02.0-1", PDB: mock.NewDB("extra")}
case "libxml2-legacy":
return &mock.Package{PName: "libxml2-legacy", PVersion: "2.10.3-1", PDB: mock.NewDB("extra")}
case "libusb-compat":
return &mock.Package{PName: "libusb-compat", PVersion: "0.1.8-1", PDB: mock.NewDB("extra")}
case "sane":
return &mock.Package{PName: "sane", PVersion: "1.2.1-1", PDB: mock.NewDB("extra")}
}
panic("implement me " + s)
},
LocalSatisfierExistsFn: func(s string) bool {
switch s {
case "samsung-unified-driver", "samsung-unified-driver-common",
"samsung-unified-driver-printer", "samsung-unified-driver-scanner":
return false
case "cups", "ghostscript", "libxml2-legacy", "libusb-compat", "sane":
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) {
for _, needle := range query.Needles {
switch needle {
case "samsung-unified-driver", "samsung-unified-driver-common",
"samsung-unified-driver-printer", "samsung-unified-driver-scanner":
samsungFn := getFromFile(t, "testdata/samsung-unified-driver.json")
return samsungFn(ctx, query)
}
}
return []aur.Pkg{}, nil
}}
tests := []struct {
name string
targets []string
wantPkgCount int
wantContains []string
wantNotContain []string
}{
{
name: "metapackage samsung-unified-driver pulls printer and scanner",
targets: []string{"samsung-unified-driver"},
wantPkgCount: 4,
wantContains: []string{
"samsung-unified-driver",
"samsung-unified-driver-printer",
"samsung-unified-driver-scanner",
"samsung-unified-driver-common",
},
},
{
name: "printer alone pulls common",
targets: []string{"samsung-unified-driver-printer"},
wantPkgCount: 2,
wantContains: []string{"samsung-unified-driver-printer", "samsung-unified-driver-common"},
wantNotContain: []string{"samsung-unified-driver-scanner"},
},
{
name: "scanner alone pulls common",
targets: []string{"samsung-unified-driver-scanner"},
wantPkgCount: 2,
wantContains: []string{"samsung-unified-driver-scanner", "samsung-unified-driver-common"},
wantNotContain: []string{"samsung-unified-driver-printer"},
},
{
name: "common alone",
targets: []string{"samsung-unified-driver-common"},
wantPkgCount: 1,
wantContains: []string{"samsung-unified-driver-common"},
},
}
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.TopoSortedLayers(nil)
allPkgs := make(map[string]bool)
for _, layer := range layers {
for pkg := range layer {
allPkgs[pkg] = true
}
}
require.Equal(t, tt.wantPkgCount, len(allPkgs))
for _, pkg := range tt.wantContains {
require.True(t, allPkgs[pkg], "expected package %s not found", pkg)
}
for _, pkg := range tt.wantNotContain {
require.False(t, allPkgs[pkg], "unexpected package %s found", pkg)
}
})
}
t.Run("split package internal deps resolved correctly", 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, []string{"samsung-unified-driver"})
require.NoError(t, err)
layers := got.TopoSortedLayers(nil)
lastLayer := layers[len(layers)-1]
require.Contains(t, lastLayer, "samsung-unified-driver-common")
for _, layer := range layers {
for _, info := range layer {
require.NotNil(t, info.AURBase)
require.Equal(t, "samsung-unified-driver", *info.AURBase)
}
}
})
}
// TestGrapher_SplitPackages_NX tests independent split packages like nxproxy and nxagent.
func TestGrapher_SplitPackages_NX(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 "nxproxy", "nxagent", "nx-x11", "libxcomp":
return nil
case "libjpeg-turbo":
return &mock.Package{PName: "libjpeg-turbo", PVersion: "2.1.5-1", PDB: mock.NewDB("extra")}
case "libpng":
return &mock.Package{PName: "libpng", PVersion: "1.6.39-1", PDB: mock.NewDB("extra")}
case "gcc-libs":
return &mock.Package{PName: "gcc-libs", PVersion: "13.2.1-1", PDB: mock.NewDB("core")}
case "libxml2":
return &mock.Package{PName: "libxml2", PVersion: "2.11.0-1", PDB: mock.NewDB("extra")}
case "xkeyboard-config":
return &mock.Package{PName: "xkeyboard-config", PVersion: "2.38-1", PDB: mock.NewDB("extra")}
case "xorg-xkbcomp":
return &mock.Package{PName: "xorg-xkbcomp", PVersion: "1.4.6-1", PDB: mock.NewDB("extra")}
case "libxfont2":
return &mock.Package{PName: "libxfont2", PVersion: "2.0.6-1", PDB: mock.NewDB("extra")}
case "libxinerama":
return &mock.Package{PName: "libxinerama", PVersion: "1.1.5-1", PDB: mock.NewDB("extra")}
case "xorg-font-util":
return &mock.Package{PName: "xorg-font-util", PVersion: "1.4.0-1", PDB: mock.NewDB("extra")}
case "pixman":
return &mock.Package{PName: "pixman", PVersion: "0.42.2-1", PDB: mock.NewDB("extra")}
case "libxrandr":
return &mock.Package{PName: "libxrandr", PVersion: "1.5.3-1", PDB: mock.NewDB("extra")}
case "libxtst":
return &mock.Package{PName: "libxtst", PVersion: "1.2.4-1", PDB: mock.NewDB("extra")}
case "libxcomposite":
return &mock.Package{PName: "libxcomposite", PVersion: "0.4.6-1", PDB: mock.NewDB("extra")}
case "libxpm":
return &mock.Package{PName: "libxpm", PVersion: "3.5.16-1", PDB: mock.NewDB("extra")}
case "libxdamage":
return &mock.Package{PName: "libxdamage", PVersion: "1.1.6-1", PDB: mock.NewDB("extra")}
case "libtirpc":
return &mock.Package{PName: "libtirpc", PVersion: "1.3.3-1", PDB: mock.NewDB("extra")}
case "xorgproto":
return &mock.Package{PName: "xorgproto", PVersion: "2023.2-1", PDB: mock.NewDB("extra")}
case "imake":
return &mock.Package{PName: "imake", PVersion: "1.0.9-1", PDB: mock.NewDB("extra")}
}
panic("implement me " + s)
},
LocalSatisfierExistsFn: func(s string) bool {
switch s {
case "nxproxy", "nxagent", "nx-x11", "libxcomp":
return false
case "libjpeg-turbo", "libpng", "gcc-libs", "libxml2",
"xkeyboard-config", "xorg-xkbcomp", "libxfont2", "libxinerama",
"xorg-font-util", "pixman", "libxrandr", "libxtst",
"libxcomposite", "libxpm", "libxdamage", "libtirpc",
"xorgproto", "imake":
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) {
for _, needle := range query.Needles {
switch needle {
case "nxproxy", "nxagent", "nx-x11", "libxcomp":
nxFn := getFromFile(t, "testdata/nx.json")
return nxFn(ctx, query)
}
}
return []aur.Pkg{}, nil
}}
installInfos := map[string]*InstallInfo{
"nxproxy exp": {Source: AUR, Reason: Explicit, Version: "3.5.99.27-3", AURBase: ptrString("nx")},
"nxagent exp": {Source: AUR, Reason: Explicit, Version: "3.5.99.27-3", AURBase: ptrString("nx")},
"nx-x11 dep": {Source: AUR, Reason: Dep, Version: "3.5.99.27-3", AURBase: ptrString("nx")},
"libxcomp dep": {Source: AUR, Reason: Dep, Version: "3.5.99.27-3", AURBase: ptrString("nx")},
"libxcomp exp": {Source: AUR, Reason: Explicit, Version: "3.5.99.27-3", AURBase: ptrString("nx")},
}
tests := []struct {
name string
targets []string
wantLayers []map[string]*InstallInfo
}{
{
name: "nxproxy independently",
targets: []string{"nxproxy"},
wantLayers: []map[string]*InstallInfo{
{"nxproxy": installInfos["nxproxy exp"]},
{"libxcomp": installInfos["libxcomp dep"]},
},
},
{
name: "nxagent independently - has more deps",
targets: []string{"nxagent"},
wantLayers: []map[string]*InstallInfo{
{"nxagent": installInfos["nxagent exp"]},
{"nx-x11": installInfos["nx-x11 dep"]},
{"libxcomp": installInfos["libxcomp dep"]},
},
},
{
name: "both nxproxy and nxagent",
targets: []string{"nxproxy", "nxagent"},
wantLayers: []map[string]*InstallInfo{
{"nxproxy": installInfos["nxproxy exp"], "nxagent": installInfos["nxagent exp"]},
{"nx-x11": installInfos["nx-x11 dep"]},
{"libxcomp": installInfos["libxcomp dep"]},
},
},
{
name: "libxcomp independently",
targets: []string{"libxcomp"},
wantLayers: []map[string]*InstallInfo{
{"libxcomp": installInfos["libxcomp exp"]},
},
},
}
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.TopoSortedLayers(nil)
require.EqualValues(t, tt.wantLayers, layers, layers)
})
}
t.Run("split packages share AURBase but can be installed independently", func(t *testing.T) {
g := NewGrapher(mockDB, mockAUR, false, true, false, false, false,
text.NewLogger(io.Discard, io.Discard, &os.File{}, true, "test"))
got1, err := g.GraphFromTargets(context.Background(), nil, []string{"nxproxy"})
require.NoError(t, err)
layers1 := got1.TopoSortedLayers(nil)
got2, err := g.GraphFromTargets(context.Background(), nil, []string{"nxagent"})
require.NoError(t, err)
layers2 := got2.TopoSortedLayers(nil)
require.Equal(t, "nx", *layers1[0]["nxproxy"].AURBase)
require.Equal(t, "nx", *layers2[0]["nxagent"].AURBase)
totalPkgs1 := 0
for _, layer := range layers1 {
totalPkgs1 += len(layer)
}
totalPkgs2 := 0
for _, layer := range layers2 {
totalPkgs2 += len(layer)
}
require.Equal(t, 2, totalPkgs1)
require.Equal(t, 3, totalPkgs2)
})
}
// TestGrapher_SplitPackages_ReversedOrder tests that split packages resolve
// correctly regardless of the order they are specified in.
func TestGrapher_SplitPackages_ReversedOrder(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 "nxproxy", "nxagent", "nx-x11", "libxcomp":
return nil
default:
return &mock.Package{PName: s, PVersion: "1.0.0-1", PDB: mock.NewDB("extra")}
}
},
LocalSatisfierExistsFn: func(s string) bool {
switch s {
case "nxproxy", "nxagent", "nx-x11", "libxcomp":
return false
default:
return true
}
},
LocalPackageFn: func(string) mock.IPackage { return nil },
}
mockAUR := &mockaur.MockAUR{GetFn: func(ctx context.Context, query *aurc.Query) ([]aur.Pkg, error) {
for _, needle := range query.Needles {
switch needle {
case "nxproxy", "nxagent", "nx-x11", "libxcomp":
nxFn := getFromFile(t, "testdata/nx.json")
return nxFn(ctx, query)
}
}
return []aur.Pkg{}, nil
}}
t.Run("nxproxy nxagent order", 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, []string{"nxproxy", "nxagent"})
require.NoError(t, err)
layers1 := got.TopoSortedLayers(nil)
require.Contains(t, layers1[0], "nxproxy")
require.Contains(t, layers1[0], "nxagent")
})
t.Run("nxagent nxproxy reversed order", 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, []string{"nxagent", "nxproxy"})
require.NoError(t, err)
layers2 := got.TopoSortedLayers(nil)
require.Contains(t, layers2[0], "nxproxy")
require.Contains(t, layers2[0], "nxagent")
})
}
// TestGrapher_MultipleInstallInfo ensures that when the same package appears as
// both explicit target and dependency, the explicit reason takes precedence.
func TestGrapher_MultipleInstallInfo(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 "samsung-unified-driver", "samsung-unified-driver-common",
"samsung-unified-driver-printer", "samsung-unified-driver-scanner":
return nil
default:
return &mock.Package{PName: s, PVersion: "1.0.0-1", PDB: mock.NewDB("extra")}
}
},
LocalSatisfierExistsFn: func(s string) bool {
switch s {
case "samsung-unified-driver", "samsung-unified-driver-common",
"samsung-unified-driver-printer", "samsung-unified-driver-scanner":
return false
default:
return true
}
},
LocalPackageFn: func(string) mock.IPackage { return nil },
}
mockAUR := &mockaur.MockAUR{GetFn: func(ctx context.Context, query *aurc.Query) ([]aur.Pkg, error) {
for _, needle := range query.Needles {
switch needle {
case "samsung-unified-driver", "samsung-unified-driver-common",
"samsung-unified-driver-printer", "samsung-unified-driver-scanner":
samsungFn := getFromFile(t, "testdata/samsung-unified-driver.json")
return samsungFn(ctx, query)
}
}
return []aur.Pkg{}, nil
}}
t.Run("explicit target takes precedence over dependency", 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,
[]string{"samsung-unified-driver", "samsung-unified-driver-common"})
require.NoError(t, err)
layers := got.TopoSortedLayers(nil)
found := false
for _, layer := range layers {
if info, ok := layer["samsung-unified-driver-common"]; ok {
found = true
require.Equal(t, Explicit, info.Reason,
"explicit target should have Explicit reason, not Dep")
}
}
require.True(t, found, "samsung-unified-driver-common should be in layers")
})
}
// TestGrapher_VersionedDependencies tests proper handling of versioned dependencies.
func TestGrapher_VersionedDependencies(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 "versioned-pkg", "dep-pkg", "dep-pkg>=2.0.0":
return nil
}
panic(fmt.Sprintf("implement me: %s", s))
},
LocalSatisfierExistsFn: func(s string) bool {
switch s {
case "versioned-pkg", "dep-pkg", "dep-pkg>=2.0.0":
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) {
mockPkgs := map[string]aur.Pkg{
"versioned-pkg": {
Name: "versioned-pkg",
PackageBase: "versioned-pkg",
Version: "1.0.0-1",
Depends: []string{"dep-pkg>=2.0.0"},
},
"dep-pkg": {
Name: "dep-pkg",
PackageBase: "dep-pkg",
Version: "2.5.0-1",
},
}
pkgs := []aur.Pkg{}
for _, needle := range query.Needles {
if pkg, ok := mockPkgs[needle]; ok {
pkgs = append(pkgs, pkg)
}
}
return pkgs, nil
}}
t.Run("versioned dependency satisfied by higher version", 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, []string{"versioned-pkg"})
require.NoError(t, err)
layers := got.TopoSortedLayers(nil)
require.Len(t, layers, 2)
require.Contains(t, layers[0], "versioned-pkg")
require.Contains(t, layers[1], "dep-pkg")
require.Equal(t, "2.5.0-1", layers[1]["dep-pkg"].Version)
})
}
================================================
FILE: pkg/dep/dep_graph_test.go
================================================
//go:build !integration
// +build !integration
package dep
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"os"
"strings"
"testing"
aurc "github.com/Jguer/aur"
alpm "github.com/Jguer/dyalpm"
mapset "github.com/deckarep/golang-set/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.TB, filePath string) mockaur.GetFunc {
t.Helper()
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_findDepsFromAUR_logsRequiredByForMissingDep(t *testing.T) {
mockDB := &mock.DBExecutor{}
mockAUR := &mockaur.MockAUR{GetFn: func(ctx context.Context, query *aurc.Query) ([]aur.Pkg, error) {
return []aur.Pkg{}, nil
}}
var stderr bytes.Buffer
logger := text.NewLogger(io.Discard, &stderr, strings.NewReader(""), true, "test")
g := NewGrapher(mockDB, mockAUR, false, true, false, false, false, logger)
graph := NewGraph()
depString := "missingdep>=1.0"
depName := "missingdep"
require.NoError(t, graph.DependOn("existingNeeds", depName))
toFind := mapset.NewThreadUnsafeSet(depString)
_ = g.findDepsFromAUR(context.Background(), graph, "currentNeeds", toFind)
out := stderr.String()
require.Contains(t, out, "No AUR package found for "+depString+" (required by:")
require.Contains(t, out, "currentNeeds")
require.Contains(t, out, "existingNeeds")
}
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.TopoSortedLayers(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.TopoSortedLayers(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.TopoSortedLayers(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.TopoSortedLayers(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.TopoSortedLayers(nil)
require.EqualValues(t, tt.wantLayers, layers, layers)
})
}
}
func TestGrapher_GraphFromTargets_TargetNotFound(t *testing.T) {
mockDB := &mock.DBExecutor{
SyncSatisfierFn: func(string) mock.IPackage { return nil },
PackagesFromGroupFn: func(string) []mock.IPackage { return nil },
LocalPackageFn: func(string) mock.IPackage { return nil },
LocalSatisfierExistsFn: func(string) bool { return false },
}
mockAUR := &mockaur.MockAUR{GetFn: func(ctx context.Context, query *aurc.Query) ([]aur.Pkg, error) {
ok := aur.Pkg{
Name: "okpkg",
PackageBase: "okpkg",
Version: "1.0.0",
}
switch query.By {
case aurc.Name:
// Return only packages that exist.
pkgs := make([]aur.Pkg, 0, len(query.Needles))
for _, needle := range query.Needles {
if needle == ok.Name {
pkgs = append(pkgs, ok)
}
}
return pkgs, nil
case aurc.Provides:
// Provider lookup is done per-target.
if len(query.Needles) > 0 && query.Needles[0] == ok.Name {
return []aur.Pkg{ok}, nil
}
return []aur.Pkg{}, nil
default:
return []aur.Pkg{}, nil
}
}}
g := NewGrapher(mockDB, mockAUR,
false, true, true, true, false,
text.NewLogger(io.Discard, io.Discard, &os.File{}, true, "test"))
t.Run("returns error when all targets are missing", func(t *testing.T) {
_, err := g.GraphFromTargets(context.Background(), nil, []string{"missing1", "missing2"})
require.Error(t, err)
var targetNotFound *aur.ErrTargetNotFound
require.ErrorAs(t, err, &targetNotFound)
})
t.Run("does not error when at least one target is found", func(t *testing.T) {
got, err := g.GraphFromTargets(context.Background(), nil, []string{"missing1", "okpkg"})
require.NoError(t, err)
layers := got.TopoSortedLayers(nil)
require.EqualValues(t, []map[string]*InstallInfo{
{
"okpkg": {
Source: AUR,
Reason: Explicit,
Version: "1.0.0",
AURBase: ptrString("okpkg"),
},
},
}, layers, layers)
})
}
// TestGrapher_GraphFromAUR_SplitPkgInternalDeps tests split packages where
// packages from the same base depend on each other (like gstreamer-git).
func TestGrapher_GraphFromAUR_SplitPkgInternalDeps(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 {
// AUR packages and versioned AUR deps return nil
case "gstreamer-git", "gst-plugins-base-libs-git", "gst-plugins-good-git",
"gstreamer-git=1.24.0.r37-1", "gst-plugins-base-libs-git=1.24.0.r37-1":
return nil
case "libxml2":
return &mock.Package{
PName: "libxml2",
PVersion: "2.12.0-1",
PDB: mock.NewDB("core"),
}
case "glib2":
return &mock.Package{
PName: "glib2",
PVersion: "2.78.0-1",
PDB: mock.NewDB("core"),
}
case "orc":
return &mock.Package{
PName: "orc",
PVersion: "0.4.34-1",
PDB: mock.NewDB("extra"),
}
case "libxv":
return &mock.Package{
PName: "libxv",
PVersion: "1.0.12-1",
PDB: mock.NewDB("extra"),
}
case "iso-codes":
return &mock.Package{
PName: "iso-codes",
PVersion: "4.15.0-1",
PDB: mock.NewDB("extra"),
}
case "libpulse":
return &mock.Package{
PName: "libpulse",
PVersion: "16.1-1",
PDB: mock.NewDB("extra"),
}
case "wavpack":
return &mock.Package{
PName: "wavpack",
PVersion: "5.6.0-1",
PDB: mock.NewDB("extra"),
}
}
panic("implement me " + s)
},
LocalSatisfierExistsFn: func(s string) bool {
switch s {
case "gstreamer-git", "gstreamer-git=1.24.0.r37-1",
"gst-plugins-base-libs-git", "gst-plugins-base-libs-git=1.24.0.r37-1",
"gst-plugins-good-git":
return false
case "libxml2", "glib2", "orc", "libxv", "iso-codes", "libpulse", "wavpack",
"git", "meson", "ninja": // makedepends
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 len(query.Needles) > 0 {
for _, needle := range query.Needles {
if needle == "gstreamer-git" || needle == "gst-plugins-base-libs-git" || needle == "gst-plugins-good-git" {
gstFn := getFromFile(t, "testdata/gstreamer-git.json")
return gstFn(ctx, query)
}
}
}
return []aur.Pkg{}, nil // Return empty for unknown packages
}}
installInfos := map[string]*InstallInfo{
"gstreamer-git exp": {
Source: AUR,
Reason: Explicit,
Version: "1.24.0.r37-1",
AURBase: ptrString("gstreamer-git"),
},
"gstreamer-git dep": {
Source: AUR,
Reason: Dep,
Version: "1.24.0.r37-1",
AURBase: ptrString("gstreamer-git"),
},
"gst-plugins-base-libs-git exp": {
Source: AUR,
Reason: Explicit,
Version: "1.24.0.r37-1",
AURBase: ptrString("gstreamer-git"),
},
"gst-plugins-base-libs-git dep": {
Source: AUR,
Reason: Dep,
Version: "1.24.0.r37-1",
AURBase: ptrString("gstreamer-git"),
},
"gst-plugins-good-git exp": {
Source: AUR,
Reason: Explicit,
Version: "1.24.0.r37-1",
AURBase: ptrString("gstreamer-git"),
},
}
tests := []struct {
name string
targets []string
wantLayers []map[string]*InstallInfo
wantErr bool
}{
{
name: "gst-plugins-good-git pulls in base libs and gstreamer",
targets: []string{"gst-plugins-good-git"},
wantLayers: []map[string]*InstallInfo{
{"gst-plugins-good-git": installInfos["gst-plugins-good-git exp"]},
{"gst-plugins-base-libs-git": installInfos["gst-plugins-base-libs-git dep"]},
{"gstreamer-git": installInfos["gstreamer-git dep"]},
},
wantErr: false,
},
{
name: "gst-plugins-base-libs-git pulls in gstreamer",
targets: []string{"gst-plugins-base-libs-git"},
wantLayers: []map[string]*InstallInfo{
{"gst-plugins-base-libs-git": installInfos["gst-plugins-base-libs-git exp"]},
{"gstreamer-git": installInfos["gstreamer-git dep"]},
},
wantErr: false,
},
{
name: "explicit gstreamer-git with gst-plugins-good-git",
targets: []string{"gstreamer-git", "gst-plugins-good-git"},
wantLayers: []map[string]*InstallInfo{
{"gst-plugins-good-git": installInfos["gst-plugins-good-git exp"]},
{"gst-plugins-base-libs-git": installInfos["gst-plugins-base-libs-git dep"]},
{"gstreamer-git": installInfos["gstreamer-git exp"]},
},
wantErr: false,
},
{
name: "all three packages explicitly",
targets: []string{"gstreamer-git", "gst-plugins-base-libs-git", "gst-plugins-good-git"},
wantLayers: []map[string]*InstallInfo{
{"gst-plugins-good-git": installInfos["gst-plugins-good-git exp"]},
{"gst-plugins-base-libs-git": installInfos["gst-plugins-base-libs-git exp"]},
{"gstreamer-git": installInfos["gstreamer-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.TopoSortedLayers(nil)
require.EqualValues(t, tt.wantLayers, layers, layers)
})
}
}
// TestGrapher_GraphFromAUR_CheckDeps tests packages with CheckDepends.
func TestGrapher_GraphFromAUR_CheckDeps(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 "python-pydantic":
return nil
case "python":
return &mock.Package{
PName: "python",
PVersion: "3.11.0-1",
PDB: mock.NewDB("core"),
}
case "python-typing-extensions":
return &mock.Package{
PName: "python-typing-extensions",
PVersion: "4.8.0-1",
PDB: mock.NewDB("extra"),
}
case "python-build":
return &mock.Package{
PName: "python-build",
PVersion: "1.0.0-1",
PDB: mock.NewDB("extra"),
}
case "python-installer":
return &mock.Package{
PName: "python-installer",
PVersion: "0.7.0-1",
PDB: mock.NewDB("extra"),
}
case "python-pytest":
return &mock.Package{
PName: "python-pytest",
PVersion: "7.4.0-1",
PDB: mock.NewDB("extra"),
}
case "python-pytest-mock":
return &mock.Package{
PName: "python-pytest-mock",
PVersion: "3.11.0-1",
PDB: mock.NewDB("extra"),
}
}
panic("implement me " + s)
},
LocalSatisfierExistsFn: func(s string) bool {
switch s {
case "python-pydantic",
"python-pytest", "python-pytest-mock": // check deps not installed
return false
case "python", "python-typing-extensions", "python-build", "python-installer":
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 len(query.Needles) > 0 && query.Needles[0] == "python-pydantic" {
pydanticFn := getFromFile(t, "testdata/python-pydantic.json")
return pydanticFn(ctx, query)
}
return []aur.Pkg{}, nil // Return empty for unknown packages
}}
t.Run("with check deps enabled", 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, []string{"python-pydantic"})
require.NoError(t, err)
layers := got.TopoSortedLayers(nil)
// Should have the main package and its check deps
require.Len(t, layers, 2)
require.Contains(t, layers[0], "python-pydantic")
// Check deps should be in the second layer
require.Contains(t, layers[1], "python-pytest")
require.Contains(t, layers[1], "python-pytest-mock")
})
t.Run("with check deps disabled", func(t *testing.T) {
g := NewGrapher(mockDB, mockAUR,
false, true, false, true, false, // noCheckDeps = true
text.NewLogger(io.Discard, io.Discard, &os.File{}, true, "test"))
got, err := g.GraphFromTargets(context.Background(), nil, []string{"python-pydantic"})
require.NoError(t, err)
layers := got.TopoSortedLayers(nil)
// Should only have the main package (no check deps)
require.Len(t, layers, 1)
require.Contains(t, layers[0], "python-pydantic")
})
}
// TestGrapher_GraphFromAUR_VirtualProvides tests packages that provide virtual packages
// (like mesa-git providing vulkan-driver, opengl-driver).
func TestGrapher_GraphFromAUR_VirtualProvides(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 "mesa-git":
return nil
case "libdrm":
return &mock.Package{
PName: "libdrm",
PVersion: "2.4.117-1",
PDB: mock.NewDB("core"),
}
case "vulkan-icd-loader":
return &mock.Package{
PName: "vulkan-icd-loader",
PVersion: "1.3.268-1",
PDB: mock.NewDB("extra"),
}
case "vulkan-radeon":
return &mock.Package{
PName: "vulkan-radeon",
PVersion: "23.3.0-1",
PDB: mock.NewDB("extra"),
PProvides: mock.DependList{
Depends: []alpm.Depend{
{Name: "vulkan-driver", Version: "", Mod: alpm.DepModAny},
},
},
}
}
// Most mesa deps are already installed
switch s {
case "libxxf86vm", "libxdamage", "libxshmfence", "libelf", "libunwind",
"libglvnd", "wayland", "lm_sensors", "zstd", "expat":
return &mock.Package{
PName: s,
PVersion: "1.0.0-1",
PDB: mock.NewDB("extra"),
}
}
panic("implement me " + s)
},
LocalSatisfierExistsFn: func(s string) bool {
switch s {
case "mesa-git", "vulkan-driver", "opengl-driver":
return false
case "libdrm", "libxxf86vm", "libxdamage", "libxshmfence", "libelf",
"libunwind", "libglvnd", "wayland", "lm_sensors", "vulkan-icd-loader",
"zstd", "expat",
"git", "meson", "ninja", "llvm", "clang": // makedepends
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 len(query.Needles) > 0 && query.Needles[0] == "mesa-git" {
mesaFn := getFromFile(t, "testdata/mesa-git.json")
return mesaFn(ctx, query)
}
return []aur.Pkg{}, nil // Return empty for unknown packages
}}
t.Run("mesa-git provides vulkan-driver and opengl-driver", 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, []string{"mesa-git"})
require.NoError(t, err)
layers := got.TopoSortedLayers(nil)
require.Len(t, layers, 1)
require.Contains(t, layers[0], "mesa-git")
require.Equal(t, "24.0.0.r1234-1", layers[0]["mesa-git"].Version)
require.Equal(t, "mesa-git", *layers[0]["mesa-git"].AURBase)
})
}
================================================
FILE: pkg/dep/mock/aur.go
================================================
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")
}
================================================
FILE: pkg/dep/target_handler.go
================================================
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()
}
================================================
FILE: pkg/dep/testdata/android-sdk.json
================================================
[
{"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"]}
]
================================================
FILE: pkg/dep/testdata/aws-cli-git.json
================================================
[
{"ID":1266542,"Name":"aws-cli-git","PackageBaseID":75029,"PackageBase":"aws-cli-git","Version":"1.27.145.r11217.g5885ee4dc-1","Description":"Universal Command Line Interface for Amazon Web Services awscli","URL":"https://github.com/aws/aws-cli","NumVotes":8,"Popularity":0.0,"OutOfDate":1710077383,"Maintainer":null,"Submitter":"flaccid","FirstSubmitted":1383449501,"LastModified":1685666227,"URLPath":"/cgit/aur.git/snapshot/aws-cli-git.tar.gz","Depends":["python","python-botocore>=1.19.35","python-docutils>=0.10","python-rsa>=3.1.2","python-s3transfer>=0.3.0","python-yaml>=3.10","python-colorama>=0.2.5","python-tox>=2.3.1","python-nose>=1.3.7","python-mock>=1.3.0","python-wheel>=0.24.0","python-dateutil>=2.1","python-sphinx>=1.1.3"],"MakeDepends":["python","python-distribute","git"],"Conflicts":["python2-aws-cli","python-aws-cli","aws-cli"],"Provides":["aws-cli=1.27.145"],"License":["Apache"]}
]
================================================
FILE: pkg/dep/testdata/clion.json
================================================
[
{"ID":1937213,"Name":"clion","PackageBaseID":92202,"PackageBase":"clion","Version":"2025.3.1.1-1","Description":"Cross-platform IDE for C and C++ from JetBrains.","URL":"http://www.jetbrains.com/clion","NumVotes":239,"Popularity":1.760009,"OutOfDate":null,"Maintainer":"Zrax","Submitter":"rdoursenaud","FirstSubmitted":1429046635,"LastModified":1768325120,"URLPath":"/cgit/aur.git/snapshot/clion.tar.gz","Depends":["libdbusmenu-glib"],"MakeDepends":["rsync"],"OptDepends":["clion-jre","clion-cmake","clion-gdb","clion-lldb","java-runtime","cmake","gdb","lldb","gcc","clang","gtest","python","doxygen"],"License":["custom"]},
{"ID":1937214,"Name":"clion-jre","PackageBaseID":92202,"PackageBase":"clion","Version":"2025.3.1.1-1","Description":"JBR (JetBrains Runtime) for CLion - aass/JVM bundle based on OpenJDK","URL":"http://www.jetbrains.com/clion","NumVotes":50,"Popularity":0.5,"OutOfDate":null,"Maintainer":"Zrax","Submitter":"rdoursenaud","FirstSubmitted":1429046635,"LastModified":1768325120,"URLPath":"/cgit/aur.git/snapshot/clion.tar.gz","Depends":["glibc","gcc-libs"],"MakeDepends":["rsync"],"Provides":["java-runtime"],"License":["custom"]},
{"ID":1937215,"Name":"clion-cmake","PackageBaseID":92202,"PackageBase":"clion","Version":"2025.3.1.1-1","Description":"CMake for CLion IDE","URL":"http://www.jetbrains.com/clion","NumVotes":30,"Popularity":0.3,"OutOfDate":null,"Maintainer":"Zrax","Submitter":"rdoursenaud","FirstSubmitted":1429046635,"LastModified":1768325120,"URLPath":"/cgit/aur.git/snapshot/clion.tar.gz","Depends":["glibc"],"MakeDepends":["rsync"],"Provides":["cmake"],"Conflicts":["cmake"],"License":["custom"]},
{"ID":1937216,"Name":"clion-gdb","PackageBaseID":92202,"PackageBase":"clion","Version":"2025.3.1.1-1","Description":"GDB for CLion IDE","URL":"http://www.jetbrains.com/clion","NumVotes":25,"Popularity":0.25,"OutOfDate":null,"Maintainer":"Zrax","Submitter":"rdoursenaud","FirstSubmitted":1429046635,"LastModified":1768325120,"URLPath":"/cgit/aur.git/snapshot/clion.tar.gz","Depends":["glibc","python"],"MakeDepends":["rsync"],"Provides":["gdb"],"Conflicts":["gdb"],"License":["custom"]},
{"ID":1937217,"Name":"clion-lldb","PackageBaseID":92202,"PackageBase":"clion","Version":"2025.3.1.1-1","Description":"LLDB for CLion IDE","URL":"http://www.jetbrains.com/clion","NumVotes":20,"Popularity":0.2,"OutOfDate":null,"Maintainer":"Zrax","Submitter":"rdoursenaud","FirstSubmitted":1429046635,"LastModified":1768325120,"URLPath":"/cgit/aur.git/snapshot/clion.tar.gz","Depends":["glibc","python"],"MakeDepends":["rsync"],"Provides":["lldb"],"Conflicts":["lldb"],"License":["custom"]}
]
================================================
FILE: pkg/dep/testdata/gstreamer-git.json
================================================
[
{"ID":1000001,"Name":"gstreamer-git","PackageBaseID":100001,"PackageBase":"gstreamer-git","Version":"1.24.0.r37-1","Description":"GStreamer open-source multimedia framework core library (git version)","URL":"https://gstreamer.freedesktop.org/","NumVotes":10,"Popularity":0.5,"OutOfDate":null,"Maintainer":"testmaint","Submitter":"testmaint","FirstSubmitted":1600000000,"LastModified":1700000000,"URLPath":"/cgit/aur.git/snapshot/gstreamer-git.tar.gz","Depends":["libxml2","glib2"],"MakeDepends":["git","meson","ninja"],"Provides":["gstreamer=1.24.0"],"Conflicts":["gstreamer"],"License":["LGPL"]},
{"ID":1000002,"Name":"gst-plugins-base-libs-git","PackageBaseID":100001,"PackageBase":"gstreamer-git","Version":"1.24.0.r37-1","Description":"GStreamer base plugins libraries (git version)","URL":"https://gstreamer.freedesktop.org/","NumVotes":10,"Popularity":0.5,"OutOfDate":null,"Maintainer":"testmaint","Submitter":"testmaint","FirstSubmitted":1600000000,"LastModified":1700000000,"URLPath":"/cgit/aur.git/snapshot/gstreamer-git.tar.gz","Depends":["gstreamer-git=1.24.0.r37-1","orc","libxv","iso-codes"],"MakeDepends":["git","meson","ninja"],"Provides":["gst-plugins-base-libs=1.24.0"],"Conflicts":["gst-plugins-base-libs"],"License":["LGPL"]},
{"ID":1000003,"Name":"gst-plugins-good-git","PackageBaseID":100001,"PackageBase":"gstreamer-git","Version":"1.24.0.r37-1","Description":"GStreamer good plugins (git version)","URL":"https://gstreamer.freedesktop.org/","NumVotes":10,"Popularity":0.5,"OutOfDate":null,"Maintainer":"testmaint","Submitter":"testmaint","FirstSubmitted":1600000000,"LastModified":1700000000,"URLPath":"/cgit/aur.git/snapshot/gstreamer-git.tar.gz","Depends":["gst-plugins-base-libs-git=1.24.0.r37-1","libpulse","wavpack"],"MakeDepends":["git","meson","ninja"],"Provides":["gst-plugins-good=1.24.0"],"Conflicts":["gst-plugins-good"],"License":["LGPL"]}
]
================================================
FILE: pkg/dep/testdata/jellyfin-server.json
================================================
[
{"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"]}
]
================================================
FILE: pkg/dep/testdata/jellyfin-web.json
================================================
[
{"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"]}
]
================================================
FILE: pkg/dep/testdata/jellyfin.json
================================================
[
{"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"]}
]
================================================
FILE: pkg/dep/testdata/liri-desktop-git.json
================================================
[
{"ID":2000001,"Name":"liri-desktop-git","PackageBaseID":200001,"PackageBase":"liri-desktop-git","Version":"0.1.0.r50-1","Description":"Liri Desktop metapackage","URL":"https://liri.io","NumVotes":10,"Popularity":0.1,"OutOfDate":null,"Maintainer":"plfiorini","Submitter":"plfiorini","FirstSubmitted":1494775706,"LastModified":1672746933,"URLPath":"/cgit/aur.git/snapshot/liri-desktop-git.tar.gz","Depends":["liri-shell-git","liri-settings-git","fluid-git","libliri-git"],"MakeDepends":["git"],"License":["GPL3"]},
{"ID":2000002,"Name":"liri-shell-git","PackageBaseID":200002,"PackageBase":"liri-shell-git","Version":"0.9.0.r100-1","Description":"Liri Shell - Wayland compositor","URL":"https://liri.io","NumVotes":8,"Popularity":0.08,"OutOfDate":null,"Maintainer":"plfiorini","Submitter":"plfiorini","FirstSubmitted":1494775706,"LastModified":1672746933,"URLPath":"/cgit/aur.git/snapshot/liri-shell-git.tar.gz","Depends":["fluid-git","libliri-git","qt5-wayland","wayland"],"MakeDepends":["git","liri-cmake-shared-git","qt5-tools"],"License":["GPL3"]},
{"ID":2000003,"Name":"liri-settings-git","PackageBaseID":200003,"PackageBase":"liri-settings-git","Version":"0.9.0.r50-1","Description":"Liri Settings","URL":"https://liri.io","NumVotes":6,"Popularity":0.06,"OutOfDate":null,"Maintainer":"plfiorini","Submitter":"plfiorini","FirstSubmitted":1494775706,"LastModified":1672746933,"URLPath":"/cgit/aur.git/snapshot/liri-settings-git.tar.gz","Depends":["fluid-git","libliri-git","qt5-declarative"],"MakeDepends":["git","liri-cmake-shared-git","qt5-tools"],"License":["GPL3"]},
{"ID":1191231,"Name":"libliri-git","PackageBaseID":122343,"PackageBase":"libliri-git","Version":"r183.5ebe982-1","Description":"Utilities for Liri Quick applications","URL":"https://liri.io","NumVotes":3,"Popularity":0.0,"OutOfDate":1751932023,"Maintainer":"plfiorini","Submitter":"plfiorini","FirstSubmitted":1494775706,"LastModified":1672746933,"URLPath":"/cgit/aur.git/snapshot/libliri-git.tar.gz","Depends":["qt5-declarative"],"MakeDepends":["git","liri-cmake-shared-git"],"Conflicts":["libliri"],"Provides":["libliri"],"Replaces":["libliri"],"License":["LGPL3"]},
{"ID":2000004,"Name":"fluid-git","PackageBaseID":200004,"PackageBase":"fluid-git","Version":"1.3.0.r50-1","Description":"Fluid - Material Design for Qt Quick","URL":"https://liri.io","NumVotes":15,"Popularity":0.15,"OutOfDate":null,"Maintainer":"plfiorini","Submitter":"plfiorini","FirstSubmitted":1494775706,"LastModified":1672746933,"URLPath":"/cgit/aur.git/snapshot/fluid-git.tar.gz","Depends":["qt5-declarative","qt5-quickcontrols2","qt5-svg","qt5-graphicaleffects"],"MakeDepends":["git","liri-cmake-shared-git","qt5-tools"],"Conflicts":["fluid"],"Provides":["fluid"],"License":["MPL2"]},
{"ID":2000005,"Name":"liri-cmake-shared-git","PackageBaseID":200005,"PackageBase":"liri-cmake-shared-git","Version":"1.0.0.r20-1","Description":"Shared CMake functions and macros for Liri","URL":"https://liri.io","NumVotes":5,"Popularity":0.05,"OutOfDate":null,"Maintainer":"plfiorini","Submitter":"plfiorini","FirstSubmitted":1494775706,"LastModified":1672746933,"URLPath":"/cgit/aur.git/snapshot/liri-cmake-shared-git.tar.gz","Depends":["cmake"],"MakeDepends":["git"],"Conflicts":["liri-cmake-shared"],"Provides":["liri-cmake-shared"],"License":["BSD"]}
]
================================================
FILE: pkg/dep/testdata/mesa-git.json
================================================
[
{"ID":3000001,"Name":"mesa-git","PackageBaseID":300001,"PackageBase":"mesa-git","Version":"24.0.0.r1234-1","Description":"Open-source implementation of the OpenGL specification (git version)","URL":"https://www.mesa3d.org/","NumVotes":100,"Popularity":3.0,"OutOfDate":null,"Maintainer":"testmaint","Submitter":"testmaint","FirstSubmitted":1600000000,"LastModified":1700000000,"URLPath":"/cgit/aur.git/snapshot/mesa-git.tar.gz","Depends":["libdrm","libxxf86vm","libxdamage","libxshmfence","libelf","libunwind","libglvnd","wayland","lm_sensors","vulkan-icd-loader","zstd","expat"],"MakeDepends":["git","meson","ninja","llvm","clang"],"Provides":["mesa=24.0.0","vulkan-intel=24.0.0","vulkan-radeon=24.0.0","vulkan-driver","opengl-driver","mesa-libgl"],"Conflicts":["mesa","vulkan-intel","vulkan-radeon","mesa-libgl"],"License":["MIT"]}
]
================================================
FILE: pkg/dep/testdata/nx.json
================================================
[
{"ID":1313798,"Name":"libxcomp","PackageBaseID":177611,"PackageBase":"nx","Version":"3.5.99.27-3","Description":"NX X compression library","URL":"https://arctica-project.org","NumVotes":4,"Popularity":1e-6,"OutOfDate":null,"Maintainer":"harrietobrien","Submitter":"arojas","FirstSubmitted":1648839853,"LastModified":1693834449,"URLPath":"/cgit/aur.git/snapshot/nx.tar.gz","Depends":["libjpeg-turbo","libpng","gcc-libs"],"MakeDepends":["libjpeg-turbo","libpng","gcc-libs","libxml2","xkeyboard-config","xorg-xkbcomp","libxfont2","libxinerama","xorg-font-util","pixman","libxrandr","libxtst","libxcomposite","libxpm","libxdamage","xorgproto","imake"],"License":["GPL"]},
{"ID":1313799,"Name":"nxproxy","PackageBaseID":177611,"PackageBase":"nx","Version":"3.5.99.27-3","Description":"NX proxy","URL":"https://arctica-project.org","NumVotes":4,"Popularity":1e-6,"OutOfDate":null,"Maintainer":"harrietobrien","Submitter":"arojas","FirstSubmitted":1648839853,"LastModified":1693834449,"URLPath":"/cgit/aur.git/snapshot/nx.tar.gz","Depends":["libxcomp"],"MakeDepends":["libjpeg-turbo","libpng","gcc-libs","libxml2","xkeyboard-config","xorg-xkbcomp","libxfont2","libxinerama","xorg-font-util","pixman","libxrandr","libxtst","libxcomposite","libxpm","libxdamage","xorgproto","imake"],"License":["GPL"]},
{"ID":1313800,"Name":"nx-x11","PackageBaseID":177611,"PackageBase":"nx","Version":"3.5.99.27-3","Description":"NX-X11 lib for the NX framework","URL":"https://arctica-project.org","NumVotes":4,"Popularity":1e-6,"OutOfDate":null,"Maintainer":"harrietobrien","Submitter":"arojas","FirstSubmitted":1648839853,"LastModified":1693834449,"URLPath":"/cgit/aur.git/snapshot/nx.tar.gz","Depends":["libxcomp"],"MakeDepends":["libjpeg-turbo","libpng","gcc-libs","libxml2","xkeyboard-config","xorg-xkbcomp","libxfont2","libxinerama","xorg-font-util","pixman","libxrandr","libxtst","libxcomposite","libxpm","libxdamage","xorgproto","imake"],"License":["GPL"]},
{"ID":1313801,"Name":"nxagent","PackageBaseID":177611,"PackageBase":"nx","Version":"3.5.99.27-3","Description":"NX X server based on Xnest","URL":"https://arctica-project.org","NumVotes":4,"Popularity":1e-6,"OutOfDate":null,"Maintainer":"harrietobrien","Submitter":"arojas","FirstSubmitted":1648839853,"LastModified":1693834449,"URLPath":"/cgit/aur.git/snapshot/nx.tar.gz","Depends":["nx-x11","libxcomp","libxml2","xkeyboard-config","xorg-xkbcomp","libxfont2","libxinerama","xorg-font-util","pixman","libxrandr","libxtst","libxcomposite","libxpm","libxdamage","libtirpc"],"MakeDepends":["libjpeg-turbo","libpng","gcc-libs","libxml2","xkeyboard-config","xorg-xkbcomp","libxfont2","libxinerama","xorg-font-util","pixman","libxrandr","libxtst","libxcomposite","libxpm","libxdamage","xorgproto","imake"],"Conflicts":["nx-xcompext"],"License":["GPL"]}
]
================================================
FILE: pkg/dep/testdata/python-pydantic.json
================================================
[
{"ID":2000001,"Name":"python-pydantic","PackageBaseID":200001,"PackageBase":"python-pydantic","Version":"2.5.0-1","Description":"Data validation using Python type hints","URL":"https://github.com/pydantic/pydantic","NumVotes":50,"Popularity":2.0,"OutOfDate":null,"Maintainer":"testmaint","Submitter":"testmaint","FirstSubmitted":1600000000,"LastModified":1700000000,"URLPath":"/cgit/aur.git/snapshot/python-pydantic.tar.gz","Depends":["python","python-typing-extensions"],"MakeDepends":["python-build","python-installer"],"CheckDepends":["python-pytest","python-pytest-mock"],"License":["MIT"]}
]
================================================
FILE: pkg/dep/testdata/samsung-unified-driver.json
================================================
[
{"ID":1734381,"Name":"samsung-unified-driver","PackageBaseID":44188,"PackageBase":"samsung-unified-driver","Version":"1.00.39-11","Description":"Samsung Unified Linux Driver for printers and scanners.","URL":"http://www.samsung.com","NumVotes":227,"Popularity":0.119974,"OutOfDate":null,"Maintainer":"christoph.gysin","Submitter":"ptb","FirstSubmitted":1291593695,"LastModified":1747833725,"URLPath":"/cgit/aur.git/snapshot/samsung-unified-driver.tar.gz","Depends":["samsung-unified-driver-printer","samsung-unified-driver-scanner"],"License":["custom:samsung"]},
{"ID":1734378,"Name":"samsung-unified-driver-common","PackageBaseID":44188,"PackageBase":"samsung-unified-driver","Version":"1.00.39-11","Description":"Samsung Unified Linux Driver (common files)","URL":"http://www.samsung.com","NumVotes":227,"Popularity":0.119974,"OutOfDate":null,"Maintainer":"christoph.gysin","Submitter":"ptb","FirstSubmitted":1291593695,"LastModified":1747833725,"URLPath":"/cgit/aur.git/snapshot/samsung-unified-driver-common.tar.gz","License":["custom:samsung"]},
{"ID":1734379,"Name":"samsung-unified-driver-printer","PackageBaseID":44188,"PackageBase":"samsung-unified-driver","Version":"1.00.39-11","Description":"Samsung Unified Linux Driver for printers.","URL":"http://www.samsung.com","NumVotes":227,"Popularity":0.119974,"OutOfDate":null,"Maintainer":"christoph.gysin","Submitter":"ptb","FirstSubmitted":1291593695,"LastModified":1747833725,"URLPath":"/cgit/aur.git/snapshot/samsung-unified-driver-printer.tar.gz","Depends":["samsung-unified-driver-common","cups","ghostscript"],"License":["custom:samsung"]},
{"ID":1734380,"Name":"samsung-unified-driver-scanner","PackageBaseID":44188,"PackageBase":"samsung-unified-driver","Version":"1.00.39-11","Description":"Samsung Unified Linux Driver for scanners.","URL":"http://www.samsung.com","NumVotes":227,"Popularity":0.119974,"OutOfDate":null,"Maintainer":"christoph.gysin","Submitter":"ptb","FirstSubmitted":1291593695,"LastModified":1747833725,"URLPath":"/cgit/aur.git/snapshot/samsung-unified-driver-scanner.tar.gz","Depends":["samsung-unified-driver-common","libxml2-legacy","libusb-compat","sane"],"License":["custom:samsung"]}
]
================================================
FILE: pkg/dep/topo/dep.go
================================================
package topo
import (
"fmt"
"maps"
"strings"
alpm "github.com/Jguer/dyalpm"
)
type (
// NodeSet is a set of nodes represented as a map for O(1) membership checks.
// The boolean value is not meaningful; presence of the key indicates membership.
NodeSet[T comparable] map[T]bool
// ProvidesMap maps a "provides" key (an alias/satisfier name) to information about the
// node that provides it.
ProvidesMap[T comparable] map[T]*DependencyInfo[T]
// DepMap maps a node to a set of nodes. In Graph this is used for adjacency:
// - dependencies: node -> its direct dependencies
// - dependents: node -> its direct dependents
DepMap[T comparable] map[T]NodeSet[T]
)
// Slice returns the set contents as a slice in unspecified order.
func (n NodeSet[T]) Slice() []T {
var slice []T
for node := range n {
slice = append(slice, node)
}
return slice
}
// NodeInfo carries optional rendering metadata (Color/Background) plus the node's value.
type NodeInfo[V any] struct {
Color string
Background string
Value V
}
// DependencyInfo describes which node provides a given dependency/satisfier, along with the
// original dependency metadata (alpm.Depend).
type DependencyInfo[T comparable] struct {
Provider T
alpm.Depend
}
// CheckFn is a callback used by traversal helpers. It receives the node id plus the node's value.
type CheckFn[T comparable, V any] func(T, V) error
// Graph is a directed dependency graph.
//
// Edge direction:
// - An edge is added with DependOn(child, parent), meaning "child depends on parent".
// - Internally, dependencies maps child -> parents (direct dependencies).
// - Internally, dependents maps parent -> children (direct dependents).
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]
}
// New returns an empty Graph.
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]),
}
}
// Len returns the number of nodes currently present in the graph.
func (g *Graph[T, V]) Len() int {
return len(g.nodes)
}
// Exists reports whether node exists in the graph's node set.
func (g *Graph[T, V]) Exists(node T) bool {
_, ok := g.nodes[node]
return ok
}
// AddNode adds node to the graph. It is safe to call multiple times.
func (g *Graph[T, V]) AddNode(node T) {
g.nodes[node] = true
}
// HasProvides reports whether the given provides key is registered.
func (g *Graph[T, V]) HasProvides(provides T) bool {
_, ok := g.provides[provides]
return ok
}
// GetProviderInfo returns the dependency info for a provider.
func (g *Graph[T, V]) GetProviderInfo(provides T) *DependencyInfo[T] {
return g.provides[provides]
}
// AddProvides registers that node provides the given provides key.
//
// Note: despite the "Add" name, this is a single mapping; calling it again with the same
// provides key overwrites the previous entry.
func (g *Graph[T, V]) AddProvides(provides T, depInfo *alpm.Depend, node T) {
g.provides[provides] = &DependencyInfo[T]{
Provider: node,
Depend: *depInfo,
}
}
// ForEach calls f for every node in the graph.
//
// The value passed to f is the node's NodeInfo.Value if set via SetNodeInfo; otherwise it is
// the zero value of V.
func (g *Graph[T, V]) ForEach(f CheckFn[T, V]) error {
for node := range g.nodes {
var v V
if info := g.nodeInfo[node]; info != nil {
v = info.Value
}
if err := f(node, v); err != nil {
return err
}
}
return nil
}
// SetNodeInfo sets metadata and value for node. Node does not need to already exist in the graph.
func (g *Graph[T, V]) SetNodeInfo(node T, nodeInfo *NodeInfo[V]) {
g.nodeInfo[node] = nodeInfo
}
// GetNodeInfo returns metadata/value for node, or nil if none was set.
func (g *Graph[T, V]) GetNodeInfo(node T) *NodeInfo[V] {
return g.nodeInfo[node]
}
// DependOn adds an edge meaning "child depends on parent".
//
// This ensures both nodes exist in the graph and rejects:
// - self edges (ErrSelfReferential)
// - edges that would introduce a cycle (ErrCircular)
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.add(parent, child)
g.dependencies.add(child, parent)
return nil
}
// String renders the graph in GraphViz DOT format.
//
// Nodes are emitted as `"node"` entries with optional color metadata from NodeInfo.
// Edges are emitted in the direction: dependent -> dependency (child -> parent).
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()
}
// DependsOn reports whether child depends (transitively) on parent.
func (g *Graph[T, V]) DependsOn(child, parent T) bool {
deps := g.Dependencies(child)
_, ok := deps[parent]
return ok
}
// HasDependent reports whether parent has dependent as a (transitive) dependent.
func (g *Graph[T, V]) HasDependent(parent, dependent T) bool {
deps := g.Dependents(parent)
_, ok := deps[dependent]
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
}
// TopoSortedLayers returns a slice of all of the graph nodes in topological sort order with their node info.
//
// The returned slice is layered: each element is a "layer" of nodes that have no remaining
// dependencies at that stage of the process.
//
// Practical meaning with this graph's edge direction (DependOn(child, parent)):
// - Earlier layers contain nodes with fewer/zero dependencies (i.e. dependencies-first order).
// - A node appears only after all of its dependencies have appeared in earlier layers.
//
// If checkFn is non-nil, it is called once per node when it is emitted in a layer. Returning an
// error causes TopoSortedLayers to return nil.
func (g *Graph[T, V]) TopoSortedLayers(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]) remove(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
//
// It returns the list of nodes that were removed (including node). The returned order is based
// on recursive traversal and is not guaranteed to be stable.
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.remove(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.remove(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.remove(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.remove(dependency, node)
}
delete(g.dependencies, node)
// Finally, remove the node itself.
delete(g.nodes, node)
}
// Dependencies returns all transitive dependencies of child (excluding child itself).
// The returned set is nil if child is not present in the graph.
func (g *Graph[T, V]) Dependencies(child T) NodeSet[T] {
return g.buildTransitive(child, g.ImmediateDependencies)
}
// ImmediateDependencies returns the direct dependencies of node.
// The returned set is nil if node has no direct dependencies (or is not present).
func (g *Graph[T, V]) ImmediateDependencies(node T) NodeSet[T] {
return g.dependencies[node]
}
// Dependents returns all transitive dependents of parent (excluding parent itself).
// The returned set is nil if parent is not present in the graph.
func (g *Graph[T, V]) Dependents(parent T) NodeSet[T] {
return g.buildTransitive(parent, g.ImmediateDependents)
}
// ImmediateDependents returns the direct dependents of node.
// The returned set is nil if node has no direct dependents (or is not present).
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))
maps.Copy(out, s)
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]) add(key, node T) {
nodes, ok := dm[key]
if !ok {
nodes = make(NodeSet[T])
dm[key] = nodes
}
nodes[node] = true
}
================================================
FILE: pkg/dep/topo/dep_test.go
================================================
package topo
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestGraph_DependenciesAndDependents_Direct(t *testing.T) {
g := New[string, struct{}]()
// yay depends on go
require.NoError(t, g.DependOn("yay", "go"))
// Dependencies("yay") => {"go"}
depsYay := g.Dependencies("yay")
require.NotNil(t, depsYay)
require.Len(t, depsYay, 1)
require.True(t, depsYay["go"])
// Dependencies("go") => {} (empty set, because "go" is in the graph but has no deps)
depsGo := g.Dependencies("go")
require.NotNil(t, depsGo)
require.Len(t, depsGo, 0)
// Dependents("go") => {"yay"}
dependentsGo := g.Dependents("go")
require.NotNil(t, dependentsGo)
require.Len(t, dependentsGo, 1)
require.True(t, dependentsGo["yay"])
// Dependents("yay") => {} (empty set, because nothing depends on yay)
dependentsYay := g.Dependents("yay")
require.NotNil(t, dependentsYay)
require.Len(t, dependentsYay, 0)
}
func TestGraph_DependenciesAndDependents_Transitive(t *testing.T) {
g := New[string, struct{}]()
// yay depends on go; foo depends on yay
require.NoError(t, g.DependOn("yay", "go"))
require.NoError(t, g.DependOn("foo", "yay"))
// Dependencies("foo") => {"yay", "go"}
depsFoo := g.Dependencies("foo")
require.NotNil(t, depsFoo)
require.Len(t, depsFoo, 2)
require.True(t, depsFoo["yay"])
require.True(t, depsFoo["go"])
// Dependents("go") => {"yay", "foo"} (transitive)
dependentsGo := g.Dependents("go")
require.NotNil(t, dependentsGo)
require.Len(t, dependentsGo, 2)
require.True(t, dependentsGo["yay"])
require.True(t, dependentsGo["foo"])
// Dependents("yay") => {"foo"}
dependentsYay := g.Dependents("yay")
require.NotNil(t, dependentsYay)
require.Len(t, dependentsYay, 1)
require.True(t, dependentsYay["foo"])
}
func TestGraph_DependenciesAndDependents_MissingNodeReturnsNil(t *testing.T) {
g := New[string, struct{}]()
// For nodes not present in the graph, transitive queries return nil.
require.Nil(t, g.Dependencies("missing"))
require.Nil(t, g.Dependents("missing"))
// Adding edges adds nodes; existing nodes with no deps/dependents return an empty set (non-nil).
require.NoError(t, g.DependOn("yay", "go"))
require.NotNil(t, g.Dependencies("go"))
require.Len(t, g.Dependencies("go"), 0)
require.NotNil(t, g.Dependents("yay"))
require.Len(t, g.Dependents("yay"), 0)
}
================================================
FILE: pkg/dep/topo/errors.go
================================================
package topo
import "errors"
var (
// ErrSelfReferential is returned when attempting to create an edge where a node depends on itself.
ErrSelfReferential = errors.New(" self-referential dependencies not allowed")
// ErrConflictingAlias is reserved for when a "provides" alias is defined more than once.
// Note: current Graph APIs overwrite provides entries; this error is not currently returned.
ErrConflictingAlias = errors.New(" alias already defined")
// ErrCircular is returned when attempting to create an edge that would introduce a cycle.
ErrCircular = errors.New(" circular dependencies not allowed")
)
================================================
FILE: pkg/download/abs.go
================================================
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")
}
================================================
FILE: pkg/download/abs_test.go
================================================
//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 {
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 {
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 {
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)
}
================================================
FILE: pkg/download/aur.go
================================================
package download
import (
"bufio"
"bytes"
"compress/gzip"
"context"
"fmt"
"io"
"net/http"
"net/url"
"path"
"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()
}
// ScannerCloser combines a bufio.Scanner with a Close method.
type ScannerCloser struct {
*bufio.Scanner
closer io.Closer
}
// Close closes the underlying gzip reader if present.
func (s *ScannerCloser) Close() error {
if s.closer != nil {
return s.closer.Close()
}
return nil
}
// GetPackageScanner fetches the AUR packages.gz file and returns a scanner for reading its contents.
// The caller must call Close() on the returned ScannerCloser when done to properly release resources.
func GetPackageScanner(ctx context.Context, client HTTPRequestDoer, aurURL string, logger *text.Logger) (*ScannerCloser, error) {
u, err := url.Parse(aurURL)
if err != nil {
return nil, err
}
u.Path = path.Join(u.Path, "packages.gz")
packagesURL := u.String()
resp, err := client.Get(packagesURL)
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
resp.Body.Close()
return nil, fmt.Errorf("invalid status code: %d", resp.StatusCode)
}
// Read the entire body to allow trying gzip decompression
body, err := io.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
return nil, err
}
// Try to decompress as gzip; if that fails, use raw body
var reader io.Reader
var closer io.Closer
gzReader, gzErr := gzip.NewReader(bytes.NewReader(body))
if gzErr == nil {
reader = gzReader
closer = gzReader
} else {
if logger != nil {
logger.Debugln("gzip decompression not needed, using raw response body")
}
reader = bytes.NewReader(body)
}
scanner := bufio.NewScanner(reader)
return &ScannerCloser{
Scanner: scanner,
closer: closer,
}, nil
}
================================================
FILE: pkg/download/aur_test.go
================================================
//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 {
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)
}
================================================
FILE: pkg/download/errors.go
================================================
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
}
================================================
FILE: pkg/download/unified.go
================================================
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"
)
// HTTPRequestDoer is an interface for HTTP clients that can perform GET requests.
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()
}
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 {
return pkg.DB().Name(), getURLName(pkg), false, false
}
// Package not found in specified DB - skip it
if 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
}
================================================
FILE: pkg/download/unified_integration_test.go
================================================
//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])
}
}
================================================
FILE: pkg/download/unified_test.go
================================================
//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)
}
================================================
FILE: pkg/download/utils_test.go
================================================
package download
import (
"context"
"io"
"net/http"
"os/exec"
"strings"
"testing"
"github.com/stretchr/testify/assert"
alpm "github.com/Jguer/dyalpm"
"github.com/Jguer/yay/v12/pkg/db"
"github.com/Jguer/yay/v12/pkg/db/mock"
"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.Database
name string
}
testPackage struct {
*mock.Package
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) DB() alpm.Database {
return p.db
}
func (d *testDBSearcher) SyncPackage(name string) db.IPackage {
if v, ok := d.absPackagesDB[name]; ok {
return &testPackage{
Package: &mock.Package{
PName: name,
PBase: 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{
Package: &mock.Package{
PName: name,
PBase: 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
}
================================================
FILE: pkg/intrange/intrange.go
================================================
package intrange
import (
"strconv"
"strings"
"unicode"
mapset "github.com/deckarep/golang-set/v2"
)
// IntRange stores a max and min amount for range.
type IntRange struct {
min int
max int
}
// IntRanges is a slice of IntRange.
type IntRanges []IntRange
func makeIntRange(minVal, maxVal int) IntRange {
return IntRange{
min: minVal,
max: maxVal,
}
}
// Get returns true if the argument n is included in the closed range
// 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.
func (rs IntRanges) Get(n int) bool {
for _, r := range rs {
if r.Get(n) {
return true
}
}
return false
}
// 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
// supports negation: ^1 ^1-4
//
// include and excule holds numbers that should be added and should not be added
// 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.
func ParseNumberMenu(input string) (include, exclude IntRanges,
otherInclude, otherExclude mapset.Set[string],
) {
include = make(IntRanges, 0)
exclude = make(IntRanges, 0)
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
num2 int
err error
)
invert := false
other := otherInclude
if word[0] == '^' {
invert = true
other = otherExclude
word = word[1:]
}
ranges := strings.SplitN(word, "-", 2)
num1, err = strconv.Atoi(ranges[0])
if err != nil {
other.Add(strings.ToLower(word))
continue
}
if len(ranges) == 2 {
num2, err = strconv.Atoi(ranges[1])
if err != nil {
other.Add(strings.ToLower(word))
continue
}
} else {
num2 = num1
}
mi := min(num1, num2)
ma := max(num1, num2)
if !invert {
include = append(include, makeIntRange(mi, ma))
} else {
exclude = append(exclude, makeIntRange(mi, ma))
}
}
return include, exclude, otherInclude, otherExclude
}
================================================
FILE: pkg/intrange/intrange_test.go
================================================
//go:build !integration
// +build !integration
package intrange
import (
"testing"
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 mapset.Set[string]
OtherExclude mapset.Set[string]
}
inputs := []string{
"1 2 3 4 5",
"1-10 5-15",
"10-5 90-85",
"1 ^2 ^10-5 99 ^40-38 ^123 60-62",
"abort all none",
"a-b ^a-b ^abort",
"-9223372036854775809-9223372036854775809",
"1\t2 3 4\t\t \t 5",
"1 2,3, 4, 5,6 ,7 ,8",
"",
" \t ",
"A B C D E",
}
expected := []result{
{IntRanges{
makeIntRange(1, 1),
makeIntRange(2, 2),
makeIntRange(3, 3),
makeIntRange(4, 4),
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(5, 10),
makeIntRange(38, 40),
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{}, 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{}, 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)
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
}
type args struct {
n int
}
tests := []struct {
name string
fields fields
args args
want bool
}{
{name: "normal range true", fields: fields{0, 10}, args: args{5}, want: true},
{name: "normal start range true", fields: fields{0, 10}, args: args{0}, want: true},
{name: "normal end range true", fields: fields{0, 10}, args: args{10}, want: true},
{name: "small range true", fields: fields{1, 1}, args: args{1}, want: true},
{name: "normal start range false", fields: fields{1, 2}, args: args{0}, want: false},
{name: "normal end range false", fields: fields{1, 2}, args: args{3}, want: false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
r := IntRange{
min: tt.fields.min,
max: tt.fields.max,
}
if got := r.Get(tt.args.n); got != tt.want {
t.Errorf("IntRange.Get() = %v, want %v", got, tt.want)
}
})
}
}
func intRangesEqual(a, b IntRanges) bool {
if a == nil && b == nil {
return true
}
if a == nil || b == nil {
return false
}
if len(a) != len(b) {
return false
}
for n := range a {
r1 := a[n]
r2 := b[n]
if r1.min != r2.min || r1.max != r2.max {
return false
}
}
return true
}
func TestIntRanges_Get(t *testing.T) {
t.Parallel()
type args struct {
n int
}
tests := []struct {
name string
rs IntRanges
args args
want bool
}{
{name: "normal range true", rs: IntRanges{{0, 10}}, args: args{5}, want: true},
{name: "normal ranges in between true", rs: IntRanges{{0, 4}, {5, 10}}, args: args{5}, want: true},
{name: "normal ranges in between false", rs: IntRanges{{0, 4}, {6, 10}}, args: args{5}, want: false},
{name: "normal start range true", rs: IntRanges{{0, 10}}, args: args{0}, want: true},
{name: "normal end range true", rs: IntRanges{{0, 10}}, args: args{10}, want: true},
{name: "small range true", rs: IntRanges{{1, 1}, {3, 3}}, args: args{1}, want: true},
{name: "normal start range false", rs: IntRanges{{1, 2}}, args: args{0}, want: false},
{name: "normal end range false", rs: IntRanges{{1, 2}}, args: args{3}, want: false},
}
for _, tt := range tests {
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)
}
})
}
}
================================================
FILE: pkg/menus/clean_menu.go
================================================
// 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
}
================================================
FILE: pkg/menus/diff_menu.go
================================================
// 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
}
================================================
FILE: pkg/menus/edit_menu.go
================================================
// 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.CommandContext(context.Background(), 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
}
================================================
FILE: pkg/menus/menu.go
================================================
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
}
================================================
FILE: pkg/multierror/multierror.go
================================================
package multierror
import "sync"
// MultiError type handles error accumulation from goroutines.
type MultiError struct {
Errors []error
mux sync.Mutex
}
// Error turns the MultiError structure into a string.
func (err *MultiError) Error() string {
str := ""
for _, e := range err.Errors {
str += e.Error() + "\n"
}
return str[:len(str)-1]
}
// Add adds an error to the Multierror structure.
func (err *MultiError) Add(e error) {
if e == nil {
return
}
err.mux.Lock()
err.Errors = append(err.Errors, e)
err.mux.Unlock()
}
// 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.
func (err *MultiError) Return() error {
if len(err.Errors) > 0 {
return err
}
return nil
}
================================================
FILE: pkg/news/.snapshots/TestPrintNewsFeed-all-quiet
================================================
[1m[35m2019-12-20[0m[0m [1mXorg cleanup requires manual intervention[0m
[1m[35m2020-01-04[0m[0m [1mNow using Zstandard instead of xz for package compression[0m
[1m[35m2020-01-15[0m[0m [1mrsync compatibility[0m
[1m[35m2020-02-17[0m[0m [1msshd needs restarting after upgrading to openssh-8.2p1[0m
[1m[35m2020-02-22[0m[0m [1mPlanet Arch Linux migration[0m
[1m[35m2020-02-24[0m[0m [1mThe Future of the Arch Linux Project Leader[0m
[1m[35m2020-03-01[0m[0m [1mfirewalld>=0.8.1-2 update requires manual intervention[0m
[1m[35m2020-03-19[0m[0m [1mhplip 3.20.3-2 update requires manual intervention[0m
[1m[35m2020-04-13[0m[0m [1mnss>=3.51.1-1 and lib32-nss>=3.51.1-1 updates require manual intervention[0m
[1m[35m2020-04-14[0m[0m [1mzn_poly 0.9.2-2 update requires manual intervention[0m
================================================
FILE: pkg/news/.snapshots/TestPrintNewsFeed-all-verbose
================================================
[1m[35m2019-12-20[0m[0m [1mXorg cleanup requires manual intervention[0m
In the process of Xorg cleanup the update requires manual
intervention when you hit this message:
[36m:: 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
[0m
when updating, use: [36mpacman -Rdd libdmx libxxf86dga libxxf86misc && pacman -Syu[0m to perform the upgrade.
[0m
[1m[35m2020-01-04[0m[0m [1mNow using Zstandard instead of xz for package compression[0m
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.
[0m
[1m[35m2020-01-15[0m[0m [1mrsync compatibility[0m
Our [36mrsync[0m package was shipped with bundled [36mzlib[0m to provide compatibility
with the old-style [36m--compress[0m 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 [36mzlib[0m. This also fixes security issues, actual ones and in future. Go
and blame those running old versions if you encounter errors with [36mrsync
3.1.3-3[0m.
[0m
[1m[35m2020-02-17[0m[0m [1msshd needs restarting after upgrading to openssh-8.2p1[0m
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 [36msystemctl restart sshd[0m right after running [36mpacman -Syu[0m. If you are upgrading to openssh-8.2p1-3 or higher, this restart will happen automatically.
[0m
[1m[35m2020-02-22[0m[0m [1mPlanet Arch Linux migration[0m
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.
[0m
[1m[35m2020-02-24[0m[0m [1mThe Future of the Arch Linux Project Leader[0m
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. I’ve 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. It’s 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)
[0m
[1m[35m2020-03-01[0m[0m [1mfirewalld>=0.8.1-2 update requires manual intervention[0m
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
[36mfirewalld: /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...
[0m
when updating, use
[36mpacman -Suy --overwrite /usr/lib/python3.8/site-packages/firewall/\*
[0m
to perform the upgrade.
[0m
[1m[35m2020-03-19[0m[0m [1mhplip 3.20.3-2 update requires manual intervention[0m
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
[36mhplip: /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...
[0m
when updating, use
[36mpacman -Suy --overwrite /usr/share/hplip/\*
[0m
to perform the upgrade.
[0m
[1m[35m2020-04-13[0m[0m [1mnss>=3.51.1-1 and lib32-nss>=3.51.1-1 updates require manual intervention[0m
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
[36mnss: /usr/lib/p11-kit-trust.so exists in filesystem
lib32-nss: /usr/lib32/p11-kit-trust.so exists in filesystem
[0m
when updating, use
[36mpacman -Syu --overwrite /usr/lib\*/p11-kit-trust.so
[0m
to perform the upgrade.
[0m
[1m[35m2020-04-14[0m[0m [1mzn_poly 0.9.2-2 update requires manual intervention[0m
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
[36mzn_poly: /usr/lib/libzn_poly-0.9.so exists in filesystem
[0m
when updating, use
[36mpacman -Syu --overwrite usr/lib/libzn_poly-0.9.so
[0m
to perform the upgrade.
[0m
================================================
FILE: pkg/news/.snapshots/TestPrintNewsFeed-latest-quiet
================================================
[1m[35m2020-04-13[0m[0m [1mnss>=3.51.1-1 and lib32-nss>=3.51.1-1 updates require manual intervention[0m
[1m[35m2020-04-14[0m[0m [1mzn_poly 0.9.2-2 update requires manual intervention[0m
================================================
FILE: pkg/news/.snapshots/TestPrintNewsFeed-latest-quiet-topdown
================================================
[1m[35m2020-04-14[0m[0m [1mzn_poly 0.9.2-2 update requires manual intervention[0m
[1m[35m2020-04-13[0m[0m [1mnss>=3.51.1-1 and lib32-nss>=3.51.1-1 updates require manual intervention[0m
================================================
FILE: pkg/news/.snapshots/TestPrintNewsFeedSameDay
================================================
[1m[35m2020-04-14[0m[0m [1mzn_poly 0.9.2-2 update requires manual intervention[0m
The zn_poly package prior to version 0.9.2-2 was missing a soname link.[0m
================================================
FILE: pkg/news/news.go
================================================
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) // #nosec G704
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()
}
================================================
FILE: pkg/news/news_test.go
================================================
//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 = `
Arch Linux: Recent news updates
https://www.archlinux.org/news/
The latest and greatest news from the Arch Linux distribution.
en-us
Tue, 14 Apr 2020 16:30:32 +0000
-
zn_poly 0.9.2-2 update requires manual intervention
https://www.archlinux.org/news/zn_poly-092-2-update-requires-manual-intervention/
<p>The zn_poly package prior to version 0.9.2-2 was missing a soname link.
Antonio Rojas
Tue, 14 Apr 2020 16:30:30 +0000
tag:www.archlinux.org,2020-04-14:/news/zn_poly-092-2-update-requires-manual-intervention/
`
const sampleNews = `
Arch Linux: Recent news updates https://www.archlinux.org/news/The latest and greatest news from the Arch Linux distribution. en-us Tue, 14 Apr 2020 16:30:32 +0000 zn_poly 0.9.2-2 update requires manual intervention https://www.archlinux.org/news/zn_poly-092-2-update-requires-manual-intervention/<p>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</p>
<pre><code>zn_poly: /usr/lib/libzn_poly-0.9.so exists in filesystem
</code></pre>
<p>when updating, use</p>
<pre><code>pacman -Syu --overwrite usr/lib/libzn_poly-0.9.so
</code></pre>
<p>to perform the upgrade.</p> Antonio Rojas Tue, 14 Apr 2020 16:30:30 +0000 tag:www.archlinux.org,2020-04-14:/news/zn_poly-092-2-update-requires-manual-intervention/ nss>=3.51.1-1 and lib32-nss>=3.51.1-1 updates require manual intervention https://www.archlinux.org/news/nss3511-1-and-lib32-nss3511-1-updates-require-manual-intervention/<p>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</p>
<pre><code>nss: /usr/lib/p11-kit-trust.so exists in filesystem
lib32-nss: /usr/lib32/p11-kit-trust.so exists in filesystem
</code></pre>
<p>when updating, use</p>
<pre><code>pacman -Syu --overwrite /usr/lib\*/p11-kit-trust.so
</code></pre>
<p>to perform the upgrade.</p> Jan Alexander Steffens Mon, 13 Apr 2020 00:35:58 +0000 tag:www.archlinux.org,2020-04-13:/news/nss3511-1-and-lib32-nss3511-1-updates-require-manual-intervention/ hplip 3.20.3-2 update requires manual intervention https://www.archlinux.org/news/hplip-3203-2-update-requires-manual-intervention/<p>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</p>
<pre><code>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...
</code></pre>
<p>when updating, use</p>
<pre><code>pacman -Suy --overwrite /usr/share/hplip/\*
</code></pre>
<p>to perform the upgrade.</p> Andreas Radke Thu, 19 Mar 2020 06:53:30 +0000 tag:www.archlinux.org,2020-03-19:/news/hplip-3203-2-update-requires-manual-intervention/ firewalld>=0.8.1-2 update requires manual intervention https://www.archlinux.org/news/firewalld081-2-update-requires-manual-intervention/<p>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</p>
<pre><code>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...
</code></pre>
<p>when updating, use</p>
<pre><code>pacman -Suy --overwrite /usr/lib/python3.8/site-packages/firewall/\*
</code></pre>
<p>to perform the upgrade.</p> Jan Alexander Steffens Sun, 01 Mar 2020 16:36:48 +0000 tag:www.archlinux.org,2020-03-01:/news/firewalld081-2-update-requires-manual-intervention/ The Future of the Arch Linux Project Leader https://www.archlinux.org/news/the-future-of-the-arch-linux-project-leader/<p>Hello everyone,</p>
<p>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. I’ve 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. It’s high time that changes.</p>
<p>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.</p>
<p>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 <a href="https://wiki.archlinux.org/index.php/DeveloperWiki:Project_Leader">here</a></p>
<p>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:</p>
<p><strong>Levente Polyak (anthraxx) will be taking over the reins of this ship. Congratulations!</strong></p>
<p><em>Thanks for everything over all these years,<br />
Aaron Griffin (phrakture)</em></p> Aaron Griffin Mon, 24 Feb 2020 15:56:28 +0000 tag:www.archlinux.org,2020-02-24:/news/the-future-of-the-arch-linux-project-leader/ Planet Arch Linux migration https://www.archlinux.org/news/planet-arch-linux-migration/<p>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.</p>
<p>The most notable changes are the offered feeds and the feed location. Archweb only offers an Atom feed which is located at <a href="https://archlinux.org/feeds/planet">here</a>.</p> Jelle van der Waa Sat, 22 Feb 2020 22:43:00 +0000 tag:www.archlinux.org,2020-02-22:/news/planet-arch-linux-migration/ sshd needs restarting after upgrading to openssh-8.2p1 https://www.archlinux.org/news/sshd-needs-restarting-after-upgrading-to-openssh-82p1/<p>After upgrading to openssh-8.2p1, the existing SSH daemon will be unable to accept new connections. (See <a href="https://bugs.archlinux.org/task/65517">FS#65517</a>.) When upgrading remote hosts, please make sure to restart the SSH daemon using <code>systemctl restart sshd</code> right after running <code>pacman -Syu</code>. If you are upgrading to openssh-8.2p1-3 or higher, this restart will happen automatically.</p> Gaetan Bisson Mon, 17 Feb 2020 01:35:04 +0000 tag:www.archlinux.org,2020-02-17:/news/sshd-needs-restarting-after-upgrading-to-openssh-82p1/ rsync compatibility https://www.archlinux.org/news/rsync-compatibility/<p>Our <code>rsync</code> package was shipped with bundled <code>zlib</code> to provide compatibility
with the old-style <code>--compress</code> 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.</p>
<p>So we decided to finally drop the bundled library and ship a package with
system <code>zlib</code>. This also fixes security issues, actual ones and in future. Go
and blame those running old versions if you encounter errors with <code>rsync
3.1.3-3</code>.</p> Christian Hesse Wed, 15 Jan 2020 20:14:43 +0000 tag:www.archlinux.org,2020-01-15:/news/rsync-compatibility/ Now using Zstandard instead of xz for package compression https://www.archlinux.org/news/now-using-zstandard-instead-of-xz-for-package-compression/<p>As announced on the <a href="https://lists.archlinux.org/pipermail/arch-dev-public/2019-December/029752.html">mailing list</a>, on Friday, Dec 27 2019, our package compression scheme has changed from xz (.pkg.tar.xz) to <a href="https://lists.archlinux.org/pipermail/arch-dev-public/2019-December/029778.html">zstd (.pkg.tar.zst)</a>.</p>
<p>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.</p>
<p>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.</p>
<p>As a packager, you will automatically start building .pkg.tar.zst packages if you are using the latest version of devtools (>= 20191227).<br />
As an end-user no manual intervention is required, assuming that you have read and followed the news post <a href="https://www.archlinux.org/news/required-update-to-recent-libarchive/">from late last year</a>.</p>
<p>If you nevertheless haven't updated libarchive since 2018, all hope is not lost! Binary builds of pacman-static are available from Eli Schwartz' <a href="https://wiki.archlinux.org/index.php/Unofficial_user_repositories#eschwartz">personal repository</a> (or direct link to <a href="https://pkgbuild.com/~eschwartz/repo/x86_64-extracted/">binary</a>), signed with their Trusted User keys, with which you can perform the update.</p> Robin Broda Sat, 04 Jan 2020 20:35:55 +0000 tag:www.archlinux.org,2020-01-04:/news/now-using-zstandard-instead-of-xz-for-package-compression/ Xorg cleanup requires manual intervention https://www.archlinux.org/news/xorg-cleanup-requires-manual-intervention/<p>In the process of <a href="https://bugs.archlinux.org/task/64892">Xorg cleanup</a> the update requires manual
intervention when you hit this message:</p>
<pre><code>:: 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
</code></pre>
<p>when updating, use: <code>pacman -Rdd libdmx libxxf86dga libxxf86misc && pacman -Syu</code> to perform the upgrade.</p> Andreas Radke Fri, 20 Dec 2019 13:37:40 +0000 tag:www.archlinux.org,2019-12-20:/news/xorg-cleanup-requires-manual-intervention/
`
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 {
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)
}
================================================
FILE: pkg/query/aur_warnings.go
================================================
package query
import (
"strings"
"github.com/leonelquinteros/gotext"
"github.com/Jguer/aur"
alpm "github.com/Jguer/dyalpm"
"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.Package, 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.Package, 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, " "))
}
================================================
FILE: pkg/query/errors.go
================================================
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")
}
type ErrTargetNotFound struct{}
func (e ErrTargetNotFound) Error() string {
return gotext.Get("no package found for targets")
}
================================================
FILE: pkg/query/filter.go
================================================
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
}
================================================
FILE: pkg/query/metric.go
================================================
package query
import (
"strings"
"github.com/adrg/strutil"
)
const minVotes = 30
const minPopularity = 0.5
const (
separateSourceMax = 45.0
separateSourceMin = 5.0
)
func (a *abstractResults) aurSortByMetric(pkg *abstractResult) float64 {
votesScore := 1 - (minVotes / (minVotes + float64(pkg.votes)))
if pkg.popularity <= 0 {
return votesScore
}
popularityScore := 1 - (minPopularity / (minPopularity + pkg.popularity))
return (votesScore + popularityScore) / 2
}
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 == "aur" {
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
}
if v, ok := a.separateSourceCache[source]; ok {
return v
}
// AUR is always lowest priority
if source == "aur" {
return 0
}
// Score sync repositories based on pacman.conf order (as reflected by dbExecutor.Repos()).
// First repo gets max, last repo gets min, evenly distributed across the range.
for i, repo := range a.repoOrder {
if repo != source {
continue
}
n := len(a.repoOrder)
if n == 1 {
a.separateSourceCache[source] = separateSourceMax
return separateSourceMax
}
step := (separateSourceMax - separateSourceMin) / float64(n-1)
sourceScore := separateSourceMax - (float64(i) * step)
a.separateSourceCache[source] = sourceScore
return sourceScore
}
return 0
}
func (a *abstractResults) calculateMetric(pkg *abstractResult) float64 {
score := a.GetMetric(pkg)
return a.separateSourceScore(pkg.source, score) + score
}
================================================
FILE: pkg/query/metric_test.go
================================================
//go:build !integration
// +build !integration
package query
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestSeparateSourceScore_UsesRepoOrderEvenlyDistributed(t *testing.T) {
t.Parallel()
// Any non-1.0 score avoids the special-case 50 return.
const sim = 0.5
const delta = 1e-1
t.Run("arch repos (core/extra/community/multilib)", func(t *testing.T) {
a := &abstractResults{
separateSources: true,
repoOrder: []string{"core", "extra", "community", "multilib"},
separateSourceCache: map[string]float64{},
}
assert.InDelta(t, 45.0, a.separateSourceScore("core", sim), delta)
assert.InDelta(t, 31.6, a.separateSourceScore("extra", sim), delta)
assert.InDelta(t, 18.3, a.separateSourceScore("community", sim), delta)
assert.InDelta(t, 5.0, a.separateSourceScore("multilib", sim), delta)
assert.Equal(t, 0.0, a.separateSourceScore("aur", sim))
})
t.Run("arch arm repos (core/extra/alarm/aur)", func(t *testing.T) {
a := &abstractResults{
separateSources: true,
repoOrder: []string{"core", "extra", "alarm", "aur"},
separateSourceCache: map[string]float64{},
}
// Note: AUR is not a sync repository; it is always lowest priority (0) regardless of repo order.
assert.InDelta(t, 45.0, a.separateSourceScore("core", sim), delta)
assert.InDelta(t, 31.6, a.separateSourceScore("extra", sim), delta)
assert.InDelta(t, 18.3, a.separateSourceScore("alarm", sim), delta)
assert.Equal(t, 0.0, a.separateSourceScore("aur", sim))
})
}
================================================
FILE: pkg/query/query_builder.go
================================================
package query
import (
"cmp"
"context"
"sort"
"strconv"
"strings"
"unicode"
"github.com/Jguer/aur"
alpm "github.com/Jguer/dyalpm"
"github.com/adrg/strutil"
"github.com/adrg/strutil/metrics"
mapset "github.com/deckarep/golang-set/v2"
"github.com/leonelquinteros/gotext"
"github.com/Jguer/yay/v12/pkg/db"
"github.com/Jguer/yay/v12/pkg/intrange"
"github.com/Jguer/yay/v12/pkg/settings/parser"
"github.com/Jguer/yay/v12/pkg/text"
)
type SearchVerbosity int
// Verbosity settings for search.
const (
NumberMenu SearchVerbosity = iota
Detailed
Minimal
)
type Builder interface {
Len() int
Execute(ctx context.Context, dbExecutor db.Executor, pkgS []string)
Results(dbExecutor db.Executor, verboseSearch SearchVerbosity) error
GetTargets(include, exclude intrange.IntRanges, otherExclude mapset.Set[string]) ([]string, error)
}
type SortFunc func(pkgA, pkgB abstractResult) int
type SourceQueryBuilder struct {
results []abstractResult
sortBy string
searchBy string
targetMode parser.TargetMode
queryMap map[string]map[string]any
bottomUp bool
singleLineResults bool
separateSources bool
aurClient aur.QueryClient
logger *text.Logger
}
func NewSourceQueryBuilder(
aurClient aur.QueryClient,
logger *text.Logger,
sortBy string,
targetMode parser.TargetMode,
searchBy string,
bottomUp,
singleLineResults bool,
separateSources bool,
) *SourceQueryBuilder {
return &SourceQueryBuilder{
aurClient: aurClient,
logger: logger,
bottomUp: bottomUp,
sortBy: sortBy,
targetMode: targetMode,
searchBy: searchBy,
singleLineResults: singleLineResults,
separateSources: separateSources,
queryMap: map[string]map[string]any{},
results: make([]abstractResult, 0, 100),
}
}
type abstractResult struct {
source string
name string
description string
packageBase string
votes int
popularity float64
firstSubmitted int
lastModified int
provides []string
}
type abstractResults struct {
results []abstractResult
search string
metric strutil.StringMetric
separateSources bool
sortByFunc SortFunc
repoOrder []string
distanceCache map[string]float64
separateSourceCache map[string]float64
}
func (a *abstractResults) Len() int { return len(a.results) }
func (a *abstractResults) Swap(i, j int) { a.results[i], a.results[j] = a.results[j], a.results[i] }
func (a *abstractResults) Less(i, j int) bool {
pkgA := a.results[i]
pkgB := a.results[j]
// Sort in descending order by default
return a.sortByFunc(pkgA, pkgB) > 0
}
func (a *abstractResults) GetSortFunc(sortBy string, bottomUp bool) SortFunc {
var sortFunc SortFunc
// Primary sort
switch sortBy {
case "base":
sortFunc = func(pkgA, pkgB abstractResult) int {
return cmp.Compare(pkgA.packageBase, pkgB.packageBase)
}
case "modified":
sortFunc = func(pkgA, pkgB abstractResult) int {
return cmp.Compare(pkgA.lastModified, pkgB.lastModified)
}
case "name":
sortFunc = func(pkgA, pkgB abstractResult) int {
return cmp.Compare(pkgA.name, pkgB.name)
}
case "popularity":
sortFunc = func(pkgA, pkgB abstractResult) int {
return cmp.Compare(pkgA.popularity, pkgB.popularity)
}
case "submitted":
sortFunc = func(pkgA, pkgB abstractResult) int {
return cmp.Compare(pkgA.firstSubmitted, pkgB.firstSubmitted)
}
case "votes":
sortFunc = func(pkgA, pkgB abstractResult) int {
return cmp.Compare(pkgA.votes, pkgB.votes)
}
default:
sortFunc = func(pkgA, pkgB abstractResult) int {
return 0
}
}
// Sort by metric as a tie-breaker. Also handle separating sources when not a tie
{
originalSortFunc := sortFunc
sortFunc = func(pkgA, pkgB abstractResult) int {
if cmpResult := originalSortFunc(pkgA, pkgB); cmpResult != 0 {
if a.separateSources {
if cmpSources := strings.Compare(pkgA.source, pkgB.source); cmpSources != 0 {
return cmpSources
}
}
return cmpResult
}
metricA := a.calculateMetric(&pkgA)
metricB := a.calculateMetric(&pkgB)
return cmp.Compare(metricA, metricB)
}
}
if bottomUp {
// Invert sort for bottom-up sorting
originalSortFunc := sortFunc
sortFunc = func(pkgA, pkgB abstractResult) int {
return -originalSortFunc(pkgA, pkgB)
}
}
return sortFunc
}
func (s *SourceQueryBuilder) Execute(ctx context.Context, dbExecutor db.Executor, pkgS []string) {
var aurErr error
pkgS = RemoveInvalidTargets(s.logger, pkgS, s.targetMode)
metric := &metrics.Hamming{
CaseSensitive: false,
}
sortableResults := &abstractResults{
results: []abstractResult{},
search: strings.Join(pkgS, ""),
metric: metric,
separateSources: s.separateSources,
repoOrder: dbExecutor.Repos(),
distanceCache: map[string]float64{},
separateSourceCache: map[string]float64{},
}
sortableResults.sortByFunc = sortableResults.GetSortFunc(s.sortBy, s.bottomUp)
var repoResults []alpm.Package
if s.targetMode.AtLeastRepo() {
repoResults = dbExecutor.SyncPackages(pkgS...)
for i := range repoResults {
dbName := repoResults[i].DB().Name()
if s.queryMap[dbName] == nil {
s.queryMap[dbName] = map[string]any{}
}
s.queryMap[dbName][repoResults[i].Name()] = repoResults[i]
rawProvides := repoResults[i].Provides()
provides := make([]string, len(rawProvides))
for j := range rawProvides {
provides[j] = rawProvides[j].Name
}
sortableResults.results = append(sortableResults.results, abstractResult{
source: repoResults[i].DB().Name(),
name: repoResults[i].Name(),
description: repoResults[i].Description(),
packageBase: repoResults[i].Base(),
votes: -1,
popularity: -1,
firstSubmitted: -1,
lastModified: -1,
provides: provides,
})
}
}
if s.targetMode.AtLeastAUR() {
var aurResults []aur.Pkg
aurResults, aurErr = queryAUR(ctx, s.aurClient, pkgS, s.searchBy)
dbName := "aur"
for i := range aurResults {
if s.queryMap[dbName] == nil {
s.queryMap[dbName] = map[string]any{}
}
by := getSearchBy(s.searchBy)
if (by == aur.NameDesc || by == aur.None || by == aur.Name) &&
!matchesSearch(&aurResults[i], pkgS) {
continue
}
s.queryMap[dbName][aurResults[i].Name] = aurResults[i]
sortableResults.results = append(sortableResults.results, abstractResult{
source: dbName,
name: aurResults[i].Name,
description: aurResults[i].Description,
packageBase: aurResults[i].PackageBase,
votes: aurResults[i].NumVotes,
popularity: aurResults[i].Popularity,
firstSubmitted: aurResults[i].FirstSubmitted,
lastModified: aurResults[i].LastModified,
provides: aurResults[i].Provides,
})
}
}
sort.Sort(sortableResults)
s.results = sortableResults.results
if aurErr != nil {
s.logger.Errorln(ErrAURSearch{inner: aurErr})
if len(repoResults) != 0 {
s.logger.Warnln(gotext.Get("Showing repo packages only"))
}
}
}
func (s *SourceQueryBuilder) Results(dbExecutor db.Executor, verboseSearch SearchVerbosity) error {
for i := range s.results {
if verboseSearch == Minimal {
s.logger.Println(s.results[i].name)
continue
}
var toPrint string
if verboseSearch == NumberMenu {
if s.bottomUp {
toPrint += text.Magenta(strconv.Itoa(len(s.results)-i)) + " "
} else {
toPrint += text.Magenta(strconv.Itoa(i+1)) + " "
}
}
pkg := s.queryMap[s.results[i].source][s.results[i].name]
switch pPkg := pkg.(type) {
case aur.Pkg:
toPrint += aurPkgSearchString(&pPkg, dbExecutor, s.singleLineResults)
case alpm.Package:
toPrint += syncPkgSearchString(pPkg, dbExecutor, s.singleLineResults)
}
s.logger.Println(toPrint)
}
return nil
}
func (s *SourceQueryBuilder) Len() int {
return len(s.results)
}
func (s *SourceQueryBuilder) GetTargets(include, exclude intrange.IntRanges,
otherExclude mapset.Set[string],
) ([]string, error) {
var (
isInclude = len(exclude) == 0 && otherExclude.Cardinality() == 0
targets []string
lenRes = len(s.results)
)
for i := 1; i <= s.Len(); i++ {
target := i - 1
if s.bottomUp {
target = lenRes - i
}
if (isInclude && include.Get(i)) || (!isInclude && !exclude.Get(i)) {
targets = append(targets, s.results[target].source+"/"+s.results[target].name)
}
}
return targets, nil
}
func matchesSearch(pkg *aur.Pkg, terms []string) bool {
if len(terms) <= 1 {
return true
}
for _, pkgN := range terms {
if strings.IndexFunc(pkgN, unicode.IsSymbol) != -1 {
return true
}
name := strings.ToLower(pkg.Name)
desc := strings.ToLower(pkg.Description)
targ := strings.ToLower(pkgN)
if !strings.Contains(name, targ) && !strings.Contains(desc, targ) {
return false
}
}
return true
}
================================================
FILE: pkg/query/query_builder_test.go
================================================
//go:build !integration
// +build !integration
package query
import (
"context"
"io"
"strings"
"testing"
"github.com/Jguer/aur"
"github.com/Jguer/yay/v12/pkg/db/mock"
mockaur "github.com/Jguer/yay/v12/pkg/dep/mock"
"github.com/Jguer/yay/v12/pkg/settings/parser"
"github.com/Jguer/yay/v12/pkg/text"
"github.com/stretchr/testify/assert"
)
func TestSourceQueryBuilder(t *testing.T) {
t.Parallel()
type testCase struct {
desc string
search []string
bottomUp bool
separateSources bool
sortBy string
verbosity SearchVerbosity
targetMode parser.TargetMode
singleLineResults bool
searchBy string
wantResults []string
wantOutput []string
}
testCases := []testCase{
{
desc: "sort-by-metric bottomup separatesources",
search: []string{"linux"},
bottomUp: true,
separateSources: true,
sortBy: "",
verbosity: Detailed,
wantResults: []string{"linux-ck", "linux-zen", "linux"},
wantOutput: []string{
"\x1b]8;;https://aur.archlinux.org/packages/linux-ck\x1b\\\x1b[1m\x1b[34maur\x1b[0m\x1b[0m/\x1b[1mlinux-ck\x1b[0m\x1b]8;;\x1b\\ \x1b[36m5.16.12-1\x1b[0m\x1b[1m (+450\x1b[0m \x1b[1m1.51) \x1b[0m\n The Linux-ck kernel and modules with ck's hrtimer patches\n",
"\x1b]8;;https://archlinux.org/packages/core/x86_64/linux-zen\x1b\\\x1b[1m\x1b[33mcore\x1b[0m\x1b[0m/\x1b[1mlinux-zen\x1b[0m\x1b]8;;\x1b\\ \x1b[36m5.16.0\x1b[0m\x1b[1m (1.0 B 1.0 B) \x1b[0m\n The Linux ZEN kernel and modules\n",
"\x1b]8;;https://archlinux.org/packages/core/x86_64/linux\x1b\\\x1b[1m\x1b[33mcore\x1b[0m\x1b[0m/\x1b[1mlinux\x1b[0m\x1b]8;;\x1b\\ \x1b[36m5.16.0\x1b[0m\x1b[1m (1.0 B 1.0 B) \x1b[0m\n The Linux kernel and modules\n",
},
},
{
desc: "sort-by-metric topdown separatesources",
search: []string{"linux"},
bottomUp: false,
separateSources: true,
sortBy: "",
verbosity: Detailed,
wantResults: []string{"linux", "linux-zen", "linux-ck"},
wantOutput: []string{
"\x1b]8;;https://archlinux.org/packages/core/x86_64/linux\x1b\\\x1b[1m\x1b[33mcore\x1b[0m\x1b[0m/\x1b[1mlinux\x1b[0m\x1b]8;;\x1b\\ \x1b[36m5.16.0\x1b[0m\x1b[1m (1.0 B 1.0 B) \x1b[0m\n The Linux kernel and modules\n",
"\x1b]8;;https://archlinux.org/packages/core/x86_64/linux-zen\x1b\\\x1b[1m\x1b[33mcore\x1b[0m\x1b[0m/\x1b[1mlinux-zen\x1b[0m\x1b]8;;\x1b\\ \x1b[36m5.16.0\x1b[0m\x1b[1m (1.0 B 1.0 B) \x1b[0m\n The Linux ZEN kernel and modules\n",
"\x1b]8;;https://aur.archlinux.org/packages/linux-ck\x1b\\\x1b[1m\x1b[34maur\x1b[0m\x1b[0m/\x1b[1mlinux-ck\x1b[0m\x1b]8;;\x1b\\ \x1b[36m5.16.12-1\x1b[0m\x1b[1m (+450\x1b[0m \x1b[1m1.51) \x1b[0m\n The Linux-ck kernel and modules with ck's hrtimer patches\n",
},
},
{
desc: "sort-by-metric bottomup noseparatesources",
search: []string{"linux"},
bottomUp: true,
separateSources: false,
sortBy: "",
verbosity: Detailed,
wantResults: []string{"linux-ck", "linux-zen", "linux"},
wantOutput: []string{
"\x1b]8;;https://aur.archlinux.org/packages/linux-ck\x1b\\\x1b[1m\x1b[34maur\x1b[0m\x1b[0m/\x1b[1mlinux-ck\x1b[0m\x1b]8;;\x1b\\ \x1b[36m5.16.12-1\x1b[0m\x1b[1m (+450\x1b[0m \x1b[1m1.51) \x1b[0m\n The Linux-ck kernel and modules with ck's hrtimer patches\n",
"\x1b]8;;https://archlinux.org/packages/core/x86_64/linux-zen\x1b\\\x1b[1m\x1b[33mcore\x1b[0m\x1b[0m/\x1b[1mlinux-zen\x1b[0m\x1b]8;;\x1b\\ \x1b[36m5.16.0\x1b[0m\x1b[1m (1.0 B 1.0 B) \x1b[0m\n The Linux ZEN kernel and modules\n",
"\x1b]8;;https://archlinux.org/packages/core/x86_64/linux\x1b\\\x1b[1m\x1b[33mcore\x1b[0m\x1b[0m/\x1b[1mlinux\x1b[0m\x1b]8;;\x1b\\ \x1b[36m5.16.0\x1b[0m\x1b[1m (1.0 B 1.0 B) \x1b[0m\n The Linux kernel and modules\n",
},
},
{
desc: "sort-by-metric topdown noseparatesources",
search: []string{"linux"},
bottomUp: false,
separateSources: false,
sortBy: "",
verbosity: Detailed,
wantResults: []string{"linux", "linux-zen", "linux-ck"},
wantOutput: []string{
"\x1b]8;;https://archlinux.org/packages/core/x86_64/linux\x1b\\\x1b[1m\x1b[33mcore\x1b[0m\x1b[0m/\x1b[1mlinux\x1b[0m\x1b]8;;\x1b\\ \x1b[36m5.16.0\x1b[0m\x1b[1m (1.0 B 1.0 B) \x1b[0m\n The Linux kernel and modules\n",
"\x1b]8;;https://archlinux.org/packages/core/x86_64/linux-zen\x1b\\\x1b[1m\x1b[33mcore\x1b[0m\x1b[0m/\x1b[1mlinux-zen\x1b[0m\x1b]8;;\x1b\\ \x1b[36m5.16.0\x1b[0m\x1b[1m (1.0 B 1.0 B) \x1b[0m\n The Linux ZEN kernel and modules\n",
"\x1b]8;;https://aur.archlinux.org/packages/linux-ck\x1b\\\x1b[1m\x1b[34maur\x1b[0m\x1b[0m/\x1b[1mlinux-ck\x1b[0m\x1b]8;;\x1b\\ \x1b[36m5.16.12-1\x1b[0m\x1b[1m (+450\x1b[0m \x1b[1m1.51) \x1b[0m\n The Linux-ck kernel and modules with ck's hrtimer patches\n",
},
},
{
desc: "sort-by-metric bottomup separatesources number-menu",
search: []string{"linux"},
bottomUp: true,
separateSources: true,
sortBy: "",
verbosity: NumberMenu,
wantResults: []string{"linux-ck", "linux-zen", "linux"},
wantOutput: []string{
"\x1b[35m3\x1b[0m \x1b]8;;https://aur.archlinux.org/packages/linux-ck\x1b\\\x1b[1m\x1b[34maur\x1b[0m\x1b[0m/\x1b[1mlinux-ck\x1b[0m\x1b]8;;\x1b\\ \x1b[36m5.16.12-1\x1b[0m\x1b[1m (+450\x1b[0m \x1b[1m1.51) \x1b[0m\n The Linux-ck kernel and modules with ck's hrtimer patches\n",
"\x1b[35m2\x1b[0m \x1b]8;;https://archlinux.org/packages/core/x86_64/linux-zen\x1b\\\x1b[1m\x1b[33mcore\x1b[0m\x1b[0m/\x1b[1mlinux-zen\x1b[0m\x1b]8;;\x1b\\ \x1b[36m5.16.0\x1b[0m\x1b[1m (1.0 B 1.0 B) \x1b[0m\n The Linux ZEN kernel and modules\n",
"\x1b[35m1\x1b[0m \x1b]8;;https://archlinux.org/packages/core/x86_64/linux\x1b\\\x1b[1m\x1b[33mcore\x1b[0m\x1b[0m/\x1b[1mlinux\x1b[0m\x1b]8;;\x1b\\ \x1b[36m5.16.0\x1b[0m\x1b[1m (1.0 B 1.0 B) \x1b[0m\n The Linux kernel and modules\n",
},
},
{
desc: "sort-by-metric topdown separatesources number-menu",
search: []string{"linux"},
bottomUp: false,
separateSources: true,
sortBy: "",
verbosity: NumberMenu,
wantResults: []string{"linux", "linux-zen", "linux-ck"},
wantOutput: []string{
"\x1b[35m1\x1b[0m \x1b]8;;https://archlinux.org/packages/core/x86_64/linux\x1b\\\x1b[1m\x1b[33mcore\x1b[0m\x1b[0m/\x1b[1mlinux\x1b[0m\x1b]8;;\x1b\\ \x1b[36m5.16.0\x1b[0m\x1b[1m (1.0 B 1.0 B) \x1b[0m\n The Linux kernel and modules\n",
"\x1b[35m2\x1b[0m \x1b]8;;https://archlinux.org/packages/core/x86_64/linux-zen\x1b\\\x1b[1m\x1b[33mcore\x1b[0m\x1b[0m/\x1b[1mlinux-zen\x1b[0m\x1b]8;;\x1b\\ \x1b[36m5.16.0\x1b[0m\x1b[1m (1.0 B 1.0 B) \x1b[0m\n The Linux ZEN kernel and modules\n",
"\x1b[35m3\x1b[0m \x1b]8;;https://aur.archlinux.org/packages/linux-ck\x1b\\\x1b[1m\x1b[34maur\x1b[0m\x1b[0m/\x1b[1mlinux-ck\x1b[0m\x1b]8;;\x1b\\ \x1b[36m5.16.12-1\x1b[0m\x1b[1m (+450\x1b[0m \x1b[1m1.51) \x1b[0m\n The Linux-ck kernel and modules with ck's hrtimer patches\n",
},
},
{
desc: "sort-by-name bottomup separatesources number-menu",
search: []string{"linux"},
bottomUp: true,
separateSources: true,
sortBy: "name",
verbosity: NumberMenu,
wantResults: []string{"linux-ck", "linux", "linux-zen"},
wantOutput: []string{
"\x1b[35m3\x1b[0m \x1b]8;;https://aur.archlinux.org/packages/linux-ck\x1b\\\x1b[1m\x1b[34maur\x1b[0m\x1b[0m/\x1b[1mlinux-ck\x1b[0m\x1b]8;;\x1b\\ \x1b[36m5.16.12-1\x1b[0m\x1b[1m (+450\x1b[0m \x1b[1m1.51) \x1b[0m\n The Linux-ck kernel and modules with ck's hrtimer patches\n",
"\x1b[35m2\x1b[0m \x1b]8;;https://archlinux.org/packages/core/x86_64/linux\x1b\\\x1b[1m\x1b[33mcore\x1b[0m\x1b[0m/\x1b[1mlinux\x1b[0m\x1b]8;;\x1b\\ \x1b[36m5.16.0\x1b[0m\x1b[1m (1.0 B 1.0 B) \x1b[0m\n The Linux kernel and modules\n",
"\x1b[35m1\x1b[0m \x1b]8;;https://archlinux.org/packages/core/x86_64/linux-zen\x1b\\\x1b[1m\x1b[33mcore\x1b[0m\x1b[0m/\x1b[1mlinux-zen\x1b[0m\x1b]8;;\x1b\\ \x1b[36m5.16.0\x1b[0m\x1b[1m (1.0 B 1.0 B) \x1b[0m\n The Linux ZEN kernel and modules\n",
},
},
{
desc: "sort-by-name topdown separatesources number-menu",
search: []string{"linux"},
bottomUp: false,
separateSources: true,
sortBy: "name",
verbosity: NumberMenu,
wantResults: []string{"linux-zen", "linux", "linux-ck"},
wantOutput: []string{
"\x1b[35m1\x1b[0m \x1b]8;;https://archlinux.org/packages/core/x86_64/linux-zen\x1b\\\x1b[1m\x1b[33mcore\x1b[0m\x1b[0m/\x1b[1mlinux-zen\x1b[0m\x1b]8;;\x1b\\ \x1b[36m5.16.0\x1b[0m\x1b[1m (1.0 B 1.0 B) \x1b[0m\n The Linux ZEN kernel and modules\n",
"\x1b[35m2\x1b[0m \x1b]8;;https://archlinux.org/packages/core/x86_64/linux\x1b\\\x1b[1m\x1b[33mcore\x1b[0m\x1b[0m/\x1b[1mlinux\x1b[0m\x1b]8;;\x1b\\ \x1b[36m5.16.0\x1b[0m\x1b[1m (1.0 B 1.0 B) \x1b[0m\n The Linux kernel and modules\n",
"\x1b[35m3\x1b[0m \x1b]8;;https://aur.archlinux.org/packages/linux-ck\x1b\\\x1b[1m\x1b[34maur\x1b[0m\x1b[0m/\x1b[1mlinux-ck\x1b[0m\x1b]8;;\x1b\\ \x1b[36m5.16.12-1\x1b[0m\x1b[1m (+450\x1b[0m \x1b[1m1.51) \x1b[0m\n The Linux-ck kernel and modules with ck's hrtimer patches\n",
},
},
{
desc: "sort-by-name bottomup noseparatesources minimal",
search: []string{"linux"},
bottomUp: true,
separateSources: false,
sortBy: "name",
verbosity: Minimal,
wantResults: []string{"linux", "linux-ck", "linux-zen"},
wantOutput: []string{
"linux\n",
"linux-ck\n",
"linux-zen\n",
},
},
{
desc: "only-aur minimal",
search: []string{"linux"},
bottomUp: true,
separateSources: true,
sortBy: "name",
verbosity: Minimal,
targetMode: parser.ModeAUR,
wantResults: []string{"linux-ck"},
wantOutput: []string{
"linux-ck\n",
},
},
{
desc: "only-repo minimal",
search: []string{"linux"},
bottomUp: true,
separateSources: true,
sortBy: "name",
verbosity: Minimal,
targetMode: parser.ModeRepo,
wantResults: []string{"linux", "linux-zen"},
wantOutput: []string{
"linux\n",
"linux-zen\n",
},
},
{
desc: "sort-by-name singleline",
search: []string{"linux"},
bottomUp: true,
separateSources: true,
sortBy: "name",
verbosity: Detailed,
singleLineResults: true,
wantResults: []string{"linux-ck", "linux", "linux-zen"},
wantOutput: []string{
"\x1b]8;;https://aur.archlinux.org/packages/linux-ck\x1b\\\x1b[1m\x1b[34maur\x1b[0m\x1b[0m/\x1b[1mlinux-ck\x1b[0m\x1b]8;;\x1b\\ \x1b[36m5.16.12-1\x1b[0m\x1b[1m (+450\x1b[0m \x1b[1m1.51) \x1b[0m\tThe Linux-ck kernel and modules with ck's hrtimer patches\n",
"\x1b]8;;https://archlinux.org/packages/core/x86_64/linux\x1b\\\x1b[1m\x1b[33mcore\x1b[0m\x1b[0m/\x1b[1mlinux\x1b[0m\x1b]8;;\x1b\\ \x1b[36m5.16.0\x1b[0m\x1b[1m (1.0 B 1.0 B) \x1b[0m\tThe Linux kernel and modules\n",
"\x1b]8;;https://archlinux.org/packages/core/x86_64/linux-zen\x1b\\\x1b[1m\x1b[33mcore\x1b[0m\x1b[0m/\x1b[1mlinux-zen\x1b[0m\x1b]8;;\x1b\\ \x1b[36m5.16.0\x1b[0m\x1b[1m (1.0 B 1.0 B) \x1b[0m\tThe Linux ZEN kernel and modules\n",
},
},
{
desc: "sort-by-name search-by-name",
search: []string{"linux-ck"},
bottomUp: true,
separateSources: true,
sortBy: "name",
verbosity: Detailed,
searchBy: "name",
targetMode: parser.ModeAUR,
wantResults: []string{"linux-ck"},
wantOutput: []string{
"\x1b]8;;https://aur.archlinux.org/packages/linux-ck\x1b\\\x1b[1m\x1b[34maur\x1b[0m\x1b[0m/\x1b[1mlinux-ck\x1b[0m\x1b]8;;\x1b\\ \x1b[36m5.16.12-1\x1b[0m\x1b[1m (+450\x1b[0m \x1b[1m1.51) \x1b[0m\n The Linux-ck kernel and modules with ck's hrtimer patches\n",
},
},
{
desc: "only-aur search-by-several-terms",
search: []string{"linux-ck", "hrtimer"},
bottomUp: true,
separateSources: true,
verbosity: Detailed,
targetMode: parser.ModeAUR,
wantResults: []string{"linux-ck"},
wantOutput: []string{
"\x1b]8;;https://aur.archlinux.org/packages/linux-ck\x1b\\\x1b[1m\x1b[34maur\x1b[0m\x1b[0m/\x1b[1mlinux-ck\x1b[0m\x1b]8;;\x1b\\ \x1b[36m5.16.12-1\x1b[0m\x1b[1m (+450\x1b[0m \x1b[1m1.51) \x1b[0m\n The Linux-ck kernel and modules with ck's hrtimer patches\n",
},
},
}
mockDB := &mock.DBExecutor{
ReposFn: func() []string {
// Match pacman.conf parsing order for source separation.
return []string{"core"}
},
SyncPackagesFn: func(pkgs ...string) []mock.IPackage {
mockDB := mock.NewDB("core")
return []mock.IPackage{
&mock.Package{
PBase: "linux",
PName: "linux",
PVersion: "5.16.0",
PDescription: "The Linux kernel and modules",
PSize: 1,
PISize: 1,
PDB: mockDB,
PArchitecture: "x86_64",
},
&mock.Package{
PBase: "linux-zen",
PName: "linux-zen",
PVersion: "5.16.0",
PDescription: "The Linux ZEN kernel and modules",
PSize: 1,
PISize: 1,
PDB: mockDB,
PArchitecture: "x86_64",
},
}
},
LocalPackageFn: func(string) mock.IPackage {
return nil
},
}
mockAUR := &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return []aur.Pkg{
{
Description: "The Linux-ck kernel and modules with ck's hrtimer patches",
FirstSubmitted: 1311346274,
ID: 1045311,
LastModified: 1646250901,
Maintainer: "graysky",
Name: "linux-ck",
NumVotes: 450,
OutOfDate: 0,
PackageBase: "linux-ck",
PackageBaseID: 50911,
Popularity: 1.511141,
URL: "https://wiki.archlinux.org/index.php/Linux-ck",
URLPath: "/cgit/aur.git/snapshot/linux-ck.tar.gz",
Version: "5.16.12-1",
},
}, nil
},
}
for _, tc := range testCases {
t.Run(tc.desc, func(t *testing.T) {
w := &strings.Builder{}
queryBuilder := NewSourceQueryBuilder(mockAUR,
text.NewLogger(w, io.Discard, strings.NewReader(""), false, "test"),
tc.sortBy, tc.targetMode, tc.searchBy, tc.bottomUp,
tc.singleLineResults, tc.separateSources)
queryBuilder.Execute(context.Background(), mockDB, tc.search)
assert.Len(t, queryBuilder.results, len(tc.wantResults))
assert.Equal(t, len(tc.wantResults), queryBuilder.Len())
for i, name := range tc.wantResults {
assert.Equal(t, name, queryBuilder.results[i].name)
}
queryBuilder.Results(mockDB, tc.verbosity)
assert.Equal(t, strings.Join(tc.wantOutput, ""), w.String())
})
}
}
func TestSourceQueryBuilderTieSortsByRepoOrder(t *testing.T) {
t.Parallel()
type testCase struct {
desc string
bottomUp bool
repoOrder []string
wantNames []string
}
testCases := []testCase{
{
desc: "sort-by-metric topdown repo-order-core-extra",
bottomUp: false,
repoOrder: []string{"core", "extra"},
wantNames: []string{"systemd", "systemd-libs", "python-systemd", "systemd-git"},
},
{
desc: "sort-by-metric topdown repo-order-extra-core",
bottomUp: false,
repoOrder: []string{"extra", "core"},
wantNames: []string{"systemd", "python-systemd", "systemd-libs", "systemd-git"},
},
{
desc: "sort-by-metric bottomup repo-order-core-extra",
bottomUp: true,
repoOrder: []string{"core", "extra"},
wantNames: []string{"systemd-git", "python-systemd", "systemd-libs", "systemd"},
},
{
desc: "sort-by-metric bottomup repo-order-extra-core",
bottomUp: true,
repoOrder: []string{"extra", "core"},
wantNames: []string{"systemd-git", "systemd-libs", "python-systemd", "systemd"},
},
}
mockDB := &mock.DBExecutor{
SyncPackagesFn: func(pkgs ...string) []mock.IPackage {
return []mock.IPackage{
&mock.Package{
PBase: "systemd",
PName: "systemd",
PVersion: "259-1",
PDescription: "system and service manager",
PSize: 1,
PISize: 1,
PDB: mock.NewDB("core"),
PArchitecture: "x86_64",
},
&mock.Package{
PBase: "systemd",
PName: "systemd-libs",
PVersion: "259-1",
PDescription: "systemd client libraries",
PSize: 1,
PISize: 1,
PDB: mock.NewDB("core"),
PArchitecture: "x86_64",
},
&mock.Package{
PBase: "python-systemd",
PName: "python-systemd",
PVersion: "235-4",
PDescription: "Python bindings for systemd",
PSize: 1,
PISize: 1,
PDB: mock.NewDB("extra"),
PArchitecture: "x86_64",
},
}
},
LocalPackageFn: func(string) mock.IPackage {
return nil
},
}
mockAUR := &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return []aur.Pkg{
{
Description: "system and service manager (git version)",
FirstSubmitted: 1445633397,
ID: 1909597,
LastModified: 1765571424,
Maintainer: "Atsutane",
Name: "systemd-git",
NumVotes: 11,
OutOfDate: 0,
PackageBase: "systemd-git",
PackageBaseID: 102323,
Popularity: 0.005618,
URL: "https://www.github.com/systemd/systemd",
URLPath: "/cgit/aur.git/snapshot/systemd-git.tar.gz",
Version: "259.rc3.r85286.7524671f74c-1",
},
}, nil
},
}
for _, tc := range testCases {
t.Run(tc.desc, func(t *testing.T) {
w := &strings.Builder{}
mockDB.ReposFn = func() []string {
return tc.repoOrder
}
queryBuilder := NewSourceQueryBuilder(mockAUR,
text.NewLogger(w, io.Discard, strings.NewReader(""), false, "test"),
"", parser.ModeAny, "", tc.bottomUp,
false, true)
queryBuilder.Execute(context.Background(), mockDB, []string{"systemd"})
gotNames := make([]string, len(queryBuilder.results))
for i, result := range queryBuilder.results {
gotNames[i] = result.name
}
assert.Equal(t, tc.wantNames, gotNames)
})
}
}
func TestSourceQueryBuilderTieDoesNotSeparateSources(t *testing.T) {
t.Parallel()
type testCase struct {
desc string
bottomUp bool
wantNames []string
}
testCases := []testCase{
{
desc: "sort-by-metric topdown",
bottomUp: false,
wantNames: []string{"yay", "ruby-yard", "yay-git"},
},
{
desc: "sort-by-metric bottomup",
bottomUp: true,
wantNames: []string{"yay-git", "ruby-yard", "yay"},
},
}
mockDB, mockAUR := newYayQueryBuilderMocks()
for _, tc := range testCases {
t.Run(tc.desc, func(t *testing.T) {
w := &strings.Builder{}
queryBuilder := NewSourceQueryBuilder(mockAUR,
text.NewLogger(w, io.Discard, strings.NewReader(""), false, "test"),
"", parser.ModeAny, "", tc.bottomUp,
false, true)
queryBuilder.Execute(context.Background(), mockDB, []string{"yay"})
gotNames := make([]string, len(queryBuilder.results))
for i, result := range queryBuilder.results {
gotNames[i] = result.name
}
assert.Equal(t, tc.wantNames, gotNames)
})
}
}
func TestSourceQueryBuilderSortByFields(t *testing.T) {
t.Parallel()
type testCase struct {
desc string
sortBy string
bottomUp bool
wantNames []string
}
testCases := []testCase{
{
desc: "sort-by-base topdown",
sortBy: "base",
bottomUp: false,
wantNames: []string{"yay-git", "yay", "ruby-yard"},
},
{
desc: "sort-by-base bottomup",
sortBy: "base",
bottomUp: true,
wantNames: []string{"ruby-yard", "yay", "yay-git"},
},
{
desc: "sort-by-modified topdown",
sortBy: "modified",
bottomUp: false,
wantNames: []string{"yay-git", "yay", "ruby-yard"},
},
{
desc: "sort-by-modified bottomup",
sortBy: "modified",
bottomUp: true,
wantNames: []string{"ruby-yard", "yay", "yay-git"},
},
{
desc: "sort-by-name topdown",
sortBy: "name",
bottomUp: false,
wantNames: []string{"yay-git", "yay", "ruby-yard"},
},
{
desc: "sort-by-name bottomup",
sortBy: "name",
bottomUp: true,
wantNames: []string{"ruby-yard", "yay", "yay-git"},
},
{
desc: "sort-by-popularity topdown",
sortBy: "popularity",
bottomUp: false,
wantNames: []string{"yay", "yay-git", "ruby-yard"},
},
{
desc: "sort-by-popularity bottomup",
sortBy: "popularity",
bottomUp: true,
wantNames: []string{"ruby-yard", "yay-git", "yay"},
},
{
desc: "sort-by-votes topdown",
sortBy: "votes",
bottomUp: false,
wantNames: []string{"yay", "yay-git", "ruby-yard"},
},
{
desc: "sort-by-votes bottomup",
sortBy: "votes",
bottomUp: true,
wantNames: []string{"ruby-yard", "yay-git", "yay"},
},
{
desc: "sort-by-submitted topdown",
sortBy: "submitted",
bottomUp: false,
wantNames: []string{"yay-git", "yay", "ruby-yard"},
},
{
desc: "sort-by-submitted bottomup",
sortBy: "submitted",
bottomUp: true,
wantNames: []string{"ruby-yard", "yay", "yay-git"},
},
{
desc: "sort-by-metric topdown",
sortBy: "",
bottomUp: false,
wantNames: []string{"yay", "yay-git", "ruby-yard"},
},
{
desc: "sort-by-metric bottomup",
sortBy: "",
bottomUp: true,
wantNames: []string{"ruby-yard", "yay-git", "yay"},
},
}
mockDB, mockAUR := newYayQueryBuilderMocks()
for _, tc := range testCases {
t.Run(tc.desc, func(t *testing.T) {
w := &strings.Builder{}
queryBuilder := NewSourceQueryBuilder(mockAUR,
text.NewLogger(w, io.Discard, strings.NewReader(""), false, "test"),
tc.sortBy, parser.ModeAny, "", tc.bottomUp,
false, false)
queryBuilder.Execute(context.Background(), mockDB, []string{"yay"})
gotNames := make([]string, len(queryBuilder.results))
for i, result := range queryBuilder.results {
gotNames[i] = result.name
}
assert.Equal(t, tc.wantNames, gotNames)
})
}
}
func newYayQueryBuilderMocks() (*mock.DBExecutor, *mockaur.MockAUR) {
mockDB := &mock.DBExecutor{
ReposFn: func() []string {
// Match pacman.conf parsing order for source separation.
return []string{"core", "extra"}
},
SyncPackagesFn: func(pkgs ...string) []mock.IPackage {
mockDB := mock.NewDB("extra")
return []mock.IPackage{
&mock.Package{
PBase: "ruby-yard",
PName: "ruby-yard",
PVersion: "0.9.34-5",
PDescription: "YARD is a Ruby Documentation tool. The Y stands for \"Yay!\"",
PSize: 1,
PISize: 1,
PDB: mockDB,
PArchitecture: "x86_64",
},
}
},
LocalPackageFn: func(string) mock.IPackage {
return nil
},
}
mockAUR := &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return []aur.Pkg{
{
Description: "Yet another yogurt. Pacman wrapper and AUR helper written in go.",
FirstSubmitted: 1475688004,
ID: 1911141,
LastModified: 1765742501,
Maintainer: "jguer",
Name: "yay",
NumVotes: 2461,
OutOfDate: 0,
PackageBase: "yay",
PackageBaseID: 115973,
Popularity: 34.903162,
URL: "https://github.com/Jguer/yay",
URLPath: "/cgit/aur.git/snapshot/yay.tar.gz",
Version: "12.5.7-1",
},
{
Description: "Yet another yogurt. Pacman wrapper and AUR helper written in go. (development version)",
FirstSubmitted: 1517205142,
ID: 1911143,
LastModified: 1765742519,
Maintainer: "jguer",
Name: "yay-git",
NumVotes: 55,
OutOfDate: 0,
PackageBase: "yay-git",
PackageBaseID: 129573,
Popularity: 1.850171,
URL: "https://github.com/Jguer/yay",
URLPath: "/cgit/aur.git/snapshot/yay-git.tar.gz",
Version: "12.5.7.r0.g44dfda05-1",
},
}, nil
},
}
return mockDB, mockAUR
}
================================================
FILE: pkg/query/source.go
================================================
package query
import (
"context"
"github.com/Jguer/aur"
"github.com/hashicorp/go-multierror"
)
// queryAUR searches AUR and narrows based on subarguments.
func queryAUR(ctx context.Context,
aurClient aur.QueryClient,
pkgS []string, searchBy string,
) ([]aur.Pkg, error) {
var (
err error
by = getSearchBy(searchBy)
)
for _, word := range pkgS {
r, errM := aurClient.Get(ctx, &aur.Query{
Needles: []string{word},
By: by,
Contains: true,
})
if errM == nil {
return r, nil
}
err = multierror.Append(err, errM)
}
return nil, err
}
================================================
FILE: pkg/query/types.go
================================================
package query
import (
"fmt"
"strconv"
"github.com/Jguer/aur"
alpm "github.com/Jguer/dyalpm"
"github.com/leonelquinteros/gotext"
"github.com/Jguer/yay/v12/pkg/db"
"github.com/Jguer/yay/v12/pkg/text"
)
type Pkg = aur.Pkg
func getSearchBy(value string) aur.By {
switch value {
case "name":
return aur.Name
case "maintainer":
return aur.Maintainer
case "submitter":
return aur.Submitter
case "depends":
return aur.Depends
case "makedepends":
return aur.MakeDepends
case "optdepends":
return aur.OptDepends
case "checkdepends":
return aur.CheckDepends
case "provides":
return aur.Provides
case "conflicts":
return aur.Conflicts
case "replaces":
return aur.Replaces
case "groups":
return aur.Groups
case "keywords":
return aur.Keywords
case "comaintainers":
return aur.CoMaintainers
default:
return aur.NameDesc
}
}
func aurPkgSearchString(
pkg *aur.Pkg,
dbExecutor db.Executor,
singleLineResults bool,
) string {
linkText := text.Bold(text.ColorHash("aur")) + "/" + text.Bold(pkg.Name)
toPrint := text.CreateRepoLink("aur", "", pkg.Name, linkText) +
" " + text.Cyan(pkg.Version) +
text.Bold(" (+"+strconv.Itoa(pkg.NumVotes)) +
" " + text.Bold(strconv.FormatFloat(pkg.Popularity, 'f', 2, 64)+") ")
if pkg.Maintainer == "" {
toPrint += text.Bold(text.Red(gotext.Get("(Orphaned)"))) + " "
}
if pkg.OutOfDate != 0 {
toPrint += text.Bold(text.Red(gotext.Get("(Out-of-date: %s)", text.FormatTime(pkg.OutOfDate)))) + " "
}
if localPkg := dbExecutor.LocalPackage(pkg.Name); localPkg != nil {
if localPkg.Version() != pkg.Version {
toPrint += text.Bold(text.Green(gotext.Get("(Installed: %s)", localPkg.Version())))
} else {
toPrint += text.Bold(text.Green(gotext.Get("(Installed)")))
}
}
if singleLineResults {
toPrint += "\t"
} else {
toPrint += "\n "
}
toPrint += pkg.Description
return toPrint
}
// PrintSearch receives a RepoSearch type and outputs pretty text.
func syncPkgSearchString(pkg alpm.Package, dbExecutor db.Executor, singleLineResults bool) string {
linkText := text.Bold(text.ColorHash(pkg.DB().Name())) + "/" + text.Bold(pkg.Name())
toPrint := text.CreateRepoLink(pkg.DB().Name(), pkg.Architecture(), pkg.Name(), linkText) +
" " + text.Cyan(pkg.Version()) +
text.Bold(" ("+text.Human(pkg.Size())+
" "+text.Human(pkg.ISize())+") ")
packageGroups := dbExecutor.PackageGroups(pkg)
if len(packageGroups) != 0 {
toPrint += fmt.Sprint(packageGroups, " ")
}
if localPkg := dbExecutor.LocalPackage(pkg.Name()); localPkg != nil {
if localPkg.Version() != pkg.Version() {
toPrint += text.Bold(text.Green(gotext.Get("(Installed: %s)", localPkg.Version())))
} else {
toPrint += text.Bold(text.Green(gotext.Get("(Installed)")))
}
}
if singleLineResults {
toPrint += "\t"
} else {
toPrint += "\n "
}
toPrint += pkg.Description()
return toPrint
}
================================================
FILE: pkg/query/version_diff.go
================================================
package query
import (
"strings"
"unicode"
"github.com/Jguer/yay/v12/pkg/text"
alpm "github.com/Jguer/dyalpm"
)
func GetVersionDiff(oldVersion, newVersion string) (left, right string) {
if oldVersion == newVersion {
return oldVersion + text.Red(""), newVersion + text.Green("")
}
diffPosition := 0
checkWords := func(str string, index int, words ...string) bool {
// Make sure the word is not part of a longer word
ongoingWord := unicode.IsLetter(rune(str[index]))
if ongoingWord {
return false
}
for _, word := range words {
wordLength := len(word)
nextIndex := index + 1
if (index < len(str)-wordLength) &&
(str[nextIndex:(nextIndex+wordLength)] == word) {
return true
}
}
return false
}
for index, char := range oldVersion {
charIsSpecial := !unicode.IsLetter(char) && !unicode.IsNumber(char)
if (index >= len(newVersion)) || (char != rune(newVersion[index])) {
if charIsSpecial {
diffPosition = index
}
break
}
if charIsSpecial ||
(((index == len(oldVersion)-1) || (index == len(newVersion)-1)) &&
((len(oldVersion) != len(newVersion)) ||
(oldVersion[index] == newVersion[index]))) ||
checkWords(oldVersion, index, "rc", "pre", "alpha", "beta") {
diffPosition = index + 1
}
}
samePart := oldVersion[0:diffPosition]
left = samePart + text.Red(oldVersion[diffPosition:])
right = samePart + text.Green(newVersion[diffPosition:])
return left, right
}
func isDevelName(name string) bool {
for _, suffix := range []string{"git", "svn", "hg", "bzr", "nightly", "insiders-bin"} {
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())
}
================================================
FILE: pkg/query/version_diff_test.go
================================================
//go:build !integration
// +build !integration
package query
import (
"testing"
"github.com/Jguer/yay/v12/pkg/text"
)
func TestVersionDiff(t *testing.T) {
testCases := []struct {
name string
a string
b string
wantDiff string
}{
{
name: "1.0.0-1 -> 1.0.0-2",
a: "1.0.0-1",
b: "1.0.0-2",
wantDiff: "1.0.0-" + text.Red("1") + " " + "1.0.0-" + text.Green("2"),
},
{
name: "1.0.0-1 -> 1.0.1-1",
a: "1.0.0-1",
b: "1.0.1-1",
wantDiff: "1.0." + text.Red("0-1") + " " + "1.0." + text.Green("1-1"),
},
{
name: "3.0.0~alpha7-3 -> 3.0.0~alpha7-4",
a: "3.0.0~alpha7-3",
b: "3.0.0~alpha7-4",
wantDiff: "3.0.0~alpha7-" + text.Red("3") + " " + "3.0.0~alpha7-" + text.Green("4"),
},
{
name: "3.0.0~beta7-3 -> 3.0.0~beta8-3",
a: "3.0.0~beta7-3",
b: "3.0.0~beta8-3",
wantDiff: "3.0.0~" + text.Red("beta7-3") + " " + "3.0.0~" + text.Green("beta8-3"),
},
{
name: "23.04.r131.b1bfe05-1 -> 23.04.r131.b1bfe07-1",
a: "23.04.r131.b1bfe05-1",
b: "23.04.r131.b1bfe07-1",
wantDiff: "23.04.r131." + text.Red("b1bfe05-1") + " " + "23.04.r131." + text.Green("b1bfe07-1"),
},
{
name: "1.0.arch0-1 -> 1.0.arch1-2",
a: "1.0.arch0-1",
b: "1.0.arch1-2",
wantDiff: "1.0." + text.Red("arch0-1") + " " + "1.0." + text.Green("arch1-2"),
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
originalUseColor := text.UseColor
text.UseColor = true
left, right := GetVersionDiff(tc.a, tc.b)
gotDiff := left + " " + right
if gotDiff != tc.wantDiff {
t.Errorf("VersionDiff(%s, %s) = %s, want %s", tc.a, tc.b, gotDiff, tc.wantDiff)
}
text.UseColor = originalUseColor
})
}
}
================================================
FILE: pkg/runtime/pacman.go
================================================
package runtime
import (
"fmt"
"os"
"github.com/Jguer/yay/v12/pkg/settings/parser"
pacmanconf "github.com/Morganamilo/go-pacmanconf"
"golang.org/x/term"
)
func retrievePacmanConfig(cmdArgs *parser.Arguments, pacmanConfigPath string) (*pacmanconf.Config, bool, error) {
root := "/"
if value, _, exists := cmdArgs.GetArg("root", "r"); exists {
root = value
}
pacmanConf, stderr, err := pacmanconf.PacmanConf("--config", pacmanConfigPath, "--root", root)
if err != nil {
cmdErr := err
if stderr != "" {
cmdErr = fmt.Errorf("%w\n%s", err, stderr)
}
return nil, false, cmdErr
}
if dbPath, _, exists := cmdArgs.GetArg("dbpath", "b"); exists {
pacmanConf.DBPath = dbPath
}
if arch := cmdArgs.GetArgs("arch"); arch != nil {
pacmanConf.Architecture = append(pacmanConf.Architecture, arch...)
}
if ignoreArray := cmdArgs.GetArgs("ignore"); ignoreArray != nil {
pacmanConf.IgnorePkg = append(pacmanConf.IgnorePkg, ignoreArray...)
}
if ignoreGroupsArray := cmdArgs.GetArgs("ignoregroup"); ignoreGroupsArray != nil {
pacmanConf.IgnoreGroup = append(pacmanConf.IgnoreGroup, ignoreGroupsArray...)
}
if cacheArray := cmdArgs.GetArgs("cachedir"); cacheArray != nil {
pacmanConf.CacheDir = cacheArray
}
if gpgDir, _, exists := cmdArgs.GetArg("gpgdir"); exists {
pacmanConf.GPGDir = gpgDir
}
useColor := pacmanConf.Color && term.IsTerminal(int(os.Stdout.Fd()))
switch value, _, _ := cmdArgs.GetArg("color"); value {
case "always":
useColor = true
case "auto":
useColor = term.IsTerminal(int(os.Stdout.Fd()))
case "never":
useColor = false
}
return pacmanConf, useColor, nil
}
================================================
FILE: pkg/runtime/pacman_test.go
================================================
//go:build !integration
// +build !integration
package runtime
import (
"path/filepath"
"runtime"
"strings"
"testing"
"github.com/Morganamilo/go-pacmanconf"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/Jguer/yay/v12/pkg/settings/parser"
)
// normalizePath removes trailing slashes from paths (except for root "/").
func normalizePath(p string) string {
if p == "/" {
return p
}
return strings.TrimSuffix(p, "/")
}
// normalizePaths normalizes a slice of paths.
func normalizePaths(paths []string) []string {
result := make([]string, len(paths))
for i, p := range paths {
result[i] = normalizePath(p)
}
return result
}
// normalizePacmanConf normalizes directory paths in a pacmanconf.Config
// to handle differences between pacman versions (with/without trailing slashes).
func normalizePacmanConf(conf *pacmanconf.Config) {
conf.RootDir = normalizePath(conf.RootDir)
conf.DBPath = normalizePath(conf.DBPath)
conf.GPGDir = normalizePath(conf.GPGDir)
conf.CacheDir = normalizePaths(conf.CacheDir)
conf.HookDir = normalizePaths(conf.HookDir)
}
func TestPacmanConf(t *testing.T) {
t.Parallel()
path := "../../testdata/pacman.conf"
absPath, err := filepath.Abs(path)
require.NoError(t, err)
// detect the architecture of the system
expectedArch := []string{"x86_64"}
if runtime.GOARCH == "arm64" {
expectedArch = []string{"aarch64"}
}
expectedPacmanConf := &pacmanconf.Config{
RootDir: "/", DBPath: "/var/lib/pacman",
CacheDir: []string{"/var/cache/pacman/pkg"},
HookDir: []string{"/etc/pacman.d/hooks"},
GPGDir: "/etc/pacman.d/gnupg", LogFile: "/var/log/pacman.log",
HoldPkg: []string{"pacman", "glibc"}, IgnorePkg: []string{"xorm"},
IgnoreGroup: []string{"yorm"}, Architecture: expectedArch,
XferCommand: "/usr/bin/wget --passive-ftp -c -O %o %u",
NoUpgrade: []string(nil), NoExtract: []string(nil), CleanMethod: []string{"KeepInstalled"},
SigLevel: []string{"PackageRequired", "PackageTrustedOnly", "DatabaseOptional", "DatabaseTrustedOnly"},
LocalFileSigLevel: []string{"PackageOptional", "PackageTrustedOnly"},
RemoteFileSigLevel: []string{"PackageRequired", "PackageTrustedOnly"}, UseSyslog: true,
Color: true, UseDelta: 0, TotalDownload: false, CheckSpace: true,
VerbosePkgLists: true, DisableDownloadTimeout: false,
Repos: []pacmanconf.Repository{
{
Name: "core", Servers: []string{"Core"},
SigLevel: []string(nil), Usage: []string{"All"},
},
{
Name: "extra", Servers: []string{"Extra"}, SigLevel: []string(nil),
Usage: []string{"All"},
},
{
Name: "multilib", Servers: []string{"repo3", "multilib"},
SigLevel: []string(nil), Usage: []string{"All"},
},
},
}
pacmanConf, color, err := retrievePacmanConfig(parser.MakeArguments(), absPath)
assert.Nil(t, err)
assert.NotNil(t, pacmanConf)
assert.Equal(t, color, false)
// Normalize paths to handle differences between pacman versions
// (some versions include trailing slashes, some don't)
normalizePacmanConf(pacmanConf)
assert.EqualValues(t, expectedPacmanConf, pacmanConf)
}
================================================
FILE: pkg/runtime/runtime.go
================================================
package runtime
import (
"context"
"fmt"
"net/http"
"os"
"path/filepath"
"time"
"github.com/leonelquinteros/gotext"
"github.com/Jguer/yay/v12/pkg/query"
"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"
"github.com/Jguer/aur"
"github.com/Jguer/aur/metadata"
"github.com/Jguer/aur/rpc"
"github.com/Jguer/votar/pkg/vote"
"github.com/Morganamilo/go-pacmanconf"
"golang.org/x/net/proxy"
)
type Runtime struct {
Cfg *settings.Configuration
QueryBuilder query.Builder
PacmanConf *pacmanconf.Config
VCSStore vcs.Store
CmdBuilder exe.ICmdBuilder
HTTPClient *http.Client
VoteClient *vote.Client
AURClient aur.QueryClient
Logger *text.Logger
}
func NewRuntime(cfg *settings.Configuration, cmdArgs *parser.Arguments, version string) (*Runtime, error) {
logger := text.NewLogger(os.Stdout, os.Stderr, os.Stdin, cfg.Debug, "runtime")
runner := exe.NewOSRunner(logger.Child("runner"))
transport := http.DefaultTransport.(*http.Transport).Clone()
transport.IdleConnTimeout = 90 * time.Second
transport.TLSHandshakeTimeout = 30 * time.Second
transport.ResponseHeaderTimeout = 30 * time.Second
transport.MaxIdleConns = 100
transport.MaxIdleConnsPerHost = 10
if socks5Proxy := os.Getenv("SOCKS5_PROXY"); socks5Proxy != "" {
dialer, err := proxy.SOCKS5("tcp", socks5Proxy, nil, proxy.Direct)
if err != nil {
return nil, err
}
contextDialer, ok := dialer.(proxy.ContextDialer)
if !ok {
return nil, fmt.Errorf("SOCKS5 dialer does not support DialContext")
}
transport.DialContext = contextDialer.DialContext
}
httpClient := &http.Client{
Timeout: 30 * time.Second,
Transport: transport,
}
userAgent := fmt.Sprintf("Yay/%s", version)
voteClient, errVote := vote.NewClient(vote.WithUserAgent(userAgent),
vote.WithHTTPClient(httpClient))
if errVote != nil {
return nil, errVote
}
voteClient.SetCredentials(
os.Getenv("AUR_USERNAME"),
os.Getenv("AUR_PASSWORD"))
userAgentFn := func(ctx context.Context, req *http.Request) error {
req.Header.Set("User-Agent", userAgent)
return nil
}
var aurCache aur.QueryClient
aurCache, errAURCache := metadata.New(
metadata.WithHTTPClient(httpClient),
metadata.WithCacheFilePath(filepath.Join(cfg.BuildDir, "aur.json")),
metadata.WithRequestEditorFn(userAgentFn),
metadata.WithBaseURL(cfg.AURURL),
metadata.WithDebugLogger(logger.Debugln),
)
if errAURCache != nil {
return nil, fmt.Errorf(gotext.Get("failed to retrieve aur Cache")+": %w", errAURCache)
}
aurClient, errAUR := rpc.NewClient(
rpc.WithHTTPClient(httpClient),
rpc.WithBaseURL(cfg.AURRPCURL),
rpc.WithRequestEditorFn(userAgentFn),
rpc.WithLogFn(logger.Debugln))
if errAUR != nil {
return nil, errAUR
}
if cfg.UseRPC {
aurCache = aurClient
}
pacmanConf, useColor, err := retrievePacmanConfig(cmdArgs, cfg.PacmanConf)
if err != nil {
return nil, err
}
// FIXME: get rid of global
text.UseColor = useColor
cmdBuilder := exe.NewCmdBuilder(cfg, runner, logger.Child("cmdbuilder"), pacmanConf.DBPath)
vcsStore := vcs.NewInfoStore(
cfg.VCSFilePath, cmdBuilder,
logger.Child("vcs"))
if err := vcsStore.Load(); err != nil {
return nil, err
}
queryBuilder := query.NewSourceQueryBuilder(
aurClient,
logger.Child("mixed.querybuilder"), cfg.SortBy,
cfg.Mode, cfg.SearchBy,
cfg.BottomUp, cfg.SingleLineResults, cfg.SeparateSources)
run := &Runtime{
Cfg: cfg,
QueryBuilder: queryBuilder,
PacmanConf: pacmanConf,
VCSStore: vcsStore,
CmdBuilder: cmdBuilder,
HTTPClient: httpClient,
VoteClient: voteClient,
AURClient: aurCache,
Logger: logger,
}
return run, nil
}
================================================
FILE: pkg/runtime/runtime_test.go
================================================
//go:build !integration
// +build !integration
package runtime_test
import (
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/Jguer/yay/v12/pkg/runtime"
"github.com/Jguer/yay/v12/pkg/settings"
"github.com/Jguer/yay/v12/pkg/settings/parser"
)
func TestBuildRuntime(t *testing.T) {
t.Parallel()
path := "../../testdata/pacman.conf"
absPath, err := filepath.Abs(path)
require.NoError(t, err)
// Prepare test inputs
cfg := &settings.Configuration{
Debug: true,
UseRPC: false,
AURURL: "https://aur.archlinux.org",
AURRPCURL: "https://aur.archlinux.org/rpc",
BuildDir: "/tmp",
VCSFilePath: "",
PacmanConf: absPath,
}
cmdArgs := parser.MakeArguments()
version := "1.0.0"
// Call the function being tested
run, err := runtime.NewRuntime(cfg, cmdArgs, version)
require.NoError(t, err)
// Assert the function's output
assert.NotNil(t, run)
assert.NotNil(t, run.QueryBuilder)
assert.NotNil(t, run.PacmanConf)
assert.NotNil(t, run.VCSStore)
assert.NotNil(t, run.CmdBuilder)
assert.NotNil(t, run.HTTPClient)
assert.NotNil(t, run.VoteClient)
assert.NotNil(t, run.AURClient)
assert.NotNil(t, run.Logger)
}
================================================
FILE: pkg/settings/args.go
================================================
package settings
import (
"strconv"
"strings"
"github.com/Jguer/yay/v12/pkg/settings/parser"
)
func (c *Configuration) ParseCommandLine(a *parser.Arguments) error {
if err := a.Parse(); err != nil {
return err
}
c.extractYayOptions(a)
return nil
}
func (c *Configuration) extractYayOptions(a *parser.Arguments) {
for option, value := range a.Options {
if c.handleOption(option, value.First()) {
a.DelArg(option)
}
}
c.AURURL = strings.TrimRight(c.AURURL, "/")
// if AurRPCURL is set, use that for /rpc calls
if c.AURRPCURL == "" {
c.AURRPCURL = c.AURURL + "/rpc?"
return
}
if !strings.HasSuffix(c.AURRPCURL, "?") {
if strings.HasSuffix(c.AURRPCURL, "/rpc") {
c.AURRPCURL += "?"
} else {
c.AURRPCURL = strings.TrimRight(c.AURRPCURL, "/") + "/rpc?"
}
}
}
func (c *Configuration) handleOption(option, value string) bool {
boolValue, err := strconv.ParseBool(value)
if err != nil {
boolValue = true
}
switch option {
case "aururl":
c.AURURL = value
case "aurrpcurl":
c.AURRPCURL = value
case "save":
c.SaveConfig = boolValue
case "afterclean", "cleanafter":
c.CleanAfter = boolValue
case "keepsrc":
c.KeepSrc = boolValue
case "debug":
c.Debug = boolValue
return !boolValue
case "devel":
c.Devel = boolValue
case "topdown":
c.BottomUp = false
case "bottomup":
c.BottomUp = true
case "singlelineresults":
c.SingleLineResults = true
case "doublelineresults":
c.SingleLineResults = false
case "completioninterval":
n, err := strconv.Atoi(value)
if err == nil {
c.CompletionInterval = n
}
case "sortby":
c.SortBy = value
case "searchby":
c.SearchBy = value
case "noconfirm":
NoConfirm = boolValue
case "config":
c.PacmanConf = value
case "redownload":
c.ReDownload = "yes"
case "redownloadall":
c.ReDownload = "all"
case "noredownload":
c.ReDownload = "no"
case "rebuild":
c.ReBuild = parser.RebuildModeYes
case "rebuildall":
c.ReBuild = parser.RebuildModeAll
case "rebuildtree":
c.ReBuild = parser.RebuildModeTree
case "norebuild":
c.ReBuild = parser.RebuildModeNo
case "batchinstall":
c.BatchInstall = boolValue
case "answerclean":
c.AnswerClean = value
case "noanswerclean":
c.AnswerClean = ""
case "answerdiff":
c.AnswerDiff = value
case "noanswerdiff":
c.AnswerDiff = ""
case "answeredit":
c.AnswerEdit = value
case "noansweredit":
c.AnswerEdit = ""
case "answerupgrade":
c.AnswerUpgrade = value
case "noanswerupgrade":
c.AnswerUpgrade = ""
case "gpgflags":
c.GpgFlags = value
case "mflags":
c.MFlags = value
case "gitflags":
c.GitFlags = value
case "builddir":
c.BuildDir = value
case "editor":
c.Editor = value
case "editorflags":
c.EditorFlags = value
case "makepkg":
c.MakepkgBin = value
case "makepkgconf":
c.MakepkgConf = value
case "nomakepkgconf":
c.MakepkgConf = ""
case "pacman":
c.PacmanBin = value
case "git":
c.GitBin = value
case "gpg":
c.GpgBin = value
case "sudo":
c.SudoBin = value
case "sudoflags":
c.SudoFlags = value
case "requestsplitn":
n, err := strconv.Atoi(value)
if err == nil && n > 0 {
c.RequestSplitN = n
}
case "sudoloop":
c.SudoLoop = boolValue
case "provides":
c.Provides = boolValue
case "pgpfetch":
c.PGPFetch = boolValue
case "cleanmenu":
c.CleanMenu = boolValue
case "diffmenu":
c.DiffMenu = boolValue
case "editmenu":
c.EditMenu = boolValue
case "useask":
c.UseAsk = boolValue
case "combinedupgrade":
c.CombinedUpgrade = boolValue
case "a", "aur":
c.Mode = parser.ModeAUR
case "N", "repo":
c.Mode = parser.ModeRepo
case "removemake":
c.RemoveMake = "yes"
case "noremovemake":
c.RemoveMake = "no"
case "askremovemake":
c.RemoveMake = "ask"
case "askyesremovemake":
c.RemoveMake = "askyes"
case "separatesources":
c.SeparateSources = boolValue
default:
return false
}
return true
}
================================================
FILE: pkg/settings/config.go
================================================
package settings
import (
"bytes"
_ "embed"
"encoding/json"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"github.com/Jguer/yay/v12/pkg/settings/parser"
"github.com/Jguer/yay/v12/pkg/text"
"github.com/leonelquinteros/gotext"
"gopkg.in/ini.v1"
)
//go:embed yay.conf
var defaultsINI []byte
// HideMenus indicates if pacman's provider menus must be hidden.
var HideMenus = false
// NoConfirm indicates if user input should be skipped.
var NoConfirm = false
// Configuration stores yay's config.
type Configuration struct {
AURURL string `json:"aururl" ini:"AurUrl"`
AURRPCURL string `json:"aurrpcurl" ini:"AurRpcUrl"`
BuildDir string `json:"buildDir" ini:"BuildDir"`
Editor string `json:"editor" ini:"Editor"`
EditorFlags string `json:"editorflags" ini:"EditorFlags"`
MakepkgBin string `json:"makepkgbin" ini:"MakepkgBin"`
MakepkgConf string `json:"makepkgconf" ini:"MakepkgConf"`
PacmanBin string `json:"pacmanbin" ini:"PacmanBin"`
PacmanConf string `json:"pacmanconf" ini:"PacmanConf"`
ReDownload string `json:"redownload" ini:"ReDownload"`
AnswerClean string `json:"answerclean" ini:"AnswerClean"`
AnswerDiff string `json:"answerdiff" ini:"AnswerDiff"`
AnswerEdit string `json:"answeredit" ini:"AnswerEdit"`
AnswerUpgrade string `json:"answerupgrade" ini:"AnswerUpgrade"`
GitBin string `json:"gitbin" ini:"GitBin"`
GpgBin string `json:"gpgbin" ini:"GpgBin"`
GpgFlags string `json:"gpgflags" ini:"GpgFlags"`
MFlags string `json:"mflags" ini:"MFlags"`
SortBy string `json:"sortby" ini:"SortBy"`
SearchBy string `json:"searchby" ini:"SearchBy"`
GitFlags string `json:"gitflags" ini:"GitFlags"`
RemoveMake string `json:"removemake" ini:"RemoveMake"`
SudoBin string `json:"sudobin" ini:"SudoBin"`
SudoFlags string `json:"sudoflags" ini:"SudoFlags"`
Version string `json:"version" ini:"-"`
RequestSplitN int `json:"requestsplitn" ini:"RequestSplitN"`
CompletionInterval int `json:"completionrefreshtime" ini:"CompletionInterval"`
MaxConcurrentDownloads int `json:"maxconcurrentdownloads" ini:"MaxConcurrentDownloads"`
BottomUp bool `json:"bottomup" ini:"BottomUp"`
SudoLoop bool `json:"sudoloop" ini:"SudoLoop"`
Devel bool `json:"devel" ini:"Devel"`
CleanAfter bool `json:"cleanAfter" ini:"CleanAfter"`
KeepSrc bool `json:"keepSrc" ini:"KeepSrc"`
Provides bool `json:"provides" ini:"Provides"`
PGPFetch bool `json:"pgpfetch" ini:"PgpFetch"`
CleanMenu bool `json:"cleanmenu" ini:"CleanMenu"`
DiffMenu bool `json:"diffmenu" ini:"DiffMenu"`
EditMenu bool `json:"editmenu" ini:"EditMenu"`
CombinedUpgrade bool `json:"combinedupgrade" ini:"CombinedUpgrade"`
UseAsk bool `json:"useask" ini:"UseAsk"`
BatchInstall bool `json:"batchinstall" ini:"BatchInstall"`
SingleLineResults bool `json:"singlelineresults" ini:"SingleLineResults"`
SeparateSources bool `json:"separatesources" ini:"SeparateSources"`
Debug bool `json:"debug" ini:"Debug"`
UseRPC bool `json:"rpc" ini:"Rpc"`
DoubleConfirm bool `json:"doubleconfirm" ini:"DoubleConfirm"` // confirm install before and after build
CompletionPath string `json:"-" ini:"-"`
VCSFilePath string `json:"-" ini:"-"`
SaveConfig bool `json:"-" ini:"-"`
Mode parser.TargetMode `json:"-" ini:"-"`
ReBuild parser.RebuildMode `json:"rebuild" ini:"ReBuild"`
}
// Save writes yay config to INI file.
func (c *Configuration) Save(configPath, version string) error {
// Use INI config path instead of JSON
iniPath := GetINIConfigPath()
if iniPath == "" {
return fmt.Errorf("unable to determine config path")
}
return c.SaveINI(iniPath)
}
func (c *Configuration) expandEnv() {
c.AURURL = os.ExpandEnv(c.AURURL)
c.AURRPCURL = os.ExpandEnv(c.AURRPCURL)
c.BuildDir = expandEnvOrHome(c.BuildDir)
c.Editor = expandEnvOrHome(c.Editor)
c.EditorFlags = os.ExpandEnv(c.EditorFlags)
c.MakepkgBin = expandEnvOrHome(c.MakepkgBin)
c.MakepkgConf = expandEnvOrHome(c.MakepkgConf)
c.PacmanBin = expandEnvOrHome(c.PacmanBin)
c.PacmanConf = expandEnvOrHome(c.PacmanConf)
c.GpgFlags = os.ExpandEnv(c.GpgFlags)
c.MFlags = os.ExpandEnv(c.MFlags)
c.GitFlags = os.ExpandEnv(c.GitFlags)
c.SortBy = os.ExpandEnv(c.SortBy)
c.SearchBy = os.ExpandEnv(c.SearchBy)
c.GitBin = expandEnvOrHome(c.GitBin)
c.GpgBin = expandEnvOrHome(c.GpgBin)
c.SudoBin = expandEnvOrHome(c.SudoBin)
c.SudoFlags = os.ExpandEnv(c.SudoFlags)
c.ReDownload = os.ExpandEnv(c.ReDownload)
c.ReBuild = parser.RebuildMode(os.ExpandEnv(string(c.ReBuild)))
c.AnswerClean = os.ExpandEnv(c.AnswerClean)
c.AnswerDiff = os.ExpandEnv(c.AnswerDiff)
c.AnswerEdit = os.ExpandEnv(c.AnswerEdit)
c.AnswerUpgrade = os.ExpandEnv(c.AnswerUpgrade)
c.RemoveMake = os.ExpandEnv(c.RemoveMake)
}
func expandEnvOrHome(path string) string {
path = os.ExpandEnv(path)
if strings.HasPrefix(path, "~/") {
path = filepath.Join(os.Getenv("HOME"), path[2:])
}
return path
}
func (c *Configuration) String() string {
var buf bytes.Buffer
enc := json.NewEncoder(&buf)
enc.SetIndent("", "\t")
if err := enc.Encode(c); err != nil {
fmt.Fprintln(os.Stderr, err)
}
return buf.String()
}
// check privilege elevator exists otherwise try to find another one.
func (c *Configuration) setPrivilegeElevator() error {
if auth := os.Getenv("PACMAN_AUTH"); auth != "" {
c.SudoBin = auth
if auth != "sudo" {
c.SudoFlags = ""
c.SudoLoop = false
}
}
for _, bin := range [...]string{c.SudoBin, "sudo"} {
if _, err := exec.LookPath(bin); err == nil {
c.SudoBin = bin
return nil // wrapper or sudo command existing. Retrocompatiblity
}
}
c.SudoFlags = ""
c.SudoLoop = false
for _, bin := range [...]string{"run0", "doas", "pkexec", "su"} {
if _, err := exec.LookPath(bin); err == nil {
c.SudoBin = bin
return nil // command existing
}
}
return &ErrPrivilegeElevatorNotFound{confValue: c.SudoBin}
}
func DefaultConfig(version string) *Configuration {
cfg := &Configuration{
Version: version,
Mode: parser.ModeAny,
}
// Load defaults from embedded INI
iniCfg, err := ini.LoadSources(ini.LoadOptions{
AllowBooleanKeys: true,
Insensitive: true,
InsensitiveSections: true,
IgnoreInlineComment: true,
}, defaultsINI)
if err != nil {
// Fallback to minimal defaults if embedded config fails
cfg.AURURL = "https://aur.archlinux.org"
cfg.BuildDir = os.ExpandEnv("$HOME/.cache/yay")
return cfg
}
// Map the default section
_ = iniCfg.Section("").MapTo(cfg)
// Also map [options] section if present
if iniCfg.HasSection("options") {
_ = iniCfg.Section("options").MapTo(cfg)
}
return cfg
}
func NewConfig(logger *text.Logger, configPath, version string) (*Configuration, error) {
newConfig := DefaultConfig(version)
cacheHome, errCache := getCacheHome()
if errCache != nil && logger != nil {
logger.Errorln(errCache)
}
newConfig.BuildDir = cacheHome
newConfig.CompletionPath = filepath.Join(cacheHome, completionFileName)
newConfig.VCSFilePath = filepath.Join(cacheHome, vcsFileName)
// Load system-wide INI config first (silently ignored if not present)
if err := newConfig.loadINI(SystemConfigPath); err != nil && logger != nil {
logger.Errorln(err)
}
// Load user JSON config (legacy, overrides system config)
newConfig.load(configPath)
// Load user INI config (takes priority over JSON when both exist)
userINIPath := GetINIConfigPath()
if userINIPath != "" {
if err := newConfig.loadINI(userINIPath); err != nil && logger != nil {
logger.Errorln(err)
}
}
if aurdest := os.Getenv("AURDEST"); aurdest != "" {
newConfig.BuildDir = aurdest
}
newConfig.expandEnv()
if newConfig.BuildDir != systemdCache {
errBuildDir := initDir(newConfig.BuildDir)
if errBuildDir != nil {
return nil, errBuildDir
}
}
if errPE := newConfig.setPrivilegeElevator(); errPE != nil {
return nil, errPE
}
return newConfig, nil
}
func (c *Configuration) load(configPath string) {
cfile, err := os.Open(configPath)
if !os.IsNotExist(err) && err != nil {
fmt.Fprintln(os.Stderr,
gotext.Get("failed to open config file '%s': %s", configPath, err))
return
}
defer cfile.Close()
if !os.IsNotExist(err) {
decoder := json.NewDecoder(cfile)
if err = decoder.Decode(c); err != nil {
fmt.Fprintln(os.Stderr,
gotext.Get("failed to read config file '%s': %s", configPath, err))
}
}
}
================================================
FILE: pkg/settings/config_test.go
================================================
//go:build !integration
// +build !integration
package settings
import (
"encoding/json"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// GIVEN a non existing build dir in the config
// WHEN the config is loaded
// THEN the directory should be created
func TestNewConfig(t *testing.T) {
configDir := t.TempDir()
err := os.MkdirAll(filepath.Join(configDir, "yay"), 0o755)
assert.NoError(t, err)
t.Setenv("XDG_CONFIG_HOME", configDir)
cacheDir := t.TempDir()
config := map[string]string{"BuildDir": filepath.Join(cacheDir, "test-build-dir")}
f, err := os.Create(filepath.Join(configDir, "yay", "config.json"))
assert.NoError(t, err)
defer f.Close()
configJSON, _ := json.Marshal(config)
_, err = f.WriteString(string(configJSON))
assert.NoError(t, err)
newConfig, err := NewConfig(nil, GetConfigPath(), "v1.0.0")
assert.NoError(t, err)
assert.Equal(t, filepath.Join(cacheDir, "test-build-dir"), newConfig.BuildDir)
_, err = os.Stat(filepath.Join(cacheDir, "test-build-dir"))
assert.NoError(t, err)
}
// GIVEN a non existing build dir in the config and AURDEST set to a non-existing folder
// WHEN the config is loaded
// THEN the directory of AURDEST should be created and selected
func TestNewConfigAURDEST(t *testing.T) {
configDir := t.TempDir()
err := os.MkdirAll(filepath.Join(configDir, "yay"), 0o755)
assert.NoError(t, err)
t.Setenv("XDG_CONFIG_HOME", configDir)
cacheDir := t.TempDir()
config := map[string]string{"BuildDir": filepath.Join(cacheDir, "test-other-dir")}
t.Setenv("AURDEST", filepath.Join(cacheDir, "test-build-dir"))
f, err := os.Create(filepath.Join(configDir, "yay", "config.json"))
assert.NoError(t, err)
defer f.Close()
configJSON, _ := json.Marshal(config)
_, err = f.WriteString(string(configJSON))
assert.NoError(t, err)
newConfig, err := NewConfig(nil, GetConfigPath(), "v1.0.0")
assert.NoError(t, err)
assert.Equal(t, filepath.Join(cacheDir, "test-build-dir"), newConfig.BuildDir)
_, err = os.Stat(filepath.Join(cacheDir, "test-build-dir"))
assert.NoError(t, err)
}
// Test tilde expansion in AURDEST
func TestNewConfigAURDESTTildeExpansion(t *testing.T) {
configDir := t.TempDir()
err := os.MkdirAll(filepath.Join(configDir, "yay"), 0o755)
assert.NoError(t, err)
t.Setenv("XDG_CONFIG_HOME", configDir)
homeDir := t.TempDir()
cacheDir := t.TempDir()
config := map[string]string{"BuildDir": filepath.Join(cacheDir, "test-other-dir")}
t.Setenv("AURDEST", "~/test-build-dir")
t.Setenv("HOME", homeDir)
f, err := os.Create(filepath.Join(configDir, "yay", "config.json"))
assert.NoError(t, err)
defer f.Close()
configJSON, _ := json.Marshal(config)
_, err = f.WriteString(string(configJSON))
assert.NoError(t, err)
newConfig, err := NewConfig(nil, GetConfigPath(), "v1.0.0")
assert.NoError(t, err)
assert.Equal(t, filepath.Join(homeDir, "test-build-dir"), newConfig.BuildDir)
_, err = os.Stat(filepath.Join(homeDir, "test-build-dir"))
assert.NoError(t, err)
}
// GIVEN default config
// WHEN setPrivilegeElevator gets called
// THEN sudobin should stay as "sudo" (given sudo exists)
func TestConfiguration_setPrivilegeElevator(t *testing.T) {
path := t.TempDir()
doas := filepath.Join(path, "sudo")
_, err := os.Create(doas)
os.Chmod(doas, 0o755)
assert.NoError(t, err)
config := DefaultConfig("test")
config.SudoLoop = true
config.SudoFlags = "-v"
t.Setenv("PATH", path)
err = config.setPrivilegeElevator()
assert.NoError(t, err)
assert.Equal(t, "sudo", config.SudoBin)
assert.Equal(t, "-v", config.SudoFlags)
assert.True(t, config.SudoLoop)
}
// GIVEN default config and sudo loop enabled
// GIVEN only su in path
// WHEN setPrivilegeElevator gets called
// THEN sudobin should be changed to "su"
func TestConfiguration_setPrivilegeElevator_su(t *testing.T) {
path := t.TempDir()
doas := filepath.Join(path, "su")
_, err := os.Create(doas)
os.Chmod(doas, 0o755)
assert.NoError(t, err)
config := DefaultConfig("test")
config.SudoLoop = true
config.SudoFlags = "-v"
t.Setenv("PATH", path)
err = config.setPrivilegeElevator()
assert.NoError(t, err)
assert.Equal(t, "su", config.SudoBin)
assert.Equal(t, "", config.SudoFlags)
assert.False(t, config.SudoLoop)
}
// GIVEN default config and sudo loop enabled
// GIVEN no sudo in path
// WHEN setPrivilegeElevator gets called
// THEN sudobin should be changed to "su"
func TestConfiguration_setPrivilegeElevator_no_path(t *testing.T) {
t.Setenv("PATH", "")
config := DefaultConfig("test")
config.SudoLoop = true
config.SudoFlags = "-v"
err := config.setPrivilegeElevator()
assert.Error(t, err)
assert.Equal(t, "sudo", config.SudoBin)
assert.Equal(t, "", config.SudoFlags)
assert.False(t, config.SudoLoop)
}
// GIVEN default config and sudo loop enabled
// GIVEN doas in path
// WHEN setPrivilegeElevator gets called
// THEN sudobin should be changed to "doas"
func TestConfiguration_setPrivilegeElevator_doas(t *testing.T) {
path := t.TempDir()
doas := filepath.Join(path, "doas")
_, err := os.Create(doas)
os.Chmod(doas, 0o755)
assert.NoError(t, err)
config := DefaultConfig("test")
config.SudoLoop = true
config.SudoFlags = "-v"
t.Setenv("PATH", path)
err = config.setPrivilegeElevator()
assert.NoError(t, err)
assert.Equal(t, "doas", config.SudoBin)
assert.Equal(t, "", config.SudoFlags)
assert.False(t, config.SudoLoop)
}
// GIVEN default config and sudo loop enabled
// GIVEN run0 in path
// WHEN setPrivilegeElevator gets called
// THEN sudobin should be changed to "run0"
func TestConfiguration_setPrivilegeElevator_run0(t *testing.T) {
path := t.TempDir()
doas := filepath.Join(path, "run0")
_, err := os.Create(doas)
os.Chmod(doas, 0o755)
assert.NoError(t, err)
config := DefaultConfig("test")
config.SudoLoop = true
config.SudoFlags = "-v"
t.Setenv("PATH", path)
err = config.setPrivilegeElevator()
assert.NoError(t, err)
assert.Equal(t, "run0", config.SudoBin)
assert.Equal(t, "", config.SudoFlags)
assert.False(t, config.SudoLoop)
}
// GIVEN config with wrapper and sudo loop enabled
// GIVEN wrapper is in path
// WHEN setPrivilegeElevator gets called
// THEN sudobin should be kept as the wrapper
func TestConfiguration_setPrivilegeElevator_custom_script(t *testing.T) {
path := t.TempDir()
wrapper := filepath.Join(path, "custom-wrapper")
_, err := os.Create(wrapper)
os.Chmod(wrapper, 0o755)
assert.NoError(t, err)
config := DefaultConfig("test")
config.SudoLoop = true
config.SudoBin = wrapper
config.SudoFlags = "-v"
t.Setenv("PATH", path)
err = config.setPrivilegeElevator()
assert.NoError(t, err)
assert.Equal(t, wrapper, config.SudoBin)
assert.Equal(t, "-v", config.SudoFlags)
assert.True(t, config.SudoLoop)
}
// GIVEN default config and sudo loop enabled
// GIVEN doas as PACMAN_AUTH env variable
// WHEN setPrivilegeElevator gets called
// THEN sudobin should be changed to "doas"
func TestConfiguration_setPrivilegeElevator_pacman_auth_doas(t *testing.T) {
path := t.TempDir()
doas := filepath.Join(path, "doas")
_, err := os.Create(doas)
os.Chmod(doas, 0o755)
require.NoError(t, err)
sudo := filepath.Join(path, "sudo")
_, err = os.Create(sudo)
os.Chmod(sudo, 0o755)
require.NoError(t, err)
config := DefaultConfig("test")
config.SudoBin = "sudo"
config.SudoLoop = true
config.SudoFlags = "-v"
t.Setenv("PACMAN_AUTH", "doas")
t.Setenv("PATH", path)
err = config.setPrivilegeElevator()
assert.NoError(t, err)
assert.Equal(t, "doas", config.SudoBin)
assert.Equal(t, "", config.SudoFlags)
assert.False(t, config.SudoLoop)
}
// GIVEN config with doas configed and sudo loop enabled
// GIVEN sudo as PACMAN_AUTH env variable
// WHEN setPrivilegeElevator gets called
// THEN sudobin should be changed to "sudo"
func TestConfiguration_setPrivilegeElevator_pacman_auth_sudo(t *testing.T) {
path := t.TempDir()
doas := filepath.Join(path, "doas")
_, err := os.Create(doas)
os.Chmod(doas, 0o755)
require.NoError(t, err)
sudo := filepath.Join(path, "sudo")
_, err = os.Create(sudo)
os.Chmod(sudo, 0o755)
require.NoError(t, err)
config := DefaultConfig("test")
config.SudoBin = "doas"
config.SudoLoop = true
config.SudoFlags = "-v"
t.Setenv("PACMAN_AUTH", "sudo")
t.Setenv("PATH", path)
err = config.setPrivilegeElevator()
assert.NoError(t, err)
assert.Equal(t, "sudo", config.SudoBin)
assert.Equal(t, "-v", config.SudoFlags)
assert.True(t, config.SudoLoop)
}
================================================
FILE: pkg/settings/dirs.go
================================================
package settings
import (
"os"
"path/filepath"
)
const (
configFileName string = "config.json" // configFileName holds the name of the config file.
iniConfigFileName string = "yay.conf" // iniConfigFileName holds the name of the INI config file.
vcsFileName string = "vcs.json" // vcsFileName holds the name of the vcs file.
completionFileName string = "completion.cache"
systemdCache string = "/var/cache/yay" // systemd should handle cache creation
)
func GetConfigPath() string {
if configHome := os.Getenv("XDG_CONFIG_HOME"); configHome != "" {
configDir := filepath.Join(configHome, "yay")
if err := initDir(configDir); err == nil {
return filepath.Join(configDir, configFileName)
}
}
if configHome := os.Getenv("HOME"); configHome != "" {
configDir := filepath.Join(configHome, ".config", "yay")
if err := initDir(configDir); err == nil {
return filepath.Join(configDir, configFileName)
}
}
return ""
}
// GetINIConfigPath returns the path to the user's INI config file (yay.conf).
// This is used for both loading (with priority over JSON) and saving.
func GetINIConfigPath() string {
if configHome := os.Getenv("XDG_CONFIG_HOME"); configHome != "" {
configDir := filepath.Join(configHome, "yay")
if err := initDir(configDir); err == nil {
return filepath.Join(configDir, iniConfigFileName)
}
}
if configHome := os.Getenv("HOME"); configHome != "" {
configDir := filepath.Join(configHome, ".config", "yay")
if err := initDir(configDir); err == nil {
return filepath.Join(configDir, iniConfigFileName)
}
}
return ""
}
func getCacheHome() (string, error) {
uid := os.Geteuid()
if cacheHome := os.Getenv("XDG_CACHE_HOME"); cacheHome != "" && uid != 0 {
cacheDir := filepath.Join(cacheHome, "yay")
if err := initDir(cacheDir); err == nil {
return cacheDir, nil
}
}
if cacheHome := os.Getenv("HOME"); cacheHome != "" && uid != 0 {
cacheDir := filepath.Join(cacheHome, ".cache", "yay")
if err := initDir(cacheDir); err == nil {
return cacheDir, nil
}
}
if uid == 0 && os.Getenv("SUDO_USER") == "" && os.Getenv("DOAS_USER") == "" {
return systemdCache, nil // Don't create directory if systemd-run takes care of it
}
tmpDir := filepath.Join(os.TempDir(), "yay")
return tmpDir, initDir(tmpDir)
}
func initDir(dir string) error {
if _, err := os.Stat(dir); os.IsNotExist(err) {
if err = os.MkdirAll(dir, 0o755); err != nil {
return &ErrRuntimeDir{inner: err, dir: dir}
}
} else if err != nil {
return err
}
return nil
}
================================================
FILE: pkg/settings/dirs_test.go
================================================
//go:build !integration
// +build !integration
package settings
import (
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// GIVEN no user directories and sudo user
// WHEN cache home is selected
// THEN the selected cache home should be in the tmp dir
func Test_getCacheHome(t *testing.T) {
dir := t.TempDir()
require.NoError(t, os.Unsetenv("XDG_CACHE_HOME"))
require.NoError(t, os.Unsetenv("HOME"))
t.Setenv("SUDO_USER", "test")
t.Setenv("TMPDIR", dir)
got, err := getCacheHome()
require.NoError(t, err)
assert.Equal(t, filepath.Join(dir, "yay"), got)
require.NoError(t, os.Unsetenv("TMPDIR"))
require.NoError(t, os.Unsetenv("SUDO_USER"))
}
================================================
FILE: pkg/settings/errors.go
================================================
package settings
import (
"fmt"
"github.com/leonelquinteros/gotext"
)
type ErrPrivilegeElevatorNotFound struct {
confValue string
}
func (e *ErrPrivilegeElevatorNotFound) Error() string {
return fmt.Sprintf("unable to find a privilege elevator, config value: %s", e.confValue)
}
type ErrRuntimeDir struct {
inner error
dir string
}
func (e *ErrRuntimeDir) Error() string {
return gotext.Get("failed to create directory '%s': %s", e.dir, e.inner)
}
type ErrUserAbort struct{}
func (e ErrUserAbort) Error() string {
return gotext.Get("aborting due to user")
}
================================================
FILE: pkg/settings/exe/cmd_builder.go
================================================
package exe
import (
"context"
"fmt"
"os"
"os/exec"
"os/user"
"path/filepath"
"strconv"
"strings"
"syscall"
"time"
mapset "github.com/deckarep/golang-set/v2"
"github.com/leonelquinteros/gotext"
"github.com/Jguer/yay/v12/pkg/settings"
"github.com/Jguer/yay/v12/pkg/settings/parser"
"github.com/Jguer/yay/v12/pkg/text"
)
const SudoLoopDuration = 241
var gitDenyList = mapset.NewThreadUnsafeSet(
"GIT_WORK_TREE",
"GIT_DIR",
)
type GitCmdBuilder interface {
Runner
BuildGitCmd(ctx context.Context, dir string, extraArgs ...string) *exec.Cmd
}
type ICmdBuilder interface {
Runner
BuildGitCmd(ctx context.Context, dir string, extraArgs ...string) *exec.Cmd
BuildGPGCmd(ctx context.Context, extraArgs ...string) *exec.Cmd
BuildMakepkgCmd(ctx context.Context, dir string, extraArgs ...string) *exec.Cmd
BuildPacmanCmd(ctx context.Context, args *parser.Arguments, mode parser.TargetMode, noConfirm bool) *exec.Cmd
AddMakepkgFlag(string)
GetKeepSrc() bool
SudoLoop()
}
type CmdBuilder struct {
GitBin string
GitFlags []string
GPGBin string
GPGFlags []string
MakepkgFlags []string
MakepkgConfPath string
MakepkgBin string
SudoBin string
SudoFlags []string
SudoLoopEnabled bool
PacmanBin string
PacmanConfigPath string
PacmanDBPath string
KeepSrc bool
Runner Runner
Log *text.Logger
}
func NewCmdBuilder(cfg *settings.Configuration, runner Runner, logger *text.Logger, dbPath string) *CmdBuilder {
return &CmdBuilder{
GitBin: cfg.GitBin,
GitFlags: strings.Fields(cfg.GitFlags),
GPGBin: cfg.GpgBin,
GPGFlags: strings.Fields(cfg.GpgFlags),
MakepkgFlags: strings.Fields(cfg.MFlags),
MakepkgConfPath: cfg.MakepkgConf,
MakepkgBin: cfg.MakepkgBin,
SudoBin: cfg.SudoBin,
SudoFlags: strings.Fields(cfg.SudoFlags),
SudoLoopEnabled: cfg.SudoLoop,
PacmanBin: cfg.PacmanBin,
PacmanConfigPath: cfg.PacmanConf,
PacmanDBPath: dbPath,
KeepSrc: cfg.KeepSrc,
Runner: runner,
Log: logger,
}
}
func (c *CmdBuilder) BuildGPGCmd(ctx context.Context, extraArgs ...string) *exec.Cmd {
args := make([]string, len(c.GPGFlags), len(c.GPGFlags)+len(extraArgs))
copy(args, c.GPGFlags)
if len(extraArgs) > 0 {
args = append(args, extraArgs...)
}
cmd := exec.CommandContext(ctx, c.GPGBin, args...)
cmd = c.deElevateCommand(ctx, cmd)
return cmd
}
func gitFilteredEnv() []string {
var env []string
for _, envVar := range os.Environ() {
envKey := strings.SplitN(envVar, "=", 2)[0]
if !gitDenyList.Contains(envKey) {
env = append(env, envVar)
}
}
env = append(env, "GIT_TERMINAL_PROMPT=0")
return env
}
func (c *CmdBuilder) BuildGitCmd(ctx context.Context, dir string, extraArgs ...string) *exec.Cmd {
args := make([]string, len(c.GitFlags), len(c.GitFlags)+len(extraArgs))
copy(args, c.GitFlags)
if dir != "" {
args = append(args, "-C", dir)
}
if len(extraArgs) > 0 {
args = append(args, extraArgs...)
}
cmd := exec.CommandContext(ctx, c.GitBin, args...)
cmd.Env = gitFilteredEnv()
cmd = c.deElevateCommand(ctx, cmd)
return cmd
}
func (c *CmdBuilder) AddMakepkgFlag(flag string) {
c.MakepkgFlags = append(c.MakepkgFlags, flag)
}
func (c *CmdBuilder) BuildMakepkgCmd(ctx context.Context, dir string, extraArgs ...string) *exec.Cmd {
args := make([]string, len(c.MakepkgFlags), len(c.MakepkgFlags)+len(extraArgs))
copy(args, c.MakepkgFlags)
if c.MakepkgConfPath != "" {
args = append(args, "--config", c.MakepkgConfPath)
}
if len(extraArgs) > 0 {
args = append(args, extraArgs...)
}
cmd := exec.CommandContext(ctx, c.MakepkgBin, args...)
cmd.Dir = dir
cmd = c.deElevateCommand(ctx, cmd)
return cmd
}
// deElevateCommand, `systemd-run` code based on pikaur.
func (c *CmdBuilder) deElevateCommand(ctx context.Context, cmd *exec.Cmd) *exec.Cmd {
if os.Geteuid() != 0 {
return cmd
}
ogCaller := ""
if caller := os.Getenv("SUDO_USER"); caller != "" {
ogCaller = caller
} else if caller := os.Getenv("DOAS_USER"); caller != "" {
ogCaller = caller
}
if userFound, err := user.Lookup(ogCaller); err == nil {
cmd.SysProcAttr = &syscall.SysProcAttr{}
uid64, errUid := strconv.ParseUint(userFound.Uid, 10, 32)
gid64, errGid := strconv.ParseUint(userFound.Gid, 10, 32)
if errUid == nil && errGid == nil {
cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid64), Gid: uint32(gid64)}
return cmd
}
}
cmdArgs := []string{
"--service-type=oneshot",
"--pipe", "--wait", "--pty", "--quiet",
"-p", "DynamicUser=yes",
"-p", "CacheDirectory=yay",
"-E", "HOME=/tmp",
}
if cmd.Dir != "" {
cmdArgs = append(cmdArgs, "-p", fmt.Sprintf("WorkingDirectory=%s", cmd.Dir))
}
for _, envVarName := range [...]string{"http_proxy", "https_proxy", "ftp_proxy"} {
if env := os.Getenv(envVarName); env != "" {
cmdArgs = append(cmdArgs, "-E", fmt.Sprintf("%s=%s", envVarName, env))
}
}
path, _ := exec.LookPath(cmd.Args[0])
cmdArgs = append(cmdArgs, path)
cmdArgs = append(cmdArgs, cmd.Args[1:]...)
systemdCmd := exec.CommandContext(ctx, "systemd-run", cmdArgs...) // #nosec G702
systemdCmd.Dir = cmd.Dir
return systemdCmd
}
func (c *CmdBuilder) buildPrivilegeElevatorCommand(ctx context.Context, ogArgs []string) *exec.Cmd {
if c.SudoBin == "su" {
return exec.CommandContext(ctx, c.SudoBin, "-c", strings.Join(ogArgs, " "))
}
argArr := make([]string, 0, len(c.SudoFlags)+len(ogArgs))
argArr = append(argArr, c.SudoFlags...)
argArr = append(argArr, ogArgs...)
return exec.CommandContext(ctx, c.SudoBin, argArr...)
}
func (c *CmdBuilder) BuildPacmanCmd(ctx context.Context, args *parser.Arguments, mode parser.TargetMode, noConfirm bool) *exec.Cmd {
argArr := make([]string, 0, 32)
needsRoot := args.NeedRoot(mode)
argArr = append(argArr, c.PacmanBin)
argArr = append(argArr, args.FormatGlobals()...)
argArr = append(argArr, args.FormatArgs()...)
if noConfirm {
argArr = append(argArr, "--noconfirm")
}
argArr = append(argArr, "--config", c.PacmanConfigPath, "--")
argArr = append(argArr, args.Targets...)
if needsRoot {
c.waitLock(c.PacmanDBPath)
if os.Geteuid() != 0 {
return c.buildPrivilegeElevatorCommand(ctx, argArr)
}
}
return exec.CommandContext(ctx, argArr[0], argArr[1:]...)
}
// waitLock will lock yay checking the status of db.lck until it does not exist.
func (c *CmdBuilder) waitLock(dbPath string) {
lockDBPath := filepath.Join(dbPath, "db.lck")
if _, err := os.Stat(lockDBPath); err != nil {
return
}
c.Log.Warnln(gotext.Get("%s is present.", lockDBPath))
c.Log.Warn(gotext.Get("There may be another Pacman instance running. Waiting..."))
for {
time.Sleep(3 * time.Second)
if _, err := os.Stat(lockDBPath); err != nil {
c.Log.Println()
return
}
}
}
func (c *CmdBuilder) SudoLoop() {
c.updateSudo()
go c.sudoLoopBackground()
}
func (c *CmdBuilder) sudoLoopBackground() {
for {
c.updateSudo()
time.Sleep(SudoLoopDuration * time.Second)
}
}
func (c *CmdBuilder) updateSudo() {
for {
err := c.Show(exec.CommandContext(context.Background(), c.SudoBin, "-v"))
if err != nil {
c.Log.Errorln(err)
} else {
break
}
}
}
func (c *CmdBuilder) Show(cmd *exec.Cmd) error {
return c.Runner.Show(cmd)
}
func (c *CmdBuilder) Capture(cmd *exec.Cmd) (stdout, stderr string, err error) {
return c.Runner.Capture(cmd)
}
func (c *CmdBuilder) GetKeepSrc() bool {
return c.KeepSrc
}
================================================
FILE: pkg/settings/exe/exec.go
================================================
package exe
import (
"errors"
"os"
"os/exec"
"strings"
"syscall"
"github.com/Jguer/yay/v12/pkg/text"
)
type Runner interface {
Capture(cmd *exec.Cmd) (stdout string, stderr string, err error)
Show(cmd *exec.Cmd) error
}
type OSRunner struct {
Log *text.Logger
}
func NewOSRunner(log *text.Logger) *OSRunner {
return &OSRunner{log}
}
func (r *OSRunner) Show(cmd *exec.Cmd) error {
cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
cmd.SysProcAttr = &syscall.SysProcAttr{
Pdeathsig: syscall.SIGTERM,
}
r.Log.Debugln("running", cmd.String())
return cmd.Run()
}
func (r *OSRunner) Capture(cmd *exec.Cmd) (stdout, stderr string, err error) {
r.Log.Debugln("capturing", cmd.String())
cmd.SysProcAttr = &syscall.SysProcAttr{
Pdeathsig: syscall.SIGTERM,
}
outbuf, err := cmd.Output()
stdout = strings.TrimSpace(string(outbuf))
if err != nil {
exitErr := &exec.ExitError{}
if errors.As(err, &exitErr) {
stderr = strings.TrimSpace(string(exitErr.Stderr))
}
}
return stdout, stderr, err
}
================================================
FILE: pkg/settings/exe/mock.go
================================================
package exe
import (
"context"
"fmt"
"os/exec"
"sync"
"github.com/Jguer/yay/v12/pkg/settings/parser"
)
type Call struct {
Res []any
Args []any
Dir string
}
func (c *Call) String() string {
return fmt.Sprintf("%+v", c.Args)
}
type MockBuilder struct {
Runner Runner
BuildMakepkgCmdCallsMu sync.Mutex
BuildMakepkgCmdCalls []Call
BuildMakepkgCmdFn func(ctx context.Context, dir string, extraArgs ...string) *exec.Cmd
BuildPacmanCmdFn func(ctx context.Context, args *parser.Arguments, mode parser.TargetMode, noConfirm bool) *exec.Cmd
GetKeepSrcFn func() bool
}
type MockRunner struct {
ShowCallsMu sync.Mutex
ShowCalls []Call
CaptureCallsMu sync.Mutex
CaptureCalls []Call
ShowFn func(cmd *exec.Cmd) error
CaptureFn func(cmd *exec.Cmd) (stdout string, stderr string, err error)
}
func (m *MockBuilder) BuildGPGCmd(ctx context.Context, extraArgs ...string) *exec.Cmd {
return exec.CommandContext(ctx, "gpg", extraArgs...)
}
func (m *MockBuilder) BuildMakepkgCmd(ctx context.Context, dir string, extraArgs ...string) *exec.Cmd {
var res *exec.Cmd
if m.BuildMakepkgCmdFn != nil {
res = m.BuildMakepkgCmdFn(ctx, dir, extraArgs...)
} else {
res = exec.CommandContext(ctx, "makepkg", extraArgs...)
}
m.BuildMakepkgCmdCallsMu.Lock()
m.BuildMakepkgCmdCalls = append(m.BuildMakepkgCmdCalls, Call{
Res: []any{res},
Args: []any{
ctx,
dir,
extraArgs,
},
})
m.BuildMakepkgCmdCallsMu.Unlock()
return res
}
func (m *MockBuilder) AddMakepkgFlag(flag string) {
}
func (m *MockBuilder) BuildGitCmd(ctx context.Context, dir string, extraArgs ...string) *exec.Cmd {
return exec.CommandContext(ctx, "git", extraArgs...)
}
func (m *MockBuilder) BuildPacmanCmd(ctx context.Context, args *parser.Arguments, mode parser.TargetMode, noConfirm bool) *exec.Cmd {
var res *exec.Cmd
if m.BuildPacmanCmdFn != nil {
res = m.BuildPacmanCmdFn(ctx, args, mode, noConfirm)
} else {
res = exec.CommandContext(ctx, "pacman")
}
return res
}
func (m *MockBuilder) SetPacmanDBPath(path string) {
}
func (m *MockBuilder) SudoLoop() {
}
func (m *MockBuilder) Capture(cmd *exec.Cmd) (stdout, stderr string, err error) {
return m.Runner.Capture(cmd)
}
func (m *MockBuilder) Show(cmd *exec.Cmd) error {
return m.Runner.Show(cmd)
}
func (m *MockBuilder) GetKeepSrc() bool {
return false
}
func (m *MockRunner) Capture(cmd *exec.Cmd) (stdout, stderr string, err error) {
m.CaptureCallsMu.Lock()
m.CaptureCalls = append(m.CaptureCalls, Call{
Args: []any{
cmd,
},
Dir: cmd.Dir,
})
m.CaptureCallsMu.Unlock()
if m.CaptureFn != nil {
return m.CaptureFn(cmd)
}
return "", "", nil
}
func (m *MockRunner) Show(cmd *exec.Cmd) error {
var err error
if m.ShowFn != nil {
err = m.ShowFn(cmd)
}
m.ShowCallsMu.Lock()
m.ShowCalls = append(m.ShowCalls, Call{
Args: []any{
cmd,
},
Dir: cmd.Dir,
})
m.ShowCallsMu.Unlock()
return err
}
================================================
FILE: pkg/settings/ini.go
================================================
package settings
import (
"fmt"
"os"
"path/filepath"
"gopkg.in/ini.v1"
)
// SystemConfigPath is the path to the system-wide INI configuration file.
const SystemConfigPath = "/etc/yay.conf"
// loadINI parses an INI configuration file and applies values to the Configuration.
// It silently returns nil if the file doesn't exist.
// Uses struct tags for mapping (e.g., `ini:"AurUrl"`).
func (c *Configuration) loadINI(path string) error {
if _, err := os.Stat(path); os.IsNotExist(err) {
return nil
}
cfg, err := ini.LoadSources(ini.LoadOptions{
AllowBooleanKeys: true,
Insensitive: true,
InsensitiveSections: true,
IgnoreInlineComment: true,
}, path)
if err != nil {
return fmt.Errorf("failed to load INI config file '%s': %w", path, err)
}
// Map the default section to the config struct
if err := cfg.Section("").MapTo(c); err != nil {
return fmt.Errorf("failed to map INI config '%s': %w", path, err)
}
// Also map [options] section if present (for compatibility)
if cfg.HasSection("options") {
if err := cfg.Section("options").MapTo(c); err != nil {
return fmt.Errorf("failed to map INI [options] section '%s': %w", path, err)
}
}
return nil
}
// SaveINI writes the configuration to an INI file at the specified path.
func (c *Configuration) SaveINI(path string) error {
cfg := ini.Empty(ini.LoadOptions{
AllowBooleanKeys: true,
})
// Use [options] section for compatibility with system config
section, err := cfg.NewSection("options")
if err != nil {
return fmt.Errorf("failed to create INI section: %w", err)
}
if err := section.ReflectFrom(c); err != nil {
return fmt.Errorf("failed to reflect config to INI: %w", err)
}
// Ensure parent directory exists
if dir := filepath.Dir(path); dir != "" {
if _, err := os.Stat(dir); os.IsNotExist(err) {
if mkErr := os.MkdirAll(dir, 0o755); mkErr != nil {
return fmt.Errorf("failed to create config directory: %w", mkErr)
}
}
}
return cfg.SaveTo(path)
}
================================================
FILE: pkg/settings/ini_test.go
================================================
package settings
import (
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestConfigurationLoadINI(t *testing.T) {
t.Parallel()
t.Run("load nonexistent file returns nil", func(t *testing.T) {
t.Parallel()
cfg := DefaultConfig("test")
err := cfg.loadINI("/nonexistent/path/yay.conf")
assert.NoError(t, err)
})
t.Run("load valid INI file", func(t *testing.T) {
t.Parallel()
content := `# System-wide yay configuration
[options]
AurUrl = https://custom.aur.org
BuildDir = /var/cache/yay
Editor = vim
Devel = true
SudoLoop = yes
RequestSplitN = 200
BottomUp = false
; This is also a comment
CleanAfter = 1
`
tmpDir := t.TempDir()
iniPath := filepath.Join(tmpDir, "yay.conf")
require.NoError(t, os.WriteFile(iniPath, []byte(content), 0o644))
cfg := DefaultConfig("test")
err := cfg.loadINI(iniPath)
require.NoError(t, err)
assert.Equal(t, "https://custom.aur.org", cfg.AURURL)
assert.Equal(t, "/var/cache/yay", cfg.BuildDir)
assert.Equal(t, "vim", cfg.Editor)
assert.True(t, cfg.Devel)
assert.True(t, cfg.SudoLoop)
assert.Equal(t, 200, cfg.RequestSplitN)
assert.False(t, cfg.BottomUp)
assert.True(t, cfg.CleanAfter)
})
t.Run("load INI file with all option types", func(t *testing.T) {
t.Parallel()
content := `
# String options
AurUrl = https://aur.example.com
AurRpcUrl = https://aur.example.com/rpc
BuildDir = /tmp/build
Editor = nvim
EditorFlags = -p
MakepkgBin = /usr/bin/makepkg
MakepkgConf = /etc/makepkg.conf
PacmanBin = /usr/bin/pacman
PacmanConf = /etc/pacman.conf
GitBin = /usr/bin/git
GpgBin = /usr/bin/gpg
GpgFlags = --keyserver-options
MFlags = -s
SortBy = votes
SearchBy = name
GitFlags = --depth=1
RemoveMake = yes
SudoBin = doas
SudoFlags = -n
ReDownload = all
AnswerClean = All
AnswerDiff = None
AnswerEdit = None
AnswerUpgrade = None
ReBuild = all
# Integer options
RequestSplitN = 100
CompletionInterval = 3
MaxConcurrentDownloads = 4
# Boolean options
BottomUp = true
SudoLoop = false
Devel = no
CleanAfter = true
KeepSrc = false
Provides = true
PgpFetch = false
CleanMenu = yes
DiffMenu = no
EditMenu = true
CombinedUpgrade = false
UseAsk = true
BatchInstall = false
SingleLineResults = true
SeparateSources = false
Debug = no
Rpc = yes
DoubleConfirm = false
`
tmpDir := t.TempDir()
iniPath := filepath.Join(tmpDir, "yay.conf")
require.NoError(t, os.WriteFile(iniPath, []byte(content), 0o644))
cfg := DefaultConfig("test")
err := cfg.loadINI(iniPath)
require.NoError(t, err)
// String options
assert.Equal(t, "https://aur.example.com", cfg.AURURL)
assert.Equal(t, "https://aur.example.com/rpc", cfg.AURRPCURL)
assert.Equal(t, "/tmp/build", cfg.BuildDir)
assert.Equal(t, "nvim", cfg.Editor)
assert.Equal(t, "-p", cfg.EditorFlags)
assert.Equal(t, "/usr/bin/makepkg", cfg.MakepkgBin)
assert.Equal(t, "/etc/makepkg.conf", cfg.MakepkgConf)
assert.Equal(t, "/usr/bin/pacman", cfg.PacmanBin)
assert.Equal(t, "/etc/pacman.conf", cfg.PacmanConf)
assert.Equal(t, "/usr/bin/git", cfg.GitBin)
assert.Equal(t, "/usr/bin/gpg", cfg.GpgBin)
assert.Equal(t, "--keyserver-options", cfg.GpgFlags)
assert.Equal(t, "-s", cfg.MFlags)
assert.Equal(t, "votes", cfg.SortBy)
assert.Equal(t, "name", cfg.SearchBy)
assert.Equal(t, "--depth=1", cfg.GitFlags)
assert.Equal(t, "yes", cfg.RemoveMake)
assert.Equal(t, "doas", cfg.SudoBin)
assert.Equal(t, "-n", cfg.SudoFlags)
assert.Equal(t, "all", cfg.ReDownload)
assert.Equal(t, "All", cfg.AnswerClean)
assert.Equal(t, "None", cfg.AnswerDiff)
assert.Equal(t, "None", cfg.AnswerEdit)
assert.Equal(t, "None", cfg.AnswerUpgrade)
assert.Equal(t, "all", string(cfg.ReBuild))
// Integer options
assert.Equal(t, 100, cfg.RequestSplitN)
assert.Equal(t, 3, cfg.CompletionInterval)
assert.Equal(t, 4, cfg.MaxConcurrentDownloads)
// Boolean options
assert.True(t, cfg.BottomUp)
assert.False(t, cfg.SudoLoop)
assert.False(t, cfg.Devel)
assert.True(t, cfg.CleanAfter)
assert.False(t, cfg.KeepSrc)
assert.True(t, cfg.Provides)
assert.False(t, cfg.PGPFetch)
assert.True(t, cfg.CleanMenu)
assert.False(t, cfg.DiffMenu)
assert.True(t, cfg.EditMenu)
assert.False(t, cfg.CombinedUpgrade)
assert.True(t, cfg.UseAsk)
assert.False(t, cfg.BatchInstall)
assert.True(t, cfg.SingleLineResults)
assert.False(t, cfg.SeparateSources)
assert.False(t, cfg.Debug)
assert.True(t, cfg.UseRPC)
assert.False(t, cfg.DoubleConfirm)
})
t.Run("load INI without section header", func(t *testing.T) {
t.Parallel()
content := `# Config without section header
AurUrl = https://custom.aur.org
Devel = true
RequestSplitN = 250
`
tmpDir := t.TempDir()
iniPath := filepath.Join(tmpDir, "yay.conf")
require.NoError(t, os.WriteFile(iniPath, []byte(content), 0o644))
cfg := DefaultConfig("test")
err := cfg.loadINI(iniPath)
require.NoError(t, err)
assert.Equal(t, "https://custom.aur.org", cfg.AURURL)
assert.True(t, cfg.Devel)
assert.Equal(t, 250, cfg.RequestSplitN)
})
t.Run("load INI with boolean keys (no value)", func(t *testing.T) {
t.Parallel()
content := `# Boolean keys without values are treated as true
Devel
SudoLoop
CleanAfter
`
tmpDir := t.TempDir()
iniPath := filepath.Join(tmpDir, "yay.conf")
require.NoError(t, os.WriteFile(iniPath, []byte(content), 0o644))
cfg := DefaultConfig("test")
cfg.Devel = false
cfg.SudoLoop = false
cfg.CleanAfter = false
err := cfg.loadINI(iniPath)
require.NoError(t, err)
assert.True(t, cfg.Devel)
assert.True(t, cfg.SudoLoop)
assert.True(t, cfg.CleanAfter)
})
t.Run("unknown options are ignored", func(t *testing.T) {
t.Parallel()
content := `AurUrl = https://custom.aur.org
unknownoption = somevalue
anotherunknown = 123
`
tmpDir := t.TempDir()
iniPath := filepath.Join(tmpDir, "yay.conf")
require.NoError(t, os.WriteFile(iniPath, []byte(content), 0o644))
cfg := DefaultConfig("test")
err := cfg.loadINI(iniPath)
require.NoError(t, err)
assert.Equal(t, "https://custom.aur.org", cfg.AURURL)
})
}
func TestSystemConfigPath(t *testing.T) {
t.Parallel()
assert.Equal(t, "/etc/yay.conf", SystemConfigPath)
}
func TestConfigurationSaveINI(t *testing.T) {
t.Parallel()
t.Run("save and reload config", func(t *testing.T) {
t.Parallel()
cfg := DefaultConfig("test")
cfg.AURURL = "https://custom.aur.org"
cfg.BuildDir = "/custom/build"
cfg.Editor = "nvim"
cfg.Devel = true
cfg.SudoLoop = true
cfg.RequestSplitN = 150
cfg.BottomUp = true
cfg.CleanAfter = false
tmpDir := t.TempDir()
iniPath := filepath.Join(tmpDir, "yay.conf")
err := cfg.SaveINI(iniPath)
require.NoError(t, err)
// Verify file was created
_, err = os.Stat(iniPath)
require.NoError(t, err)
// Load into new config and verify values
cfg2 := DefaultConfig("test")
err = cfg2.loadINI(iniPath)
require.NoError(t, err)
assert.Equal(t, "https://custom.aur.org", cfg2.AURURL)
assert.Equal(t, "/custom/build", cfg2.BuildDir)
assert.Equal(t, "nvim", cfg2.Editor)
assert.True(t, cfg2.Devel)
assert.True(t, cfg2.SudoLoop)
assert.Equal(t, 150, cfg2.RequestSplitN)
assert.True(t, cfg2.BottomUp)
assert.False(t, cfg2.CleanAfter)
})
t.Run("save creates directory if not exists", func(t *testing.T) {
t.Parallel()
cfg := DefaultConfig("test")
cfg.AURURL = "https://test.aur.org"
tmpDir := t.TempDir()
nestedPath := filepath.Join(tmpDir, "nested", "dir", "yay.conf")
err := cfg.SaveINI(nestedPath)
require.NoError(t, err)
// Verify file was created
_, err = os.Stat(nestedPath)
require.NoError(t, err)
})
t.Run("roundtrip preserves all field types", func(t *testing.T) {
t.Parallel()
cfg := DefaultConfig("test")
// String fields
cfg.AURURL = "https://roundtrip.aur.org"
cfg.AURRPCURL = "https://roundtrip.aur.org/rpc"
cfg.BuildDir = "/roundtrip/build"
cfg.Editor = "emacs"
cfg.EditorFlags = "-nw"
cfg.SudoBin = "doas"
cfg.SudoFlags = "-n"
cfg.ReDownload = "all"
cfg.ReBuild = "tree"
// Integer fields
cfg.RequestSplitN = 75
cfg.CompletionInterval = 5
cfg.MaxConcurrentDownloads = 8
// Boolean fields
cfg.BottomUp = true
cfg.SudoLoop = false
cfg.Devel = true
cfg.CleanAfter = false
cfg.UseRPC = true
cfg.BatchInstall = true
tmpDir := t.TempDir()
iniPath := filepath.Join(tmpDir, "yay.conf")
err := cfg.SaveINI(iniPath)
require.NoError(t, err)
cfg2 := DefaultConfig("test")
err = cfg2.loadINI(iniPath)
require.NoError(t, err)
// Verify all fields
assert.Equal(t, cfg.AURURL, cfg2.AURURL)
assert.Equal(t, cfg.AURRPCURL, cfg2.AURRPCURL)
assert.Equal(t, cfg.BuildDir, cfg2.BuildDir)
assert.Equal(t, cfg.Editor, cfg2.Editor)
assert.Equal(t, cfg.EditorFlags, cfg2.EditorFlags)
assert.Equal(t, cfg.SudoBin, cfg2.SudoBin)
assert.Equal(t, cfg.SudoFlags, cfg2.SudoFlags)
assert.Equal(t, cfg.ReDownload, cfg2.ReDownload)
assert.Equal(t, cfg.ReBuild, cfg2.ReBuild)
assert.Equal(t, cfg.RequestSplitN, cfg2.RequestSplitN)
assert.Equal(t, cfg.CompletionInterval, cfg2.CompletionInterval)
assert.Equal(t, cfg.MaxConcurrentDownloads, cfg2.MaxConcurrentDownloads)
assert.Equal(t, cfg.BottomUp, cfg2.BottomUp)
assert.Equal(t, cfg.SudoLoop, cfg2.SudoLoop)
assert.Equal(t, cfg.Devel, cfg2.Devel)
assert.Equal(t, cfg.CleanAfter, cfg2.CleanAfter)
assert.Equal(t, cfg.UseRPC, cfg2.UseRPC)
assert.Equal(t, cfg.BatchInstall, cfg2.BatchInstall)
})
}
================================================
FILE: pkg/settings/parser/parser.go
================================================
package parser
import (
"bufio"
"errors"
"fmt"
"maps"
"os"
"strings"
"github.com/leonelquinteros/gotext"
)
type Option struct {
Global bool
Args []string
}
func (o *Option) Add(args ...string) {
if o.Args == nil {
o.Args = args
return
}
o.Args = append(o.Args, args...)
}
func (o *Option) First() string {
if len(o.Args) == 0 {
return ""
}
return o.Args[0]
}
func (o *Option) Set(arg string) {
o.Args = []string{arg}
}
func (o *Option) String() string {
return fmt.Sprintf("Global:%v Args:%v", o.Global, o.Args)
}
// Arguments 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]*Option
Targets []string
}
func (a *Arguments) String() string {
return fmt.Sprintf("Op:%v Options:%+v Targets: %v", a.Op, a.Options, a.Targets)
}
func (a *Arguments) CreateOrAppendOption(option string, values ...string) {
if a.Options[option] == nil {
a.Options[option] = &Option{
Args: values,
}
} else {
a.Options[option].Add(values...)
}
}
func MakeArguments() *Arguments {
return &Arguments{
"",
make(map[string]*Option),
make([]string, 0),
}
}
func (a *Arguments) CopyGlobal() *Arguments {
cp := MakeArguments()
for k, v := range a.Options {
if v.Global {
cp.Options[k] = v
}
}
return cp
}
func (a *Arguments) Copy() (cp *Arguments) {
cp = MakeArguments()
cp.Op = a.Op
maps.Copy(cp.Options, a.Options)
cp.Targets = make([]string, len(a.Targets))
copy(cp.Targets, a.Targets)
return
}
func (a *Arguments) DelArg(options ...string) {
for _, option := range options {
delete(a.Options, option)
}
}
func (a *Arguments) NeedRoot(mode TargetMode) bool {
if a.ExistsArg("h", "help") {
return false
}
switch a.Op {
case "D", "database":
if a.ExistsArg("k", "check") {
return false
}
return true
case "F", "files":
if a.ExistsArg("y", "refresh") {
return true
}
return false
case "Q", "query":
if a.ExistsArg("k", "check") {
return true
}
return false
case "R", "remove":
if a.ExistsArg("p", "print", "print-format") {
return false
}
return true
case "S", "sync":
switch {
case a.ExistsArg("y", "refresh"):
return true
case a.ExistsArg("p", "print", "print-format"):
return false
case a.ExistsArg("s", "search"):
return false
case a.ExistsArg("l", "list"):
return false
case a.ExistsArg("g", "groups"):
return false
case a.ExistsArg("i", "info"):
return false
case a.ExistsArg("c", "clean") && mode == ModeAUR:
return false
}
return true
case "U", "upgrade":
return true
default:
return false
}
}
func (a *Arguments) addOP(op string) error {
if a.Op != "" {
return errors.New(gotext.Get("only one operation may be used at a time"))
}
a.Op = op
return nil
}
func (a *Arguments) addParam(option, arg string) error {
if !isArg(option) {
return errors.New(gotext.Get("invalid option '%s'", option))
}
if isOp(option) {
return a.addOP(option)
}
a.CreateOrAppendOption(option, strings.Split(arg, ",")...)
if isGlobal(option) {
a.Options[option].Global = true
}
return nil
}
func (a *Arguments) AddArg(options ...string) error {
for _, option := range options {
err := a.addParam(option, "")
if err != nil {
return err
}
}
return nil
}
// Multiple args acts as an OR operator.
func (a *Arguments) ExistsArg(options ...string) bool {
for _, option := range options {
if _, exists := a.Options[option]; exists {
return true
}
}
return false
}
func (a *Arguments) GetArg(options ...string) (arg string, double, exists bool) {
for _, option := range options {
value, exists := a.Options[option]
if exists {
return value.First(), len(value.Args) >= 2, len(value.Args) >= 1
}
}
return arg, false, false
}
func (a *Arguments) GetArgs(option string) (args []string) {
value, exists := a.Options[option]
if exists {
return value.Args
}
return nil
}
func (a *Arguments) AddTarget(targets ...string) {
a.Targets = append(a.Targets, targets...)
}
func (a *Arguments) ClearTargets() {
a.Targets = make([]string, 0)
}
// Multiple args acts as an OR operator.
func (a *Arguments) ExistsDouble(options ...string) bool {
for _, option := range options {
if value, exists := a.Options[option]; exists {
return len(value.Args) >= 2
}
}
return false
}
func (a *Arguments) FormatArgs() (args []string) {
if a.Op != "" {
args = append(args, formatArg(a.Op))
}
for option, arg := range a.Options {
if arg.Global || option == "--" {
continue
}
formattedOption := formatArg(option)
for _, value := range arg.Args {
args = append(args, formattedOption)
if hasParam(option) {
args = append(args, value)
}
}
}
return args
}
func (a *Arguments) FormatGlobals() (args []string) {
for option, arg := range a.Options {
if !arg.Global {
continue
}
formattedOption := formatArg(option)
for _, value := range arg.Args {
args = append(args, formattedOption)
if hasParam(option) {
args = append(args, value)
}
}
}
return args
}
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 "W", "web":
case "P", "show":
case "B", "build":
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 "noprogressbar":
case "numberupgrades":
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":
case "disable-sandbox":
// yay options
case "aururl":
case "aurrpcurl":
case "save":
case "afterclean", "cleanafter":
case "keepsrc":
case "devel":
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 "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 "provides":
case "pgpfetch":
case "cleanmenu":
case "diffmenu":
case "editmenu":
case "useask":
case "combinedupgrade":
case "a", "aur":
case "N", "repo":
case "removemake":
case "noremovemake":
case "askremovemake":
case "askyesremovemake":
case "complete":
case "stats":
case "news":
case "gendb":
case "currentconfig":
case "defaultconfig":
case "singlelineresults":
case "doublelineresults":
case "separatesources":
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 "W", "web":
case "B", "build":
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 "aurrpcurl":
case "mflags":
case "gpgflags":
case "gitflags":
case "builddir":
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 (a *Arguments) parseShortOption(arg, param string) (usedNext bool, err error) {
if arg == "-" {
err = a.AddArg("-")
return
}
arg = arg[1:]
for k, _char := range arg {
char := string(_char)
if hasParam(char) {
if k < len(arg)-1 {
err = a.addParam(char, arg[k+1:])
} else {
usedNext = true
err = a.addParam(char, param)
}
break
} else {
err = a.AddArg(char)
if err != nil {
return
}
}
}
return
}
// Parses full length options such as:
// --sync --refresh --sysupgrade --dbpath /some/path --.
func (a *Arguments) parseLongOption(arg, param string) (usedNext bool, err error) {
if arg == "--" {
err = a.AddArg(arg)
return
}
arg = arg[2:]
switch split := strings.SplitN(arg, "=", 2); {
case len(split) == 2:
err = a.addParam(split[0], split[1])
case hasParam(arg):
err = a.addParam(arg, param)
usedNext = true
default:
err = a.AddArg(arg)
}
return
}
func (a *Arguments) parseStdin() error {
fi, err := os.Stdin.Stat()
if err != nil {
return err
}
// Ensure data is piped
if (fi.Mode() & os.ModeCharDevice) != 0 {
return errors.New(gotext.Get("argument '-' specified without input on stdin"))
}
scanner := bufio.NewScanner(os.Stdin)
scanner.Split(bufio.ScanLines)
for scanner.Scan() {
a.AddTarget(scanner.Text())
}
return os.Stdin.Close()
}
func (a *Arguments) Parse() error {
args := os.Args[1:]
usedNext := false
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 a.ExistsArg("--"):
a.AddTarget(arg)
case strings.HasPrefix(arg, "--"):
usedNext, err = a.parseLongOption(arg, nextArg)
case strings.HasPrefix(arg, "-"):
usedNext, err = a.parseShortOption(arg, nextArg)
default:
a.AddTarget(arg)
}
if err != nil {
return err
}
}
if a.Op == "" {
if len(a.Targets) > 0 {
a.Op = "Y"
} else {
// Handle args incompatible with -Syu
if a.ExistsArg("c", "clean") {
fmt.Println(gotext.Get("the clean command requires an operation to be specified"))
fmt.Println(gotext.Get("did you mean yay -Scc (clean caches) or yay -Ycc (clean orphaned packages)?"))
return errors.New(gotext.Get("invalid option"))
}
if _, err := a.parseShortOption("-Syu", ""); err != nil {
return err
}
}
}
if a.ExistsArg("-") {
if err := a.parseStdin(); err != nil {
return err
}
a.DelArg("-")
file, err := os.Open("/dev/tty")
if err != nil {
return err
}
os.Stdin = file
}
return nil
}
================================================
FILE: pkg/settings/parser/parser_test.go
================================================
//go:build !integration
// +build !integration
package parser
import (
"os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestOption_Add(t *testing.T) {
t.Parallel()
type fields struct {
Args []string
}
type args struct {
arg string
}
tests := []struct {
name string
fields fields
args args
want []string
}{
{name: "simple add", fields: fields{
Args: []string{"a", "b"},
}, args: args{
arg: "c",
}, want: []string{"a", "b", "c"}},
{name: "null add", fields: fields{
Args: nil,
}, args: args{
arg: "c",
}, want: []string{"c"}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
o := &Option{
Args: tt.fields.Args,
}
o.Add(tt.args.arg)
assert.ElementsMatch(t, tt.want, o.Args)
})
}
}
func TestOption_Set(t *testing.T) {
t.Parallel()
type fields struct {
Args []string
}
type args struct {
arg string
}
tests := []struct {
name string
fields fields
args args
want []string
}{
{name: "simple set", fields: fields{
Args: []string{"a", "b"},
}, args: args{
arg: "c",
}, want: []string{"c"}},
{name: "null set", fields: fields{
Args: nil,
}, args: args{
arg: "c",
}, want: []string{"c"}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
o := &Option{
Args: tt.fields.Args,
}
o.Set(tt.args.arg)
assert.ElementsMatch(t, tt.want, o.Args)
})
}
}
func TestOption_First(t *testing.T) {
t.Parallel()
type fields struct {
Args []string
}
tests := []struct {
name string
fields fields
want string
}{
{name: "simple first", fields: fields{
Args: []string{"a", "b"},
}, want: "a"},
{name: "null first", fields: fields{
Args: nil,
}, want: ""},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
o := &Option{
Args: tt.fields.Args,
}
assert.Equal(t, tt.want, o.First())
})
}
}
func TestMakeArguments(t *testing.T) {
t.Parallel()
args := MakeArguments()
assert.NotNil(t, args)
assert.Equal(t, "", args.Op)
assert.Empty(t, args.Options)
assert.Empty(t, args.Targets)
}
func TestArguments_CopyGlobal(t *testing.T) {
t.Parallel()
type fields struct {
Op string
Options map[string]*Option
Targets []string
}
tests := []struct {
name string
fields fields
want *Arguments
}{
{name: "simple", fields: fields{
Op: "Q",
Options: map[string]*Option{
"a": {}, "arch": {
Global: true,
Args: []string{"x86_x64"},
}, "boo": {Global: true, Args: []string{"a", "b"}},
},
Targets: []string{"a", "b"},
}, want: &Arguments{
Op: "",
Options: map[string]*Option{
"arch": {
Global: true,
Args: []string{"x86_x64"},
}, "boo": {Global: true, Args: []string{"a", "b"}},
},
Targets: []string{},
}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
cmdArgs := &Arguments{
Op: tt.fields.Op,
Options: tt.fields.Options,
Targets: tt.fields.Targets,
}
got := cmdArgs.CopyGlobal()
assert.NotEqualValues(t, tt.fields.Options, got.Options)
assert.NotEqualValues(t, tt.fields.Targets, got.Targets)
assert.NotEqual(t, tt.fields.Op, got.Op)
assert.Equal(t, tt.want, got)
})
}
}
func TestArguments_Copy(t *testing.T) {
t.Parallel()
type fields struct {
Op string
Options map[string]*Option
Targets []string
}
tests := []struct {
name string
fields fields
want *Arguments
}{
{name: "simple", fields: fields{
Op: "Q",
Options: map[string]*Option{
"a": {}, "arch": {
Args: []string{"x86_x64"}, Global: true,
}, "boo": {Args: []string{"a", "b"}, Global: true},
},
Targets: []string{"a", "b"},
}, want: &Arguments{
Op: "Q",
Options: map[string]*Option{
"a": {}, "arch": {
Global: true,
Args: []string{"x86_x64"},
}, "boo": {Args: []string{"a", "b"}, Global: true},
},
Targets: []string{"a", "b"},
}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
cmdArgs := &Arguments{
Op: tt.fields.Op,
Options: tt.fields.Options,
Targets: tt.fields.Targets,
}
got := cmdArgs.Copy()
assert.Equal(t, cmdArgs, got)
assert.Equal(t, tt.want, got)
})
}
}
func TestArguments_DelArg(t *testing.T) {
t.Parallel()
args := MakeArguments()
args.addParam("arch", "arg")
args.addParam("ask", "arg")
args.DelArg("arch", "ask")
assert.Empty(t, args.Options)
}
func TestArguments_FormatArgs(t *testing.T) {
t.Parallel()
type fields struct {
Op string
Options map[string]*Option
Targets []string
}
tests := []struct {
name string
fields fields
wantArgs []string
}{
{name: "simple", fields: fields{
Op: "S",
Options: map[string]*Option{},
Targets: []string{"yay", "yay-bin", "yay-git"},
}, wantArgs: []string{"-S"}},
{name: "only global", fields: fields{
Op: "Y",
Options: map[string]*Option{"noconfirm": {Global: true, Args: []string{""}}},
Targets: []string{"yay", "yay-bin", "yay-git"},
}, wantArgs: []string{"-Y"}},
{name: "options single", fields: fields{
Op: "Y",
Options: map[string]*Option{"overwrite": {Args: []string{"/tmp/a"}}, "useask": {Args: []string{""}}},
Targets: []string{},
}, wantArgs: []string{"-Y", "--overwrite", "/tmp/a", "--useask"}},
{name: "options doubles", fields: fields{
Op: "Y",
Options: map[string]*Option{"overwrite": {Args: []string{"/tmp/a", "/tmp/b", "/tmp/c"}}, "needed": {Args: []string{""}}},
Targets: []string{},
}, wantArgs: []string{"-Y", "--overwrite", "/tmp/a", "--overwrite", "/tmp/b", "--overwrite", "/tmp/c", "--needed"}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
cmdArgs := &Arguments{
Op: tt.fields.Op,
Options: tt.fields.Options,
Targets: tt.fields.Targets,
}
gotArgs := cmdArgs.FormatArgs()
assert.ElementsMatch(t, gotArgs, tt.wantArgs)
})
}
}
func TestArguments_FormatGlobalArgs(t *testing.T) {
t.Parallel()
type fields struct {
Op string
Options map[string]*Option
Targets []string
}
tests := []struct {
name string
fields fields
wantArgs []string
}{
{name: "simple", fields: fields{
Op: "S",
Options: map[string]*Option{"dbpath": {Global: true, Args: []string{"/tmp/a", "/tmp/b"}}},
Targets: []string{"yay", "yay-bin", "yay-git"},
}, wantArgs: []string{"--dbpath", "/tmp/a", "--dbpath", "/tmp/b"}},
{name: "only global", fields: fields{
Op: "Y",
Options: map[string]*Option{"noconfirm": {Global: true, Args: []string{""}}},
Targets: []string{"yay", "yay-bin", "yay-git"},
}, wantArgs: []string{"--noconfirm"}},
{name: "options single", fields: fields{
Op: "Y",
Options: map[string]*Option{"overwrite": {Args: []string{"/tmp/a"}}, "useask": {Args: []string{""}}},
Targets: []string{},
}, wantArgs: []string(nil)},
{name: "options doubles", fields: fields{
Op: "Y",
Options: map[string]*Option{"overwrite": {Args: []string{"/tmp/a", "/tmp/b", "/tmp/c"}}, "needed": {Args: []string{""}}},
Targets: []string{},
}, wantArgs: []string(nil)},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
cmdArgs := &Arguments{
Op: tt.fields.Op,
Options: tt.fields.Options,
Targets: tt.fields.Targets,
}
gotArgs := cmdArgs.FormatGlobals()
assert.ElementsMatch(t, tt.wantArgs, gotArgs)
})
}
}
func Test_isArg(t *testing.T) {
t.Parallel()
got := isArg("zorg")
assert.False(t, got)
got = isArg("dbpath")
assert.True(t, got)
}
func TestArguments_ParseStdin(t *testing.T) {
input := []byte("yay")
r, w, err := os.Pipe()
require.NoError(t, err)
_, err = w.Write(input)
require.NoError(t, err)
w.Close()
// Restore stdin after the test.
defer func(o *os.File) { os.Stdin = o }(os.Stdin)
os.Stdin = r
args := MakeArguments()
err = args.parseStdin()
assert.NoError(t, err)
expectedTargets := []string{string(input)}
assert.ElementsMatch(t, args.Targets, expectedTargets)
}
func TestArguments_ParseStdin_broken_pipe(t *testing.T) {
r, _, err := os.Pipe()
require.NoError(t, err)
r.Close() // Close early to break pipe
// Restore stdin after the test.
defer func(o *os.File) { os.Stdin = o }(os.Stdin)
os.Stdin = r
args := MakeArguments()
err = args.parseStdin()
assert.Error(t, err)
}
================================================
FILE: pkg/settings/parser/rebuild_mode.go
================================================
package parser
type RebuildMode string
const (
RebuildModeNo RebuildMode = "no"
RebuildModeYes RebuildMode = "yes"
RebuildModeTree RebuildMode = "tree"
RebuildModeAll RebuildMode = "all"
)
================================================
FILE: pkg/settings/parser/target_mode.go
================================================
package parser
type TargetMode int
const (
ModeAny TargetMode = iota
ModeAUR
ModeRepo
)
func (mode TargetMode) AtLeastAUR() bool {
return mode == ModeAny || mode == ModeAUR
}
func (mode TargetMode) AtLeastRepo() bool {
return mode == ModeAny || mode == ModeRepo
}
================================================
FILE: pkg/settings/yay.conf
================================================
# /etc/yay.conf - System-wide yay configuration
[options]
# AUR Settings
AurUrl = https://aur.archlinux.org
#AurRpcUrl = https://aur.archlinux.org/rpc
# Directories
BuildDir = ~/.cache/yay
# Binaries
#Editor = vim
#EditorFlags =
MakepkgBin = makepkg
#MakepkgConf =
PacmanBin = pacman
PacmanConf = /etc/pacman.conf
GitBin = git
GpgBin = gpg
SudoBin = sudo
# Flags
# GpgFlags =
# MFlags =
# GitFlags =
# SudoFlags =
# Search/Display
BottomUp
# SingleLineResults
SeparateSources
# Sorting
#SortBy =
SearchBy = name-desc
# Build options
#Devel
#CleanAfter
#KeepSrc
#BatchInstall
RemoveMake = ask
# Download options
ReDownload = no
ReBuild = no
PgpFetch
# Menus
CleanMenu
DiffMenu
# EditMenu
# Prompts
#AnswerClean =
#AnswerDiff =
#AnswerEdit =
#AnswerUpgrade =
# Behavior
CombinedUpgrade
#SudoLoop
#Provides
#UseAsk
DoubleConfirm
Rpc
#Debug
# Limits
RequestSplitN = 150
CompletionInterval = 7
MaxConcurrentDownloads = 1
================================================
FILE: pkg/sync/build/errors.go
================================================
package build
import (
"errors"
"strings"
"github.com/leonelquinteros/gotext"
)
var ErrInstallRepoPkgs = errors.New(gotext.Get("error installing repo packages"))
type FailedIgnoredPkgError struct {
pkgErrors map[string]error
}
func (e *FailedIgnoredPkgError) Error() string {
var sb strings.Builder
sb.WriteString(gotext.Get("Failed to install the following packages. Manual intervention is required:"))
for pkg, err := range e.pkgErrors {
sb.WriteString("\n")
sb.WriteString(pkg)
sb.WriteString(" - ")
sb.WriteString(err.Error())
}
return sb.String()
}
type PkgDestNotInListError struct {
name string
}
func (e *PkgDestNotInListError) Error() string {
return gotext.Get("could not find PKGDEST for: %s", e.name)
}
type FindPkgDestError struct {
name, pkgDest string
}
func (e *FindPkgDestError) Error() string {
return gotext.Get(
"the PKGDEST for %s is listed by makepkg but does not exist: %s",
e.name, e.pkgDest)
}
type SetPkgReasonError struct {
exp bool // explicit
}
func (e *SetPkgReasonError) Error() string {
reason := gotext.Get("explicit")
if !e.exp {
reason = gotext.Get("dependency")
}
return gotext.Get("error updating package install reason to %s", reason)
}
type NoPkgDestsFoundError struct {
dir string
}
func (e *NoPkgDestsFoundError) Error() string {
return gotext.Get("could not find any package archives listed in %s", e.dir)
}
================================================
FILE: pkg/sync/build/installer.go
================================================
package build
import (
"context"
"fmt"
"maps"
"os"
"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/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"
mapset "github.com/deckarep/golang-set/v2"
"github.com/leonelquinteros/gotext"
)
type (
PostInstallHookFunc func(ctx context.Context) error
Installer struct {
dbExecutor db.Executor
postInstallHooks []PostInstallHookFunc
failedAndIgnored map[string]error
exeCmd exe.ICmdBuilder
vcsStore vcs.Store
targetMode parser.TargetMode
rebuildMode parser.RebuildMode
origTargets mapset.Set[string]
downloadOnly bool
installBuiltPackages bool
log *text.Logger
manualConfirmRequired bool
}
)
func NewInstaller(dbExecutor db.Executor,
exeCmd exe.ICmdBuilder, vcsStore vcs.Store, targetMode parser.TargetMode,
rebuildMode parser.RebuildMode, downloadOnly bool, logger *text.Logger,
) *Installer {
return &Installer{
dbExecutor: dbExecutor,
postInstallHooks: []PostInstallHookFunc{},
failedAndIgnored: map[string]error{},
exeCmd: exeCmd,
vcsStore: vcsStore,
targetMode: targetMode,
rebuildMode: rebuildMode,
downloadOnly: downloadOnly,
installBuiltPackages: true,
log: logger,
manualConfirmRequired: true,
}
}
func (installer *Installer) SetInstallBuiltPackages(install bool) {
installer.installBuiltPackages = install
}
func (installer *Installer) CompileFailedAndIgnored() (map[string]error, error) {
if len(installer.failedAndIgnored) == 0 {
return installer.failedAndIgnored, nil
}
return installer.failedAndIgnored, &FailedIgnoredPkgError{
pkgErrors: installer.failedAndIgnored,
}
}
func (installer *Installer) AddPostInstallHook(hook PostInstallHookFunc) {
if hook == nil {
return
}
installer.postInstallHooks = append(installer.postInstallHooks, hook)
}
func (installer *Installer) RunPostInstallHooks(ctx context.Context) error {
var errMulti multierror.MultiError
for _, hook := range installer.postInstallHooks {
if err := hook(ctx); err != nil {
errMulti.Add(err)
}
}
return errMulti.Return()
}
func (installer *Installer) Install(ctx context.Context,
cmdArgs *parser.Arguments,
targets []map[string]*dep.InstallInfo,
pkgBuildDirs map[string]string,
excluded []string,
manualConfirmRequired bool,
) error {
installer.log.Debugln("manualConfirmRequired:", manualConfirmRequired)
installer.manualConfirmRequired = manualConfirmRequired
installer.origTargets = mapset.NewThreadUnsafeSet[string]()
for _, targetString := range cmdArgs.Targets {
installer.origTargets.Add(dep.ToTarget(targetString).Name)
}
installer.log.Debugln("origTargets:", installer.origTargets)
// Reorganize targets into layers of dependencies
var errMulti multierror.MultiError
for i := len(targets) - 1; i >= 0; i-- {
lastLayer := i == 0
errI := installer.handleLayer(ctx, cmdArgs, targets[i], pkgBuildDirs, lastLayer, excluded)
if errI == nil && lastLayer {
// success after rollups
return nil
}
if errI != nil {
errMulti.Add(errI)
if lastLayer {
break
}
// rollup
installer.log.Warnln(gotext.Get("Failed to install layer, rolling up to next layer."), "error:", errI)
targets[i-1] = mergeLayers(targets[i-1], targets[i])
}
}
return errMulti.Return()
}
func mergeLayers(layer1, layer2 map[string]*dep.InstallInfo) map[string]*dep.InstallInfo {
maps.Copy(layer1, layer2)
return layer1
}
func (installer *Installer) appendNoConfirm() bool {
return !installer.manualConfirmRequired || settings.NoConfirm
}
func (installer *Installer) handleLayer(ctx context.Context,
cmdArgs *parser.Arguments,
layer map[string]*dep.InstallInfo,
pkgBuildDirs map[string]string,
lastLayer bool,
excluded []string,
) error {
// Install layer
nameToBaseMap := make(map[string]string, len(layer))
syncDeps, syncExp, syncGroups := mapset.NewThreadUnsafeSet[string](),
mapset.NewThreadUnsafeSet[string](), mapset.NewThreadUnsafeSet[string]()
aurDeps, aurExp, aurOrigTargetBases := mapset.NewThreadUnsafeSet[string](),
mapset.NewThreadUnsafeSet[string](), mapset.NewThreadUnsafeSet[string]()
upgradeSync := false
for name, info := range layer {
switch info.Source {
case dep.AUR, dep.SrcInfo:
nameToBaseMap[name] = *info.AURBase
if installer.origTargets.Contains(name) {
aurOrigTargetBases.Add(*info.AURBase)
}
switch info.Reason {
case dep.Explicit:
if cmdArgs.ExistsArg("asdeps", "asdep") {
aurDeps.Add(name)
} else {
aurExp.Add(name)
}
case dep.Dep, dep.MakeDep, dep.CheckDep:
// Only apply --asexplicit to packages that are explicitly targeted
if cmdArgs.ExistsArg("asexplicit", "asexp") && installer.origTargets.Contains(name) {
aurExp.Add(name)
} else {
aurDeps.Add(name)
}
}
case dep.Sync:
if info.Upgrade {
upgradeSync = true
continue // do not add to targets, let pacman handle it
}
compositePkgName := fmt.Sprintf("%s/%s", *info.SyncDBName, name)
if info.IsGroup {
syncGroups.Add(compositePkgName)
continue
}
switch info.Reason {
case dep.Explicit:
if cmdArgs.ExistsArg("asdeps", "asdep") {
syncDeps.Add(compositePkgName)
} else {
syncExp.Add(compositePkgName)
}
case dep.Dep, dep.MakeDep, dep.CheckDep:
// Only apply --asexplicit to packages that are explicitly targeted
if cmdArgs.ExistsArg("asexplicit", "asexp") && installer.origTargets.Contains(name) {
syncExp.Add(compositePkgName)
} else {
syncDeps.Add(compositePkgName)
}
}
}
}
installer.log.Debugln("syncDeps", syncDeps, "SyncExp", syncExp,
"aurDeps", aurDeps, "aurExp", aurExp, "upgrade", upgradeSync)
errShow := installer.installSyncPackages(ctx, cmdArgs, syncDeps, syncExp, syncGroups,
excluded, upgradeSync, installer.appendNoConfirm())
if errShow != nil {
return ErrInstallRepoPkgs
}
errAur := installer.installAURPackages(ctx, cmdArgs, aurDeps, aurExp,
aurOrigTargetBases, nameToBaseMap, pkgBuildDirs, true, lastLayer,
installer.appendNoConfirm())
return errAur
}
func (installer *Installer) installAURPackages(ctx context.Context,
cmdArgs *parser.Arguments,
aurDepNames, aurExpNames, aurOrigTargetBases mapset.Set[string],
nameToBase, pkgBuildDirsByBase map[string]string,
installIncompatible bool,
lastLayer bool,
noConfirm bool,
) error {
all := aurDepNames.Union(aurExpNames).ToSlice()
if len(all) == 0 {
return nil
}
builtPkgDests := make(map[string]map[string]string)
deps := make([]string, 0, aurDepNames.Cardinality())
exps := make([]string, 0, aurExpNames.Cardinality())
pkgArchives := make([]string, 0, len(all))
for _, name := range all {
base := nameToBase[name]
dir := pkgBuildDirsByBase[base]
pkgdests, ok := builtPkgDests[base]
if ok {
installer.log.Debugln("skipping built pkgbase", base, "package", name)
} else {
var errMake error
installer.log.Debugln("building pkgbase", base, "package", name)
pkgdests, errMake = installer.buildPkg(ctx, dir, base,
installIncompatible, cmdArgs.ExistsArg("needed"), aurOrigTargetBases.Contains(base))
builtPkgDests[base] = pkgdests
if errMake != nil {
if !lastLayer {
return fmt.Errorf("%s - %w", gotext.Get("error making: %s", base), errMake)
}
installer.failedAndIgnored[name] = errMake
installer.log.Errorln(gotext.Get("error making: %s", base), "-", errMake)
continue
}
}
if len(pkgdests) == 0 {
installer.log.Warnln(gotext.Get("nothing to install for %s", text.Cyan(base)))
continue
}
newPKGArchives, hasDebug, err := installer.getNewTargets(pkgdests, name)
if err != nil {
return err
}
pkgArchives = append(pkgArchives, newPKGArchives...)
if isDep := installer.isDep(cmdArgs, aurExpNames, name); isDep {
deps = append(deps, name)
} else {
exps = append(exps, name)
}
if hasDebug {
deps = append(deps, name+"-debug")
}
}
if len(pkgArchives) == 0 || !installer.installBuiltPackages {
return nil
}
if err := installPkgArchive(ctx, installer.exeCmd, installer.targetMode,
installer.vcsStore, cmdArgs, pkgArchives, noConfirm); err != nil {
return fmt.Errorf("%s - %w", fmt.Sprintf(gotext.Get("error installing:")+" %v", pkgArchives), err)
}
if err := setInstallReason(ctx, installer.exeCmd, installer.targetMode, cmdArgs, deps, exps); err != nil {
return fmt.Errorf("%s - %w", fmt.Sprintf(gotext.Get("error installing:")+" %v", pkgArchives), err)
}
return nil
}
func (installer *Installer) buildPkg(ctx context.Context,
dir, base string,
installIncompatible, needed, isTarget bool,
) (map[string]string, error) {
args := []string{"--nobuild", "-f"}
if !installer.exeCmd.GetKeepSrc() {
args = append(args, "-C")
}
if installIncompatible {
args = append(args, "--ignorearch")
}
// pkgver bump
if err := installer.exeCmd.Show(
installer.exeCmd.BuildMakepkgCmd(ctx, dir, args...)); err != nil {
return nil, err
}
pkgdests, pkgVersion, errList := parsePackageList(ctx, installer.exeCmd, dir)
if errList != nil {
return nil, errList
}
switch {
case needed && installer.pkgsAreAlreadyInstalled(pkgdests, pkgVersion) || installer.downloadOnly:
args = []string{"--nobuild", "--noextract", "--ignorearch"}
pkgdests = map[string]string{}
installer.log.Warnln(gotext.Get("%s is up to date -- skipping", text.Cyan(base+"-"+pkgVersion)))
case installer.skipAlreadyBuiltPkg(isTarget, pkgdests):
args = []string{"--nobuild", "--noextract", "--ignorearch"}
installer.log.Warnln(gotext.Get("%s already made -- skipping build", text.Cyan(base+"-"+pkgVersion)))
default:
args = []string{"-f", "--noconfirm", "--noextract", "--noprepare", "--holdver"}
if installIncompatible {
args = append(args, "--ignorearch")
}
}
if !installer.exeCmd.GetKeepSrc() {
args = append(args, "-c")
}
errMake := installer.exeCmd.Show(
installer.exeCmd.BuildMakepkgCmd(ctx,
dir, args...))
if errMake != nil {
return nil, errMake
}
if installer.downloadOnly {
return map[string]string{}, nil
}
return pkgdests, nil
}
func (installer *Installer) pkgsAreAlreadyInstalled(pkgdests map[string]string, pkgVersion string) bool {
for pkgName := range pkgdests {
if !installer.dbExecutor.IsCorrectVersionInstalled(pkgName, pkgVersion) {
return false
}
}
return true
}
func pkgsAreBuilt(logger *text.Logger, pkgdests map[string]string) bool {
for _, pkgdest := range pkgdests {
if _, err := os.Stat(pkgdest); err != nil {
logger.Debugln("pkgIsBuilt:", pkgdest, "does not exist")
return false
}
}
return true
}
func (installer *Installer) skipAlreadyBuiltPkg(isTarget bool, pkgdests map[string]string) bool {
switch installer.rebuildMode {
case parser.RebuildModeNo:
return pkgsAreBuilt(installer.log, pkgdests)
case parser.RebuildModeYes:
return !isTarget && pkgsAreBuilt(installer.log, pkgdests)
// case parser.RebuildModeTree: // TODO
// case parser.RebuildModeAll: // TODO
default:
// same as RebuildModeNo
return pkgsAreBuilt(installer.log, pkgdests)
}
}
func (*Installer) isDep(cmdArgs *parser.Arguments, aurExpNames mapset.Set[string], name string) bool {
switch {
case cmdArgs.ExistsArg("asdeps", "asdep"):
return true
case cmdArgs.ExistsArg("asexplicit", "asexp"):
return false
case aurExpNames.Contains(name):
return false
}
return true
}
func (installer *Installer) getNewTargets(pkgdests map[string]string, name string,
) (archives []string, good bool, err error) {
pkgdest, ok := pkgdests[name]
if !ok {
return nil, false, &PkgDestNotInListError{name: name}
}
pkgArchives := make([]string, 0, 2)
if _, errStat := os.Stat(pkgdest); os.IsNotExist(errStat) {
return nil, false, &FindPkgDestError{name: name, pkgDest: pkgdest}
}
pkgArchives = append(pkgArchives, pkgdest)
debugName := name + "-debug"
pkgdestDebug, ok := pkgdests[debugName]
if ok {
if _, errStat := os.Stat(pkgdestDebug); errStat == nil {
pkgArchives = append(pkgArchives, pkgdestDebug)
} else {
ok = false
}
}
return pkgArchives, ok, nil
}
func (installer *Installer) installSyncPackages(ctx context.Context, cmdArgs *parser.Arguments,
syncDeps, // repo targets that are deps
syncExp mapset.Set[string], // repo targets that are exp
syncGroups mapset.Set[string], // repo targets that are groups
excluded []string,
upgrade bool, // run even without targets
noConfirm bool,
) error {
repoTargets := syncDeps.Union(syncExp).Union(syncGroups).ToSlice()
if len(repoTargets) == 0 && !upgrade {
return nil
}
arguments := cmdArgs.Copy()
arguments.DelArg("asdeps", "asdep")
arguments.DelArg("asexplicit", "asexp")
arguments.DelArg("i", "install")
arguments.Op = "S"
arguments.ClearTargets()
arguments.AddTarget(repoTargets...)
// Don't upgrade all repo packages if only AUR upgrades are specified
if installer.targetMode == parser.ModeAUR {
arguments.DelArg("u", "upgrades")
}
if len(excluded) > 0 {
arguments.CreateOrAppendOption("ignore", excluded...)
}
errShow := installer.exeCmd.Show(installer.exeCmd.BuildPacmanCmd(ctx,
arguments, installer.targetMode, noConfirm))
if errShow != nil {
return errShow
}
if errD := asdeps(ctx, installer.exeCmd, installer.targetMode, cmdArgs, syncDeps.ToSlice()); errD != nil {
return errD
}
if errE := asexp(ctx, installer.exeCmd, installer.targetMode, cmdArgs, syncExp.ToSlice()); errE != nil {
return errE
}
return nil
}
================================================
FILE: pkg/sync/build/installer_test.go
================================================
package build
import (
"context"
"errors"
"fmt"
"io"
"os"
"os/exec"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/Jguer/yay/v12/pkg/db/mock"
"github.com/Jguer/yay/v12/pkg/dep"
"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 newTestLogger() *text.Logger {
return text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), true, "test")
}
func ptrString(s string) *string {
return &s
}
func TestInstaller_InstallNeeded(t *testing.T) {
t.Parallel()
makepkgBin := t.TempDir() + "/makepkg"
pacmanBin := t.TempDir() + "/pacman"
f, err := os.OpenFile(makepkgBin, os.O_RDONLY|os.O_CREATE, 0o755)
require.NoError(t, err)
require.NoError(t, f.Close())
f, err = os.OpenFile(pacmanBin, os.O_RDONLY|os.O_CREATE, 0o755)
require.NoError(t, err)
require.NoError(t, f.Close())
type testCase struct {
desc string
isInstalled bool
isBuilt bool
wantShow []string
wantCapture []string
}
testCases := []testCase{
{
desc: "not installed and not built",
isInstalled: false,
isBuilt: false,
wantShow: []string{
"makepkg --nobuild -f -C --ignorearch",
"makepkg -f -c --noconfirm --noextract --noprepare --holdver --ignorearch",
"pacman -U --needed --config -- /testdir/yay-91.0.0-1-x86_64.pkg.tar.zst",
"pacman -D -q --asexplicit --config -- yay",
},
wantCapture: []string{"makepkg --packagelist"},
},
{
desc: "not installed and built",
isInstalled: false,
isBuilt: true,
wantShow: []string{
"makepkg --nobuild -f -C --ignorearch",
"makepkg -c --nobuild --noextract --ignorearch",
"pacman -U --needed --config -- /testdir/yay-91.0.0-1-x86_64.pkg.tar.zst",
"pacman -D -q --asexplicit --config -- yay",
},
wantCapture: []string{"makepkg --packagelist"},
},
{
desc: "installed",
isInstalled: true,
isBuilt: false,
wantShow: []string{
"makepkg --nobuild -f -C --ignorearch",
"makepkg -c --nobuild --noextract --ignorearch",
},
wantCapture: []string{"makepkg --packagelist"},
},
}
for _, tc := range testCases {
t.Run(tc.desc, func(td *testing.T) {
tmpDir := td.TempDir()
pkgTar := tmpDir + "/yay-91.0.0-1-x86_64.pkg.tar.zst"
captureOverride := func(cmd *exec.Cmd) (stdout string, stderr string, err error) {
return pkgTar, "", nil
}
i := 0
showOverride := func(cmd *exec.Cmd) error {
i++
if i == 2 {
if !tc.isBuilt {
f, err := os.OpenFile(pkgTar, os.O_RDONLY|os.O_CREATE, 0o666)
require.NoError(td, err)
require.NoError(td, f.Close())
}
}
return nil
}
// create a mock file
if tc.isBuilt {
f, err := os.OpenFile(pkgTar, os.O_RDONLY|os.O_CREATE, 0o666)
require.NoError(td, err)
require.NoError(td, f.Close())
}
isCorrectInstalledOverride := func(string, string) bool {
return tc.isInstalled
}
mockDB := &mock.DBExecutor{IsCorrectVersionInstalledFn: isCorrectInstalledOverride}
mockRunner := &exe.MockRunner{CaptureFn: captureOverride, ShowFn: showOverride}
cmdBuilder := &exe.CmdBuilder{
MakepkgBin: makepkgBin,
SudoBin: "su",
PacmanBin: pacmanBin,
Runner: mockRunner,
SudoLoopEnabled: false,
}
cmdBuilder.Runner = mockRunner
installer := NewInstaller(mockDB, cmdBuilder, &vcs.Mock{}, parser.ModeAny,
parser.RebuildModeNo, false, newTestLogger())
cmdArgs := parser.MakeArguments()
cmdArgs.AddArg("needed")
cmdArgs.AddTarget("yay")
pkgBuildDirs := map[string]string{
"yay": tmpDir,
}
targets := []map[string]*dep.InstallInfo{
{
"yay": {
Source: dep.AUR,
Reason: dep.Explicit,
Version: "91.0.0-1",
SrcinfoPath: ptrString(tmpDir + "/.SRCINFO"),
AURBase: ptrString("yay"),
},
},
}
errI := installer.Install(context.Background(), cmdArgs, targets, pkgBuildDirs, []string{}, false)
require.NoError(td, errI)
require.Len(td, mockRunner.ShowCalls, len(tc.wantShow))
require.Len(td, mockRunner.CaptureCalls, len(tc.wantCapture))
for i, call := range mockRunner.ShowCalls {
show := call.Args[0].(*exec.Cmd).String()
show = strings.ReplaceAll(show, tmpDir, "/testdir") // replace the temp dir with a static path
show = strings.ReplaceAll(show, makepkgBin, "makepkg")
show = strings.ReplaceAll(show, pacmanBin, "pacman")
// options are in a different order on different systems and on CI root user is used
assert.Subset(td, strings.Split(show, " "), strings.Split(tc.wantShow[i], " "), show)
}
for i, call := range mockRunner.CaptureCalls {
capture := call.Args[0].(*exec.Cmd).String()
capture = strings.ReplaceAll(capture, tmpDir, "/testdir") // replace the temp dir with a static path
capture = strings.ReplaceAll(capture, makepkgBin, "makepkg")
capture = strings.ReplaceAll(capture, pacmanBin, "pacman")
assert.Subset(td, strings.Split(capture, " "), strings.Split(tc.wantCapture[i], " "), capture)
}
})
}
}
func TestInstaller_BuildOnlySkipsInstall(t *testing.T) {
t.Parallel()
makepkgBin := t.TempDir() + "/makepkg"
pacmanBin := t.TempDir() + "/pacman"
f, err := os.OpenFile(makepkgBin, os.O_RDONLY|os.O_CREATE, 0o755)
require.NoError(t, err)
require.NoError(t, f.Close())
f, err = os.OpenFile(pacmanBin, os.O_RDONLY|os.O_CREATE, 0o755)
require.NoError(t, err)
require.NoError(t, f.Close())
tmpDir := t.TempDir()
pkgTar := tmpDir + "/yay-91.0.0-1-x86_64.pkg.tar.zst"
captureOverride := func(cmd *exec.Cmd) (stdout string, stderr string, err error) {
return pkgTar, "", nil
}
i := 0
showOverride := func(cmd *exec.Cmd) error {
i++
if i == 2 {
f, err := os.OpenFile(pkgTar, os.O_RDONLY|os.O_CREATE, 0o666)
require.NoError(t, err)
require.NoError(t, f.Close())
}
return nil
}
mockDB := &mock.DBExecutor{}
mockRunner := &exe.MockRunner{CaptureFn: captureOverride, ShowFn: showOverride}
cmdBuilder := &exe.CmdBuilder{
MakepkgBin: makepkgBin,
SudoBin: "su",
PacmanBin: pacmanBin,
Runner: mockRunner,
SudoLoopEnabled: false,
}
cmdBuilder.Runner = mockRunner
installer := NewInstaller(mockDB, cmdBuilder, &vcs.Mock{}, parser.ModeAny,
parser.RebuildModeNo, false, newTestLogger())
installer.SetInstallBuiltPackages(false)
cmdArgs := parser.MakeArguments()
cmdArgs.AddTarget("yay")
pkgBuildDirs := map[string]string{
"yay": tmpDir,
}
targets := []map[string]*dep.InstallInfo{
{
"yay": {
Source: dep.AUR,
Reason: dep.Explicit,
Version: "91.0.0-1",
SrcinfoPath: ptrString(tmpDir + "/.SRCINFO"),
AURBase: ptrString("yay"),
},
},
}
err = installer.Install(context.Background(), cmdArgs, targets, pkgBuildDirs, []string{}, false)
require.NoError(t, err)
wantShow := []string{
"makepkg --nobuild -f -C --ignorearch",
"makepkg -f -c --noconfirm --noextract --noprepare --holdver --ignorearch",
}
require.Len(t, mockRunner.ShowCalls, len(wantShow))
require.Len(t, mockRunner.CaptureCalls, 1)
for i, call := range mockRunner.ShowCalls {
show := call.Args[0].(*exec.Cmd).String()
show = strings.ReplaceAll(show, tmpDir, "/testdir")
show = strings.ReplaceAll(show, makepkgBin, "makepkg")
show = strings.ReplaceAll(show, pacmanBin, "pacman")
assert.Subset(t, strings.Split(show, " "), strings.Split(wantShow[i], " "), show)
assert.NotContains(t, show, "pacman -U")
}
for _, call := range mockRunner.CaptureCalls {
capture := call.Args[0].(*exec.Cmd).String()
capture = strings.ReplaceAll(capture, tmpDir, "/testdir")
capture = strings.ReplaceAll(capture, makepkgBin, "makepkg")
capture = strings.ReplaceAll(capture, pacmanBin, "pacman")
assert.Subset(t, strings.Split(capture, " "), strings.Split("makepkg --packagelist", " "), capture)
}
}
func TestInstaller_InstallMixedSourcesAndLayers(t *testing.T) {
t.Parallel()
makepkgBin := t.TempDir() + "/makepkg"
pacmanBin := t.TempDir() + "/pacman"
f, err := os.OpenFile(makepkgBin, os.O_RDONLY|os.O_CREATE, 0o755)
require.NoError(t, err)
require.NoError(t, f.Close())
f, err = os.OpenFile(pacmanBin, os.O_RDONLY|os.O_CREATE, 0o755)
require.NoError(t, err)
require.NoError(t, f.Close())
type testCase struct {
desc string
targets []map[string]*dep.InstallInfo
wantShow []string
wantCapture []string
}
tmpDir := t.TempDir()
tmpDirJfin := t.TempDir()
testCases := []testCase{
{
desc: "same layer -- different sources",
wantShow: []string{
"pacman -S --config /etc/pacman.conf -- core/linux",
"pacman -D -q --asdeps --config /etc/pacman.conf -- linux",
"makepkg --nobuild -f -C --ignorearch",
"makepkg -f -c --noconfirm --noextract --noprepare --holdver --ignorearch",
"pacman -U --config /etc/pacman.conf -- /testdir/yay-91.0.0-1-x86_64.pkg.tar.zst",
"pacman -D -q --asexplicit --config /etc/pacman.conf -- yay",
},
wantCapture: []string{"makepkg --packagelist"},
targets: []map[string]*dep.InstallInfo{
{
"yay": {
Source: dep.AUR,
Reason: dep.Explicit,
Version: "91.0.0-1",
SrcinfoPath: ptrString(tmpDir + "/.SRCINFO"),
AURBase: ptrString("yay"),
},
"linux": {
Source: dep.Sync,
Reason: dep.Dep,
Version: "17.0.0-1",
SyncDBName: ptrString("core"),
},
},
},
},
{
desc: "different layer -- different sources",
wantShow: []string{
"pacman -S --config /etc/pacman.conf -- core/linux",
"pacman -D -q --asdeps --config /etc/pacman.conf -- linux",
"makepkg --nobuild -f -C --ignorearch",
"makepkg -f -c --noconfirm --noextract --noprepare --holdver --ignorearch",
"pacman -U --config /etc/pacman.conf -- /testdir/yay-91.0.0-1-x86_64.pkg.tar.zst",
"pacman -D -q --asexplicit --config /etc/pacman.conf -- yay",
},
wantCapture: []string{"makepkg --packagelist"},
targets: []map[string]*dep.InstallInfo{
{
"yay": {
Source: dep.AUR,
Reason: dep.Explicit,
Version: "91.0.0-1",
SrcinfoPath: ptrString(tmpDir + "/.SRCINFO"),
AURBase: ptrString("yay"),
},
}, {
"linux": {
Source: dep.Sync,
Reason: dep.Dep,
Version: "17.0.0-1",
SyncDBName: ptrString("core"),
},
},
},
},
{
desc: "same layer -- sync",
wantShow: []string{
"pacman -S --config /etc/pacman.conf -- extra/linux-zen core/linux",
"pacman -D -q --asexplicit --config /etc/pacman.conf -- linux-zen linux",
},
wantCapture: []string{},
targets: []map[string]*dep.InstallInfo{
{
"linux-zen": {
Source: dep.Sync,
Reason: dep.Explicit,
Version: "18.0.0-1",
SyncDBName: ptrString("extra"),
},
"linux": {
Source: dep.Sync,
Reason: dep.Explicit,
Version: "17.0.0-1",
SyncDBName: ptrString("core"),
},
},
},
},
{
desc: "same layer -- aur",
wantShow: []string{
"makepkg --nobuild -f -C --ignorearch",
"makepkg -f -c --noconfirm --noextract --noprepare --holdver --ignorearch",
"makepkg --nobuild -f -C --ignorearch",
"makepkg -f -c --noconfirm --noextract --noprepare --holdver --ignorearch",
"pacman -U --config /etc/pacman.conf -- pacman -U --config /etc/pacman.conf -- /testdir/yay-91.0.0-1-x86_64.pkg.tar.zst",
"pacman -D -q --asexplicit --config /etc/pacman.conf -- yay",
},
wantCapture: []string{"makepkg --packagelist", "makepkg --packagelist"},
targets: []map[string]*dep.InstallInfo{
{
"yay": {
Source: dep.AUR,
Reason: dep.Explicit,
Version: "91.0.0-1",
SrcinfoPath: ptrString(tmpDir + "/.SRCINFO"),
AURBase: ptrString("yay"),
},
"jellyfin-server": {
Source: dep.AUR,
Reason: dep.Explicit,
Version: "10.8.8-1",
SrcinfoPath: ptrString(tmpDirJfin + "/.SRCINFO"),
AURBase: ptrString("jellyfin"),
},
},
},
},
{
desc: "different layer -- aur",
wantShow: []string{
"makepkg --nobuild -f -C --ignorearch",
"makepkg -f -c --noconfirm --noextract --noprepare --holdver --ignorearch",
"pacman -U --config /etc/pacman.conf -- pacman -U --config /etc/pacman.conf -- /testdir/jellyfin-server-10.8.8-1-x86_64.pkg.tar.zst",
"pacman -D -q --asdeps --config /etc/pacman.conf -- jellyfin-server",
"makepkg --nobuild -f -C --ignorearch",
"makepkg -f -c --noconfirm --noextract --noprepare --holdver --ignorearch",
"pacman -U --config /etc/pacman.conf -- pacman -U --config /etc/pacman.conf -- /testdir/yay-91.0.0-1-x86_64.pkg.tar.zst",
"pacman -D -q --asexplicit --config /etc/pacman.conf -- yay",
},
wantCapture: []string{"makepkg --packagelist", "makepkg --packagelist"},
targets: []map[string]*dep.InstallInfo{
{
"yay": {
Source: dep.AUR,
Reason: dep.Explicit,
Version: "91.0.0-1",
SrcinfoPath: ptrString(tmpDir + "/.SRCINFO"),
AURBase: ptrString("yay"),
},
}, {
"jellyfin-server": {
Source: dep.AUR,
Reason: dep.MakeDep,
Version: "10.8.8-1",
SrcinfoPath: ptrString(tmpDirJfin + "/.SRCINFO"),
AURBase: ptrString("jellyfin"),
},
},
},
},
}
for _, tc := range testCases {
t.Run(tc.desc, func(td *testing.T) {
pkgTar := tmpDir + "/yay-91.0.0-1-x86_64.pkg.tar.zst"
jfinPkgTar := tmpDirJfin + "/jellyfin-server-10.8.8-1-x86_64.pkg.tar.zst"
captureOverride := func(cmd *exec.Cmd) (stdout string, stderr string, err error) {
if cmd.Dir == tmpDirJfin {
return jfinPkgTar, "", nil
}
if cmd.Dir == tmpDir {
return pkgTar, "", nil
}
return "", "", fmt.Errorf("unexpected command: %s - %s", cmd.String(), cmd.Dir)
}
showOverride := func(cmd *exec.Cmd) error {
if strings.Contains(cmd.String(), "makepkg -f --noconfirm") && cmd.Dir == tmpDir {
f, err := os.OpenFile(pkgTar, os.O_RDONLY|os.O_CREATE, 0o666)
require.NoError(td, err)
require.NoError(td, f.Close())
}
if strings.Contains(cmd.String(), "makepkg -f --noconfirm") && cmd.Dir == tmpDirJfin {
f, err := os.OpenFile(jfinPkgTar, os.O_RDONLY|os.O_CREATE, 0o666)
require.NoError(td, err)
require.NoError(td, f.Close())
}
return nil
}
defer os.Remove(pkgTar)
defer os.Remove(jfinPkgTar)
isCorrectInstalledOverride := func(string, string) bool {
return false
}
mockDB := &mock.DBExecutor{IsCorrectVersionInstalledFn: isCorrectInstalledOverride}
mockRunner := &exe.MockRunner{CaptureFn: captureOverride, ShowFn: showOverride}
cmdBuilder := &exe.CmdBuilder{
MakepkgBin: makepkgBin,
SudoBin: "su",
PacmanBin: pacmanBin,
PacmanConfigPath: "/etc/pacman.conf",
Runner: mockRunner,
SudoLoopEnabled: false,
}
cmdBuilder.Runner = mockRunner
installer := NewInstaller(mockDB, cmdBuilder, &vcs.Mock{}, parser.ModeAny, parser.RebuildModeNo, false, newTestLogger())
cmdArgs := parser.MakeArguments()
cmdArgs.AddTarget("yay")
pkgBuildDirs := map[string]string{
"yay": tmpDir,
"jellyfin": tmpDirJfin,
}
errI := installer.Install(context.Background(), cmdArgs, tc.targets, pkgBuildDirs, []string{}, false)
require.NoError(td, errI)
require.Len(td, mockRunner.ShowCalls, len(tc.wantShow))
require.Len(td, mockRunner.CaptureCalls, len(tc.wantCapture))
for i, call := range mockRunner.ShowCalls {
show := call.Args[0].(*exec.Cmd).String()
show = strings.ReplaceAll(show, tmpDir, "/testdir") // replace the temp dir with a static path
show = strings.ReplaceAll(show, tmpDirJfin, "/testdir") // replace the temp dir with a static path
show = strings.ReplaceAll(show, makepkgBin, "makepkg")
show = strings.ReplaceAll(show, pacmanBin, "pacman")
// options are in a different order on different systems and on CI root user is used
assert.Subset(td, strings.Split(show, " "), strings.Split(tc.wantShow[i], " "), show)
}
for i, call := range mockRunner.CaptureCalls {
capture := call.Args[0].(*exec.Cmd).String()
capture = strings.ReplaceAll(capture, tmpDir, "/testdir") // replace the temp dir with a static path
capture = strings.ReplaceAll(capture, tmpDirJfin, "/testdir")
capture = strings.ReplaceAll(capture, makepkgBin, "makepkg")
capture = strings.ReplaceAll(capture, pacmanBin, "pacman")
assert.Subset(td, strings.Split(capture, " "), strings.Split(tc.wantCapture[i], " "), capture)
}
})
}
}
func TestInstaller_RunPostHooks(t *testing.T) {
mockDB := &mock.DBExecutor{}
mockRunner := &exe.MockRunner{}
cmdBuilder := &exe.CmdBuilder{
MakepkgBin: "makepkg",
SudoBin: "su",
PacmanBin: "pacman",
PacmanConfigPath: "/etc/pacman.conf",
Runner: mockRunner,
SudoLoopEnabled: false,
}
cmdBuilder.Runner = mockRunner
installer := NewInstaller(mockDB, cmdBuilder, &vcs.Mock{}, parser.ModeAny,
parser.RebuildModeNo, false, newTestLogger())
called := false
hook := func(ctx context.Context) error {
called = true
return nil
}
installer.AddPostInstallHook(hook)
installer.RunPostInstallHooks(context.Background())
assert.True(t, called)
}
func TestInstaller_CompileFailed(t *testing.T) {
t.Parallel()
makepkgBin := t.TempDir() + "/makepkg"
pacmanBin := t.TempDir() + "/pacman"
f, err := os.OpenFile(makepkgBin, os.O_RDONLY|os.O_CREATE, 0o755)
require.NoError(t, err)
require.NoError(t, f.Close())
f, err = os.OpenFile(pacmanBin, os.O_RDONLY|os.O_CREATE, 0o755)
require.NoError(t, err)
require.NoError(t, f.Close())
type testCase struct {
desc string
targets []map[string]*dep.InstallInfo
wantErrInstall bool
wantErrCompile bool
failBuild bool
failPkgInstall bool
}
tmpDir := t.TempDir()
testCases := []testCase{
{
desc: "one layer",
wantErrInstall: false,
wantErrCompile: true,
failBuild: true,
failPkgInstall: false,
targets: []map[string]*dep.InstallInfo{
{
"yay": {
Source: dep.AUR,
Reason: dep.Explicit,
Version: "91.0.0-1",
SrcinfoPath: ptrString(tmpDir + "/.SRCINFO"),
AURBase: ptrString("yay"),
},
},
},
},
{
desc: "one layer -- fail install",
wantErrInstall: true,
wantErrCompile: false,
failBuild: false,
failPkgInstall: true,
targets: []map[string]*dep.InstallInfo{
{
"yay": {
Source: dep.AUR,
Reason: dep.Explicit,
Version: "91.0.0-1",
SrcinfoPath: ptrString(tmpDir + "/.SRCINFO"),
AURBase: ptrString("yay"),
},
},
},
},
{
desc: "two layers",
wantErrInstall: false,
wantErrCompile: true,
failBuild: true,
failPkgInstall: false,
targets: []map[string]*dep.InstallInfo{
{"bob": {
AURBase: ptrString("yay"),
}},
{
"yay": {
Source: dep.AUR,
Reason: dep.Explicit,
Version: "91.0.0-1",
SrcinfoPath: ptrString(tmpDir + "/.SRCINFO"),
AURBase: ptrString("yay"),
},
},
},
},
}
for _, tc := range testCases {
t.Run(tc.desc, func(td *testing.T) {
pkgTar := tmpDir + "/yay-91.0.0-1-x86_64.pkg.tar.zst"
captureOverride := func(cmd *exec.Cmd) (stdout string, stderr string, err error) {
return pkgTar, "", nil
}
showOverride := func(cmd *exec.Cmd) error {
if tc.failBuild && strings.Contains(cmd.String(), "makepkg -f --noconfirm") && cmd.Dir == tmpDir {
return errors.New("makepkg failed")
}
return nil
}
isCorrectInstalledOverride := func(string, string) bool {
return false
}
mockDB := &mock.DBExecutor{IsCorrectVersionInstalledFn: isCorrectInstalledOverride}
mockRunner := &exe.MockRunner{CaptureFn: captureOverride, ShowFn: showOverride}
cmdBuilder := &exe.CmdBuilder{
MakepkgBin: makepkgBin,
SudoBin: "su",
PacmanBin: pacmanBin,
Runner: mockRunner,
SudoLoopEnabled: false,
}
cmdBuilder.Runner = mockRunner
installer := NewInstaller(mockDB, cmdBuilder, &vcs.Mock{}, parser.ModeAny,
parser.RebuildModeNo, false, newTestLogger())
cmdArgs := parser.MakeArguments()
cmdArgs.AddArg("needed")
cmdArgs.AddTarget("yay")
pkgBuildDirs := map[string]string{
"yay": tmpDir,
}
errI := installer.Install(context.Background(), cmdArgs, tc.targets, pkgBuildDirs, []string{}, false)
if tc.wantErrInstall {
require.Error(td, errI)
} else {
require.NoError(td, errI)
}
failed, err := installer.CompileFailedAndIgnored()
if tc.wantErrCompile {
require.Error(td, err)
for key := range failed {
assert.ErrorContains(td, err, key)
}
uniqueBases := make(map[string]struct{})
for _, layer := range tc.targets {
for _, info := range layer {
if info.AURBase != nil {
uniqueBases[*info.AURBase] = struct{}{}
}
}
}
require.Len(td, failed, len(uniqueBases))
} else {
require.NoError(td, err)
}
})
}
}
func TestInstaller_InstallSplitPackage(t *testing.T) {
t.Parallel()
makepkgBin := t.TempDir() + "/makepkg"
pacmanBin := t.TempDir() + "/pacman"
f, err := os.OpenFile(makepkgBin, os.O_RDONLY|os.O_CREATE, 0o755)
require.NoError(t, err)
require.NoError(t, f.Close())
f, err = os.OpenFile(pacmanBin, os.O_RDONLY|os.O_CREATE, 0o755)
require.NoError(t, err)
require.NoError(t, f.Close())
type testCase struct {
desc string
wantShow []string
wantCapture []string
targets []map[string]*dep.InstallInfo
}
tmpDir := t.TempDir()
testCases := []testCase{
{
desc: "jellyfin",
targets: []map[string]*dep.InstallInfo{
{"jellyfin": {
Source: dep.AUR,
Reason: dep.Explicit,
Version: "10.8.4-1",
SrcinfoPath: ptrString(tmpDir + "/.SRCINFO"),
AURBase: ptrString("jellyfin"),
}},
{
"jellyfin-server": {
Source: dep.AUR,
Reason: dep.Dep,
Version: "10.8.4-1",
SrcinfoPath: ptrString(tmpDir + "/.SRCINFO"),
AURBase: ptrString("jellyfin"),
},
"jellyfin-web": {
Source: dep.AUR,
Reason: dep.Dep,
Version: "10.8.4-1",
SrcinfoPath: ptrString(tmpDir + "/.SRCINFO"),
AURBase: ptrString("jellyfin"),
},
},
{
"dotnet-runtime-6.0": {
Source: dep.Sync,
Reason: dep.Dep,
Version: "6.0.12.sdk112-1",
SyncDBName: ptrString("community"),
},
"aspnet-runtime": {
Source: dep.Sync,
Reason: dep.Dep,
Version: "6.0.12.sdk112-1",
SyncDBName: ptrString("community"),
},
"dotnet-sdk-6.0": {
Source: dep.Sync,
Reason: dep.MakeDep,
Version: "6.0.12.sdk112-1",
SyncDBName: ptrString("community"),
},
},
},
wantShow: []string{
"pacman -S --config /etc/pacman.conf -- community/dotnet-runtime-6.0 community/aspnet-runtime community/dotnet-sdk-6.0",
"pacman -D -q --asdeps --config /etc/pacman.conf -- dotnet-runtime-6.0 aspnet-runtime dotnet-sdk-6.0",
"makepkg --nobuild -f -C --ignorearch",
"makepkg -f -c --noconfirm --noextract --noprepare --holdver --ignorearch",
"pacman -U --config /etc/pacman.conf -- /testdir/jellyfin-web-10.8.4-1-x86_64.pkg.tar.zst /testdir/jellyfin-server-10.8.4-1-x86_64.pkg.tar.zst",
"pacman -D -q --asdeps --config /etc/pacman.conf -- jellyfin-server jellyfin-web",
"makepkg --nobuild -f -C --ignorearch",
"makepkg -c --nobuild --noextract --ignorearch",
"pacman -U --config /etc/pacman.conf -- /testdir/jellyfin-10.8.4-1-x86_64.pkg.tar.zst",
"pacman -D -q --asexplicit --config /etc/pacman.conf -- jellyfin",
},
wantCapture: []string{"makepkg --packagelist", "makepkg --packagelist"},
},
}
for _, tc := range testCases {
t.Run(tc.desc, func(td *testing.T) {
pkgTars := []string{
tmpDir + "/jellyfin-10.8.4-1-x86_64.pkg.tar.zst",
tmpDir + "/jellyfin-web-10.8.4-1-x86_64.pkg.tar.zst",
tmpDir + "/jellyfin-server-10.8.4-1-x86_64.pkg.tar.zst",
}
captureOverride := func(cmd *exec.Cmd) (stdout string, stderr string, err error) {
return strings.Join(pkgTars, "\n"), "", nil
}
i := 0
showOverride := func(cmd *exec.Cmd) error {
i++
if i == 4 {
for _, pkgTar := range pkgTars {
f, err := os.OpenFile(pkgTar, os.O_RDONLY|os.O_CREATE, 0o666)
require.NoError(td, err)
require.NoError(td, f.Close())
}
}
return nil
}
isCorrectInstalledOverride := func(string, string) bool {
return false
}
mockDB := &mock.DBExecutor{IsCorrectVersionInstalledFn: isCorrectInstalledOverride}
mockRunner := &exe.MockRunner{CaptureFn: captureOverride, ShowFn: showOverride}
cmdBuilder := &exe.CmdBuilder{
MakepkgBin: makepkgBin,
SudoBin: "su",
PacmanBin: pacmanBin,
PacmanConfigPath: "/etc/pacman.conf",
Runner: mockRunner,
SudoLoopEnabled: false,
}
cmdBuilder.Runner = mockRunner
installer := NewInstaller(mockDB, cmdBuilder, &vcs.Mock{}, parser.ModeAny,
parser.RebuildModeNo, false, newTestLogger())
cmdArgs := parser.MakeArguments()
cmdArgs.AddTarget("jellyfin")
pkgBuildDirs := map[string]string{
"jellyfin": tmpDir,
}
errI := installer.Install(context.Background(), cmdArgs, tc.targets, pkgBuildDirs, []string{}, false)
require.NoError(td, errI)
require.Len(td, mockRunner.ShowCalls, len(tc.wantShow))
require.Len(td, mockRunner.CaptureCalls, len(tc.wantCapture))
for i, call := range mockRunner.ShowCalls {
show := call.Args[0].(*exec.Cmd).String()
show = strings.ReplaceAll(show, tmpDir, "/testdir") // replace the temp dir with a static path
show = strings.ReplaceAll(show, makepkgBin, "makepkg")
show = strings.ReplaceAll(show, pacmanBin, "pacman")
// options are in a different order on different systems and on CI root user is used
assert.Subset(td, strings.Split(show, " "),
strings.Split(tc.wantShow[i], " "),
fmt.Sprintf("got at %d: %s \n", i, show))
}
for i, call := range mockRunner.CaptureCalls {
capture := call.Args[0].(*exec.Cmd).String()
capture = strings.ReplaceAll(capture, tmpDir, "/testdir") // replace the temp dir with a static path
capture = strings.ReplaceAll(capture, makepkgBin, "makepkg")
capture = strings.ReplaceAll(capture, pacmanBin, "pacman")
assert.Subset(td, strings.Split(capture, " "), strings.Split(tc.wantCapture[i], " "), capture)
}
})
}
}
func TestInstaller_InstallDownloadOnly(t *testing.T) {
t.Parallel()
makepkgBin := t.TempDir() + "/makepkg"
pacmanBin := t.TempDir() + "/pacman"
f, err := os.OpenFile(makepkgBin, os.O_RDONLY|os.O_CREATE, 0o755)
require.NoError(t, err)
require.NoError(t, f.Close())
f, err = os.OpenFile(pacmanBin, os.O_RDONLY|os.O_CREATE, 0o755)
require.NoError(t, err)
require.NoError(t, f.Close())
type testCase struct {
desc string
isInstalled bool
isBuilt bool
wantShow []string
wantCapture []string
}
testCases := []testCase{
{
desc: "not installed and not built",
isInstalled: false,
isBuilt: false,
wantShow: []string{
"makepkg --nobuild -f -C --ignorearch",
"makepkg -c --nobuild --noextract --ignorearch",
},
wantCapture: []string{"makepkg --packagelist"},
},
{
desc: "not installed and built",
isInstalled: false,
isBuilt: true,
wantShow: []string{
"makepkg --nobuild -f -C --ignorearch",
"makepkg -c --nobuild --noextract --ignorearch",
},
wantCapture: []string{"makepkg --packagelist"},
},
{
desc: "installed",
isInstalled: true,
isBuilt: false,
wantShow: []string{
"makepkg --nobuild -f -C --ignorearch",
"makepkg -c --nobuild --noextract --ignorearch",
},
wantCapture: []string{"makepkg --packagelist"},
},
}
for _, tc := range testCases {
t.Run(tc.desc, func(td *testing.T) {
tmpDir := td.TempDir()
pkgTar := tmpDir + "/yay-91.0.0-1-x86_64.pkg.tar.zst"
captureOverride := func(cmd *exec.Cmd) (stdout string, stderr string, err error) {
return pkgTar, "", nil
}
i := 0
showOverride := func(cmd *exec.Cmd) error {
i++
if i == 2 {
if !tc.isBuilt {
f, err := os.OpenFile(pkgTar, os.O_RDONLY|os.O_CREATE, 0o666)
require.NoError(td, err)
require.NoError(td, f.Close())
}
}
return nil
}
// create a mock file
if tc.isBuilt {
f, err := os.OpenFile(pkgTar, os.O_RDONLY|os.O_CREATE, 0o666)
require.NoError(td, err)
require.NoError(td, f.Close())
}
isCorrectInstalledOverride := func(string, string) bool {
return tc.isInstalled
}
mockDB := &mock.DBExecutor{IsCorrectVersionInstalledFn: isCorrectInstalledOverride}
mockRunner := &exe.MockRunner{CaptureFn: captureOverride, ShowFn: showOverride}
cmdBuilder := &exe.CmdBuilder{
MakepkgBin: makepkgBin,
SudoBin: "su",
PacmanBin: pacmanBin,
Runner: mockRunner,
SudoLoopEnabled: false,
}
cmdBuilder.Runner = mockRunner
installer := NewInstaller(mockDB, cmdBuilder, &vcs.Mock{}, parser.ModeAny,
parser.RebuildModeNo, true, newTestLogger())
cmdArgs := parser.MakeArguments()
cmdArgs.AddTarget("yay")
pkgBuildDirs := map[string]string{
"yay": tmpDir,
}
targets := []map[string]*dep.InstallInfo{
{
"yay": {
Source: dep.AUR,
Reason: dep.Explicit,
Version: "91.0.0-1",
SrcinfoPath: ptrString(tmpDir + "/.SRCINFO"),
AURBase: ptrString("yay"),
},
},
}
errI := installer.Install(context.Background(), cmdArgs, targets, pkgBuildDirs, []string{}, false)
require.NoError(td, errI)
require.Len(td, mockRunner.ShowCalls, len(tc.wantShow))
require.Len(td, mockRunner.CaptureCalls, len(tc.wantCapture))
require.Empty(td, installer.failedAndIgnored)
for i, call := range mockRunner.ShowCalls {
show := call.Args[0].(*exec.Cmd).String()
show = strings.ReplaceAll(show, tmpDir, "/testdir") // replace the temp dir with a static path
show = strings.ReplaceAll(show, makepkgBin, "makepkg")
show = strings.ReplaceAll(show, pacmanBin, "pacman")
// options are in a different order on different systems and on CI root user is used
assert.Subset(td, strings.Split(show, " "), strings.Split(tc.wantShow[i], " "), show)
}
for i, call := range mockRunner.CaptureCalls {
capture := call.Args[0].(*exec.Cmd).String()
capture = strings.ReplaceAll(capture, tmpDir, "/testdir") // replace the temp dir with a static path
capture = strings.ReplaceAll(capture, makepkgBin, "makepkg")
capture = strings.ReplaceAll(capture, pacmanBin, "pacman")
assert.Subset(td, strings.Split(capture, " "), strings.Split(tc.wantCapture[i], " "), capture)
}
})
}
}
func TestInstaller_InstallGroup(t *testing.T) {
t.Parallel()
makepkgBin := t.TempDir() + "/makepkg"
pacmanBin := t.TempDir() + "/pacman"
f, err := os.OpenFile(makepkgBin, os.O_RDONLY|os.O_CREATE, 0o755)
require.NoError(t, err)
require.NoError(t, f.Close())
f, err = os.OpenFile(pacmanBin, os.O_RDONLY|os.O_CREATE, 0o755)
require.NoError(t, err)
require.NoError(t, f.Close())
type testCase struct {
desc string
wantShow []string
wantCapture []string
}
testCases := []testCase{
{
desc: "group",
wantShow: []string{
"pacman -S --noconfirm --config -- community/kubernetes-tools",
},
wantCapture: []string{},
},
}
for _, tc := range testCases {
t.Run(tc.desc, func(td *testing.T) {
tmpDir := td.TempDir()
captureOverride := func(cmd *exec.Cmd) (stdout string, stderr string, err error) {
return "", "", nil
}
showOverride := func(cmd *exec.Cmd) error {
return nil
}
mockDB := &mock.DBExecutor{}
mockRunner := &exe.MockRunner{CaptureFn: captureOverride, ShowFn: showOverride}
cmdBuilder := &exe.CmdBuilder{
MakepkgBin: makepkgBin,
SudoBin: "su",
PacmanBin: pacmanBin,
Runner: mockRunner,
SudoLoopEnabled: false,
}
cmdBuilder.Runner = mockRunner
installer := NewInstaller(mockDB, cmdBuilder, &vcs.Mock{}, parser.ModeAny,
parser.RebuildModeNo, true, newTestLogger())
cmdArgs := parser.MakeArguments()
cmdArgs.AddTarget("kubernetes-tools")
pkgBuildDirs := map[string]string{}
targets := []map[string]*dep.InstallInfo{
{
"kubernetes-tools": {
Source: dep.Sync,
Reason: dep.Explicit,
Version: "",
IsGroup: true,
SyncDBName: ptrString("community"),
},
},
}
errI := installer.Install(context.Background(), cmdArgs, targets, pkgBuildDirs, []string{}, false)
require.NoError(td, errI)
require.Len(td, mockRunner.ShowCalls, len(tc.wantShow))
require.Len(td, mockRunner.CaptureCalls, len(tc.wantCapture))
require.Empty(td, installer.failedAndIgnored)
for i, call := range mockRunner.ShowCalls {
show := call.Args[0].(*exec.Cmd).String()
show = strings.ReplaceAll(show, tmpDir, "/testdir") // replace the temp dir with a static path
show = strings.ReplaceAll(show, makepkgBin, "makepkg")
show = strings.ReplaceAll(show, pacmanBin, "pacman")
// options are in a different order on different systems and on CI root user is used
assert.Subset(td, strings.Split(show, " "), strings.Split(tc.wantShow[i], " "), show)
}
for i, call := range mockRunner.CaptureCalls {
capture := call.Args[0].(*exec.Cmd).String()
capture = strings.ReplaceAll(capture, tmpDir, "/testdir") // replace the temp dir with a static path
capture = strings.ReplaceAll(capture, makepkgBin, "makepkg")
capture = strings.ReplaceAll(capture, pacmanBin, "pacman")
assert.Subset(td, strings.Split(capture, " "), strings.Split(tc.wantCapture[i], " "), capture)
}
})
}
}
func TestInstaller_InstallRebuild(t *testing.T) {
t.Parallel()
makepkgBin := t.TempDir() + "/makepkg"
pacmanBin := t.TempDir() + "/pacman"
f, err := os.OpenFile(makepkgBin, os.O_RDONLY|os.O_CREATE, 0o755)
require.NoError(t, err)
require.NoError(t, f.Close())
f, err = os.OpenFile(pacmanBin, os.O_RDONLY|os.O_CREATE, 0o755)
require.NoError(t, err)
require.NoError(t, f.Close())
type testCase struct {
desc string
rebuildOption parser.RebuildMode
isInstalled bool
isBuilt bool
wantShow []string
wantCapture []string
targets []map[string]*dep.InstallInfo
}
tmpDir := t.TempDir()
testCases := []testCase{
{
desc: "--norebuild(default) when built and not installed",
rebuildOption: parser.RebuildModeNo,
isBuilt: true,
isInstalled: false,
wantShow: []string{
"makepkg --nobuild -f -C --ignorearch",
"makepkg -c --nobuild --noextract --ignorearch",
"pacman -U --config -- /testdir/yay-91.0.0-1-x86_64.pkg.tar.zst",
"pacman -D -q --asexplicit --config -- yay",
},
wantCapture: []string{"makepkg --packagelist"},
targets: []map[string]*dep.InstallInfo{
{
"yay": {
Source: dep.AUR,
Reason: dep.Explicit,
Version: "91.0.0-1",
SrcinfoPath: ptrString(tmpDir + "/.SRCINFO"),
AURBase: ptrString("yay"),
},
},
},
},
{
desc: "--rebuild when built and not installed",
rebuildOption: parser.RebuildModeYes,
isBuilt: true,
isInstalled: false,
wantShow: []string{
"makepkg --nobuild -f -C --ignorearch",
"makepkg -f -c --noconfirm --noextract --noprepare --holdver --ignorearch",
"pacman -U --config -- /testdir/yay-91.0.0-1-x86_64.pkg.tar.zst",
"pacman -D -q --asexplicit --config -- yay",
},
wantCapture: []string{"makepkg --packagelist"},
targets: []map[string]*dep.InstallInfo{
{
"yay": {
Source: dep.AUR,
Reason: dep.Explicit,
Version: "91.0.0-1",
SrcinfoPath: ptrString(tmpDir + "/.SRCINFO"),
AURBase: ptrString("yay"),
},
},
},
},
{
desc: "--rebuild when built and installed",
rebuildOption: parser.RebuildModeYes,
isInstalled: true,
isBuilt: true,
wantShow: []string{
"makepkg --nobuild -f -C --ignorearch",
"makepkg -f -c --noconfirm --noextract --noprepare --holdver --ignorearch",
"pacman -U --config -- /testdir/yay-91.0.0-1-x86_64.pkg.tar.zst",
"pacman -D -q --asexplicit --config -- yay",
},
wantCapture: []string{"makepkg --packagelist"},
targets: []map[string]*dep.InstallInfo{
{
"yay": {
Source: dep.AUR,
Reason: dep.Explicit,
Version: "91.0.0-1",
SrcinfoPath: ptrString(tmpDir + "/.SRCINFO"),
AURBase: ptrString("yay"),
},
},
},
},
{
desc: "--rebuild when built and installed previously as dep",
rebuildOption: parser.RebuildModeYes,
isInstalled: true,
isBuilt: true,
wantShow: []string{
"makepkg --nobuild -f -C --ignorearch",
"makepkg -f -c --noconfirm --noextract --noprepare --holdver --ignorearch",
"pacman -U --config -- /testdir/yay-91.0.0-1-x86_64.pkg.tar.zst",
"pacman -D -q --asdeps --config -- yay",
},
wantCapture: []string{"makepkg --packagelist"},
targets: []map[string]*dep.InstallInfo{
{
"yay": {
Source: dep.AUR,
Reason: dep.Dep,
Version: "91.0.0-1",
SrcinfoPath: ptrString(tmpDir + "/.SRCINFO"),
AURBase: ptrString("yay"),
},
},
},
},
}
for _, tc := range testCases {
t.Run(tc.desc, func(td *testing.T) {
tmpDir := td.TempDir()
pkgTar := tmpDir + "/yay-91.0.0-1-x86_64.pkg.tar.zst"
captureOverride := func(cmd *exec.Cmd) (stdout string, stderr string, err error) {
return pkgTar, "", nil
}
i := 0
showOverride := func(cmd *exec.Cmd) error {
i++
if i == 2 {
if !tc.isBuilt {
f, err := os.OpenFile(pkgTar, os.O_RDONLY|os.O_CREATE, 0o666)
require.NoError(td, err)
require.NoError(td, f.Close())
}
}
return nil
}
// create a mock file
if tc.isBuilt {
f, err := os.OpenFile(pkgTar, os.O_RDONLY|os.O_CREATE, 0o666)
require.NoError(td, err)
require.NoError(td, f.Close())
}
isCorrectInstalledOverride := func(string, string) bool {
return tc.isInstalled
}
mockDB := &mock.DBExecutor{IsCorrectVersionInstalledFn: isCorrectInstalledOverride}
mockRunner := &exe.MockRunner{CaptureFn: captureOverride, ShowFn: showOverride}
cmdBuilder := &exe.CmdBuilder{
MakepkgBin: makepkgBin,
SudoBin: "su",
PacmanBin: pacmanBin,
Runner: mockRunner,
SudoLoopEnabled: false,
}
cmdBuilder.Runner = mockRunner
installer := NewInstaller(mockDB, cmdBuilder, &vcs.Mock{}, parser.ModeAny,
tc.rebuildOption, false, newTestLogger())
cmdArgs := parser.MakeArguments()
cmdArgs.AddTarget("yay")
pkgBuildDirs := map[string]string{
"yay": tmpDir,
}
errI := installer.Install(context.Background(), cmdArgs, tc.targets, pkgBuildDirs, []string{}, false)
require.NoError(td, errI)
require.Len(td, mockRunner.ShowCalls, len(tc.wantShow))
require.Len(td, mockRunner.CaptureCalls, len(tc.wantCapture))
for i, call := range mockRunner.ShowCalls {
show := call.Args[0].(*exec.Cmd).String()
show = strings.ReplaceAll(show, tmpDir, "/testdir") // replace the temp dir with a static path
show = strings.ReplaceAll(show, makepkgBin, "makepkg")
show = strings.ReplaceAll(show, pacmanBin, "pacman")
// options are in a different order on different systems and on CI root user is used
assert.Subset(td, strings.Split(show, " "), strings.Split(tc.wantShow[i], " "), show)
}
for i, call := range mockRunner.CaptureCalls {
capture := call.Args[0].(*exec.Cmd).String()
capture = strings.ReplaceAll(capture, tmpDir, "/testdir") // replace the temp dir with a static path
capture = strings.ReplaceAll(capture, makepkgBin, "makepkg")
capture = strings.ReplaceAll(capture, pacmanBin, "pacman")
assert.Subset(td, strings.Split(capture, " "), strings.Split(tc.wantCapture[i], " "), capture)
}
})
}
}
func TestInstaller_InstallUpgrade(t *testing.T) {
t.Parallel()
makepkgBin := t.TempDir() + "/makepkg"
pacmanBin := t.TempDir() + "/pacman"
f, err := os.OpenFile(makepkgBin, os.O_RDONLY|os.O_CREATE, 0o755)
require.NoError(t, err)
require.NoError(t, f.Close())
f, err = os.OpenFile(pacmanBin, os.O_RDONLY|os.O_CREATE, 0o755)
require.NoError(t, err)
require.NoError(t, f.Close())
type testCase struct {
desc string
targetMode parser.TargetMode
}
tmpDir := t.TempDir()
testCases := []testCase{
{
desc: "target any",
targetMode: parser.ModeAny,
},
{
desc: "target repo",
targetMode: parser.ModeRepo,
},
{
desc: "target aur",
targetMode: parser.ModeAUR,
},
}
for _, tc := range testCases {
t.Run(tc.desc, func(td *testing.T) {
mockDB := &mock.DBExecutor{}
mockRunner := &exe.MockRunner{}
cmdBuilder := &exe.CmdBuilder{
MakepkgBin: makepkgBin,
SudoBin: "su",
PacmanBin: pacmanBin,
Runner: mockRunner,
SudoLoopEnabled: false,
}
installer := NewInstaller(mockDB, cmdBuilder, &vcs.Mock{}, tc.targetMode,
parser.RebuildModeNo, false, newTestLogger())
cmdArgs := parser.MakeArguments()
cmdArgs.AddArg("u", "upgrades") // Make sure both args are removed
targets := []map[string]*dep.InstallInfo{
{
"linux": {
Source: dep.Sync,
Reason: dep.Dep,
Version: "17.0.0-1",
SyncDBName: ptrString("core"),
},
},
}
errI := installer.Install(context.Background(), cmdArgs, targets, map[string]string{}, []string{}, false)
require.NoError(td, errI)
require.NotEmpty(td, mockRunner.ShowCalls)
// The first call is the only call being test
call := mockRunner.ShowCalls[0]
show := call.Args[0].(*exec.Cmd).String()
show = strings.ReplaceAll(show, tmpDir, "/testdir") // replace the temp dir with a static path
show = strings.ReplaceAll(show, makepkgBin, "makepkg")
show = strings.ReplaceAll(show, pacmanBin, "pacman")
if tc.targetMode == parser.ModeAUR {
assert.NotContains(td, show, "--upgrades")
assert.NotContains(td, show, "-u")
} else {
assert.Contains(td, show, "--upgrades")
assert.Contains(td, show, "-u")
}
})
}
}
func TestInstaller_KeepSrc(t *testing.T) {
t.Parallel()
makepkgBin := t.TempDir() + "/makepkg"
pacmanBin := t.TempDir() + "/pacman"
f, err := os.OpenFile(makepkgBin, os.O_RDONLY|os.O_CREATE, 0o755)
require.NoError(t, err)
require.NoError(t, f.Close())
f, err = os.OpenFile(pacmanBin, os.O_RDONLY|os.O_CREATE, 0o755)
require.NoError(t, err)
require.NoError(t, f.Close())
type testCase struct {
desc string
wantShow []string
targets []map[string]*dep.InstallInfo
}
tmpDir := t.TempDir()
testCases := []testCase{
{
desc: "--keepsrc",
wantShow: []string{
"makepkg --nobuild -f --ignorearch",
"makepkg --nobuild --noextract --ignorearch",
"pacman -U --config -- /testdir/yay-92.0.0-1-x86_64.pkg.tar.zst",
"pacman -D -q --asexplicit --config -- yay",
},
targets: []map[string]*dep.InstallInfo{
{
"yay": {
Source: dep.AUR,
Reason: dep.Explicit,
Version: "92.0.0-1",
SrcinfoPath: ptrString(tmpDir + "/.SRCINFO"),
AURBase: ptrString("yay"),
},
},
},
},
}
for _, tc := range testCases {
t.Run(tc.desc, func(td *testing.T) {
tmpDir := td.TempDir()
pkgTar := tmpDir + "/yay-92.0.0-1-x86_64.pkg.tar.zst"
captureOverride := func(cmd *exec.Cmd) (stdout string, stderr string, err error) {
return pkgTar, "", nil
}
// create a mock file
f, err := os.OpenFile(pkgTar, os.O_RDONLY|os.O_CREATE, 0o666)
require.NoError(td, err)
require.NoError(td, f.Close())
mockDB := &mock.DBExecutor{}
mockRunner := &exe.MockRunner{CaptureFn: captureOverride}
cmdBuilder := &exe.CmdBuilder{
MakepkgBin: makepkgBin,
SudoBin: "su",
PacmanBin: pacmanBin,
KeepSrc: true,
Runner: mockRunner,
SudoLoopEnabled: false,
}
installer := NewInstaller(mockDB, cmdBuilder, &vcs.Mock{}, parser.ModeAny,
parser.RebuildModeNo, false, newTestLogger())
cmdArgs := parser.MakeArguments()
cmdArgs.AddTarget("yay")
pkgBuildDirs := map[string]string{
"yay": tmpDir,
}
errI := installer.Install(context.Background(), cmdArgs, tc.targets, pkgBuildDirs, []string{}, false)
require.NoError(td, errI)
require.Len(td, mockRunner.ShowCalls, len(tc.wantShow))
for i, call := range mockRunner.ShowCalls {
show := call.Args[0].(*exec.Cmd).String()
show = strings.ReplaceAll(show, tmpDir, "/testdir") // replace the temp dir with a static path
show = strings.ReplaceAll(show, makepkgBin, "makepkg")
show = strings.ReplaceAll(show, pacmanBin, "pacman")
// options are in a different order on different systems and on CI root user is used
assert.Subset(td, strings.Split(show, " "), strings.Split(tc.wantShow[i], " "), show)
// Only assert makepkg commands don't have clean arguments
if strings.HasPrefix(show, "makepkg") {
assert.NotContains(td, show, "-c")
assert.NotContains(td, show, "-C")
}
}
})
}
}
// TestInstaller_InstallAsExplicit tests that --asexplicit flag only affects
// targeted packages, not their dependencies. This is a regression test for
// the bug where --asexplicit was ignored when reinstalling packages previously
// installed as dependencies.
func TestInstaller_InstallAsExplicit(t *testing.T) {
t.Parallel()
makepkgBin := t.TempDir() + "/makepkg"
pacmanBin := t.TempDir() + "/pacman"
f, err := os.OpenFile(makepkgBin, os.O_RDONLY|os.O_CREATE, 0o755)
require.NoError(t, err)
require.NoError(t, f.Close())
f, err = os.OpenFile(pacmanBin, os.O_RDONLY|os.O_CREATE, 0o755)
require.NoError(t, err)
require.NoError(t, f.Close())
tmpDir := t.TempDir()
type testCase struct {
desc string
cmdArgs func() *parser.Arguments
targets []map[string]*dep.InstallInfo
wantShow []string
wantCapture []string
}
testCases := []testCase{
{
desc: "reinstall dep as explicit with --asexplicit (AUR)",
cmdArgs: func() *parser.Arguments {
args := parser.MakeArguments()
args.AddArg("asexplicit")
args.AddTarget("yay")
return args
},
targets: []map[string]*dep.InstallInfo{
{
"yay": {
Source: dep.AUR,
Reason: dep.Dep,
Version: "91.0.0-1",
SrcinfoPath: ptrString(tmpDir + "/.SRCINFO"),
AURBase: ptrString("yay"),
},
},
},
wantShow: []string{
"makepkg --nobuild --ignorearch",
"makepkg --noconfirm --noextract --noprepare --holdver --ignorearch",
"pacman -U --config -- /testdir/yay-91.0.0-1-x86_64.pkg.tar.zst",
"pacman -D --asexplicit -q --config -- yay",
},
wantCapture: []string{"makepkg --packagelist"},
},
{
desc: "reinstall dep as explicit with --asexplicit (Sync)",
cmdArgs: func() *parser.Arguments {
args := parser.MakeArguments()
args.AddArg("asexplicit")
args.AddTarget("linux")
return args
},
targets: []map[string]*dep.InstallInfo{
{
"linux": {
Source: dep.Sync,
Reason: dep.Dep,
Version: "17.0.0-1",
SyncDBName: ptrString("core"),
},
},
},
wantShow: []string{
"pacman -S --config /etc/pacman.conf -- core/linux",
"pacman -D -q --asexplicit --config /etc/pacman.conf -- linux",
},
wantCapture: []string{},
},
}
for _, tc := range testCases {
t.Run(tc.desc, func(td *testing.T) {
pkgTar := tmpDir + "/yay-91.0.0-1-x86_64.pkg.tar.zst"
captureOverride := func(cmd *exec.Cmd) (stdout string, stderr string, err error) {
return pkgTar, "", nil
}
showOverride := func(cmd *exec.Cmd) error {
if strings.Contains(cmd.String(), "makepkg -f --noconfirm") && cmd.Dir == tmpDir {
f, err := os.OpenFile(pkgTar, os.O_RDONLY|os.O_CREATE, 0o666)
require.NoError(td, err)
require.NoError(td, f.Close())
}
return nil
}
isCorrectInstalledOverride := func(string, string) bool {
return false
}
mockDB := &mock.DBExecutor{IsCorrectVersionInstalledFn: isCorrectInstalledOverride}
mockRunner := &exe.MockRunner{CaptureFn: captureOverride, ShowFn: showOverride}
cmdBuilder := &exe.CmdBuilder{
MakepkgBin: makepkgBin,
SudoBin: "su",
PacmanBin: pacmanBin,
PacmanConfigPath: "/etc/pacman.conf",
Runner: mockRunner,
SudoLoopEnabled: false,
}
installer := NewInstaller(mockDB, cmdBuilder, &vcs.Mock{}, parser.ModeAny,
parser.RebuildModeNo, false, newTestLogger())
cmdArgs := tc.cmdArgs()
pkgBuildDirs := map[string]string{
"yay": tmpDir,
}
errI := installer.Install(context.Background(), cmdArgs, tc.targets, pkgBuildDirs, []string{}, false)
require.NoError(td, errI)
require.Len(td, mockRunner.ShowCalls, len(tc.wantShow))
require.Len(td, mockRunner.CaptureCalls, len(tc.wantCapture))
for i, call := range mockRunner.ShowCalls {
show := call.Args[0].(*exec.Cmd).String()
show = strings.ReplaceAll(show, tmpDir, "/testdir")
show = strings.ReplaceAll(show, makepkgBin, "makepkg")
show = strings.ReplaceAll(show, pacmanBin, "pacman")
assert.Subset(td, strings.Split(show, " "), strings.Split(tc.wantShow[i], " "), show)
}
for i, call := range mockRunner.CaptureCalls {
capture := call.Args[0].(*exec.Cmd).String()
capture = strings.ReplaceAll(capture, tmpDir, "/testdir")
capture = strings.ReplaceAll(capture, makepkgBin, "makepkg")
capture = strings.ReplaceAll(capture, pacmanBin, "pacman")
assert.Subset(td, strings.Split(capture, " "), strings.Split(tc.wantCapture[i], " "), capture)
}
})
}
}
================================================
FILE: pkg/sync/build/pkg_archive.go
================================================
package build
import (
"context"
"errors"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/leonelquinteros/gotext"
"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/vcs"
)
func installPkgArchive(ctx context.Context,
cmdBuilder exe.ICmdBuilder,
mode parser.TargetMode,
vcsStore vcs.Store,
cmdArgs *parser.Arguments,
pkgArchives []string,
noConfirm bool,
) error {
if len(pkgArchives) == 0 {
return nil
}
arguments := cmdArgs.Copy()
arguments.ClearTargets()
arguments.Op = "U"
arguments.DelArg("confirm")
arguments.DelArg("noconfirm")
arguments.DelArg("c", "clean")
arguments.DelArg("i", "install")
arguments.DelArg("q", "quiet")
arguments.DelArg("y", "refresh")
arguments.DelArg("u", "sysupgrade")
arguments.DelArg("w", "downloadonly")
arguments.DelArg("asdeps", "asdep")
arguments.DelArg("asexplicit", "asexp")
arguments.AddTarget(pkgArchives...)
if errShow := cmdBuilder.Show(cmdBuilder.BuildPacmanCmd(ctx,
arguments, mode, noConfirm)); errShow != nil {
return errShow
}
if errStore := vcsStore.Save(); errStore != nil {
fmt.Fprintln(os.Stderr, errStore)
}
return nil
}
func setInstallReason(ctx context.Context,
cmdBuilder exe.ICmdBuilder, mode parser.TargetMode,
cmdArgs *parser.Arguments, deps, exps []string,
) error {
if len(deps)+len(exps) == 0 {
return nil
}
if errDeps := asdeps(ctx, cmdBuilder, mode, cmdArgs, deps); errDeps != nil {
return errDeps
}
return asexp(ctx, cmdBuilder, mode, cmdArgs, exps)
}
func setPkgReason(ctx context.Context,
cmdBuilder exe.ICmdBuilder,
mode parser.TargetMode,
cmdArgs *parser.Arguments, pkgs []string, exp bool,
) error {
if len(pkgs) == 0 {
return nil
}
cmdArgs = cmdArgs.CopyGlobal()
if exp {
if err := cmdArgs.AddArg("q", "D", "asexplicit"); err != nil {
return err
}
} else {
if err := cmdArgs.AddArg("q", "D", "asdeps"); err != nil {
return err
}
}
for _, compositePkgName := range pkgs {
pkgSplit := strings.Split(compositePkgName, "/")
pkgName := pkgSplit[0]
if len(pkgSplit) > 1 {
pkgName = pkgSplit[1]
}
cmdArgs.AddTarget(pkgName)
}
if err := cmdBuilder.Show(cmdBuilder.BuildPacmanCmd(ctx,
cmdArgs, mode, settings.NoConfirm)); err != nil {
return &SetPkgReasonError{exp: exp}
}
return nil
}
func asdeps(ctx context.Context,
cmdBuilder exe.ICmdBuilder,
mode parser.TargetMode, cmdArgs *parser.Arguments, pkgs []string,
) error {
return setPkgReason(ctx, cmdBuilder, mode, cmdArgs, pkgs, false)
}
func asexp(ctx context.Context,
cmdBuilder exe.ICmdBuilder,
mode parser.TargetMode, cmdArgs *parser.Arguments, pkgs []string,
) error {
return setPkgReason(ctx, cmdBuilder, mode, cmdArgs, pkgs, true)
}
func parsePackageList(ctx context.Context, cmdBuilder exe.ICmdBuilder,
dir string,
) (pkgdests map[string]string, pkgVersion string, err error) {
args := []string{"--packagelist", "--ignorearch"}
stdout, stderr, err := cmdBuilder.Capture(
cmdBuilder.BuildMakepkgCmd(ctx, dir, args...))
if err != nil {
return nil, "", fmt.Errorf("%s %w", stderr, err)
}
lines := strings.Split(stdout, "\n")
pkgdests = make(map[string]string, len(lines))
for _, line := range lines {
if line == "" {
continue
}
fileName := filepath.Base(line)
split := strings.Split(fileName, "-")
if len(split) < 4 {
return nil, "", errors.New(gotext.Get("cannot find package name: %v", split))
}
// pkgname-pkgver-pkgrel-arch.pkgext
// This assumes 3 dashes after the pkgname, Will cause an error
// if the PKGEXT contains a dash. Please no one do that.
pkgName := strings.Join(split[:len(split)-3], "-")
pkgVersion = strings.Join(split[len(split)-3:len(split)-1], "-")
pkgdests[pkgName] = line
}
if len(pkgdests) == 0 {
return nil, "", &NoPkgDestsFoundError{dir}
}
return pkgdests, pkgVersion, nil
}
================================================
FILE: pkg/sync/build/pkg_archive_test.go
================================================
package build
import (
"context"
"fmt"
"os/exec"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/Jguer/yay/v12/pkg/settings/exe"
)
func TestParsePackageList(t *testing.T) {
t.Parallel()
type testCase struct {
desc string
mockStdout string
mockStderr string
mockErr error
wantPkgDests map[string]string
wantPkgVersion string
wantErr bool
wantErrText string // Optional: specific error text to check
}
testCases := []testCase{
{
desc: "Standard package",
mockStdout: "/path/to/package-1.2.3-4-x86_64.pkg.tar.zst\n",
wantPkgDests: map[string]string{
"package": "/path/to/package-1.2.3-4-x86_64.pkg.tar.zst",
},
wantPkgVersion: "1.2.3-4",
wantErr: false,
},
{
desc: "Package with dash in name",
mockStdout: "/path/to/package-name-with-dash-1.0.0-1-any.pkg.tar.gz\n",
wantPkgDests: map[string]string{
"package-name-with-dash": "/path/to/package-name-with-dash-1.0.0-1-any.pkg.tar.gz",
},
wantPkgVersion: "1.0.0-1",
wantErr: false, // This should fail with current logic but pass with regex
},
{
desc: "Multiple packages",
mockStdout: "/path/to/pkg1-1.0-1-x86_64.pkg.tar.zst\n/other/path/pkg2-2.5-3-any.pkg.tar.xz\n",
wantPkgDests: map[string]string{
"pkg1": "/path/to/pkg1-1.0-1-x86_64.pkg.tar.zst",
"pkg2": "/other/path/pkg2-2.5-3-any.pkg.tar.xz",
},
wantPkgVersion: "2.5-3", // Version of the last package processed
wantErr: false,
},
{
desc: "Empty input",
mockStdout: "",
wantErr: true, // Expect NoPkgDestsFoundError
},
{
desc: "Input with only newline",
mockStdout: "\n",
wantErr: true, // Expect NoPkgDestsFoundError
},
{
desc: "Makepkg error",
mockStderr: "makepkg failed",
mockErr: fmt.Errorf("exit status 1"),
wantErr: true,
},
{
desc: "Malformed filename (too few dashes)",
mockStdout: "/path/to/malformed-package.pkg.tar.zst\n",
wantErr: true, // Expect "cannot find package name" error
},
{
desc: "Package with epoch",
mockStdout: "/path/to/epochpkg-1:2.0.0-1-x86_64.pkg.tar.zst\n",
wantPkgDests: map[string]string{
"epochpkg": "/path/to/epochpkg-1:2.0.0-1-x86_64.pkg.tar.zst",
},
wantPkgVersion: "1:2.0.0-1",
wantErr: false, // This might fail with current logic
},
{
desc: "Package with .zst extension",
mockStdout: "/path/to/zstdpkg-3.3-1-any.pkg.tar.zst\n",
wantPkgDests: map[string]string{
"zstdpkg": "/path/to/zstdpkg-3.3-1-any.pkg.tar.zst",
},
wantPkgVersion: "3.3-1",
wantErr: false,
},
{
desc: "Package with .gz extension",
mockStdout: "/path/to/gzpkg-3.3-1-any.pkg.tar.gz\n",
wantPkgDests: map[string]string{
"gzpkg": "/path/to/gzpkg-3.3-1-any.pkg.tar.gz",
},
wantPkgVersion: "3.3-1",
wantErr: false,
},
{
desc: "Package with .xz extension",
mockStdout: "/path/to/xzpkg-3.3-1-any.pkg.tar.xz\n",
wantPkgDests: map[string]string{
"xzpkg": "/path/to/xzpkg-3.3-1-any.pkg.tar.xz",
},
wantPkgVersion: "3.3-1",
wantErr: false,
},
{
desc: "Package with .bz2 extension",
mockStdout: "/path/to/bz2pkg-3.3-1-any.pkg.tar.bz2\n",
wantPkgDests: map[string]string{
"bz2pkg": "/path/to/bz2pkg-3.3-1-any.pkg.tar.bz2",
},
wantPkgVersion: "3.3-1",
wantErr: false,
},
{
desc: "Package with .tar extension (uncompressed)",
mockStdout: "/path/to/tarpkg-3.3-1-any.pkg.tar\n",
wantPkgDests: map[string]string{
"tarpkg": "/path/to/tarpkg-3.3-1-any.pkg.tar",
},
wantPkgVersion: "3.3-1",
wantErr: false,
},
}
for _, tc := range testCases {
// capture range variable
t.Run(tc.desc, func(t *testing.T) {
t.Parallel()
mockRunner := &exe.MockRunner{
CaptureFn: func(cmd *exec.Cmd) (string, string, error) {
// Basic check to ensure the command looks right
require.Contains(t, cmd.String(), "--packagelist")
return tc.mockStdout, tc.mockStderr, tc.mockErr
},
}
cmdBuilder := &exe.CmdBuilder{Runner: mockRunner} // Simplified for this test
pkgdests, pkgVersion, err := parsePackageList(context.Background(), cmdBuilder, "/fake/dir")
if tc.wantErr {
assert.Error(t, err)
if tc.wantErrText != "" {
assert.Contains(t, err.Error(), tc.wantErrText)
}
// Check for specific error types if needed
if tc.desc == "Empty input" || tc.desc == "Input with only newline" {
assert.IsType(t, &NoPkgDestsFoundError{}, err)
}
} else {
assert.NoError(t, err)
assert.Equal(t, tc.wantPkgDests, pkgdests)
assert.Equal(t, tc.wantPkgVersion, pkgVersion)
}
})
}
}
================================================
FILE: pkg/sync/srcinfo/pgp/keys.go
================================================
package pgp
import (
"bytes"
"context"
"errors"
"os/exec"
"strings"
gosrc "github.com/Morganamilo/go-srcinfo"
"github.com/leonelquinteros/gotext"
"github.com/Jguer/yay/v12/pkg/settings/exe"
"github.com/Jguer/yay/v12/pkg/text"
)
// pgpKeySet maps a PGP key with a list of PKGBUILDs that require it.
// This is similar to stringSet, used throughout the code.
type pgpKeySet map[string][]string
func (set pgpKeySet) toSlice() []string {
slice := make([]string, 0, len(set))
for v := range set {
slice = append(slice, v)
}
return slice
}
func (set pgpKeySet) set(key, p string) {
// Using ToUpper to make sure keys with a different case will be
// considered the same.
upperKey := strings.ToUpper(key)
set[upperKey] = append(set[upperKey], p)
}
func (set pgpKeySet) get(key string) bool {
upperKey := strings.ToUpper(key)
_, exists := set[upperKey]
return exists
}
type GPGCmdBuilder interface {
exe.Runner
BuildGPGCmd(ctx context.Context, extraArgs ...string) *exec.Cmd
}
// CheckPgpKeys iterates through the keys listed in the PKGBUILDs and if needed,
// asks the user whether yay should try to import them.
func CheckPgpKeys(ctx context.Context, logger *text.Logger, pkgbuildDirsByBase map[string]string, srcinfos map[string]*gosrc.Srcinfo,
cmdBuilder GPGCmdBuilder, noConfirm bool,
) ([]string, error) {
// Let's check the keys individually, and then we can offer to import
// the problematic ones.
problematic := make(pgpKeySet)
// Mapping all the keys.
for pkg := range pkgbuildDirsByBase {
srcinfo := srcinfos[pkg]
for _, key := range srcinfo.ValidPGPKeys {
// If key already marked as problematic, indicate the current
// PKGBUILD requires it.
if problematic.get(key) {
problematic.set(key, pkg)
continue
}
if err := cmdBuilder.Show(cmdBuilder.BuildGPGCmd(ctx, "--list-keys", key)); err != nil {
problematic.set(key, pkg)
}
}
}
// No key issues!
if len(problematic) == 0 {
return []string{}, nil
}
str, err := formatKeysToImport(logger, problematic)
if err != nil {
return nil, err
}
logger.Println("\n", str)
if logger.ContinueTask(gotext.Get("Import?"), true, noConfirm) {
return problematic.toSlice(), importKeys(ctx, logger, cmdBuilder, problematic.toSlice())
}
return problematic.toSlice(), nil
}
// importKeys tries to import the list of keys specified in its argument.
func importKeys(ctx context.Context, logger *text.Logger, cmdBuilder GPGCmdBuilder, keys []string) error {
logger.OperationInfoln(gotext.Get("Importing keys with gpg..."))
if err := cmdBuilder.Show(cmdBuilder.BuildGPGCmd(ctx, append([]string{"--recv-keys"}, keys...)...)); err != nil {
return errors.New(gotext.Get("problem importing keys"))
}
return nil
}
// formatKeysToImport receives a set of keys and returns a string containing the
// question asking the user wants to import the problematic keys.
func formatKeysToImport(logger *text.Logger, keys pgpKeySet) (string, error) {
if len(keys) == 0 {
return "", errors.New(gotext.Get("no keys to import"))
}
var buffer bytes.Buffer
buffer.WriteString(logger.SprintOperationInfo(gotext.Get("PGP keys need importing:")))
for key, bases := range keys {
pkglist := ""
for _, base := range bases {
pkglist += base + " "
}
pkglist = strings.TrimRight(pkglist, " ")
buffer.WriteString("\n" + logger.SprintWarn(gotext.Get("%s, required by: %s", text.Cyan(key), text.Cyan(pkglist))))
}
return buffer.String(), nil
}
================================================
FILE: pkg/sync/srcinfo/pgp/keys_test.go
================================================
//go:build !integration
// +build !integration
package pgp
import (
"context"
"fmt"
"io"
"os"
"os/exec"
"sort"
"strings"
"testing"
gosrc "github.com/Morganamilo/go-srcinfo"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/Jguer/yay/v12/pkg/settings/exe"
"github.com/Jguer/yay/v12/pkg/text"
)
func newTestLogger() *text.Logger {
return text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), true, "test")
}
func makeSrcinfo(pkgbase string, pgpkeys ...string) *gosrc.Srcinfo {
srcinfo := gosrc.Srcinfo{}
srcinfo.Pkgbase = pkgbase
srcinfo.ValidPGPKeys = pgpkeys
return &srcinfo
}
func TestCheckPgpKeys(t *testing.T) {
gpgBin := t.TempDir() + "/gpg"
f, err := os.OpenFile(gpgBin, os.O_RDONLY|os.O_CREATE, 0o755)
require.NoError(t, err)
require.NoError(t, f.Close())
testcases := []struct {
name string
pkgs map[string]string
srcinfos map[string]*gosrc.Srcinfo
wantError bool
wantShow []string
wantCapture []string
showFn func(cmd *exec.Cmd) error
expected []string
}{
// cower: single package, one valid key not yet in the keyring.
// 487EACC08557AD082088DABA1EB2638FF56C0C53: Dave Reisner.
{
name: " one valid key not yet in the keyring",
pkgs: map[string]string{"cower": ""},
srcinfos: map[string]*gosrc.Srcinfo{"cower": makeSrcinfo("cower", "487EACC08557AD082088DABA1EB2638FF56C0C53")},
wantError: false,
wantShow: []string{
"gpg --homedir /tmp --list-keys 487EACC08557AD082088DABA1EB2638FF56C0C53",
"gpg --homedir /tmp --recv-keys 487EACC08557AD082088DABA1EB2638FF56C0C53",
},
wantCapture: []string{},
showFn: func(cmd *exec.Cmd) error {
s := cmd.String()
if strings.Contains(s, "--list-keys") {
return fmt.Errorf("key not found")
}
return nil
},
expected: []string{"487EACC08557AD082088DABA1EB2638FF56C0C53"},
},
// libc++: single package, two valid keys not yet in the keyring.
// 11E521D646982372EB577A1F8F0871F202119294: Tom Stellard.
// B6C8F98282B944E3B0D5C2530FC3042E345AD05D: Hans Wennborg.
{
name: "two valid keys not yet in the keyring",
pkgs: map[string]string{"libc++": ""},
srcinfos: map[string]*gosrc.Srcinfo{"libc++": makeSrcinfo("libc++", "11E521D646982372EB577A1F8F0871F202119294", "B6C8F98282B944E3B0D5C2530FC3042E345AD05D")},
wantError: false,
wantShow: []string{
"gpg --homedir /tmp --list-keys 11E521D646982372EB577A1F8F0871F202119294",
"gpg --homedir /tmp --list-keys B6C8F98282B944E3B0D5C2530FC3042E345AD05D",
"gpg --homedir /tmp --recv-keys 11E521D646982372EB577A1F8F0871F202119294 B6C8F98282B944E3B0D5C2530FC3042E345AD05D",
},
wantCapture: []string{},
showFn: func(cmd *exec.Cmd) error {
s := cmd.String()
if strings.Contains(s, "--list-keys") {
return fmt.Errorf("key not found")
}
return nil
},
expected: []string{"11E521D646982372EB577A1F8F0871F202119294", "B6C8F98282B944E3B0D5C2530FC3042E345AD05D"},
},
{
name: "Two dummy packages requiring the same key",
pkgs: map[string]string{"dummy-1": "", "dummy-2": ""},
srcinfos: map[string]*gosrc.Srcinfo{
"dummy-1": makeSrcinfo("dummy-1",
"ABAF11C65A2970B130ABE3C479BE3E4300411886"),
"dummy-2": makeSrcinfo("dummy-2", "ABAF11C65A2970B130ABE3C479BE3E4300411886"),
},
wantError: false,
expected: []string{"ABAF11C65A2970B130ABE3C479BE3E4300411886"},
wantCapture: []string{},
wantShow: []string{
"gpg --homedir /tmp --list-keys ABAF11C65A2970B130ABE3C479BE3E4300411886",
"gpg --homedir /tmp --recv-keys ABAF11C65A2970B130ABE3C479BE3E4300411886",
},
showFn: func(cmd *exec.Cmd) error {
s := cmd.String()
if strings.Contains(s, "--list-keys") {
return fmt.Errorf("key not found")
}
return nil
},
},
// dummy package: single package, two valid keys, one of them already
// in the keyring.
// 11E521D646982372EB577A1F8F0871F202119294: Tom Stellard.
// C52048C0C0748FEE227D47A2702353E0F7E48EDB: Thomas Dickey.
{
name: "one already in keyring",
pkgs: map[string]string{"dummy-3": ""},
srcinfos: map[string]*gosrc.Srcinfo{
"dummy-3": makeSrcinfo("dummy-3", "11E521D646982372EB577A1F8F0871F202119294", "C52048C0C0748FEE227D47A2702353E0F7E48EDB"),
},
wantError: false,
expected: []string{"C52048C0C0748FEE227D47A2702353E0F7E48EDB"},
wantCapture: []string{},
showFn: func(cmd *exec.Cmd) error {
s := cmd.String()
if strings.Contains(s, "--list-keys") &&
!strings.Contains(s, "11E521D646982372EB577A1F8F0871F202119294") {
return fmt.Errorf("key not found")
}
return nil
},
wantShow: []string{
"gpg --homedir /tmp --list-keys 11E521D646982372EB577A1F8F0871F202119294",
"gpg --homedir /tmp --list-keys C52048C0C0748FEE227D47A2702353E0F7E48EDB",
"gpg --homedir /tmp --recv-keys C52048C0C0748FEE227D47A2702353E0F7E48EDB",
},
},
// Two dummy packages with existing keys.
{
name: "two existing",
pkgs: map[string]string{"dummy-4": "", "dummy-5": ""},
srcinfos: map[string]*gosrc.Srcinfo{
"dummy-4": makeSrcinfo("dummy-4", "11E521D646982372EB577A1F8F0871F202119294"),
"dummy-5": makeSrcinfo("dummy-5", "C52048C0C0748FEE227D47A2702353E0F7E48EDB"),
},
wantError: false,
expected: []string{},
wantCapture: []string{},
showFn: func(cmd *exec.Cmd) error {
return nil
},
wantShow: []string{
"gpg --homedir /tmp --list-keys 11E521D646982372EB577A1F8F0871F202119294",
"gpg --homedir /tmp --list-keys C52048C0C0748FEE227D47A2702353E0F7E48EDB",
},
},
// Dummy package with invalid key, should fail.
{
name: "one invalid",
pkgs: map[string]string{"dummy-7": ""},
srcinfos: map[string]*gosrc.Srcinfo{"dummy-7": makeSrcinfo("dummy-7", "THIS-SHOULD-FAIL")},
wantError: true,
wantCapture: []string{},
wantShow: []string{
"gpg --homedir /tmp --list-keys THIS-SHOULD-FAIL",
"gpg --homedir /tmp --recv-keys THIS-SHOULD-FAIL",
},
showFn: func(cmd *exec.Cmd) error {
s := cmd.String()
if strings.Contains(s, "--list-keys") {
return fmt.Errorf("key not found")
}
if strings.Contains(s, "--recv-keys") {
return fmt.Errorf("invalid key")
}
return nil
},
},
// Dummy package with both an invalid an another valid key, should fail.
// A314827C4E4250A204CE6E13284FC34C8E4B1A25: Thomas Bächler.
{
name: "one invalid, one valid",
pkgs: map[string]string{"dummy-8": ""},
srcinfos: map[string]*gosrc.Srcinfo{"dummy-8": makeSrcinfo("dummy-8", "A314827C4E4250A204CE6E13284FC34C8E4B1A25", "THIS-SHOULD-FAIL")},
wantError: true,
expected: []string{},
wantCapture: []string{},
showFn: func(cmd *exec.Cmd) error {
s := cmd.String()
if strings.Contains(s, "--list-keys") {
return fmt.Errorf("key not found")
}
if strings.Contains(s, "--recv-keys") {
return fmt.Errorf("invalid key")
}
return nil
},
wantShow: []string{
"gpg --homedir /tmp --list-keys A314827C4E4250A204CE6E13284FC34C8E4B1A25",
"gpg --homedir /tmp --list-keys THIS-SHOULD-FAIL",
"gpg --homedir /tmp --recv-keys A314827C4E4250A204CE6E13284FC34C8E4B1A25 THIS-SHOULD-FAIL",
},
},
}
for _, tt := range testcases {
t.Run(tt.name, func(t *testing.T) {
mockRunner := &exe.MockRunner{
ShowFn: tt.showFn,
CaptureFn: func(cmd *exec.Cmd) (stdout string, stderr string, err error) {
return "", "", nil
},
}
cmdBuilder := exe.CmdBuilder{
GPGBin: gpgBin,
GPGFlags: []string{"--homedir /tmp"},
Runner: mockRunner,
}
problematic, err := CheckPgpKeys(context.Background(), newTestLogger(), tt.pkgs, tt.srcinfos, &cmdBuilder, true)
require.Len(t, mockRunner.ShowCalls, len(tt.wantShow))
require.Len(t, mockRunner.CaptureCalls, len(tt.wantCapture))
sort.SliceStable(mockRunner.ShowCalls, func(i, j int) bool {
return mockRunner.ShowCalls[i].Args[0].(*exec.Cmd).String() < mockRunner.ShowCalls[j].Args[0].(*exec.Cmd).String()
})
for i, call := range mockRunner.ShowCalls {
show := call.Args[0].(*exec.Cmd).String()
show = strings.ReplaceAll(show, gpgBin, "gpg")
// options are in a different order on different systems and on CI root user is used
assert.Subset(t, strings.Split(show, " "), strings.Split(tt.wantShow[i], " "), show)
}
for i, call := range mockRunner.CaptureCalls {
capture := call.Args[0].(*exec.Cmd).String()
capture = strings.ReplaceAll(capture, gpgBin, "gpg")
assert.Subset(t, strings.Split(capture, " "), strings.Split(tt.wantCapture[i], " "), capture)
}
if tt.wantError {
require.Error(t, err)
return
}
require.NoError(t, err)
assert.ElementsMatch(t, tt.expected, problematic, fmt.Sprintf("%#v", problematic))
})
}
}
================================================
FILE: pkg/sync/srcinfo/pgp/testdata/11E521D646982372EB577A1F8F0871F202119294
================================================
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
=/J5n
================================================
FILE: pkg/sync/srcinfo/pgp/testdata/487EACC08557AD082088DABA1EB2638FF56C0C53
================================================
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
v5Wb1miAZnGDGEfyuYnSAw==
=bVs6
================================================
FILE: pkg/sync/srcinfo/pgp/testdata/647F28654894E3BD457199BE38DBBDC86092693E
================================================
mQINBE58tdUBEADY5iQsoL4k8l06dNt+uP2lH8IPi14M51/tOHsW1ZNc8Iok0stH+uA8w0Lp
N97UgNhsvXFEkIK2JjLalasUTiUoIeeTshD9t+ekFBx5a9SbLCFlBrDSTwfieK2xalzomoL2
2N5ztj1XbdLWh6NRM6kKMeYvgAGo8p884WJk4pPIJK6G0wEwe9/TG6ilRSLOtxyaF9yZ+FC1
eOA1S47Ld2K25Y5GsQF5agwi7nES+9tVVBZp97kB8IOvELeiSiY0xFXi60yfwIlK6x9dfcxs
x5nCyrp2qdqQiPiMD0EJMiuA6wymoi5WXtmfCpweTB8TvW8Y8uqrwYApzmDleBDTIDP0vCY1
o9eftJcWWMkRKC9c7Ziy4nT6TzmVkNXgqC8/BuOQbpU7I/1VCMoa6e+2a8jrgy5to4dGgu6x
Q6jTxWbvgDeB6HctWGqf8f9s5lSpH8D8OZLDOXKolqnBd5YrJr0Qmpq4cCcIqwNCMbURtsTp
bW/EdWl+AKwnStXXLI5O6Hg+m4c3O8ZwbzcnAOgTJePm2Xoi71t9SbAZZx1/W7p6/57UGrXR
Q4WfiwpOPD0siF33yO2L7G7Gmm4zh8ieX8aS8guqfWFhuSsDta77F2FB9ozD9WN0Z5tJowiy
3Z1VkxvZjZH8IbcB05yBBBV47BJxrPnSuDT+w45yNTqZ6m4VYwARAQABtCZHcmVnIEtyb2Fo
LUhhcnRtYW4gPGdyZWdraEBrZXJuZWwub3JnPokCMwQQAQgAHRYhBN3Lr44BMqpUIKu4ZECB
Cxge2Og4BQJamsvEAAoJEECBCxge2Og44JsP+wZI/IBC3+CuwyZ4uXGTeQsrs5iVNa3lpshL
FIozKhq6lfAln/Vz9HVKwNRc3rY1+gdWYM2e8cPm06sUbAJDS7tOQjp5jSy2/5IO77PBgQIA
KTRWAp452ZLaGq5V0zY9f1q0WnqolGmXucbKVudEKy0/uWRn1tm0oAtRFxPnLc/D9HnAuYj0
f/ZzWtYH5DfujQvciMWESN6MgNBegvUALhP9xMRJi3GyUl7flxn1JoPJrb4pnnp7WfV8vHMP
16+hVW6KWBy99Yjj+wlJKvPVvYIqtbAVWiWRSa3hH81igdMRHNWyQkHfaXJMyLXAqpUE3f5c
FD7YvoHUtHK6V90b6apEga1Zdoe7kV2tC1mA6yYIoofZf6JimgTG/eC1m77V4WK3uzFFs4yu
yGZW4DNtsJyfAUEUCZOFY/HGc9VLpidIl6bvoKwMPU8rGeM4Mvq0ubpiApk+1ORNvt1hqOFQ
Be8f7bAaNRjGCzZRDyfITZHBLmY7EXXBI5wsKXIhRl/UiqvEReqO32kz9NZVfRgZfqRwYD7u
GzXTRUsxAbxOX7hacxim/mEVCntoF/ALl43J2wjdIkmAmKKbeE8V0pHBMzGvI6E0ngHBrFMI
B1RhoFDHmAXN4U26Yun5ieYZKToGhCK7R8Xr+P/6t8w3eY+pkLmJ/6b960RaTslYMcw3KZNH
iQI3BBMBCAAhFiEE4inmFHgL1AZaVdBZ/Qv9aFby0/QFAlpNpJYDBQF4AAoJEP0L/WhW8tP0
w+QP/RWuNROIOHVGxUnNCBO2EX5W0VDpGWVbMQYpPp8TgoT+igv/9UJIZj/CN5hLvcYyhQgO
3UD8KqN8WmUdNJHokH9+z2bR2HkCnLjkK4Mef7GZYlO5HSv19LczHHKocmybxuwTK6Txe2Ey
nkKQw7oegx4YQWfagGgH2FXM3uayNCnGRI/jyp8GpwV7aFciXtJriIdArP/ZO+pn0dvSYGBy
4C5aWpxQCTaIX4W6vLeVneY2Cv1OURRV2ppb10N7dZZhO40cfjs+QjRx1fGR6dM3O+X9G6jV
Y/LWXVFOUTYmKys0WnGANWYUHwkhMd6kM6tZ1SRH97F+2TsQswEm9ZInR/naT5dyJgEN6sFv
zTsrb7v0jX69r3MGk/XAXXlII4/3GSUcWJ7bvrf13WIgdrUvonyFQ6shWecLmy0NBLTgn5Oa
AuDJhsgAWQF1OihOXFse1syYDruOX5Mod86qmv4c0RasRRSKx89RjRwzLQUIJu5sF1cE08fX
0xdzigZ0bj2n2zmeO9zy006bozHbmxdp0RPFGvczjfP806Ns13hYDtiaR0t3VoYmX0si6O4t
ydxbripq/CwW7T/NkyhogCQ0S7KjhkBkSc1QpHZ1F1l031fB32uUM2wVOODsWweqQ/LlMc4D
0uz0YeDuUgIBMkeoxFxsGyJL/Nw9v8ZPCuu8okFGiQJOBBMBCAA4FiEEZH8oZUiU471FcZm+
ONu9yGCSaT4FAloe80ACGwMFCwkIBwIGFQgJCgsCBBYCAwECHgECF4AACgkQONu9yGCSaT5M
sRAAnVTH/qf/9/aMRUF1ZvXepgUEHEokw15NT3I8x51AACGu3rwhTUG3z7+eZkubWJdY689D
TX3bWLccHEA2dtOoRhhhadAknJE6LJO7BjsdaWC5BeWTEtrWZqFW4V57oMc747wgZTwgz48d
0QvTx7rb2wiW67dYaY1dcnfTuPjtQG7dAUDrthDtgGYKGXsAcFxKkpKmWA3za1sBi2ubnLBg
czm4wWGZvD8MmnMt0WyfNFP0Ts8qEf6rUzI+iMZPg7K2PcPVBmuGnzJn5mnxNCeV5d4FzMt+
lA927O6upWiZmlbAhkscKAElFabPv8zSRMMid1CV70tVwm0mpqytGaAvc2wqthgHD6MecF7a
GnxqrpbcNVpSaRJ2yGtqf1kJosKnG3rY0CBSQj96DWxKWOjVp5eYXffKZginFq9bkIZRgqWp
hEtRPt6fq3+YWJgLA8PbYo50tJ+y9Ub6mvhQ2x9Jl0gX5b38UtbsclVkYwiV2ysYW/iU62qJ
d1tFp8y3KVjD+ZT7GG7342yVFGtuLVOVZL8EftQti73MpqECl/UMVFyvHQ/K+2UWV6gNlrzy
4huZxQ7wbNlcSF6NO/282duaOC9ixHLAVwk+Qn9M5t9qy+9JT0ZmfppTfpjsZhJQaMPVqcuJ
SmvQUqSu7KaoF1cdpcYnjgZnweF3lkxNq/ARfsG0L0dyZWcgS3JvYWgtSGFydG1hbiA8Z3Jl
Z2toQGxpbnV4Zm91bmRhdGlvbi5vcmc+iQIzBBABCAAdFiEE3cuvjgEyqlQgq7hkQIELGB7Y
6DgFAlqay7YACgkQQIELGB7Y6DjJGA/5AfnvTkXp0YoCgZL8JaRQ1oSW2LCkegxOyMVqwjz+
W4OvKYWjxwktMrlZJab0N6WTU4AfBIp52U00kff60VCRyn+XTGGCMsT7S8E7OzVJ+keUza3+
ylnHrXKzwYz8iYPeunJ2tR6rigrc8NVsJcOQlMICj/xxWU1q88JcEg4IxTlzC9jt3hbomVUn
FxdWLOjB9Fo1wxBfFnk3bQf7cHoeBbTzBkZx6LlDzfvpjXmN+nHqDDIywa1wxDB9juMEtcNK
1hxoT6fSqkjoft+Q5Wo1fpqUKLkiVd7g7UKo4+wqkAtUpYVH7w12K1YWvYQ1wVqfAzMvRb5m
ASUWAc2DGHpQc+rCqDEFf7Dgwckr1sYP23mPELadapeIi66aoFD04b+yIjcBiwtbU3LhebOa
3FRPIGoHkhm9iiHmWrJuDV7bm//amBdID4jCLAHxkIWqg0+fefT5ldM3iyzdPQTUfcSH8UJ8
F2KsrYPFT0jrqXXJ4Tgot+NIUkodP8AlimNCkmHPC1ynBV4dWiB8YQ67Y+ILM7fpd/zD5MRj
u+9uYJo6A42x2O0E73PmWDcRSiXrpEuWvJuN96WX0y9Ax1HdFE5fbluboANEuh3J14CeK80X
3wrqV0iuYT8AitnhHWUP21TPlR2z6Qrj5krTvaFhK5rWuCjynYkmtvdOKPnUobwTuleJAjcE
EwEIACEWIQTiKeYUeAvUBlpV0Fn9C/1oVvLT9AUCWk2khQMFAXgACgkQ/Qv9aFby0/TxRg/+
PxcwPccQJB7T8P3dYJWxVwfiLTbC7Qb7TcflRzwq07pnQwxXlTmIx0+GTgqbbQNkjhRuERFq
jSXL0yPp9AUSE0vwEiq20VRkw9wPDJ8cuB9+GCPCfkNW3purcCbzUcxlt3FEIHG3VDXq1AOs
nJjDQpqUvKvGK9fLT8UglXjFfCxDtyf4LT+gbt9VCYnPMD5/tj7L5EJd7GdnyfIlVTgb9TM2
f0N3RjWbmGFS4JQ+TuX7qN1XkhFxG+S9d1vXThg/hTZZKIp8vIDpxhioUi9rNyU7kdXfrb+s
kkA/o3S0rlRPb/riqfpQpiqOBzLlHDYmSB5hG38etegiecJfXMXUCV4Rj9bq+PPmqd0SAhat
uevLCLTX45ftZIBNj2iAno/RoPrn49YOZvpG9OXVG+VdOOM0zW/X10CB2rnLoUIArEsSZr7h
4nUPGT3xSk2oKzqcQIz1+2eCiRGf9eCvvOepjp5QZyU+DH5vrC0kpvdQOfO9da0zWTBQt4oL
JnsKrJFti3Y9413fEv1fbrBEdVvPBjpVAkubCkLWYCk2stCx3PpYs4Z4iT5K+DSDp/19uFPy
V+vNH5FtiyJK0/yKLrM8OBdizwBxmRWgD+uxzsm3CVzLuaNaw1gyuVOjSCG77ZT1qs3UTTJ/
+DVTwDblCVBsWd6foaemc3EmnDfOhoiC0qaJAk4EEwEIADgWIQRkfyhlSJTjvUVxmb44273I
YJJpPgUCWh70EQIbAwULCQgHAgYVCAkKCwIEFgIDAQIeAQIXgAAKCRA4273IYJJpPt//D/oC
fOX2A816MuO8vdBRYjyaqrZGYX/1TzD+WDic8QpxlYdAlMAtnxhVekiFOVe/BHXvoAK6HGKv
i6U1eJyc+UiWWcTQc7hFvLeNo7hehmfbgeUjZamB1I1mzNGy6ZdYLrd6Xvcie5kNg2mVDGJW
rw+7DgIisP+aQshS72PWumSZqfEJj1FPBW/ZG27ncX48YA6wMLlZZxlPyPGCGTh4V7NYYwTW
oGQlnMWsJLQyz8pYnzTi+VLTbqCyUY7FFWrQBI6foNV6tgPP7f5n4uStaY7kGhK+X1c6oZD6
re1EG09ULmYIUrWCeUEbp2mE9ba7wV5q1ys3mwSpPOfp5YefIH0TU8hLdnn5TZFNV6RbTXAl
qOq5Hvz2Y7R29Qorxw/xocqIHhgX0QDEX5nzQ2UWcHuVfhATSPkVHsbHL4Vck6dF4cYF3Rsv
SqUqVm7sBmkPqvZlS62EZTZ9ZN3Z2GlsQ55NMe8RghOfXXer6XJHA+rf0l7o0cE+G9GKcNY8
t1Xt4fDkwebD+UgaQgN9w6ELPTjW4LS951910k+3618QZHwg+w+fIBgrxb2WABHdTAxo99ud
FJwR1Dxnlae//rf8BzCfxN672+OURmGnXe9+otL5yUrTyGnuDP21RuanXUY1OpnKYq+amBwC
Kw8+kMFlhdgb0e76mCmt+kxl+0zkcbsn6LRNR3JlZyBLcm9haC1IYXJ0bWFuIChMaW51eCBr
ZXJuZWwgc3RhYmxlIHJlbGVhc2Ugc2lnbmluZyBrZXkpIDxncmVnQGtyb2FoLmNvbT6IRgQQ
EQIABgUCTny39AAKCRAxR9QN2y37KbHPAKCXidCvfpY77MCdn1Nn0DDrdxSwQACgp1wFBPe/
JoR/ez/sqzy9WaikldeIRgQQEQIABgUCTozPlgAKCRClpOmccR07YTfLAKCBUZ0sJJZ3LMWq
/DAkKM0pHw/QBwCdHQ0WcJ2bhjFNLKp+XuOijICPxl6IRgQQEQIABgUCTpTG7wAKCRCnVqmV
Dgj2ZZ96AJ0VWRDwSbluJ6zNWhujfRrnBtFCvACeOeElpsEZnTvz9+9eSGFknX6uJxCIRgQQ
EQIABgUCTqcxHAAKCRD8TeTJZT12J5ncAJ0abQP1y4/EwbQdGcsp1/WbT3CA6wCfSZ3K9nGt
Jjkr/KtAmFj8bCM/MnWIRgQQEQIABgUCTqdh0wAKCRC8YirTDnq97MD0AJ0YmUxiPN5TYdSK
G5q+pzVAIF2gKgCdHPze13iabPag+4FMaHQjXBXc9cSIRgQQEQIABgUCTq/k5QAKCRBnpz+U
b+7arK1EAKCXB/iLgAz4OwS848wDq0ePKF51iQCfd1ij2HXhcno6n8QgiEV3uwS4lOyIRgQQ
EQIABgUCTt2MKwAKCRDtOjnjk2dMQMReAJwNA+20QJj+uTqy1WNRadobk//cBgCdGcgzja2Q
4PNV9AXejHnVNcFjs26IRgQQEQIABgUCUD6nKwAKCRDX/r5woJ+1V8jlAKCr0QiJdHGwIQ/x
O8X7iz3LH53KkQCfS3LmlLPuQN96cRp9GUC9Tyia9ECIRgQQEQIABgUCUvUt+QAKCRA7b4rx
Q8IfO82NAKDyFFUorMOWpTX9MvDx2Z345KRvPgCfURh7L6uBTe7pjDaV4UmlxMQs63OIRgQQ
EQIABgUCVJYlUwAKCRDf1F5chthrXk5pAJ9zbjinNmx9WkdM8TOxrW6nTuk+ugCfa8T75nr2
LRyTgYrRGxyDweeOlFWIRgQQEQgABgUCTqbjXgAKCRDv1k0JEgZiB5xiAJ0cQShx0xeYBCu6
CuZJQVld3ed/mwCgo5np15shFJPNKX/z5CcDZT6b0HGIowQRFgoASxYhBOU7YErdNopTm7nr
M6oU6WIA9eAGBQJZumHdLRpodHRwOi8vZm94Y3BwLmR1Y2tkbnMub3JnL3BncC1rZXktcG9s
aWN5LnR4dAAKCRCqFOliAPXgBgnGAQDiMbZ3q0mECu80G3U/ZorAn5SVbHl/8HKviXjzRs4h
wAEAn/y3EFr448WlBMt9OCGEo2jPt9eRTz+xglg8KighVQyJARwEEAECAAYFAk58xHMACgkQ
yDsHRd8Yjf5GvAgAgSS4TkAdoOVu0mhvgt3JWRzkQQKD1VPLCX5ukpOiplPFkOLbl5MODIqX
P8CkXKR/iN4bYu9q7GMjolFSunS+hhtSOhLYGO76bxTeJIZ801MMlWjAe9eeOeiGfUD5yi/f
81jo3ZpLbWQWMkwrWR3qBaYqRqr07aHw5wJ2l50FtdE/bn+Jutsn12yC3AB4NXoB9/JHBPX0
7dWVuVoi7z02drQNtSUUL/DmqdONkv2yCm6NFyhb06qISu0YbWqHbjUiN0W8fPsLWngafKub
rZIt49cwjxNd77NARgEOf75Y7EggAKPReoMesqRKAZH+KrhKjxiXOummkiIITWkNy6ZLbokB
HAQQAQIABgUCTqcTWQAKCRB5vj5DAEEYhrqAB/9VhdSgG9BK/YBwJo2wVS6wInPSdtyAI+Wd
K35MhtoJV6ZVL0GYxItAXY+qjD7SxHzugPATzy5EZpDtu44xrx1T9H8die/djCQPoVkOVQZF
T/1rUFP6xuQKFIe+TaYBW8dL7hpzTl06ArOCf34Ne2yTN8YiGYDN+wZuUaiEAq4XqM0LA/fu
4WCN4NKGcksKi1yXyPy9+5lBQ9PQHU4jgEbAKw1jiOEmO2fynDEVv8XN/MoiZcUjzLHHjuFI
saAi3fVZAKpRZyS7jmwMe1G0v73QgYeR/DEcQjM+Fk7QMzOijA9vhWUjf0fJtfEhYDH8L6LG
DvLHEdEqn2Ij0lCWfIftiQEcBBABAgAGBQJOqOY/AAoJEMCt//qx+xwYwL0H/idzEKAM0Lg5
05zJ87pYQUfMXVHx5N36gVdzQYB3H/fE/09NZZF9Tchrq841xRT/A/NtLLwmFQhK0IqJZDBn
nCFxNUqHpiDevhkll7aUIrc6NYN/6UtZPeIWre/RrkCVFqILq6YFxd4BgwbWKdKDH0CgoDsk
sdtApZyq21ewobDJb2KXiidfVWqqim8PhHBzyWUbq7RVyjWLrkpkf6bToaj6UURdmKZ7JFbl
7o4ZPqW33mruWCjb6uluYQBqKxJluNpA8BMhNqbiIP9M8rPBCK1OgYWdGDf+kFnvgEVMn6Zv
9nsv3RQ9uBSCCdPaXEWhEBA5w3NmE161BKFhOldP5iWJARwEEAECAAYFAk6pilgACgkQQd1L
GHgNWcSv0wgA2eeITSpZlbY6PUTYEy+hMCGPeJgSx55eVVyP8PNw27M2APj5c+k+6rSbWFRs
BntHYaWfs4LVtJUIoXqHBGpaAh11JPVS8oSx92rtFFauJb7Ze1f1fRSkknJilma+uf2226wr
DMLqxO0lvQwBheF0J5b20n9zn1LmxBAx/pTVEEv9xl+SFeNltNBwm5dRfB8pAUduhsJ3fCw6
22svRYyhjdfBJh5NdlT4jqjOc7Ct0CmkXBk15DJ0r2ywZ4L/IIE399Q6yZ/3gUfeW3LSGnWp
pD5+U+k8KlFCR8LAM9PTOd0OvLedytAMwmmKr80LplfAfvMfGyFx9W4oeHT0zNedLokBHAQQ
AQIABgUCTqo/FwAKCRAXKAqXgRhqz5GaCACQ2k63ZB2+7/VzgCsoxaXJEpoYTsswoautHToy
wrTITMaUbV+wufUHMHwcmYJ9fel06XtE5ElYICZ0T6zjW01GkDTK0/gE8XtAVbpK6niVCV12
Hz4INKnA+y98VLK2AOgxXOsi0LOjZ9onRcZGr+yIqwPCySUJGRhWKiA2Tlpz014iMo/+zshh
8tJKFquUc9lqAsXfb7zIpnbrHLCjyLTcuhKAMfhAWSMdUzV7fMYAGJIMx0lUVxzzUoeYIWhF
79eZyh7gsaIBFKQCyeqre3dH6YynykeCwj+yGDIZma+nUt2EkceNvmiNDttLrs/pDBpG39jf
3MyCJay/A0DsDCG8iQEcBBABAgAGBQJPB5lPAAoJEA7GeOb5igF2sW4H/3/rPV2SpfJ6SVIV
nsWeH5BqO/ZhOd50R/rwpZk8NSNZ4EIYcdrVpCqHT9rvPeCiUvuL8FcxhOHsMXXaNiv7xRiO
D2TBAl7MrfJj4rWykQdkOwyQgPM41G6qK2aHqgzRfGDmniO5CDhlO68cQppX/n2IEnmv+1dR
4bfOd1k7Evg99ftM3kua7HB7x/GjV9snCZ33BhfOa0MYVXEZiaEqHy6ZmOmGrNcqxy1MZgSL
qfKRqhqhuGECA2sxN9l12YUtnhvOknhUvtTBeeMxzTEf/ibW/Me3FfoDfHmzLDj6xu4Ik5+o
rrOl5y12Hv386oQqKRYZ4fRChHedFm1Ry4jemvqJARwEEAECAAYFAlDLckEACgkQhi6rXkhK
zHnVpAf+L6OjFCSZ5q087tPq5jy3hxfj552c/2/yqxoTT6Jy/8Gdd8sf2Po+ACHiT6b/UlFz
HOZ5Jqv6x3HAq6/aTGVZwFYp6J7sDIjbvt6tCvWiBu3SmSbf2o3rPY4KXAUsPZl2l9+Et9TQ
+GkYAOCxoU/WlkIrSMWcIkgiQsD6zL3aJHrp4qxRP5ZR7pUmxvgHYNjEtQRGODyyW8wFkml7
mGbDM2MM1Sjil4LUpYE4wq2bKchG4YN4ridjwbZM7hc28QOYmXtlyu/+b7dKQ2hHtRNqZR70
NKtTKFU80H/CpHQ8wG1FtRARWnIMZNyV79ruw7/4U6HxEVQZFCw97dUtKRtwI4kBHAQQAQIA
BgUCUXMX0QAKCRDxmgnYXrAQ89LlB/0bsbnUDD+TKOROPCEPfp+u3iS6RhtwIMOq4608BkNU
scgVus2ovnIo1iJ7nvh7cQ/GqKfK19nxfc+sSsBBrnyJ5oteYb3iLixb2+fl6JF288JAEmV4
OCBWrLREzC4KMyMeAMvqOEhxN7FR/zUjBmLNLl44Rr7oSlAAMx4D86Gr+9iPWNt2qSTGsg/M
cRP96BeNS4aWeBR10u7DMrqbHUssQQjeRxakG9IGMNn5bI3R3H/Xdmn/395U9VnTz52clq5g
jH0nGBna/QLOoaAiVbgld83IZIrrVQUM+srQj0Z87Lu1PyDdLvhNquaG6u6ExAb3lP74BGsO
/zrypNqUjUcPiQEcBBABAgAGBQJSDoOWAAoJELdz64Laus2ov94H/RuJzlpq5LSYsGp42g0b
3EW9PIJWurC+jIGsIjMnD0qxYH5MJuY0eX2PKXL8gnG8LWmWnC3jN2o3j++6V+u6sciygBSO
myR43JZiHCQ0roEPACgqrOckUJRubazdivoDOLDe+U1L5tWippOmCTJSkclizY9oEta1nRjF
29iFAIo46fc5wYo3MthQKR+xccYX+J/cHrpyTF9FqyY7t1PMqeszhrWyQMPCrz15QhVQiZV4
917ZdlYYXaxJ4LRk2/FESxGh18bhmI8N+mA5nIAXEmurn1mC5nVWwCeG1HOmtX4llAvR2Dkw
5HdDpRX0V75NADeLJpyQshsgFpacvHTVfl2JARwEEAECAAYFAlI3FZcACgkQivfzbXgFA8z9
DAgAl9AYkIf1xWUpzTF0lOTUsC59UWL4dk6qJmGCBA0Pnbu8ePyjk5jOILTEs0def6kvgtl1
dmrtm4KPQ655bbNvvBkCcZpjl1CRTcN9kkiMyAjM66I6iXbosommVovEPFa32MnP2Z7y8POU
l8WrezT+iHjCMcACKoWl8ob+rAE/9DFkHMV3Zsb9V7TSwXvGg9Bi5imc/Lho1tA5x5tnO85m
16QrPdO3WGTKJGpE8j/4QVXr2kE5cBXbAnT03Hd2h5/HwijX0PtsMA8FQodqi3KiHXQC4kxX
a6OCD4pY2OwjsAt0geCzu2xWZHx4+qVAKiLUsT0CY8q02t/ebEpxL05ycIkBHAQQAQIABgUC
VX+2BAAKCRDq0lnEECISpAfYB/9NtLTc+Uxeo59xWkV/Jmp3T3EXdlLsvAUBE2J0r9ODp4tl
vSd+NSKCXYsSv/ahI9IQmf2ql/c3RTnc7gdKCuHRrbyLNy4tDaiRkVxgmr/bkxzhdOb5rusW
jeBfnXeZPOmCqJ/8U8l/imjVK+1xVwC5tyYx9qo3zfy3uQ9lQAikMjTWlSDE/ZDZ5tKyzq/D
trlLBgDlM33sdC51HESD4osZ73GS6jTXPRj8CGCPcupCb+ESUluOAIp2JtEAtkfnGZiqG4Fc
WpaXMg7YyDTqH9zOrBgVr6f+TpJmnVj0kxIwD6fHgKrhjtSSg3viwCsrdcYS6jAk/HPZ3j55
9mTw6kqSiQEcBBABAgAGBQJYMvcOAAoJEMeAwkXlEt8Clc0H/0Gwq4xV1fU7JgQAtMH9uOFL
qG7omWxHJWnMaOEwCazvUsOWme0A7PaV7U2irtG3aklR77nfDAP38QSLytVMzP8FJ1ngxe67
julSqmuFFRkIShQ+jdecfz3kJ7S6gW65aszuGcv7tG+OtWlRl7OovcrXbkKaQhlrvvhde19R
yl27l+ozagY78367ElPDl4rHuMReShHLV3M7dAWRsBgcV2HOtj96TAz7fHDH2/J7Lk5ZMVpU
H1fbBdwtGMf3xjZPMzDtwWy8zMX6b14ATCdSkzMYQYIG7Gh5hA3sotYV6BhUMNC8l9e3mQXW
ODV6YXaP/AeS0i+gVe7o9M6OA3EXQj6JARwEEAEIAAYFAlQ5/XsACgkQBMshPTVuyaoi8Af+
MDWlVElyK1haJx5QfhpxksXN4f8QFaCfIWVdjlatO0/6pOyHKcAP7Cw0rF31jHOuU88FbUkN
Gjjl3hgQkqeGJUk7LLEMSAjA3uhuvH7tGzGpzCVt8OOXNnGDQQf70COaCR9ig+GWUfaTGcAK
zCZ/c0BQ7WV5nKZ/nksKcpDnJLFpI6GiTkail2wHkhyl60ioBcr1GWgbNuEo5q8UBjmZjkbD
xIzVDG7ApGt/BWDIMBPXHKFm6JSGEYr84EByxAavOfiPqCgRUVz/Uxc8vQS5653VXguGtS70
llqjQ9nUXp7qq1WwkhOZSL31aEVJfn1HPHsuLHKQf4ct6MoP39E4UokBHAQQAQgABgUCVxCS
nwAKCRB+oORAvdZuzPlPCACm+3PXMkqPipWReHPWcn+CGpdj0eXDB6r857uRT4/OpsezR/bU
6azHiekkcgzFbNYp/zONAqnyEcB66CqB2O+o/FGf+lf5G4C2PJwFyR3bDRXxKFs+CezoPYX+
Q0cs1rI5H8LAr7GQLFELf76rAqYcnXdfU4Od0i9u1ADnVxbKA3/t83mtwbo6PfYEqSZgR/tJ
Lwb1spoVQSi45IMgK3yHNuv02aT35O7x4JhDX8hNm30fMcoD//hsPGZQSTHAseoVpKC/xMZV
nGa75KHY8vBuijpy/LZBDPzMjn+K7YrMMFG5mYjetYBgN76PFhKYWnOzjAgthbyrVtkNR2Ya
oqM5iQEcBBABCgAGBQJR4k1nAAoJEK2XNk/CC+yAHecH/iwdl8joZn+58HbnPlmrJy/FHlwT
3AG40nvhhVcDVQZeaWkBvQSTx0ogb2W9mFJSr7rOtekaXcuYf7By4b6Ubu4CEvX4OhcK/Q5d
geJ3+gobBvvnOYpBXZHAxOJj+xvIQMPArb/LImaw1X2/9zfoZBJVczrN7laq9KC1nS4baBU2
DSzBFgshiP14JJ6XXowq6dU4Ok6l0PKpCUsJdatiW19rj8pKxe/Bvl/5JMxxB/yKmIPkC1J1
x0SQcaszjueE1EJlzjCNRWySh5xQzRiQEM/PrkDLGWfSM7xYL41SLQWtTYP+7cEVuEEUk5BK
Z2bsb1VTOUo3AZT50rIiisvVIUeJARwEEAEKAAYFAlf3WgAACgkQRACtkVJNXIqKGwf9EqZs
RNnBCBC11AM9UGkrAyzumYB76KyHiuIlVjudo58NUQ7oN0tNdaj5Blv7atM9a18mpupWJXIO
EAhAY7o97d/Rk/1HSxbZhO3bL0aO4Be6QZ35qfIWor+9icgFLmAnEyIX+wsO3fTF/WAvmcpb
iUdfIn40r7cwtD8tLCfe11WqXKl/MlgF88hr2VIdmMdM3SRqH6FLz0XwSryFIGROiAssx6Lq
LqIZkHL0fXzK7/Cmb5pYfb7SWuS5fVaEskGEwyF0ZtLncMEJjhELRWw2K8/HsSL2oyjApQ4f
v680Dbp9XWGFVbx2yRuSBVqo7VT9idHOsf2vZtBObpI0B8/kBYkBHAQSAQIABgUCUqHV2QAK
CRCiw+2B7ro/CSG1B/9CEZgcT1wZlmBnbXiM7AC+lYgHvJ66+TiJb1+ye0NiVBXC1ZbuCarO
FsVO1gjPZC7T5jbWCK/4uISPVqj5pQNjJokMYzoMKrWYHREjqsIwnXlUSBl46+PhL7IMaqpE
nAVGE6+P6y6vri3VpKCbdBF8eWvnCynM1Gd0daxRIA8GMyfkHm1aicGtQ6UJLYSs5glva2G4
fsE1eywrWR8TnBt0xjpdEA4bPDV2lG5GNdqu0VzP2UO0Arm3HbkEUYaG4/U73TClrS319k43
qS/kf4c+pWUra7QCuW6G+EpP3StUUglJwVqO7pHU2vCCduR/q680KEIZC0Uox9XCSP5z14ob
iQEcBBMBCAAGBQJRgPWOAAoJEJr7e4yaX1u8OFsH/i650aOJ4MjU7QK4VEZAzBrKMTXOmd1s
Qa0KHkBygUWrfxFav8zwBpQfKC0E2+iotD3R7vkaB4cqR7udkcLsYp4V6evDHt/oBsH/4Aka
ylju3Ik/Ys+4MroytcGbNTX5Vs2YaGsjOwHKwzmtojlk7xgNi9L5IsI+eZobWBgoKKcHRBPh
W/vQNlxlBu4WjroQChABZ99hO5Q4iCJJzADSkuQlLfx/YkrCOjTLX3JliVDpFqNVDQLBZm3T
IXFtC0WFX+uD3AoU/q2mdz8wnhqqM6rnDGgyNNhaa9NEY0js6Y0Feuh3LOfBCTHmo217iWWM
yftGqIj6Rat4hXI0hc6Jyk+JASAEEAECAAoFAk6GAmIDBQF4AAoJEIFK5HwhSFTWhikH+wc0
DD45Z0j/E2AmfKcdlG3oD5ngaOFTvUkfblE40W3FrQSkpHlSY7rH67kFgddsTE1N6w6xRf/s
6ub6aTgVpNEp0LucBrXwu+4PecmNiCGvzVQBQYCbLxHOLtLB1haI3dX3I8Uce94FfQ4D/AZl
aoUO2Aa6r9OsvbUre9lnTjStG8PmoU7gots0pB+RxuaK1ftPOZ6fWSgeZoj7gpuAA8fkyVmp
gnXpeARl63/o9LF6FQLT99wI/uka449eSRl/J8ypuWP8+yECbQEmw1iZMKneEkkwKMAcgHTW
WE49kU7df23TtDSQcy3EpGIDKNOj5MfrCEs3nCrzYEOfQYm8YnmJASAEEAECAAoFAldcm4sD
BQJ4AAoJEPBLY3YIel7BhNkH+gLlu7f7VOpb3sB7bWpUNZt/unlJd78SXWcGxjSadtDIH+rc
pLR7kZ2nYxGatMZfhpAUFhns+z+C+bxJij7Ed1yqn8lEUPiV1xm5jLBtRdm/ChujpAKcgvnu
dX0n++1vwjU17woJg7xwNctLx1yJImlcUUs8YlhR7Puq+9yKZNvs+KACLF7bPOb1I55qSYdK
WSoNY6t5xbwaIi54YuywmFoXZy/n36E4fSqezoL3dLvA7HpuzBL+XnCjODpxwNbgjNmakEwZ
P2rX5BkWOOJKK7wZFymglg+54M4KdhgL0Sv1gJ86GXWlpgrb9OFRdPqGfUX36pGeo7rHIBrX
rDgfMYWJASIEEAECAAwFAlIT4fkFAwASdQAACgkQlxC4m8pXrXzepgf/ZcCTP9OR2m3gIPOn
65efZPUoXdhsOWuJBIsNweL9kUqfPN+lFaaBpAsuq3zfit4qMfrm6xyjzTn0TQutxSmz36NN
vdAmpu2H6o+LX/7qQD2uv9fE0xajDxJfrwITMx0V4eXB+Pjd1Do7mta7F49Rv98Rqc9WXcL4
Hrl/pGSZCQbfsLvm/y+QaDsAbjSqTJ3JIfdIbuIYd5HjGBUm27g5XuGpg7pPI40oNIFX6F9Z
FCZoNLt/5lFmR7EO6yqU1B7Zo+eHRdiUj674apSTOtcmdaZ7ec9q/bG3IvGYteJNzg4jz078
OawnjGzqGaEDl6kFLGGtihN3Vb4WwaOuO+nfHIkBIgQQAQIADAUCUlmgegUDABJ1AAAKCRCX
ELibyletfI0fCACPAGGCVcL5tO88aSPfLQK7oMLtJtI6/zDqnHF9KEhHAN2X4sJQ/NJd7EnO
kowCciUwMXr0K8+vdKzM9S1lVgm2Vb+W1i9J04pXU8LlcnaAevPFIagUHZhAEQS3R2gUBwlb
Lydj48tX5iXTxoT6FDnFPGGpBPSJsj+HMERiCAZIRaX3x/w7sAIrtoSxQEI4y2O6Nq/kvqG7
4bcEncDipUm6EjBzmFhlnPZuPisQlyHG0X5K2C0Qj1LKE3X2VgABdYQ6rdaZvOiBNm97m44C
Fj2xLUPyicP/6mXZYtE1krduRhJfYAl3gCw9brvECcyXz1eCrTrxw3qnyBtl4OAAXiJ4iQEi
BBABAgAMBQJSa22YBQMAEnUAAAoJEJcQuJvKV6184VIH/RoLWxlsclVGtCxiC6MTDBOTqVlY
ps5o739kWNpCk/QAzwV+f5oD3AZSX+t6XaAWDb440Wmj9ZmUMpPFttseTHDCt096sjVsEBB4
RbLdMBb+9/oxQtMJ7dF7RxDx2KayrynfHxJgzv8O8HCheETiu8ix66UNDhnaoO9j0RxljLGh
GixvzNtbs12m9H5d3xrn5FBKkWtQRJ/Y5j+/+LTd0kDm47NtZ/CzLYpRM860baDdJHRCsS7j
93jEzwzy1LkM4bNjpqeQcXJvPTaFJI7MtmXoGkG+9IYPN7FrX/khedqXLtg5UXT8dssmj2cD
y4YiSrTeDvhpCrC3BYHqOTCsCf6JASIEEAECAAwFAlJ8o4UFAwASdQAACgkQlxC4m8pXrXw+
zggApkKv1WMwm2DOKBsb2IQGB3TtDiaQo/5oazJryTQ9G0XmmayM+EdkyZdlcseGrVbUQEdZ
bKfU8Pk4ECLygMfFiLWHb4Ch/6NzA+mAaoq4hfeDyu1p9WrZKFVeyEsa9aefvNdm94jJGLVf
TLFnUCp5UIQb5tUUpVTfhO3qOW+5MoSlvsCXMg/SUu4q6q7oSRv1qwih6YTZ09H5X2UqZBI2
RvUWaAgge4tHBvlL5S6b4o6O1T31grdkYrNjv575+JmX91I/p/2dXxpF8ShnqlaxldVrO4/2
y4p4nIU5hxQDo/NO7BQYkG2Rzc95aNud7GyFfpzsphvV9rTX6GNqy5PXAYkBIgQQAQIADAUC
UqA7IgUDABJ1AAAKCRCXELibyletfCi4B/0ZBNg81cygXoP0ikh8nHd3iMM2dgNnjhso0sf4
KKMaYNvCJCkRrxHPEOSB2ccfTmSpFmChCcxz6G1kkfGobMrw6PkzIAzM9EjQIkjwLqdMdJWX
K/V7aHgEqFtZIWCvRPsFJn9BvlHdmMibpWBXvoCJsJRMtoOnkhYZfiFNqVnLMdgR8/RZwRmV
DhHFe74WUlY82upW7bRn21bXifRrJYKIj8YiBM/L2CkxDWtWzI5MPL6k2ziS7FS2kzIFFbXQ
DVfsG6j2Z91uwNYfp8/VHJ3509evNr1AWeizK/dJ8ajpFXZPbBFQ2fTitXhkpKu5q74vJsNr
pJj3UNa9odJim6J/iQEzBBABCAAdFiEEoxZaTrcpruksFzXfYUp4iUZseYwFAloONc4ACgkQ
YUp4iUZseYyooAf8DmuGPyRbWEBhde3br+kw77Q0whoAaRkn1M1UVdgbslKiKZ1ZqylY422N
AJRVAxAh1Pmf0vP043JUErtnTx1uk9Iq+xtfTYpE++TZeJlUtg/Kb+zCM+IQ2S7Nhp5xFvbF
2Gc+cAlbJr9rpzxUbC50r9d5WIV2SmZ005lM5pPMq+cePhJPJv/LJ0vvhm2qCSW3JNaz010l
8CYeYbiNgV/LrVHOSeCt8TfVrRUCpG0AL5sfrcsQIv/vqXPGpYsFwpp+zJc+i2jN1DBv9G/J
RQ/3cR9D2ZtVlEc38zp2nQk5WTpQm22N4DrWGHeKijTNftt8NH0sl7VDvxtBvQMqe/Be54kC
HAQQAQIABgUCTnzTAQAKCRC9oGCFSTus5LEiD/sHre85Pc0kPT59xGkFDONgwr6AUOAk8UbK
JwjMynT6hv/MN86P4iG+UO6W6XlPk+3Df5Q08sQquE6IOJ8QKKAT3mVzRxJgtM/TbZSi58tl
nwa0COfJH5ettqOxgrlmxnxexbfMlOuDR0rFRDMZPNVWSD3Stj+116FRLy4wckGqAZ1BFvFW
066vydkdgahQAvzHM3P1teNsNH37AtplKwRsPnxyJ5nMf2nvJY3DHWowW2+pLfbCAvIj9xaO
15UCqeYLURU/TTSXslwjlHd+7S2KuFLoYPNaYjWk4j3haRQ9ArMBYByZUS5d2r+LNNev6Qgn
rgoWy26qOgAyhYMgTSVH7cQ6RDShviEVpgWFkNn/YNFxwHGOyDt4pqF8DwrzCJ0BIYxw4Xrj
4ErM4HQboga03jxTJXsGBNTHs+hx/a36m4nXW7DoH42xLMjeD80sdeKgE3ts8Q5jcjWYplbk
t8jSw5WEGFBsMJWlirYzOloHHHs/fq1otRJBmCa8aS183OpACfbC4dJvM9dJSByCyP0QzQ7H
/289vXcdSOVarmni1oh4BIald73KSkjqKWPqX0U2ux36bdhaYR69eYe2yOp0kzFpuu6KivsP
qxtTfyAeIL3cJbRUosJc4jIqo8yZ4RL45nkAd/CfeQBWKX/1gsopSJsmCc4e2VYpH7QaAIb2
dYkCHAQQAQIABgUCToo2tAAKCRAFg1gQ3ZKewbJ3EACukrooWocgUdjPtaNVOMI1rkMAHQvo
yompTu8IPXBdLdx9krhbfEhcoEGBduBM1xvfwX5B8tMpUyoUuQd4uIdCckFJtQwPtJQJIV6A
psDX7AS74SKdP7RMcMDAqk05ZCeFy9sXs0Jm6DBfzhZqy4VXK4n2jSbI59QZ2PjoxNvglkoO
jytzi0LSjwu2ssabJ4h1oWfPo1OqYlqyD+5jf0pYQxQqe0JJCpOf0zUe9jQ+NMfXa+gwbhIM
JmNHevQeyg8lyTTN1Xp7RINY/Hl7d/gjNmCKIgwv4vSqM+qHnYxkkrnqJ3mCJiXRmYma6j6H
WSkhA72dQ7uLgqu+ITz06eVVKBFe92k1PI0SQ/iNTSdko6BkUArEH9qIUv06bvyrFPlEeAFh
7/8UBUCn2yah1mp6d7FUYsdh/6m35J2Q+SBbBM53xCKUktxnaUCMvIia7QUrlptDzTSgQnYY
opdbabwkPNcj3oQbPcHgqlBsI6c1hHCP4v6XdGu1KicOXuz1iTX6/Z+c4B7B5b3w7nQKO0So
/HAMjKokROcLfu+cK28uqHVeRSFLKoX3Gk4HYuU/pRlFsMj5U1xYTIFoDEl6FMNnglmMRyDu
q3YZxQrJgPAn1G+BJ1SV2Elxc3JBTX+OwQp9G2hrodcjcVAeBUguRhqVA+VSsAWWwMIGfT57
fLCQ74kCHAQQAQIABgUCTosu0QAKCRCmYvOPa+1gSXSLD/9Xhut8xvkKqFCKlBGoOJgzpOnI
MeLrZYPCWC153oKjKml1AEvyZVIroGnvN13ZdgmXs9Sjog9YDXUX3hxCbzZqnR3QT2RfaPCX
y+HQeVZM/Q+wHHgPRvPhtL/ESWSjLJY4NaFl3X1lif6e2T0p1Kbodrzd046CW5d5qtdmV0AW
MpO1MhrxKBazQKj5Rgf1ujKVtnGz0KzJwlMojs8sgDDj79oTPq3DOp3KZ4fP9nhoXxplaCUd
2+sYz/JXm12DFmzXVfoveyNKrak99RPSJCPkDeRgQs0oFdhZX+F6m6Y273gIoPeL82Tp64Rz
VSAxdrWxzaZS54PbUwZTqMeh0PGf6If6ybyAyBF6uqWu4OKXKdt/dro4vdyKD11PCLt7JZfb
jbaaenjU5UKgm/KKbBg4MiAmrSfU2R3VkRxcTgBroJA7q57ypTUorFB1Y7Vm31QNaoO2cgmm
Pj6zqMwO6M0Rhm2BsF9oR/tby0Iro5w00Z2zzJJdFe0DKo6AFR6GyOW1cBuPu9BhSkyV9zah
Sy9NYBjF3jwLaGkQd8E4UMhfc5GJiebshHWNj40lMeL1MDzYE1/kl1IyO6voqubcC6Ve1cPN
mnD714wkViwxRs5f8NJTj9T7SRoYZ4vuXqRZaZw1RINn93Dy1S1sLIDdlaiUVWCQ2vzVOQkH
Ezd0/6eVr4kCHAQQAQIABgUCTot2KQAKCRCoTn+5pfLjbKV1EACr/h9tZPTeRSkOvCzYrZW/
ff11Rj0xPQIUIC/I+XXUThkMvXy/ROxA3fC+kcUwNeRvUwsvY+4pfzHBw4hrYgaJ0f9F6it0
j8QelmC7zKBrFKjjwtV5fknHuB+BmZ62DuKLrE5xM3qKwT9RhrbwqXdhEoQiV5HtXAht1XG3
72dN0Hr/cxOLvdxXwrmW0mIuK5NanGB6Gbn4bB4mKTJVjImDzGCMouKMravr4h5SM7Pg67bh
ARFv3N49AumYDLKNC5VYCzNq0exZMm9pTncLeEQZwSZsiKuKw8m6p+TgMFqGLQ7z5RL6h2VI
4C7oKNbxyZyr4cCR9QNA7l1jh7Gl0Ybw/wLHf9U7Im/wf6P9MVEiMx5LzDGuX0ym2LRaFTF+
9lmyQUWtA+iJCTNPx4dpb6FxQ1yZXSXD4ZGPtZVNWaxddW6qdF/MqJfm2ZCzPgbMsuC2EOC6
mwz/XblH1NDv6+nqS6GH/1IWl7MTMQdVd2y3AmpENUwX9DhhSYvNXS17Xxy71s+YPNDPg2lc
ZdRHrDqYG8HzgkScM49kNUSGczQH2KtXllPSuPe68tuJWwnFIsBt6gdJ/vSxPq8OzYELtw3/
+Hy1/2tEbpv0ADgb4G6pK/ftHslpJCm/qJZfIj2CJxCVY+G2qovlX6eNh10teO7pLHO1TbqL
4kYTzQkyAt+JTYkCHAQQAQIABgUCTot2QwAKCRCsHHNDKkpIawtzD/4lug/LpmyoPGS304xL
WwVn04XvVI9bWOXhVFKK5JdK1ItjiI9nC64Vf8EKgdD2+x2I2KYkbFSXCOQqWhQAxGme1W+K
BvHNCHOEs8EWaCrATe3glYoEuXEYr5Cutrdc7JaG4CZkzJa28VfAkCe8M+DPabvAO1K1l00U
d7Cc5Ns0emf6IREeMNs/PoYo6XLZ8IXDx/UyoJ9tzYq58e/ucPVOBQQzfck4gHWJTgknG8V1
KiwsJ9xS76/+0mOzUhYIVK8dQ4gSTLEvmwxgcvTrPFrs/vf+A29Mcw7sDxgXaXhFjgHlThYn
TK+KEc+ZYGTxDQreAYFi4e1TT6OI0x0KCdfTAFrJYiGjQMSk4wZbd6bAp9pg7/aVT6QX7sOF
KtnWfcXSIdyix5BOtAAdRR3BXORGPhUVbRRcqLZ2pe4xv2AchKC+mS7ZwMjiLXoX30mZwmGD
CEtS5cuaxVq8UHCINIcEbGJ5vKqlNvnFFNKpy51G7BZX48tYI4i0BbO5fxzhGrtrCXS7Kkr5
3jUxVke0fxsxMeupH7tZSUF/zlV1gu5WQBBo1zyZ8CpT8dA0uErWku8QQPczEpLeFwFNoeN8
3zO7TeFqE92yw3w9eOAUPS65OET3Aitx8x4+L/8e+zAfT+vjwBnNvW0ljMi+ME6hUcZ7XWMv
d5EhjyDeIqGfaKuXv4kCHAQQAQIABgUCTo32YgAKCRATBljC4tRnOY+MD/4zyZNrd6DZRRA+
80KzIIUKIWJlOQq53aphJn4ctJm/hqQmdkZjnDoUXgSHDU2oFxN5HDXMPlXoMzTFQhaskutM
L8ZScabM+0kSmdwgmW0SOcRay4FjzhwQwGg028izbOThI+8yynk9P8VFPaDjKw+CV/b1tZhh
2dLh1Ex26jzNjFZ2Y7b3REjIZTWG/BshWZYOYFKaG8wqlg1lL7o01vv4a9lKlEWJ/gHOm4xg
nU+Zn9YQ7YzHBse5KH28q3q6MKqwXd6nPey+uMfDnpe1RQv/wQOfzKB5r/bTSE8G5LFyCTyD
vOvVzGfKs+g1EFoOX9GYmf0HjRMJvwIvN+CgM15ZySEKxVJxaDS7BZi9Wcbjodf4Wf4PkSYI
YWzXErgQViChIwcP5kBmladU8xrW4v7deSl7BrolJyp2ExkuheScyulXr9y2GWLbM6mSdtNq
aA9RPQNC4LXYCBKdpy4xMpVTDYNPJs8gvT3IPCdfjgFdSU3m3uIjRcMQ0lUfXC8Axd+ap7H+
qoQ3p7QGdDsF0E8b3SuNHvu5sThe/CPLomp4AXqQNayGthCwsvzYV2qBVtGozLWPUQ/OlNbk
+fiLZ3nta5VDygvLrfHM3diFVatOdKzZGOj64MNh1LHrsL+KjIaozSaiXlAcdl3QDMmDqRM5
SoFFBHOAx1eRo5gqY4G3zIkCHAQQAQIABgUCTo4DmgAKCRCpwXbMf6x9VoPHD/97TyJ7Keir
qZGIOH6AXa9uXiSKfoEOwsba+lfduuhcIsxwj1tOu8Il/1YFdwMG6IEN111VQqe9PdPeh3CX
Omma8FyxIaKY3GJeQKHlFCo6/KnALvkeuTo5sD/2AGTaHQou98aXxrmiWBeAkHeS/awWGULp
rQJWBbbxCFa/5Q+zUWOV1/FGdQddhm66QA3J3RbP6/Vv+l7QnD/vKXmfQ91Pv8sH4Deh9LwP
6mUg3i1O0MxV8cDMtBbQ71R0GBms2BddBstgQJ8IOpm67XcVw7yuACUzidSR1cdRK5OSVViL
9y6h6PSc75oZgsk7PKaniApQGJrGbOw1fNhmnH4oQAdfZQovHED/B8IU00CXiYhNGdTIP0x2
wGk78IcCsp+FSdpg3LPObzBgJAUPwM/yIm26ZcNYlYDuP8Se9vhNQZNpkVeW7xSG41aLNq2X
SHD+y4tzERor3X3paF//KtOlJryRc7UhMb1tCZxyFyHQqGiHzzGfklJhl6GZNPXhlEfzVS+1
VzcA2+kgQ5Gfg023a7XLrQCMENW5/EODgLpTviB9RXDJ8ZKpmxdc5nhB+8bhHJgc6zPBzeWp
6iw1kpU0EqKUNKgW33kSpzwve0+CL5xEyPN4X0oO9SiCTRpMUU+3BonSpXCq9KnYJL8i3XEm
hAu3fwr8JgQAZ/rhNOvbHoKN+4kCHAQQAQIABgUCTo4MtgAKCRAup2ucK0ZtnYcFD/0fRdXU
KWjNgscrGhYtgyUX0Rq7U2lifz9XDcoiHUWHXdAleTLOCNbyk7pW5EtODOXV4uQtgiZZGUE5
lJSHM9FAZUAl4+ln8rwr8lnfRpFIG0TpP0nm/BIiaY67/gY0KlYnKt0xHrl6bresKmrrKDQr
Pki+m5FngQNhKzsJe+SbrO0nqzUnnR6W5yghHSDtY4c+7XWK77tWmXH6J9qMeaaecTGYLdTr
GCDqXSx1eDym3JJ1PfMsn0LAd3qMSDrqEg4YU6AxZ41gHX1aMNRELEk1WGbGOgfaHE1BpWAC
KJzxU53/xO4zD7s2+whEuLeSzBjxJ482lSPCIySsR9jZ3/STn0857CfaFjwG4fRCYUK6babS
kJ+eGpxo9dJ/y+NgWaupx0SSMjVVFDDSCXxOW6bZbz2Ldu2oMFekHESsT6G30w21p9dm4TAC
r1kg3yzZZhZYvZnCk3iKsY8D7GwFD6SFfI7tUA5mXclOvhLJAPd5KIhWOm590lxZ2HY2qwmJ
ZIsQif+103A3XkNWflL/ghsbXYytKf7z3hMPSIlAUieVIzuIW6BzOR8z0MjKy/QF8TYUheBG
wxLBm4KIdCcUxphRBuqDd9MnMoRZAO7wUvNy/T/zn2T5JdkNU36qHGqMsYpaZ8bB7bjKs4uG
54rVYt8xjnQvQOrUbl5Fv4WonKRG7IkCHAQQAQIABgUCTo4NfQAKCRCXLVv03GE4BiQ9D/4l
oorpIw4+wIYki0020ctvaUC8XoaoEncFfUq+aun5H6g+2cMEl1tbBGwmVRJ5Rl14xkFgfvPh
M2IJlOH9h9sZrD3BBGtV1r9netvIPlXN0BTFcfRLl1uXRcPoYHHTI7nVHtHLWxYUlxdd5TkA
ybVIlX484ju/SGa9LhztJW81Duswk90sPzWbe+/8Y6wFyjYzUFvEzs9S4JsPGfxiAwQzRa6B
0gT3MK3sqwPgahfWBbB3v4G4L6uQsWlhNSeyfI8zl8tpBcLwYFj3D8OtXN4XSn0lSQBGReH/
nY5qOsQZGPaWtH7C0aZ49dBtn8W6wZaf97NdwkpzDb5surMXoYsGAiydh/GZO8BTcYmB14h4
z5sfYBJuvlB9G6O4Tg4EaR6w3FHOMM7Sc2AxRdAC6tgXg//AenU4//nfHawy+8ZfQFcgeJIo
qLRJi4hCvQRaBBs3JMkv73GGPeVZQRfGIiNpRw9is7XogZ/ZwFP2E6xs6+a55qmyJHzNToNa
ej0R7ghYuss0CnlTQfig6N0jTWPq9B1ziqQFWKLDI2IDhyBUuKq/08js6qn0LSPX0cHlfEj/
w9fjTbIgEuR+lBGAsO7rTjqoBKoKv3gV1dZQC71BrN6MUXevCyUcBg5hBx6WotVA1FhH6EDL
UdTIGKPDd+7a+NX962mTRKz3D81ykawZHokCHAQQAQIABgUCTo4ONgAKCRBBZwbddg/ZAUKB
D/0YWIBRrCXcDLtb7Kue4MRgEDwUsP5lgVZ65DBHheRLFIHhlIkipcUarYlEOE7EnrLNLRR3
CWvVqzyZ2vcao2IvVx4L4+uiJVctb3SlAoRMK9oedYeeEMogKuRLcULumy3BGhJwNnipVGGX
sqTZnDgN1WqWzTs3Ghwn0ikEVuaOSbNvIkq5jMun3leyKO2+0jaI9J2V/QfnedROg8/XESOw
hfCsRjyYq0vqasYvuCk2HF1OCyjYIm2NjNgnpyPTPW5H/HjCI6OLGgD16wbtXHX6M3fhwg3z
GezkW+tS+KkHIDVZTSQ+nPfzvblUZuZJq7JuJfh/iqZO3UhMBSjwsSLEwpDCcj5HHnJpEBy1
728THma7v2/0KRSgaq9dTm0BRL9b5P1rrBgidOu9qsPfLCAP8n60X0imjX6gAH+2ZTw4MnOk
PPRUBeMQdI/Sjy8DWg2HzNY/j92fkOqY/Yf7/V4+t7rWCcpLcB7QIQpkkaO8WpqxhOXiuUAR
ueizlUzp2TIWR3+C5cIjgY8HH2NqXetLn/qSe1jN/lHi/82GlDyDZuMBesN65Ayz3F4hItrf
iW/kJIqfzHdq6USAxPRa8LTRbQrh49o+0e2b4MnNqfjE/UR3X/PU5rqgapF255908yVlzrOf
t3oHxjJkG8TN3Y+HytBiaiDh77gdoGCFk01dzokCHAQQAQIABgUCTo4OxQAKCRCNzjVWMCLl
euCOD/0S/zMc0miN/MvtNla2uAyl5Bdgn6VX54Ejz2Es3Y14At3DAGgRjL980xJIAuvj1/2v
3ZdoSHLXX8ULboVWX2Dk6ggAW/G+lZCwS9QmZIbYzqcpcf2KfW4Ixh/ZxnYvjTx55oqpKB1B
8CNsIY82FCDjHYGJJiZFsWqx1U+6M6rcsyCZ9DEEFplVRSvfThl0z1/QDGJUCbRL6xZMp+Bh
/5yTvDjgjY8r+MrXS1vo8cLsGDr47ZjZnHtBJTGWOZUV+KN79A2Da1xun4Vzt2lZv19jZnJn
Ez2FjFxaxkCaeK7inwKMr+Pp1YNjtGKixNLatRu9npND6jMrdWA80wkeYwbadG1CERJHDh17
gHigGvtq47yqYnDOH25npI/ZRpJJamQLzYDAgmDQukhpUfvyJCcKe5ufPzljyIl2lV9nuXee
S3B+QAYPQEIf9g/kYPYDP8EfKvgXtt3F7nLj8W15XHL50iihT1aj9ZYyh2PrPpKCNW9JtaNN
gYiad+gx4r7EV/9GymlPqXBtblOxanhiOqvL4ozvECARWTIXcatX7ChHGwmTAgtzipWDF9pA
HQxmZavYAMpMNa4JZS6OF4RDzBXmKnQNY60obh+VwITCoamjIj7Z1kDppFqcLUD06xQ3Va36
JXTeNEoSAW+5sXS2dZn4gT11Lu3S0eTI0X4jdRY9+IkCHAQQAQIABgUCTo4P6wAKCRAQnwmF
Bv8LFNw4D/wIDiPEEI+IZA+CUKvfFatLV5oc3txbbw0sj/F8nJxbHvxUpyUolbUMYcLYo2fm
kPROP17KdVxqEFtFJG9S9Inrty6u338iRM9k/wzDv+P54iS6wOr1tPghoK/rV4ecNpvNjK/L
3/Q4D0L2qcXsYqhuJ+PvsQVjTq1/MHmi/NPARHvWe82oVzPVYGPn4tRc8mb9i9GnzmWrktkz
7nOfZzIbnrWnjFW4kE02he3vFHk/BR7rqgd696fWC710gPUmU6sKwDcwpMgnEW49bdmhQ0yt
sF8YZR4byov0i8HWFNIbaLUTKnkRmB4OmHzTBthzl0w5U97bLnDDzHSM8Iw8AHDxxFRxGxmP
DWk/lsLxJhdIw1pxWi8SgdWKWTphHYj8JU1lXw/1V25np1GvEqEPpuNWR5BnJI5GZH7lS1iw
Wj3ApMpEwWi0zLE6eepq6S9Jt8bcY0lCDwcg3zr+3QkSWt3K+od4HOnDdlL0yn49OFeAsL3T
eQ+vaah3emn9+I3Pud4tqtgQgYvrkbExMGNSmMBBhChyI7tSvDSf2xvZDbbeqJ76nniHXG3R
G9LII50P6pqH9Ln/OOJdjkAhItW3LFnYle5p2VCbVWMarncgwDCTRZSdqxUl4HQxYe4V+Y7A
wgkwTelp2/lxrxotwxZkcDlee3NGh9wda41lzjyS4VXNookCHAQQAQIABgUCTqPXegAKCRDJ
Oo8QkWvKOaZ9EAChGISIVHn5+HGFmHlwYlJkF5yrAN5MMwxlGsfSpO151jVZGEdc+cSA06CO
8GAFISr/1cj7897PRuiwghYqhG4gMZJEtHjhJD+e+8yvEB80Dmjfy74hsEcfLaCW58ylWR0D
6r37yaN6aqH93J168hEgJmOOQIGhR3ANDiWHrAm43Bb+9++3iDo3DSLml+HY2hJfTVGVpVwY
fRKbQiAwqrhjJSMfOv0wcpYMHaEGu7TVI8dSzYPEBNkWQ+EA7nXRBRxlJhr+j7bbK5J2O1SU
L1/oXywO4Gj5FOt48mbybJ0zdTe6LUuNylB5POZCz4sG2siZFwm8PTW6IWwPTExj5jsok1KB
z+E7uqwXOR5jpFyVN2YAKU7hrVGG1qW0AVEFnXUTvQc+XYPZihPiJQsZ6ch8hO2UbKUnR/5C
vdZxbKUBahiOYuzwLruRxEyWmu4Pj19SoYUflUZ/+pKcbwpLPvHkBif8MJmWliVnsUDQ4iEn
4qRTRFnx+Da/MO8UfoUJKWRtWXdD08e8GP4teDcPYpUjRTMOMTknZEhgsQRCC6t3RPgGyRCq
CuSdG9iyRW19Wbdg6AOFMHFm6PEJSv6glnJfzoyI/L1iOehVi7oTLQnCkG+lru6RzLsol0JF
onDXfEDVZQjabn40ZQFJS02oebfm/dfBAg8E/EgACR9kZLY0eokCHAQQAQIABgUCTqbTNgAK
CRDq48FeWTEX9pe6EADVXXm3+h705Nrv27WvI4IZtq5tUXI7U9NVWyqdjc++KdOEZ2mlQA7y
AfouxOlOx+VmpTlOaq5VRFrQ+B/LXWyDjr6UKS0kjmguGzhkRtxWOCxLJKFNXXR5nnoQQ65x
HwFvgMKmEtHGn4J0odX5de9eZaEKsDi3KyDPDQMHg1k/FUOwNBGICkz2q2FgmDuU1xjZSnFN
jafRUmFyeLC1B8AadHOMjdoSXsleaWT6emztpaM9LO/PQsqVXf3SyYv5bHpZoxGTCWmtCeAn
C5vFQwwPjweEcDbJzaRRR2KFFwk2rAFH7T3/nxqQev3Ay0qzsLDwFqTWw9o35cq0lUKXGESv
pEpq7iXr2aO9Rjnx/IdIDRNlT+pAvnxoDWQ86Y1kUdwYTuS/+mSsLvSQrG4AYWi09dbB1XxV
FOaMLF6XKanjZ7T15CU0gMxru8cCHQmtv7R55MHW1dO31famp32uqP27xb69PPkPM4gXMmwU
2O9bI6/sHKTqwLzzvYYBF4W/Ufpep+SsNa2/AO1kD7ajJ9sHbM4eHWv4v+Y9DIABSqwBeg3X
bcY9omXLJCUQmeZdf6dPDmO38wSTR2OuCe9DiMklRdNBzxf8Qx57HYrEI/pCHD3Tfb2z0oYf
rkcV66O0vSWaS5P8WA1jv5bm48DtToHPzbsI7SPxj5VkmUs/QDzi44kCHAQQAQIABgUCTqcZ
SQAKCRCY5dzIFke3KvWUEACOIsVDn9Gprydr9rZtyEjeMDbG41iUfP6LVPo+nHzX915EzF6v
zrETeYhR5sYkGfhaChlZt/2Kpz5zyNSUTrmB6OTycdLUILE/TrGyJy9jqmixNLBWzMdOy5Ir
Lwuy9pOBXJck+27laxanegZKUi3shVjeP45Mhq/m023FAAx6Ppl/tDH9E6QjSDy5bxUxm1jH
yiF//F+QXmcrEjXVZZmdTmhBXUKefFLX2U9v30qLPqk5mYzylc893mEEzUdgHNdhyB+m45vx
2D5btJEnJTlvZJCibfDImhHZpyK5uLxs+mV2h8y6ixRfHoyMCjcelQ1J+V1RszFUUuAuAJw2
B7V8u0lhmsY49sH7dWJvLJvJ1GnNooXiuAoeQgRTfDvybjDJjOCn+1mNqWyop9tV/NvFcWW1
AhN1oDQo8ZB+4z1FJNTjZkPLAJaQ5HtzK4bKEtcyb2O4T6XTKQD5qCHBIz3al5rUG+hFFrGU
WxiphHGc7QtUR2+5p00nw/jpl/0k0dv/7iD7ItPLP4HipMUPSfclf+w8tVXBBvTFLxdoqqXl
hW1mRLyfDNaGS/0q9bdc6Ej8L/86x0Dh1WKF9Lqa4doB2NQWiTso2JlV+o7dsm7u01cswBMz
Os9JikcSvzoOAijwUPDNFOPT2qhsiF9rD7c43tcw87WQJ42s8IMBzntRHYkCHAQQAQIABgUC
TqcaBAAKCRBzQWGe0zcWDIflD/9ZNzThnqs2/eCKcTqnz00OMvL8hcCkvOWmwPxxxGkeXe9o
9Nnkz90g0WZtMzS9n2z+IF32ylIjgwiIxCHNeNwhE5cZIoMsdklyU9Q2Q9FO6d8vlAu8serb
OcmG4B3t20Nlp9hsqwMxKo8y2Lb0MOqOI74zNVtBkjcj21AqIXK973M4prPSHKLTB3Ugs3g+
43o8oe08I2fSIVj7DG8uKSRVAqx3yPG5/CbJLF1Oo2iOtzMFcjJ8Z5zSH8lpHd+8dt0/SXcb
e31u6BSWTalU4sOemdfijXWlBpEkU1InY+eGJi44Sjud3o4X1DIKzWI3UjN6lLkiik1Ror1Y
JsN31jM0Br+Tn+qYRkEBd6LczQvu13RjyQo2vHw3qz6zMpIN9ZA0VU/iOF8GjM67q+Xbr8qi
g0X0dI5+1JKjCjQT741aLZJU2p++BBAfU6FyHiypX3fMTIh1B9KoiIKrIEkua1WJ3IQ3WzuV
p7jtC1N81HnGEVsWsZ40vd8u154v4p7PJOSK1ExhiHU24zT/MbkXKLAa610jj6LszlmHzHlI
GUUM5kHF+yam2k72hzeqM5nDjqLntCWlo8dvfT0T8P7AX2AQBejub4Bl3G/IPnGFKj5moJ4z
3o0W722nIlAZaDJ1JmQoH/dWipMw3VJLwgBHVDgM/9uzCJS5RWulIWzHXsgVqokCHAQQAQIA
BgUCTqcaEQAKCRCbjacJJlQhnNmcEACf5A8aTKQd9CWt3fixVEGHK30L55Z09rXHqdSYR6YP
X0p4Vi7llsM/Hx7Uj9qWonp9Rp18/Cj7rYwS5Z/ipbokc4lGjrOJY+pibearblQJ2uX0k0bp
WBmF4Su2TcZJ28vefoP6YDo/1NzS+AtrtMgv/XbdmaIgLpWR2gT/lIgWIu+QDKv54uICUsaL
F0kzpmbDbuqlo0gjlOorzy3rYwkT8leqIqS4gkZMh5FnCWqsa7xchuMa6qABVxz7oKUhhGTO
9WrYLvlc1K//5KUu7Z0AyGeoxa8onKReHJazZ8iNqNNy5BEyNvh1ufUVSks9LfLNSQPmcAbk
pnLBgb8RhQN0EFuQPl4vlu802Xu3bD0lLUkXh1ZSaHGsJ2pNxY/SMDPm9x9EyQe9dSG7HVS/
gWib7W/OJLc9Fg22IQb+4xKVTD8jPC8mqu3TGSZhW+8Etfp7/TsdO4J3vaWKFSWs+rHnl8lT
z49jioaqo4xg7hSF1V0Njnj2bgYvHz4XAWKRv80KO+OZnXGXlqARuuu6QpJBj0OjVDQKWI+h
Pr3McLpySb+YEkhP2jz9hg6YDgV8NdZf2oxjtkfi3JqCLZvT8JLNBB7DS+JCLnhz0GLTt8ML
mVTJEBIl9Wcz4OHj2HKvmmeZoFKdUSFhDI2PstgY44uBGplQvrmlHW/o5zIPG2dmaokCHAQQ
AQIABgUCTqcaJgAKCRDM62pObW8nMW/FEACoh3GFgINpFxSDl3T3EK/JvXmKq3+VvRKDxo78
oWjrDYyuYhA/CsVyuAhehnJONef5EtfGmneFL35Ln/yWpRF+J/ddfF6dUoJdCbmwzf8IcUln
By/N9E8hI0nhVm+mcWVeHDAbQgypY309OtVXDt4p851+ov1RNPE0/z0g1HA3zzcyepknCARw
6DJnlHNK5bDvMDcU1SPmDy2g3iCh68UCdrtO+7GuvDIt7YuJ1dpm+vjIETbXPRfOufUJWMSz
sXAhX9/lovPNI5wD/EUfRtEdmm2weUWxVALeLi5iKgSdiLFjg99alv3sYgvwejwBVa3iE5El
ffWB3ZsP5ikOJwRtIK68Xb3DNbEf9DXQP7QB5tGlL0eBPFTZc7cScCQhQEDqxIBeqazNLv87
yA6osMjwFPSLTo5VCgPzZOYQsA0MTe5sWtM7ShCEiknE7qEa2IiiKejH59a9TCqZS7BH9OA6
A4sLSdWBZYmOMXYjERO7KcsqKvMNujV/Piqp3rWHQAClhe6ZOSeTOdLZEKyKxj9jFr6lZORl
FyVFVXpo4KgW3shN41aj7MDw/OsQNO6a+PLm6H4iL0DBvzmEJWYkGE5AZUQlXkvGq2LlGoA3
YDldk4eAFE3BuCG5E37l/a5/l8bG+AASUYMV6vhUfunqUrBckuOtgiAqIMtRJk+f3Kwy/okC
HAQQAQIABgUCTqcaJgAKCRDM62pObW8nMW/FEACoh3GFgINpFxSDl3T3EK/JvXmKq3+VvRKD
xo78oWjrDYyuYhA/CsVyuAhehnJONef5EtfGmneFL35Ln/yWpRF+J/ddfF6dUoJdCbmwzf8I
cUlnBy/N9E8hI0nhVm+mcWVeHDAbQgypY309OtVXDt4p851+ov1RNPE0/z0g1HA3zzcyepkn
CARw6DJnlHNK5bDvMDcU1SPmDy2g3iCh68UCdrtO+7GuvDIt7YuJ1dpm+vjIETbXPRfOufUJ
WMSzsXAhX9/lovPNI5wD/EUfRtEdmm2weUWxVALeLi5iKgSdiLFjg99alv3sYgvwejwBVa3i
E5ElffWB3ZsP5ikOJwRtIK68Xb3DNbEf9DXQP7QB5tGlL0eBPFTZc7cScCQhQEDqxIBeqazN
Lv87yA6osMjwFPSLTo5VCgPzZOYQsA0MTe5sWtM7ShCEiknE7qEa2IiiKejH59a9TCqZS7BH
9OA6A4sLSdWBzKJRkEXG3OuLE3fRo/0n5AJZT1BVqiBUEMnlR97zz2CTOdLZEKyKxj9jFr6l
ZORlFyVFVXpo4KgW3shN41aj7MDw/OsQNO6a+PLm6H4iL0DBvzmEJWYkGE5AZUQlXkvGq2Ll
GoA3YDldk4eAFE3BuCG5E37l/a5/l8bG+AASUYMV6vhUfunqUrBckuOtgiAqIMtRJk+f3Kwy
/okCHAQQAQIABgUCTqcajAAKCRBBYzuf6Df1gROOEADUpZRQ8GEdGMI9VoLqHl3j8hC6k1q/
4wsKZsClyHucVsfH8Zq/MCO2cZoSfCzow4ukSjNvzugyiKukFFjdAWK5V/cponACySm/SdEm
8sYkiEcZBIdYXOviDu7Xb40rhazIVEO/WyBnRpikzWp9/zzfu6lT8C9tX7NZuvnfoWrN8v4c
SwPuvmZufs2OnPP8iYrivEuUR7fCu4zdoGg0s2z3APplo0yjC6iueEDTNTDf5FTi7MPIGaBh
HqWpjiemCEYAndYf0jDTVPi1RDgiYgbZ9uRZvkCwaBLs1cYh7xwHvPx61ftb2HvAHKx7QYk9
qTMrkEW33PzJ8+rSPuNeFUP5eHJxzyuhDaKwN6YLi68/p55NkXsPNs2MD0N6us6mWjLY0CEg
kxz3O9kLpLDQ0qobuOUDrHmGBCA0FOrHtmM1kZ8walD2tvfUzivZniEDskYO4YZDd86gGszp
ShBra+KDI2xxY4BQOW/uk0xsbEplyusmCZDlAGzjKk168YIjKQIgBLaVZgWGgHi3L5Ce8w6s
ppi7P1vmWcqLbXMc3ajImQRuDu8VSBUUH+4RZbkxQN1nmYCTMFprLgi2OHtBI+flS54tNvnr
d2gf44Dphv/XV6YyZThI2zIwCPtVR45kqMemlIVSvisjuQtEc+HlO+ErrmhiWQQ6T1CY3P6t
uzjE04kCHAQQAQIABgUCTqcajAAKCRBBYzuf6Df1gROOEADUpZRQ8GEdGMI9VoLqHl3j8hC6
k1q/4wsKZsClyHucVsfH8Zq/MCO2cZoSfCzow4ukSjNvzugyiKukFFjdAWK5V/cponACySm/
SdEm8sYkiEcZBIdYXOviDu7Xb40rhazIVEO/WyBnRpikzWp9/zzfu6lT8C9tX7NZuvnfoWrN
8v4cSwPuvmZufs2OnPP8iYrivEuUR7fCu4zdoGg0s2z3APplo0yjC6iueEDTNTDf5FTi7MPI
GaBhHqWpjiemCEYAndYf0jDTVPi1RDgiYgbZ9uRZvkCwaBLs1cYh7xwHvPx61ftb2HvAHKx7
QYk9qTMrkEW33PzJ8+rSPuNeFUP5eHJxzyuhDaKwN6YLi68/p55NkXsPNs2MD0N6us6mWjLY
0CEgkxz3O9kLpLDQ0qobuOUDrHmGBCA0FOrHtmM1kZ8walD2tvfUzivZniEDskYO4YZDd86g
GszpShBra+KDI2xxY4BQOW/uk0xsbEplyusmCZDlAGzjKk168YIjKQIgBLaVZgWGgHi3L5Ce
8w6sppi7P1vmWcqLbXP1qyeeuyj+moLhtAD8+HwIZUkOfKVFN+7K3sn9DDFvi3tBI+flS54t
Nvnrd2gf44Dphv/XV6YyZThI2zIwCPtVR45kqMemlIVSvisjuQtEc+HlO+ErrmhiWQQ6T1CY
3P6tuzjE04kCHAQQAQIABgUCTqcgBQAKCRD7t1drp8sLa4RGD/0XqiNLSGfOdJ4zPbe7fAyn
HSpe7iqYMU5vSx97K8WCVT1Xu9OzpFVYMfA9GV/tF0RKxPRcB7sHaLkfYqd1SAk5DnK6p0VB
XBi5B529Xt4mw24AUP7l7ZvXahxsrK7ahINMAINK6NyxgzWlPX0Fh8Hq+WqI1h3Udk9XmxAx
Ao1pxa98m5FeG/QkHf2akKXEw5NMxFzODycxMW9doQY6f8NKXOpG8M0Mt3dX50rKCSN2Ja+T
aU2HMkTNJIvd1FpgMyQTW8YAbQDT5VuAwKlmUQ4Rs9uSBs9+AW0vQU63FtFpY0gpCjbazYHJ
r6XoURANh7LhwmB2T5OFplSVLEEYKH0+EW2Xteb9xzrMYyABkwFQHKj/FYTJndhPBqxg/fmj
1OZGLRwKbSy1FCpO+hN8VHeNxuZf68yf0RMSxabxXj8Ry/wMv5XtwdGoynOJqcCwzNt/9zBw
+IXUJxv0Qk32xV9l+x7lrB4zm9Uu7RP4pmrHSWXMgr7dQ0R3nlPh8bWxHtalYk7Yp3GYQzYy
qP2uFE2WWN7qvsYYhUbbiJgj4+Am1/cnXM6+vsFw2b2rGGsBSfQ2nxbjY+29EOMB1gTsMZ97
2K6x0yIVwQdYVWiK5kSdthdtEdsVSAzqAIwxQ2BXOwNlJJt+0w0DKEWmqYIDD+MS7Gfw2R/L
WitcInR4gMQPzYkCHAQQAQIABgUCTqc2XAAKCRDu6WAadcXePaJfD/40sEQenbyPwC61/KDl
7YdyOYGk4CyastdJ+6N5dbojGoeDOM0RD9tO8kmGKg3BeQVDk1vcoSzRaD+4/zfYv6W3+vdY
m+/4mwOUyx++hiu5Sn4bEvSa0HG2k5mwPIG+MeTqtJOZM3pne+ZPiKddQXRK/Ohn0b4Fh+AM
+22jonBbUlBlwqwno+r9hQBmYLzzs3yFkXcRZqWIuAQGqlAp4akSJ0A6pZX3sIkQyy/fvDj2
4//MFdyWZ+Zhh2M6IrkQPC8SQsy3Wf+3lmBSGsBCOLd4xF54V8GjnwdmJ3APpDOgJeN7hm6e
c7MiMjMzilWBgFicRNPWc+YoNpBuJOXf1wAGXEbMSxYJQ0AJ00Li6e01ZhUfGy39+8yki9JE
Ul7JKnOOimeYErVS9WMTf/5zbpaaUeF4mvRhtyDWKGaSvMtQozQUEkRUif7U+hWz6OilN/xu
m+d6aMkmf4jQXO15IM1W2sb8LdZL6fuNzvdo15Yx5ud7DKDD5V1uPZPZZMjR1igCn/Dn1rVp
FvpcdpLIATtdVSuoVu61jSWYQuR1QQE80Q8rIo+KptioC6Gd2HHOUW6+3BRAD7GrusuHhpEU
z6iliSg17t3lI0PWfHtUzC80g8VK9lhaIlAjWiVGQHPLUHkAE1LRrrFBWycZm1z0j5qZpS2b
fzAXyrpHNwMTz3yM8YkCHAQQAQIABgUCTqdCCwAKCRBjtPAZfzxC55hdEACt6bZDsYHwzJAl
g6N8Fpyi9SJ1R17OK8fDw9Lc1+F2Lkho5i0U0hpary/oN1UFA3AVnmaSXIUQUtjutO5NOYcD
2ypJwIZ0CqvhVOQCtD/dqhToYAO36IzKK3CkJr5wRYVnmG3qF6E8ZtSByCK5HpWpPQ1hAN0N
9w3mWOJEXeCKUiCNx+PlwlwJikk/d7d7DCUrsxTDnJxH45D88e8IqBhiXhx9pd1pKLHbYHNR
Q19i6pzTnYwQKx5GNj0f0qgodDhcPkHHC0qC2upstXE1cCXGhVQD3M5wJY1+RVdk0XUojXIh
i2xRNg4J/4+hC6nlFEQkt4uE0mBWVG9N+QaZrBKamp09hwIJ6hIlI7ZxXTMK+Df9QWAsGuHr
RASg57rQljiMS86sHA9d4PX43PbIsJS90g6JEMyzI5Evs9RnIl78/87eQq+k5ew7YoOff4Xs
i47O+uKVdTJfe7wNOy+yZ0lkiMOYVnHes4Q0rpZ5EUko7BfwKY2Mw/CgWE+dfLYYStJrI58Q
XdTTaZJ36yjs1eZd6oyl1JqSJpIZnHSd4NkoaSpkDqnHwOQ/frCWWUEZbzhDRi2wP8PNeOIm
vFF2Hy2n7clo+YH3JULv2WtIH+wB6XE8p9n4+HDvV2EkpNJOVqwglV3iTrTDEHbcBKLhkwAv
pgPI9dX1QZuqW0yRebFHuokCHAQQAQIABgUCTqdhzAAKCRBnC+eKlgoA8u7VEAC5Z65bfH/K
gVigebGOZiYzJPe1Hew8jNGnM8j8Wkb8O4mzYgWovbQRU++NsYL0hW4bbKtpj6ORhDWwBdon
ZZl3hlDMcgurcNONN3ZKsMJPsBCVOIQ9qN1mxMMT2+SN1wvojDrFXZsdsJ34oPsAxa2iBRA2
LfgrJmSIOVZUHxzr6EspbV9BuLOEmzP82QPwbpoWMAz8KoFcylFrpvqMG5CbXClLgizTi3qz
MluTxg0Ewm69vthFVbMznW8xSkN5qb0VuxWjCsXeD4FtLTzdjatbJyAFFKLTXdV0abQ0XJxt
3gVylu+SuQDA5TevMxYvvAtM8KHxM6rgvYj57GfRoJzWCx2SNDa2gNUxj4LjH4PDxXGCclHC
C7UdYwd32BwgxZW12UJVRIovB6PmulW59+Ywt6Bq8l4F4QZozmH+/x14x/THi+bBVB/eswo8
6HytAMP3PTwT9q6tngkH0ty4G8buakDNxovtusIhvmYdA83KXoUts277Bn1qkOasXMjMaGoW
lm/UeWNGprkzAe1IPq1vOySBqBuVHmWnJKNt2kUX/rxFHturJQkhYNk+mF4M1sURgXH9DKes
pUBoK1uSpsQEFXFgtAjsXBEKUpArdNRUSxCAFRdFAyO1Uae3VPUls9R/BDmK6YUCvdCNtXfx
svSppcoCchzVDJX8YTQdt+GwuIkCHAQQAQIABgUCTqfZlAAKCRC3qxXtQrqaRDtmEACOMtuq
7S1ADzweLUDco59w7Sp+bH6jsye/LaLLAAXFdr3rWtjphqlhdRthO4dCZKVlu3fey3m0YoCp
JPmWcD1U17IumVPELsZFzCFisam1V6WdvlRqX9LnH+ziAmEWrQSIsltxHgOMRHPvdKjsC15n
CZIcZZsMv1GNvmSZ68Fv1ICHNn02azyLT9NuKKpNw+v5Aof/wTmJpRoSvE6oEYIpdEaRsm6+
7nPjnR1qIcVIEO1nZb02i+A7GQXZ3KpnUhjrAbiH1j7j8MZZIXCTyU7BdT0rGJhujq3kJgYU
wxd30o4Kh92C6EQkRTVipRWuRK9Ft7pULaCom6aQk2gEHF++UYvSsoOgb7m3iMKF3jQXMg8v
gMgwNuOnSjOwsAU8XxSltYkiUV+aHjTo3CzYRw+9y83/erJJPEMB1XaqS2joh9Qfnt8wnTq8
zZ0m/JYk7yzfsjjwZizVhckQ3Beyg+UWMq+kJfq+z5w3bFSaZap3dvtmeLeraSN/c+9QS8Ps
n3VulizeLXvvK9VU6Kx/vkqvr6LNJZoKT9JU1Z+sYA3oO5FT1wtanhfudKwUMo0aliXggAy7
DAAPGGgmOrN4zIdfBT1+qEp1yrQYBoobi0CsrXVPdcYwMM8Pwp0NUvQAaCwO4bnnWzJTA7/A
Uf51XuqGzD5bugRnSDut8f3dQGesyYkCHAQQAQIABgUCTqfavQAKCRCevxLzctn7jPsMD/0U
OPhs3Ov+H3hnVWMa+60xI9zlfTm2gfu7KpXpZG/eGZTHMG6A1Zwkmb5yMLratZcpRAJP/gQ3
1c7V1fPCCzHnKyonSndNYR1rmHphUW+mj055nuBv3opaGTu7l/8IQWvCIELidHw/aY6XuNkw
n9Npjda/en+9AWFBpUmpZfXr0FfHvseLBEI4ud8PrdLGepJMTFkPlXk8J2f3dHcx/7Rqet4T
JeCYTQ5XpmlIbq4Xy9hjT7chqzP7ZgMUGV7eeCNdkP3LUdHgpoXtwiJSQ4c8LrfY7lK7warK
ejZ3NwsQeecqKLzjcNCrh3oCX3qFGW01BOclGTYUdHI+1Uf/rVw21spEPdQ4avBHXo1WydpY
Rm9y2gAa6P8AG0DFvFZbt662H89Pg6wd+cu8pEz+QmGGMAzrGjPJaKCRAQMcg1RlfUlTYuxv
H2+5bSbpWLXC2MsWuubK8vxzoGY3AKg6PyTEQtrVKoUx1OBpaVq9StNy/LshiMMT9N+B+Go/
RvGBOdup1HEV3+Bf6aSFR9+6LSvfg7xaUC3kq/L4/tMbdn5XnJcY7RAUZFI97UUgbKRQr0K0
l6mZqsiPdOAnumhYOI561P+XfqJ4sQIKlwzSuc4xaKtYHwWs2HuBUlIYYh8jJdrcz30zD9tJ
p2ZO0Dx+NrwoX7tJ52uOgjhcsj8AELeJEYkCHAQQAQIABgUCTqfoDAAKCRBa0kIRwGDRyJmm
EACp3GxEcWwQTJ26iAAWKNWDPlF5LD1uDak0yLX4fvT8PMT75nGIvd+PbnquSm77645ddBzG
atMQcqU7GrW/r83NtI5PMKaVy4vmsqNGniZLufznGv4QXjIWf2I6i7igHmt4ELhTqiVdyPW6
ozQhSf20+quN+4C6rk1TinbRDhTYOTo9JDQuiuh0N3cMRyagoeBh3bRb0ZF0LtjKpkuoinGo
oyO3MSFcrF1S62Q6r4HkKWguShvMnoRsWHAhnDVWUWoX/cHneUhnVVMKCJlPmeOOK09yRiUh
2i7v3yUut2ajNYum0uHI0PR93unH/vc9eod/bQEY7qluT745Ms4rGatTu9rUf/V52JgcU4IM
JAl+Uf+VI6Z9BB+dSq3fb0s8uTsjIoJb9i2mqCNprhsXb7XK+UIybvT05uR4TAZ94P/vbhaB
Ou863L5r78f16KynV4W7awXXqzvUogm0BPFdNm28PoqlR7gUPqlIxizmVfsm8NojhQi27z4v
EHUaCQ0BvpuBb2eBVcKPXAKkMoMjchSCw3HhV+kQYwbH2yHLrcXgVHiEBwoAgCvU5Xl1po+F
/gsLo10/BI5xHG7A/WpVfH6dO42ilhDZK6WUnkjApr7YU0SDc/IFgd3IVzZTkSqW5UdpdkGU
ApwWM4SBfqap//0WrNgPtkIUHKX7kWIpGgkKIokCHAQQAQIABgUCTqfoEwAKCRBAsZjzlF+R
RMXRD/9efEX126631g0T8y3E7C02awKfJ7j9kGRSjxZFVRj/RD5mcC7sE1XLUZFH12QV0ZOL
iVNd2/jpnRnuusMqhc3lTBwp6wVmu2Jl1OWTBVWu1l4F4sTM3MPB2zRXwT1vQJXW0c81iyep
mNoUv3uv7ca1DChSqqmgWDLN+kpgrGFnoPuHMH1Wi/wm9haHGEH+J4TumRUotJa4A6w5deNN
axiIRKuMNBQPCZkkckhNSAACse427qArVAkOMD5/gBFScsaD6gXdjl/C7GGG3XBeyI68H+FI
IjggvkuXO1061oGescUtLomdBKEWtRpOWdQOm0Im6Nkyovy7bfPfn4Siv4MSkWBGzLckuCEB
UgKjZ2436xppdqr/yGdVL0oD4v6HAeGFSTKWwgSpkqDb0IE58tt5S2LjRtC+cZJKlffl9fT+
ap+Pf7RkAgwZTS4H13Reeq0jYSwLWSjglzi/2FeYyvb/I9ayoqbfwrwnzPKHSgc3RQyr7unH
IEfNT/Wd0em9s8pENplH+YHHYCTPveog4QbdgQU+dSi9jy9qjAT0jTfQ8UPHr0AyVCWZzAEK
2Y54PgaMjnT/iosCRhFs9rxWxppbOH9M1tZjIRdEC6FO1N9rkBfz56YAorVCRiDRxdR82R8+
rXIhBO553CUe9AYfCBzlngFAaLqT7fc8wS1FvTuy5IkCHAQQAQIABgUCTqgKyQAKCRAc5QqT
CBi6nCwnD/4mN9aWyR7/dR7n29hvbd5HBZ6ttIuuZrWvxRQZ8KGX1PL8FGUYuoe+SE7GaJnx
DP3fjHLckog8N6bOSKFLWnL4YtguAD7XznjBJG+2ZIgpAGp6UUIJHuZszOmqYU/RWma9G7sc
EIG6YK6sQYmNqWaUFJsoW1d7JR8tV0tMTwyy3EnyE3Ksf6nXUNvBnENqX4ZKwUrR6LKjOrpF
A5aslFMM6Gs115vobOw3SLOCjC/Wiy/8JLg5BYRrawitfcAkKrMuZegoM/g+gBrHDM1gdNJi
qbfSDgRkdJKuEZ4rIxzd4maAeBsi9VfkNl/g7UfHA44pWwuBy1Sm6HKM1d7Nu/+97Dw2aw0R
aWd+ARzcFmGluv1/m4HQmwH2itWN+P8hmhvstIwYszqeRF9w0X0i6jmyxLHZ36cbj2scVl3P
/bDMDXeMrJ9S1zq2B9TInsDZGVLBXH/smEcPMpC4uN5K86p+IpQAsIdxu4M6OdLEqJPczBQw
ehgAHoQMTS0kYoH/T3uGF2VL3dX+F/JJ6GgEKgZGOnre483+LV/6jS5J6OAAev4vGq8MvAhv
EjFTarHFvfKnHyIv+ZMjjgd3ZtB9hRvTn+e/4a4d0KTyGDu/lAGCUNkLPVFfzaz8uZU7EtCR
kfmzT/La+Dcm9guSXCE9gYZ+ufmwv1RLRq9PDVDb3h08bokCHAQQAQIABgUCTqgRRwAKCRAV
vIM0QwONjDxcD/0a0Pvy5fU0/6GCNGQ5bvJckBf+gSR6aFvoGlrhFtgPbJS+/LoIIanaKekT
C0k80GWu6sQRMqZbVcy+NV+/c3N4YdlwTREo5ddOBaZLYeh5um500C2g3fXk/dlsV1mlG6+V
p+Hd/zLDojmRWwv99kMCP83l7gA1uLoZD7peI2enssEf7X+9dXTBww3w6wYGsbq0J0TuridR
wm9nLbGzeEsDWxtXErruUzKk88e+wHS1CL32bz9WaoGt4fy6NXz0uCJmfY586IBz6f5bgLy6
ZuqOo3RznZ+WFcI+5HET0+DeyyNx9+U5sU5D56gXI+b88DaZracbNRyIUzZzb6fSuYCcTr4e
XpXjofjmPoCZv/fecItcX7jL0L1Mp295amI7BoFoduvdrUexzLUox05vHNzNmJ+4LNppMC6a
hhbobHrU34Mfv8RPfdi7/vzYaMT4t+Yh83tpMbTlzc+8SF8t7mWTw8JlZUkrUvNOWHUCCR1c
S7YR3GsJE40QYfM94AE/X5fSfR0mg0VmUlNiCVcHdTi8xbIZezIjtwrx9JQPNTG44lve6o71
XzFd1kPKvpbvWWAf06TVEDJt+sutHhgGkw7cVeC3d5We1zMVy9hXCunSPw0vG9yOEvgu9w9j
yKeHixbsiyDg0HjUdRQpIiTXqpBd0w3D059BELIpLvRBQcr31okCHAQQAQIABgUCTqhoOwAK
CRB8xvwzRLJH4vU7D/9VQCHpyD3Lsq4P3KQG0/tZHS9GoFgIeU/BMdIEuOUbSnxv0JD1WVM4
5Yfnk65KlnGtgZU0IIKxnzkV1fSGkk59MO/h8hVJ9MEko90TYJX+qG89mC3z8MnDMblqhMh5
r2y4q1sNWTw6PGayAs2aKKSLfaz3c2qy1OINfNzfb4YD75ucdeaidS3zt5+dK6Kwko7z6PX5
W4gJdVEYLfSu0+4hR9PRckaI/fQZxyJhjLNWdRVrdABswJ+IRPMY0lQ42KhEIcsJbkLdUv7g
OK9AwukLSs3Nmk0H6askQNjHRTJFFMTvx28elspZnjP8pAYwXR4VhygYQKjr1ksVa2V+MKDH
bcrhtQY2B41sBIcwILoKIYa54D3uMXIQTV8rHc8Lqm8pDeBi9nvSgpSEr2K2OWs+DU2GAdaY
DPsdSpFEiEKeGfpxWQof8VM/ic2n9SOFTzkOzRjPAgossYVngSA1B3FU6sqdi6LKvUSwUz7h
GgYU7jfTRLuCo9Tk15TR/U6IPIzF6nXNZbiNb//ttPmcEK7Qphq+U9yh0WP6X80jTax7y4PD
Sc/LYYgAbkOdyy6+nmthzOA1HwhnmziAUd4qTIZUmgGqXfFFZUt6wVtLqKlRZEPKfvBNBbGs
PTHHfvWkcHBg0T3w3fxV2yuEkDebF6TxT2sgfugGxwXptoP9S+zTe4kCHAQQAQIABgUCTqjj
3gAKCRDHj7ENC70qSyE/EACBua8oXH1SPleZaYd6IF6AxAW1yiXfVy74HxK7oQ5GHHhX4KNF
TxKUcEl3pScVuRWDxPkKqW8cGDMBrXMlDCJQYw+VERLwi3pJUNrstZctSUy7nH66aliD1iyL
10ooWW1oFhEkWDM3WzgHPgd4KFnsuvUZxBCz4Tqzv0ysxQ51Vph7GGwPC/JNsyMa083fd+H7
yeBTiBmVOS0SYth3caANmXj6W94rZQlQsHTSRbnqmH695dwQsnbZSfIQnWIuEvBdKSzgb3Z3
l1E5itjBnvlWJvckxqtTqO3mLiv+iPoEJ+r4hFGocKsAk5GdEqTUwFY1yZLc9K196Nd8kzBF
QVU8V7uBKrbRtR1Zd4hbBnQzX27z6Rgc73t8q50YDKW9nlPNarX1+ZVucX+BfnhRvUDl8f6D
BsFWglkF6dA7UGC7P7pOaqx9HBruQp4zfAqcd9iYWXzcnSWNxQX5GNUIvYhakX1y72duJ5Vw
zmU1CSbDdyM0vWNqhKoVGgXRQpwlLsa/SwA7aoN4zxh6UgZ7Mihw8/99JE1tPueRSXlWXx1Z
treQy02Fp8l6jHjfaWxlSZ0ht5CTFjChlxjQb/642hMixKm/nmdNOoLtDnLUR/C9Rhkr6asj
wv6i5X27pWLyb37FxmIwFJGnRFjMxNRXZq9Jnd4VCNb/p5WFWxh4PPuK1IkCHAQQAQIABgUC
TqkfMwAKCRAb1D7JXKulc19READJ/l5u+IbopmBpQEUAhShl0vyHZ9AW8IyS5/zGyIQ0zgwo
70/+0vzVGGBfOXa2almUSmAisJzq3GOg8D9UHgeRBdmmdIzMD7SQ8jCdc5x0pZKVcXbyL7fg
QaNko7KSD4QktO0If7oYC6QBOs/V2fsaInugmC1BdTpZmpePJJweBwUeR6fjcPm3pKbvYADR
nuh9O9bRwx3GRiu4Y1mt8q4Vk4xsl/c7ddkrlvKso0TKa44VKJzp7+9ksIQzrXNBQYWOl9Jp
+hvsb+3Z3oxpSYx7lGXUBq2E2+IFyqAcAH28n+UnbCiydbMYbdUCZb/5W07wdXmVih2RLK6e
fs4mjsKKjpIl2sN3qZ345cUmscKPEIzrg2P1gMOxn4/I/VYih/6lYqkSbW7PJbE3vH9IBx84
w8dBlfc/gDGMr4F3mrJmp62O+Awhi+r/TreBQbSuaVd5R/uhtLxURIqgjM3kqp8TBXYJhyMx
EPIjhT/vEvByUHwTnYQsa1uwkxPu5lVphwO1D3P1t/X2qPpgYOwDEtzPnsAVUQUr/gD166WW
Cgk0NDbbDfNZO9tmU1ql9/nl2SidMvto8ssLLe6c0jgVUDTCxPKX0yxFU4oCoFP6YNyy1kI2
YI+4mOwCFw/wbjNXMbLnOnPvrbUB/9b7+xiinBsLnelw2OjkwM/0V02xJYjtYYkCHAQQAQIA
BgUCTqknYQAKCRBsuookORlo/hKnEACoBm7dldU+fTBFJ3UBitHr7eqGKCTE9Y8nQxQp5ksv
3+c+h1+5rqA3NgQ0oS/69Ay/l9xoZylyJ+VT3y0Z6c5+KZuFF8Wdw4stgIWy0QNw6XxHr+Ey
LnwZNQ1POAkXWbcRTKApenJrXgVce8+6ASo3CN709Zq3eJ0OPrWgxNqr/QNAUafemBy4f9DY
ocyVBXBL4zHdkJlwU3D4WVW1hcaMSxqrVsFsh5VkLVRcTZBp9fsWwrZJ80fNGQ/txpuC5Tl9
4uvZ1o81bP4TVLzNoFFZzbjiO8BeWdtaN189GWvC3c7l+Vb78M7dG+RgRHarSctbtFtf87gQ
AiAlBmNjYOBOKqcLu9XCuBwmal3ZpetpRgqPjsY5PvK0J5CdTtyEBmM0U1Ytdan9EQXkZDT6
WPrd1J6kTmw1S02R4AfIzpoyNsE+OzEM6i/QuRB0X3EmiH4ENxUTEZtFEj4Jy9ziMu+StXxS
Djmfw2SjiMq+TssabyvXO7WlSoiWO5aFz6RDxfurRih1qur3NbMC6FTceSsh08u3Jmlituv4
Wn9WwXdChLYpjMQ3uxmZW2Igkj4dxM7Idd1RN6zKSVQmhtSiv18x4QKBDSf2odt6Y9lt4C0H
NWAd4qs30noT1oDho5a0rdO6G/5aQeoYmPbhDmJgbU+dRvl9SItGhZPI/YU8waaYJIkCHAQQ
AQIABgUCTqkpYAAKCRAryoMKfkvCWhnlD/9LFsA8RvwaAxe+3jtezsoqlU0GAZ3S0/nN2QDl
tABON+45ZNcwL8n+mlo4qOrni13DgTbEMj1p5KG7APPmNGoVuC8Jpn2SSjMPZOjcW/jgRgVn
ZgRvKDbp7N8c6wLYw+KDNQp5eaOsF2ymI3sAchSluG9MRuKy0wCirqk7DUcB8ysOjQPKpe8y
V1J+mNQK1L4qQB6RvBm5zqnA6AfC8XSWgLyv0+WA6RUaW12/ZHpfK+CKNiSPUq5IGgfhRJXH
9ftQzfDzOn2JLZ5EW5wnxCBnyWvNl5aMi4KioXeMKeNgiNIQA6UhmHTb7qus50K5C7sh2lGy
bCRia8XrIc02xlBDo2nYLWPJDZtubsqCTj/QXtYMfMIiLonIfNcXkJ7K/cfZ16/2Y4mROJJF
a11lkOInFjaW9UaAmCJkGL4dptRSESVBdhCLwTKGAWKZDcuOAudxLbJcTzS0RiaigGCFrWdr
k7vEu0CcwgMBAW7rzhjYCM3gAy+bjef4Ucfv+YptGt9jLFR6+yUAcKpONG1yDrIXAoxlwgt/
eyTZOxEA0nhIpBIiOr9PjT06Sldqa94VQvH8nNELDi0EJ8P5TBjXQJti/RX4Yb0/8LS8VwUO
2yjyFSoyj2q2tJjqTVXo1xTvcw1mG266k1a7c7F8RJJ7fU97ff9+drqKjUb60SQhJ2WJuIkC
HAQQAQIABgUCTqlmAQAKCRBIav29Kq06LsDCD/0VHdspO6DQ9hahnVnNLDv3i7wVu3R7R7t2
HP9p0hX/hE/CLFMr65Jp+spI98uiNS4XJDZjNxaX/ni9/eG2hybtK5XBdd6uFoTjP59OiTTu
9xIUojDl3n4ilL9z78EPwsWpRKh9r8vJK2oSqQKTLuhpATJubWxa5nqTl30jp3g1r7haK7o3
pZUtGA+ExCjXZVhyXjl5SXFFxPPgYE2zKCX2PAhyHEa76KpzdbiqDFWT8wr5/mBYycHuDLZN
xWKSO/PhQQDBf2UVNQa/ww7MvxKLbG5UrK3MB4dR4C/6yhVdzrQfyhXnld1tJWqPdHfTYtKa
SCX3Wba77vivIfZE8qFuUEJeGHoLlUu62zF5mD5nAjS6GkLBR095lRc61lY4ypv4x21fx/vy
AyMDf6KQ2GruUaaZtp6v/KaTdAzUODBVBtOEjk05ZU78vZywzjTf7pVGELz+abu4ShC4zq2P
ICgb4+KRkoUhRARLOeShBSrkATTVhVT5ALdDcsG/7EBvCIlc7e5YvL3ayrJCAfrvalNbBnQ8
pb8KIauqhq8QjYFFK/EqLjFKtLGactYqTGsXuwL3qDeAHX1UaLosLiviwI1eU5A/2RDmXQpq
5DvwK5dvVOctWL4ekF7ful2043xMMuqLzRkaGOSixuGE+Si5AEoJPE+h6QQWZbksYuMyHuPB
EYkCHAQQAQIABgUCTql5xAAKCRCQHIfw9X2x1Ln3D/9BIiTlYSXJbgvO3HhQpiL55JqgSVBG
rN4+ZAPE1gZVeBzDjfkItmlYNqa0GfCQRr56/NIVRpFn9fJ0uO2Q8vRA4e9IFnEp7tRFZkWn
fZPk/BHjgQF7ImriM5B+eH1XuY+0SuSibwxBUfanU2p1xM7uYZApWsrsFyrcA2Yfu2h0Ejrj
hh3TbefPrgc1Cq5/ZgkF8aH7uZIm8PEV6XpP8JbnDL1UjV8hebz8pbCga02+3/7c500fN1lY
kqzIvzl5KhwsAb9v0QRBF2keguVLe4VnMUw4pgGu3YWC3dgqYtla5XfRPcyGq1NWRgASPWic
vjjrwajiyXo6Nr/eh5N44BKbE9gVrb97HpEr1JuV7DBUIqWG6MSG55myoaE3yjHsI50FcM05
aBsuIzXQsQLyw0FmjypH4yHawLCeeUADLp0CtaNDgiZ8/cdDgQhICUsVKfegZoVgL0iYbVxq
jkw3f7qZvT9PiowZy7PrctztcfLBsNqL6ZQozeA/wGoARNf54yb7BfNWujsTTLUydAwpfFgS
I1hWv3sFuJ26jVx1za/v2PuuLwlXeWem/ULRQBL9IdO+UiwW6o4SLQh/kSITlQpUicjKB8EH
aBAageZAzuMjMpjV8OGW/LRQJODCMrRP+WTCvIReLM4bo6MUd0CH2egPbKX7ePfSvgt3NPYb
1VKfwIkCHAQQAQIABgUCTql+9AAKCRBsMYBUPSkppJWvEACEkoDziwVE7+PWr4Pq5fFK84KI
p9IDXdXfzaJ31R9ERGnrFzNSliIGLJ+PTcR7mXspvKO7uiTZhfF23oBgN3RExnObbJ8X4QCV
NgXKORHltvoyaV5LxhZRal94qbY9sZE65WXpea+drgrQJ8BsEC1IctLfkhL3Zab8jY/uTmAJ
MHtfG/p4aN97+HgkHt6cRh2lgiIIdlYoj0YpOwjUApnIxZJtNoM/u4a38Bwafa40xE4TAz97
JQB6D1ePs2h3NAsA+4avHZwUfCfYXHCy05HXiqlfW6IVi2xNpZucP7qzZ3ydFSQdbPvUttBJ
r1z7ijI/hUcBN+9M6PqA8tuJmt0IuiA6Y6wq8r0Z3eCtpMsyi+K3u54zyADwvmkBOrPuCHp1
TBMHHvFbXBk8oRUyIRRSPIiZ0isr8u1k04tqJhbDbpmJkiRT+tEb6nRsRWp6QxqcWUUvmtKs
OJv4sVHw1G1lZ6pFZcFLIaEFp0/QE8VGJjlc1TwnKGy7PzaIsgij4cCY7FWRsdqmPu8XEbjO
ZN99IlOEPwtzBFYSgl9AkPAw2Nc+leq9qgYO6nKCkvMBJiRdMLsL0sHEQB6ioZPkvpjGX6Lf
DW3k+RZgm5mH+jNMRArD8xFgj6gH6hQ3X47BCHXxQay6I1S/Xpz/D1kBF3TeDIjcfOMiq9Cg
1PCNFLgX44kCHAQQAQIABgUCTqmI7QAKCRClUmubs81OajBtD/0Qz8Qh7tqFVVeYt8gxq64f
QXCyv2+VgOY8gsfgm0G+TpRuZc6DTYXwNBa+DzAzi2ss9Hf6fGeR2o7QaEGWxRCKJIPOPIG5
5mJ6S98zDzMarmpZdZJ2HCXafYopT1zvhpvOMrfRPnuHb4If9V3lcPzI4wvhsVOawrH7OqW1
B9gblvddfH6UHvXq9w0I+6SaH9pR4PFMxlHvpcz/o2tXJEjyFCr/e4L1osi4RXkFSPAdvlsg
RWntgbBZ8fLn5ln7kdPCLIpcwlD8c6sbwSTen5iOZYCvf0hbG2qLCKAsuPn9otWam1KYxxAW
39+X5MmbfXAZyNGIg4gBPdcnwyO9iDTUQkK3qHu1oiLQjq8xB4It89/4IrvNyHpIpn7OpwrI
6QDkL3o8fLDp+AXjidsoWQu70RXb67a9PKeYIDOnzQGPlYmegTp6FRkq2WjTryaK0ZXhtjJ9
gmXFaBrMWROapHDNMd/7CCixm8SJ5/E4VT5MCMVs21BXyG8WGpyi+SzRt0iu0kAAx1QvIYc6
1o25Uim2rM6JjvIXNH3x+bU2d/nc0zxrhQEqae4OypSGZKP8euz9TICZesLh8z2iL0n2SOfi
I+pW9YI1Pvye4HBKpXrlXzYFucTJj74KJAeKb6KY1ZEHWSXz7TrnWJJfFhyrWWRgeIGBGFHZ
H2fSgJ91Iu15E4kCHAQQAQIABgUCTqqELgAKCRAH0EU6Frc2F27cD/9rO09pPjdBksuiqoQC
53654sq/DERIT7Ic7xKDWwUAqcGqYbUngfG0CrjsRQf50y6/lbw3Pb4tGTJV3D8ZXaZc07Ec
VJnMxcpY9bjRIdwjOMMh4Pk2J+AhnGjLzmWn0hrzvxAIBNzyFz2qQJi1e4/+W5KtZ9dAglRe
lhJ+8ts2S7eolzYXqO/9gXqhJiC+k4ZZ9XuzqXmbGk5DiSRbzZS13H6vYvdIqfeCJRAz8Thb
eCGgh/99VbqLKGWTLXy80+FQtbKAxw/tTFAJ0F3++y4WMopPUw+JAhIo9W02+9MLC4VQc1Ta
Rs79U459fzCE37mlosvRV1amiBpntmPdASh/5gq3Y3h3vh7eqoKu3k9mLDhVjM1PcAtNoSrU
JfpDGF1voHZDW9f9xnTytM6kSfeAkFf3rNTtxiZyXr0aIFRCb/gCLr7NC04rtJzxI7KSNKHo
QQIawITHg019+qouseB8UaVhpqpkcUQ/5Lc/cqbANWeb+zMmkz0kTkxco98vuP42rn5Mqdqo
fAgah7TT9vZrPfjBwovR1RhaJu7I5wtXE9Mcy66758b1a3XxUc86GaloHoB4ZA4paGmdVaio
HTBfQ2PwuEjw/IZj7TsrXiiWcyPaXT4clCC9uIf84bi3o7W7Znyc/+jnqXyNDc84tV3NTQfm
NV/QsFLEdv3YyGpneokCHAQQAQIABgUCTq2jRQAKCRDp2ZFyP2B6kVJvEACvTmvruOYNCCXv
536XGMzEDE7CvH3fEQLp0mehhB19hUDpNNqU/JbA6DZLteIwH8ylvRbiQvEAiz4hHblqSGFH
r1eWFIh3m5jBTFTYKTkVuc1OmEnQUclxkyW2X7CfWekeO1PCn29MCOgMyPB1vP+ZAl/pa62X
a5GbnPR0AJemN4kZXW0ykQzTqxYL7eNQWXuxqpfJ81AfzilEtgyFZX7lPFhb34Lt3rHMzUBk
OI6FvcP3xPexgKD3dRZc5odmBW88ceHl+YWWFph0ODRvm050nvUEE2VzakBjdy9kG4mldtiK
/SwHH36UkPvEnQYxzMkIW0k0UFF25zfwpMqWGOHCM4POGyZ9kkqGpIQhtcvJWXHlE00SFiCi
3lnQHRJHCFVw8va7s6l3vj1vYuxVHpJSDJ8GflVNjlRm5bJQtd58Pz7jgNsD5jhDppdIR/PW
IvL8g/3eK6siX09T3kZMDGAh34nGUV2z6qtOIwUXzDJRfU9bOzTrlGrXjOitAP8a9dzjcpwv
kh8iHytDFD0hqBKSdni2NBdG/9yWkyP9HyQBSGGEkbbPsQp/edbcyLwcRgUJ7sndHIZfl4Ks
lxbxD1V/78pO4Ucv7Xmf+P4VJ8Ma43dDtDArWo3d3fWwBVPEuPNHBjo+VrWNhRGCvtkycRTh
mk8czSz2XvUZ/hRobv8584kCHAQQAQIABgUCTq5RYAAKCRAO5w4nc/MHc/qED/9mMORsi3Wx
DppkuEAz8TsfidwHg+0nx1GGiRwqhXb5MSsHhu9fLyFIyfCdYV2jmrJKf/bdH3UscaFVFywi
yrYoKePlQYYx+E/bL+gpjgYq+OVCgxldQPUvzUlmMSa1pKzVHzSE/4Aab71feJaENPL3uNkl
kkPrfQV6tuMZw+bOd0A32gAIpuT2dh7DGaEIb85bLSbQdgYm6HJj0nYXfs3laoW6jwGwjlDA
85MCntIqPknIbrWyozQejds+u9RsqA2FO16nRgWEPPO8uiRBImQYjtJofk4UXuYJHH96xz78
Jt/SC1TqbP82uY33uaS6sX/gyPuwIxAfX0GH6Tn2D+PUBamVqW4BvFWQTVG4FwQE7jYEREZ8
Tu24VFnDucoDxvXCknaQM+RLigpQXN616yoET+y0GuX2J69z1Re2sRax6vV4VORRT0G5YAvt
oWUzFRAX14CGh/C+gIQuQP3tyawWltRsc3lIXVhFMd7hBTr161jikZkPOfzXnPqyy7mF0TkD
ehMbvx6hz1DLwHmLtWdnfS7PpalDsPksIfV+kYRAXqGZDjJkDTQ9T4JsIq6e8Zhs8D4gNhyC
rhfJbpNMWU/w6yPyDDiTofGZSuMqLXcZG2zWXHAJKgNf3NmoOUgJSssNkILRvZDNSOoJqvSA
JximRhQeFfw5Us/IfvWnqfXaqYkCHAQQAQIABgUCTq5fwQAKCRB+gLLbBe0jbQUFD/9ZXGT1
ougJNEbjI9rKizPzVphCZju7BAcDq3JnIUIxLOZkbqlYbIdTBVMqhmXyYZ7YuJgR+bxFkIm2
j5dIX5GFiDDOvlnCNCR1IcPBr/t3YRxc/rP+2tFeWmkFqdW9kYRdZIAt067vR9V3pAXkXAMc
ay6aSRS8yw7SilVJgtd20jocpb1ihxBCe9uZT+ZbXTwT86FH7u+PeK2E8eRhilTMETEwQ3Jy
GvzxuGm6wljybFxUuc7d7TY6GYBohBAE6RudkRb37muJYWz0UYJEp8Ior6bcWpgh2Ke2CF4V
Jk0UkAZw9PCTgFQYvrVjtgfhty6c4POO5hiJAPXWyd8oRR1IBid6ugSDfvcBDUWgtMEsAXrH
80sFGbuw6H0mQ2ZrE2DL9EJBn9tFZ9Qu6jfTUokJnpGAQ6qsYMcfMAyfw6Vm90+Ao8rvCb08
MT95L9UA1L+zHVHgnySt0mzC33KDo3pVeCEoN8HVe9IXrxm2q2EWlChRX2zocgeYtAskLAXY
QLnuwtWsZseSxbBqCkfL1bGEK32nnqR8FTmCuGTnzrB5gMUX9FX8DCdCwBBNQnn2IYXdhVIS
RQJSnIrGSZn6zoWEZPVY0GNEj/qWvvP7TvBd3dGr0AZWhbkkBP+PhVvoeGs5Dg0mou1INcuS
WteDcyACPRVX6/hEMgro51+JBL1EZYkCHAQQAQIABgUCTq5f1QAKCRCZ3UxQwX+oQ9HQD/97
iTuxPMr/L2xGjU9e3xc/IHfWhUE8aamvFz2xYLbM8CUXSoPQFLCs/2BbU2oywi7yKiLefIxN
VJzLV4kWWDSxl0hx+5VqFGTsJaUfltC5kLwXS17XhLrd10JLJ/EtzctgFPIhx49ITInKbMbD
fmPZ7aHNqJRqmSj7L+ujTC0AZX9ega17omR7csycxYg4IkBRzaXSMG3oS6CdwMQOQZh+4gZj
Fdvrx5qbcaolr20qxzm7n/NbTu0IDXJqpk6/qwI1p+pWoutMbda4e9vpeQ3l8v/wwdzkaJQO
sNgctCmEVY/xke0T5ltxJvyQjJO3gbSGcToHsLu+j9o2mXgGxaAhS7xMsbicY0O39epa0tcM
KC+E5nlRsSodUZnTxZOtEbU/oSeyCkcSrr/V+Z/wR/KFvHy0prWMDbTNRQ8sKFPqR66plQLY
j8gThg8Uh9oUkxBljil8z7plT88pvgcoXx5SkAJ6to/UC1Sx4lxmjKmLkqQXagir3079vn7v
2Lvqi2OnqBUk1rhB5eVh+OVoS7VyTjMY5VyvloH1yZvvmj1jSzLaFrTAgYp/XIM6Ah6JH6KN
l7ncyMvT7L9yNrlncaMWcjgKfVXtUlLLJWxxv6fE+uDnrt+m2bmuK1paxDLZ9l0iVYEVbhgi
tDNoAakZWRwtZXDnFmS6y/FQ7TQMYaL5iIkCHAQQAQIABgUCTq5x8AAKCRDHJx0KSbGLp4tT
EACeJ2cDcVobXUTJf3ngqx/4Zm2wcRehOrjJ1tBm+hec00JO1aYgIj2IHgJiaqntB66Zy+s4
Y7ivjiXvLnlJT+5BXc/V2qhu5o8zOsDi/LnEqfVx+RN9LtZqkfZV+xF4oPnO6qdvguPfzF1l
SuJJRJ49drkkTJ20CcNXk5g1VvzenMBBVxmsV3n3JAGGFE3dRcjeDP/nlnmlG8zdbt6QxLiz
LauyQ+ULNgbLlV1vvxqE4MNnkiG2qBKDoIO6VZap/Ey/xAloQtLEuRycsVd/X0vx84BVaWBQ
0y0n1qOp1wEIEEK7sU8NoomVEBJKWyi4uJV0gshR5zXqk3N9WRR28hhJAjHN9dNwUkuI1uxd
kJUeCo8b47+A6xYKu3Y2bpTtc9hzAJb3V06w+U0rPRPWt+5tPhDD6Rc5ZxnDuQqBlmbcbBsc
M9FTnbMHcM9SK2j+o01QBnJPNf0I1IaeiFqjDTD1RghXDp9MaZWaCy822aOMT2YqlpGFgMIT
bApfHxH9kqiStpyfpJiZQPWqQ/UeQGq5c6z3+WVoZ2BnT0TuCBjWIIol/k2pObKfWyrP8h4W
ExZyyyCayzCfMpabQcSwruJHCESy4FNc1jkIlregPdSY3CMfaYtCkJEww8bv3qvruQBtm0bq
OSu8vY7BPfUSLTIO8mNlWLgGVdu6zsfTIOTsfokCHAQQAQIABgUCTq/1RgAKCRCT5QTI+GMN
jTpeD/9ptyiWG/EQhQb7qrVQr3Pk3fRruMHAQnHO7FSEr9I41wBSB+ck+YT2iuIShH6tg+K4
nsuAx5gz0UjBnIK0YFXVxl3v89nkGgb0umnDSXBBNejQI2DJYQq7pM1kbLWm7xlB7k/0AjVM
7VESauzY7xmIBXDg0eB1FP39SA8Dc5iI4dShme4wahuPSzFfma6Wjy45amgooIbYBem+pmos
ZjbkA6loi6gB+MtqMDWuOfXtIOwAgFHCJlF9ZQdoDvYugecQ2dsyf/lh+tbvao+j8MsOcxnz
m6NzlMRGrNPcvRJG+zhYDn6YQWbNPQchC/VUpgaAQP7iydE4nYqRFxHNYV1hPDK+SpHnGnV6
swWDqUifTmCyfDNGsEtWcZ/0E7ipmhQC2fdH9QblIywvaaxN0rESmmcwNc51l61WapiAn85Y
zZOtX+zIOFlB36SeiigHHPlE1kx0CzqkZWScpPB0dWsfWFccAG6+fuIZ1s5+nsNiFP2ebcPf
GhkJT8JBo2PnDMc2+lEMroIYjfky37/Vz9ZCrk7rIzXoltCRBMk+dKJr4sJp0gW5AjlgRXX3
66kYa8WHn86vacg++XUpEPob8JKnMbf/RoG54G/q1GJ81PKEmOiO6mzUxHf3WXCT6nAIT4bK
GA0vruDm1AzvnGrcrd1YkHwgn5qBqtRyFwabbWtmookCHAQQAQIABgUCTrCOawAKCRChb5q4
DLLTlVeAD/wNvnidcTpU5L2+2kzdsx1/OtPxdfJa8jXz3wJYYuxnbHYsoxIrwVdWc4DuS+m4
wxUuhNY+oXczhEJhBvDEfPr0D3o1mUs2WuIPPK0huwiHnjhtKsqOSpuAoChLvowmQpoeFNgV
bTTRaX3pqab0wZchLPziI7LQCiekExeOT9Ti71Bh+yj8o2clUPXrv95Ti5ayY7Wz1l9JZR+C
lMCjdvAajEuzoj1k6YZ98uCdubLqW6UlmStTpTTppJmQ6xMNRsCb7xAbmoi4C08/Bbel+KBn
R7OjjmjqrPVeaNf3Hsm5Z4/sAwgrjIQTG/YaUA9muHk9Bog8WsBQXbKUw1EINnGmlG5UZ7E1
YUz0YPIu+Fu/Ljp1a46OsQQIjqiPlEJq2yjXS4hllP5iBk6Nvn235ZTHlGO9b+BeISkxthjq
sm78zg0PFyzsZ5DblsBK8qeECPzmxxqRCGzHs9MuR+M/CMU9zeQZ4hVwnCJrlnkg7GAiFWdc
23+7Do/h+M1NoxM6vcnQgClv20f04yo3l1YWyAJuHy0BNHbzNIYkfRp3I6z6xNJtlfYIrsxZ
MA9FfAneENDWth9cJ6bwzmmWrKAe4wDSdAnGwOrwtImE/21mZI5u3Z8QcojJGrWvsBHvnL0h
KniODTbDzZvrEgEO4P0p+9c8PFUr8cReXe1PBK6JtSn4vIkCHAQQAQIABgUCTrHLrAAKCRBZ
Nxia0/vGZbSpD/9sYsZ7TN7/z87+HB3AuqCZodd3YASLRoub0uIrXEU77/eij/lDlvqF8U8H
yh7Ctl+Vv/nqxAbrMUNd0rW1b3YpR6ovnRMWWEOsC6AcY0ayu3srh18tPOdBlM8BfEYGrMjO
M2FhjYKpcccxyItf+zRJL4ln0E370CrxzyDGelnbj9Es6uPlo7O2ky/vgU4TeO+8SdIP3szS
uNmeJyBbl7nB5+7HksMgkZrTN8rNj4Qn1cGzDxVKftFZgYAIWFl6JEpLVgV5aG5xSCHiNWT0
S6G4dT/qG9Sli8eLeUpllpdIRrY1E5KnlEPmfMc71w5xLTEBjNEdrKYXtmp+VTzZc3SQBaBs
PdSjS+lBSJdQSCAokPWfGL+SV+TOWBCgheQ62nD8EfFFUH+kV6FHc/qnJDpKSQ5ARFROxJN3
fAIfXN1W7B+sjXnrfimQ4+K/8Auxj8i5UfXDvqDiFQPYwi052BUeLQctawNtZV3eCBGDGNvN
HXr3SLG7Zg0y7y02YA6VUFBap/fTDZzuB17agpwaejc3opVkxSipYu4WnOSxYCm8KAoHy0F3
4w2rn0TGdcYSO3iq+xtDvv0Nld0qc4A6W86TnrXKWOs7FbSej+tk5p54AvNVK82uM8lnClwC
SOAmtsWqvAnB9p1gh9fkKlVNPZwegtj+jSIdQX5i5ftECzZjpokCHAQQAQIABgUCTrar9AAK
CRB7lugWKoz10UV1D/0f2IcM1JVYhmi8uAB9G/Udh/i+7s8yA66tzGlSOETnjNUSJXJTDY4D
GawuY4vpnFQMPNJJRYMDksEEE4NOeUjcAnm9Pn/35qnx82sq16iAn3Tc9VimAy6ug6Adunuh
lquyfyZZ10vkg9JIkx/yM4HrpaQyBjUKpZDZtIJa+lV8N49ZH5aYjOOUoDjtDgOmDNFV2qq4
LXx1BzKAt2JdxZA/XkstpAXl0kNjbPmoNh04txcKK8wLpN6UXG5+/jHHuvj3SUw9jPS4D72y
9UU+y3wstBYco+aA6buvaiTUQ2ODgWQKPS2sTyF6HeFMSYuQCzfSKYgYHtLL0seYAVjSmlqe
sALP3OCmGeDk5HQXg3I6dCbUKg/JmxMM3Ad7DvN0eCXG5D3gpI1TvfkdtC4Qs/TOkbzBYuUD
p9TrM5Byjhv6SlKhCv51Bnr/cgXjMRdCCIw60inZnPjCIrotfAf02f9L1pxhHSmcJnaO8ait
w6bLOskKa00DnXCEQ7J6GfwVCpPdr33UMT9SBxyZy591FqlfG7JVwGUZpoYIpgI1Fa4n4LRC
vPqWXRQyCqIVsyFlI+VEF6I59PrRs5DH1+lV1Y2zEk4GuQpClTFnuqXWivqYME/RaO9hdhlB
3KU0xhuVX/xfeChl9NXS5dzjYolpqt+2VDHpNpzyxpQQTGMB876AoIkCHAQQAQIABgUCTrxr
KAAKCRCy6JTJU0SKDN2pD/9qxwe2bLcQlPnqaMRRlpM3/CgqBf3csjzMuvTDdTdqBPwYGrup
RCuvVOEB8rJUaMg3GLBVCd8pBnuhqHSYFZnkBqvKb+UYqMxO6wKU1WesvkMgh2ux904cTIOg
adOT2RTQEq24oFnIDgzX2qS6chwOo8IrT2iiVF/qwD1JolHm9zolm6VCGV9GWzDe42MYFGH7
rT51bzMsiFUEq1i8oEDtas5RQwhqTv+Eb5jECP0NLjE/rfV+upHgtaUgs6/ukgzgxRdIPd01
64ul9NMMQVL3pxb6pUziRIpii59pFAMlEpU/LcxMqakPt89TOlvUo3Bt18oEDhul5v/81kDB
5c6MxXV3bly3RatmGKreQ+/6K4BbTeO2ET1vpqJBFtR7u3fH1d2nEDjmzpr/xa+8IxaHtMWc
4U/tVoBT4NgEwglzphi0bN96EXNS1c250bmPUflNS5DMuyQNOwM3Iq7jXGkk+OT04VDJsIGj
9kugCXV/FDnvsCTpjhcIEyWXYi9q4/oE2/zEhANwqSN7bk/5Yo/ZcTIwVLdBL6g+IfuHI8Up
o2rhJL/czsI7hxYfF75Kxb/I+PWVNg84EGqyqNpNZTD3pTDSR4IifLo1EtdQ8DKOLfYSOGik
zbgTmi78UGWSjKsFaC0E0uvMkUB7Lx3UgQB501+TeRPM03hGuPeUd0G+2okCHAQQAQIABgUC
TtMrdwAKCRAyJH+7QK0fpj5lD/4nbI9uXwXRXP8Dex7ixNmjDaiK8cL3OJyaHgbQ14884trg
4tg5mfyFl4oKZVtMML6uK3+XnosMyuAly1m1w1no4m8RE3qMwwf3jFz/LvfdQJqllRY810FN
KNWPkKsxXA+AG6UdNLd+LlaB2nofRSsstDw7OMC7M/hi2+nK6Tcgot8ESm8AXlswim9YuAH5
PjuW63z+HSBUsrCn1WMLszHFbHDwA60vpw13xl/7anZntlbB+NynO3O3tSnsTemvSS+jJIV1
nHb6a1fzAyXh0kppNKMBTEbXfjcQR7mwFR2DL3p68t9nXR9a9BSxFTnXUUKDLRTxURwkQ8fY
F1hX3cRhGABIDnJqQM7eWv3deoF/AJEwRZiLvJdKlH0dCI/LCM5hgRJ6Epos40H0qXXPZFck
ualw5FPqt45GlEcfdIvtntpIYfNpGDJtfy4I5VNSbfuHWaxaoIgV4LBmNB0by1u0uCbNp081
eZ+Q8tkuUbsRkUncbgq/XzvUP50spoNWtizkmMGZQMqid8OOTBIBx/Nuy8Tw27GhH/xRy4yi
GOMASMZ5CuD2Tlp87HeBCcBJVJWmxNTAevaO3oRM0TP4N6oarykLo2ohjootyrwqMH1HAb0P
LqySYnFXxBE9KdC6d18fCKeyp6170cOPzJLzrmwp2VEULbfiLW+wS+qtXNACs4kCHAQQAQIA
BgUCTtQzmwAKCRCgjGIsCkVFOuGCD/4+URJJVe579ihdxobTeXi89gIBx7EwFiXZVWWLGWzK
fHxX9PkMuSamILe4FOV/B7FAV0VfXoglxm9aI8qGZXJGIY+sw8AcRdPZxBKd8PIPX5cRE8Up
o+pml3igRJ8OrEYDO9lO5OCobameoZMmfEaJZxo67afv9IGXoTrhg0pqOLAuA/lnhvrOQXFE
J9JhLyT6mbUO6u6rp/8oLAwwYsXR/pn8fYmBDuQuw+aHMEr8lwizhoqgqbaudi3J1RGq5QZ/
VeOoqEAlxIbGLa9FpSU9hVWuMedsYUJ9PxqM2c55I/WFWbE9695NXIm7HV9h8A4qqKw3A3PZ
Cz5DGWljG/AixJA9GJ9HhmQgIvShL0iBTELqaWDU8S/SQN+h9dh6y9/stDlQJfcvTP+8Jdy/
Oqc1ADHq46femLx0gWw4bph/n/PewMQocB00q2QS6DW444cow8VjyvOhQPqAXGncgLoROICH
AuW12JRPmo3+1cQrtndZ+9XxVy2pijN0+LHRtwa5dr6HrKZ4uhtit2VizZw9MjZV3v33G3Ol
PPW2AovJHPmzOBwV3VY32ltmj/y2w9c/K5ytwgtP5q9pdvjeQSHuErxA6EmhvI32aD3vyM7a
aqj0L+VPe2fgAtLJJZFFfS6OLWZYG77znDiSCEcQ2BTAULU+UWxLAvJX0MplXMhesokCHAQQ
AQIABgUCTtQz/wAKCRB4qXP32VQsFmp6EACyte/iWXgmmOen4bScN43A9X4JVgy+WNVZxMtl
+h/RW0VzZGcQUjKH71Kl5fUU9ac0PJ1L8gitkJVq5euYShtc7Rn7zz7X7AQD2+ZeHMhxT833
OdNJPoshSPJDBKbcQAn4RoEqfuZNgNmj8gJjfJJhw8WytYn9E+ArFlw63A4fS+/0NckMS6Sf
3ItW8HuAtB0m0/lC4qxymr2JGSzjqi8PN255JXaY8ih+3/T83fPEEwv3GeICF//bZ/Fys6/a
rbZf3BzqlxrCJ7fEGYmZfpR2zbPaRx8xc0qFWO0xOD+pKiWt5/ZTM9+vtcAFtQ61dWJ3AxVp
Wqw5o954J2SfrocDhugDx26o0sOJy4XMXlYL54emD58UkVFfhllrSoqvYrYWJujcuiUQeYmp
uTdg1gEC8WQIrQoHohnrxuBFE3oNd7htBtfHqqEJxSj9q5NrFYcsaH8h1r9/ndN5pS56dcoa
u1RKWxHlckjfvJkD5vXhhvQXHF1wHjze/Esd/TiP9+VPvMXpR3JGvANL02obi/3RZSSjO6yV
7ORI35BW67ZrVzbulabeKmHEDIH1bJrSwSffWCtu5YrnGDB/vZcLo0/K9Si2QyK6+2+QAtOs
rdBCP6ScuwBsJAlKZWSz6FG3Fe6Ran1ck1IRnf6/l9Dkx5xksnKZRcgJ6B1f8c4EER4POIkC
HAQQAQIABgUCTtQ21wAKCRDXz2RpajdPvjvnD/4+zuWNHY7fRzPU5XfUWGIB6kVpXiiHqvEk
ysrY2o/rirzV2dfYCYerTApEVUkNuKcENNTGz3BzqW4zixUZW0wZpueAWgqfvn8xsKR7a/zU
eutzVWoWXizBVUW0BcxcN0eDcqr9B6JxlGv2dH0SjNwjLrarNr0G8lxCCYmHGONLbaGUSh7R
N/bwryFZBBdnkS/HibqcHWFhq1qndDqyKzEDYVA89oq68qcDyGQwB2xvMmjh2Qg3o3zfNaXq
rejKWbBzsO2jgKcMWmuuhZzzDUGyXoEsQI801cO61GJEwyYyruKmfNrR34NYea9fNxL3Emag
vhkE0STmfcymPOD0QsNMtbOMjJgi+94gA47l2Bq3Qzcs9hH8ZRE+mJR0Rhe//WXNVg5cH8TB
abZsrlFlEG8weXPzIZsP3m32YVSkwG/2874atVFY9yf37Zu/zAitfLB8tIEP1AqpIYTKlVA0
8bGKTGBx++TkQe3pEQp9qY97UPoLUfHj7LsrrIOp6pxjieJXYZrFyFGgdgns3UyxDjlMZqza
saRjbEt47XGAztaj9CY0QzE8saSxXC2VOJXhZFP4LYZ8E6wK55oLfXp3hNFm7Xb0u++i3iDA
TiS/b3Ks75pNyzLE37439zcLb695X1KIMYmnL88waFqRzYv6sC+YLYHeiiRrgEOFYfFDPSwN
fokCHAQQAQIABgUCTtTg+wAKCRB2R+GkS8Dkuu9aEAC0gdF1c8s8f+kO9vap6PFt9d6DYfM/
17rtYx9D+kGD9US75H8R2JrE+h55VIvZP4+gCCAxH+AMeIuLCBmKBL520nbfXq2GIFtTKEd8
Dey///2PgnwJmnGQROHNJxBpDL2u+mbm+MA/l2UNEkcgfoS3PfviMSUAIhDG2dKfTPC5H18E
CDZShDDQt53hnn8Dzwkzy+q/R+hV2wpX614zYU04e0FyMJoVoLTl09rDoYXGZlPWqFmH3LxK
C5WZDh9qDYJFAjHaY5d8L4ZGaECGQleTZgKCJsugpqUr8PaAcOx8Oyi1Ftr8i72V7amcTpVL
eZhjqRSnRimgPqJHuDaB8HdCkexvDSJMck6Ax+pkXEIFp48dE1ok++j42e4P877rOAaK0pVW
5qrdU7jXw1ZPt8eMIZZs7kxpIQwZ1rat4t+Wi6KZCP4SM0ARCA9Y2xdlPLpLbu7Q9mPHJkmU
9uGVKV62HPJ3JwTU1EmmW3rE3kaQlQKJr9MurPV83hAlstR+slnVtXctXh4SYCWZZnQXdN+a
R+GIMHx5IDM7IhhZXA69sOTwKQuyg5gWthvur6P4l1gjaUxIGscUuC1TDMPBh5pHxpys6VR5
SpMnIxeVnelXt6PiHvcQl6RwdBqVZuojh4LufH+o2xM6IVB+NsUWoxnzPwxtqQnnh+/Rs7nh
vlalZIkCHAQQAQIABgUCTt2MEgAKCRDTb3abwRgE8CJ0EACmZGgqmD06Dniob1bnGnATLk8j
WiHJgREUjoSzyCYGTPdUuj+Nd8m8bIb5zb2crjwf/svrkXe5g5BmCAiFDBQAY6jIf9q14WBE
91XjK6OyjCX5zGaQv2hhgncXl0T5dwOlcd1jmJRG63fkycB3//l37QjZnlh9Z3/qm5U2oiTt
P7ICFiB734dqA2uGViWiHih9oq+ZMhkq7LEaXGzkOMGEun2hbvljL6QpHyAaYSSgLhSxcH4S
ZHrUPEIQNjls7ICqg+zDz6gBZIRTGgpizN4P5ghVUWNz/R4HiIY0bEmhL7C5afFogABLx6HZ
xO4Q2KW1ceATalyhyHMOpDH1o/OQVEK9BdNbwhm/XO+ZNCx9t6mJDNmaVzhkXDv1K1UNrLZN
N+hu3KAVJox044g9JXS4/PlME4d5826YYKFxETtxO3EUMoro5Zl4SvXfvMWpXwhZ3TdZ2FUB
9aB+8ZYiSpZ1365jgJfl13u5WmTPmUoQdiVegI3oAKSVeF673Zfhu/aQINmIxZXoo+3nThXJ
X3HnwSsq+QG/ZaWZAW+23F8auqj9f+8FOP6JTR+0JHgFT3gzO8HqOKC2HEYKdX93sbdOOCvg
LRYe19cuy8X7bYnV1Q+bJarn41QIKsNbjXrMXfAP4Qt7LYBqM9tyxBsZ2UasZAepveReeUNv
c0Aq2ddsc4kCHAQQAQIABgUCT4+WRwAKCRDeFqsANW79TKilEACQZYeNBk4iY0MD6BtVluRk
u6r+ZQrrIfFwj5el3nX2vbKvfqXDg3e2cl960wxoOhtE5nuQwJBLLjN7b1d4tTqrBs3yKMhG
pm1oeMKyKUeMs5q1s88SSN9tjurpY/VbmswWwIWjIJNDEuGL5JkF3MXKUdrytQ+/4gIElJG8
3S0UcgS4ZkVsbkc3QSQv7IP4duccZf9qg7P3uyPUprUCdIQajC2oPF8MMiZvpqChL38jfByW
JR4pWt18gwI7Rfv+Dm4aktr6yoFzUwWJ84xzHf73GHlhxvyWWSFxOO5XFFGq69oTNkjtZvOh
zupoCM+1ppBdGvl5RuZFoEnJyUabWMekBJU7j2CCwzJNkdgW/I6Lr2WQZJnFRdhJniuJJl0z
ipLRUsuFCmDu9g9qZ32A0E7roKqhhxh751hpo9WMybeofk7PDZTrN+EatKf0hkkDiPY1HVk6
xjuUF3oDMBYI1Bn/rE7RgxpChiotYUm6B8+yDIHiR4+6Lei887Vh7SZ9rabV7tzJIRfwVITU
ehCcZvqQCLxEinURsfhatLWxEVZL8eRJpVdUefTsnEU6uEjES2Tb5LpHwblZ/ikwVS8iM7In
4HmqoixCacLBgXGlxN4/6UDp9tUxVQDPhCAJ2aGorvo1nuItSHyhsUDqBDHSnrKNc16Z3CUC
RYaGCXHmQYBzSYkCHAQQAQIABgUCUD6hcQAKCRB+9PlvK+mWKut2EACGrjaCZlyWqk5YITLv
mlolZTY+Qst9yVjAVBA+kf+XCUfjWMxvseVTamwC6mqntB2o2aP52UTQZPEQIngxC1RkjWYy
lekTOymP94Bm7kttLdTy/i0PNpwv+LF5LYOueYIKkpXteMNzRV5Aco4zZiQg4wFacA8zWI5R
zOUQiwx21u3p+dSiS6bxqc1crhKrg9N8nACvFjXkU2Qe1gr2Nm0OVcq4tWh+vu9BRXD5eFxG
x6sF0X5J4+pjGcTkXowBXS+IAgmk2LpusQyYKjaAFkOnyyuDfE94AseIOQjihwIJG70MWQAd
reL48NeHZ+jbtVX4Jh60s1C+JshWzmsg/JNoqBWQRcGxG4jLfui1ddBI9rQx3t0LqJabfFG/
timFHdOQ9U14Wuv7T6+LiZB0YqI4WS0VdhQI0tkDAGdF6+ECjxu6vwOASKlD71Dyj7+prwX8
sYrk6hyC0arIOy3n3zDDFAl3xHRbs+MLBeN47QCcSatF+AANsHL+5JDGdf7j5AgqPD0gY2I5
t7fBNS2HcsRc33bptDUPgiHG7hyMS3HtWQ42KeS25q6Zkm/q/HqorELuArQxgjeSdSo8OeDF
tuLvIBQ254c2rtw5VSDCwDUBbehwGlUtQ0i8RgEmpPN/SNntpKy0AIQfxeJLYIxoNjz7IrYz
94zlXpc8TLSXBUPnWokCHAQQAQIABgUCUGuBdAAKCRAr10MPiAa9mu5iD/9qk0g88A1zrVc3
UEQzHdNBRJ4MDT9HbdijN8do9nzxOLoZR4zEEoxZMgrT2eouDkvXAmC5emymwNtp3jAGH8Pj
PQCJIlP7MJdEi3YHTDYadBs147awrI633BsiBAeyZRILGStHWBxdJBZkTnO7mne2sM9YOH2/
LdUwNH0XquB3nSAs4vM/T+CaZ5n9Nr+1Hxd+1m4iY3Y8KDeOid4ZchfF5DQ/r0NGVRg1AePA
whMXxY8qE7fDtT7kUnkCf4LNDOwjjb0YJtiYlurnAUVIlxSeS5KLCWvV9WIA9zp3L6Evnlzm
8JoT49SpjLU3OFK26ZTz9ODX23ABqKBWknzMAtyhp7xCTMr17yYgcyk23EZ3tTuTXJ3jezH3
pP61VFPlbZvdVErSr1yXu3eTzNc2wVTpRroMOpu/nNI/WMHla4bqdO2S194c5aMU07K8cxuS
0S4/TRhEYeiQ1KMddpDPIXTEIC2A5zDrxHlmQKkqrY92nBimzbN1FUln4RyuwTuxNNP50TmZ
98uNEXhlfVYj4RCjn0CGDYYpLi6AVD4AlJj1btowPEM0HnBlSnEpaDAZguZmJz8FVgQZ+iFV
wG6X8f1aN3A/iWv9EoYu9JRRhojCdSPpFRxVKHtfncxyxgJrKCO+8+p75hK33HbUB8c98OyB
J/6eb8AGBdewb/blyRUewYkCHAQQAQIABgUCUJznfAAKCRBtRjF4K8D7DYZbD/0ZeF+aQSVn
++uHkqaQnhAPkmSEa08Y7Mnj/4Sp3GmONY9A6KCQthnjB1DT6hUWYcwFMchk0sKGP7HKe2vC
XgHFebPk6tgDcVQxp3WqoWJuELatAGN2HByWPg0agHlEuC9R5HdQcNUVjj/y29wYrnM/HWOs
M1tEpsBnd906vBiFxG430kAJU2qNVqsnovKaGY+TmMx1zSuuE8NmJqN3Tt4n5k61qA3UVgk0
5WG2IQz3Rt4B4PWpJzmQ8c7Dsrqwf1iZf1QTZK3GV59yhI3YeL2bwZl9GBtexPjZ+CrVvGXd
MxFkMfxkclMp012+wyRj3Ec2ITQyP6S0n5cLQfz8ieihPSJuxZLHo8vgj9Aj/uwlfUbhbD/I
gAcmOXlpYx15yH7pbK2enrwoNsRKk4WeOeDvUvtFZo+chgJ4mOXzB8a7oAS4kYuv+ZJ52J6v
lsOjHulYEMNVf1EqwI5kV6wOPcXDKlx/D8mzNtQ/JfIrVRbsNTOaY8y3BVxMq4tmNAYDobOi
ikV6zPU8Oz57q/RdiQ5ndonY5+n1dzIRdf0tUdOfMH3B4xxDgz+fVTqGMbim4SxK7/lxfwPd
AEFD8McRlTjuOSPgOMHBpgMIQSHCTtetxqqYS8/kvwXsK8NCEbEdUA+hQI+S7T/CkB5FQbIr
XHnrHxMwD9Ea/lsoWk2SLjL6VokCHAQQAQIABgUCUKbNwgAKCRDQd3Zr7oeFiL5JEACdCuIZ
gP7t9asq1TMhwxTdFspWnmqmvO8HFOC+2R2MD8JW+Ts38EbMgkYOM7EB9xoRtiTyvP0HeSdv
LyLdr6uFETcjws+Wd9lquV3b60EWfRyEKndOOQWFtz1dBffSB4hLL7VNmo7aedNshXefLdlH
qXKnyDXZXHazLbIdAM4YXNKkJJhiOUOIdaHXCzt+XtRhLxvHjtcCMlr+dR8A8jDFcWCm8MV2
bK3CFetOjtvKdLtvkBvWjq5tkVN7iVG4RfDJZmdItnuykblyrgDjb/wsMpfzDPyJKg+1jGMi
RVDiKnUVebqCB9xHZPboY5sbpMIW2c8sPhVxxWqPy5aTLbtMOhVpYsb2VUG7efeIijAsdYbh
n4wgqtkY+KW7gVyybw3X/AHJGNUYtBpUvlESQrCVuyleMa/Kfln+1r++rJwvz+RG5Jp1n2Hw
yjM2gMr+ZYq5umG4zHdC0rLfq8gIuoZZJN3+mRr8fDpe0KXVt7HkxhAxJktwTYCqxF3snRWK
Dm8ddf1mhnigHCdB1kGt2jv+32eBQ8V+8cyP9NYcs3sGvjEm3UIHsUIhW5sDCR0GIhSBq81W
epW5LbKUksmsm3hgtgXhFG1SX2tJ3MOre31YH+xSoeCCI84mRjesNEm/yD8fTdqLtlIwty43
AmwwaPiKFBS7CV/AaddRNMstfmhLNokCHAQQAQIABgUCUWhNKwAKCRCc9qumhuVUcwbCD/0c
ud+g5WtyO560M7o1ae6J9NM17qDU/H33Q2ot5w3jF+HbePxKLi02TAAwfq15NauBEnluIThT
fKFDuoJK7N5orGuGwqmBHQBW6N1hE/kkORN825nHyq2q0ti+CC6Dvg9CKgodxLs/wC2IQYji
DWT6h9LcaOu0vQEs1jd9vKd4gNlcS0sGaLlXh78fFwuYLxSuwm+Ezpr5D7W/xQS8/H7n50xq
AFCdl2dPFAfwXdokt/x72zeLEK2LWTUlFnjA1hXYlry1vwziTIxlQTSu1cZAW+3Gxb9T5TTj
jejeyN9W3fvYsCkDgSVEzBTn8Im6LHs/FW8kMwATZWwyIOzpoJb4M6xdsmhK1/ZxTyaUpGbw
H5+lUShpLAir9R5NqIyWYH3u0nzaOxUh7nxKAKYc8fe0t1Al/r2pb9QPozwsBopSSsb6bkHf
AxUkwfvU4x9BT3JyZk5uCIiOBGtK5FovlNg7/GY8/uVk8mQ7DrTb/ZIrJVKrA5ZLReuh0OLW
2dSWEykpTpUHdRQwFfbV2YhFwIcO3T8VR+gULDN54eLcaTUv2gQrk+i6cO4pA+3lTDD5UZ0l
UqHV9ZuWRnuTJCuQ5BaY1+8V0Wl9xWVg0tTL7HuZzPx1nFEWChlfJ2ApuSK5c0SogxabjmCj
JpyG4hUVDdRSxnM5ynbJknCmk50Y7yIzFokCHAQQAQIABgUCUii8zAAKCRAfLtaCUKDHuIQ9
EACmEbQpCoFjk8ZDc5pGzKXg0Yu5nrZ/B3L1j/qLCZQcoizrIykJdgKG0R/MosAXT4Vy+qK1
w1CEl/OSB1gKgoXtnYlXQ66bAlaFUExL7Ax28hMVh6x5rl9EaDCisngsT6VaWPKJX2uo9rma
JGIPhQBTb5rdifIRhO1BPvfJt8Ok6Ed2KqEM0KT1bedxGicksGN2gA3RqkKYnU7u/Cv7XoWl
rqKxFZTWpRjSu01P2jGyOgQIIwWht4WNgK9KdKjKE24vrGYg5iTGUjAknmR3Uzsi/Ujc2cds
cmga7ctC1L7v76yA61CW0JwSSnILhhyqYVKWbvHrbisvf1hUHa2oMco5T01FWcKg81oV+TYF
oTbiwhyTiOQ8Ggb+jdpkjxUr17DYbTIiWH38HeCD1dN8fpgWDXSNotjY2nZPUobbr8SurxEN
S6elZP7/OtpyYwKQGf2UOMb1eXAR9RFUDKA8SiYr5qjR5QYf9TXMMZzn4puc2WQ7JO5ickZD
xEGLCGnNHE43cY6d22c96JWS07Uf53oB8HyWvGWku2LGpIvZA08SUSaorAC4zWKwr1ApzZLN
8sHW2/IlZc8a5O5bmkddC0tbs2ZEghHYKkYyJ7COoZu8uQTO6YajBKMJ63mP5Mm9Hv8LJ8rE
SG55MVSkNRw5EC5WrJ8vqJTUyuWE75RZEM1+PIkCHAQQAQIABgUCVkMxUAAKCRDtjQ1uwEP6
AlWwD/9FKMKhpeL/2xzfeX0/leCr/5pqWgeFY7vrgoe6D+yy17dF2dwBTW3R7CFpaR9SqpfK
Kt877ALs7P4L8hqMnjrPgPL23F+pOPZSp6Qt64Lwa2k3hd7d6QdSPXN3WES6hbBUI9XCb0sG
PzEMgyQjVL7dx03qR5x0Ug/WlxxA1ADfadsPV4vAlGX65zIW5fTCYkRBAUNx6Xd4/vr4hcsU
GT63OAEb5yWqwmlt/8qvJnXUUXkd+7Mws4LeGDBbMaxuAltmzp0Sw2HGorl5qs1Nih3KcygX
MzSFcPqbM9+2IW/idnlciCTKxB41pZ4PgnnPT7Ia9scDo7KyMhoF2jFnbhaXRRaCR6DfSlk0
ph7GK9qJ/GDTPiBs/HtQYzf9+Y4fgMB0SkxwnBxPPA8KfC+czY2pfILyre+mvpNVFXOesH7s
tGVTU7NXD/JJbTLrB2mkOT4PgoO6bunXwBy21Z1QIe0tqiNLSSgDDgN8qah0zmtvNV4k5b+7
Py2BzY/ofsruBp4JGZveOOl3kT7c6kwVdvKEgwz9C4CqrrIJemiGqks0kIxeREae5F4TV6YM
rhsiBdDRwaMhdi5rq1rNyfnRN+b0JognzpE2oRU3kpoODZbNfHpWTMj02ScfwBxagEhRer/4
1Dl7HBMJYC088bTcSWCKIhLiWPkoc5zHI+aTUe2jC4kCHAQQAQIABgUCV4nOVwAKCRA9iwEH
neDbBoIpEACClTY7tyjWmR2fy50h43wvcIMudYHNWVT3yHAyoG4a/MfKHHYKL2pnyEWHIuwa
PQDIYMxY6HuGeasVPBJyucb9ZMONlKl+Fdo/h0QTzdkWIuZt57alsKSf2wiJQb9O9SDURLh9
PmlvrNNoXg1Bw6dc1dJqIFQV0S8OhAyJqzq7xti+hCQFlgn/giCJx+zvIqo89wphlDCjgbez
C+Lqp6yzWnx66b3ka3tqwhmZ2zg0DxEdb08rnQp+BuWu/+bAhPbJk+WabuFp7viH4cLz9lxZ
PHVlG/Uz+S9Sg3OT6IaGwgKIdHTiG3p91Fp0sTA2sqK9z6+NEYUcADIGX1okdhHf2UmLpzjd
a0P0+ItviwcTAf4c1zltScd7qiVlfjvZh7BTM2hH13y4vAmgIER0ik1Oqsn/Pk3A7mlN/NHq
4GeanuaNLDGKT74cLoicQIyD12Vb6OQt2zhazB4wKxPYhIxYDCb/LWBQFpHDlEPLYTsHEcfR
U2qx+RqzExN6roE4bOvqgMr5yKRgE/DLAiefMCNC11cqQHRSM6+rGfPPVvgnEceFke8x8mzg
VpNg3duDRX7tTXjTFaRDXiZBtUYJG8MoRvQR2YMWuMbMkG51znIpX5UslFgeoRH4Jj3NoN2J
jC6yTMdXQhE8yGK/qW4C/M1vI9mEWUNwdrF4YITxqUANfokCHAQQAQgABgUCTqVpVQAKCRD3
01j7KXHgpgEGD/9AiIeyxhPx9tUevlfjzLl9mEvj35EMJKLWczQYWdEK1MmgLawL697xkER9
+dY83FOBVDW/fTj6gqupD1kTzRNNr/Ijot1PRiCdoVC/egQTg+XcgAuo/OKCGbDh4Nf+lsTF
KE7Zm8FP9VPmCdFgICF6sUElQBaJT/30BSDu2ldGGGv+FYlr3LzUsewwR9aRlciYv3HfIfFF
kvmEIsXpOaBu23nPYAla/hXi7A54PC4ewwwEh8jv5j4iOyS68IscCb4g/LN4N6OHUI2OIaKu
Fw6GHwqgFeDAi2zq7Hl3RhKcL46BGuPhW1KtV3KNPZQ5ZJ6ZGoLNnKxuFRO5x1aSq6FhrRqS
7Pq+wBv+kuLccg/vsGOy1rMmuVFWPMrvCuEz5X5Ho2/mESwmdF293n4Sth8vfpbnYgDwqPI3
HX+kVKnnVpmGDlRZdOi8AVDKOANzF5gF8QErsE9Xata2/mzhVMSdpFUU5BJRXXYsfy76SLYG
qLNcCO2ua9BmcRNta6oTv7lw3WkBuAuTwnpdXMdmjsP9+R2wfvDHuOO13Y/4Lq8g6xcrFKsO
BeZKb1UFhxkha3/KBkcChDkszAvKIZqoBKYtWMk4pGtqahMe6BWPNwgFzkRMywbBHanWvzia
pKwS7hvd36nXk7/FqDD78oOyhdBIfuv6Jrcru0A6Tp9rA2hGdYkCHAQQAQgABgUCTqbjdwAK
CRDnv8jslYYRCRh6D/9L9QjJ6iiMmPyB0VcoaZ624bkK8kJZ8jIgtkGGk+gylet0zfLInsU2
/5t5iuqhP9DnIlQtu6HxqvTB+SMVRTZfg7Nn0XStaV3ZjlNO6oC0X/LZe65Ma2aDjr233gFS
1+Rt5xkoAeCO/mi4iogIkZcmTfk2cpyohnOZvB0wvUFi0D4BW8y4nYKBLdj3d+ht+C6R2sCt
hZR1JZgEYNnwH+Trw7Sq/56GRy1qUKxSBFYmGI5qhrQe/V+KXgyny5P+hapi4Af5qPd+JHnI
uniyl8PALF5icG4dP3JV3tl1ndqVj4rWxpI3SlqcqMFRMEnzPStzZS2X06xLNFoEymp5cpYu
wpyYo31IblZlT4BX3IW2jhRgbx1HV01sHsagpErD0xmBUDYrwxdievJgQCgIL+mOOWt8W0aT
V2a7EfWBBVsLBwe8D0NhDLHPj6Eh75G4w8Nic/mKdoO5J3w9AMnlgN6isRCzOtIKy59e4roM
Lyvk8NRKREbCXlgU7/bdT/tYXASjQNExoAB4eYmpHJ88DkddhAxpX18+WghFyZBvCxZXmq4U
R2j4fcHvJunar43v+qr2lxYG957eZHrTH0pmK62chWCBhvr9xe0Rfw9+dUbSMQlH7rdpEPlG
pNg+lru2TK1pIcnuooJF72wfv6mXV/ER7oEpa4CxH8dX9kk46YYivIkCHAQQAQgABgUCTqhm
DwAKCRDg83Pze/kJmtP/D/oC2pe2Ry8IY1Y287GNpDX18/LQCac+yG7LkXQxpYsyQph9Jc5M
nzhv5uOiPB/eXpaYIWkwQLCUiwJ2KLx8FKRjKI3hug2qQoIRGm9dypW1uG8VhoKTShpJ17fC
9Q6NrAVktLTVCS1/fnraY+U/4OmxA6jHvlWJjL2ReNBRGRVi9j+byU39Lvt2CIRzp+fLUQMV
dCodPcLjX7sPctMpe+kwOBW6y31iaBkevhy+yvIXDUQe82SfXbtYMoxWwtnea4s9RLVzUWTo
HQkkpCTSjCwBKo56FPaaWMQ3tez7VBkCBsIYpyeaVnmCUp7psbfPQpjb5fRYDVz9HU2vF9cq
ykKOUtKqmM0/ktaENiC7qCtOA7XNYPm6zbNqxBL96uTqQnDaXTcPZLVx4PkuidIWUGPNdMN0
DBNJJf5YKmD4Qn6Kpq0wXT0BUm3cGJWjqa+ag8D7Nudb8jAGF9ehVuSn0n6M8ac1Sa5Om6T8
VTLJYWM40Y6nGZWKYGUnN15MprQ1WxhNd25d6Fu8UOM1jxqrzrKT8nI9QzXm5IDKeampZsWS
SgbYWQY4a6+3L3AvPS+yk2RcBxlXiPGQyiUeRoCmaX76NoBGEkxuh6Iq9scS2eOqqwwKQzj4
WZjZ0NZvZL12W8ZDVy8lvZVO2kxusMsiMfOht1FPURcSmqVRns7TTVui4okCHAQQAQgABgUC
UkDP4AAKCRDvH364dl5DXd0eD/40Rso/RhJ0vqdhPX0Zn0IVBqSa6PZCczakEgDwxiySlA6u
SVVvNL8SPmOX9hphVi5TcPIZb6Df6rndBmqs9Jf4rlglOQI7bYveLuE9u2jABr8tdOHC1F3z
Zbpxi3d9n+yEtB7zKDg7g7ILYJrsBrWykVYZVjQGoL3HFRU8i5BoxP4XdBWxCCLgscKT96SR
SWq5/WTZrJXKM32c0QkSJuMwmQtGZ9w5wc9KBFj8he0OBzxtBpC36VZAEAm7rariq8VG6vW4
XXbUnkF/qFh6xYtVgkVa/mZ27UPzcqYsyk4JCSxoRER+l+z5XzIBuoJTLgTa0okwfI/5rtpK
quBz3izzWQTyQYYx2igswa7ImJdqn6iFom5GsHO3s04Bq5O7PQu2LVgfQPRw/bwQQ+0bKBe+
pjPXRfhIDhHz6cwiASIwZm/lqi7lqq3T/5YwXb1nh6Rwq2ZgQdz0Ji6naTK7W6HWjk24CCha
YWrlXtoKxZzNK7rhue3NfPMCy6KQITQ4eDj7u2VGO1qa59lp5kToixXcH+HDynnYBk77pQDu
GUvbR9b2XfBBgcY6AUvmWOuitzxmdM9zamAJi95kgeJ8gOEMDUtTbYZTfixyBi7XYf275Coo
JOi9Vms0xZHeDypgVqTeZIdYQHTPcthSXFBaA+TlI6W1ohKCQdfGNpH4FrAo6YkCHAQQAQgA
BgUCVBe1gAAKCRC8VG+zsWyLCzTlD/4tTt7HVLjrFLbQk66CYdj+9tXX+Gp+8e8v27JrmV1f
pWpqhH8XjnKXi+vfsk1Nq7q2brMJbb63hRC/ASgoVXa2HbgePnjkKxScoeTaVI29zwrNNM4q
SBGd9LcZ0pZmEhjPncrnRG5UexEavo4aFex7hVKQ+OeBpmqNkrEPNWeoSR4WQJxlvFn8mtcO
MQ48LuPsToy/408wpKHvhExNwcrYT5PDrQ4RfTe97VzTzA2ByCpX1sAqXUNnf4UaQiaiajW9
etTqfpanXxILnBMmieZ/pmY//fHNS/pwb6c3oRlzdAEl63N+Xh5aQqVyZ+zXrrQyocIgUQsG
fh7zkwX5e2OkwoYT1fNbM9bvEl1F2lXCRCaTXhHf+CWqJVMPTPuBMc5DWzPgr/4/6B0ezZWW
Rj8odqMCa1aT2H6baatZ9PGxLIZAHsES/eJ+7Yw8wotx5fiuwHhCobez0254IN6tRKi/9f15
dC0FjM9NIcsbK38X9JXPd2YJ3iRK2RDKiwWbszV/inF1+n/dWjmMHxisoZMx1Sd2X50HNpLg
HexxLlBfEgwa45rXj6i0scUKlFO6ps69JBDy3HN3XivigjOMiRnhfl3TgMGHAag8kUxsIfTz
nU39m76NqyZu/GVAw3DcUJSDUl7w4H07K9u7MrT9Nu95whAtmWVcZ9Fx76+0q4rXkIkCHAQQ
AQgABgUCVhfy6wAKCRBJ/HASpd4DruXlEADcx8xbZ5KYeuthpf3Uqc0wjf7+kvpTyV30zlYL
1afVJwVxmRfq5vH/DphKdNVB818VhXto0Gi4fXc4i/TuSIFgc9UXhX4j78h/TV5nYDsD5Biw
igUSZdrXeL9qk9TkPGoej+xVRI4SvVxxrKnvnwODH0bxff2YD1DZVN/aJiA0sdb0y4mFIMyV
J0olvgVY2V4H+1SJB+B/204Oy3u0FwhxXXDm3QAsquTCG6ZhNAL10tNhQepY8Uao6E7xbymK
aQwxyZtZZzjO4IXoD+WAQf95Ua7aAukUAO1NFtI01vvNsRRs2/FsWseICXOmKu60AidJVM9n
KzklKvE7Lia8arUp7GYu4pZz6n7ZRksDIx76EI++AOEp8nLY1nA863xYmZiCL9H/c72w45nR
xqYxn+Yy8GGS7WwWscj4uZjiNnL51OKmvHWu6BCMudRnpwnAYe6JZCvx2RAT98yzauRVY22/
4ZMTl01xpX6fOsjzzfgpMSQDwm73uKpZCWmIsyEj4CVb8IMkYiNOEqbLuc64JraAf9uz2tSn
iIFqHhfgX6e2ZPnQEWixTaH1QFZs8EyGi6xb9vMru3ykCvRSm3GCUUQ8RNqO2P9ZReSjDlsy
NKXvY5XdWgQrYxicI+mz/Kvg1X3St/5ocl8vueHewnhoGJcOWhYaUm8HqFj3gkvdXdkJHYkC
HAQQAQgABgUCVued3wAKCRAiGyMZxeLyALzUD/sFhMBERYMctc6AUFBHrrcBbqEz2FNSG+Cq
Wgoegkkp1P4i8sDifOvjw5eq+tsCJj7hg02dJG3xSXq9KNyVlX+aRx64BNutWOfC1kvGef7x
ZYKFaEZXXUeXngTFJol6v3dSmoV5bha/pQbs8kOxG/59ENhMeMRnMrUsIXfQRn1mX2twgsCo
j9gkZ3VW5yz3xJcwsvlSaSH7K4Ugo5ES6n3TuPAzeAqDQIEp0x/DWfGkWc9uyDnqxLZxkbpO
6j9J6I0u1RPHICoTMX3M5tRSFzjy99hs1FlcVspiq7+PwrSXXq8dTVDc2uY+sPlMh15HQA6c
XRrBDcwij6OtM3LhAFZJgeHEfoBuBBL1ZXh2dWjwnAkyN1aGu/toAOrZL78LyvbjUUgGNNuv
jb8S4vX3L41n0Plzu5NQ6JLOylrVQk9LMhYhJNCmq4o0DJahF+5RPd1xuSGLy5WsFb8IIcO2
IjI/LV2ROj8Qhg6OsnxQt0lNjYsUKZYlh6uSyjCibkw/+AwR0wfpe0lRm9M5xdi6x/sG2hON
qWcRVe7zsMhWJVp93pwlGN4db/cob7xbB9NQYGNS0c6uOUPI55wYQXoh6VmM7WUxs4z39BB3
ovtaVH62yaNpmJfdJBLp/lnTZgwyPsJQdTylySBpe6wLQCStNGvAuo9k2FJQiP+H7SUCAunF
zIkCHAQQAQgABgUCV/9N8wAKCRAQScIEtcIO1sy/EACBHRhzWyzO+rU7lDBe0m3by0C+z6Di
a/tQALcKjts4Fl4TiG0CxDZwQiVyL8mLmfs4m2KT7cHDlnJEFwvSDAr7PPj9RRchf+yW7d6h
614bOpqBIZ1MwEGyLYHVujk5KWvUp6qZS5WzdQR7ifcerfrW5VYtB0AzeQBQa7/K0yU4AoEr
NjbZY5fcbt7LGGMfSPlhl+VO2jDHLGvum6N4BvzFySBInVcodZ4VM/a2TQUm6cjDCj8p5/LT
VME9MPa01mCYcf/o5P95QNYp4wsCXOsT4jDwRrvb/OUblT7PBYvcKVcy+mu13FkMp2Qmw0fh
mAEJw56OLGHiZbDeTMQPrn6IPI1SGcORD2z0IpRNf6lcSgQ+x/Je+nU1JUbiXZxfkcZJp9V3
jLQA9vL5hTSxcp5FWDHjX3FsoPRHryFBsfbDiwe8nqawe264oI4kFLMSlJAa5atRLok/UVx8
HkZhkIRLZ81XQuQDmBPhp9Z0sogNwKZJhtbkQl7Ck6Kuml+5qbDMf49BQmMrSrkZcI4X7K9h
0xENyuK5vim1mspSAA3KKC6Qyr/Km2anJCmETCUpGWGccIkiSOYU4rC216pnS/L75SfttQW8
tItgEshGUi9kpRdDDEI5KB7XgQ1UrsWY6QpKAUem0+8ZLU7aHfp9af+YNdbETJ4Xi3chomoX
sILLFYkCHAQQAQgABgUCWDKbfQAKCRBcuS9sKD31GoznD/9VI8S5ZB9779wo7K1L1Xeao3gZ
XlWOPQ528DYSx5oDhC5DouRBGvVDAu5rweh16QRATeNNWx73iBzF/+TC3LC3wIDs75pdp91U
ELgwNNfovrIlKWq6cnhXT/S0nlsU75eAqn6bsxy23dy/MdH0GXND1hfYtjolzHvbDV1KjIhX
7z+wXokNCa4Z58NAlCsoyR5GUhB97HIQpl1ifTMerZbj2xFpguvTtjPKKrejPmjyhDiN2KtW
LrVhhq6n1s/PiDCSGKAp9P2KLFmHdtoL4o88TC0wWpmczFO4PuJMfn7WAQUFa4uMyHCiqS1x
PvGIZRdLaBaXPhyNwy5DcA+Y3GwK3bWAVpvXRfe7mGVk2NFFaAaeyTSmUcpSjvpQ04PPIxQw
PYvkF2zkb09DtuONJB9fLA8Qv/ewC2imAjq4CfcnnY9APCGe31/UfUsLE9Q8ARjGQcnblcBc
ftsyI3n2vrMgO/Oyl4qYiaJ15uIuCggYw67Ts53ybNUb+QJeSO7r7vX290kF7fpecGsWZJdZ
+7xccZ8BAac1YzhteCXnpbYekm6xbA1Fs0xH+T9MrIlBkaBTd28wTUzvI/6CJhoddMvWvWD+
pGNoWqnkC3lR76YriTYEbWWfyFs5v/noN4Jbe570ODBvkDp15IRhzefOQ5b6hEIg7m5usBIN
6GTSQbr/JYkCHAQQAQgABgUCWDKcCgAKCRBGsD0LbopzhZ6hEADBbaivPfLVQJrC4lO6bDEr
nEGhfUCnNUw/PXbYVyOK8lzY6elrFHyh1TDLVLUQbrzeAuXbXME038Ur9fLCiPIQvLxvzGHH
WK5rWsjgBu9BoN1Df1lWvBx4aSbStZZpXPZ9QJKkfPjh0zOgCljCkA6DoD1eSdbQUCvpDA2b
1AE5aux9XGbN85XgBuTRi6+aY6YzGs75e73mY71tNkmMfkvncVMdjMjnZCMtpMxZthekduTK
ep3jOaCMD8uZyZWwpggeUAn+OrEOuNi76KZvtnK6MK77CBHRedRP+M+5+ZUBHvVFOgYpr+p8
ezf2YUIqEm/JRUOYVsSLAFYcNAz6hem0Z/SvOB8izC7ZRzMbTVhVo49r6WJbdWkWj+mn3LCK
2JaPm/bCM3XntSrf1zmMMQMWdX8cGJGu+cd3naOaEuV/pURrewluTJbaCdD2LBxYhmIku2sg
Ct0z7p1CHkPjg7a040M5vALysI/Os7kdCV95WZmHLSr+oowCdhZjP8b+p3sHatKaPzfq5LS+
fqD5X7BLlDk6mAWwEh14RFEeu25rwuLTfUFLoBLNFodBG/OtvywrAoztS9WY82vsTMjmQASR
w2l58qYwtjadxZtZnawefxjhty/2LOu879vwvqMztjPfSKj7p6CBrpx6zYmuVAZMw5xirHrO
r9bLvXj6TkLzeYkCHAQQAQgABgUCWDdA5AAKCRBfA8dnwkek1RJOD/0V3/psALTZ7A7NEMDe
72Qnp5oOcLlE0RGcf5UvA/wNXqpGXl6YBQ/mNYkQadxEB/yD4U1ScMYGXokkUHq1wg4SaGhN
XLdqxvzvKt1mvfJo5tmfVGfoic0BnxEo++zQeEf0yPTxnjwkhP880M20dKozIGFw582ZHkgc
rF9Y1Kb+YkrogR7wVue43JS5MGycr2hKeKufU68UQDLfmNNZpgFPPdHEiLpcqadRAcrkmcZB
tpQiJe63EMy/D4aitxeuQwCIU7RSSuespXOTTdJdvwzCDxA6kJSzimw2/sfFS3ZC19iYOpvn
WZtHtltmcU9r0cgDqhXvmehMbRPueNXjkq+3BqCky9kyWG0l6F3hq90hWgaknulAkIaq3qzV
MkID17FRhiim5okW0ntwb/U7QjFzcMqK2NCeKzvK5wilgiwzL+akRrZXpfqryRDJg1lg4flV
D3CqOzsSGyHXmQfAlKIympm3Fs9F0JyZQYDXtDd5MzEgFOzlewwI5ZPblZM+ohtI0qSntJPo
tkO60OwS9LDW1q5Mw+BFp3+ujUhideb6J/R+v24RskipE8zs9zwiaiSC9SYQPAhFaBeE3oYp
fGb3F/3013n8Er4NjHfGlLjYX/zL7XOzrAHtLEK50CEbMap1sZQmvR2PvervE24rh6ns8+BW
HMorG0OSkwx4IjvevYkCHAQQAQoABgUCTozMfwAKCRA6k2GWwJXZQamLD/0XUM36vtusMbm2
28B9DEJ3LlZrKefmSz2FFX3prjgSpkFQbvfWcNetRsT6DHB7TVLkqfW+vyxkKAXEIyW801b2
LcY5sMPQlmyZW/J8xgyPaYYFvx+jDcugmq0yT5RAM09nYVfeQ4kLVWQNJH0dH8SfI+mq91S0
Vf6zfxSyrXcE4JEgz0ifuNNslPs0K4NHJpMMzOgb77uZeqPqf5uxWWS4xOCRYDx9w2i9k3jU
eo/bs4rWAzOMNNTl+EarmUZ/5u5TZW6XK0j9CnQLm9nFGtMsqw6DIxKX2D6ZrYFMttZWC7Go
/sKlH/10dtUstbZL16i70w1WFp5k+DuUGLdZ1T/unaRW1hDoXgA1xlXkz2mRgNbNjSo44N74
cVS43v4QO4H1RQmR1jYNhUbrSMmUhQreP8xUWwWJ7Aw+QRcJwzc3Hxevdxv8y7qGyL6ccWHo
zZgFH601i3v251tbdyuuFv7Z5UkqHmlGke7eNXHLHkclbmuZEWNQopWm4DJlY225LPZukdzX
yyT9G0ub7sBTNmsQpI3n5XFX75rgRMRQCMgkNJqyf5BozYrIEyuIT1shjV64sgGwkdCcKFmW
29KlQ/92X49qiP/gQwJXkO194BimbXry7e+13XoG6cCMM32J0tRZK9PYkShHzQvJZGMOnTMh
H/i21+y9suCHUEjehaqt/okCHAQQAQoABgUCWKcweAAKCRBiq2wtqTZrTNCED/9aHSHFQKf0
FAeX5F78nTwVQ2GPzUWehz+XgXRTNLz8Xwzpr1Z53eiIRgF36NLQyo3SJ7CrWgbi4Y2GJdw5
iG1t4LmE3GY3pWQW2GShMtTX+e0W7kpwWwZimo+Ama5YJ+tjoCWIriHS3FmvtbQs5Uhz2YyR
ylYV9qO0xXE5jJygEx5M8EmmyyTDzwomlz01MrL93oXNf5OZu00Sr8vQIxXDMjiElqCvQ3Ei
gh1IsQ8DtAeS+zArblziDB94EdjGNVjd/W15HXn1MA2+t51pWI45aDNp+nkWXvSb9OZhX8Lc
6bYHr88i3e59GKp5oF7V5OFv92RU/4318ZjdpM7VIIWuDrYLasnDCBN00YSry5jdyDjKsADz
wPRA2P2ksZCuJ7MesmEZzdeBj29ii0ZFTBFU3IvhN6IyK8ZisqGjUVXJ2VRKWBcNHEUH+fnG
wTNda/XuNtRU7XrWElhHR75ZNfVLHibp+TBQz1mmDdfhyyNdhowyv/rf+oKj5MrS2ArpGour
ojR28tv9AbWOzcEt341Ka3qgnfCJ5YjABl3R5/zNSiaaXf+ApkRPGRo0B3Sw689rgmI5GjB0
2wt2SEf4NaXbqo4v3MqXhz9IpKzcptEBaNv73TYsOi15yD5Fen0lOMNGcRg5JtMJU/q3aCpH
TdyV8PvAmRy1Wa4COHe72kbyYokCHAQSAQoABgUCUvpJQgAKCRAzYP+3A6naH4ydEACeWbfj
dc5DjnYZo3QCzeVnUmYEnAt7u1EXxoDSp922beYxq1AhoNYVoE/GKZjuVcIfc2ghKTgLLUlP
avEveIHJQUzgAS2VqIJ3BTCIZKUfIwmrG5uFAs26VHuMOaYsCGMJ+kJAtGFqCjOn/IirydmZ
cJnRUCVf1DyeQNhKIcZuQZDbEIuM0l3keTbciibTWiwV88U2z/g29IqA/bQgZZ6CNQuJUDfU
7K3AZlummLBxmJBNFWm0Ggmbx+6KyuQZk+XY2ZCLWLfRaf/mwwnD+vKOoq73Hh6XlXAgndFh
oL8DTeztoEGCkmkMgtCfh6mCrNc4pZfWRlU4ZskfB96K4QGgAfEmBdxkeBUDDzHZHkn0MWwz
wcz4T3VqGelt8yQiJs++xMSw1qZP/lzGHRe7TfwbV7G3l+rETVptp8FCy+whLmph3qNvqCaL
kctDFNhnFEMhqiaDbkdb2qiunWW8lUl2lU0O9ZBkFF/YwEBkUR/546tchTItghl78OO52FNb
rrVQjUYtav7qRM6tVTyp/G0RXYC3RLoA0HEoPWRNMvQqXxUFKi5WjSwXW/6r97RJU4JYP+YZ
+eQhmb54MYC8CKPEJ0gQszTia/92GFCDmWElQr7Z/cjAa7zFzjsEQR3Nzf8TwYvcM8Qk5PUp
XAIdISJIvkGDv+2wW1fZI127gdQ2iIkCHAQTAQIABgUCTo0o6wAKCRCkHscxUxntqpcyEADn
P1u32rhVVhrQ5WMTtUWoak6Ah2HQyRH5+6W8h2yPhdDKgycysGuSfchvtjrysH+uhx7snZBG
SNPnNkI67egpA5XnKy/jy0TbVUUYE2hF+Ow/eVTzb7q4dpF/IgZTcIxtSyDWbGLN7lI6tKsJ
fN6TM7LVsWAFYQzmphPOIozTa8tOUowXcBKw8eGurXC/RWXqAoP+BDAuF7X0yiBBrLFiAg6n
Da67dnurfRoGcka0wVupwWYGIANRTETOyyd6LDIqpiWwpl6CYTdvZFWD77fwVbnAkpKfJ2gU
Wt8exlWiyBYmfpd/WBwo4HZ7owI5BvesjP0uwRSlnlYySSnegfs7c2Bsb+a+I3lnCSA8axDA
HpwEdz0+VFOZXJ1h2S6s7AjNoCSoRTUQn5YQWv45iAqd8yCixqvNknzXvdF4XUnstbMleO5/
YPCi12EPyEGQVvdqbjT+3FAZSR/y9VoMd8njsXL/bhS2oBSnQG3u5NkOjTWKt51S7Ov+OkRK
nh7xLj4hSnO1ow24ANrkprAB/QjkqxbBq02jo1mtoOxhtFgu0ozd7j93T0mZVLtQ0ce/phsg
C6yjTACC2OYS09nppiKc0H7FISQz2ZeCcNh0LFoIcdB23OymB+LlDJzfZQBVSN4+2jMxE8oF
2lhHh3qxXs74eYNBNYYkKWKRbvuTG5joP4kCHAQTAQIABgUCTqaYjQAKCRDBpGByW1HLzypl
EACDN1dLNR4DM3j4b53eO95HcWOnCPSKxUFTGPLh8TUYdMcTgiyFpv19j9VPpGCJs2hXnCL9
BaV9UmtKmJ3Lqql6OnNVbrtGy/duiu3rSLkv8scJB51ex6iwaCtin3ifi22EA82zU4EAsWNv
9SvOHysOw4trmt1C5Lz6WX5As+7/7p5oA5NpvLi6gfYotC96VSZD1wD5n+0ebu1vecpWPEZo
4ZYaiaz4QIwaLL2djQWlv2BVb2OpyCmGcVUOxYn7hsc4NLrBvH7UUw/xZfgviwkqwcOl9gFH
bPopUYHoiaJkHDShaz4eLG5Km3eU0u16sFVj+OWzQMYY03oA93B8R8DPykFrw4GwnlQnGbvn
nhLbtWjj6giEwFCnBXMYp06l7bZnMSkI8Vxv8f4fFhV3aA4b0urYbRvctuGnWsUTAkQFPGCV
ZRgPGxgAK+PqNWDPhMJS5GaUTXMT6vWfL7y+jtoeyS4IS0Fr/r/qSjx7vj/eZlmyphxmGY3n
NFi7EMG9l3ZkPIn737/6ydGyv2RtUvAshEqwgK+O4gbXOj2OYmB8k/KlLvLYmJgwfrONtGde
5Rbrt4YljPFppoQtXLwNFyufR6EmhF5AHgZGBRiAvmN6tVqTIjiD315YYHNL/j4QjVgrOe/t
K9UW5LtPO1WgtJHkhtneQRrAPzvgcc7kLYiZZokCHAQTAQIABgUCTqez9AAKCRC9JbEEBrRw
SfYGD/0eEWf0qPO6k62+3CBtJPVA0U+yrH00fhKLe4HXl1CtwJEd6SeGNeiRZdbTfSdVrjVX
4imhNhNuJzObOJQxEsGTtiqjVWqXRTpfBHqTBCY3twSJPZr1CbGVQ9AZ/jnOoCgMztkx13gl
u1VwzWZu/EJ8PxvMOyOH1E8sbncHPDt7N9/JoMr2Njda0UvKvbp+cnv5kS4AXYUo+4ORAoJX
C5LQ+K3n7+iH2fnhV0Gv+hAwWmBM3dln9YK2lL8oxMoHOBOqTtvspJoCsOL7ns+qbVkcNptM
BDUGf0FkLCWPIKSP2H2y/cX9Hzfp9GdgJDD6X1Vu7RfR1287CN+ynLV8c9p+zPGOmwAnIqUU
HTps+RaIxWAMy8hEp5KYXgAXX1IB/tTbSi5FT49hldK0SboE+VWFBPlKF3fdtkmYuhJuZ2CI
cx4Tnce+kZCrMwHy/dztgj2gQJfCtkTkmL4UNBxCMhDLYOXI/+gj3kQuo7LwzN4nL7gszRCt
4UBTqCsxEmPA3K8Vn+6CR/St+3sx+/k+tBbf1gVVw7BoK1dixSmi3SXUIUqo6iKjNAS6xitR
a8hmYpYkBsadBMBhTUD+eftK1PpaYzy3b+4K8aLo0ldtVeV5jX8GZj4HflFsIWzglBh6VDwg
ACOmXNc5p+ucA/ELmb2HqoYFFkvpkkcoNIww8bH+r4kCHAQTAQIABgUCTq67wAAKCRBr1rEt
AMhe8Vk8D/9nuzxvW2eJgh1QirJagCS/EXrRWsDmYiOKerjWQDzbVahuV1MfKkUf4xTtfpl5
YIGO9ZZn5jkMtqKvbjPERyQlmKuHxhokcifILoZzvuoghTKTVTSRtH1DiEoymCn348thO8af
vmVjC4E24UOSkbdy5cccVCjPf7sWnd2y9a1aUfsYE9CLyZVMdBmEt45756pwmyzlhNtgWxaY
eVVJxkfbw6Zba1VzhTBCm5ZQT/YkFLwH59cjGd8bdoIZ1k4QBXMdc3svFQb1Hb+6IdKMS4gV
PjEfdDHszKZ/H/RQs1lYowAkYggJ+YX4gczeq4sgVkLVKCP5WukOvSCB3QRkigYyShBy1mSj
qOONPc/+oROiuUkHpc+i1BwfkOX3zbj1SpuTT9FOjK7hfF9QL79TLwAS4fS7pkxKAdTKhoy+
s20VwXEMgdUEYRNpPF6r1VHt0WfC/Yory9GqwRKQoTqomNsFbZopS6aFpSWOrSOcOcPgQ4at
Y6ZOIVi251dMyu9vkdfjNBzOL21RpDiKY6DYjsRcEIwXYybYJmzLr+1WTbh/Gi+qrLFfFLXC
VERLCMhS51XaZPcMEyRJXOM45WwiAnhgzyySDIJz1o5q0OGG2FFPTUSTIB1xjXpiP9aZ8MA9
eWv82gEWMHdstMlZwuiEz0t6pl/I02xvnC5auurRmSrk5YkCHAQTAQoABgUCTo1NAwAKCRDl
b++VWkvtznsxEACJmZ/1qBkoVa2APnb0KyYnGCHfkBG0KU7l0p+GXnTebexpIfv46tFpmnqP
65r/nnPx122WIVBL70qcc/jLFibZKzGgt8Pkkr6ttz58Z5t3s8oJBIKDrl/91VDXIU2iDl3B
8Y5p8VcltsrS7Z/Dzgs4I9cNTOeN/4Tqm7VtJqC1/RE11esQ6T8CVTwzisEsycAIw6vvGuWZ
9PvqaebL0bPXYx72ewObmby/PJ7AMzQX46G+8Tp2WuoAvk3peHlGyyuZdpujavLRLrHBKyHR
X7qYuD6GINFkdst7mkK7cGEldApPnymEK24XHqsiNRPC1rR8lbQMRD+vmMl0gSOXvgbCrXWG
EESbNBXnoHU7j27LorAxzXj0FiL2DjnKTT8ECebWcSTmHpu/TLGBmuk/w4SdK+GC+5h8/iwF
7mFPn9LLaFboVoMDUWrAWPuc/tBMIt+xzoKgA7s1Fg2gqfLLIJA6vFY9F+LrOz08SW7OeUuf
eXTdX6R0KTHHdqHcaw4ut280Xt5rbbjEsaIQAk43WM9V9rmstRpOvmg4iN5xpJAm7VTHHb3J
8avJwqkZJCMM3/TvaSrXjaC2ytK6d7mOsxobdjZ74xIgKIk3AE/sQyvv+zqR0nbe5WBiOlrd
paqgR147cl9WN9xc33NYdoQH3ON81USjD1gpFxK9Pj0w0l/XL4kCIAQQAQIACgUCTo6Y/QMF
AXgACgkQkNE7g/JkgWVxfBAAiGnLNj9bfAVRkMjtfKdTNNnYhetcn3e+w+H5/15nX0eCKrpY
zpa5No9B6I42UF6BwWQZgzK2pXkqU+SdiygMcZyMVbEA38TjqLFZ5wn7vqu3H6OBN50bipJk
e1GgqokIinAiSpsQ00Va+/q8zgFM10GFVoE/4Fo3nGMgF+cR78KGyThXK2G258impFGhdpeH
NvX7c4WfoWiUKpavb9aLl53qqb3SGqzNZ+LlecQsBEAvsTYYwhnPBB1cJ0DpxF5q3H1w/sux
FwOpzM2a4RwtZrHzLpaDjcViqhtpxB/LM+ahlCRRuw+IqkKwQnVBF6SW/R+znR6ReSh+rJGr
nBmsMFTWDI0ONoweE16HchhuNdHTvFkT2M5a1nkOJ0SpsRutHn1VwNBySDilQLQ/IUKAn7Ah
DgMu6iznDAYhnyIg1Ej9Epelnb4exGB56ITLoMlIjTTdLJJCVNLiLUFmms7ip+mEKSLjMVJN
EGnT4NDg+eA+mXDvtvXkAdbh3w7K6sOZxIh9/wmTM/TeOBOGvAJvMdlKtgHAavivKFyonkOc
SShd2lmyrUmwi2CS56ACsL6byW2vTk4vF2etOmgar3kdavzPxszdXAKXkldpRl96lYcYlpIt
5vofUVVOb6MnTDMtrqRxqeBJER6g00tMBTmP39FOS9vDa7yqAiWvhYXmzf6JAiAEEAEIAAoF
AlgMDdMDBQF4AAoJEBzg941QZrLbzCIQAL18Sx8rQtJ30cacR/+gKVykiAszR3bzgulLl/Y/
l8VVVQtQQUtz+A+wJyROfooXu43DIKKifeQdTqbiWk+zmdENfds5n/qELNmhk2OhbtdcrgGS
5uPuq0gx3Hv9NsV/rr68bVntPIjuVmZA8qKgfhjWK9yBN55xR0MhFtbeUgubm7j34Q+0CJ38
xfMiMaBVkvubfPpxi+bjV5flMGoj875i/SC9vUjteZyB6LCrUVCK9Ez63zTNEkXjZPStcYSf
lNEc5sfB0+XJ5lVhxlKcJsBv0z1uexgAoiwMEQTRNnQ4Ov34hZnNcuk/mPdVgf7LxglEHtJq
oUwvZJ6RjphiNzaH6uMUBZa1PMclvTbXwRNi4Z+YN0XjYTj8BLywcYLruiF/gk3ZoELUwIEg
U6JtcDrPlXTLRVQjGH4cjlV7SYmR6l+VlwCg2z7zLxJDtO8xc5DeGm+QVZCXf9bs7NqzUIbY
Q3b18GQsKCJgEsovozp/AJLb+1YQR/LoZFHPkEv6DPf3bk+n7AKpd/rw+vCm7DjZCHAfIxx6
743fWsNXOTqGlJa+B+HSlPW8hbYYvQY931tRogjnJ0sMk7hcAWRYZdTtDKLvwP+Hl+icrQtK
i2P9Xdlb2c2cUk+eck+wfwb8WotBZiRHByILgeoM0XmoDDUs3aFg/y9uHEr/2fsaG7BEiQIg
BBABCAAKBQJYG4qfAwUBeAAKCRA+reRcbuDi1kTND/4y2XSp+sbEk7SODdwBwOi9xe7LyPaG
omMtIym5tyz2OGN9yCpMJ+Zn0lrigwkTNQsFPYFLSs5qU1vsuW6znJ+P1ylwk6uuU4joaeD4
A6y7blhOkfgpR+y6HPyMprFV5zYJHlVskPXOqwcDJXFD9SeW1gS00nRmbtuE1BgENcXhecrm
5Bsvshh/QJ0+hyt59OTRrJYOzPlshU0mvOOTABqAmagRZ2MfMMlvd8GStJabaiv8PFTzAfs3
I9tSoT2p5TB6Ey9IAt+PpmRRx4nampkJVjPHohlRiDENZR/0FeRUZeTn6YAe2ZLvzrwFMry1
BP4Q0rLpcIttoPsquToQVv2foncsK19lJy/okJBLpRHf0IlyObllq2vxM41AQHuMLXlXAbPR
GzLpufucnpsXdKB6NUt2W3L/7oGlhoJD+db5lOwfmwKxgsl3jBtr4GaUjRy6oMCdTaLOAEAt
dIVa1m56F4Q0HbLKMvfDN07TKQZ5g+G+HiU3J53M11f6xFmW0YLox1m6MPHSZTSJPekabpDU
Lq3bHSSHgDA2jFGjwOCTd+XYBczmzSrX+2xQdOtaJZQi6KQ9tviV4lwKD1MXwrVAjg1R+cms
17jJ3M+XzwAhmDq+zH498z3emfiHhHB2GPxVAHKy8ZLKnePGXhxSm/FGQJ+X0fMql/eyafrN
sIF3VIkCMwQQAQgAHRYhBN3Lr44BMqpUIKu4ZECBCxge2Og4BQJamsu9AAoJEECBCxge2Og4
5ogP/32U+nnxzUlobo5RsWfnIYOYla6XWVCEgIV4vlPh80a0wAl9ZoITA0fP54iOIvGEx6p5
E8oicMCvY5eB4ZAYguD9ktts4eIhGFTDoCEdmsjAM9BPoi4UAkQ2cPgsUL2v/002vLZtlFaF
+b9DsnO2aexuHktUbPka+IeHU/aBJ6S+Rt8CRdSjfbqW+mFZkT0yXq9n/pbwqWF+w9ItQ9e3
OL74BgKRQvnv5VuHNCN5UTA3ROe16U2DVHXJrnPu2X6adaKAnAUIo1DTEISVbveP6kDh4BHO
Ts4IChMRNctTqroql5Ff4ioItGSuCpuKaRjr8B1WW52OBJVh/Ku9MXDONBQKuBet/r1FJ4Kz
XuXCHNZEPfttkZvtYW3TGGgTCoJw4GoOUow12fbaEIsB19NNM+ZyZQ7jOVqZx2YRubvux+8T
oraR+39b4bxERp2R032NnK7PLCxzYALISSq7RPgXBTFWZQyj0LSKcNWl53saumjtGm5oFOM0
2ItFsAksk/EKriScONYNAKWb34hjH524rMUerweTromll133z4M+Kxdo0lgqr7Kd52LNqheh
wINZbQ2jmiyb6Mb0A7KE7xcRdUhFtVxlEL2X02azuf6KzC8ZStRT0JQtQYUQyLmMGwUXMpmV
bq51B+QtHKmfSQhQppEYZEMXqe6IiPEcHPbroV0wiQI3BBMBCAAhFiEE4inmFHgL1AZaVdBZ
/Qv9aFby0/QFAlpNpI4DBQF4AAoJEP0L/WhW8tP0BCAP/j8BkUHnbn5/r6cJRZLbCKWOywB9
Fpjya7tMQ4HcyIrqYtXRFd8bETij41M/qJ93hU6zYKakOq6Y5F+m9QShx+KYgks8k1RCLiac
oCEJRTmqSLIBwVrm2ioVEZl2azG3zGhnE013WLYiDzpoOr8xP5/31s9peQs0ycAEbdH18B12
XDCmkNbrTF2xYP2QT76NEuUQFkKDeGpmhZVLDJMQxd/cc96kf2ubXPOIUCdVaUVxI33K00US
te0oiFsbgahz7KbWy8qbzYjKfJKDERyflkNrFULI1hiJuHzX/qfbF1lw53MipwCFhhTqW3Di
MVJWrTlZ99QBZJfoG/Lfg6HnMBdkAOnYE7HGHR7E7gl5leJ/5Fr4swB1IDFZ3uZWCSMDG1Ty
2PI4KnVzsiaq8vZKjltltkmAwxDvZsmx68la1zyXmcp3JRU3JdOwqgxo4h9oACMNm/yZxQ/w
bl2PZ+ADy5QQHj0WGA9uVBEO8nGkXkucngr27B4y6iBtKLrh1aPVScvWMFl6r9oWldmzbXhc
Vb/sX4vsUVH3UE9TQ1v80+k/WgL/HoBzBTgBxoYrxlXI/LCRTMKhlRElEkKy+iVklNY//7Hr
JvfCVXMQxVRPOpglsVIoDBI99K5dUe4n5XxWJlqvgjLX64STXEyPXcj8DhxyARrxWoR8VoOi
D09GE5TTiQI4BBMBAgAiBQJOfLXVAhsDBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRA4
273IYJJpPl9cEADHY19NvUhnn15Xc8q2o0bSrBB2r/rahKo0qW+Wn8nOR7KeN4nuJzUg6z8J
NO/b7Gg+Qs1w2E4/Pu56b/hpr+bL0DQASnlSwXou92j8fLsnFz77QSBb/9PK9ZxT2p3mpL/z
lVl/wjmkv1Nm0dXB8h8swT4byGRDHT4JZEmWgsUbjyXCLySGn5OwJFi/W1B6LsbbbEBjmz7n
dOyxoLUxfJBPtokEaHCduyljYhpEzOytfSaxmf4WflaXHEwPFSZQAIUzIoYn7UXpVjTWdaiV
PcbW506el57rqpccmjpefOQ2HN4fxV/ewx7SxfHxiQ3rzaDU0Vpr56XRINxdm8H3WkWRadBp
BJ9pzmAjgqJKHECp48Afo6D5r9+Gzld3JjDRI3nQ77JpKeoQOXsmQHJdUomJVWKEO4R7klle
Sgg795M3q5x4Xz1lCI790WfQpypzi8vu64joUMxFkruZjtuMXEVbVWOylzGFPU11HtIU2srw
aXN3kVLruhPBI0HcLOQnc43+Q4nwlLEUS3VlNzZmlC6OsXJO1uSxabdjLreRunDFGhHrGBob
C3HRJ2SRYMJwD7YZnKYKOTkUVCQCGgSTS27+fl+8dLtNNm6L1rYcyzKqaBMnN5TR5AkOCZv9
qHNbRWCMe0AFomvjRDzcz26mRl0cVdJlvtQcViQB0fU7F29IVYkEHAQQAQoABgUCU1b7DQAK
CRALhdqK9yc3SdCwIACoZvCHMkeuuf8MYbkBTH9gynxS5xGf/xNXy0lu1ukq3iRhim5walY6
JkU2PWqidhphGBkhof1kjA0jR9Kz+q6i+FhdxgpnPlxdKTIgJppSzvmEfiJVXJCTQBn2EYID
Ocbiu/53WICEvjBvrlSxSDn/hs+b4dTYfyMTfjvTqa81dQSx3I6A/lIZOyABzy3Zh2aWuBVO
1jAxYsdb6iuIwSrqf4auqn0ifeHKO92huqJih714YhgXaFaElVZ/eEB/x0/takQKCrb0t9oN
F2KDJvrzhbtJyXq44ci9c94nH/laFtadB45444sLl1wpKz29YmnYR8dNnqV2IBG3er8JfyH5
6etP9ldSfLq1u5LPapYSbuggGTc59gwny6OmLQvTZeLdAonCGyCBKo9YbYoUNpCtVsXqCQYg
N91GlRqGEZ11nqOT04YPexhUrfvt+QyepLMiI9aspi/x5vpKsi7DqUJIn5UNDIS4ksk1EkHt
Ng+MRsrRh4+nPiDdiK6Zvo3zKWoa9wzJvhxCXKcQp4+tBNUwzLJsh6m61TEpiLXXPg5IbRyH
BIAoORDWNEz9UXDHUC11dCjej1bvj+IlcXYXNWMA/z+hI5SsC4WFajtyTBCXWk6zFwevJbbU
fnMD9T7xvVW6E0RvMefBJW9Cxxn4L21jbe4WIRrgcy/TJQ6L0+ekdrL7OS+BDZjlOS7H1TzK
WSbUJzTwzOZI2Mr7B5a8jViDG2gClqudyhcgcgNXWjQz4J2qLn8LVY87HY80QgMgcr4c8AL/
Oo639d7XZnTrv5f+ZluKJ05ynQo7BzzhpzwMgMEycWXDNxxfm7FlWjH0G3dgM1nlzY/J8KGs
/e8whUERGhN0/OHSKFtGHEdoOMtyLvaXip4YtSwNYvYqDW8XB5RmOya9rGhwoZ2o/X4TQgQc
wM+C7tHZJnH54nOnqHbOUijpKXJLk5j/OEJVpS8QeIxJwMbe5hGZw16kkr4njkNT9V0eF+sV
2x0o9w2RLI8IX1NeN0ISF76HcNy9AOMa1e8VyWuDZL0reZtd8ZtxJRoLtXJstxDgZZqcG8gJ
ASj3TrDqxDJLYn0Zpe+1gvLAuTzIviI66QVW40Zc4UHAdnfVhlmXPpJmX9UpfQS5SC6xSKr6
YsXsMpwn187sJUnLaA/xPKIlO4/mwaP9WWkvlkHFjlekl7BfrR4izxKV2QjTVmWLbFAraqFl
4jn38St74IEblfPXHVNdvQEVclH3btcQzZhaTA475o74heRkwS2ijRJK4FvEkcwyHQuqc83i
7caUZoma/Lv+62eZtBqNeFgZKKXwqngtUu9MPC2u+vZAwrxOJF6dseqgm6Xz8V7P/jLvPcrX
iYOKChQ2V6M6dMvwiQQcBBABCgAGBQJTWl28AAoJEJST9BviigOMLPUf/2Y/ct11YTMYEqGc
of9zR/3XW8O01j9EMXVVNh55E8mRHeWkSmk5/c7Y3rFkxzPzcWtig0lkD0qkpZTGbeAGEkzj
W5HozGDYiWO9TO8dB8Hmrq8km6WBaawh77QVK13bE252NDua5KPhC1TyBoBt4cVpeG3zUpOm
XZwCvfkRiE4qkPCRe6z97S2b54nxmlsCbrgRc9YsKPnU7wLCLPUHR8LVCe0+VSxCiqdmSnfi
9iNtrj4TyVHdHuZQ56E0o1ZXqTy+oXZ3vusKs6Q/juGfml2cUmNV7FjWt58yoygffXNGVCl+
ROGY/IybrUKYcNgoMx01SHRbJQoDHGyHWr5hdn9A8SUJvl9lirZX82MPra2jCSJUUR5KELHI
IfFBLky9eRSRr82pfXOkRv1mz4K/Ztc94jgXLi/yCJYA+wNKcXJ7av4yO58MXTLzxwuqBL90
KHhbsNJLW8XLRdRhw30LQ5vsunG4wMzkR+Jsa1PfTev2Lxwa+ydethpsQvCgajI+ITOjYRQG
7xEMuIsPi1T2t0alcFPHvDyjPrW0ftF2Gfg/4Oue9nMCvvXCAPkOMcVGdOwLUWx4x/vnZjcf
09Uvj6wyVPvThwFYRcq4ryhUdlODVBqdJj7pbNSM3nSuHPKcLed1sp/vO46RsWmsuEKcxh/c
wYpBtSye6zSggy6OKIQBm60MOAnbjU6cev9ydt213EfpZ2dzfaJP9ZHwCBLWCvoW1OdCSGf3
p/Tvj6RdLdzj2qqH1RZ7XrJzNCnoureEEKIOCAkm9VDm397naqMG3givOu4T0ay358PPKGdb
9je9Jz7oQyKZEm1wGktg+XVJQvSZSWO/MRlBFEFtMWnYC12ud538eOnZXNOb5Vt1dGfELoQQ
laTAkV2n+8Xm9CJIHeOj2Itv04cF5RM0kzzIX3bybSK+PIx5VZRp6+jcUnLi4JOVGwElwLue
l7P/7gWZOvJAGP6QsuhmE0ENDigqO4SEbDCYnMNFg6u1vtJzKkfHFz6GKLtIkGDZ+eUW2ors
6rYj0rLLOMTzVJuG/eoo2lBz2oQQ1sV7fW8W2eMOEWFUHFPBvd4GFX+wjlVj12bX2XQWoUCi
RIuXxFAGqaM7krMQY+iLPJDDwhwaQTJPi1AdqXEcSbGSwikHa95D/Cj0WUT9pywY00JnsiZ9
nQwFKbIxOd2GQxTcp16PHuHgbBAx6nnGbyneVb9bRwihMOsnSXEQyTTOEbkdEwm1EI22ws3C
xzM/nUUNwX+wX08IxAjeZ+WAsnPe6DD026IaDjOcB1oB7Kt4PkAe+PYkTI1PrcKeHGH/tkNx
aKYmyPiipk0D9w8jlzm4GiICOlAU+nLWPV8R7V02uY0NtBLw+dFgGc+JCBwEEAECAAYFAlfz
Xb0ACgkQncwdvX/Hu7Kb8D/+KmJd2QDoRKHx6aOTZ/xiIRvrxdpZD3qrBU3IQQnClhyLbuB3
25rIgJC44v8uLvvpfqAGyxROkRdrRQlRRhP1TfVM+ik0cXrQUb8YtBfq85s95KKU/J2betZ6
+HVyVNKv1JFB4alQ0CNMj6IJfuUSHWqh/s97WEDSPwuQfHPaC52B1DfbNNSiWxI8eiTedkyW
vDA+segg3rRCnQ1nwUSogKMCuzVCE/0KJPosoY53/yoEkuA6+qYhKIn8V+ma3YsHrH2+dUrO
gYjcq3EqODvfcvfSaqjLbFuScpVq4hEQe4Xf53hWnJFElRNbpK6hSR9i5MhihY/H4l5XFTWs
a57I2XEhJ7icfgfSHfluNpgKk0DYUIOP9o9Ye6P7VzuxsbIloan2CXBi+CY40g+yujK5waL9
yXpVyWZTJlVvvukHYeRZ98xv+RK82byGOU1kG5J+w57xdLwJJAKWYXUGxJ0WEopdfGWAdciu
MK//qj41Qp5gl+IM1u1+NguEx/sqFzmd7SVFpGQovTDBuEYBCz5xK5cCEIw5AiwtWIFW656N
vqvNXCDQDUo5xqdeTPMpZhNrZR0nhivT2NwIGOy9kxdY0Ycj9w1RNG44OjjgXLzTNT2NX1Yd
kd+Wlve4ayZtY2KQpJD49Bx28God8a+6CH0E9ei4MAvKXvU9iS+jcp8l4BUgD+dx5Bt8DwOx
Baos9WHDoSQ+Axt2WocUdiXsfB4ofgsaaJ1u/xeG3LZDFclM6VxLZI+xyYd3sayPl7O3C5/4
McO2WtoK1aggBBl8wJ8y4roKp3/A7tYaK1V/DzbWraX/a0yJnmmxae+3pTmXoAcjz6xi9foJ
gGQyHS/vSzlMqhVxIBeWSFz07FiFQo2PcZcK9PaTJqgY0JB69Wv6QJiOrozwhPmGtEc4hqUl
Wh7526LNT+WQZBYFQcTY9wQfkoeGMoYZrv0W9C/ARTqROqgtRIisg63kOAcwaKRXYUuvuDEM
MAc6WEbqgwX2XoS7gaioVQqJlP9OsZo6SPmx4qNUWmAWAgDUZgEF/RJpyNsUInNM/RVbnrPi
jCGcTyaMThpZxixIGgu83R8kkVFdOPbud+NraftfDgBsIzQaFbuJIoy4ax+9werZxMqODssi
NbTnQhUzS0ex+EAab0pjWC/sLGtOVJbpYO2Vjvm+WwX6oX/99aHzBYHiHV7OVV21agd5CM74
IS5XDXji/AH1ctAeA3HGZ3oKxNpSSp1F6TPrCm+RkgEOwUbtubAPhw/6baGlnO4lZsb9OwgW
Wa6RQ6Tv480jvpK7WHJ2//erDb7PySr0NaFfs/J2EjmtQBAkPvNmuDf3/f+LE+hiIi/rJkpR
wNVIxFuUoIeM1hxOGoTvKLZ/eetE7fgodrVcWrT77qQo3/RaX9KU0yaB4Lj1NL1l5H0y1+ms
mBCYN9aYOkheaaebXkydnsU6IkDlh4sVjudVRk8OMxOVmi6Aaw8Vxh/6SjaP4vUJD+w5ai2i
faGADbUMAJ1JUa6o+ITxn6mg1Q2dIB3YoAEfkefCTsLFxL2+M20B1o159W47A6a7jFtaQoIz
VorHZvlkNkstsAch4HWLWvIq8PVxmd97K0XzVvlfXCucq0GO975tKx5B/XebLon41t+YV3jI
nsmgQIy/DDzSd7MVJVtTsgeBCE/Al9ZevqXDiIn1VmbB0HI/J2eCeS8EIBI6DJhQd5x8gRNz
S/kT71v0SjSMCJahUucpWrgW0aef2sy6T9nUjY1Aphn65eFL9dNWtQHMqGF24roIIHuw6hkR
OFbOOlh7Bhe1FTdFHfAGttO0IxIc+z9QlqtPNSVSUPfpaDU2q126mM4UwijlWKVZvdDzdsgK
dGKhw5W/EoJ4nozpn78l+x82prvLB5LH+gunfoAssgwXBr72aQWuinS7JVnY6pr6aemdf6sm
7568h6hnPf+JD6aqmfXkCmnaUPkrNOg60GXu0e15F3tCE0kicoQQzmLN4eAqXVoOrUNdgb1q
QSEwYVHiaKIklAGuMDGpot6HdlF+ZPRtJ9qL6pNoFvlx+ZIixSpnAV6X/N+Z3dMFLqnzlDIx
1O1BVa1K/EQA3Ix3nPb2tLkzGRq5d/ODRa3k4+XBcEVpM9crTvl2yn3rpaijZdUYVx8YtfV2
0bd8S+IB77PXXrfj7tNT+cO7LL6c/dcz6+ImIeLKrw6SlhDsHiiFoJJmAmS9q2XRUR3QScH6
VDOu32uh+KObaNrhme550gI+FDAL1LMglkhEBuCZqJLg21gxoYn6+q3GazxCCNXcDOjc9U0w
mEzbgxPjwnTkn9gPPHR3tW3YYE3MEaMQLZv5+IKU7GOIDurkkrwh9RTaBUdEzw0u7SBXGy/a
fvvbEAS0iaZb7k9sgRAX8nYbyfexu8ROHv0ja2FZIACNZl4m+6/vdWaCLxoYp3AUhwdXxxcg
AtI1vjPha7dmgKydbZnapmkl1hneTatan45bGXkuXGeDq5jc5tsjWwH8fd+4y7y5G9WjoUo9
ijY8eDdGxua0GIR0t0gZHha4yi/1CX6ecpJQ45jWGba20vvIh/AaY2hcXnq8no7HK+YuxdFP
fM16bYI9Ix2cCeG4i8V3tWottkH82kcBfZY3D9oLxg+rqqetk0nzehXXlRCUtWkTpagQFpBU
viVo49FZXeP/oEYH1gAUG9+AfS3kJMM1sRPZyZYiFT4BWVCW+o5SQmCa0Z1H4qzPBo2qy4dU
GQ/BJFnuapkApDSxM3W5Ag0ETny11QEQALIiIb/niWy6M6GfBMt/2EBWpLuE+FYVeUQGpGhX
D2rUhOo9UpoxBD/Y5mc5OaJsVL3fySYQldVFOaT7Pu0J1N5FXIBckgtbT3eg+TGD9WIfJy6Z
pWjBKf6K4frwTwRpLBKqZhcA/78KzxFHeRHjV4cEVZVNoRtVqLYuTlbdlkH6G2YxgCioxAfq
vsGjsg2ES7Xl6xz3uaBH1DFX7S2LXHkDHnloWOTaDRe/4h2VnFHf76xsJCgt2seJp91kI8bh
uR7CUrO5mkRMhnp/z9v6vc2qcMv8EMK62FiBaqENaKg56ag8Icujar1YwXG7oYhOuYiWxqGp
JUwg5+h/HeYw5Q8ue0UwHPCUZR14pzQCKxagRMibiufOlS6URbCcBG44ddFAt2vqqopIo069
moxfqt6OGig59cYv7PSMfHX25dV01Ns+2R1eo7qiktkV+3CSSs/dUArcTxyovuadIAUaZAJ3
XqsS3FGzZsPYMYNM9faZqOfF6mmGmCZRJMMESWuWjc8ZnVAv4luyD18vlsr/J9rO0t28s4PJ
yqJGozEXLBLtsaCVihxBHMY7QK/pC0jRniLpeniDDHY875TIiG3nrmtR84nnW9WNOG6tuaIc
B6hD/DmSr72rRoNEpCa/eT7XiCOymGHS5gWR+94R1+J1rQZbd1T8gSq/nQQluJII7oz7ABEB
AAGIowQRFgoASxYhBOU7YErdNopTm7nrM6oU6WIA9eAGBQJZumHdLRpodHRwOi8vZm94Y3Bw
LmR1Y2tkbnMub3JnL3BncC1rZXktcG9saWN5LnR4dAAKCRCqFOliAPXgBgnGAQDiMbZ3q0mE
Cu80G3U/ZorAn5SVbHl/8HKviXjzRs4hwAEAn/y3EFr448WlBMt9OCGEo2jPt9eRTz+xglg8
KighVQyJAh8EGAECAAkFAk58tdUCGwwACgkQONu9yGCSaT4wUxAAvup1iyrlHcch2RHfxpmF
RBYNOwtmpExJBy+KUzDZ6RjMTTHFbw3YrkkXA1cMQobF2vTxnNZs5B2I3u2sp/AD1MeFxD/M
e5tebZcjJTBH8DBfKMRwFwX3fbH4X7McLD6XYMIEz7Vo0e3sTzCVqZM27NmPZrhWHj05LQIl
iLeUuyX54vYwL66hlvPuNPhEsIuabVGYYhVWd4ZafhcI0V3LGY/KJwBZq4pqlzVPELMkxcvC
Ghi19GDeF31Z89plugV0207kIjFb+117oX4Fezlu1BGpcC9s12Zd9rhy3KzLqwCoxAgbZLvC
waGfELDSikPJgpBOvph1gTApX11/7a2/kfOYYEU+htnqTm4k56kTrllRX+CCgxQ2aZ13cdaF
tHTzAOnnYJNEjXS0ClEyxIXXnoLnwjcuLcTTVb3kNH7LAoR/x1JmbR1onhIOB/RwFJcUT3/m
lJFtXUacGCSSCpCtL0HewU0Yr0uL5Nx51i7pNG4acIJNteKz1PMyaYZLETVY/euNZ1A/zyaN
Ks7Y/SCba5q4yOmDc/skSKUQfP7yQ8KiU6tUmeWAafqUuNI946M0RRsKnxmc4guWXyvUWwdr
p+AAYfzckZU4gGIRVWKSvG6CTKDs0HtZ5W1cA3+lrcur6HpKyzk57uGORqWOFquQERMs0oXd
HKc5w55soziCllQ=
=PQVe
================================================
FILE: pkg/sync/srcinfo/pgp/testdata/A314827C4E4250A204CE6E13284FC34C8E4B1A25
================================================
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
oSf3hW01pA==
=cv82
================================================
FILE: pkg/sync/srcinfo/pgp/testdata/ABAF11C65A2970B130ABE3C479BE3E4300411886
================================================
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
8jMWgR7U4vQMGuOReoECpO8gNUWr2yAhHelm+A/8L1Un8q7S6eJafBeCsg8WD1eRApvD76cC
ViDfbyoeSU/hWxitB1OdV1FzdbGa7/dQuYUBmi8MtFAZ4okCHAQQAQgABgUCTnplUgAKCRDT
b3abwRgE8JZYD/0WH4k/timxBCGW3ERN6cCyOOzqj52cdZBXwyzaobeA6m7/d5+GzpmvVqHG
e3Arb4PoQH6L462iH1iCCbKvK7lhHKCmtVXM6orcbkfZTi1IYy2lOATsRX1OKWL3O9U/wKhU
tw62pQEBt0FIFZLd00Jamh5iT3MxUhcrT4ZWlXjC4cfJ89vzOjvpLs1hnhqx9t/HfPDwMsbG
e40PxGrLzq2pLpwS/L3M703JGTCblrHTLbVmGWfLpMBsp2g3L5V5+JSHnI0wgYrF9S+lTkOS
bFmIbmYuwaJHCaGtVTd3Mx3MUJspZuT9Oj4MeB0a/X++8YbSXA7uKkzqLFv0Yv//////////
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////4kCHAQQAQgABgUCTqblgAAK
CRDnv8jslYYRCd8mEACQnqydgXN+myvn0uZZeic7wSqbHOXLcG9z2fS40pujAvmTEeO0994O
BrvfM95BRcLBch9ZpHK5asvwoYy9nnXQ0Rn16ldKahYd9TpZ+eJUohIRI5gKkIiwD5nmot+F
NYMQLD0SOn5WHPuINIg72TK7oUKobCP7Tbb5Kv7rf2VmqpcCcYwNXsQ1lMPnR3msZlNuidlr
ZhAS7RMvb08eUYAso3KHR0srQoqGkYWZdjQnnj4Ver8Y4q9REEpDw67r4lxp5LBCgBGY78c4
tbTVZa7Efa5zA282+v7bkzoaMgJxqDns61de3Af8snUEpCFTBWkqCMGkWiNx70ZNfX3Z1z3c
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
LddgYrylZH+MwNDlB5u3I138
=6ybs
================================================
FILE: pkg/sync/srcinfo/pgp/testdata/B6C8F98282B944E3B0D5C2530FC3042E345AD05D
================================================
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
ix4eSNK5nln0jUq167H2IH//2sf8dw==
=pTiW
================================================
FILE: pkg/sync/srcinfo/pgp/testdata/C52048C0C0748FEE227D47A2702353E0F7E48EDB
================================================
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
=Nbc4
================================================
FILE: pkg/sync/srcinfo/service.go
================================================
package srcinfo
import (
"context"
"errors"
"fmt"
"path/filepath"
gosrc "github.com/Morganamilo/go-srcinfo"
"github.com/leonelquinteros/gotext"
"github.com/Jguer/yay/v12/pkg/db"
"github.com/Jguer/yay/v12/pkg/dep"
"github.com/Jguer/yay/v12/pkg/settings"
"github.com/Jguer/yay/v12/pkg/settings/exe"
"github.com/Jguer/yay/v12/pkg/sync/srcinfo/pgp"
"github.com/Jguer/yay/v12/pkg/text"
"github.com/Jguer/yay/v12/pkg/vcs"
)
type Service struct {
dbExecutor db.Executor
cfg *settings.Configuration
cmdBuilder pgp.GPGCmdBuilder
vcsStore vcs.Store
log *text.Logger
pkgBuildDirs map[string]string
srcInfos map[string]*gosrc.Srcinfo
}
func NewService(dbExecutor db.Executor, cfg *settings.Configuration, logger *text.Logger,
cmdBuilder exe.ICmdBuilder, vcsStore vcs.Store, pkgBuildDirs map[string]string,
) (*Service, error) {
srcinfos, err := ParseSrcinfoFilesByBase(logger, pkgBuildDirs, true)
if err != nil {
return nil, err
}
return &Service{
dbExecutor: dbExecutor,
cfg: cfg,
cmdBuilder: cmdBuilder,
vcsStore: vcsStore,
pkgBuildDirs: pkgBuildDirs,
srcInfos: srcinfos,
log: logger,
}, nil
}
func (s *Service) IncompatiblePkgs(ctx context.Context) ([]string, error) {
incompatible := []string{}
alpmArch, err := s.dbExecutor.AlpmArchitectures()
if err != nil {
return nil, err
}
nextpkg:
for base, srcinfo := range s.srcInfos {
for _, arch := range srcinfo.Arch {
if db.ArchIsSupported(alpmArch, arch) {
continue nextpkg
}
}
incompatible = append(incompatible, base)
}
return incompatible, nil
}
func (s *Service) CheckPGPKeys(ctx context.Context) error {
_, errCPK := pgp.CheckPgpKeys(ctx, s.log.Child("pgp"), s.pkgBuildDirs, s.srcInfos, s.cmdBuilder, settings.NoConfirm)
return errCPK
}
func (s *Service) UpdateVCSStore(ctx context.Context, targets []map[string]*dep.InstallInfo, ignore map[string]error,
) error {
for _, srcinfo := range s.srcInfos {
if srcinfo.Source == nil {
continue
}
// TODO: high complexity - refactor
for i := range srcinfo.Packages {
for j := range targets {
if _, ok := targets[j][srcinfo.Packages[i].Pkgname]; !ok {
s.log.Debugln("skipping VCS update for", srcinfo.Packages[i].Pkgname, "not in targets")
continue
}
if _, ok := ignore[srcinfo.Packages[i].Pkgname]; ok {
s.log.Debugln("skipping VCS update for", srcinfo.Packages[i].Pkgname, "due to install error")
continue
}
s.log.Debugln("checking VCS entry for", srcinfo.Packages[i].Pkgname, fmt.Sprintf("source: %v", srcinfo.Source))
s.vcsStore.Update(ctx, srcinfo.Packages[i].Pkgname, srcinfo.Source)
}
}
}
return nil
}
func ParseSrcinfoFilesByBase(logger *text.Logger, pkgBuildDirs map[string]string, errIsFatal bool) (map[string]*gosrc.Srcinfo, error) {
srcinfos := make(map[string]*gosrc.Srcinfo)
k := 0
for base, dir := range pkgBuildDirs {
logger.OperationInfoln(gotext.Get("(%d/%d) Parsing SRCINFO: %s", k+1, len(pkgBuildDirs), text.Cyan(base)))
pkgbuild, err := gosrc.ParseFile(filepath.Join(dir, ".SRCINFO"))
if err != nil {
if !errIsFatal {
logger.Warnln(gotext.Get("failed to parse %s -- skipping: %s", base, err))
continue
}
return nil, errors.New(gotext.Get("failed to parse %s: %s", base, err))
}
srcinfos[base] = pkgbuild
k++
}
return srcinfos, nil
}
================================================
FILE: pkg/sync/srcinfo/service_test.go
================================================
package srcinfo
import (
"context"
"io"
"strings"
"testing"
gosrc "github.com/Morganamilo/go-srcinfo"
"github.com/stretchr/testify/assert"
"github.com/Jguer/yay/v12/pkg/db/mock"
"github.com/Jguer/yay/v12/pkg/dep"
"github.com/Jguer/yay/v12/pkg/settings"
"github.com/Jguer/yay/v12/pkg/settings/exe"
"github.com/Jguer/yay/v12/pkg/text"
"github.com/Jguer/yay/v12/pkg/vcs"
)
func newTestLogger() *text.Logger {
return text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), true, "test")
}
func TestNewService(t *testing.T) {
dbExecutor := &mock.DBExecutor{}
cfg := &settings.Configuration{}
cmdBuilder := &exe.MockBuilder{}
vcsStore := &vcs.Mock{}
pkgBuildDirs := map[string]string{
"jellyfin": "../../../testdata/jfin",
"cephbin": "../../../testdata/cephbin",
}
srv, err := NewService(dbExecutor, cfg, newTestLogger(), cmdBuilder, vcsStore, pkgBuildDirs)
assert.NoError(t, err)
assert.NotNil(t, srv)
assert.Equal(t, dbExecutor, srv.dbExecutor)
assert.Equal(t, cfg, srv.cfg)
assert.Equal(t, cmdBuilder, srv.cmdBuilder)
assert.Equal(t, vcsStore, srv.vcsStore)
assert.Equal(t, pkgBuildDirs, srv.pkgBuildDirs)
assert.NotNil(t, srv.srcInfos)
}
func TestService_IncompatiblePkgs(t *testing.T) {
srv := &Service{
dbExecutor: &mock.DBExecutor{AlpmArchitecturesFn: func() ([]string, error) {
return []string{"x86_64"}, nil
}},
srcInfos: map[string]*gosrc.Srcinfo{
"pkg1": {
Package: gosrc.Package{
Arch: []string{"x86_64", "any"},
},
},
"pkg2": {
Package: gosrc.Package{
Arch: []string{"any"},
},
},
"pkg3": {
Package: gosrc.Package{
Arch: []string{"armv7h"},
},
},
"pkg4": {
Package: gosrc.Package{
Arch: []string{"i683", "x86_64"},
},
},
},
}
incompatible, err := srv.IncompatiblePkgs(context.Background())
assert.NoError(t, err)
assert.ElementsMatch(t, []string{"pkg3"}, incompatible)
}
func TestService_CheckPGPKeys(t *testing.T) {
srv := &Service{
log: newTestLogger(),
pkgBuildDirs: map[string]string{
"pkg1": "/path/to/pkg1",
"pkg2": "/path/to/pkg2",
},
srcInfos: map[string]*gosrc.Srcinfo{
"pkg1": {
Packages: []gosrc.Package{
{Pkgname: "pkg1"},
},
},
"pkg2": {
Packages: []gosrc.Package{
{Pkgname: "pkg2"},
},
},
},
}
err := srv.CheckPGPKeys(context.Background())
assert.NoError(t, err)
}
func TestService_UpdateVCSStore(t *testing.T) {
srv := &Service{
srcInfos: map[string]*gosrc.Srcinfo{
"pkg1": {
Packages: []gosrc.Package{
{Pkgname: "pkg1"},
},
},
"pkg2": {
Packages: []gosrc.Package{
{Pkgname: "pkg2"},
},
},
},
vcsStore: &vcs.Mock{},
}
targets := []map[string]*dep.InstallInfo{
{
"pkg1": {},
"pkg2": {},
},
}
ignore := map[string]error{}
err := srv.UpdateVCSStore(context.Background(), targets, ignore)
assert.NoError(t, err)
}
================================================
FILE: pkg/sync/sync.go
================================================
package sync
import (
"context"
"github.com/Jguer/yay/v12/pkg/completion"
"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/parser"
"github.com/Jguer/yay/v12/pkg/sync/build"
"github.com/Jguer/yay/v12/pkg/sync/srcinfo"
"github.com/Jguer/yay/v12/pkg/sync/workdir"
"github.com/Jguer/yay/v12/pkg/text"
"github.com/leonelquinteros/gotext"
)
type OperationService struct {
ctx context.Context
cfg *settings.Configuration
dbExecutor db.Executor
logger *text.Logger
}
func NewOperationService(ctx context.Context,
dbExecutor db.Executor,
run *runtime.Runtime,
) *OperationService {
return &OperationService{
ctx: ctx,
cfg: run.Cfg,
dbExecutor: dbExecutor,
logger: run.Logger.Child("operation"),
}
}
func (o *OperationService) Run(ctx context.Context, run *runtime.Runtime,
cmdArgs *parser.Arguments,
targets []map[string]*dep.InstallInfo, excluded []string,
) error {
if len(targets) == 0 {
o.logger.Println("", gotext.Get("there is nothing to do"))
return nil
}
preparer := workdir.NewPreparer(o.dbExecutor, run.CmdBuilder, o.cfg, o.logger.Child("workdir"))
installer := build.NewInstaller(o.dbExecutor, run.CmdBuilder,
run.VCSStore, o.cfg.Mode, o.cfg.ReBuild,
cmdArgs.ExistsArg("w", "downloadonly"), run.Logger.Child("installer"))
shouldInstall := !cmdArgs.ExistsArg("w", "downloadonly")
if cmdArgs.Op == "B" && !cmdArgs.ExistsArg("i", "install") {
shouldInstall = false
}
installer.SetInstallBuiltPackages(shouldInstall)
pkgBuildDirs, errInstall := preparer.Run(ctx, run, targets)
if errInstall != nil {
return errInstall
}
if cleanFunc := preparer.ShouldCleanMakeDeps(run, cmdArgs); cleanFunc != nil {
installer.AddPostInstallHook(cleanFunc)
}
if cleanAURDirsFunc := preparer.ShouldCleanAURDirs(run, pkgBuildDirs); cleanAURDirsFunc != nil {
installer.AddPostInstallHook(cleanAURDirsFunc)
}
if completion.NeedsUpdate(o.cfg.CompletionPath, o.cfg.CompletionInterval, false) {
go func() {
errComp := completion.UpdateCache(ctx, run.HTTPClient, o.dbExecutor,
o.cfg.AURURL, o.cfg.CompletionPath, o.logger)
if errComp != nil {
o.logger.Warnln(errComp)
}
}()
}
srcInfo, errInstall := srcinfo.NewService(o.dbExecutor, o.cfg,
o.logger.Child("srcinfo"), run.CmdBuilder, run.VCSStore, pkgBuildDirs)
if errInstall != nil {
return errInstall
}
incompatible, errInstall := srcInfo.IncompatiblePkgs(ctx)
if errInstall != nil {
return errInstall
}
if errIncompatible := confirmIncompatible(o.logger, incompatible); errIncompatible != nil {
return errIncompatible
}
if errPGP := srcInfo.CheckPGPKeys(ctx); errPGP != nil {
return errPGP
}
if errInstall := installer.Install(ctx, cmdArgs, targets, pkgBuildDirs,
excluded, o.manualConfirmRequired(cmdArgs)); errInstall != nil {
return errInstall
}
var multiErr multierror.MultiError
failedAndIgnored, err := installer.CompileFailedAndIgnored()
if err != nil {
multiErr.Add(err)
}
if !cmdArgs.ExistsArg("w", "downloadonly") {
if err := srcInfo.UpdateVCSStore(ctx, targets, failedAndIgnored); err != nil {
o.logger.Warnln(err)
}
}
if err := installer.RunPostInstallHooks(ctx); err != nil {
multiErr.Add(err)
}
return multiErr.Return()
}
func (o *OperationService) manualConfirmRequired(cmdArgs *parser.Arguments) bool {
return (!cmdArgs.ExistsArg("u", "sysupgrade") && cmdArgs.Op != "Y") || o.cfg.DoubleConfirm
}
func confirmIncompatible(logger *text.Logger, incompatible []string) error {
if len(incompatible) > 0 {
logger.Warnln(gotext.Get("The following packages are not compatible with your architecture:"))
for _, pkg := range incompatible {
logger.Print(" " + text.Cyan(pkg))
}
logger.Println()
if !logger.ContinueTask(gotext.Get("Try to build them anyway?"), true, settings.NoConfirm) {
return &settings.ErrUserAbort{}
}
}
return nil
}
================================================
FILE: pkg/sync/workdir/aur_source.go
================================================
package workdir
import (
"context"
"fmt"
"runtime"
"sync"
mapset "github.com/deckarep/golang-set/v2"
"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"
)
type ErrDownloadSource struct {
inner error
pkgName string
errOut string
}
func (e ErrDownloadSource) Error() string {
return fmt.Sprintln(gotext.Get("error downloading sources: %s", text.Cyan(e.pkgName)),
"\n\t context:", e.inner.Error(), "\n\t", e.errOut)
}
func (e *ErrDownloadSource) Unwrap() error {
return e.inner
}
func downloadPKGBUILDSource(ctx context.Context,
cmdBuilder exe.ICmdBuilder, pkgBuildDir string, installIncompatible bool,
) error {
args := []string{"--verifysource", "--skippgpcheck", "-f"}
if !cmdBuilder.GetKeepSrc() {
args = append(args, "-Cc")
}
if installIncompatible {
args = append(args, "--ignorearch")
}
err := cmdBuilder.Show(
cmdBuilder.BuildMakepkgCmd(ctx, pkgBuildDir, args...))
if err != nil {
return ErrDownloadSource{inner: err, pkgName: pkgBuildDir}
}
return nil
}
func downloadPKGBUILDSourceWorker(ctx context.Context, wg *sync.WaitGroup,
dirChannel <-chan string, valOut chan<- string, errOut chan<- error,
cmdBuilder exe.ICmdBuilder, incompatible bool,
) {
for pkgBuildDir := range dirChannel {
err := downloadPKGBUILDSource(ctx, cmdBuilder, pkgBuildDir, incompatible)
if err != nil {
errOut <- ErrDownloadSource{inner: err, pkgName: pkgBuildDir, errOut: ""}
} else {
valOut <- pkgBuildDir
}
}
wg.Done()
}
func downloadPKGBUILDSourceFanout(ctx context.Context, cmdBuilder exe.ICmdBuilder, pkgBuildDirs map[string]string,
incompatible bool, maxConcurrentDownloads int,
) error {
if len(pkgBuildDirs) == 0 {
return nil // no work to do
}
if len(pkgBuildDirs) == 1 {
for _, pkgBuildDir := range pkgBuildDirs {
return downloadPKGBUILDSource(ctx, cmdBuilder, pkgBuildDir, incompatible)
}
}
var (
numOfWorkers = runtime.NumCPU()
wg = &sync.WaitGroup{}
c = make(chan string)
fanInChanValues = make(chan string)
fanInChanErrors = make(chan error)
)
if maxConcurrentDownloads != 0 {
numOfWorkers = maxConcurrentDownloads
}
dedupSet := mapset.NewThreadUnsafeSet[string]()
go func() {
for _, pkgbuildDir := range pkgBuildDirs {
if !dedupSet.Contains(pkgbuildDir) {
c <- pkgbuildDir
dedupSet.Add(pkgbuildDir)
}
}
close(c)
}()
// Launch Workers
wg.Add(numOfWorkers)
for s := 0; s < numOfWorkers; s++ {
go downloadPKGBUILDSourceWorker(ctx, wg, c,
fanInChanValues, fanInChanErrors, cmdBuilder, incompatible)
}
go func() {
wg.Wait()
close(fanInChanValues)
close(fanInChanErrors)
}()
returnErr := multierror.MultiError{}
receiver:
for {
select {
case _, ok := <-fanInChanValues:
if !ok {
break receiver
}
case err, ok := <-fanInChanErrors:
if !ok {
break receiver
}
returnErr.Add(err)
}
}
return returnErr.Return()
}
================================================
FILE: pkg/sync/workdir/aur_source_test.go
================================================
//go:build !integration
// +build !integration
package workdir
import (
"context"
"fmt"
"os/exec"
"path/filepath"
"sync/atomic"
"testing"
"github.com/stretchr/testify/assert"
"github.com/Jguer/yay/v12/pkg/multierror"
"github.com/Jguer/yay/v12/pkg/settings/exe"
)
type TestMakepkgBuilder struct {
exe.ICmdBuilder
parentBuilder *exe.CmdBuilder
test *testing.T
passes uint32
want string
wantDir string
showError error
}
func (z *TestMakepkgBuilder) BuildMakepkgCmd(ctx context.Context, dir string, extraArgs ...string) *exec.Cmd {
cmd := z.parentBuilder.BuildMakepkgCmd(ctx, dir, extraArgs...)
if z.want != "" {
assert.Contains(z.test, cmd.String(), z.want)
}
if z.GetKeepSrc() {
assert.NotContains(z.test, cmd.String(), "-Cc")
}
if z.wantDir != "" {
assert.Equal(z.test, z.wantDir, cmd.Dir)
}
atomic.AddUint32(&z.passes, 1)
return cmd
}
func (z *TestMakepkgBuilder) Show(cmd *exec.Cmd) error {
return z.showError
}
func (z *TestMakepkgBuilder) GetKeepSrc() bool {
return z.parentBuilder.KeepSrc
}
// GIVEN 1 package
// WHEN downloadPKGBUILDSource is called
// THEN 1 call should be made to makepkg with the specified parameters and dir
func Test_downloadPKGBUILDSource(t *testing.T) {
t.Parallel()
type testCase struct {
desc string
keepSrc bool
want string
}
testCases := []testCase{
{
desc: "keepsrc",
keepSrc: true,
want: "makepkg --nocheck --config /etc/not.conf --verifysource --skippgpcheck -f",
},
{
desc: "nokeepsrc",
keepSrc: false,
want: "makepkg --nocheck --config /etc/not.conf --verifysource --skippgpcheck -f -Cc",
},
}
for _, tc := range testCases {
t.Run(tc.desc, func(td *testing.T) {
cmdBuilder := &TestMakepkgBuilder{
parentBuilder: &exe.CmdBuilder{
MakepkgConfPath: "/etc/not.conf",
MakepkgFlags: []string{"--nocheck"},
MakepkgBin: "makepkg",
KeepSrc: tc.keepSrc,
},
test: t,
want: tc.want,
wantDir: "/tmp/yay-bin",
}
err := downloadPKGBUILDSource(context.Background(), cmdBuilder, filepath.Join("/tmp", "yay-bin"), false)
assert.NoError(t, err)
assert.Equal(t, 1, int(cmdBuilder.passes))
})
}
}
// GIVEN 1 package
// WHEN downloadPKGBUILDSource is called
// THEN 1 call should be made to makepkg which should return error
func Test_downloadPKGBUILDSourceError(t *testing.T) {
t.Parallel()
cmdBuilder := &TestMakepkgBuilder{
parentBuilder: &exe.CmdBuilder{MakepkgConfPath: "/etc/not.conf", MakepkgFlags: []string{"--nocheck"}, MakepkgBin: "makepkg"},
test: t,
want: "makepkg --nocheck --config /etc/not.conf --verifysource --skippgpcheck -f -Cc",
wantDir: "/tmp/yay-bin",
showError: &exec.ExitError{},
}
err := downloadPKGBUILDSource(context.Background(), cmdBuilder, filepath.Join("/tmp", "yay-bin"), false)
assert.Error(t, err)
assert.EqualError(t, err, "error downloading sources: \x1b[36m/tmp/yay-bin\x1b[0m \n\t context: \n\t \n")
}
// GIVEN 5 packages
// WHEN downloadPKGBUILDSourceFanout is called
// THEN 5 calls should be made to makepkg
func Test_downloadPKGBUILDSourceFanout(t *testing.T) {
t.Parallel()
pkgBuildDirs := map[string]string{
"yay": "/tmp/yay",
"yay-bin": "/tmp/yay-bin",
"yay-git": "/tmp/yay-git",
"yay-v11": "/tmp/yay-v11",
"yay-v12": "/tmp/yay-v12",
}
for _, maxConcurrentDownloads := range []int{0, 3} {
t.Run(fmt.Sprintf("maxconcurrentdownloads set to %d", maxConcurrentDownloads), func(t *testing.T) {
cmdBuilder := &TestMakepkgBuilder{
parentBuilder: &exe.CmdBuilder{
MakepkgConfPath: "/etc/not.conf",
MakepkgFlags: []string{"--nocheck"}, MakepkgBin: "makepkg",
},
test: t,
}
err := downloadPKGBUILDSourceFanout(context.Background(), cmdBuilder, pkgBuildDirs, true, maxConcurrentDownloads)
assert.NoError(t, err)
assert.Equal(t, 5, int(cmdBuilder.passes))
})
}
}
// GIVEN 1 package
// WHEN downloadPKGBUILDSourceFanout is called
// THEN 1 calls should be made to makepkg without concurrency
func Test_downloadPKGBUILDSourceFanoutNoCC(t *testing.T) {
t.Parallel()
cmdBuilder := &TestMakepkgBuilder{
parentBuilder: &exe.CmdBuilder{
MakepkgConfPath: "/etc/not.conf",
MakepkgFlags: []string{"--nocheck"}, MakepkgBin: "makepkg",
},
test: t,
}
pkgBuildDirs := map[string]string{"yay": "/tmp/yay"}
err := downloadPKGBUILDSourceFanout(context.Background(), cmdBuilder, pkgBuildDirs, false, 0)
assert.NoError(t, err)
assert.Equal(t, 1, int(cmdBuilder.passes))
}
// GIVEN 5 packages
// WHEN downloadPKGBUILDSourceFanout is called
// THEN 5 calls should be made to makepkg
func Test_downloadPKGBUILDSourceFanoutError(t *testing.T) {
t.Parallel()
cmdBuilder := &TestMakepkgBuilder{
parentBuilder: &exe.CmdBuilder{
MakepkgConfPath: "/etc/not.conf",
MakepkgFlags: []string{"--nocheck"}, MakepkgBin: "makepkg",
},
test: t,
showError: &exec.ExitError{},
}
pkgBuildDirs := map[string]string{
"yay": "/tmp/yay",
"yay-bin": "/tmp/yay-bin",
"yay-git": "/tmp/yay-git",
"yay-v11": "/tmp/yay-v11",
"yay-v12": "/tmp/yay-v12",
}
err := downloadPKGBUILDSourceFanout(context.Background(), cmdBuilder, pkgBuildDirs, false, 0)
assert.Error(t, err)
assert.Equal(t, 5, int(cmdBuilder.passes))
assert.Len(t, err.(*multierror.MultiError).Errors, 5)
}
================================================
FILE: pkg/sync/workdir/clean.go
================================================
package workdir
import (
"context"
"github.com/leonelquinteros/gotext"
"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"
)
func removeMake(ctx context.Context, config *settings.Configuration,
cmdBuilder exe.ICmdBuilder, makeDeps []string, cmdArgs *parser.Arguments,
) error {
removeArguments := cmdArgs.CopyGlobal()
err := removeArguments.AddArg("R", "s", "u")
if err != nil {
return err
}
for _, pkg := range makeDeps {
removeArguments.AddTarget(pkg)
}
oldValue := settings.NoConfirm
settings.NoConfirm = true
err = cmdBuilder.Show(cmdBuilder.BuildPacmanCmd(ctx,
removeArguments, config.Mode, settings.NoConfirm))
settings.NoConfirm = oldValue
return err
}
func cleanAfter(ctx context.Context, run *runtime.Runtime,
cmdBuilder exe.ICmdBuilder, pkgbuildDirs map[string]string,
) {
run.Logger.Println(gotext.Get("removing untracked AUR files from cache..."))
i := 0
for _, dir := range pkgbuildDirs {
run.Logger.OperationInfoln(gotext.Get("Cleaning (%d/%d): %s", i+1, len(pkgbuildDirs), text.Cyan(dir)))
_, stderr, err := cmdBuilder.Capture(
cmdBuilder.BuildGitCmd(
ctx, dir, "reset", "--hard", "HEAD"))
if err != nil {
run.Logger.Errorln(gotext.Get("error resetting %s: %s", dir, stderr))
}
if err := run.CmdBuilder.Show(
run.CmdBuilder.BuildGitCmd(
ctx, dir, "clean", "-fx", "--exclude", "*.pkg.*")); err != nil {
run.Logger.Errorln(err)
}
i++
}
}
================================================
FILE: pkg/sync/workdir/merge.go
================================================
package workdir
import (
"context"
"errors"
"github.com/leonelquinteros/gotext"
"github.com/Jguer/yay/v12/pkg/settings/exe"
)
func gitMerge(ctx context.Context, cmdBuilder exe.ICmdBuilder, dir string) error {
_, stderr, err := cmdBuilder.Capture(
cmdBuilder.BuildGitCmd(ctx,
dir, "reset", "--hard", "HEAD"))
if err != nil {
return errors.New(gotext.Get("error resetting %s: %s", dir, stderr))
}
_, stderr, err = cmdBuilder.Capture(
cmdBuilder.BuildGitCmd(ctx,
dir, "merge", "--no-edit", "--ff"))
if err != nil {
return errors.New(gotext.Get("error merging %s: %s", dir, stderr))
}
return nil
}
func mergePkgbuilds(ctx context.Context, cmdBuilder exe.ICmdBuilder, pkgbuildDirs map[string]string) error {
for _, dir := range pkgbuildDirs {
err := gitMerge(ctx, cmdBuilder, dir)
if err != nil {
return err
}
}
return nil
}
================================================
FILE: pkg/sync/workdir/preparer.go
================================================
package workdir
import (
"context"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"github.com/Jguer/yay/v12/pkg/db"
"github.com/Jguer/yay/v12/pkg/dep"
"github.com/Jguer/yay/v12/pkg/download"
"github.com/Jguer/yay/v12/pkg/menus"
"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/build"
"github.com/Jguer/yay/v12/pkg/text"
gosrc "github.com/Morganamilo/go-srcinfo"
mapset "github.com/deckarep/golang-set/v2"
"github.com/leonelquinteros/gotext"
)
type HookType string
const (
// PreDownloadSourcesHook is called before sourcing a package
PreDownloadSourcesHook HookType = "pre-download-sources"
)
type HookFn func(ctx context.Context, run *runtime.Runtime, w io.Writer,
pkgbuildDirsByBase map[string]string, installed mapset.Set[string],
) error
type Hook struct {
Name string
Hookfn HookFn
Type HookType
}
type Preparer struct {
dbExecutor db.Executor
cmdBuilder exe.ICmdBuilder
cfg *settings.Configuration
hooks []Hook
downloadSources bool
log *text.Logger
makeDeps []string
}
func NewPreparerWithoutHooks(dbExecutor db.Executor, cmdBuilder exe.ICmdBuilder,
cfg *settings.Configuration, logger *text.Logger, downloadSources bool,
) *Preparer {
return &Preparer{
dbExecutor: dbExecutor,
cmdBuilder: cmdBuilder,
cfg: cfg,
hooks: []Hook{},
downloadSources: downloadSources,
log: logger,
}
}
func NewPreparer(dbExecutor db.Executor, cmdBuilder exe.ICmdBuilder,
cfg *settings.Configuration, logger *text.Logger,
) *Preparer {
preper := NewPreparerWithoutHooks(dbExecutor, cmdBuilder, cfg, logger, true)
if cfg.CleanMenu {
preper.hooks = append(preper.hooks, Hook{
Name: "clean",
Hookfn: menus.CleanFn,
Type: PreDownloadSourcesHook,
})
}
if cfg.DiffMenu {
preper.hooks = append(preper.hooks, Hook{
Name: "diff",
Hookfn: menus.DiffFn,
Type: PreDownloadSourcesHook,
})
}
if cfg.EditMenu {
preper.hooks = append(preper.hooks, Hook{
Name: "edit",
Hookfn: menus.EditFn,
Type: PreDownloadSourcesHook,
})
}
return preper
}
func (preper *Preparer) ShouldCleanAURDirs(run *runtime.Runtime, pkgBuildDirs map[string]string) build.PostInstallHookFunc {
if !preper.cfg.CleanAfter || len(pkgBuildDirs) == 0 {
return nil
}
preper.log.Debugln("added post install hook to clean up AUR dirs", pkgBuildDirs)
return func(ctx context.Context) error {
cleanAfter(ctx, run, run.CmdBuilder, pkgBuildDirs)
return nil
}
}
func (preper *Preparer) ShouldCleanMakeDeps(run *runtime.Runtime, cmdArgs *parser.Arguments) build.PostInstallHookFunc {
if len(preper.makeDeps) == 0 {
return nil
}
switch preper.cfg.RemoveMake {
case "yes":
break
case "no":
return nil
default:
isYesDefault := preper.cfg.RemoveMake == "askyes"
if !preper.log.ContinueTask(gotext.Get("Remove make dependencies after install?"),
isYesDefault, settings.NoConfirm) {
return nil
}
}
preper.log.Debugln("added post install hook to clean up AUR makedeps", preper.makeDeps)
return func(ctx context.Context) error {
return removeMake(ctx, preper.cfg, run.CmdBuilder, preper.makeDeps, cmdArgs)
}
}
func (preper *Preparer) Run(ctx context.Context, run *runtime.Runtime,
targets []map[string]*dep.InstallInfo,
) (pkgbuildDirsByBase map[string]string, err error) {
preper.Present(targets)
pkgBuildDirs, err := preper.PrepareWorkspace(ctx, run, targets)
if err != nil {
return nil, err
}
return pkgBuildDirs, nil
}
func (preper *Preparer) Present(targets []map[string]*dep.InstallInfo) {
pkgsBySourceAndReason := map[string]map[string][]string{}
for _, layer := range targets {
for pkgName, info := range layer {
source := dep.SourceNames[info.Source]
reason := dep.ReasonNames[info.Reason]
var pkgStr string
if info.Version != "" {
pkgStr = text.Cyan(fmt.Sprintf("%s-%s", pkgName, info.Version))
} else {
pkgStr = text.Cyan(pkgName)
}
if _, ok := pkgsBySourceAndReason[source]; !ok {
pkgsBySourceAndReason[source] = map[string][]string{}
}
pkgsBySourceAndReason[source][reason] = append(pkgsBySourceAndReason[source][reason], pkgStr)
if info.Reason == dep.MakeDep {
preper.makeDeps = append(preper.makeDeps, pkgName)
}
}
}
for source, pkgsByReason := range pkgsBySourceAndReason {
for reason, pkgs := range pkgsByReason {
preper.log.Printf(text.Bold("%s %s (%d):")+" %s\n",
source,
reason,
len(pkgs),
strings.Join(pkgs, ", "))
}
}
}
func (preper *Preparer) PrepareWorkspace(ctx context.Context,
run *runtime.Runtime, targets []map[string]*dep.InstallInfo,
) (map[string]string, error) {
aurBasesToClone := mapset.NewThreadUnsafeSet[string]()
pkgBuildDirsByBase := make(map[string]string, len(targets))
for _, layer := range targets {
for _, info := range layer {
switch info.Source {
case dep.AUR:
pkgBase := *info.AURBase
pkgBuildDir := filepath.Join(preper.cfg.BuildDir, pkgBase)
if preper.needToCloneAURBase(info, pkgBuildDir) {
aurBasesToClone.Add(pkgBase)
}
pkgBuildDirsByBase[pkgBase] = pkgBuildDir
case dep.SrcInfo:
pkgBase := *info.AURBase
pkgBuildDirsByBase[pkgBase] = *info.SrcinfoPath
}
}
}
if _, errA := download.AURPKGBUILDRepos(ctx,
preper.cmdBuilder, preper.log.Child("download"), aurBasesToClone.ToSlice(),
preper.cfg.AURURL, preper.cfg.BuildDir, false); errA != nil {
return nil, errA
}
if !preper.downloadSources {
return pkgBuildDirsByBase, nil
}
if err := mergePkgbuilds(ctx, preper.cmdBuilder, pkgBuildDirsByBase); err != nil {
return nil, err
}
remoteNames := preper.dbExecutor.InstalledRemotePackageNames()
remoteNamesCache := mapset.NewThreadUnsafeSet(remoteNames...)
for _, hookFn := range preper.hooks {
if hookFn.Type == PreDownloadSourcesHook {
if err := hookFn.Hookfn(ctx, run, os.Stdout, pkgBuildDirsByBase, remoteNamesCache); err != nil {
return nil, err
}
}
}
if errP := downloadPKGBUILDSourceFanout(ctx, preper.cmdBuilder,
pkgBuildDirsByBase, false, preper.cfg.MaxConcurrentDownloads); errP != nil {
preper.log.Errorln(errP)
}
return pkgBuildDirsByBase, nil
}
func (preper *Preparer) needToCloneAURBase(installInfo *dep.InstallInfo, pkgbuildDir string) bool {
if preper.cfg.ReDownload == "all" {
return true
}
srcinfoFile := filepath.Join(pkgbuildDir, ".SRCINFO")
if pkgbuild, err := gosrc.ParseFile(srcinfoFile); err == nil {
if db.VerCmp(pkgbuild.Version(), installInfo.Version) >= 0 {
preper.log.OperationInfoln(
gotext.Get("PKGBUILD up to date, skipping download: %s",
text.Cyan(*installInfo.AURBase)))
return false
}
}
return true
}
================================================
FILE: pkg/sync/workdir/preparer_test.go
================================================
//go:build !integration
// +build !integration
package workdir
import (
"io"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/Jguer/yay/v12/pkg/settings"
"github.com/Jguer/yay/v12/pkg/text"
)
func newTestLogger() *text.Logger {
return text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), true, "test")
}
// Test order of pre-download-sources hooks
func TestPreDownloadSourcesHooks(t *testing.T) {
testCases := []struct {
name string
cfg *settings.Configuration
wantHook []string
}{
{
name: "clean, diff, edit",
cfg: &settings.Configuration{
CleanMenu: true,
DiffMenu: true,
EditMenu: true,
},
wantHook: []string{"clean", "diff", "edit"},
},
{
name: "clean, edit",
cfg: &settings.Configuration{
CleanMenu: true,
DiffMenu: false,
EditMenu: true,
},
wantHook: []string{"clean", "edit"},
},
{
name: "clean, diff",
cfg: &settings.Configuration{
CleanMenu: true,
DiffMenu: true,
EditMenu: false,
},
wantHook: []string{"clean", "diff"},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
preper := NewPreparer(nil, nil, tc.cfg, newTestLogger())
assert.Len(t, preper.hooks, len(tc.wantHook))
got := make([]string, 0, len(preper.hooks))
for _, hook := range preper.hooks {
got = append(got, hook.Name)
}
assert.Equal(t, tc.wantHook, got)
})
}
}
================================================
FILE: pkg/text/color.go
================================================
package text
import "fmt"
const (
redCode = "\x1b[31m"
greenCode = "\x1b[32m"
yellowCode = "\x1b[33m"
blueCode = "\x1b[34m"
magentaCode = "\x1b[35m"
CyanCode = "\x1b[36m"
boldCode = "\x1b[1m"
ResetCode = "\x1b[0m"
)
// UseColor determines if package will emit colors.
var UseColor = true
func stylize(startCode, in string) string {
if UseColor {
return startCode + in + ResetCode
}
return in
}
func Red(in string) string {
return stylize(redCode, in)
}
func Green(in string) string {
return stylize(greenCode, in)
}
func yellow(in string) string {
return stylize(yellowCode, in)
}
func Cyan(in string) string {
return stylize(CyanCode, in)
}
func Magenta(in string) string {
return stylize(magentaCode, in)
}
func Blue(in string) string {
return stylize(blueCode, in)
}
func Bold(in string) string {
return stylize(boldCode, in)
}
// ColorHash Colors text using a hashing algorithm. The same text will always produce the
// same color while different text will produce a different color.
func ColorHash(name string) (output string) {
if !UseColor {
return name
}
var hash uint = 5381
for i := 0; i < len(name); i++ {
hash = uint(name[i]) + ((hash << 5) + (hash))
}
return fmt.Sprintf("\x1b[%dm%s\x1b[0m", hash%6+31, name)
}
================================================
FILE: pkg/text/convert.go
================================================
package text
import (
"fmt"
)
// Human method returns results in human readable format.
func Human(size int64) string {
floatsize := float32(size)
units := [...]string{"", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi", "Yi"}
for _, unit := range units {
if floatsize < 1024 {
return fmt.Sprintf("%.1f %sB", floatsize, unit)
}
floatsize /= 1024
}
return fmt.Sprintf("%d%s", size, "B")
}
================================================
FILE: pkg/text/errors.go
================================================
package text
import "github.com/leonelquinteros/gotext"
type ErrInputOverflow struct{}
func (e ErrInputOverflow) Error() string {
return gotext.Get("input too long")
}
================================================
FILE: pkg/text/input.go
================================================
package text
import (
"bufio"
"fmt"
"strings"
"unicode"
"unicode/utf8"
"github.com/leonelquinteros/gotext"
)
func (l *Logger) GetInput(defaultValue string, noConfirm bool) (string, error) {
l.Info()
if defaultValue != "" || noConfirm {
l.Println(defaultValue)
return defaultValue, nil
}
reader := bufio.NewReader(l.r)
buf, overflow, err := reader.ReadLine()
if err != nil {
return "", err
}
if overflow {
return "", ErrInputOverflow{}
}
return string(buf), nil
}
// ContinueTask prompts if user wants to continue task.
// If NoConfirm is set the action will continue without user input.
func (l *Logger) ContinueTask(s string, preset, noConfirm bool) bool {
if noConfirm {
return preset
}
var (
response string
postFix string
n string
y string
yes = gotext.Get("yes")
no = gotext.Get("no")
)
// Only use localized "y" and "n" if they are latin characters.
if nRune, _ := utf8.DecodeRuneInString(no); unicode.Is(unicode.Latin, nRune) {
n = string(nRune)
} else {
n = nDefault
}
if yRune, _ := utf8.DecodeRuneInString(yes); unicode.Is(unicode.Latin, yRune) {
y = string(yRune)
} else {
y = yDefault
}
if preset { // If default behavior is true, use y as default.
postFix = fmt.Sprintf(" [%s/%s] ", strings.ToUpper(y), n)
} else { // If default behavior is anything else, use n as default.
postFix = fmt.Sprintf(" [%s/%s] ", y, strings.ToUpper(n))
}
l.OperationInfo(Bold(s), Bold(postFix))
if _, err := fmt.Fscanln(l.r, &response); err != nil {
return preset
}
return strings.EqualFold(response, yes) ||
strings.EqualFold(response, y) ||
(!strings.EqualFold(yDefault, n) && strings.EqualFold(response, yDefault))
}
================================================
FILE: pkg/text/service.go
================================================
package text
import (
"fmt"
"io"
)
const (
arrow = "==>"
smallArrow = " ->"
opSymbol = "::"
)
type Logger struct {
name string
Debug bool
stdout io.Writer
stderr io.Writer
r io.Reader
}
func NewLogger(stdout, stderr io.Writer, r io.Reader, debug bool, name string) *Logger {
return &Logger{
Debug: debug,
name: name,
r: r,
stderr: stderr,
stdout: stdout,
}
}
func (l *Logger) Child(name string) *Logger {
return NewLogger(l.stdout, l.stderr, l.r, l.Debug, name)
}
func (l *Logger) Debugln(a ...any) {
if !l.Debug {
return
}
l.Println(append([]any{
Bold(yellow(fmt.Sprintf("[DEBUG:%s]", l.name))),
}, a...)...)
}
func (l *Logger) OperationInfoln(a ...any) {
l.Println(l.SprintOperationInfo(a...))
}
func (l *Logger) OperationInfo(a ...any) {
l.Print(l.SprintOperationInfo(a...))
}
func (l *Logger) SprintOperationInfo(a ...any) string {
return fmt.Sprint(append([]any{Bold(Cyan(opSymbol + " ")), boldCode}, a...)...) + ResetCode
}
func (l *Logger) Info(a ...any) {
l.Print(append([]any{Bold(Green(arrow + " "))}, a...)...)
}
func (l *Logger) Infoln(a ...any) {
l.Println(append([]any{Bold(Green(arrow))}, a...)...)
}
func (l *Logger) Warn(a ...any) {
l.Print(l.SprintWarn(a...))
}
func (l *Logger) Warnln(a ...any) {
l.Println(l.SprintWarn(a...))
}
func (l *Logger) SprintWarn(a ...any) string {
return fmt.Sprint(append([]any{Bold(yellow(smallArrow + " "))}, a...)...)
}
func (l *Logger) Error(a ...any) {
fmt.Fprint(l.stderr, l.SprintError(a...))
}
func (l *Logger) Errorln(a ...any) {
fmt.Fprintln(l.stderr, l.SprintError(a...))
}
func (l *Logger) SprintError(a ...any) string {
return fmt.Sprint(append([]any{Bold(Red(smallArrow + " "))}, a...)...)
}
func (l *Logger) Printf(format string, a ...any) {
fmt.Fprintf(l.stdout, format, a...)
}
func (l *Logger) Println(a ...any) {
fmt.Fprintln(l.stdout, a...)
}
func (l *Logger) Print(a ...any) {
fmt.Fprint(l.stdout, a...)
}
================================================
FILE: pkg/text/text.go
================================================
package text
import (
"strings"
"unicode"
)
const (
yDefault = "y"
nDefault = "n"
)
// SplitDBFromName 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]
}
// LessRunes compares two rune values, and returns true if the first argument is lexicographicaly smaller.
func LessRunes(iRunes, jRunes []rune) bool {
maxLen := min(len(iRunes), len(jRunes))
for idx := 0; idx < maxLen; idx++ {
ir := iRunes[idx]
jr := jRunes[idx]
lir := unicode.ToLower(ir)
ljr := unicode.ToLower(jr)
if lir != ljr {
return lir < ljr
}
// the lowercase runes are the same, so compare the original
if ir != jr {
return ir < jr
}
}
return len(iRunes) < len(jRunes)
}
var RepoUrls = map[string]string{
"core": "https://archlinux.org/packages/core",
"core-testing": "https://archlinux.org/packages/core-testing",
"extra": "https://archlinux.org/packages/extra",
"extra-testing": "https://archlinux.org/packages/extra-testing",
"gnome-unstable": "https://archlinux.org/packages/gnome-unstable",
"kde-unstable": "https://archlinux.org/packages/kde-unstable",
"multilib": "https://archlinux.org/packages/multilib",
"multilib-testing": "https://archlinux.org/packages/multilib-testing",
"testing": "https://archlinux.org/packages/testing",
"aur": "https://aur.archlinux.org/packages",
"devel": "https://aur.archlinux.org/packages",
}
func CreateOSC8Link(url, text string) string {
osc8Start := "\033]8;;" + url + "\033\\"
osc8End := "\033]8;;\033\\"
return osc8Start + text + osc8End
}
func CreateRepoLink(repo, arch, pkgName, text string) string {
if !UseColor {
return text
}
urlBase, ok := RepoUrls[repo]
if !ok {
return text
}
var url string
if repo == "aur" || repo == "devel" {
url = urlBase + "/" + pkgName
} else {
url = urlBase + "/" + arch + "/" + pkgName
}
return CreateOSC8Link(url, text)
}
================================================
FILE: pkg/text/text_test.go
================================================
//go:build !integration
// +build !integration
package text
import (
"io"
"os"
"path"
"strings"
"testing"
"github.com/leonelquinteros/gotext"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestLessRunes(t *testing.T) {
t.Parallel()
type args struct {
iRunes []rune
jRunes []rune
}
tests := []struct {
name string
args args
want bool
}{
{name: "nilslices", args: args{iRunes: nil, jRunes: nil}, want: false},
{name: "emptyslices", args: args{iRunes: []rune{}, jRunes: []rune{}}, want: false},
{name: "simpleslice a,b", args: args{iRunes: []rune{'a'}, jRunes: []rune{'b'}}, want: true},
{name: "simpleslice b,a", args: args{iRunes: []rune{'b'}, jRunes: []rune{'a'}}, want: false},
{name: "equalslice", args: args{iRunes: []rune{'a', 'a', 'a'}, jRunes: []rune{'a', 'a', 'a'}}, want: false},
{name: "uppercase", args: args{iRunes: []rune{'a'}, jRunes: []rune{'A'}}, want: false},
{name: "longerFirstArg", args: args{iRunes: []rune{'a', 'b'}, jRunes: []rune{'a'}}, want: false},
{name: "longerSecondArg", args: args{iRunes: []rune{'a'}, jRunes: []rune{'a', 'b'}}, want: true},
{name: "utf8 less", args: args{iRunes: []rune{'世', '2', '0'}, jRunes: []rune{'世', '界', '3'}}, want: true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
got := LessRunes(tt.args.iRunes, tt.args.jRunes)
assert.Equal(t, tt.want, got)
})
}
}
func TestContinueTask(t *testing.T) {
t.Parallel()
type args struct {
s string
preset bool
noConfirm bool
input string
}
tests := []struct {
name string
args args
want bool
}{
{name: "noconfirm-true", args: args{s: "", preset: true, noConfirm: true}, want: true},
{name: "noconfirm-false", args: args{s: "", preset: false, noConfirm: true}, want: false},
{name: "noinput-false", args: args{s: "", preset: false, noConfirm: false}, want: false},
{name: "noinput-true", args: args{s: "", preset: true, noConfirm: false}, want: true},
{name: "input-false", args: args{s: "", input: "n", preset: true, noConfirm: false}, want: false},
{name: "input-true", args: args{s: "", input: "y", preset: false, noConfirm: false}, want: true},
{name: "input-false-complete", args: args{s: "", input: "no", preset: true, noConfirm: false}, want: false},
{name: "input-true-complete", args: args{s: "", input: "yes", preset: false, noConfirm: false}, want: true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// create io.Reader with value of input
in := strings.NewReader(tt.args.input)
logger := NewLogger(io.Discard, io.Discard, in, false, "test")
got := logger.ContinueTask(tt.args.s, tt.args.preset, tt.args.noConfirm)
require.Equal(t, tt.want, got)
})
}
}
func TestContinueTaskRU(t *testing.T) {
strCustom := `
msgid "yes"
msgstr "да"
`
// Create Locales directory and files on temp location
tmpDir := t.TempDir()
dirname := path.Join(tmpDir, "en_US")
err := os.MkdirAll(dirname, os.ModePerm)
require.NoError(t, err)
fDefault, err := os.Create(path.Join(dirname, "yay.po"))
require.NoError(t, err)
defer fDefault.Close()
_, err = fDefault.WriteString(strCustom)
require.NoError(t, err)
gotext.Configure(tmpDir, "en_US", "yay")
require.Equal(t, "да", gotext.Get("yes"))
type args struct {
s string
preset bool
noConfirm bool
input string
}
tests := []struct {
name string
args args
want bool
}{
{name: "default input false", args: args{s: "", input: "n", preset: true, noConfirm: false}, want: false},
{name: "default input true", args: args{s: "", input: "y", preset: false, noConfirm: false}, want: true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
in := strings.NewReader(tt.args.input)
logger := NewLogger(io.Discard, io.Discard, in, false, "test")
got := logger.ContinueTask(tt.args.s, tt.args.preset, tt.args.noConfirm)
require.Equal(t, tt.want, got)
})
}
gotext.SetLanguage("")
}
func TestContinueTaskDE(t *testing.T) {
strCustom := `
msgid "yes"
msgstr "ja"
`
// Create Locales directory and files on temp location
tmpDir := t.TempDir()
dirname := path.Join(tmpDir, "en_US")
err := os.MkdirAll(dirname, os.ModePerm)
require.NoError(t, err)
fDefault, err := os.Create(path.Join(dirname, "yay.po"))
require.NoError(t, err)
defer fDefault.Close()
_, err = fDefault.WriteString(strCustom)
require.NoError(t, err)
gotext.Configure(tmpDir, "en_US", "yay")
require.Equal(t, "ja", gotext.Get("yes"))
type args struct {
s string
preset bool
noConfirm bool
input string
}
tests := []struct {
name string
args args
want bool
}{
{name: "default input false", args: args{s: "", input: "n", preset: true, noConfirm: false}, want: false},
{name: "default input true", args: args{s: "", input: "y", preset: false, noConfirm: false}, want: true},
{name: "custom input true", args: args{s: "", input: "j", preset: false, noConfirm: false}, want: true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
in := strings.NewReader(tt.args.input)
logger := NewLogger(io.Discard, io.Discard, in, false, "test")
got := logger.ContinueTask(tt.args.s, tt.args.preset, tt.args.noConfirm)
require.Equal(t, tt.want, got)
})
}
gotext.SetLanguage("")
}
func TestCreateRepoLink(t *testing.T) {
tests := []struct {
name string
useColor bool
repo string
arch string
pkgName string
text string
want string
}{
{
name: "color disabled returns text",
useColor: false,
repo: "core",
arch: "x86_64",
pkgName: "linux",
text: "core/linux",
want: "core/linux",
},
{
name: "unknown repo returns text",
useColor: true,
repo: "unknown",
arch: "x86_64",
pkgName: "linux",
text: "core/linux",
want: "core/linux",
},
{
name: "aur repo uses package url",
useColor: true,
repo: "aur",
arch: "any",
pkgName: "yay",
text: "aur/yay",
want: "\033]8;;https://aur.archlinux.org/packages/yay\033\\aur/yay\033]8;;\033\\",
},
{
name: "core repo uses arch in url",
useColor: true,
repo: "core",
arch: "x86_64",
pkgName: "linux",
text: "core/linux",
want: "\033]8;;https://archlinux.org/packages/core/x86_64/linux\033\\core/linux\033]8;;\033\\",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
original := UseColor
UseColor = tt.useColor
t.Cleanup(func() { UseColor = original })
got := CreateRepoLink(tt.repo, tt.arch, tt.pkgName, tt.text)
assert.Equal(t, tt.want, got)
})
}
}
================================================
FILE: pkg/text/time.go
================================================
package text
import "time"
// Formats a unix timestamp to ISO 8601 date (yyyy-mm-dd).
func FormatTime(i int) string {
t := time.Unix(int64(i), 0)
return t.Format("2006-01-02")
}
// Formats a unix timestamp to ISO 8601 date (Mon 02 Jan 2006 03:04:05 PM MST).
func FormatTimeQuery(i int) string {
t := time.Unix(int64(i), 0)
return t.Format("Mon 02 Jan 2006 03:04:05 PM MST")
}
================================================
FILE: pkg/upgrade/.snapshots/Test_upAUR-No_Updates
================================================
[1m[33m ->[0m[0m [36mignored[0m: ignoring package upgrade ([31m1.0.0[0m => [32m2.0.0[0m)
================================================
FILE: pkg/upgrade/.snapshots/Test_upAUR-Simple_Update
================================================
================================================
FILE: pkg/upgrade/.snapshots/Test_upAUR-Time_Update
================================================
================================================
FILE: pkg/upgrade/service.go
================================================
package upgrade
import (
"context"
"fmt"
"sort"
"strings"
"github.com/Jguer/aur"
alpm "github.com/Jguer/dyalpm"
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"
"github.com/Jguer/yay/v12/pkg/dep/topo"
"github.com/Jguer/yay/v12/pkg/intrange"
"github.com/Jguer/yay/v12/pkg/multierror"
"github.com/Jguer/yay/v12/pkg/query"
"github.com/Jguer/yay/v12/pkg/settings"
"github.com/Jguer/yay/v12/pkg/text"
"github.com/Jguer/yay/v12/pkg/vcs"
)
const cutOffExtra = 2
type UpgradeService struct {
grapher *dep.Grapher
aurCache aur.QueryClient
dbExecutor db.Executor
vcsStore vcs.Store
cfg *settings.Configuration
log *text.Logger
noConfirm bool
AURWarnings *query.AURWarnings
}
func NewUpgradeService(grapher *dep.Grapher, aurCache aur.QueryClient,
dbExecutor db.Executor, vcsStore vcs.Store,
cfg *settings.Configuration, noConfirm bool, logger *text.Logger,
) *UpgradeService {
return &UpgradeService{
grapher: grapher,
aurCache: aurCache,
dbExecutor: dbExecutor,
vcsStore: vcsStore,
cfg: cfg,
noConfirm: noConfirm,
log: logger,
AURWarnings: query.NewWarnings(logger.Child("warnings")),
}
}
// upGraph adds packages to upgrade to the graph.
func (u *UpgradeService) upGraph(ctx context.Context, graph *topo.Graph[string, *dep.InstallInfo],
enableDowngrade bool,
filter Filter,
) (err error) {
var (
develUp UpSlice
errs multierror.MultiError
aurdata = make(map[string]*aur.Pkg)
aurUp UpSlice
)
remote := u.dbExecutor.InstalledRemotePackages()
remoteNames := u.dbExecutor.InstalledRemotePackageNames()
if u.cfg.Mode.AtLeastAUR() {
u.log.OperationInfoln(gotext.Get("Searching AUR for updates..."))
_aurdata, err := u.aurCache.Get(ctx, &aur.Query{Needles: remoteNames, By: aur.Name})
errs.Add(err)
if err == nil {
for i := range _aurdata {
pkg := &_aurdata[i]
aurdata[pkg.Name] = pkg
u.AURWarnings.AddToWarnings(remote, pkg)
}
u.AURWarnings.CalculateMissing(remoteNames, remote, aurdata)
aurUp = UpAUR(u.log, remote, aurdata, enableDowngrade)
if u.cfg.Devel {
u.log.OperationInfoln(gotext.Get("Checking development packages..."))
develUp = UpDevel(ctx, u.log, remote, aurdata, u.vcsStore)
u.vcsStore.CleanOrphans(remote)
}
}
}
aurPkgsAdded := []*aur.Pkg{}
names := mapset.NewThreadUnsafeSet[string]()
for i := range develUp.Up {
up := &develUp.Up[i]
// check if deps are satisfied for aur packages
reason := dep.Explicit
if up.Reason == alpm.PkgReasonDepend {
reason = dep.Dep
}
if filter != nil && !filter(up) {
continue
}
aurPkg := aurdata[up.Name]
graph = u.grapher.GraphAURTarget(ctx, graph, aurPkg, &dep.InstallInfo{
Reason: reason,
Source: dep.AUR,
AURBase: &aurPkg.PackageBase,
Upgrade: true,
Devel: true,
LocalVersion: up.LocalVersion,
Version: up.RemoteVersion,
})
names.Add(up.Name)
aurPkgsAdded = append(aurPkgsAdded, aurPkg)
}
for i := range aurUp.Up {
up := &aurUp.Up[i]
// add devel packages if they are not already in the list
if names.Contains(up.Name) {
continue
}
// check if deps are satisfied for aur packages
reason := dep.Explicit
if up.Reason == alpm.PkgReasonDepend {
reason = dep.Dep
}
if filter != nil && !filter(up) {
continue
}
aurPkg := aurdata[up.Name]
graph = u.grapher.GraphAURTarget(ctx, graph, aurPkg, &dep.InstallInfo{
Reason: reason,
Source: dep.AUR,
AURBase: &aurPkg.PackageBase,
Upgrade: true,
Version: up.RemoteVersion,
LocalVersion: up.LocalVersion,
})
aurPkgsAdded = append(aurPkgsAdded, aurPkg)
}
u.grapher.AddDepsForPkgs(ctx, aurPkgsAdded, graph)
if u.cfg.Mode.AtLeastRepo() {
u.log.OperationInfoln(gotext.Get("Searching databases for updates..."))
syncUpgrades, err := u.dbExecutor.SyncUpgrades(enableDowngrade)
for _, up := range syncUpgrades {
if filter != nil && !filter(&db.Upgrade{
Name: up.Package.Name(),
RemoteVersion: up.Package.Version(),
Repository: up.Package.DB().Name(),
Base: up.Package.Base(),
LocalVersion: up.LocalVersion,
Reason: up.Reason,
}) {
continue
}
upgradeInfo := up
graph = u.grapher.GraphSyncPkg(ctx, graph, up.Package, &upgradeInfo)
}
errs.Add(err)
}
return errs.Return()
}
func (u *UpgradeService) graphToUpSlice(graph *topo.Graph[string, *dep.InstallInfo]) (aurUp, repoUp UpSlice) {
aurUp = UpSlice{Up: make([]Upgrade, 0, graph.Len())}
repoUp = UpSlice{Up: make([]Upgrade, 0, graph.Len()), Repos: u.dbExecutor.Repos()}
_ = graph.ForEach(func(name string, info *dep.InstallInfo) error {
alpmReason := alpm.PkgReasonDepend
if info.Reason == dep.Explicit {
alpmReason = alpm.PkgReasonExplicit
}
parents := graph.ImmediateDependencies(name)
extra := ""
if len(parents) > 0 && !info.Upgrade && info.Reason == dep.MakeDep {
reducedParents := parents.Slice()[:min(cutOffExtra, len(parents))]
if len(parents) > cutOffExtra {
reducedParents = append(reducedParents, "...")
}
extra = fmt.Sprintf(" (%s of %s)", dep.ReasonNames[info.Reason], strings.Join(reducedParents, ", "))
}
switch info.Source {
case dep.AUR:
aurRepo := "aur"
if info.Devel {
aurRepo = "devel"
}
aurUp.Up = append(aurUp.Up, Upgrade{
Name: name,
RemoteVersion: info.Version,
Repository: aurRepo,
Base: *info.AURBase,
LocalVersion: info.LocalVersion,
Reason: alpmReason,
Extra: extra,
})
case dep.Sync:
repoUp.Up = append(repoUp.Up, Upgrade{
Name: name,
RemoteVersion: info.Version,
Repository: *info.SyncDBName,
Base: "",
LocalVersion: info.LocalVersion,
Reason: alpmReason,
Extra: extra,
})
}
return nil
})
return aurUp, repoUp
}
func (u *UpgradeService) GraphUpgrades(ctx context.Context,
graph *topo.Graph[string, *dep.InstallInfo],
enableDowngrade bool, filter Filter,
) (*topo.Graph[string, *dep.InstallInfo], error) {
if graph == nil {
graph = dep.NewGraph()
}
err := u.upGraph(ctx, graph, enableDowngrade, filter)
if err != nil {
return graph, err
}
return graph, nil
}
// userExcludeUpgrades asks the user which packages to exclude from the upgrade and
// removes them from the graph
func (u *UpgradeService) UserExcludeUpgrades(graph *topo.Graph[string, *dep.InstallInfo]) ([]string, error) {
if graph.Len() == 0 {
return []string{}, nil
}
aurUp, repoUp := u.graphToUpSlice(graph)
sort.Sort(repoUp)
sort.Sort(aurUp)
allUp := UpSlice{Repos: append(repoUp.Repos, aurUp.Repos...)}
for _, up := range repoUp.Up {
if up.LocalVersion == "" && up.Reason != alpm.PkgReasonExplicit {
allUp.PulledDeps = append(allUp.PulledDeps, up)
} else {
allUp.Up = append(allUp.Up, up)
}
}
for _, up := range aurUp.Up {
if up.LocalVersion == "" && up.Reason != alpm.PkgReasonExplicit {
allUp.PulledDeps = append(allUp.PulledDeps, up)
} else {
allUp.Up = append(allUp.Up, up)
}
}
if len(allUp.PulledDeps) > 0 {
u.log.Printf("%s"+text.Bold(" %d ")+"%s\n", text.Bold(text.Cyan("::")),
len(allUp.PulledDeps), text.Bold(gotext.Get("%s will also be installed for this operation.",
gotext.GetN("dependency", "dependencies", len(allUp.PulledDeps)))))
allUp.PrintDeps(u.log)
}
u.log.Printf("%s"+text.Bold(" %d ")+"%s\n", text.Bold(text.Cyan("::")),
len(allUp.Up), text.Bold(gotext.Get("%s to upgrade/install.", gotext.GetN("package", "packages", len(allUp.Up)))))
allUp.Print(u.log)
u.log.Infoln(gotext.Get("Packages to exclude: (eg: \"1 2 3\", \"1-3\", \"^4\" or repo name)"))
u.log.Warnln(gotext.Get("Excluding packages may cause partial upgrades and break systems"))
numbers, err := u.log.GetInput(u.cfg.AnswerUpgrade, settings.NoConfirm)
if err != nil {
return nil, err
}
// upgrade menu asks you which packages to NOT upgrade so in this case
// exclude and include are kind of swapped
exclude, include, otherExclude, otherInclude := intrange.ParseNumberMenu(numbers)
// true if user doesn't want to include specific repositories/packages
noIncludes := len(include) == 0 && otherInclude.Cardinality() == 0
// No exclusions or inclusions specified, return early
if noIncludes && len(exclude) == 0 && otherExclude.Cardinality() == 0 {
return []string{}, nil
}
excluded := make([]string, 0)
for i := range allUp.Up {
up := &allUp.Up[i]
upgradeID := len(allUp.Up) - i
// check if user wants to exclude specific things (true) or include specific things
if noIncludes {
// exclude repositories mentioned by the user
if otherExclude.Contains(up.Repository) {
u.log.Debugln("pruning", up.Name)
excluded = append(excluded, graph.Prune(up.Name)...)
}
// exclude packages mentioned by the user
if exclude.Get(upgradeID) {
u.log.Debugln("pruning", up.Name)
excluded = append(excluded, graph.Prune(up.Name)...)
}
// If the user explicitly wants to include a package/repository, exclude everything else
} else if !include.Get(upgradeID) && !otherInclude.Contains(up.Repository) {
u.log.Debugln("pruning", up.Name)
excluded = append(excluded, graph.Prune(up.Name)...)
}
}
return excluded, nil
}
================================================
FILE: pkg/upgrade/service_test.go
================================================
//go:build !integration
// +build !integration
package upgrade
import (
"context"
"io"
"os"
"strings"
"testing"
"github.com/Jguer/aur"
alpm "github.com/Jguer/dyalpm"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/Jguer/yay/v12/pkg/db"
"github.com/Jguer/yay/v12/pkg/db/mock"
"github.com/Jguer/yay/v12/pkg/dep"
"github.com/Jguer/yay/v12/pkg/dep/topo"
"github.com/Jguer/yay/v12/pkg/query"
"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/yay/v12/pkg/vcs"
mockaur "github.com/Jguer/yay/v12/pkg/dep/mock"
)
func ptrString(s string) *string {
return &s
}
func TestUpgradeService_GraphUpgrades(t *testing.T) {
t.Parallel()
linuxDepInfo := &dep.InstallInfo{
Reason: dep.Explicit,
Source: dep.Sync,
AURBase: nil,
LocalVersion: "4.5.0-1",
Version: "5.0.0-1",
SyncDBName: ptrString("core"),
Upgrade: true,
Devel: false,
}
exampleDepInfoDevel := &dep.InstallInfo{
Source: dep.AUR,
Reason: dep.Dep,
AURBase: ptrString("example"),
LocalVersion: "2.2.1.r32.41baa362-1",
Version: "latest-commit",
Upgrade: true,
Devel: true,
}
newDepInfo := &dep.InstallInfo{
Source: dep.Sync,
Reason: dep.Dep,
SyncDBName: ptrString("core"),
Version: "3.0.1-2",
LocalVersion: "",
Upgrade: true,
Devel: false,
}
exampleDepInfoAUR := &dep.InstallInfo{
Source: dep.AUR,
Reason: dep.Dep,
AURBase: ptrString("example"),
LocalVersion: "2.2.1.r32.41baa362-1",
Version: "2.2.1.r69.g8a10460-1",
Upgrade: true,
Devel: false,
}
yayDepInfo := &dep.InstallInfo{
Reason: dep.Explicit,
Source: dep.AUR,
AURBase: ptrString("yay"),
LocalVersion: "10.2.3",
Version: "10.2.4",
Upgrade: true,
Devel: false,
}
coreDB := mock.NewDB("core")
dbExe := &mock.DBExecutor{
InstalledRemotePackageNamesFn: func() []string {
return []string{"yay", "example-git"}
},
InstalledRemotePackagesFn: func() map[string]mock.IPackage {
mapRemote := make(map[string]mock.IPackage)
mapRemote["yay"] = &mock.Package{
PName: "yay",
PBase: "yay",
PVersion: "10.2.3",
PReason: alpm.PkgReasonExplicit,
}
mapRemote["example-git"] = &mock.Package{
PName: "example-git",
PBase: "example",
PVersion: "2.2.1.r32.41baa362-1",
PReason: alpm.PkgReasonDepend,
}
return mapRemote
},
LocalSatisfierExistsFn: func(string) bool { return false },
SyncSatisfierFn: func(s string) mock.IPackage {
return &mock.Package{
PName: "new-dep",
PVersion: "3.0.1-2",
PDB: coreDB,
}
},
SyncUpgradesFn: func(bool) (map[string]db.SyncUpgrade, error) {
mapUpgrades := make(map[string]db.SyncUpgrade)
mapUpgrades["linux"] = db.SyncUpgrade{
Package: &mock.Package{
PName: "linux",
PVersion: "5.0.0-1",
PReason: alpm.PkgReasonDepend,
PDB: coreDB,
PDepends: mock.DependList{Depends: []alpm.Depend{
{Name: "new-dep", Version: "3.0.1"},
}},
},
LocalVersion: "4.5.0-1",
Reason: alpm.PkgReasonExplicit,
}
mapUpgrades["new-dep"] = db.SyncUpgrade{
Package: &mock.Package{
PName: "new-dep",
PVersion: "3.0.1-2",
PReason: alpm.PkgReasonDepend,
PDB: coreDB,
},
LocalVersion: "",
Reason: alpm.PkgReasonDepend,
}
return mapUpgrades, nil
},
ReposFn: func() []string { return []string{"core"} },
}
vcsStore := &vcs.Mock{
ToUpgradeReturn: []string{"example-git"},
}
mockAUR := &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return []aur.Pkg{
{Name: "yay", Version: "10.2.4", PackageBase: "yay"},
{
Name: "example-git", Version: "2.2.1.r69.g8a10460-1",
PackageBase: "example", Depends: []string{"new-dep"},
},
}, nil
},
}
type fields struct {
input io.Reader
output io.Writer
noConfirm bool
devel bool
}
type args struct {
graph *topo.Graph[string, *dep.InstallInfo]
enableDowngrade bool
}
tests := []struct {
name string
fields fields
args args
mustExist map[string]*dep.InstallInfo
mustNotExist map[string]bool
wantExclude []string
wantErr bool
}{
{
name: "no input",
fields: fields{
input: strings.NewReader("\n"),
output: io.Discard,
noConfirm: false,
},
args: args{
graph: nil,
enableDowngrade: false,
},
mustExist: map[string]*dep.InstallInfo{
"yay": yayDepInfo,
"linux": linuxDepInfo,
"example-git": exampleDepInfoAUR,
"new-dep": newDepInfo,
},
mustNotExist: map[string]bool{},
wantErr: false,
wantExclude: []string{},
},
{
name: "no input devel",
fields: fields{
input: strings.NewReader("\n"),
output: io.Discard,
noConfirm: false,
devel: true,
},
args: args{
graph: nil,
enableDowngrade: false,
},
mustExist: map[string]*dep.InstallInfo{
"yay": yayDepInfo,
"linux": linuxDepInfo,
"example-git": exampleDepInfoDevel,
},
mustNotExist: map[string]bool{},
wantErr: false,
wantExclude: []string{},
},
{
name: "exclude example-git",
fields: fields{
input: strings.NewReader("2\n"),
output: io.Discard,
noConfirm: false,
},
args: args{
graph: nil,
enableDowngrade: false,
},
mustExist: map[string]*dep.InstallInfo{
"yay": yayDepInfo,
"linux": linuxDepInfo,
},
mustNotExist: map[string]bool{"example-git": true, "new-dep": true},
wantErr: false,
wantExclude: []string{"example-git", "new-dep"},
},
{
name: "exclude new-dep should have no effect",
fields: fields{
input: strings.NewReader("1 3 4\n"),
output: io.Discard,
noConfirm: false,
},
args: args{
graph: nil,
enableDowngrade: false,
},
mustExist: map[string]*dep.InstallInfo{
"example-git": exampleDepInfoAUR,
"new-dep": newDepInfo,
},
mustNotExist: map[string]bool{"linux": true, "yay": true},
wantErr: false,
wantExclude: []string{"linux", "yay"},
},
{
name: "exclude yay",
fields: fields{
input: strings.NewReader("1\n"),
output: io.Discard,
noConfirm: false,
},
args: args{
graph: nil,
enableDowngrade: false,
},
mustExist: map[string]*dep.InstallInfo{
"linux": linuxDepInfo,
"example-git": exampleDepInfoAUR,
},
mustNotExist: map[string]bool{"yay": true},
wantErr: false,
wantExclude: []string{"yay"},
},
{
name: "exclude linux",
fields: fields{
input: strings.NewReader("3\n"),
output: io.Discard,
noConfirm: false,
},
args: args{
graph: nil,
enableDowngrade: false,
},
mustExist: map[string]*dep.InstallInfo{
"yay": yayDepInfo,
"example-git": exampleDepInfoAUR,
"new-dep": newDepInfo,
},
mustNotExist: map[string]bool{"linux": true},
wantErr: false,
wantExclude: []string{"linux"},
},
{
name: "only linux",
fields: fields{
input: strings.NewReader("^3\n"),
output: io.Discard,
noConfirm: false,
},
args: args{
graph: nil,
enableDowngrade: false,
},
mustExist: map[string]*dep.InstallInfo{
"linux": linuxDepInfo,
},
mustNotExist: map[string]bool{"yay": true, "example-git": true},
wantErr: false,
wantExclude: []string{"yay", "example-git", "new-dep"},
},
{
name: "exclude all",
fields: fields{
input: strings.NewReader("1-4\n"),
output: io.Discard,
noConfirm: false,
},
args: args{
graph: nil,
enableDowngrade: false,
},
mustExist: map[string]*dep.InstallInfo{},
mustNotExist: map[string]bool{"yay": true, "example-git": true, "linux": true},
wantErr: false,
wantExclude: []string{"yay", "example-git", "linux", "new-dep"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
grapher := dep.NewGrapher(dbExe, mockAUR,
false, true, false, false, false, text.NewLogger(tt.fields.output, os.Stderr,
tt.fields.input, true, "test"))
cfg := &settings.Configuration{
Devel: tt.fields.devel, Mode: parser.ModeAny,
}
logger := text.NewLogger(tt.fields.output, os.Stderr,
tt.fields.input, true, "test")
u := &UpgradeService{
log: logger,
grapher: grapher,
aurCache: mockAUR,
dbExecutor: dbExe,
vcsStore: vcsStore,
cfg: cfg,
noConfirm: tt.fields.noConfirm,
AURWarnings: query.NewWarnings(logger),
}
got, err := u.GraphUpgrades(context.Background(), tt.args.graph, tt.args.enableDowngrade, func(*Upgrade) bool { return true })
if (err != nil) != tt.wantErr {
t.Errorf("UpgradeService.GraphUpgrades() error = %v, wantErr %v", err, tt.wantErr)
return
}
excluded, err := u.UserExcludeUpgrades(got)
require.NoError(t, err)
for node, info := range tt.mustExist {
assert.True(t, got.Exists(node), node)
assert.Equal(t, info, got.GetNodeInfo(node).Value)
}
for node := range tt.mustNotExist {
assert.False(t, got.Exists(node), node)
}
assert.ElementsMatch(t, tt.wantExclude, excluded)
})
}
}
func TestUpgradeService_GraphUpgradesMissingDep(t *testing.T) {
t.Parallel()
newDepMissingInfo := &dep.InstallInfo{
Source: dep.Missing,
Reason: dep.Dep,
Version: "",
}
exampleDepInfoAUR := &dep.InstallInfo{
Source: dep.AUR,
Reason: dep.Dep,
AURBase: ptrString("example"),
LocalVersion: "2.2.1.r32.41baa362-1",
Version: "2.2.1.r69.g8a10460-1",
Upgrade: true,
Devel: false,
}
yayDepInfo := &dep.InstallInfo{
Reason: dep.Explicit,
Source: dep.AUR,
AURBase: ptrString("yay"),
LocalVersion: "10.2.3",
Version: "10.2.4",
Upgrade: true,
Devel: false,
}
dbExe := &mock.DBExecutor{
InstalledRemotePackageNamesFn: func() []string {
return []string{"yay", "example-git"}
},
InstalledRemotePackagesFn: func() map[string]mock.IPackage {
mapRemote := make(map[string]mock.IPackage)
mapRemote["yay"] = &mock.Package{
PName: "yay",
PBase: "yay",
PVersion: "10.2.3",
PReason: alpm.PkgReasonExplicit,
}
mapRemote["example-git"] = &mock.Package{
PName: "example-git",
PBase: "example",
PVersion: "2.2.1.r32.41baa362-1",
PReason: alpm.PkgReasonDepend,
}
return mapRemote
},
LocalSatisfierExistsFn: func(string) bool { return false },
SyncSatisfierFn: func(s string) mock.IPackage { return nil },
SyncUpgradesFn: func(bool) (map[string]db.SyncUpgrade, error) {
return map[string]db.SyncUpgrade{}, nil
},
ReposFn: func() []string { return nil },
}
vcsStore := &vcs.Mock{
ToUpgradeReturn: []string{"example-git"},
}
mockAUR := &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return []aur.Pkg{
{
Name: "yay",
Version: "10.2.4",
PackageBase: "yay",
},
{
Name: "example-git",
Version: "2.2.1.r69.g8a10460-1",
PackageBase: "example",
Depends: []string{"new-dep-missing"},
},
}, nil
},
}
type fields struct {
input io.Reader
output io.Writer
noConfirm bool
devel bool
}
type args struct {
graph *topo.Graph[string, *dep.InstallInfo]
enableDowngrade bool
}
tests := []struct {
name string
fields fields
args args
mustExist map[string]*dep.InstallInfo
mustNotExist map[string]bool
wantExclude []string
wantErr bool
}{
{
name: "no input",
fields: fields{
input: strings.NewReader("\n"),
output: io.Discard,
noConfirm: false,
},
args: args{
graph: nil,
enableDowngrade: false,
},
mustExist: map[string]*dep.InstallInfo{
"yay": yayDepInfo,
"example-git": exampleDepInfoAUR,
"new-dep-missing": newDepMissingInfo,
},
mustNotExist: map[string]bool{},
wantErr: false,
wantExclude: []string{},
},
{
name: "exclude example-git(with missing dep)",
fields: fields{
input: strings.NewReader("2\n"),
output: io.Discard,
noConfirm: false,
},
args: args{
graph: nil,
enableDowngrade: false,
},
mustExist: map[string]*dep.InstallInfo{
"yay": yayDepInfo,
},
mustNotExist: map[string]bool{"example-git": true, "new-dep-missing": true},
wantErr: false,
wantExclude: []string{"example-git", "new-dep-missing"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
grapher := dep.NewGrapher(dbExe, mockAUR,
false, true, false, false, false, text.NewLogger(tt.fields.output, os.Stderr,
tt.fields.input, true, "test"))
cfg := &settings.Configuration{
Devel: tt.fields.devel, Mode: parser.ModeAny,
}
logger := text.NewLogger(tt.fields.output, os.Stderr,
tt.fields.input, true, "test")
u := &UpgradeService{
log: logger,
grapher: grapher,
aurCache: mockAUR,
dbExecutor: dbExe,
vcsStore: vcsStore,
cfg: cfg,
noConfirm: tt.fields.noConfirm,
AURWarnings: query.NewWarnings(logger),
}
got, err := u.GraphUpgrades(context.Background(), tt.args.graph, tt.args.enableDowngrade, func(*Upgrade) bool { return true })
if (err != nil) != tt.wantErr {
t.Errorf("UpgradeService.GraphUpgrades() error = %v, wantErr %v", err, tt.wantErr)
return
}
excluded, err := u.UserExcludeUpgrades(got)
require.NoError(t, err)
for node, info := range tt.mustExist {
assert.True(t, got.Exists(node), node)
assert.Equal(t, info, got.GetNodeInfo(node).Value)
}
for node := range tt.mustNotExist {
assert.False(t, got.Exists(node), node)
}
assert.ElementsMatch(t, tt.wantExclude, excluded)
})
}
}
func TestUpgradeService_GraphUpgradesNoUpdates(t *testing.T) {
t.Parallel()
dbExe := &mock.DBExecutor{
InstalledRemotePackageNamesFn: func() []string {
return []string{"yay", "example-git"}
},
InstalledRemotePackagesFn: func() map[string]mock.IPackage {
mapRemote := make(map[string]mock.IPackage)
mapRemote["yay"] = &mock.Package{
PName: "yay",
PBase: "yay",
PVersion: "10.2.3",
PReason: alpm.PkgReasonExplicit,
}
mapRemote["example-git"] = &mock.Package{
PName: "example-git",
PBase: "example",
PVersion: "2.2.1.r32.41baa362-1",
PReason: alpm.PkgReasonDepend,
}
return mapRemote
},
SyncUpgradesFn: func(bool) (map[string]db.SyncUpgrade, error) {
mapUpgrades := make(map[string]db.SyncUpgrade)
return mapUpgrades, nil
},
ReposFn: func() []string { return []string{"core"} },
}
vcsStore := &vcs.Mock{
ToUpgradeReturn: []string{},
}
mockAUR := &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return []aur.Pkg{}, nil
},
}
type fields struct {
input io.Reader
output io.Writer
noConfirm bool
devel bool
}
type args struct {
graph *topo.Graph[string, *dep.InstallInfo]
enableDowngrade bool
}
tests := []struct {
name string
fields fields
args args
mustExist map[string]*dep.InstallInfo
mustNotExist map[string]bool
wantExclude []string
wantErr bool
}{
{
name: "no input",
fields: fields{
input: strings.NewReader(""),
output: io.Discard,
noConfirm: false,
},
args: args{
graph: nil,
enableDowngrade: false,
},
mustExist: map[string]*dep.InstallInfo{},
mustNotExist: map[string]bool{},
wantErr: false,
wantExclude: []string{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
grapher := dep.NewGrapher(dbExe, mockAUR,
false, true, false, false, false, text.NewLogger(tt.fields.output, os.Stderr,
tt.fields.input, true, "test"))
cfg := &settings.Configuration{
Devel: tt.fields.devel,
Mode: parser.ModeAny,
}
logger := text.NewLogger(tt.fields.output, os.Stderr,
tt.fields.input, true, "test")
u := &UpgradeService{
log: logger,
grapher: grapher,
aurCache: mockAUR,
dbExecutor: dbExe,
vcsStore: vcsStore,
cfg: cfg,
noConfirm: tt.fields.noConfirm,
AURWarnings: query.NewWarnings(logger),
}
got, err := u.GraphUpgrades(context.Background(), tt.args.graph, tt.args.enableDowngrade, func(*Upgrade) bool { return true })
if (err != nil) != tt.wantErr {
t.Errorf("UpgradeService.GraphUpgrades() error = %v, wantErr %v", err, tt.wantErr)
return
}
excluded, err := u.UserExcludeUpgrades(got)
require.NoError(t, err)
for node, info := range tt.mustExist {
assert.True(t, got.Exists(node), node)
assert.Equal(t, info, got.GetNodeInfo(node).Value)
}
for node := range tt.mustNotExist {
assert.False(t, got.Exists(node), node)
}
assert.ElementsMatch(t, tt.wantExclude, excluded)
})
}
}
func TestUpgradeService_Warnings(t *testing.T) {
t.Parallel()
dbExe := &mock.DBExecutor{
InstalledRemotePackageNamesFn: func() []string {
return []string{"orphan", "outdated", "missing", "orphan-ignored"}
},
InstalledRemotePackagesFn: func() map[string]mock.IPackage {
mapRemote := make(map[string]mock.IPackage)
mapRemote["orphan"] = &mock.Package{
PName: "orphan",
PBase: "orphan",
PVersion: "10.2.3",
PReason: alpm.PkgReasonExplicit,
}
mapRemote["outdated"] = &mock.Package{
PName: "outdated",
PBase: "outdated",
PVersion: "10.2.3",
PReason: alpm.PkgReasonExplicit,
}
mapRemote["missing"] = &mock.Package{
PName: "missing",
PBase: "missing",
PVersion: "10.2.3",
PReason: alpm.PkgReasonExplicit,
}
mapRemote["orphan-ignored"] = &mock.Package{
PName: "orphan-ignored",
PBase: "orphan-ignored",
PVersion: "10.2.3",
PReason: alpm.PkgReasonExplicit,
PShouldIgnore: true,
}
return mapRemote
},
LocalSatisfierExistsFn: func(string) bool { return false },
SyncSatisfierFn: func(s string) mock.IPackage {
return nil
},
SyncUpgradesFn: func(bool) (map[string]db.SyncUpgrade, error) {
mapUpgrades := make(map[string]db.SyncUpgrade)
return mapUpgrades, nil
},
ReposFn: func() []string { return []string{"core"} },
}
vcsStore := &vcs.Mock{
ToUpgradeReturn: []string{},
}
mockAUR := &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return []aur.Pkg{
{
Name: "outdated", Version: "10.2.4", PackageBase: "orphan",
OutOfDate: 100, Maintainer: "bob",
},
{
Name: "orphan", Version: "10.2.4", PackageBase: "orphan",
Maintainer: "",
},
}, nil
},
}
logger := text.NewLogger(io.Discard, os.Stderr,
strings.NewReader("\n"), true, "test")
grapher := dep.NewGrapher(dbExe, mockAUR,
false, true, false, false, false, logger)
cfg := &settings.Configuration{
Devel: false, Mode: parser.ModeAUR,
}
u := &UpgradeService{
log: logger,
grapher: grapher,
aurCache: mockAUR,
dbExecutor: dbExe,
vcsStore: vcsStore,
cfg: cfg,
noConfirm: true,
AURWarnings: query.NewWarnings(logger),
}
_, err := u.GraphUpgrades(context.Background(), nil, false, func(*Upgrade) bool { return true })
require.NoError(t, err)
assert.Equal(t, []string{"missing"}, u.AURWarnings.Missing)
assert.Equal(t, []string{"outdated"}, u.AURWarnings.OutOfDate)
assert.Equal(t, []string{"orphan"}, u.AURWarnings.Orphans)
}
func TestUpgradeService_GraphUpgrades_zfs_dkms(t *testing.T) {
t.Parallel()
zfsDKMSInfo := &dep.InstallInfo{
Reason: dep.Explicit,
Source: dep.AUR,
AURBase: ptrString("zfs-dkms"),
LocalVersion: "2.1.10-1",
Version: "2.1.11-1",
Upgrade: true,
Devel: false,
}
zfsUtilsInfo := &dep.InstallInfo{
Reason: dep.Dep,
Source: dep.AUR,
AURBase: ptrString("zfs-utils"),
LocalVersion: "2.1.10-1",
Version: "2.1.11-1",
Upgrade: true,
Devel: false,
}
vcsStore := &vcs.Mock{ToUpgradeReturn: []string{}}
mockAUR := &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
if len(query.Needles) == 2 {
return []aur.Pkg{
{
Name: "zfs-dkms", Version: "2.1.11-1",
PackageBase: "zfs-dkms", Depends: []string{"zfs-utils=2.1.11"},
},
{Name: "zfs-utils", Version: "2.1.11-1", PackageBase: "zfs-utils"},
}, nil
}
if len(query.Needles) == 1 {
return []aur.Pkg{
{Name: "zfs-utils", Version: "2.1.11-1", PackageBase: "zfs-utils"},
}, nil
}
panic("not implemented")
},
}
type fields struct {
input io.Reader
noConfirm bool
devel bool
}
type args struct {
graph *topo.Graph[string, *dep.InstallInfo]
enableDowngrade bool
}
tests := []struct {
name string
fields fields
args args
mustExist map[string]*dep.InstallInfo
mustNotExist map[string]bool
wantExclude []string
wantErr bool
remotePackages []string
}{
{
name: "no input",
fields: fields{
input: strings.NewReader("\n"),
noConfirm: false,
},
args: args{
graph: nil,
enableDowngrade: false,
},
mustExist: map[string]*dep.InstallInfo{
"zfs-dkms": zfsDKMSInfo,
"zfs-utils": zfsUtilsInfo,
},
remotePackages: []string{"zfs-utils", "zfs-dkms"},
mustNotExist: map[string]bool{},
wantErr: false,
wantExclude: []string{},
},
{
name: "no input - inverted order",
fields: fields{
input: strings.NewReader("\n"),
noConfirm: false,
},
args: args{
graph: nil,
enableDowngrade: false,
},
mustExist: map[string]*dep.InstallInfo{
"zfs-dkms": zfsDKMSInfo,
"zfs-utils": zfsUtilsInfo,
},
remotePackages: []string{"zfs-dkms", "zfs-utils"},
mustNotExist: map[string]bool{},
wantErr: false,
wantExclude: []string{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
dbExe := &mock.DBExecutor{
InstalledRemotePackageNamesFn: func() []string {
return tt.remotePackages
},
InstalledRemotePackagesFn: func() map[string]mock.IPackage {
mapRemote := make(map[string]mock.IPackage)
mapRemote["zfs-dkms"] = &mock.Package{
PName: "zfs-dkms",
PBase: "zfs-dkms",
PVersion: "2.1.10-1",
PReason: alpm.PkgReasonExplicit,
PDepends: mock.DependList{Depends: []alpm.Depend{
{Name: "zfs-utils", Version: "2.1.10-1"},
}},
}
mapRemote["zfs-utils"] = &mock.Package{
PName: "zfs-utils",
PBase: "zfs-utils",
PVersion: "2.1.10-1",
PReason: alpm.PkgReasonDepend,
}
return mapRemote
},
LocalSatisfierExistsFn: func(string) bool { return false },
SyncSatisfierFn: func(s string) mock.IPackage {
return nil
},
SyncUpgradesFn: func(bool) (map[string]db.SyncUpgrade, error) {
mapUpgrades := make(map[string]db.SyncUpgrade)
return mapUpgrades, nil
},
ReposFn: func() []string { return []string{"core"} },
}
logger := text.NewLogger(io.Discard, os.Stderr,
tt.fields.input, true, "test")
grapher := dep.NewGrapher(dbExe, mockAUR,
false, true, false, false, false, logger)
cfg := &settings.Configuration{
Devel: tt.fields.devel, Mode: parser.ModeAny,
}
u := &UpgradeService{
log: logger,
grapher: grapher,
aurCache: mockAUR,
dbExecutor: dbExe,
vcsStore: vcsStore,
cfg: cfg,
noConfirm: tt.fields.noConfirm,
AURWarnings: query.NewWarnings(logger),
}
got, err := u.GraphUpgrades(context.Background(), tt.args.graph, tt.args.enableDowngrade, func(*Upgrade) bool { return true })
if (err != nil) != tt.wantErr {
t.Errorf("UpgradeService.GraphUpgrades() error = %v, wantErr %v", err, tt.wantErr)
return
}
excluded, err := u.UserExcludeUpgrades(got)
require.NoError(t, err)
for node, info := range tt.mustExist {
assert.True(t, got.Exists(node), node)
assert.Equal(t, info, got.GetNodeInfo(node).Value)
}
for node := range tt.mustNotExist {
assert.False(t, got.Exists(node), node)
}
assert.ElementsMatch(t, tt.wantExclude, excluded)
})
}
}
================================================
FILE: pkg/upgrade/sources.go
================================================
package upgrade
import (
"context"
"github.com/leonelquinteros/gotext"
"github.com/Jguer/yay/v12/pkg/db"
"github.com/Jguer/yay/v12/pkg/query"
"github.com/Jguer/yay/v12/pkg/text"
"github.com/Jguer/yay/v12/pkg/vcs"
)
func UpDevel(
ctx context.Context,
log *text.Logger,
remote map[string]db.IPackage,
aurdata map[string]*query.Pkg,
localCache vcs.Store,
) UpSlice {
toRemove := make([]string, 0)
toUpgrade := UpSlice{Up: make([]Upgrade, 0), Repos: []string{"devel"}}
for pkgName, pkg := range remote {
if localCache.ToUpgrade(ctx, pkgName) {
if _, ok := aurdata[pkgName]; !ok {
log.Warnln(gotext.Get("ignoring package devel upgrade (no AUR info found):"), pkgName)
continue
}
if pkg.ShouldIgnore() {
printIgnoringPackage(log, pkg, "latest-commit")
continue
}
toUpgrade.Up = append(toUpgrade.Up,
Upgrade{
Name: pkg.Name(),
Base: pkg.Base(),
Repository: "devel",
LocalVersion: pkg.Version(),
RemoteVersion: "latest-commit",
Reason: pkg.Reason(),
})
}
}
localCache.RemovePackages(toRemove)
return toUpgrade
}
func printIgnoringPackage(log *text.Logger, pkg db.IPackage, newPkgVersion string) {
left, right := query.GetVersionDiff(pkg.Version(), newPkgVersion)
pkgName := pkg.Name()
log.Warnln(gotext.Get("%s: ignoring package upgrade (%s => %s)",
text.Cyan(pkgName),
left, right,
))
}
// UpAUR gathers foreign packages and checks if they have new versions.
// Output: Upgrade type package list.
func UpAUR(log *text.Logger, remote map[string]db.IPackage, aurdata map[string]*query.Pkg,
enableDowngrade bool,
) UpSlice {
toUpgrade := UpSlice{Up: make([]Upgrade, 0), Repos: []string{"aur"}}
for name, pkg := range remote {
aurPkg, ok := aurdata[name]
if !ok {
continue
}
if (db.VerCmp(pkg.Version(), aurPkg.Version) < 0) ||
(enableDowngrade && (db.VerCmp(pkg.Version(), aurPkg.Version) > 0)) {
if pkg.ShouldIgnore() {
printIgnoringPackage(log, pkg, aurPkg.Version)
} else {
toUpgrade.Up = append(toUpgrade.Up,
Upgrade{
Name: aurPkg.Name,
Base: aurPkg.PackageBase,
Repository: "aur",
LocalVersion: pkg.Version(),
RemoteVersion: aurPkg.Version,
Reason: pkg.Reason(),
})
}
}
}
return toUpgrade
}
================================================
FILE: pkg/upgrade/sources_test.go
================================================
//go:build !integration
// +build !integration
package upgrade
import (
"context"
"io"
"os"
"strings"
"testing"
aur "github.com/Jguer/aur"
"github.com/stretchr/testify/assert"
alpm "github.com/Jguer/dyalpm"
"github.com/Jguer/yay/v12/pkg/db/mock"
"github.com/Jguer/yay/v12/pkg/text"
"github.com/Jguer/yay/v12/pkg/vcs"
)
func Test_upAUR(t *testing.T) {
t.Parallel()
type args struct {
remote map[string]alpm.Package
aurdata map[string]*aur.Pkg
enableDowngrade bool
}
tests := []struct {
name string
args args
want UpSlice
}{
{
name: "No Updates",
args: args{
remote: map[string]alpm.Package{
"hello": &mock.Package{PName: "hello", PVersion: "2.0.0"},
"local_pkg": &mock.Package{PName: "local_pkg", PVersion: "1.1.0"},
"ignored": &mock.Package{PName: "ignored", PVersion: "1.0.0", PShouldIgnore: true},
},
aurdata: map[string]*aur.Pkg{
"hello": {Version: "2.0.0", Name: "hello"},
"ignored": {Version: "2.0.0", Name: "ignored"},
},
},
want: UpSlice{Repos: []string{"aur"}, Up: []Upgrade{}},
},
{
name: "Simple Update",
args: args{
remote: map[string]alpm.Package{
"hello": &mock.Package{PName: "hello", PVersion: "2.0.0"},
},
aurdata: map[string]*aur.Pkg{"hello": {Version: "2.1.0", Name: "hello"}},
},
want: UpSlice{Repos: []string{"aur"}, Up: []Upgrade{{Name: "hello", Repository: "aur", LocalVersion: "2.0.0", RemoteVersion: "2.1.0"}}},
},
{
name: "Downgrade",
args: args{
remote: map[string]alpm.Package{
"hello": &mock.Package{PName: "hello", PVersion: "2.0.0"},
},
aurdata: map[string]*aur.Pkg{"hello": {Version: "1.0.0", Name: "hello"}},
enableDowngrade: true,
},
want: UpSlice{Repos: []string{"aur"}, Up: []Upgrade{{Name: "hello", Repository: "aur", LocalVersion: "2.0.0", RemoteVersion: "1.0.0"}}},
},
{
name: "Downgrade Disabled",
args: args{
remote: map[string]alpm.Package{
"hello": &mock.Package{PName: "hello", PVersion: "2.0.0"},
},
aurdata: map[string]*aur.Pkg{"hello": {Version: "1.0.0", Name: "hello"}},
enableDowngrade: false,
},
want: UpSlice{Repos: []string{"aur"}, Up: []Upgrade{}},
},
{
name: "Mixed Updates Downgrades",
args: args{
enableDowngrade: true,
remote: map[string]alpm.Package{
"up": &mock.Package{PName: "up", PVersion: "2.0.0"},
"same": &mock.Package{PName: "same", PVersion: "3.0.0"},
"down": &mock.Package{PName: "down", PVersion: "1.1.0"},
"ignored": &mock.Package{PName: "ignored", PVersion: "1.0.0", PShouldIgnore: true},
},
aurdata: map[string]*aur.Pkg{
"up": {Version: "2.1.0", Name: "up"},
"same": {Version: "3.0.0", Name: "same"},
"down": {Version: "1.0.0", Name: "down"},
"ignored": {Version: "2.0.0", Name: "ignored"},
},
},
want: UpSlice{Repos: []string{"aur"}, Up: []Upgrade{
{Name: "up", Repository: "aur", LocalVersion: "2.0.0", RemoteVersion: "2.1.0"},
{Name: "down", Repository: "aur", LocalVersion: "1.1.0", RemoteVersion: "1.0.0"},
}},
},
{
name: "Ignore LastModified When Version Is Unchanged",
args: args{
remote: map[string]alpm.Package{
"hello": &mock.Package{PName: "hello", PVersion: "2.0.0"},
},
aurdata: map[string]*aur.Pkg{"hello": {Version: "2.0.0", Name: "hello", LastModified: 9999999999}},
},
want: UpSlice{Repos: []string{"aur"}, Up: []Upgrade{}},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
got := UpAUR(text.NewLogger(io.Discard, os.Stderr, strings.NewReader(""), false, "test"),
tt.args.remote, tt.args.aurdata, tt.args.enableDowngrade)
assert.ElementsMatch(t, tt.want.Repos, got.Repos)
assert.ElementsMatch(t, tt.want.Up, got.Up)
assert.Equal(t, tt.want.Len(), got.Len())
})
}
}
func Test_upDevel(t *testing.T) {
t.Parallel()
type args struct {
remote map[string]alpm.Package
aurdata map[string]*aur.Pkg
cached vcs.Store
}
tests := []struct {
name string
args args
want UpSlice
finalLen int
}{
{
name: "No Updates",
args: args{
cached: &vcs.Mock{},
remote: map[string]alpm.Package{
"hello": &mock.Package{PName: "hello", PVersion: "2.0.0"},
"local_pkg": &mock.Package{PName: "local_pkg", PVersion: "1.1.0"},
"ignored": &mock.Package{PName: "ignored", PVersion: "1.0.0", PShouldIgnore: true},
},
aurdata: map[string]*aur.Pkg{
"hello": {Version: "2.0.0", Name: "hello"},
"ignored": {Version: "2.0.0", Name: "ignored"},
},
},
want: UpSlice{Repos: []string{"devel"}},
},
{
name: "Simple Update",
finalLen: 3,
args: args{
cached: &vcs.Mock{
ToUpgradeReturn: []string{"hello", "hello4"},
},
remote: map[string]alpm.Package{
"hello": &mock.Package{PName: "hello", PVersion: "2.0.0"},
"hello2": &mock.Package{PName: "hello2", PVersion: "3.0.0"},
"hello4": &mock.Package{PName: "hello4", PVersion: "4.0.0"},
},
aurdata: map[string]*aur.Pkg{
"hello": {Version: "2.0.0", Name: "hello"},
"hello2": {Version: "2.0.0", Name: "hello2"},
"hello4": {Version: "2.0.0", Name: "hello4"},
},
},
want: UpSlice{
Repos: []string{"devel"}, Up: []Upgrade{
{
Name: "hello",
Repository: "devel",
LocalVersion: "2.0.0",
RemoteVersion: "latest-commit",
},
{
Name: "hello4",
Repository: "devel",
LocalVersion: "4.0.0",
RemoteVersion: "latest-commit",
},
},
},
},
{
name: "No update returned",
finalLen: 1,
args: args{
cached: &vcs.Mock{ToUpgradeReturn: []string{}},
remote: map[string]alpm.Package{
"hello": &mock.Package{PName: "hello", PVersion: "2.0.0"},
},
aurdata: map[string]*aur.Pkg{"hello": {Version: "2.0.0", Name: "hello"}},
},
want: UpSlice{Repos: []string{"devel"}},
},
{
name: "No update returned - ignored",
finalLen: 1,
args: args{
cached: &vcs.Mock{
ToUpgradeReturn: []string{"hello"},
},
remote: map[string]alpm.Package{
"hello": &mock.Package{PName: "hello", PVersion: "2.0.0", PShouldIgnore: true},
},
aurdata: map[string]*aur.Pkg{"hello": {Version: "2.0.0", Name: "hello"}},
},
want: UpSlice{Repos: []string{"devel"}},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
got := UpDevel(context.Background(),
text.NewLogger(io.Discard, os.Stderr, strings.NewReader(""), false, "test"),
tt.args.remote, tt.args.aurdata, tt.args.cached)
assert.ElementsMatch(t, tt.want.Up, got.Up)
})
}
}
================================================
FILE: pkg/upgrade/upgrade.go
================================================
package upgrade
import (
"fmt"
"strings"
"github.com/Jguer/yay/v12/pkg/db"
"github.com/Jguer/yay/v12/pkg/query"
"github.com/Jguer/yay/v12/pkg/text"
)
// Filter decides if specific package should be included in the results.
type Filter func(*Upgrade) bool
// Upgrade type describes a system upgrade.
type Upgrade = db.Upgrade
func StylizedNameWithRepository(u *Upgrade) string {
return text.Bold(text.ColorHash(u.Repository)) + "/" + text.Bold(u.Name)
}
// upSlice is a slice of Upgrades.
type UpSlice struct {
Up []Upgrade
Repos []string
PulledDeps []Upgrade
}
func (u UpSlice) Len() int { return len(u.Up) }
func (u UpSlice) Swap(i, j int) { u.Up[i], u.Up[j] = u.Up[j], u.Up[i] }
func (u UpSlice) Less(i, j int) bool {
if u.Up[i].Repository == u.Up[j].Repository {
iRunes := []rune(u.Up[i].Name)
jRunes := []rune(u.Up[j].Name)
return text.LessRunes(iRunes, jRunes)
}
for _, db := range u.Repos {
switch db {
case u.Up[i].Repository:
return true
case u.Up[j].Repository:
return false
}
}
iRunes := []rune(u.Up[i].Repository)
jRunes := []rune(u.Up[j].Repository)
return text.LessRunes(iRunes, jRunes)
}
// calculateFormatting calculates formatting parameters for printing upgrades
func calculateFormatting(upgrades []Upgrade) (longestName, longestVersion, longestNumber int) {
for i := range upgrades {
upgrade := &upgrades[i]
packNameLen := len(StylizedNameWithRepository(upgrade))
packVersion, _ := query.GetVersionDiff(upgrade.LocalVersion, upgrade.RemoteVersion)
packVersionLen := len(packVersion)
longestName = max(packNameLen, longestName)
longestVersion = max(packVersionLen, longestVersion)
}
lenUp := len(upgrades)
longestNumber = len(fmt.Sprintf("%v", lenUp))
return
}
// Print prints the details of the packages to upgrade.
func (u UpSlice) Print(logger *text.Logger) {
longestName, longestVersion, longestNumber := calculateFormatting(u.Up)
namePadding := fmt.Sprintf("%%-%ds ", longestName)
versionPadding := fmt.Sprintf("%%-%ds", longestVersion)
numberPadding := fmt.Sprintf("%%%dd ", longestNumber)
for k := range u.Up {
upgrade := &u.Up[k]
left, right := query.GetVersionDiff(upgrade.LocalVersion, upgrade.RemoteVersion)
logger.Print(text.Magenta(fmt.Sprintf(numberPadding, len(u.Up)-k)))
logger.Print(fmt.Sprintf(namePadding, StylizedNameWithRepository(upgrade)))
logger.Printf("%s -> %s\n", fmt.Sprintf(versionPadding, left), right)
if upgrade.Extra != "" {
logger.Println(strings.Repeat(" ", longestNumber), upgrade.Extra)
}
}
}
func (u UpSlice) PrintDeps(logger *text.Logger) {
longestName, longestVersion, longestNumber := calculateFormatting(u.PulledDeps)
namePadding := fmt.Sprintf(" %s%%-%ds ", strings.Repeat(" ", longestNumber), longestName)
versionPadding := fmt.Sprintf("%%-%ds", longestVersion)
for k := range u.PulledDeps {
upgrade := &u.PulledDeps[k]
left, right := query.GetVersionDiff(upgrade.LocalVersion, upgrade.RemoteVersion)
logger.Printf("%s", fmt.Sprintf(namePadding, StylizedNameWithRepository(upgrade)))
logger.Printf("%s -> %s\n", fmt.Sprintf(versionPadding, left), right)
if upgrade.Extra != "" {
logger.Println(strings.Repeat(" ", longestNumber), strings.ToLower(upgrade.Extra))
}
}
logger.Println()
}
================================================
FILE: pkg/upgrade/upgrade_test.go
================================================
//go:build !integration
// +build !integration
package upgrade
import (
"testing"
"github.com/Jguer/yay/v12/pkg/query"
"github.com/Jguer/yay/v12/pkg/text"
)
func TestGetVersionDiff(t *testing.T) {
text.UseColor = true
type versionPair struct {
Old string
New string
}
in := []versionPair{
{"1-1", "1-1"},
{"1-1", "2-1"},
{"2-1", "1-1"},
{"1-1", "1-2"},
{"1-2", "1-1"},
{"1.2.3-1", "1.2.4-1"},
{"1.8rc1+6+g0f377f94-1", "1.8rc1+1+g7e949283-1"},
{"1.8rc1+6+g0f377f94-1", "1.8rc2+1+g7e949283-1"},
{"1.8rc2", "1.9rc1"},
{"2.99.917+812+g75795523-1", "2.99.917+823+gd9bf46e4-1"},
{"1.2.9-1", "1.2.10-1"},
{"1.2.10-1", "1.2.9-1"},
{"1.2-1", "1.2.1-1"},
{"1.2.1-1", "1.2-1"},
{"0.7-4", "0.7+4+gd8d8c67-1"},
{"1.0.2_r0-1", "1.0.2_r0-2"},
{"1.0.2_r0-1", "1.0.2_r1-1"},
{"1.0.2_r0-1", "1.0.3_r0-1"},
}
out := []versionPair{
{"1-1" + text.Red(""), "1-1" + text.Green("")},
{text.Red("1-1"), text.Green("2-1")},
{text.Red("2-1"), text.Green("1-1")},
{"1-" + text.Red("1"), "1-" + text.Green("2")},
{"1-" + text.Red("2"), "1-" + text.Green("1")},
{"1.2." + text.Red("3-1"), "1.2." + text.Green("4-1")},
{"1.8rc1+" + text.Red("6+g0f377f94-1"), "1.8rc1+" + text.Green("1+g7e949283-1")},
{"1.8" + text.Red("rc1+6+g0f377f94-1"), "1.8" + text.Green("rc2+1+g7e949283-1")},
{"1." + text.Red("8rc2"), "1." + text.Green("9rc1")},
{"2.99.917+" + text.Red("812+g75795523-1"), "2.99.917+" + text.Green("823+gd9bf46e4-1")},
{"1.2." + text.Red("9-1"), "1.2." + text.Green("10-1")},
{"1.2." + text.Red("10-1"), "1.2." + text.Green("9-1")},
{"1.2" + text.Red("-1"), "1.2" + text.Green(".1-1")},
{"1.2" + text.Red(".1-1"), "1.2" + text.Green("-1")},
{"0.7" + text.Red("-4"), "0.7" + text.Green("+4+gd8d8c67-1")},
{"1.0.2_r0-" + text.Red("1"), "1.0.2_r0-" + text.Green("2")},
{"1.0.2_" + text.Red("r0-1"), "1.0.2_" + text.Green("r1-1")},
{"1.0." + text.Red("2_r0-1"), "1.0." + text.Green("3_r0-1")},
}
for i, pair := range in {
o, n := query.GetVersionDiff(pair.Old, pair.New)
if o != out[i].Old || n != out[i].New {
t.Errorf("Test %-2d failed for update: expected (%s => %s) got (%s => %s) %d %d %d %d",
i+1, out[i].Old, out[i].New, o, n, len(out[i].Old), len(out[i].New), len(o), len(n))
}
}
}
================================================
FILE: pkg/vcs/.snapshots/TestInfoStore_Update-simple
================================================
{
"hello": {
"github.com/jguer/yay.git": {
"protocols": [
"git"
],
"branch": "master",
"sha": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
}
}
}
================================================
FILE: pkg/vcs/mock.go
================================================
package vcs
import (
"context"
"slices"
alpm "github.com/Jguer/dyalpm"
gosrc "github.com/Morganamilo/go-srcinfo"
)
type Mock struct {
OriginsByPackage map[string]OriginInfoByURL
ToUpgradeReturn []string
}
func (m *Mock) ToUpgrade(ctx context.Context, pkgName string) bool {
return slices.Contains(m.ToUpgradeReturn, pkgName)
}
func (m *Mock) Update(ctx context.Context, pkgName string, sources []gosrc.ArchString) {
}
func (m *Mock) Save() error {
return nil
}
func (m *Mock) RemovePackages(pkgs []string) {
}
func (m *Mock) Load() error {
return nil
}
func (m *Mock) CleanOrphans(pkgs map[string]alpm.Package) {
}
================================================
FILE: pkg/vcs/vcs.go
================================================
package vcs
import (
"context"
"encoding/json"
"errors"
"fmt"
"os"
"os/exec"
"slices"
"strings"
"sync"
"time"
alpm "github.com/Jguer/dyalpm"
gosrc "github.com/Morganamilo/go-srcinfo"
"github.com/leonelquinteros/gotext"
"github.com/Jguer/yay/v12/pkg/settings/exe"
"github.com/Jguer/yay/v12/pkg/text"
)
const defaultTimeout = 15 * time.Second
type Store interface {
// ToUpgrade returns true if the package needs to be updated.
ToUpgrade(ctx context.Context, pkgName string) bool
// Update updates the VCS info of a package.
Update(ctx context.Context, pkgName string, sources []gosrc.ArchString)
// RemovePackages removes the VCS info of the packages given as arg if they exist.
RemovePackages(pkgs []string)
// Clean orphaned VCS info.
CleanOrphans(pkgs map[string]alpm.Package)
// Load loads the VCS info from disk.
Load() error
// Save saves the VCS info to disk.
Save() error
}
// InfoStore is a collection of OriginInfoByURL by Package.
// Containing a map of last commit SHAs of a repo.
type InfoStore struct {
OriginsByPackage map[string]OriginInfoByURL
FilePath string
CmdBuilder exe.GitCmdBuilder
mux sync.Mutex
logger *text.Logger
}
// OriginInfoByURL stores the OriginInfo of each origin URL provided.
type OriginInfoByURL map[string]OriginInfo
// OriginInfo contains the last commit sha of a repo
// Example:
//
// "github.com/Jguer/yay.git": {
// "protocols": [
// "https"
// ],
// "branch": "next",
// "sha": "c1171d41467c68ffd3c46748182a16366aaaf87b"
// }.
type OriginInfo struct {
Protocols []string `json:"protocols"`
Branch string `json:"branch"`
SHA string `json:"sha"`
}
func NewInfoStore(filePath string, cmdBuilder exe.GitCmdBuilder,
logger *text.Logger,
) *InfoStore {
infoStore := &InfoStore{
CmdBuilder: cmdBuilder,
FilePath: filePath,
OriginsByPackage: map[string]OriginInfoByURL{},
mux: sync.Mutex{},
logger: logger,
}
return infoStore
}
// GetCommit parses HEAD commit from url and branch.
func (v *InfoStore) getCommit(ctx context.Context, url, branch string, protocols []string) string {
if len(protocols) > 0 {
protocol := protocols[len(protocols)-1]
ctxTimeout, cancel := context.WithTimeout(ctx, defaultTimeout)
defer cancel()
cmd := v.CmdBuilder.BuildGitCmd(ctxTimeout, "", "ls-remote", protocol+"://"+url, branch)
stdout, stderr, err := v.CmdBuilder.Capture(cmd)
if err != nil {
exitError := &exec.ExitError{}
if ok := errors.As(err, &exitError); ok && exitError.ExitCode() == 128 {
v.logger.Warnln(gotext.Get("devel check for package failed: '%s' encountered an error", cmd.String()), ": ", stderr)
return ""
}
v.logger.Warnln(gotext.Get("devel check for package failed: '%s' encountered an error", cmd.String()), ": ", err)
return ""
}
split := strings.Fields(stdout)
if len(split) < 2 {
return ""
}
commit := split[0]
return commit
}
return ""
}
func (v *InfoStore) Update(ctx context.Context, pkgName string, sources []gosrc.ArchString) {
var wg sync.WaitGroup
info := make(OriginInfoByURL)
checkSource := func(source gosrc.ArchString) {
defer wg.Done()
url, branch, protocols := parseSource(source.Value)
if url == "" || branch == "" {
return
}
commit := v.getCommit(ctx, url, branch, protocols)
if commit == "" {
return
}
v.mux.Lock()
info[url] = OriginInfo{
protocols,
branch,
commit,
}
v.OriginsByPackage[pkgName] = info
v.logger.Debugln(gotext.Get("Found git repo: %s", text.Cyan(url)))
if err := v.Save(); err != nil {
fmt.Fprintln(os.Stderr, err)
}
v.mux.Unlock()
}
for _, source := range sources {
wg.Add(1)
go checkSource(source)
}
wg.Wait()
}
// parseSource returns the git url, default branch and protocols it supports.
func parseSource(source string) (url, branch string, protocols []string) {
split := strings.Split(source, "::")
source = split[len(split)-1]
split = strings.SplitN(source, "://", 2)
if len(split) != 2 {
return "", "", nil
}
protocols = strings.SplitN(split[0], "+", 2)
git := slices.Contains(protocols, "git")
protocols = protocols[len(protocols)-1:]
if !git {
return "", "", nil
}
split = strings.SplitN(split[1], "#", 2)
if len(split) == 2 {
secondSplit := strings.SplitN(split[1], "=", 2)
if secondSplit[0] != "branch" {
// source has #commit= or #tag= which makes them not vcs
// packages because they reference a specific point
return "", "", nil
}
if len(secondSplit) == 2 {
url = split[0]
branch = secondSplit[1]
}
} else {
url = split[0]
branch = "HEAD"
}
url = strings.Split(url, "?")[0]
branch = strings.Split(branch, "?")[0]
return url, branch, protocols
}
func (v *InfoStore) ToUpgrade(ctx context.Context, pkgName string) bool {
if infos, ok := v.OriginsByPackage[pkgName]; ok {
return v.needsUpdate(ctx, infos)
}
return false
}
func (v *InfoStore) needsUpdate(ctx context.Context, infos OriginInfoByURL) bool {
// used to signal we have gone through all sources and found nothing
finished := make(chan struct{})
alive := 0
// if we find an update we use this to exit early and return true
hasUpdate := make(chan struct{})
closed := make(chan struct{})
defer close(closed)
checkHash := func(url string, info OriginInfo) {
hash := v.getCommit(ctx, url, info.Branch, info.Protocols)
var sendTo chan<- struct{}
if hash != "" && hash != info.SHA {
sendTo = hasUpdate
} else {
sendTo = finished
}
select {
case sendTo <- struct{}{}:
case <-closed:
}
}
for url, info := range infos {
alive++
go checkHash(url, info)
}
for {
select {
case <-hasUpdate:
return true
case <-finished:
alive--
if alive == 0 {
return false
}
}
}
}
func (v *InfoStore) Save() error {
marshalledinfo, err := json.MarshalIndent(v.OriginsByPackage, "", "\t")
if err != nil || string(marshalledinfo) == "null" {
return err
}
in, err := os.OpenFile(v.FilePath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o644)
if err != nil {
return err
}
defer in.Close()
if _, errM := in.Write(marshalledinfo); errM != nil {
return errM
}
return in.Sync()
}
// RemovePackage removes package from VCS information.
func (v *InfoStore) RemovePackages(pkgs []string) {
updated := false
for _, pkgName := range pkgs {
if _, ok := v.OriginsByPackage[pkgName]; ok {
delete(v.OriginsByPackage, pkgName)
updated = true
}
}
if updated {
if err := v.Save(); err != nil {
fmt.Fprintln(os.Stderr, err)
}
}
}
// LoadStore reads a json file and populates a InfoStore structure.
func (v *InfoStore) Load() error {
vfile, err := os.Open(v.FilePath)
if !os.IsNotExist(err) && err != nil {
return fmt.Errorf("failed to open vcs file '%s': %w", v.FilePath, err)
}
defer vfile.Close()
if !os.IsNotExist(err) {
decoder := json.NewDecoder(vfile)
if err = decoder.Decode(&v.OriginsByPackage); err != nil {
return fmt.Errorf("failed to read vcs '%s': %w", v.FilePath, err)
}
}
return nil
}
func (v *InfoStore) CleanOrphans(pkgs map[string]alpm.Package) {
missing := make([]string, 0)
for pkgName := range v.OriginsByPackage {
if _, ok := pkgs[pkgName]; !ok {
v.logger.Debugln("removing orphaned vcs package:", pkgName)
missing = append(missing, pkgName)
}
}
v.RemovePackages(missing)
}
================================================
FILE: pkg/vcs/vcs_test.go
================================================
//go:build !integration
// +build !integration
package vcs
import (
"context"
"encoding/json"
"errors"
"io"
"os"
"os/exec"
"strings"
"testing"
gosrc "github.com/Morganamilo/go-srcinfo"
"github.com/bradleyjkemp/cupaloy"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/Jguer/yay/v12/pkg/db"
"github.com/Jguer/yay/v12/pkg/settings/exe"
"github.com/Jguer/yay/v12/pkg/text"
)
func newTestLogger() *text.Logger {
return text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), true, "test")
}
func TestParsing(t *testing.T) {
t.Parallel()
type source struct {
URL string
Branch string
Protocols []string
}
urls := []string{
"git+https://github.com/neovim/neovim.git",
"git://github.com/jguer/yay.git#branch=master",
"git://github.com/davidgiven/ack",
"git://github.com/jguer/yay.git#tag=v3.440",
"git://github.com/jguer/yay.git#commit=e5470c88c6e2f9e0f97deb4728659ffa70ef5d0c",
"a+b+c+d+e+f://github.com/jguer/yay.git#branch=foo",
}
sources := []source{
{"github.com/neovim/neovim.git", "HEAD", []string{"https"}},
{"github.com/jguer/yay.git", "master", []string{"git"}},
{"github.com/davidgiven/ack", "HEAD", []string{"git"}},
{"", "", nil},
{"", "", nil},
{"", "", nil},
}
for n, url := range urls {
url, branch, protocols := parseSource(url)
compare := sources[n]
assert.Equal(t, compare.URL, url)
assert.Equal(t, compare.Branch, branch)
assert.Equal(t, compare.Protocols, protocols)
}
}
func TestNewInfoStore(t *testing.T) {
t.Parallel()
type args struct {
filePath string
cmdBuilder *exe.CmdBuilder
}
tests := []struct {
name string
args args
}{
{
name: "normal",
args: args{
"/tmp/a.json",
&exe.CmdBuilder{GitBin: "git", GitFlags: []string{"--a", "--b"}, Runner: &exe.OSRunner{
Log: text.NewLogger(io.Discard, os.Stderr, strings.NewReader(""), true, "test"),
}},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
got := NewInfoStore(tt.args.filePath, tt.args.cmdBuilder,
text.NewLogger(io.Discard, os.Stderr, strings.NewReader(""), true, "test"))
assert.NotNil(t, got)
assert.Equal(t, []string{"--a", "--b"}, got.CmdBuilder.(*exe.CmdBuilder).GitFlags)
assert.Equal(t, tt.args.cmdBuilder, got.CmdBuilder)
assert.Equal(t, "/tmp/a.json", got.FilePath)
})
}
}
type MockRunner struct {
Returned []string
Index int
}
func (r *MockRunner) Show(cmd *exec.Cmd) error {
return nil
}
func (r *MockRunner) Capture(cmd *exec.Cmd) (stdout, stderr string, err error) {
stdout = r.Returned[r.Index]
if r.Returned[0] == "error" {
err = errors.New("possible error")
}
return stdout, stderr, err
}
func TestInfoStoreToUpgrade(t *testing.T) {
t.Parallel()
type fields struct {
CmdBuilder *exe.CmdBuilder
}
type args struct {
infos OriginInfoByURL
}
tests := []struct {
name string
fields fields
args args
want bool
}{
{
name: "simple-has_update",
args: args{infos: OriginInfoByURL{
"github.com/Jguer/z.git": OriginInfo{
Protocols: []string{"https"},
Branch: "0",
SHA: "991c5b4146fd27f4aacf4e3111258a848934aaa1",
},
}}, fields: fields{
CmdBuilder: &exe.CmdBuilder{GitBin: "git", GitFlags: []string{""}, Runner: &MockRunner{
Returned: []string{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa HEAD"},
}},
},
want: true,
},
{
name: "double-has_update",
args: args{infos: OriginInfoByURL{
"github.com/Jguer/z.git": OriginInfo{
Protocols: []string{"https"},
Branch: "0",
SHA: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
},
"github.com/Jguer/a.git": OriginInfo{
Protocols: []string{"https"},
Branch: "0",
SHA: "991c5b4146fd27f4aacf4e3111258a848934aaa1",
},
}}, fields: fields{
CmdBuilder: &exe.CmdBuilder{GitBin: "git", GitFlags: []string{""}, Runner: &MockRunner{
Returned: []string{
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa HEAD",
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa HEAD",
},
}},
},
want: true,
},
{
name: "simple-no_update",
args: args{infos: OriginInfoByURL{
"github.com/Jguer/z.git": OriginInfo{
Protocols: []string{"https"},
Branch: "0",
SHA: "991c5b4146fd27f4aacf4e3111258a848934aaa1",
},
}}, fields: fields{
CmdBuilder: &exe.CmdBuilder{GitBin: "git", GitFlags: []string{""}, Runner: &MockRunner{
Returned: []string{"991c5b4146fd27f4aacf4e3111258a848934aaa1 HEAD"},
}},
},
want: false,
},
{
name: "simple-no_split",
args: args{infos: OriginInfoByURL{
"github.com/Jguer/z.git": OriginInfo{
Protocols: []string{"https"},
Branch: "0",
SHA: "991c5b4146fd27f4aacf4e3111258a848934aaa1",
},
}}, fields: fields{
CmdBuilder: &exe.CmdBuilder{GitBin: "git", GitFlags: []string{""}, Runner: &MockRunner{
Returned: []string{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},
}},
},
want: false,
},
{
name: "simple-error",
args: args{infos: OriginInfoByURL{
"github.com/Jguer/z.git": OriginInfo{
Protocols: []string{"https"},
Branch: "0",
SHA: "991c5b4146fd27f4aacf4e3111258a848934aaa1",
},
}}, fields: fields{
CmdBuilder: &exe.CmdBuilder{
GitBin: "git", GitFlags: []string{""},
Runner: &MockRunner{
Returned: []string{"error"},
},
},
},
want: false,
},
{
name: "simple-no protocol",
args: args{infos: OriginInfoByURL{
"github.com/Jguer/z.git": OriginInfo{
Protocols: []string{},
Branch: "0",
SHA: "991c5b4146fd27f4aacf4e3111258a848934aaa1",
},
}}, fields: fields{
CmdBuilder: &exe.CmdBuilder{GitBin: "git", GitFlags: []string{""}, Runner: &MockRunner{
Returned: []string{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},
}},
},
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
v := &InfoStore{
logger: newTestLogger(),
CmdBuilder: tt.fields.CmdBuilder,
OriginsByPackage: map[string]OriginInfoByURL{
"yay": tt.args.infos,
},
}
got := v.ToUpgrade(context.Background(), "yay")
assert.Equal(t, tt.want, got)
})
}
}
func TestInfoStore_NeedsUpdate(t *testing.T) {
t.Parallel()
type fields struct {
CmdBuilder *exe.CmdBuilder
}
type args struct {
infos OriginInfoByURL
}
tests := []struct {
name string
fields fields
args args
want bool
}{
{
name: "simple-has_update",
args: args{infos: OriginInfoByURL{
"github.com/Jguer/z.git": OriginInfo{
Protocols: []string{"https"},
Branch: "0",
SHA: "991c5b4146fd27f4aacf4e3111258a848934aaa1",
},
}}, fields: fields{
CmdBuilder: &exe.CmdBuilder{GitBin: "git", GitFlags: []string{""}, Runner: &MockRunner{
Returned: []string{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa HEAD"},
}},
},
want: true,
},
{
name: "double-has_update",
args: args{infos: OriginInfoByURL{
"github.com/Jguer/z.git": OriginInfo{
Protocols: []string{"https"},
Branch: "0",
SHA: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
},
"github.com/Jguer/a.git": OriginInfo{
Protocols: []string{"https"},
Branch: "0",
SHA: "991c5b4146fd27f4aacf4e3111258a848934aaa1",
},
}}, fields: fields{
CmdBuilder: &exe.CmdBuilder{GitBin: "git", GitFlags: []string{""}, Runner: &MockRunner{
Returned: []string{
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa HEAD",
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa HEAD",
},
}},
},
want: true,
},
{
name: "simple-no_update",
args: args{infos: OriginInfoByURL{
"github.com/Jguer/z.git": OriginInfo{
Protocols: []string{"https"},
Branch: "0",
SHA: "991c5b4146fd27f4aacf4e3111258a848934aaa1",
},
}}, fields: fields{
CmdBuilder: &exe.CmdBuilder{GitBin: "git", GitFlags: []string{""}, Runner: &MockRunner{
Returned: []string{"991c5b4146fd27f4aacf4e3111258a848934aaa1 HEAD"},
}},
},
want: false,
},
{
name: "simple-no_split",
args: args{infos: OriginInfoByURL{
"github.com/Jguer/z.git": OriginInfo{
Protocols: []string{"https"},
Branch: "0",
SHA: "991c5b4146fd27f4aacf4e3111258a848934aaa1",
},
}}, fields: fields{
CmdBuilder: &exe.CmdBuilder{GitBin: "git", GitFlags: []string{""}, Runner: &MockRunner{
Returned: []string{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},
}},
},
want: false,
},
{
name: "simple-error",
args: args{infos: OriginInfoByURL{
"github.com/Jguer/z.git": OriginInfo{
Protocols: []string{"https"},
Branch: "0",
SHA: "991c5b4146fd27f4aacf4e3111258a848934aaa1",
},
}}, fields: fields{
CmdBuilder: &exe.CmdBuilder{
GitBin: "git", GitFlags: []string{""},
Runner: &MockRunner{
Returned: []string{"error"},
},
},
},
want: false,
},
{
name: "simple-no protocol",
args: args{infos: OriginInfoByURL{
"github.com/Jguer/z.git": OriginInfo{
Protocols: []string{},
Branch: "0",
SHA: "991c5b4146fd27f4aacf4e3111258a848934aaa1",
},
}}, fields: fields{
CmdBuilder: &exe.CmdBuilder{GitBin: "git", GitFlags: []string{""}, Runner: &MockRunner{
Returned: []string{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},
}},
},
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
v := &InfoStore{
logger: newTestLogger(),
CmdBuilder: tt.fields.CmdBuilder,
}
got := v.needsUpdate(context.Background(), tt.args.infos)
assert.Equal(t, tt.want, got)
})
}
}
func TestInfoStore_Update(t *testing.T) {
t.Parallel()
type fields struct {
OriginsByPackage map[string]OriginInfoByURL
CmdBuilder *exe.CmdBuilder
}
type args struct {
pkgName string
sources []gosrc.ArchString
}
tests := []struct {
name string
fields fields
args args
}{
{
name: "simple",
args: args{
pkgName: "hello",
sources: []gosrc.ArchString{{Value: "git://github.com/jguer/yay.git#branch=master"}},
},
fields: fields{
OriginsByPackage: make(map[string]OriginInfoByURL),
CmdBuilder: &exe.CmdBuilder{
GitBin: "git", GitFlags: []string{""},
Runner: &MockRunner{Returned: []string{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa HEAD"}},
},
},
},
}
file, err := os.CreateTemp("/tmp", "yay-infostore-*-test")
filePath := file.Name()
require.NoError(t, err)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
v := &InfoStore{
OriginsByPackage: tt.fields.OriginsByPackage,
logger: newTestLogger(),
FilePath: filePath,
CmdBuilder: tt.fields.CmdBuilder,
}
v.Update(context.Background(), tt.args.pkgName, tt.args.sources)
assert.Len(t, tt.fields.OriginsByPackage, 1)
marshalledinfo, err := json.MarshalIndent(tt.fields.OriginsByPackage, "", "\t")
assert.NoError(t, err)
cupaloy.SnapshotT(t, marshalledinfo)
v.Load()
assert.Len(t, tt.fields.OriginsByPackage, 1)
marshalledinfo, err = json.MarshalIndent(tt.fields.OriginsByPackage, "", "\t")
assert.NoError(t, err)
cupaloy.SnapshotT(t, marshalledinfo)
})
}
require.NoError(t, os.Remove(filePath))
}
func TestInfoStore_Remove(t *testing.T) {
t.Parallel()
type fields struct {
OriginsByPackage map[string]OriginInfoByURL
}
type args struct {
pkgs []string
}
tests := []struct {
name string
fields fields
args args
}{
{
name: "simple",
args: args{pkgs: []string{"a", "c"}},
fields: fields{
OriginsByPackage: map[string]OriginInfoByURL{
"a": {},
"b": {},
"c": {},
"d": {},
},
},
},
}
file, err := os.CreateTemp("/tmp", "yay-vcs-*-test")
filePath := file.Name()
require.NoError(t, err)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
v := &InfoStore{
OriginsByPackage: tt.fields.OriginsByPackage,
logger: newTestLogger(),
FilePath: filePath,
}
v.RemovePackages(tt.args.pkgs)
assert.Len(t, tt.fields.OriginsByPackage, 2)
})
}
require.NoError(t, os.Remove(filePath))
}
func TestInfoStore_CleanOrphans(t *testing.T) {
t.Parallel()
type fields struct {
OriginsByPackage map[string]OriginInfoByURL
}
type args struct {
pkgs map[string]db.IPackage
}
tests := []struct {
name string
fields fields
args args
}{
{
name: "simple",
args: args{pkgs: map[string]db.IPackage{"a": nil, "b": nil, "d": nil}},
fields: fields{
OriginsByPackage: map[string]OriginInfoByURL{
"a": {},
"b": {},
"c": {},
"d": {},
},
},
},
}
file, err := os.CreateTemp("/tmp", "yay-vcs-*-test")
filePath := file.Name()
require.NoError(t, err)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
v := &InfoStore{
OriginsByPackage: tt.fields.OriginsByPackage,
FilePath: filePath,
logger: text.NewLogger(io.Discard, os.Stderr, strings.NewReader(""), false, "test"),
}
v.CleanOrphans(tt.args.pkgs)
assert.Len(t, tt.fields.OriginsByPackage, 3)
})
}
require.NoError(t, os.Remove(filePath))
}
================================================
FILE: po/ca.po
================================================
#
# Translators:
# Davidmp , 2025
#
msgid ""
msgstr ""
"Last-Translator: Davidmp , 2025\n"
"Language-Team: Catalan (https://app.transifex.com/yay-1/teams/123732/ca/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: ca\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: xgotext\n"
#: clean.go:83
msgid ""
"\n"
"Build directory:"
msgstr ""
"\n"
"Directori de construcció:"
#: pkg/db/ialpm/alpm.go:201 pkg/dep/dep_graph.go:740
msgid ""
"\n"
"Enter a number (default=1): "
msgstr ""
"\n"
"Introduïu un número (per defecte = 1):"
#: pkg/menus/menu.go:32
msgid " (Build Files Exist)"
msgstr "(Els fitxers de compilació ja existeixen)"
#: pkg/menus/menu.go:27
msgid " (Installed)"
msgstr "(Instal·lat)"
#: cmd.go:453
msgid " [Installed]"
msgstr "[Instal·lat]"
#: cmd.go:410 vote.go:36
msgid " there is nothing to do"
msgstr "No hi ha res per fer."
#: pkg/menus/menu.go:49
msgid "%s [A]ll [Ab]ort [I]nstalled [No]tInstalled or (1 2 3, 1-3, ^4)"
msgstr "%s [T]ot [Av]orta [I]nstal·lat [No] instal·lat o (1 2 3, 1-3, ^4)"
#: pkg/sync/build/installer.go:308
msgid "%s already made -- skipping build"
msgstr "%s ja fet: se n'omet la construcció."
#: pkg/menus/edit_menu.go:57
msgid "%s is not set"
msgstr "%s no està establert."
#: pkg/settings/exe/cmd_builder.go:257
msgid "%s is present."
msgstr "%s és present."
#: pkg/dep/dep_graph.go:460 pkg/sync/build/installer.go:305
msgid "%s is up to date -- skipping"
msgstr "%s està al dia: s'omet."
#: pkg/upgrade/service.go:291
msgid "%s to upgrade/install."
msgstr "%s per actualitzar / instal·lar."
#: pkg/upgrade/service.go:285
msgid "%s will also be installed for this operation."
msgstr "%s també s'instal·larà per a aquesta operació."
#: pkg/sync/srcinfo/pgp/keys.go:124
msgid "%s, required by: %s"
msgstr "%s, requerit per %s"
#: pkg/menus/diff_menu.go:49
msgid "%s: No changes -- skipping"
msgstr "%s: sense canvis, s'omet."
#: pkg/query/filter.go:22
msgid "%s: can't use target with option --aur -- skipping"
msgstr "%s: no es pot usar l'objectiu amb l'opció --aur, s'omet."
#: pkg/query/filter.go:17
msgid "%s: can't use target with option --repo -- skipping"
msgstr "%s: no es pot usar l'objectiu amb l'opció --repo, s'omet."
#: pkg/upgrade/sources.go:57
msgid "%s: ignoring package upgrade (%s => %s)"
msgstr "%s: s'ignora l'actualització del paquet (%s => %s)"
#: pkg/query/aur_warnings.go:46
msgid "%s: local (%s) is newer than AUR (%s)"
msgstr "%s: el paquet local (%s) és més nou que el de l'AUR (%s)"
#: vote.go:51
msgid ""
"%s: please set AUR_USERNAME and AUR_PASSWORD environment variables for "
"voting"
msgstr ""
"%s: establiu les variables d'entorn AUR_NOMDUSUARI i AUR_CONTRASENYA per "
"votar."
#: pkg/download/unified.go:192
msgid "(%d/%d) Downloaded PKGBUILD from ABS: %s"
msgstr "(%d/%d) PKGBUILD baixat de l'ABS: %s"
#: pkg/download/aur.go:92 pkg/download/unified.go:188
msgid "(%d/%d) Downloaded PKGBUILD: %s"
msgstr "(%d/%d) PKGBUILD baixat: %s"
#: pkg/download/aur.go:82
msgid "(%d/%d) Failed to download PKGBUILD: %s"
msgstr "(%d/%d) Ha fallat baixar el PKGBUILD: %s"
#: pkg/sync/srcinfo/service.go:109
msgid "(%d/%d) Parsing SRCINFO: %s"
msgstr "(%d/%d) S'analitza SRCINFO: %s"
#: pkg/query/types.go:103 pkg/query/types.go:72
msgid "(Installed)"
msgstr "(Instal·lat)"
#: pkg/query/types.go:101 pkg/query/types.go:70
msgid "(Installed: %s)"
msgstr "(Instal·lat: %s)"
#: pkg/query/types.go:61
msgid "(Orphaned)"
msgstr "(Orfes)"
#: pkg/query/types.go:65
msgid "(Out-of-date: %s)"
msgstr "(Obsolet: %s)"
#: pkg/dep/dep_graph.go:75
msgid "AUR"
msgstr "AUR"
#: print.go:44
msgid "AUR URL"
msgstr "URL de l'AUR"
#: pkg/menus/edit_menu.go:58
msgid "Add %s or %s to your environment variables"
msgstr "Afegiu %s o %s a les variables d'entorn."
#: main.go:60
msgid "Avoid running yay as root/sudo."
msgstr "Eviteu executar el yay com a root / sudo."
#: pkg/dep/dep_graph.go:63
msgid "Check Dependency"
msgstr "Comprova'n la dependència"
#: print.go:41
msgid "Check Deps"
msgstr "Comprova les dependències"
#: pkg/upgrade/service.go:89
msgid "Checking development packages..."
msgstr "Es comproven els paquets de desenvolupament..."
#: pkg/sync/workdir/clean.go:45
msgid "Cleaning (%d/%d): %s"
msgstr "Es neteja (%d/%d): %s"
#: print.go:42
msgid "Conflicts With"
msgstr "Té conflicte amb"
#: pkg/menus/clean_menu.go:62
msgid "Deleting (%d/%d): %s"
msgstr "Se suprimeix (%d/%d): %s"
#: pkg/dep/dep_graph.go:61
msgid "Dependency"
msgstr "Dependència"
#: print.go:38
msgid "Depends On"
msgstr "Depèn de"
#: print.go:33
msgid "Description"
msgstr "Descripció"
#: pkg/menus/diff_menu.go:160
msgid "Diffs to show?"
msgstr "Diferències per mostrar?"
#: pkg/settings/migrations.go:25
msgid "Disable 'provides' setting by default"
msgstr "Desactiva la configuració de \"proporciona\" per defecte."
#: clean.go:78
msgid "Do you want to remove ALL AUR packages from cache?"
msgstr "Voleu suprimir TOTS els paquets de l'AUR de la memòria cau?"
#: clean.go:95
msgid "Do you want to remove ALL untracked AUR files?"
msgstr "Voleu suprimir TOTS els fitxers de l'AUR sense seguiment?"
#: clean.go:80
msgid "Do you want to remove all other AUR packages from cache?"
msgstr "Voleu suprimir tots els altres paquets de l'AUR de la memòria cau?"
#: pkg/menus/edit_menu.go:61
msgid "Edit PKGBUILD with?"
msgstr "Amb què voleu editar el PKGBUILD?"
#: pkg/query/errors.go:13
msgid "Error during AUR search: %s\n"
msgstr "Error durant la cerca a l'AUR: %s\n"
#: pkg/upgrade/service.go:295
msgid "Excluding packages may cause partial upgrades and break systems"
msgstr ""
"L'exclusió de paquets pot provocar actualitzacions parcials i trencar els "
"sistemes."
#: pkg/dep/dep_graph.go:60
msgid "Explicit"
msgstr "Explícit"
#: print.go:91
msgid "Explicitly installed packages: %s"
msgstr "Paquets instal·lats explícitament: %s"
#: pkg/dep/dep_graph.go:437 pkg/dep/dep_graph.go:535
msgid "Failed to find AUR package for"
msgstr "No s'ha pogut trobar el paquet de l'AUR per a"
#: pkg/sync/build/installer.go:120
msgid "Failed to install layer, rolling up to next layer."
msgstr "Ha fallat instal·lar la capa. Es passa a la següent."
#: pkg/sync/build/errors.go:16
msgid ""
"Failed to install the following packages. Manual intervention is required:"
msgstr "Ha fallat instal·lar els paquets següents. Cal intervenció manual:"
#: print.go:45
msgid "First Submitted"
msgstr "Enviat primer"
#: pkg/query/aur_warnings.go:83
msgid "Flagged Out Of Date AUR Packages:"
msgstr "Paquets de l'AUR obsolets marcats:"
#: print.go:90
msgid "Foreign installed packages: %s"
msgstr "Paquets forans instal·lats: %s"
#: pkg/vcs/vcs.go:144
msgid "Found git repo: %s"
msgstr "S'ha trobat el repositori git: %s"
#: vcs.go:72
msgid "GenDB finished. No packages were installed"
msgstr "GenDB ha acabat. No s'ha instal·lat cap paquet."
#: print.go:36
msgid "Groups"
msgstr "Grups"
#: pkg/sync/srcinfo/pgp/keys.go:88
msgid "Import?"
msgstr "Ho importo?"
#: pkg/sync/srcinfo/pgp/keys.go:97
msgid "Importing keys with gpg..."
msgstr "S'importen claus amb gpg..."
#: print.go:46
msgid "Keywords"
msgstr "Paraules clau"
#: print.go:47
msgid "Last Modified"
msgstr "Darrera modificació"
#: print.go:35
msgid "Licenses"
msgstr "Llicències"
#: pkg/dep/dep_graph.go:77
msgid "Local"
msgstr "Local"
#: print.go:48
msgid "Maintainer"
msgstr "Mantenidor"
#: pkg/dep/dep_graph.go:62
msgid "Make Dependency"
msgstr "Dependència de construcció"
#: print.go:40
msgid "Make Deps"
msgstr "Dependències de construcció"
#: pkg/dep/dep_graph.go:79
msgid "Missing"
msgstr "Manca"
#: pkg/query/aur_warnings.go:75
msgid "Missing AUR Debug Packages:"
msgstr "Manquen paquets de depuració de l'AUR:"
#: print.go:31
msgid "Name"
msgstr "Nom"
#: pkg/dep/dep_graph.go:442 pkg/dep/dep_graph.go:564
msgid "No AUR package found for"
msgstr "No s'ha trobat cap paquet d'AUR per a"
#: pkg/dep/dep_graph.go:564
msgid "required by"
msgstr "requerit per "
#: pkg/dep/dep_graph.go:182
msgid "No package found for"
msgstr "No s'ha trobat cap paquet per"
#: print.go:225
msgid "None"
msgstr "Cap"
#: print.go:39
msgid "Optional Deps"
msgstr "Dependències opcionals"
#: pkg/query/aur_warnings.go:79
msgid "Orphan (unmaintained) AUR Packages:"
msgstr "Paquets d'AUR orfes (no mantinguts):"
#: print.go:53 print.go:55
msgid "Out-of-date"
msgstr "Obsolet"
#: pkg/sync/srcinfo/pgp/keys.go:115
msgid "PGP keys need importing:"
msgstr "Cal importar claus PGP:"
#: pkg/sync/workdir/preparer.go:252
msgid "PKGBUILD up to date, skipping download: %s"
msgstr "PKGBUILD actualitzat, s'omet la baixada: %s"
#: pkg/menus/edit_menu.go:130
msgid "PKGBUILDs to edit?"
msgstr "PKGBUILDs per editar?"
#: print.go:61
msgid "Package Base"
msgstr "Base de paquets"
#: print.go:60
msgid "Package Base ID"
msgstr "ID de la base de paquets"
#: pkg/query/aur_warnings.go:71
msgid "Packages not in AUR:"
msgstr "Paquets no a l'AUR:"
#: pkg/menus/clean_menu.go:54
msgid "Packages to cleanBuild?"
msgstr "Paquets per a la neteja de la construcció?"
#: pkg/dep/dep_graph.go:202
msgid "Packages to exclude"
msgstr "Paquets per excloure"
#: pkg/upgrade/service.go:294
msgid "Packages to exclude: (eg: \"1 2 3\", \"1-3\", \"^4\" or repo name)"
msgstr "Paquets per excloure: (p. ex.: \"1 2 3\", \"1-3\", \"^4\" o nom del repositori)"
#: cmd.go:392
msgid "Packages to install (eg: 1 2 3, 1-3 or ^4)"
msgstr "Paquets per instal·lar (p. ex.: 1 2 3, 1-3 o ^4)"
#: print.go:49
msgid "Popularity"
msgstr "Popularitat"
#: pkg/menus/diff_menu.go:172 pkg/menus/edit_menu.go:143
msgid "Proceed with install?"
msgstr "Voleu continuar la instal·lació?"
#: print.go:37
msgid "Provides"
msgstr "Proporciona"
#: pkg/sync/workdir/preparer.go:125
msgid "Remove make dependencies after install?"
msgstr ""
"Suprimeixo les dependències de construcció després de la instal·lació?"
#: print.go:43
msgid "Replaces"
msgstr "Reemplaça"
#: pkg/db/ialpm/alpm.go:191 print.go:30
msgid "Repository"
msgstr "Repositori"
#: pkg/dep/dep_graph.go:730
msgid "Repository AUR"
msgstr "Repositori de l'AUR"
#: pkg/dep/dep_graph.go:78
msgid "SRCINFO"
msgstr "SRCINFO"
#: pkg/upgrade/service.go:71
msgid "Searching AUR for updates..."
msgstr "Se cerquen actualitzacions a l'AUR..."
#: pkg/upgrade/service.go:159
msgid "Searching databases for updates..."
msgstr "Se cerquen actualitzacions a les bases de dades..."
#: pkg/query/query_builder.go:214
msgid "Showing repo packages only"
msgstr "Es mostren només paquets dels repositoris."
#: print.go:95
msgid "Size of pacman cache %s: %s"
msgstr "Mida de la memòria cau del pacman %s: %s"
#: print.go:98
msgid "Size of yay cache %s: %s"
msgstr "Mida de la memòria cau del yay %s: %s"
#: print.go:62
msgid "Snapshot URL"
msgstr "URL de la instantània"
#: pkg/dep/dep_graph.go:76
msgid "Sync"
msgstr "Sincronització"
#: print.go:100
msgid "Ten biggest packages:"
msgstr "Els deu paquets més grossos:"
#: pkg/sync/sync.go:124
msgid "The following packages are not compatible with your architecture:"
msgstr "Els paquets següents no són compatibles amb la vostra arquitectura:"
#: pkg/db/ialpm/alpm.go:179 pkg/dep/dep_graph.go:726
msgid "There are %[1]d providers available for %[2]s:"
msgstr "Hi ha %[1]d proveïdors disponibles per a %[2]s:"
#: pkg/settings/exe/cmd_builder.go:258
msgid "There may be another Pacman instance running. Waiting..."
msgstr ""
"Pot ser que hi hagi una altra instància del Pacman en execució. S'espera..."
#: print.go:92
msgid "Total Size occupied by packages: %s"
msgstr "Mida total ocupada pels paquets: %s"
#: print.go:89
msgid "Total installed packages: %s"
msgstr "Total de paquets instal·lats: %s"
#: pkg/sync/sync.go:132
msgid "Try to build them anyway?"
msgstr "Intento construir-los tanmateix?"
#: print.go:34
msgid "URL"
msgstr "URL"
#: clean.go:194 pkg/menus/clean_menu.go:65 pkg/menus/clean_menu.go:71
msgid "Unable to clean:"
msgstr "No es pot netejar:"
#: get.go:42 get.go:74
msgid "Unable to find the following packages:"
msgstr "No s'han pogut trobar els paquets següents:"
#: vote.go:20
msgid "Unable to handle package vote for: %s. err: %s"
msgstr "No es pot gestionar el vot del paquet per a %s. Error: %s"
#: clean.go:170
msgid "Unable to remove %s: %s"
msgstr "No es pot suprimir %s: %s"
#: print.go:32
msgid "Version"
msgstr "Versió"
#: print.go:50
msgid "Votes"
msgstr "Vots"
#: print.go:87
msgid "Yay version v%s"
msgstr "Versió del yay: v%s"
#: pkg/menus/menu.go:49
msgid "[N]one"
msgstr "Ca[p]"
#: pkg/settings/errors.go:29
msgid "aborting due to user"
msgstr "s'avorta a causa de l'usuari"
#: pkg/settings/parser/parser.go:608
msgid "argument '-' specified without input on stdin"
msgstr "argument '-' especificat sense entrada a stdin"
#: local_install.go:26
msgid "cannot find PKGBUILD and .SRCINFO in directory"
msgstr "no es pot trobar PKGBUILD i SRCINFO al directori"
#: pkg/sync/build/pkg_archive.go:148
msgid "cannot find package name: %v"
msgstr "no es pot trobar el nom del paquet: %v"
#: pkg/sync/build/errors.go:30
msgid "could not find PKGDEST for: %s"
msgstr "no s'ha pogut trobar PKGDEST per a %s"
#: errors.go:9
msgid "could not find all required packages"
msgstr "no s'han pogut trobar tots els paquets necessaris"
#: pkg/sync/build/errors.go:61
msgid "could not find any package archives listed in %s"
msgstr "no s'ha pogut trobar cap arxiu de paquets llistat a %s"
#: pkg/sync/build/errors.go:50 pkg/upgrade/service.go:286
msgid "dependency"
msgstr "dependència"
#: pkg/vcs/vcs.go:100 pkg/vcs/vcs.go:96
msgid "devel check for package failed: '%s' encountered an error"
msgstr "la comprovació del paquet ha fallat: %s ha trobat un error"
#: pkg/menus/edit_menu.go:110
msgid "editor did not exit successfully, aborting: %s"
msgstr "l'editor no ha sortit correctament, s'avorta: %s"
#: pkg/sync/workdir/aur_source.go:24
msgid "error downloading sources: %s"
msgstr "error en baixar les fonts: %s"
#: pkg/download/errors.go:25
msgid "error fetching %s: %s"
msgstr "error en obtenir %s: %s"
#: pkg/sync/build/errors.go:9
msgid "error installing repo packages"
msgstr "error en instal·lar paquets dels repositoris"
#: pkg/sync/build/installer.go:266 pkg/sync/build/installer.go:270
msgid "error installing:"
msgstr "error d'instal·lació:"
#: pkg/sync/build/installer.go:233 pkg/sync/build/installer.go:237
msgid "error making: %s"
msgstr "error de construcció: %s"
#: pkg/sync/workdir/merge.go:24
msgid "error merging %s: %s"
msgstr "error en combinar %s: %s"
#: pkg/download/unified.go:59
msgid "error reading %s"
msgstr "error en llegir %s"
#: sync.go:36
msgid "error refreshing databases"
msgstr "error en actualitzar les bases de dades"
#: pkg/sync/workdir/clean.go:51 pkg/sync/workdir/merge.go:17
msgid "error resetting %s: %s"
msgstr "error en restablir %s: %s"
#: pkg/sync/build/errors.go:53
msgid "error updating package install reason to %s"
msgstr "error en actualitzar el motiu d'instal·lació del paquet a %s"
#: pkg/sync/build/errors.go:48
msgid "explicit"
msgstr "explícit"
#: pkg/settings/errors.go:23
msgid "failed to create directory '%s': %s"
msgstr "ha fallat crear el directori %s: %s"
#: pkg/settings/config.go:281
msgid "failed to open config file '%s': %s"
msgstr "ha fallat obrir el fitxer de configuració %s: %s"
#: pkg/sync/srcinfo/service.go:114
msgid "failed to parse %s -- skipping: %s"
msgstr "ha fallat analitzar %s, s'omet: %s"
#: pkg/sync/srcinfo/service.go:118
msgid "failed to parse %s: %s"
msgstr "no s'ha pogut analitzar %s: %s"
#: local_install.go:77
msgid "failed to parse .SRCINFO"
msgstr "ha fallat analitzar .SRCINFO"
#: pkg/settings/config.go:291
msgid "failed to read config file '%s': %s"
msgstr "ha fallat llegir el fitxer de configuració %s: %s"
#: pkg/cmd/graph/main.go:46 pkg/runtime/runtime.go:73
msgid "failed to retrieve aur Cache"
msgstr "ha fallat obtenir la cau de l'AUR"
#: pkg/upgrade/sources.go:27
msgid "ignoring package devel upgrade (no AUR info found):"
msgstr ""
"s'ignora l'actualització del paquet de desenvolupament (no se n'ha trobat "
"cap informació a l'AUR):"
#: pkg/text/errors.go:8
msgid "input too long"
msgstr "entrada massa llarga"
#: pkg/db/ialpm/alpm.go:222 pkg/dep/dep_graph.go:761
msgid "invalid number: %s"
msgstr "número no vàlid: %s"
#: pkg/settings/parser/parser.go:174
msgid "invalid option '%s'"
msgstr "opció no vàlida: %s"
#: cmd.go:197
msgid "invalid option: '--deps' and '--explicit' may not be used together"
msgstr "opció no vàlida: \"--deps\" i \"--explicit\" no es poden usar juntes"
#: pkg/download/abs.go:22
msgid "invalid repository"
msgstr "repositori no vàlid"
#: pkg/db/ialpm/alpm.go:227 pkg/dep/dep_graph.go:767
msgid "invalid value: %d is not between %d and %d"
msgstr "valor no vàlid: %d no és entre %d i %d"
#: pkg/text/input.go:48
msgid "no"
msgstr "no"
#: pkg/sync/srcinfo/pgp/keys.go:110
msgid "no keys to import"
msgstr "sense claus per importar"
#: pkg/query/errors.go:20
msgid "no query was executed"
msgstr "no s'ha executat cap consulta"
#: local_install.go:66
msgid "no target directories specified"
msgstr "no s'han especificat directoris de destinació"
#: pkg/sync/build/installer.go:242
msgid "nothing to install for %s"
msgstr "no hi ha res per instal·lar per %s"
#: pkg/settings/parser/parser.go:164
msgid "only one operation may be used at a time"
msgstr "només es pot usar una operació alhora"
#: pkg/cmd/graph/main.go:70
msgid "only one target is allowed"
msgstr "només es permet una destinació"
#: pkg/upgrade/service.go:291
msgid "package"
msgid_plural "packages"
msgstr[0] "paquet"
msgstr[1] "paquets"
#: print.go:187
msgid "package '%s' was not found"
msgstr "no s'ha trobat el paquet %s"
#: pkg/download/errors.go:15
msgid "package not found in AUR"
msgstr "paquet no trobat a l'AUR"
#: pkg/download/abs.go:23
msgid "package not found in repos"
msgstr "paquet no trobat als repositoris"
#: pkg/sync/srcinfo/pgp/keys.go:100
msgid "problem importing keys"
msgstr "problema d'importació de claus"
#: clean.go:105
msgid "removing AUR packages from cache..."
msgstr "se suprimeixen paquets de l'AUR de la memòria cau..."
#: clean.go:178 pkg/sync/workdir/clean.go:41
msgid "removing untracked AUR files from cache..."
msgstr ""
"se suprimeixen els fitxers de l'AUR sense seguiment de la memòria cau..."
#: pkg/sync/build/errors.go:38
msgid "the PKGDEST for %s is listed by makepkg but does not exist: %s"
msgstr "el PKGDEST per a %s està llistat per makepkg però no existeix:%s"
#: pkg/sync/sync.go:45
msgid "there is nothing to do"
msgstr "No hi ha res per fer."
#: pkg/db/ialpm/alpm.go:247
msgid "unable to CreateHandle: %s"
msgstr "no se'n pot crear el maneig: %s"
#: cmd.go:186
msgid "unhandled operation"
msgstr "operació no manejada"
#: cmd.go:450
msgid "unknown-version"
msgstr "versió desconeguda"
#: pkg/text/input.go:47
msgid "yes"
msgstr "sí"
================================================
FILE: po/ca_ES.po
================================================
#
# Translators:
# Davidmp , 2025
#
msgid ""
msgstr ""
"Last-Translator: Davidmp , 2025\n"
"Language-Team: Catalan (Spain) (https://app.transifex.com/yay-1/teams/123732/ca_ES/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: ca_ES\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: xgotext\n"
#: clean.go:83
msgid ""
"\n"
"Build directory:"
msgstr ""
"\n"
"Directori de construcció:"
#: pkg/db/ialpm/alpm.go:201 pkg/dep/dep_graph.go:740
msgid ""
"\n"
"Enter a number (default=1): "
msgstr ""
"\n"
"Introduïu un número (per defecte = 1):"
#: pkg/menus/menu.go:32
msgid " (Build Files Exist)"
msgstr "(Els fitxers de compilació ja existeixen)"
#: pkg/menus/menu.go:27
msgid " (Installed)"
msgstr "(Instal·lat)"
#: cmd.go:453
msgid " [Installed]"
msgstr "[Instal·lat]"
#: cmd.go:410 vote.go:36
msgid " there is nothing to do"
msgstr "No hi ha res per fer."
#: pkg/menus/menu.go:49
msgid "%s [A]ll [Ab]ort [I]nstalled [No]tInstalled or (1 2 3, 1-3, ^4)"
msgstr "%s [T]ot [Av]orta [I]nstal·lat [No] instal·lat o (1 2 3, 1-3, ^4)"
#: pkg/sync/build/installer.go:308
msgid "%s already made -- skipping build"
msgstr "%s ja fet: se n'omet la construcció."
#: pkg/menus/edit_menu.go:57
msgid "%s is not set"
msgstr "%s no està establert."
#: pkg/settings/exe/cmd_builder.go:257
msgid "%s is present."
msgstr "%s és present."
#: pkg/dep/dep_graph.go:460 pkg/sync/build/installer.go:305
msgid "%s is up to date -- skipping"
msgstr "%s està al dia: s'omet."
#: pkg/upgrade/service.go:291
msgid "%s to upgrade/install."
msgstr "%s per actualitzar / instal·lar."
#: pkg/upgrade/service.go:285
msgid "%s will also be installed for this operation."
msgstr "%s també s'instal·larà per a aquesta operació."
#: pkg/sync/srcinfo/pgp/keys.go:124
msgid "%s, required by: %s"
msgstr "%s, requerit per %s"
#: pkg/menus/diff_menu.go:49
msgid "%s: No changes -- skipping"
msgstr "%s: sense canvis, s'omet."
#: pkg/query/filter.go:22
msgid "%s: can't use target with option --aur -- skipping"
msgstr "%s: no es pot usar l'objectiu amb l'opció --aur, s'omet."
#: pkg/query/filter.go:17
msgid "%s: can't use target with option --repo -- skipping"
msgstr "%s: no es pot usar l'objectiu amb l'opció --repo, s'omet."
#: pkg/upgrade/sources.go:57
msgid "%s: ignoring package upgrade (%s => %s)"
msgstr "%s: s'ignora l'actualització del paquet (%s => %s)"
#: pkg/query/aur_warnings.go:46
msgid "%s: local (%s) is newer than AUR (%s)"
msgstr "%s: el paquet local (%s) és més nou que el de l'AUR (%s)"
#: vote.go:51
msgid ""
"%s: please set AUR_USERNAME and AUR_PASSWORD environment variables for "
"voting"
msgstr ""
"%s: establiu les variables d'entorn AUR_NOMDUSUARI i AUR_CONTRASENYA per "
"votar."
#: pkg/download/unified.go:192
msgid "(%d/%d) Downloaded PKGBUILD from ABS: %s"
msgstr "(%d/%d) PKGBUILD baixat de l'ABS: %s"
#: pkg/download/aur.go:92 pkg/download/unified.go:188
msgid "(%d/%d) Downloaded PKGBUILD: %s"
msgstr "(%d/%d) PKGBUILD baixat: %s"
#: pkg/download/aur.go:82
msgid "(%d/%d) Failed to download PKGBUILD: %s"
msgstr "(%d/%d) Ha fallat baixar el PKGBUILD: %s"
#: pkg/sync/srcinfo/service.go:109
msgid "(%d/%d) Parsing SRCINFO: %s"
msgstr "(%d/%d) S'analitza SRCINFO: %s"
#: pkg/query/types.go:103 pkg/query/types.go:72
msgid "(Installed)"
msgstr "(Instal·lat)"
#: pkg/query/types.go:101 pkg/query/types.go:70
msgid "(Installed: %s)"
msgstr "(Instal·lat: %s)"
#: pkg/query/types.go:61
msgid "(Orphaned)"
msgstr "(Orfes)"
#: pkg/query/types.go:65
msgid "(Out-of-date: %s)"
msgstr "(Obsolet: %s)"
#: pkg/dep/dep_graph.go:75
msgid "AUR"
msgstr "AUR"
#: print.go:44
msgid "AUR URL"
msgstr "URL de l'AUR"
#: pkg/menus/edit_menu.go:58
msgid "Add %s or %s to your environment variables"
msgstr "Afegiu %s o %s a les variables d'entorn."
#: main.go:60
msgid "Avoid running yay as root/sudo."
msgstr "Eviteu executar el yay com a root / sudo."
#: pkg/dep/dep_graph.go:63
msgid "Check Dependency"
msgstr "Comprova'n la dependència"
#: print.go:41
msgid "Check Deps"
msgstr "Comprova les dependències"
#: pkg/upgrade/service.go:89
msgid "Checking development packages..."
msgstr "Es comproven els paquets de desenvolupament..."
#: pkg/sync/workdir/clean.go:45
msgid "Cleaning (%d/%d): %s"
msgstr "Es neteja (%d/%d): %s"
#: print.go:42
msgid "Conflicts With"
msgstr "Té conflicte amb"
#: pkg/menus/clean_menu.go:62
msgid "Deleting (%d/%d): %s"
msgstr "Se suprimeix (%d/%d): %s"
#: pkg/dep/dep_graph.go:61
msgid "Dependency"
msgstr "Dependència"
#: print.go:38
msgid "Depends On"
msgstr "Depèn de"
#: print.go:33
msgid "Description"
msgstr "Descripció"
#: pkg/menus/diff_menu.go:160
msgid "Diffs to show?"
msgstr "Diferències per mostrar?"
#: pkg/settings/migrations.go:25
msgid "Disable 'provides' setting by default"
msgstr "Desactiva la configuració de \"proporciona\" per defecte."
#: clean.go:78
msgid "Do you want to remove ALL AUR packages from cache?"
msgstr "Voleu suprimir TOTS els paquets de l'AUR de la memòria cau?"
#: clean.go:95
msgid "Do you want to remove ALL untracked AUR files?"
msgstr "Voleu suprimir TOTS els fitxers de l'AUR sense seguiment?"
#: clean.go:80
msgid "Do you want to remove all other AUR packages from cache?"
msgstr "Voleu suprimir tots els altres paquets de l'AUR de la memòria cau?"
#: pkg/menus/edit_menu.go:61
msgid "Edit PKGBUILD with?"
msgstr "Amb què voleu editar el PKGBUILD?"
#: pkg/query/errors.go:13
msgid "Error during AUR search: %s\n"
msgstr "Error durant la cerca a l'AUR: %s\n"
#: pkg/upgrade/service.go:295
msgid "Excluding packages may cause partial upgrades and break systems"
msgstr ""
"L'exclusió de paquets pot provocar actualitzacions parcials i trencar els "
"sistemes."
#: pkg/dep/dep_graph.go:60
msgid "Explicit"
msgstr "Explícit"
#: print.go:91
msgid "Explicitly installed packages: %s"
msgstr "Paquets instal·lats explícitament: %s"
#: pkg/dep/dep_graph.go:437 pkg/dep/dep_graph.go:535
msgid "Failed to find AUR package for"
msgstr "No s'ha pogut trobar el paquet de l'AUR per a"
#: pkg/sync/build/installer.go:120
msgid "Failed to install layer, rolling up to next layer."
msgstr "Ha fallat instal·lar la capa. Es passa a la següent."
#: pkg/sync/build/errors.go:16
msgid ""
"Failed to install the following packages. Manual intervention is required:"
msgstr "Ha fallat instal·lar els paquets següents. Cal intervenció manual:"
#: print.go:45
msgid "First Submitted"
msgstr "Enviat primer"
#: pkg/query/aur_warnings.go:83
msgid "Flagged Out Of Date AUR Packages:"
msgstr "Paquets de l'AUR obsolets marcats:"
#: print.go:90
msgid "Foreign installed packages: %s"
msgstr "Paquets forans instal·lats: %s"
#: pkg/vcs/vcs.go:144
msgid "Found git repo: %s"
msgstr "S'ha trobat el repositori git: %s"
#: vcs.go:72
msgid "GenDB finished. No packages were installed"
msgstr "GenDB ha acabat. No s'ha instal·lat cap paquet."
#: print.go:36
msgid "Groups"
msgstr "Grups"
#: pkg/sync/srcinfo/pgp/keys.go:88
msgid "Import?"
msgstr "Ho importo?"
#: pkg/sync/srcinfo/pgp/keys.go:97
msgid "Importing keys with gpg..."
msgstr "S'importen claus amb gpg..."
#: print.go:46
msgid "Keywords"
msgstr "Paraules clau"
#: print.go:47
msgid "Last Modified"
msgstr "Darrera modificació"
#: print.go:35
msgid "Licenses"
msgstr "Llicències"
#: pkg/dep/dep_graph.go:77
msgid "Local"
msgstr "Local"
#: print.go:48
msgid "Maintainer"
msgstr "Mantenidor"
#: pkg/dep/dep_graph.go:62
msgid "Make Dependency"
msgstr "Dependència de construcció"
#: print.go:40
msgid "Make Deps"
msgstr "Dependències de construcció"
#: pkg/dep/dep_graph.go:79
msgid "Missing"
msgstr "Manca"
#: pkg/query/aur_warnings.go:75
msgid "Missing AUR Debug Packages:"
msgstr "Manquen paquets de depuració de l'AUR:"
#: print.go:31
msgid "Name"
msgstr "Nom"
#: pkg/dep/dep_graph.go:442 pkg/dep/dep_graph.go:564
msgid "No AUR package found for"
msgstr "No s'ha trobat cap paquet d'AUR per a"
#: pkg/dep/dep_graph.go:564
msgid "required by"
msgstr "requerit per "
#: pkg/dep/dep_graph.go:182
msgid "No package found for"
msgstr "No s'ha trobat cap paquet per"
#: print.go:225
msgid "None"
msgstr "Cap"
#: print.go:39
msgid "Optional Deps"
msgstr "Dependències opcionals"
#: pkg/query/aur_warnings.go:79
msgid "Orphan (unmaintained) AUR Packages:"
msgstr "Paquets d'AUR orfes (no mantinguts):"
#: print.go:53 print.go:55
msgid "Out-of-date"
msgstr "Obsolet"
#: pkg/sync/srcinfo/pgp/keys.go:115
msgid "PGP keys need importing:"
msgstr "Cal importar claus PGP:"
#: pkg/sync/workdir/preparer.go:252
msgid "PKGBUILD up to date, skipping download: %s"
msgstr "PKGBUILD actualitzat, s'omet la baixada: %s"
#: pkg/menus/edit_menu.go:130
msgid "PKGBUILDs to edit?"
msgstr "PKGBUILDs per editar?"
#: print.go:61
msgid "Package Base"
msgstr "Base de paquets"
#: print.go:60
msgid "Package Base ID"
msgstr "ID de la base de paquets"
#: pkg/query/aur_warnings.go:71
msgid "Packages not in AUR:"
msgstr "Paquets no a l'AUR:"
#: pkg/menus/clean_menu.go:54
msgid "Packages to cleanBuild?"
msgstr "Paquets per a la neteja de la construcció?"
#: pkg/dep/dep_graph.go:202
msgid "Packages to exclude"
msgstr "Paquets per excloure"
#: pkg/upgrade/service.go:294
msgid "Packages to exclude: (eg: \"1 2 3\", \"1-3\", \"^4\" or repo name)"
msgstr "Paquets per excloure: (p. ex.: \"1 2 3\", \"1-3\", \"^4\" o nom del repositori)"
#: cmd.go:392
msgid "Packages to install (eg: 1 2 3, 1-3 or ^4)"
msgstr "Paquets per instal·lar (p. ex.: 1 2 3, 1-3 o ^4)"
#: print.go:49
msgid "Popularity"
msgstr "Popularitat"
#: pkg/menus/diff_menu.go:172 pkg/menus/edit_menu.go:143
msgid "Proceed with install?"
msgstr "Voleu continuar la instal·lació?"
#: print.go:37
msgid "Provides"
msgstr "Proporciona"
#: pkg/sync/workdir/preparer.go:125
msgid "Remove make dependencies after install?"
msgstr ""
"Suprimeixo les dependències de construcció després de la instal·lació?"
#: print.go:43
msgid "Replaces"
msgstr "Reemplaça"
#: pkg/db/ialpm/alpm.go:191 print.go:30
msgid "Repository"
msgstr "Repositori"
#: pkg/dep/dep_graph.go:730
msgid "Repository AUR"
msgstr "Repositori de l'AUR"
#: pkg/dep/dep_graph.go:78
msgid "SRCINFO"
msgstr "SRCINFO"
#: pkg/upgrade/service.go:71
msgid "Searching AUR for updates..."
msgstr "Se cerquen actualitzacions a l'AUR..."
#: pkg/upgrade/service.go:159
msgid "Searching databases for updates..."
msgstr "Se cerquen actualitzacions a les bases de dades..."
#: pkg/query/query_builder.go:214
msgid "Showing repo packages only"
msgstr "Es mostren només paquets dels repositoris."
#: print.go:95
msgid "Size of pacman cache %s: %s"
msgstr "Mida de la memòria cau del pacman %s: %s"
#: print.go:98
msgid "Size of yay cache %s: %s"
msgstr "Mida de la memòria cau del yay %s: %s"
#: print.go:62
msgid "Snapshot URL"
msgstr "URL de la instantània"
#: pkg/dep/dep_graph.go:76
msgid "Sync"
msgstr "Sincronització"
#: print.go:100
msgid "Ten biggest packages:"
msgstr "Els deu paquets més grossos:"
#: pkg/sync/sync.go:124
msgid "The following packages are not compatible with your architecture:"
msgstr "Els paquets següents no són compatibles amb la vostra arquitectura:"
#: pkg/db/ialpm/alpm.go:179 pkg/dep/dep_graph.go:726
msgid "There are %[1]d providers available for %[2]s:"
msgstr "Hi ha %[1]d proveïdors disponibles per a %[2]s:"
#: pkg/settings/exe/cmd_builder.go:258
msgid "There may be another Pacman instance running. Waiting..."
msgstr ""
"Pot ser que hi hagi una altra instància del Pacman en execució. S'espera..."
#: print.go:92
msgid "Total Size occupied by packages: %s"
msgstr "Mida total ocupada pels paquets: %s"
#: print.go:89
msgid "Total installed packages: %s"
msgstr "Total de paquets instal·lats: %s"
#: pkg/sync/sync.go:132
msgid "Try to build them anyway?"
msgstr "Intento construir-los tanmateix?"
#: print.go:34
msgid "URL"
msgstr "URL"
#: clean.go:194 pkg/menus/clean_menu.go:65 pkg/menus/clean_menu.go:71
msgid "Unable to clean:"
msgstr "No es pot netejar:"
#: get.go:42 get.go:74
msgid "Unable to find the following packages:"
msgstr "No s'han pogut trobar els paquets següents:"
#: vote.go:20
msgid "Unable to handle package vote for: %s. err: %s"
msgstr "No es pot gestionar el vot del paquet per a %s. Error: %s"
#: clean.go:170
msgid "Unable to remove %s: %s"
msgstr "No es pot suprimir %s: %s"
#: print.go:32
msgid "Version"
msgstr "Versió"
#: print.go:50
msgid "Votes"
msgstr "Vots"
#: print.go:87
msgid "Yay version v%s"
msgstr "Versió del yay: v%s"
#: pkg/menus/menu.go:49
msgid "[N]one"
msgstr "Ca[p]"
#: pkg/settings/errors.go:29
msgid "aborting due to user"
msgstr "s'avorta a causa de l'usuari"
#: pkg/settings/parser/parser.go:608
msgid "argument '-' specified without input on stdin"
msgstr "argument '-' especificat sense entrada a stdin"
#: local_install.go:26
msgid "cannot find PKGBUILD and .SRCINFO in directory"
msgstr "no es pot trobar PKGBUILD i SRCINFO al directori"
#: pkg/sync/build/pkg_archive.go:148
msgid "cannot find package name: %v"
msgstr "no es pot trobar el nom del paquet: %v"
#: pkg/sync/build/errors.go:30
msgid "could not find PKGDEST for: %s"
msgstr "no s'ha pogut trobar PKGDEST per a %s"
#: errors.go:9
msgid "could not find all required packages"
msgstr "no s'han pogut trobar tots els paquets necessaris"
#: pkg/sync/build/errors.go:61
msgid "could not find any package archives listed in %s"
msgstr "no s'ha pogut trobar cap arxiu de paquets llistat a %s"
#: pkg/sync/build/errors.go:50 pkg/upgrade/service.go:286
msgid "dependency"
msgstr "dependència"
#: pkg/vcs/vcs.go:100 pkg/vcs/vcs.go:96
msgid "devel check for package failed: '%s' encountered an error"
msgstr "la comprovació del paquet ha fallat: %s ha trobat un error"
#: pkg/menus/edit_menu.go:110
msgid "editor did not exit successfully, aborting: %s"
msgstr "l'editor no ha sortit correctament, s'avorta: %s"
#: pkg/sync/workdir/aur_source.go:24
msgid "error downloading sources: %s"
msgstr "error en baixar les fonts: %s"
#: pkg/download/errors.go:25
msgid "error fetching %s: %s"
msgstr "error en obtenir %s: %s"
#: pkg/sync/build/errors.go:9
msgid "error installing repo packages"
msgstr "error en instal·lar paquets dels repositoris"
#: pkg/sync/build/installer.go:266 pkg/sync/build/installer.go:270
msgid "error installing:"
msgstr "error d'instal·lació:"
#: pkg/sync/build/installer.go:233 pkg/sync/build/installer.go:237
msgid "error making: %s"
msgstr "error de construcció: %s"
#: pkg/sync/workdir/merge.go:24
msgid "error merging %s: %s"
msgstr "error en combinar %s: %s"
#: pkg/download/unified.go:59
msgid "error reading %s"
msgstr "error en llegir %s"
#: sync.go:36
msgid "error refreshing databases"
msgstr "error en actualitzar les bases de dades"
#: pkg/sync/workdir/clean.go:51 pkg/sync/workdir/merge.go:17
msgid "error resetting %s: %s"
msgstr "error en restablir %s: %s"
#: pkg/sync/build/errors.go:53
msgid "error updating package install reason to %s"
msgstr "error en actualitzar el motiu d'instal·lació del paquet a %s"
#: pkg/sync/build/errors.go:48
msgid "explicit"
msgstr "explícit"
#: pkg/settings/errors.go:23
msgid "failed to create directory '%s': %s"
msgstr "ha fallat crear el directori %s: %s"
#: pkg/settings/config.go:281
msgid "failed to open config file '%s': %s"
msgstr "ha fallat obrir el fitxer de configuració %s: %s"
#: pkg/sync/srcinfo/service.go:114
msgid "failed to parse %s -- skipping: %s"
msgstr "ha fallat analitzar %s, s'omet: %s"
#: pkg/sync/srcinfo/service.go:118
msgid "failed to parse %s: %s"
msgstr "no s'ha pogut analitzar %s: %s"
#: local_install.go:77
msgid "failed to parse .SRCINFO"
msgstr "ha fallat analitzar .SRCINFO"
#: pkg/settings/config.go:291
msgid "failed to read config file '%s': %s"
msgstr "ha fallat llegir el fitxer de configuració %s: %s"
#: pkg/cmd/graph/main.go:46 pkg/runtime/runtime.go:73
msgid "failed to retrieve aur Cache"
msgstr "ha fallat obtenir la cau de l'AUR"
#: pkg/upgrade/sources.go:27
msgid "ignoring package devel upgrade (no AUR info found):"
msgstr ""
"s'ignora l'actualització del paquet de desenvolupament (no se n'ha trobat "
"cap informació a l'AUR):"
#: pkg/text/errors.go:8
msgid "input too long"
msgstr "entrada massa llarga"
#: pkg/db/ialpm/alpm.go:222 pkg/dep/dep_graph.go:761
msgid "invalid number: %s"
msgstr "número no vàlid: %s"
#: pkg/settings/parser/parser.go:174
msgid "invalid option '%s'"
msgstr "opció no vàlida: %s"
#: cmd.go:197
msgid "invalid option: '--deps' and '--explicit' may not be used together"
msgstr "opció no vàlida: \"--deps\" i \"--explicit\" no es poden usar juntes"
#: pkg/download/abs.go:22
msgid "invalid repository"
msgstr "repositori no vàlid"
#: pkg/db/ialpm/alpm.go:227 pkg/dep/dep_graph.go:767
msgid "invalid value: %d is not between %d and %d"
msgstr "valor no vàlid: %d no és entre %d i %d"
#: pkg/text/input.go:48
msgid "no"
msgstr "no"
#: pkg/sync/srcinfo/pgp/keys.go:110
msgid "no keys to import"
msgstr "sense claus per importar"
#: pkg/query/errors.go:20
msgid "no query was executed"
msgstr "no s'ha executat cap consulta"
#: local_install.go:66
msgid "no target directories specified"
msgstr "no s'han especificat directoris de destinació"
#: pkg/sync/build/installer.go:242
msgid "nothing to install for %s"
msgstr "no hi ha res per instal·lar per %s"
#: pkg/settings/parser/parser.go:164
msgid "only one operation may be used at a time"
msgstr "només es pot usar una operació alhora"
#: pkg/cmd/graph/main.go:70
msgid "only one target is allowed"
msgstr "només es permet una destinació"
#: pkg/upgrade/service.go:291
msgid "package"
msgid_plural "packages"
msgstr[0] "paquet"
msgstr[1] "paquets"
#: print.go:187
msgid "package '%s' was not found"
msgstr "no s'ha trobat el paquet %s"
#: pkg/download/errors.go:15
msgid "package not found in AUR"
msgstr "paquet no trobat a l'AUR"
#: pkg/download/abs.go:23
msgid "package not found in repos"
msgstr "paquet no trobat als repositoris"
#: pkg/sync/srcinfo/pgp/keys.go:100
msgid "problem importing keys"
msgstr "problema d'importació de claus"
#: clean.go:105
msgid "removing AUR packages from cache..."
msgstr "se suprimeixen paquets de l'AUR de la memòria cau..."
#: clean.go:178 pkg/sync/workdir/clean.go:41
msgid "removing untracked AUR files from cache..."
msgstr ""
"se suprimeixen els fitxers de l'AUR sense seguiment de la memòria cau..."
#: pkg/sync/build/errors.go:38
msgid "the PKGDEST for %s is listed by makepkg but does not exist: %s"
msgstr "el PKGDEST per a %s està llistat per makepkg però no existeix:%s"
#: pkg/sync/sync.go:45
msgid "there is nothing to do"
msgstr "No hi ha res per fer."
#: pkg/db/ialpm/alpm.go:247
msgid "unable to CreateHandle: %s"
msgstr "no se'n pot crear el maneig: %s"
#: cmd.go:186
msgid "unhandled operation"
msgstr "operació no manejada"
#: cmd.go:450
msgid "unknown-version"
msgstr "versió desconeguda"
#: pkg/text/input.go:47
msgid "yes"
msgstr "sí"
================================================
FILE: po/cs.po
================================================
#
# Translators:
# Fjuro Fjuro, 2022
# a30a45d5047eb877c4dc397ded846f36_1de9be1, 2023
# Matyáš Černý, 2023
# walken, 2024
#
msgid ""
msgstr ""
"Last-Translator: walken, 2024\n"
"Language-Team: Czech (https://app.transifex.com/yay-1/teams/123732/cs/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: cs\n"
"Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n"
"X-Generator: xgotext\n"
#: pkg/menus/menu.go:32
msgid " (Build Files Exist)"
msgstr "(Soubory sestavení existují)"
#: pkg/menus/menu.go:27
msgid " (Installed)"
msgstr "(Nainstalováno)"
#: cmd.go:453
msgid " [Installed]"
msgstr "[Nainstalováno]"
#: cmd.go:410 vote.go:36
msgid " there is nothing to do"
msgstr "není co dělat"
#: pkg/menus/menu.go:49
msgid "%s [A]ll [Ab]ort [I]nstalled [No]tInstalled or (1 2 3, 1-3, ^4)"
msgstr ""
"%s [A]Vše [Ab]Zrušit [I]Nainstalováno [No]Nenainstalováno nebo (1 2 3, 1-3, "
"^4)"
#: pkg/sync/build/installer.go:308
msgid "%s already made -- skipping build"
msgstr "%sjiž existuje -- přeskakuji sestavení"
#: pkg/menus/edit_menu.go:57
msgid "%s is not set"
msgstr "%s není nastaven"
#: pkg/settings/exe/cmd_builder.go:257
msgid "%s is present."
msgstr "%s je přítomný"
#: pkg/dep/dep_graph.go:460 pkg/sync/build/installer.go:305
msgid "%s is up to date -- skipping"
msgstr "%s je aktualizován -- přeskakuji"
#: pkg/upgrade/service.go:292
msgid "%s to upgrade/install."
msgstr " %s k povýšení/instalaci."
#: pkg/upgrade/service.go:286
msgid "%s will also be installed for this operation."
msgstr "%s bude také nainstalován pro tuto operaci. "
#: pkg/sync/srcinfo/pgp/keys.go:124
msgid "%s, required by: %s"
msgstr "%s, vyžadován balíčkem %s"
#: pkg/menus/diff_menu.go:49
msgid "%s: No changes -- skipping"
msgstr "%s: Žádné změny -- přeskakuji"
#: pkg/query/filter.go:22
msgid "%s: can't use target with option --aur -- skipping"
msgstr "%s: nelze použít cíl s možností --aur -- přeskakuji"
#: pkg/query/filter.go:17
msgid "%s: can't use target with option --repo -- skipping"
msgstr "%s: nelze použít cíl s možností --repo -- přeskakuji"
#: pkg/upgrade/sources.go:57
msgid "%s: ignoring package upgrade (%s => %s)"
msgstr "%s: ignoruji aktualizaci balíčku (%s => %s)"
#: pkg/query/aur_warnings.go:46
msgid "%s: local (%s) is newer than AUR (%s)"
msgstr "%s: místní (%s) je novější než AUR (%s) "
#: vote.go:51
msgid ""
"%s: please set AUR_USERNAME and AUR_PASSWORD environment variables for "
"voting"
msgstr ""
"prosím nastavte proměnné prostředí AUR_USERNAME a AUR_PASSWORD pro hodnocení"
#: pkg/download/unified.go:192
msgid "(%d/%d) Downloaded PKGBUILD from ABS: %s"
msgstr "(%d/%d) Stažen PKGBUILD z ABS: %s"
#: pkg/download/aur.go:92 pkg/download/unified.go:188
msgid "(%d/%d) Downloaded PKGBUILD: %s"
msgstr "(%d/%d) Stažen PKGBUILD: %s"
#: pkg/download/aur.go:82
msgid "(%d/%d) Failed to download PKGBUILD: %s"
msgstr "(%d/%d) Nepodařilo se stáhnout PKGBUILD\\: %s "
#: pkg/sync/srcinfo/service.go:109
msgid "(%d/%d) Parsing SRCINFO: %s"
msgstr "(%d/%d) Parsování SRCINFO: %s"
#: pkg/query/types.go:72 pkg/query/types.go:103
msgid "(Installed)"
msgstr "(Nainstalováno)"
#: pkg/query/types.go:70 pkg/query/types.go:101
msgid "(Installed: %s)"
msgstr "(Nainstalováno: %s)"
#: pkg/query/types.go:61
msgid "(Orphaned)"
msgstr "(Osamocené)"
#: pkg/query/types.go:65
msgid "(Out-of-date: %s)"
msgstr "(Zastaralé: %s)"
#: print.go:44
msgid "AUR URL"
msgstr "URL AUR"
#: pkg/dep/dep_graph.go:75
msgid "AUR"
msgstr "AUR"
#: pkg/menus/edit_menu.go:58
msgid "Add %s or %s to your environment variables"
msgstr "Přidejte %s nebo %s do svých proměnných prostředí "
#: main.go:60
msgid "Avoid running yay as root/sudo."
msgstr "Vyhněte se spuštění yay jako root/sudo."
#: pkg/dep/dep_graph.go:63
msgid "Check Dependency"
msgstr "Zkontrolovat závislost"
#: print.go:41
msgid "Check Deps"
msgstr "Zkontrolovat závislosti"
#: pkg/upgrade/service.go:90
msgid "Checking development packages..."
msgstr "Kontrola vývojových balíčků..."
#: pkg/sync/workdir/clean.go:45
msgid "Cleaning (%d/%d): %s"
msgstr "Čištění (%d/%d): %s"
#: print.go:42
msgid "Conflicts With"
msgstr "Konflikt s"
#: pkg/menus/clean_menu.go:62
msgid "Deleting (%d/%d): %s"
msgstr "Mazání (%d/%d): %s"
#: pkg/dep/dep_graph.go:61
msgid "Dependency"
msgstr "Závislost"
#: print.go:38
msgid "Depends On"
msgstr "Závisí na"
#: print.go:33
msgid "Description"
msgstr "Popis"
#: pkg/menus/diff_menu.go:160
msgid "Diffs to show?"
msgstr "Zobrazit rozdíly?"
#: pkg/settings/migrations.go:25
msgid "Disable 'provides' setting by default"
msgstr "Zákaz nastavení 'provides' jako výchozí"
#: clean.go:78
msgid "Do you want to remove ALL AUR packages from cache?"
msgstr "Chcete odstranit VŠECHNY balíčky AUR z mezipaměti?"
#: clean.go:95
msgid "Do you want to remove ALL untracked AUR files?"
msgstr "Chcete odstranit VŠECHNY nesledované soubory AUR?"
#: clean.go:80
msgid "Do you want to remove all other AUR packages from cache?"
msgstr "Chcete odstranit všechny ostatní balíčky AUR z mezipaměti?"
#: pkg/menus/edit_menu.go:61
msgid "Edit PKGBUILD with?"
msgstr "Upravit PKGBUILD pomocí?"
#: pkg/query/errors.go:13
msgid "Error during AUR search: %s\n"
msgstr "Chyby při hledání v AUR: %s\n"
#: pkg/upgrade/service.go:296
msgid "Excluding packages may cause partial upgrades and break systems"
msgstr ""
"Vyjímání balíčků může způsobovat částečné aktualizace a rozbíjet systémy."
#: pkg/dep/dep_graph.go:60
msgid "Explicit"
msgstr "Explicitní"
#: print.go:91
msgid "Explicitly installed packages: %s"
msgstr "Explicitně nainstalované balíčky: %s"
#: pkg/dep/dep_graph.go:437 pkg/dep/dep_graph.go:535
msgid "Failed to find AUR package for"
msgstr "Chyba hledání balíčku AUR:"
#: pkg/sync/build/installer.go:120
msgid "Failed to install layer, rolling up to next layer."
msgstr "Chyba instalace vrstvy, zabaluji k další vrstvě."
#: pkg/sync/build/errors.go:16
msgid ""
"Failed to install the following packages. Manual intervention is required:"
msgstr "Selhala instalace těchto balíčků. Ruční zásah je vyžadován."
#: print.go:45
msgid "First Submitted"
msgstr "První odeslané"
#: pkg/query/aur_warnings.go:79
msgid "Flagged Out Of Date AUR Packages:"
msgstr "Označené zastaralé balíčky AUR:"
#: print.go:90
msgid "Foreign installed packages: %s"
msgstr "Neznámé nainstalované balíčky: %s"
#: pkg/vcs/vcs.go:144
msgid "Found git repo: %s"
msgstr "Nalezen repozitář git: %s"
#: vcs.go:72
msgid "GenDB finished. No packages were installed"
msgstr "GenDB dokončena. Nebyly nainstalovány žádné balíčky"
#: print.go:36
msgid "Groups"
msgstr "Skupiny"
#: pkg/sync/srcinfo/pgp/keys.go:88
msgid "Import?"
msgstr "Importovat?"
#: pkg/sync/srcinfo/pgp/keys.go:97
msgid "Importing keys with gpg..."
msgstr "Importování klíčů pomocí gpg..."
#: print.go:46
msgid "Keywords"
msgstr "Klíčová slova"
#: print.go:47
msgid "Last Modified"
msgstr "Naposledy upraveno"
#: print.go:35
msgid "Licenses"
msgstr "Licence"
#: pkg/dep/dep_graph.go:77
msgid "Local"
msgstr "Místní"
#: print.go:48
msgid "Maintainer"
msgstr "Správce"
#: pkg/dep/dep_graph.go:62
msgid "Make Dependency"
msgstr "Závislosti pro sestavení"
#: print.go:40
msgid "Make Deps"
msgstr "Vytvořit závislosti"
#: pkg/query/aur_warnings.go:71
msgid "Missing AUR Debug Packages:"
msgstr "Chybějící ladicí balíčky AUR:"
#: pkg/dep/dep_graph.go:79
msgid "Missing"
msgstr "Chybějící"
#: print.go:31
msgid "Name"
msgstr "Název"
#: pkg/dep/dep_graph.go:442 pkg/dep/dep_graph.go:548
msgid "No AUR package found for"
msgstr "AUR balíček nenalezen pro"
#: pkg/dep/dep_graph.go:182
msgid "No package found for"
msgstr "Nebyly nalezeny žádné balíčky pro"
#: print.go:225
msgid "None"
msgstr "Žádné"
#: print.go:39
msgid "Optional Deps"
msgstr "Volitelné závislosti"
#: pkg/query/aur_warnings.go:75
msgid "Orphan (unmaintained) AUR Packages:"
msgstr "Osamocené (neudržované) balíčky AUR:"
#: print.go:53 print.go:55
msgid "Out-of-date"
msgstr "Zastaralé"
#: pkg/sync/srcinfo/pgp/keys.go:115
msgid "PGP keys need importing:"
msgstr "Je třeba importovat klíče PGP:"
#: pkg/sync/workdir/preparer.go:252
msgid "PKGBUILD up to date, skipping download: %s"
msgstr "PKGBUILD je aktuální, přeskakuji: %s"
#: pkg/menus/edit_menu.go:130
msgid "PKGBUILDs to edit?"
msgstr "PKGBUILDy k upravení?"
#: print.go:60
msgid "Package Base ID"
msgstr "Základní ID aplikace"
#: print.go:61
msgid "Package Base"
msgstr "Základ aplikace"
#: pkg/query/aur_warnings.go:67
msgid "Packages not in AUR:"
msgstr "Balíčky mimo AUR:"
#: pkg/menus/clean_menu.go:54
msgid "Packages to cleanBuild?"
msgstr "Balíčky ke cleanBuild?"
#: pkg/dep/dep_graph.go:202
msgid "Packages to exclude"
msgstr "Balíčky k vyjmutí."
#: pkg/upgrade/service.go:295
msgid "Packages to exclude: (eg: \"1 2 3\", \"1-3\", \"^4\" or repo name)"
msgstr "Balíčky k vyloučení: (např.: \"1 2 3\", \"1-3\", \"^4\" nebo název repozitáře)"
#: cmd.go:392
msgid "Packages to install (eg: 1 2 3, 1-3 or ^4)"
msgstr "Balíčky k instalaci (např.: 1 2 3, 1-3 nebo ^4)"
#: print.go:49
msgid "Popularity"
msgstr "Popularita"
#: pkg/menus/diff_menu.go:172 pkg/menus/edit_menu.go:143
msgid "Proceed with install?"
msgstr "Pokračovat v instalaci?"
#: print.go:37
msgid "Provides"
msgstr "Poskytuje"
#: pkg/sync/workdir/preparer.go:125
msgid "Remove make dependencies after install?"
msgstr "Odstranit vytvořené závislosti po instalaci?"
#: print.go:43
msgid "Replaces"
msgstr "Nahrazuje"
#: pkg/dep/dep_graph.go:730
msgid "Repository AUR"
msgstr "Repozitář AUR"
#: print.go:30 pkg/db/ialpm/alpm.go:191
msgid "Repository"
msgstr "Repozitář"
#: pkg/dep/dep_graph.go:78
msgid "SRCINFO"
msgstr "SRCINFO"
#: pkg/upgrade/service.go:72
msgid "Searching AUR for updates..."
msgstr "Prohledávání AUR pro aktualizace..."
#: pkg/upgrade/service.go:160
msgid "Searching databases for updates..."
msgstr "Prohledávání databází k aktualizaci..."
#: pkg/query/query_builder.go:214
msgid "Showing repo packages only"
msgstr "Zobrazování pouze balíčků repozitáře"
#: print.go:95
msgid "Size of pacman cache %s: %s"
msgstr "Velikost mezipaměti pacman %s: %s"
#: print.go:98
msgid "Size of yay cache %s: %s"
msgstr "Velikost mezipaměti yay %s: %s"
#: print.go:62
msgid "Snapshot URL"
msgstr "URL snapshotu"
#: pkg/dep/dep_graph.go:76
msgid "Sync"
msgstr "Synchronizace"
#: print.go:100
msgid "Ten biggest packages:"
msgstr "Deset největších balíčků"
#: pkg/sync/sync.go:124
msgid "The following packages are not compatible with your architecture:"
msgstr "Následující balíčky nejsou kompatibilní s vaší architekturou:"
#: pkg/db/ialpm/alpm.go:179 pkg/dep/dep_graph.go:726
msgid "There are %[1]d providers available for %[2]s:"
msgstr "Pro %[2]s je dostupných %[1]d poskytovatelů:"
#: pkg/settings/exe/cmd_builder.go:258
msgid "There may be another Pacman instance running. Waiting..."
msgstr "Možná běží další instance Pacman. Čekání..."
#: print.go:92
msgid "Total Size occupied by packages: %s"
msgstr "Celková velikost zabraná balíčky: %s"
#: print.go:89
msgid "Total installed packages: %s"
msgstr "Celkem nainstalovaných balíčků: %s"
#: pkg/sync/sync.go:132
msgid "Try to build them anyway?"
msgstr "Pokusit se je přesto sestavit?"
#: print.go:34
msgid "URL"
msgstr "URL"
#: clean.go:194 pkg/menus/clean_menu.go:65 pkg/menus/clean_menu.go:71
msgid "Unable to clean:"
msgstr "Nepodařilo se vyčistit:"
#: get.go:42 get.go:74
msgid "Unable to find the following packages:"
msgstr "Nepodařilo se nalézt následující balíčky:"
#: vote.go:20
msgid "Unable to handle package vote for: %s. err: %s"
msgstr "Nelze hlasovat pro balíček: %s. chyba: %s"
#: clean.go:170
msgid "Unable to remove %s: %s"
msgstr "Nelze odebrat %s:%s"
#: print.go:32
msgid "Version"
msgstr "Verze"
#: print.go:50
msgid "Votes"
msgstr "Hlasy"
#: print.go:87
msgid "Yay version v%s"
msgstr "Verze yay v%s"
#: pkg/menus/menu.go:49
msgid "[N]one"
msgstr "[N]Žádné"
#: clean.go:83
msgid ""
"\n"
"Build directory:"
msgstr ""
"\n"
"Adresář sestavení:"
#: pkg/db/ialpm/alpm.go:201 pkg/dep/dep_graph.go:740
msgid ""
"\n"
"Enter a number (default=1): "
msgstr ""
"\n"
"Zadejte číslo (výchozí=1):"
#: pkg/settings/errors.go:29
msgid "aborting due to user"
msgstr "rušení kvůli uživateli"
#: pkg/settings/parser/parser.go:608
msgid "argument '-' specified without input on stdin"
msgstr "argument '-' použit bez vstupu na stdin"
#: local_install.go:26
msgid "cannot find PKGBUILD and .SRCINFO in directory"
msgstr "PKGBUILD a .SRCINFO nebyly nalezeny ve složce"
#: pkg/sync/build/pkg_archive.go:148
msgid "cannot find package name: %v"
msgstr "nepodařilo se nalézt název balíčku: %v"
#: pkg/sync/build/errors.go:30
msgid "could not find PKGDEST for: %s"
msgstr "nepodařilo se nalézt PKGDEST pro: %s"
#: errors.go:9
msgid "could not find all required packages"
msgstr "Nepodařilo se nalézt všechny požadované balíčky"
#: pkg/sync/build/errors.go:61
msgid "could not find any package archives listed in %s"
msgstr "nenalezeny žádné archivy balíčků v %s"
#: pkg/sync/build/errors.go:50 pkg/upgrade/service.go:287
msgid "dependency"
msgstr "závislost"
#: pkg/vcs/vcs.go:96 pkg/vcs/vcs.go:100
msgid "devel check for package failed: '%s' encountered an error"
msgstr "kontrola devel balíčku selhala: '%s' narazil na chybu"
#: pkg/menus/edit_menu.go:110
msgid "editor did not exit successfully, aborting: %s"
msgstr "editor nebyl úspěšně ukončen, rušení: %s"
#: pkg/sync/workdir/aur_source.go:24
msgid "error downloading sources: %s"
msgstr "chyba při stahování zdrojů: %s"
#: pkg/download/errors.go:25
msgid "error fetching %s: %s"
msgstr "chyba při načítání %s: %s"
#: pkg/sync/build/errors.go:9
msgid "error installing repo packages"
msgstr "chyba při instalaci balíčků repozitáře"
#: pkg/sync/build/installer.go:266 pkg/sync/build/installer.go:270
msgid "error installing:"
msgstr "chyba při instalaci:"
#: pkg/sync/build/installer.go:233 pkg/sync/build/installer.go:237
msgid "error making: %s"
msgstr "chyba při vytváření: %s"
#: pkg/sync/workdir/merge.go:24
msgid "error merging %s: %s"
msgstr "chyba při slučování %s: %s"
#: pkg/download/unified.go:59
msgid "error reading %s"
msgstr "chyba při čtení %s"
#: sync.go:36
msgid "error refreshing databases"
msgstr "chyba při obnovování databází"
#: pkg/sync/workdir/clean.go:51 pkg/sync/workdir/merge.go:17
msgid "error resetting %s: %s"
msgstr "chyba při resetování %s: %s"
#: pkg/sync/build/errors.go:53
msgid "error updating package install reason to %s"
msgstr "chyba při aktualizaci důvodu instalace balíčku na"
#: pkg/sync/build/errors.go:48
msgid "explicit"
msgstr "explicitní"
#: pkg/settings/errors.go:23
msgid "failed to create directory '%s': %s"
msgstr "nepodařilo se vytvořit adresář '%s': %s"
#: pkg/settings/config.go:281
msgid "failed to open config file '%s': %s"
msgstr "nepodařilo se otevřít konfigurační soubor '%s': %s"
#: pkg/sync/srcinfo/service.go:114
msgid "failed to parse %s -- skipping: %s"
msgstr "nepodařilo se parsovat %s -- přeskakuji: %s"
#: pkg/sync/srcinfo/service.go:118
msgid "failed to parse %s: %s"
msgstr "nepodařilo se parsovat %s: %s"
#: local_install.go:77
msgid "failed to parse .SRCINFO"
msgstr "nepodařilo se parsovat .SRCINFO"
#: pkg/settings/config.go:291
msgid "failed to read config file '%s': %s"
msgstr "nepodařilo se přečíst konfigurační soubor '%s': %s"
#: pkg/cmd/graph/main.go:46 pkg/runtime/runtime.go:73
msgid "failed to retrieve aur Cache"
msgstr "nepodařilo se získat aur Cache"
#: pkg/upgrade/sources.go:27
msgid "ignoring package devel upgrade (no AUR info found):"
msgstr "ignoruji vývojovou aktualizaci balíčku (AUR informace nenalezeny)"
#: pkg/text/errors.go:8
msgid "input too long"
msgstr "vstup příliš dlouhý"
#: pkg/db/ialpm/alpm.go:222 pkg/dep/dep_graph.go:761
msgid "invalid number: %s"
msgstr "neplatné číslo: %s"
#: pkg/settings/parser/parser.go:174
msgid "invalid option '%s'"
msgstr "neplatná možnost '%s'"
#: cmd.go:197
msgid "invalid option: '--deps' and '--explicit' may not be used together"
msgstr ""
"neplatná možnost: '--deps' a '--explicit' nemohou být použity současně"
#: pkg/download/abs.go:22
msgid "invalid repository"
msgstr "neplatný repozitář"
#: pkg/db/ialpm/alpm.go:227 pkg/dep/dep_graph.go:767
msgid "invalid value: %d is not between %d and %d"
msgstr "neplatná hodnota: %d není mezi %d a %d"
#: pkg/sync/srcinfo/pgp/keys.go:110
msgid "no keys to import"
msgstr "žádné klíče k importování"
#: pkg/query/errors.go:20
msgid "no query was executed"
msgstr "nebyl vykonán žádný dotaz"
#: local_install.go:66
msgid "no target directories specified"
msgstr "nebyly specifikovány cílové adresáře"
#: pkg/text/input.go:48
msgid "no"
msgstr "ne"
#: pkg/sync/build/installer.go:242
msgid "nothing to install for %s"
msgstr "nic k nainstalování pro %s"
#: pkg/settings/parser/parser.go:164
msgid "only one operation may be used at a time"
msgstr "naráz může být použita pouze jedna operace"
#: pkg/cmd/graph/main.go:70
msgid "only one target is allowed"
msgstr "je dovolený pouze jeden cíl"
#: print.go:187
msgid "package '%s' was not found"
msgstr "balíček '%s' nebyl nalezen"
#: pkg/download/errors.go:15
msgid "package not found in AUR"
msgstr "balíček nenalezen v AUR"
#: pkg/download/abs.go:23
msgid "package not found in repos"
msgstr "balíček nenalezen v repozitářích"
#: pkg/upgrade/service.go:292
msgid "package"
msgid_plural "packages"
msgstr[0] "balíček"
msgstr[1] "balíčky"
msgstr[2] "balíčky"
msgstr[3] "balíčky"
#: pkg/sync/srcinfo/pgp/keys.go:100
msgid "problem importing keys"
msgstr "problém při importování klíčů"
#: clean.go:105
msgid "removing AUR packages from cache..."
msgstr "odstraňování balíčků AUR z mezipaměti..."
#: clean.go:178 pkg/sync/workdir/clean.go:41
msgid "removing untracked AUR files from cache..."
msgstr "odstraňování nesledovaných souborů AUR z mezipaměti..."
#: pkg/sync/build/errors.go:38
msgid "the PKGDEST for %s is listed by makepkg but does not exist: %s"
msgstr "PKGDEST pro %s je v seznamu makepkg ale neexistuje: %s"
#: pkg/sync/sync.go:45
msgid "there is nothing to do"
msgstr "není co dělat"
#: pkg/db/ialpm/alpm.go:247
msgid "unable to CreateHandle: %s"
msgstr "nepodařilo se CreateHandle: %s"
#: cmd.go:186
msgid "unhandled operation"
msgstr "neznámá operace"
#: cmd.go:450
msgid "unknown-version"
msgstr "neznámá-verze"
#: pkg/text/input.go:47
msgid "yes"
msgstr "ano"
================================================
FILE: po/de.po
================================================
#
# Translators:
# Julian Neupert , 2021
# Chris Boesch, 2021
# Oliver Conzen, 2021
# J G, 2022
# Andre Bouillet, 2022
# cf9fef41dd781c154ed35b0d2df25c28_1d7ee01, 2022
# Lukas Müller (Lukas), 2022
# Arne Brücher, 2023
# Ananas 77, 2023
# 452f4d2d2d15be3f170375e4fa84f9da_7bd63b3, 2023
# Lukas Esc, 2023
# Manuel Schneider, 2023
# Marethyu _, 2023
# Severin Hamader , 2025
# 0x3C50 No, 2026
#
msgid ""
msgstr ""
"Last-Translator: 0x3C50 No, 2026\n"
"Language-Team: German (https://app.transifex.com/yay-1/teams/123732/de/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: de\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: xgotext\n"
#: clean.go:83
msgid ""
"\n"
"Build directory:"
msgstr ""
"\n"
"Build-Verzeichnis:"
#: pkg/db/ialpm/alpm.go:201 pkg/dep/dep_graph.go:740
msgid ""
"\n"
"Enter a number (default=1): "
msgstr ""
"\n"
"Geben Sie eine Zahl ein (Vorgabe=1):"
#: pkg/menus/menu.go:32
msgid " (Build Files Exist)"
msgstr " (Build-Dateien sind vorhanden)"
#: pkg/menus/menu.go:27
msgid " (Installed)"
msgstr " (Installiert)"
#: cmd.go:453
msgid " [Installed]"
msgstr "[Installiert]"
#: cmd.go:410 vote.go:36
msgid " there is nothing to do"
msgstr " es gibt nichts zu tun"
#: pkg/menus/menu.go:49
msgid "%s [A]ll [Ab]ort [I]nstalled [No]tInstalled or (1 2 3, 1-3, ^4)"
msgstr ""
"%s [A]lle [Ab]brechen [I]nstalliert [No]nicht installiert oder (1 2 3, 1-3, "
"^4)"
#: pkg/sync/build/installer.go:308
msgid "%s already made -- skipping build"
msgstr "%s Bereits erledigt -- Build wird übersprungen"
#: pkg/menus/edit_menu.go:57
msgid "%s is not set"
msgstr "%s ist nicht definiert"
#: pkg/settings/exe/cmd_builder.go:257
msgid "%s is present."
msgstr "%s ist vorhanden."
#: pkg/dep/dep_graph.go:460 pkg/sync/build/installer.go:305
msgid "%s is up to date -- skipping"
msgstr "%s ist bereits aktuell -- wird übersprungen"
#: pkg/upgrade/service.go:291
msgid "%s to upgrade/install."
msgstr "%s zu upgraden/installieren."
#: pkg/upgrade/service.go:285
msgid "%s will also be installed for this operation."
msgstr "%s wird für diesen Vorgang ebenfalls installiert."
#: pkg/sync/srcinfo/pgp/keys.go:124
msgid "%s, required by: %s"
msgstr "%s, benötigt von: %s"
#: pkg/menus/diff_menu.go:49
msgid "%s: No changes -- skipping"
msgstr "%s: Keine Änderungen -- wird übersprungen"
#: pkg/query/filter.go:22
msgid "%s: can't use target with option --aur -- skipping"
msgstr "%s: Argument --aur nicht möglich -- wird übersprungen"
#: pkg/query/filter.go:17
msgid "%s: can't use target with option --repo -- skipping"
msgstr "%s: Argument --repo nicht möglich -- wird übersprungen"
#: pkg/upgrade/sources.go:57
msgid "%s: ignoring package upgrade (%s => %s)"
msgstr "%s: Ignoriere Paketaktualisierung (%s => %s)"
#: pkg/query/aur_warnings.go:46
msgid "%s: local (%s) is newer than AUR (%s)"
msgstr "%s: lokales (%s) ist neuer als das AUR (%s)"
#: vote.go:51
msgid ""
"%s: please set AUR_USERNAME and AUR_PASSWORD environment variables for "
"voting"
msgstr ""
"Bitte AUR_USERNAME und AUR_PASSWORD Umgebungsvariablen setzen um abzustimmen"
#: pkg/download/unified.go:192
msgid "(%d/%d) Downloaded PKGBUILD from ABS: %s"
msgstr "(%d/%d) PKGBUILD von ABS heruntergeladen: %s"
#: pkg/download/aur.go:92 pkg/download/unified.go:188
msgid "(%d/%d) Downloaded PKGBUILD: %s"
msgstr "(%d/%d) PKGBUILD heruntergeladen: %s"
#: pkg/download/aur.go:82
msgid "(%d/%d) Failed to download PKGBUILD: %s"
msgstr "(%d/%d) Es wurde nicht geschaft PKGBUILD zu aktualisieren: %s"
#: pkg/sync/srcinfo/service.go:109
msgid "(%d/%d) Parsing SRCINFO: %s"
msgstr "(%d/%d) SRCINFO geparst: %s"
#: pkg/query/types.go:103 pkg/query/types.go:72
msgid "(Installed)"
msgstr "(Installiert)"
#: pkg/query/types.go:101 pkg/query/types.go:70
msgid "(Installed: %s)"
msgstr "(Installiert: %s)"
#: pkg/query/types.go:61
msgid "(Orphaned)"
msgstr "(Verwaist)"
#: pkg/query/types.go:65
msgid "(Out-of-date: %s)"
msgstr "(Veraltet: %s)"
#: pkg/dep/dep_graph.go:75
msgid "AUR"
msgstr "AUR"
#: print.go:44
msgid "AUR URL"
msgstr "AUR-URL"
#: pkg/menus/edit_menu.go:58
msgid "Add %s or %s to your environment variables"
msgstr "%s oder %s zu den Umgebungsvariablen hinzufügen"
#: main.go:60
msgid "Avoid running yay as root/sudo."
msgstr "Vermeide es yay als root/sudo zu nutzen!"
#: pkg/dep/dep_graph.go:63
msgid "Check Dependency"
msgstr "Überprüfe Abhängigkeiten"
#: print.go:41
msgid "Check Deps"
msgstr "Prüfe Abhängigkeiten"
#: pkg/upgrade/service.go:89
msgid "Checking development packages..."
msgstr "Prüfe Entwicklungspakete..."
#: pkg/sync/workdir/clean.go:45
msgid "Cleaning (%d/%d): %s"
msgstr "Säubere (%d/%d): %s"
#: print.go:42
msgid "Conflicts With"
msgstr "Ist in Konflikt mit"
#: pkg/menus/clean_menu.go:62
msgid "Deleting (%d/%d): %s"
msgstr "Lösche (%d/%d): %s"
#: pkg/dep/dep_graph.go:61
msgid "Dependency"
msgstr "Abhängigkeit"
#: print.go:38
msgid "Depends On"
msgstr "Ist abhängig von"
#: print.go:33
msgid "Description"
msgstr "Beschreibung"
#: pkg/menus/diff_menu.go:160
msgid "Diffs to show?"
msgstr "Unterschiede zeigen?"
#: pkg/settings/migrations.go:25
msgid "Disable 'provides' setting by default"
msgstr "'provides' Einstellung automatisch abschalten"
#: clean.go:78
msgid "Do you want to remove ALL AUR packages from cache?"
msgstr "Wollen Sie ALLE AUR-Pakete aus dem Cache entfernen?"
#: clean.go:95
msgid "Do you want to remove ALL untracked AUR files?"
msgstr "Wollen Sie ALLE nicht überwachten AUR-Dateien entfernen?"
#: clean.go:80
msgid "Do you want to remove all other AUR packages from cache?"
msgstr "Wollen Sie alle anderen AUR-Pakete aus dem Cache entfernen?"
#: pkg/menus/edit_menu.go:61
msgid "Edit PKGBUILD with?"
msgstr "PKGBUILD editieren mit?"
#: pkg/query/errors.go:13
msgid "Error during AUR search: %s\n"
msgstr "Fehler während AUR-Suche: %s\n"
#: pkg/upgrade/service.go:295
msgid "Excluding packages may cause partial upgrades and break systems"
msgstr ""
"Das Ausschließen von Paketen kann zu teilweisen Aktualisierungen führen und "
"Systeme beschädigen."
#: pkg/dep/dep_graph.go:60
msgid "Explicit"
msgstr "Explizit"
#: print.go:91
msgid "Explicitly installed packages: %s"
msgstr "Explizit installierte Pakete: %s"
#: pkg/dep/dep_graph.go:437 pkg/dep/dep_graph.go:535
msgid "Failed to find AUR package for"
msgstr "AUR-Paket nicht gefunden für"
#: pkg/sync/build/installer.go:120
msgid "Failed to install layer, rolling up to next layer."
msgstr ""
"Die Installationsschicht ist fehlgeschlagen, es wird zur nächsten Schicht "
"übergegangen."
#: pkg/sync/build/errors.go:16
msgid ""
"Failed to install the following packages. Manual intervention is required:"
msgstr ""
"Die folgenden Pakete konnten nicht installiert werden. Ein manueller "
"Eingriff ist erforderlich:"
#: print.go:45
msgid "First Submitted"
msgstr "Erstmals eingereicht"
#: pkg/query/aur_warnings.go:83
msgid "Flagged Out Of Date AUR Packages:"
msgstr "Als nicht aktuell markierte AUR-Pakete:"
#: print.go:90
msgid "Foreign installed packages: %s"
msgstr "Installierte Fremdpakete: %s"
#: pkg/vcs/vcs.go:144
msgid "Found git repo: %s"
msgstr "Git-Repo gefunden: %s"
#: vcs.go:72
msgid "GenDB finished. No packages were installed"
msgstr "GenDB beendet. Es wurden keine Pakete installiert"
#: print.go:36
msgid "Groups"
msgstr "Gruppen"
#: pkg/sync/srcinfo/pgp/keys.go:88
msgid "Import?"
msgstr "Importieren?"
#: pkg/sync/srcinfo/pgp/keys.go:97
msgid "Importing keys with gpg..."
msgstr "Importiere Schlüssel mit gpg..."
#: print.go:46
msgid "Keywords"
msgstr "Stichworte"
#: print.go:47
msgid "Last Modified"
msgstr "Zuletzt geändert"
#: print.go:35
msgid "Licenses"
msgstr "Lizenzen"
#: pkg/dep/dep_graph.go:77
msgid "Local"
msgstr "Lokal"
#: print.go:48
msgid "Maintainer"
msgstr "Betreuer"
#: pkg/dep/dep_graph.go:62
msgid "Make Dependency"
msgstr "Erstelle Abhängigkeit."
#: print.go:40
msgid "Make Deps"
msgstr "Abhängigkeiten herstellen"
#: pkg/dep/dep_graph.go:79
msgid "Missing"
msgstr "Fehlend"
#: pkg/query/aur_warnings.go:75
msgid "Missing AUR Debug Packages:"
msgstr "Fehlende AUR Debug-Pakete: "
#: print.go:31
msgid "Name"
msgstr "Name"
#: pkg/dep/dep_graph.go:442 pkg/dep/dep_graph.go:564
msgid "No AUR package found for"
msgstr "Kein AUR-Paket gefunden für"
#: pkg/dep/dep_graph.go:564
msgid "required by"
msgstr "benötigt von"
#: pkg/dep/dep_graph.go:182
msgid "No package found for"
msgstr "Kein gefunden für"
#: print.go:225
msgid "None"
msgstr "Keine"
#: print.go:39
msgid "Optional Deps"
msgstr "Optionale Abhängigkeiten"
#: pkg/query/aur_warnings.go:79
msgid "Orphan (unmaintained) AUR Packages:"
msgstr "Verwaiste (nicht gepflegte) AUR-Pakete:"
#: print.go:53 print.go:55
msgid "Out-of-date"
msgstr "Veraltet"
#: pkg/sync/srcinfo/pgp/keys.go:115
msgid "PGP keys need importing:"
msgstr "PGP-Schlüssel müssen importiert werden: "
#: pkg/sync/workdir/preparer.go:252
msgid "PKGBUILD up to date, skipping download: %s"
msgstr "PKGBUILD ist auf dem neuesten Stand, überspringe Herunterladen: %s"
#: pkg/menus/edit_menu.go:130
msgid "PKGBUILDs to edit?"
msgstr "Welche PKGBUILDs editieren?"
#: print.go:61
msgid "Package Base"
msgstr "Paketbasis"
#: print.go:60
msgid "Package Base ID"
msgstr "Paket-Basis-ID"
#: pkg/query/aur_warnings.go:71
msgid "Packages not in AUR:"
msgstr "Pakete nicht im AUR:"
#: pkg/menus/clean_menu.go:54
msgid "Packages to cleanBuild?"
msgstr "Pakete neu erstellen?"
#: pkg/dep/dep_graph.go:202
msgid "Packages to exclude"
msgstr "Auszuschließende Pakete"
#: pkg/upgrade/service.go:294
msgid "Packages to exclude: (eg: \"1 2 3\", \"1-3\", \"^4\" or repo name)"
msgstr "Pakete zum Ausschließen: (z.B. \"1 2 3\", \"1-3\", \"^4\" oder Repo-Name)"
#: cmd.go:392
msgid "Packages to install (eg: 1 2 3, 1-3 or ^4)"
msgstr "Zu installierende Pakete: (z.B. 1 2 3, 1-3 oder ^4)"
#: print.go:49
msgid "Popularity"
msgstr "Beliebtheit"
#: pkg/menus/diff_menu.go:172 pkg/menus/edit_menu.go:143
msgid "Proceed with install?"
msgstr "Mit der Installation fortfahren?"
#: print.go:37
msgid "Provides"
msgstr "Bietet"
#: pkg/sync/workdir/preparer.go:125
msgid "Remove make dependencies after install?"
msgstr "Abhängigkeiten nach der Installation entfernen?"
#: print.go:43
msgid "Replaces"
msgstr "Ersetzt"
#: pkg/db/ialpm/alpm.go:191 print.go:30
msgid "Repository"
msgstr "Repository"
#: pkg/dep/dep_graph.go:730
msgid "Repository AUR"
msgstr "Repository AUR"
#: pkg/dep/dep_graph.go:78
msgid "SRCINFO"
msgstr "SRCINFO"
#: pkg/upgrade/service.go:71
msgid "Searching AUR for updates..."
msgstr "Durchsuche AUR nach Updates..."
#: pkg/upgrade/service.go:159
msgid "Searching databases for updates..."
msgstr "Durchsuche Datenbanken nach Updates..."
#: pkg/query/query_builder.go:214
msgid "Showing repo packages only"
msgstr "Zeige nur Repo-Pakete"
#: print.go:95
msgid "Size of pacman cache %s: %s"
msgstr "Größe des pacman-Cache %s: %s"
#: print.go:98
msgid "Size of yay cache %s: %s"
msgstr "Größe des yay-Cache %s: %s"
#: print.go:62
msgid "Snapshot URL"
msgstr "Snapshot-URL"
#: pkg/dep/dep_graph.go:76
msgid "Sync"
msgstr "Synchronisieren"
#: print.go:100
msgid "Ten biggest packages:"
msgstr "Die zehn größten Pakete:"
#: pkg/sync/sync.go:124
msgid "The following packages are not compatible with your architecture:"
msgstr "Die folgenden Pakete sind mit Ihrer Architektur inkompatibel:"
#: pkg/db/ialpm/alpm.go:179 pkg/dep/dep_graph.go:726
msgid "There are %[1]d providers available for %[2]s:"
msgstr "Es sind %[1]d Anbieter verfügbar für %[2]s:"
#: pkg/settings/exe/cmd_builder.go:258
msgid "There may be another Pacman instance running. Waiting..."
msgstr "Eine andere Instanz von Pacman ist aktiv. Warte..."
#: print.go:92
msgid "Total Size occupied by packages: %s"
msgstr "Gesamtgröße der installierten Pakete: %s"
#: print.go:89
msgid "Total installed packages: %s"
msgstr "Insgesamt installierte Pakete: %s"
#: pkg/sync/sync.go:132
msgid "Try to build them anyway?"
msgstr "Build trotzdem versuchen?"
#: print.go:34
msgid "URL"
msgstr "URL"
#: clean.go:194 pkg/menus/clean_menu.go:65 pkg/menus/clean_menu.go:71
msgid "Unable to clean:"
msgstr "Kann nicht entfernen:"
#: get.go:42 get.go:74
msgid "Unable to find the following packages:"
msgstr "Konnte die folgenden Pakete nicht finden: "
#: vote.go:20
msgid "Unable to handle package vote for: %s. err: %s"
msgstr "Paketabstimmung nicht möglich für:%s. Fehler: %s"
#: clean.go:170
msgid "Unable to remove %s: %s"
msgstr "Kann nicht entfernt werden %s: %s"
#: print.go:32
msgid "Version"
msgstr "Version"
#: print.go:50
msgid "Votes"
msgstr "Stimmen"
#: print.go:87
msgid "Yay version v%s"
msgstr "Yay-Version v%s"
#: pkg/menus/menu.go:49
msgid "[N]one"
msgstr "[N] Keine"
#: pkg/settings/errors.go:29
msgid "aborting due to user"
msgstr "Abbruch durch den Benutzer"
#: pkg/settings/parser/parser.go:608
msgid "argument '-' specified without input on stdin"
msgstr "Argument '-' ohne Eingabe in stdin angegeben"
#: local_install.go:26
msgid "cannot find PKGBUILD and .SRCINFO in directory"
msgstr "kann PKGBUILD und .SRCINFO im Verzeichnis nicht finden"
#: pkg/sync/build/pkg_archive.go:148
msgid "cannot find package name: %v"
msgstr "Kann Paketname nicht finden: %v"
#: pkg/sync/build/errors.go:30
msgid "could not find PKGDEST for: %s"
msgstr "Kann PKGDEST nicht finden für: %s"
#: errors.go:9
msgid "could not find all required packages"
msgstr "konnte nicht alle erforderlichen Pakete finden"
#: pkg/sync/build/errors.go:61
msgid "could not find any package archives listed in %s"
msgstr "konnte keine Paketarchive finden ist nicht aufgeführt in %s"
#: pkg/sync/build/errors.go:50 pkg/upgrade/service.go:286
msgid "dependency"
msgstr "Abhängigkeit"
#: pkg/vcs/vcs.go:100 pkg/vcs/vcs.go:96
msgid "devel check for package failed: '%s' encountered an error"
msgstr "Entwicklungstest fehlgeschlagen für Paket: '%s' Fehler gefunden."
#: pkg/menus/edit_menu.go:110
msgid "editor did not exit successfully, aborting: %s"
msgstr "Editor wurde nicht erfogreich beendet, breche ab: %s"
#: pkg/sync/workdir/aur_source.go:24
msgid "error downloading sources: %s"
msgstr "Fehler beim Herunterladen der Quellen: %s"
#: pkg/download/errors.go:25
msgid "error fetching %s: %s"
msgstr "Fehler beim Abrufen von %s: %s"
#: pkg/sync/build/errors.go:9
msgid "error installing repo packages"
msgstr "Fehler bei der Installation der Repo-Pakete"
#: pkg/sync/build/installer.go:266 pkg/sync/build/installer.go:270
msgid "error installing:"
msgstr "Fehler bei der Installation: "
#: pkg/sync/build/installer.go:233 pkg/sync/build/installer.go:237
msgid "error making: %s"
msgstr "Fehler beim Erstellen: %s"
#: pkg/sync/workdir/merge.go:24
msgid "error merging %s: %s"
msgstr "Fehler beim Zusammenführen von %s: %s"
#: pkg/download/unified.go:59
msgid "error reading %s"
msgstr "Fehler beim Lesen von %s"
#: sync.go:36
msgid "error refreshing databases"
msgstr "Fehler beim Aktualisieren der Datenbanken"
#: pkg/sync/workdir/clean.go:51 pkg/sync/workdir/merge.go:17
msgid "error resetting %s: %s"
msgstr "Fehler beim Zurücksetzen von %s: %s"
#: pkg/sync/build/errors.go:53
msgid "error updating package install reason to %s"
msgstr "Fehler bei der Aktualisierung der Paket-Installation aufgrund von %s"
#: pkg/sync/build/errors.go:48
msgid "explicit"
msgstr "explizit"
#: pkg/settings/errors.go:23
msgid "failed to create directory '%s': %s"
msgstr "Fehler beim Erstellen von Verzeichnis '%s': %s"
#: pkg/settings/config.go:281
msgid "failed to open config file '%s': %s"
msgstr "Fehler beim Öffnen der Konfigurationsdatei '%s': %s"
#: pkg/sync/srcinfo/service.go:114
msgid "failed to parse %s -- skipping: %s"
msgstr "Fehler beim Parsen von %s -- überspringe: %s"
#: pkg/sync/srcinfo/service.go:118
msgid "failed to parse %s: %s"
msgstr "Fehler beim Parsen von: %s: %s"
#: local_install.go:77
msgid "failed to parse .SRCINFO"
msgstr "Fehler von Parsen von .SRCINFO"
#: pkg/settings/config.go:291
msgid "failed to read config file '%s': %s"
msgstr "Fehler beim Lesen der Konfigurationsdatei '%s': %s"
#: pkg/cmd/graph/main.go:46 pkg/runtime/runtime.go:73
msgid "failed to retrieve aur Cache"
msgstr "Erhalten des AUR Caches ist fehlgeschlagen"
#: pkg/upgrade/sources.go:27
msgid "ignoring package devel upgrade (no AUR info found):"
msgstr "ignoriere devel Paket Aktualisierung (keine AUR Info gefunden):"
#: pkg/text/errors.go:8
msgid "input too long"
msgstr "Eingabe zu lang"
#: pkg/db/ialpm/alpm.go:222 pkg/dep/dep_graph.go:761
msgid "invalid number: %s"
msgstr "Ungültige Nummer: %s"
#: pkg/settings/parser/parser.go:174
msgid "invalid option '%s'"
msgstr "Ungültige Option '%s'"
#: cmd.go:197
msgid "invalid option: '--deps' and '--explicit' may not be used together"
msgstr ""
"Ungültige Option: '--deps' und '--explicit' können nicht zusammen genutzt "
"werden"
#: pkg/download/abs.go:22
msgid "invalid repository"
msgstr "Ungültiges Repository"
#: pkg/db/ialpm/alpm.go:227 pkg/dep/dep_graph.go:767
msgid "invalid value: %d is not between %d and %d"
msgstr "Ungültiger Wert: %d ist nicht zwischen %d und %d"
#: pkg/text/input.go:48
msgid "no"
msgstr "nein"
#: pkg/sync/srcinfo/pgp/keys.go:110
msgid "no keys to import"
msgstr "Keine Schlüssel zu importieren"
#: pkg/query/errors.go:20
msgid "no query was executed"
msgstr "Es wurde keine Abfrage ausgeführt"
#: local_install.go:66
msgid "no target directories specified"
msgstr "Kein Ziel Verzeichnis angegeben"
#: pkg/sync/build/installer.go:242
msgid "nothing to install for %s"
msgstr "nichts zu installieren für %s"
#: pkg/settings/parser/parser.go:164
msgid "only one operation may be used at a time"
msgstr "Nur eine Operation kann gleichzeitig benutzt werden"
#: pkg/cmd/graph/main.go:70
msgid "only one target is allowed"
msgstr "Nur ein Ziel ist erlaubt"
#: pkg/upgrade/service.go:291
msgid "package"
msgid_plural "packages"
msgstr[0] "Paket"
msgstr[1] "Pakete"
#: print.go:187
msgid "package '%s' was not found"
msgstr "Paket '%s' konnte nicht gefunden werden"
#: pkg/download/errors.go:15
msgid "package not found in AUR"
msgstr "Paket nicht im AUR gefunden"
#: pkg/download/abs.go:23
msgid "package not found in repos"
msgstr "Paket nicht in den Repos gefunden"
#: pkg/sync/srcinfo/pgp/keys.go:100
msgid "problem importing keys"
msgstr "Problem beim Importieren der Schlüssel"
#: clean.go:105
msgid "removing AUR packages from cache..."
msgstr "Entferne AUR-Pakete aus dem Cache..."
#: clean.go:178 pkg/sync/workdir/clean.go:41
msgid "removing untracked AUR files from cache..."
msgstr "Entferne nicht überwachte AUR-Dateien aus dem Cache..."
#: pkg/sync/build/errors.go:38
msgid "the PKGDEST for %s is listed by makepkg but does not exist: %s"
msgstr "PKGDEST für %s ist in MAKEPKG aufgeführt, existiert aber nicht: %s"
#: pkg/sync/sync.go:45
msgid "there is nothing to do"
msgstr "es gibt nichts zu tun"
#: pkg/db/ialpm/alpm.go:247
msgid "unable to CreateHandle: %s"
msgstr "Kann CreateHandle nicht erstellen: %s"
#: cmd.go:186
msgid "unhandled operation"
msgstr "Unbehandelte Operation"
#: cmd.go:450
msgid "unknown-version"
msgstr "Unbekannte Version"
#: pkg/text/input.go:47
msgid "yes"
msgstr "ja"
================================================
FILE: po/en.po
================================================
msgid ""
msgstr ""
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: \n"
"X-Generator: xgotext\n"
#: clean.go:83
msgid ""
"\n"
"Build directory:"
msgstr ""
#: pkg/db/ialpm/alpm.go:201
#: pkg/dep/dep_graph.go:740
msgid ""
"\n"
"Enter a number (default=1): "
msgstr ""
#: pkg/menus/menu.go:32
msgid " (Build Files Exist)"
msgstr ""
#: pkg/menus/menu.go:27
msgid " (Installed)"
msgstr ""
#: cmd.go:453
msgid " [Installed]"
msgstr ""
#: cmd.go:410
#: vote.go:36
msgid " there is nothing to do"
msgstr ""
#: pkg/menus/menu.go:49
msgid "%s [A]ll [Ab]ort [I]nstalled [No]tInstalled or (1 2 3, 1-3, ^4)"
msgstr ""
#: pkg/sync/build/installer.go:308
msgid "%s already made -- skipping build"
msgstr ""
#: pkg/menus/edit_menu.go:57
msgid "%s is not set"
msgstr ""
#: pkg/settings/exe/cmd_builder.go:257
msgid "%s is present."
msgstr ""
#: pkg/dep/dep_graph.go:460
#: pkg/sync/build/installer.go:305
msgid "%s is up to date -- skipping"
msgstr ""
#: pkg/upgrade/service.go:291
msgid "%s to upgrade/install."
msgstr ""
#: pkg/upgrade/service.go:285
msgid "%s will also be installed for this operation."
msgstr ""
#: pkg/sync/srcinfo/pgp/keys.go:124
msgid "%s, required by: %s"
msgstr ""
#: pkg/menus/diff_menu.go:49
msgid "%s: No changes -- skipping"
msgstr ""
#: pkg/query/filter.go:22
msgid "%s: can't use target with option --aur -- skipping"
msgstr ""
#: pkg/query/filter.go:17
msgid "%s: can't use target with option --repo -- skipping"
msgstr ""
#: pkg/upgrade/sources.go:57
msgid "%s: ignoring package upgrade (%s => %s)"
msgstr ""
#: pkg/query/aur_warnings.go:46
msgid "%s: local (%s) is newer than AUR (%s)"
msgstr ""
#: vote.go:51
msgid "%s: please set AUR_USERNAME and AUR_PASSWORD environment variables for voting"
msgstr ""
#: pkg/download/unified.go:192
msgid "(%d/%d) Downloaded PKGBUILD from ABS: %s"
msgstr ""
#: pkg/download/aur.go:92
#: pkg/download/unified.go:188
msgid "(%d/%d) Downloaded PKGBUILD: %s"
msgstr ""
#: pkg/download/aur.go:82
msgid "(%d/%d) Failed to download PKGBUILD: %s"
msgstr ""
#: pkg/sync/srcinfo/service.go:109
msgid "(%d/%d) Parsing SRCINFO: %s"
msgstr ""
#: pkg/query/types.go:103
#: pkg/query/types.go:72
msgid "(Installed)"
msgstr ""
#: pkg/query/types.go:101
#: pkg/query/types.go:70
msgid "(Installed: %s)"
msgstr ""
#: pkg/query/types.go:61
msgid "(Orphaned)"
msgstr ""
#: pkg/query/types.go:65
msgid "(Out-of-date: %s)"
msgstr ""
#: pkg/dep/dep_graph.go:75
msgid "AUR"
msgstr ""
#: print.go:44
msgid "AUR URL"
msgstr ""
#: pkg/menus/edit_menu.go:58
msgid "Add %s or %s to your environment variables"
msgstr ""
#: main.go:60
msgid "Avoid running yay as root/sudo."
msgstr ""
#: pkg/dep/dep_graph.go:63
msgid "Check Dependency"
msgstr ""
#: print.go:41
msgid "Check Deps"
msgstr ""
#: pkg/upgrade/service.go:89
msgid "Checking development packages..."
msgstr ""
#: pkg/sync/workdir/clean.go:45
msgid "Cleaning (%d/%d): %s"
msgstr ""
#: print.go:42
msgid "Conflicts With"
msgstr ""
#: pkg/menus/clean_menu.go:62
msgid "Deleting (%d/%d): %s"
msgstr ""
#: pkg/dep/dep_graph.go:61
msgid "Dependency"
msgstr ""
#: print.go:38
msgid "Depends On"
msgstr ""
#: print.go:33
msgid "Description"
msgstr ""
#: pkg/menus/diff_menu.go:160
msgid "Diffs to show?"
msgstr ""
#: pkg/settings/migrations.go:25
msgid "Disable 'provides' setting by default"
msgstr ""
#: clean.go:78
msgid "Do you want to remove ALL AUR packages from cache?"
msgstr ""
#: clean.go:95
msgid "Do you want to remove ALL untracked AUR files?"
msgstr ""
#: clean.go:80
msgid "Do you want to remove all other AUR packages from cache?"
msgstr ""
#: pkg/menus/edit_menu.go:61
msgid "Edit PKGBUILD with?"
msgstr ""
#: pkg/query/errors.go:13
msgid "Error during AUR search: %s\n"
msgstr ""
#: pkg/upgrade/service.go:295
msgid "Excluding packages may cause partial upgrades and break systems"
msgstr ""
#: pkg/dep/dep_graph.go:60
msgid "Explicit"
msgstr ""
#: print.go:91
msgid "Explicitly installed packages: %s"
msgstr ""
#: pkg/dep/dep_graph.go:437
#: pkg/dep/dep_graph.go:535
msgid "Failed to find AUR package for"
msgstr ""
#: pkg/sync/build/installer.go:120
msgid "Failed to install layer, rolling up to next layer."
msgstr ""
#: pkg/sync/build/errors.go:16
msgid "Failed to install the following packages. Manual intervention is required:"
msgstr ""
#: print.go:45
msgid "First Submitted"
msgstr ""
#: pkg/query/aur_warnings.go:83
msgid "Flagged Out Of Date AUR Packages:"
msgstr ""
#: print.go:90
msgid "Foreign installed packages: %s"
msgstr ""
#: pkg/vcs/vcs.go:144
msgid "Found git repo: %s"
msgstr ""
#: vcs.go:72
msgid "GenDB finished. No packages were installed"
msgstr ""
#: print.go:36
msgid "Groups"
msgstr ""
#: pkg/sync/srcinfo/pgp/keys.go:88
msgid "Import?"
msgstr ""
#: pkg/sync/srcinfo/pgp/keys.go:97
msgid "Importing keys with gpg..."
msgstr ""
#: print.go:46
msgid "Keywords"
msgstr ""
#: print.go:47
msgid "Last Modified"
msgstr ""
#: print.go:35
msgid "Licenses"
msgstr ""
#: pkg/dep/dep_graph.go:77
msgid "Local"
msgstr ""
#: print.go:48
msgid "Maintainer"
msgstr ""
#: pkg/dep/dep_graph.go:62
msgid "Make Dependency"
msgstr ""
#: print.go:40
msgid "Make Deps"
msgstr ""
#: pkg/dep/dep_graph.go:79
msgid "Missing"
msgstr ""
#: pkg/query/aur_warnings.go:75
msgid "Missing AUR Debug Packages:"
msgstr ""
#: print.go:31
msgid "Name"
msgstr ""
#: pkg/dep/dep_graph.go:442
#: pkg/dep/dep_graph.go:564
msgid "No AUR package found for"
msgstr ""
#: pkg/dep/dep_graph.go:564
msgid "required by"
msgstr ""
#: pkg/dep/dep_graph.go:182
msgid "No package found for"
msgstr ""
#: print.go:225
msgid "None"
msgstr ""
#: print.go:39
msgid "Optional Deps"
msgstr ""
#: pkg/query/aur_warnings.go:79
msgid "Orphan (unmaintained) AUR Packages:"
msgstr ""
#: print.go:53
#: print.go:55
msgid "Out-of-date"
msgstr ""
#: pkg/sync/srcinfo/pgp/keys.go:115
msgid "PGP keys need importing:"
msgstr ""
#: pkg/sync/workdir/preparer.go:252
msgid "PKGBUILD up to date, skipping download: %s"
msgstr ""
#: pkg/menus/edit_menu.go:130
msgid "PKGBUILDs to edit?"
msgstr ""
#: print.go:61
msgid "Package Base"
msgstr ""
#: print.go:60
msgid "Package Base ID"
msgstr ""
#: pkg/query/aur_warnings.go:71
msgid "Packages not in AUR:"
msgstr ""
#: pkg/menus/clean_menu.go:54
msgid "Packages to cleanBuild?"
msgstr ""
#: pkg/dep/dep_graph.go:202
msgid "Packages to exclude"
msgstr ""
#: pkg/upgrade/service.go:294
msgid "Packages to exclude: (eg: \"1 2 3\", \"1-3\", \"^4\" or repo name)"
msgstr ""
#: cmd.go:392
msgid "Packages to install (eg: 1 2 3, 1-3 or ^4)"
msgstr ""
#: print.go:49
msgid "Popularity"
msgstr ""
#: pkg/menus/diff_menu.go:172
#: pkg/menus/edit_menu.go:143
msgid "Proceed with install?"
msgstr ""
#: print.go:37
msgid "Provides"
msgstr ""
#: pkg/sync/workdir/preparer.go:125
msgid "Remove make dependencies after install?"
msgstr ""
#: print.go:43
msgid "Replaces"
msgstr ""
#: pkg/db/ialpm/alpm.go:191
#: print.go:30
msgid "Repository"
msgstr ""
#: pkg/dep/dep_graph.go:730
msgid "Repository AUR"
msgstr ""
#: pkg/dep/dep_graph.go:78
msgid "SRCINFO"
msgstr ""
#: pkg/upgrade/service.go:71
msgid "Searching AUR for updates..."
msgstr ""
#: pkg/upgrade/service.go:159
msgid "Searching databases for updates..."
msgstr ""
#: pkg/query/query_builder.go:214
msgid "Showing repo packages only"
msgstr ""
#: print.go:95
msgid "Size of pacman cache %s: %s"
msgstr ""
#: print.go:98
msgid "Size of yay cache %s: %s"
msgstr ""
#: print.go:62
msgid "Snapshot URL"
msgstr ""
#: pkg/dep/dep_graph.go:76
msgid "Sync"
msgstr ""
#: print.go:100
msgid "Ten biggest packages:"
msgstr ""
#: pkg/sync/sync.go:124
msgid "The following packages are not compatible with your architecture:"
msgstr ""
#: pkg/db/ialpm/alpm.go:179
#: pkg/dep/dep_graph.go:726
msgid "There are %[1]d providers available for %[2]s:"
msgstr ""
#: pkg/settings/exe/cmd_builder.go:258
msgid "There may be another Pacman instance running. Waiting..."
msgstr ""
#: print.go:92
msgid "Total Size occupied by packages: %s"
msgstr ""
#: print.go:89
msgid "Total installed packages: %s"
msgstr ""
#: pkg/sync/sync.go:132
msgid "Try to build them anyway?"
msgstr ""
#: print.go:34
msgid "URL"
msgstr ""
#: clean.go:194
#: pkg/menus/clean_menu.go:65
#: pkg/menus/clean_menu.go:71
msgid "Unable to clean:"
msgstr ""
#: get.go:42
#: get.go:74
msgid "Unable to find the following packages:"
msgstr ""
#: vote.go:20
msgid "Unable to handle package vote for: %s. err: %s"
msgstr ""
#: clean.go:170
msgid "Unable to remove %s: %s"
msgstr ""
#: print.go:32
msgid "Version"
msgstr ""
#: print.go:50
msgid "Votes"
msgstr ""
#: print.go:87
msgid "Yay version v%s"
msgstr ""
#: pkg/menus/menu.go:49
msgid "[N]one"
msgstr ""
#: pkg/settings/errors.go:29
msgid "aborting due to user"
msgstr ""
#: pkg/settings/parser/parser.go:608
msgid "argument '-' specified without input on stdin"
msgstr ""
#: local_install.go:26
msgid "cannot find PKGBUILD and .SRCINFO in directory"
msgstr ""
#: pkg/sync/build/pkg_archive.go:148
msgid "cannot find package name: %v"
msgstr ""
#: pkg/sync/build/errors.go:30
msgid "could not find PKGDEST for: %s"
msgstr ""
#: errors.go:9
msgid "could not find all required packages"
msgstr ""
#: pkg/sync/build/errors.go:61
msgid "could not find any package archives listed in %s"
msgstr ""
#: pkg/sync/build/errors.go:50
#: pkg/upgrade/service.go:286
msgid "dependency"
msgstr ""
#: pkg/vcs/vcs.go:100
#: pkg/vcs/vcs.go:96
msgid "devel check for package failed: '%s' encountered an error"
msgstr ""
#: pkg/menus/edit_menu.go:110
msgid "editor did not exit successfully, aborting: %s"
msgstr ""
#: pkg/sync/workdir/aur_source.go:24
msgid "error downloading sources: %s"
msgstr ""
#: pkg/download/errors.go:25
msgid "error fetching %s: %s"
msgstr ""
#: pkg/sync/build/errors.go:9
msgid "error installing repo packages"
msgstr ""
#: pkg/sync/build/installer.go:266
#: pkg/sync/build/installer.go:270
msgid "error installing:"
msgstr ""
#: pkg/sync/build/installer.go:233
#: pkg/sync/build/installer.go:237
msgid "error making: %s"
msgstr ""
#: pkg/sync/workdir/merge.go:24
msgid "error merging %s: %s"
msgstr ""
#: pkg/download/unified.go:59
msgid "error reading %s"
msgstr ""
#: sync.go:36
msgid "error refreshing databases"
msgstr ""
#: pkg/sync/workdir/clean.go:51
#: pkg/sync/workdir/merge.go:17
msgid "error resetting %s: %s"
msgstr ""
#: pkg/sync/build/errors.go:53
msgid "error updating package install reason to %s"
msgstr ""
#: pkg/sync/build/errors.go:48
msgid "explicit"
msgstr ""
#: pkg/settings/errors.go:23
msgid "failed to create directory '%s': %s"
msgstr ""
#: pkg/settings/config.go:281
msgid "failed to open config file '%s': %s"
msgstr ""
#: pkg/sync/srcinfo/service.go:114
msgid "failed to parse %s -- skipping: %s"
msgstr ""
#: pkg/sync/srcinfo/service.go:118
msgid "failed to parse %s: %s"
msgstr ""
#: local_install.go:77
msgid "failed to parse .SRCINFO"
msgstr ""
#: pkg/settings/config.go:291
msgid "failed to read config file '%s': %s"
msgstr ""
#: pkg/cmd/graph/main.go:46
#: pkg/runtime/runtime.go:73
msgid "failed to retrieve aur Cache"
msgstr ""
#: pkg/upgrade/sources.go:27
msgid "ignoring package devel upgrade (no AUR info found):"
msgstr ""
#: pkg/text/errors.go:8
msgid "input too long"
msgstr ""
#: pkg/db/ialpm/alpm.go:222
#: pkg/dep/dep_graph.go:761
msgid "invalid number: %s"
msgstr ""
#: pkg/settings/parser/parser.go:174
msgid "invalid option '%s'"
msgstr ""
#: cmd.go:197
msgid "invalid option: '--deps' and '--explicit' may not be used together"
msgstr ""
#: pkg/download/abs.go:22
msgid "invalid repository"
msgstr ""
#: pkg/db/ialpm/alpm.go:227
#: pkg/dep/dep_graph.go:767
msgid "invalid value: %d is not between %d and %d"
msgstr ""
#: pkg/text/input.go:48
msgid "no"
msgstr ""
#: pkg/sync/srcinfo/pgp/keys.go:110
msgid "no keys to import"
msgstr ""
#: pkg/query/errors.go:20
msgid "no query was executed"
msgstr ""
#: local_install.go:66
msgid "no target directories specified"
msgstr ""
#: pkg/sync/build/installer.go:242
msgid "nothing to install for %s"
msgstr ""
#: pkg/settings/parser/parser.go:164
msgid "only one operation may be used at a time"
msgstr ""
#: pkg/cmd/graph/main.go:70
msgid "only one target is allowed"
msgstr ""
#: pkg/upgrade/service.go:291
msgid "package"
msgid_plural "packages"
msgstr[0] ""
msgstr[1] ""
#: print.go:187
msgid "package '%s' was not found"
msgstr ""
#: pkg/download/errors.go:15
msgid "package not found in AUR"
msgstr ""
#: pkg/download/abs.go:23
msgid "package not found in repos"
msgstr ""
#: pkg/sync/srcinfo/pgp/keys.go:100
msgid "problem importing keys"
msgstr ""
#: clean.go:105
msgid "removing AUR packages from cache..."
msgstr ""
#: clean.go:178
#: pkg/sync/workdir/clean.go:41
msgid "removing untracked AUR files from cache..."
msgstr ""
#: pkg/sync/build/errors.go:38
msgid "the PKGDEST for %s is listed by makepkg but does not exist: %s"
msgstr ""
#: pkg/sync/sync.go:45
msgid "there is nothing to do"
msgstr ""
#: pkg/db/ialpm/alpm.go:247
msgid "unable to CreateHandle: %s"
msgstr ""
#: cmd.go:186
msgid "unhandled operation"
msgstr ""
#: cmd.go:450
msgid "unknown-version"
msgstr ""
#: pkg/text/input.go:47
msgid "yes"
msgstr ""
================================================
FILE: po/es.po
================================================
#
# Translators:
# J G, 2021
# Ivan Garcia, 2021
# Percy De La Rosa, 2022
# Brighton Saldaña, 2022
# Antonio Alvarado-Hernández, 2023
# brandon galvis, 2023
# Angel López, 2023
# C C, 2023
# Claudio Yanez, 2025
# Pablo Lezaeta Reyes , 2025
#
msgid ""
msgstr ""
"Last-Translator: Pablo Lezaeta Reyes , 2025\n"
"Language-Team: Spanish (https://app.transifex.com/yay-1/teams/123732/es/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: es\n"
"Plural-Forms: nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n"
"X-Generator: xgotext\n"
#: clean.go:83
msgid ""
"\n"
"Build directory:"
msgstr ""
"\n"
"Carpeta de compilación:"
#: pkg/db/ialpm/alpm.go:201 pkg/dep/dep_graph.go:740
msgid ""
"\n"
"Enter a number (default=1): "
msgstr ""
"\n"
"Introduzca un número (por omisión=1): "
#: pkg/menus/menu.go:32
msgid " (Build Files Exist)"
msgstr " (Archivos de compilación existen)"
#: pkg/menus/menu.go:27
msgid " (Installed)"
msgstr " (Instalado)"
#: cmd.go:453
msgid " [Installed]"
msgstr " [Instalado]"
#: cmd.go:410 vote.go:36
msgid " there is nothing to do"
msgstr " no hay nada que hacer"
#: pkg/menus/menu.go:49
msgid "%s [A]ll [Ab]ort [I]nstalled [No]tInstalled or (1 2 3, 1-3, ^4)"
msgstr "%s [A]Todos [Ab]ortar [I]nstalados [No]Instalados o (1 2 3, 1-3, ^4)"
#: pkg/sync/build/installer.go:308
msgid "%s already made -- skipping build"
msgstr "%s ya creado -- omitiendo compilación"
#: pkg/menus/edit_menu.go:57
msgid "%s is not set"
msgstr "%s no está definido"
#: pkg/settings/exe/cmd_builder.go:257
msgid "%s is present."
msgstr "%s está presente."
#: pkg/dep/dep_graph.go:460 pkg/sync/build/installer.go:305
msgid "%s is up to date -- skipping"
msgstr "%s está actualizado -- ignorando"
#: pkg/upgrade/service.go:291
msgid "%s to upgrade/install."
msgstr "%s por actualizar/instalar."
#: pkg/upgrade/service.go:285
msgid "%s will also be installed for this operation."
msgstr "%s también será instalado para esta operación."
#: pkg/sync/srcinfo/pgp/keys.go:124
msgid "%s, required by: %s"
msgstr "%s, necesario para: %s"
#: pkg/menus/diff_menu.go:49
msgid "%s: No changes -- skipping"
msgstr "%s: Sin cambios -- ignorando"
#: pkg/query/filter.go:22
msgid "%s: can't use target with option --aur -- skipping"
msgstr ""
"%s: no es posible utilizar el objetivo con la opción --aur -- ignorando"
#: pkg/query/filter.go:17
msgid "%s: can't use target with option --repo -- skipping"
msgstr ""
"%s: no es posible utilizar el objetivo con la opción --repo -- ignorando"
#: pkg/upgrade/sources.go:57
msgid "%s: ignoring package upgrade (%s => %s)"
msgstr "%s: actualización del paquete ignorada (%s => %s)"
#: pkg/query/aur_warnings.go:46
msgid "%s: local (%s) is newer than AUR (%s)"
msgstr ""
"%s: paquete local (%s) es más nuevo que el paquete disponible en el AUR (%s)"
#: vote.go:51
msgid ""
"%s: please set AUR_USERNAME and AUR_PASSWORD environment variables for "
"voting"
msgstr ""
"%s: por favor, configure las variables de entorno AUR_USERNAME y "
"AUR_PASSWORD para votar"
#: pkg/download/unified.go:192
msgid "(%d/%d) Downloaded PKGBUILD from ABS: %s"
msgstr "(%d/%d) PKGBUILD descargado de ABS: %s"
#: pkg/download/aur.go:92 pkg/download/unified.go:188
msgid "(%d/%d) Downloaded PKGBUILD: %s"
msgstr "(%d/%d) PKGBUILD Descargado : %s"
#: pkg/download/aur.go:82
msgid "(%d/%d) Failed to download PKGBUILD: %s"
msgstr "(%d/%d) No se pudo descargar el PKGBUILD: %s"
#: pkg/sync/srcinfo/service.go:109
msgid "(%d/%d) Parsing SRCINFO: %s"
msgstr "(%d/%d) Analizando SRCINFO: %s"
#: pkg/query/types.go:103 pkg/query/types.go:72
msgid "(Installed)"
msgstr "(Instalado)"
#: pkg/query/types.go:101 pkg/query/types.go:70
msgid "(Installed: %s)"
msgstr "(Instalado: %s)"
#: pkg/query/types.go:61
msgid "(Orphaned)"
msgstr "(Huérfano)"
#: pkg/query/types.go:65
msgid "(Out-of-date: %s)"
msgstr "(Desactualizado: %s)"
#: pkg/dep/dep_graph.go:75
msgid "AUR"
msgstr "AUR"
#: print.go:44
msgid "AUR URL"
msgstr "URL del AUR"
#: pkg/menus/edit_menu.go:58
msgid "Add %s or %s to your environment variables"
msgstr "Añade %s o %s a tus variables de entorno"
#: main.go:60
msgid "Avoid running yay as root/sudo."
msgstr "Evite ejecutar yay como superusuario. root/sudo."
#: pkg/dep/dep_graph.go:63
msgid "Check Dependency"
msgstr "Verificar dependencia"
#: print.go:41
msgid "Check Deps"
msgstr "Dependencias de verificación"
#: pkg/upgrade/service.go:89
msgid "Checking development packages..."
msgstr "Verificando paquetes de desarrollo..."
#: pkg/sync/workdir/clean.go:45
msgid "Cleaning (%d/%d): %s"
msgstr "Limpiando (%d/%d): %s"
#: print.go:42
msgid "Conflicts With"
msgstr "Conflictos con"
#: pkg/menus/clean_menu.go:62
msgid "Deleting (%d/%d): %s"
msgstr "Borrando (%d/%d): %s"
#: pkg/dep/dep_graph.go:61
msgid "Dependency"
msgstr "Dependencia"
#: print.go:38
msgid "Depends On"
msgstr "Depende de"
#: print.go:33
msgid "Description"
msgstr "Descripción"
#: pkg/menus/diff_menu.go:160
msgid "Diffs to show?"
msgstr "¿Diffs a mostrar?"
#: pkg/settings/migrations.go:25
msgid "Disable 'provides' setting by default"
msgstr "Deshabilitar ajuste «provee» por omisión "
#: clean.go:78
msgid "Do you want to remove ALL AUR packages from cache?"
msgstr "¿Quieres borrar TODOS los paquetes del AUR de la caché?"
#: clean.go:95
msgid "Do you want to remove ALL untracked AUR files?"
msgstr "¿Quieres borrar TODOS los archivos del AUR sin rastrear?"
#: clean.go:80
msgid "Do you want to remove all other AUR packages from cache?"
msgstr "¿Quieres borrar los otros paquetes del AUR de la caché?"
#: pkg/menus/edit_menu.go:61
msgid "Edit PKGBUILD with?"
msgstr "¿Editar PKGBUILD con?"
#: pkg/query/errors.go:13
msgid "Error during AUR search: %s\n"
msgstr "Error al buscar en el AUR: %s\n"
#: pkg/upgrade/service.go:295
msgid "Excluding packages may cause partial upgrades and break systems"
msgstr ""
"Excluir paquetes puede causar actualizaciones parciales y rupturas de "
"sistemas"
#: pkg/dep/dep_graph.go:60
msgid "Explicit"
msgstr "Explícito"
#: print.go:91
msgid "Explicitly installed packages: %s"
msgstr "Paquetes explícitamente instalados: %s"
#: pkg/dep/dep_graph.go:437 pkg/dep/dep_graph.go:535
msgid "Failed to find AUR package for"
msgstr "No se pudo encontrar el paquete en el AUR para"
#: pkg/sync/build/installer.go:120
msgid "Failed to install layer, rolling up to next layer."
msgstr "No se pudo instalar la capa, rodando hacia la siguiente capa."
#: pkg/sync/build/errors.go:16
msgid ""
"Failed to install the following packages. Manual intervention is required:"
msgstr ""
"No se pudieron instalar los siguientes paquetes. Se requiere intervención "
"manual:"
#: print.go:45
msgid "First Submitted"
msgstr "Primera vez subido"
#: pkg/query/aur_warnings.go:83
msgid "Flagged Out Of Date AUR Packages:"
msgstr "Paquetes del AUR marcados como desactualizados:"
#: print.go:90
msgid "Foreign installed packages: %s"
msgstr "Paquetes exteriores instalados: %s"
#: pkg/vcs/vcs.go:144
msgid "Found git repo: %s"
msgstr "Repositorio git encontrado: %s"
#: vcs.go:72
msgid "GenDB finished. No packages were installed"
msgstr "GenDB finalizó. Ningún paquete fue instalado"
#: print.go:36
msgid "Groups"
msgstr "Grupos"
#: pkg/sync/srcinfo/pgp/keys.go:88
msgid "Import?"
msgstr "¿Importar?"
#: pkg/sync/srcinfo/pgp/keys.go:97
msgid "Importing keys with gpg..."
msgstr "Importando llaves con gpg..."
#: print.go:46
msgid "Keywords"
msgstr "Palabras clave"
#: print.go:47
msgid "Last Modified"
msgstr "Última vez modificado"
#: print.go:35
msgid "Licenses"
msgstr "Licencias"
#: pkg/dep/dep_graph.go:77
msgid "Local"
msgstr "Local"
#: print.go:48
msgid "Maintainer"
msgstr "Responsable"
#: pkg/dep/dep_graph.go:62
msgid "Make Dependency"
msgstr "Crear dependencia"
#: print.go:40
msgid "Make Deps"
msgstr "Dependencias de compilación"
#: pkg/dep/dep_graph.go:79
msgid "Missing"
msgstr "Ausente"
#: pkg/query/aur_warnings.go:75
msgid "Missing AUR Debug Packages:"
msgstr "Paquetes de depuración del AUR faltantes:"
#: print.go:31
msgid "Name"
msgstr "Nombre"
#: pkg/dep/dep_graph.go:442 pkg/dep/dep_graph.go:548
msgid "No AUR package found for"
msgstr "Ningún paquete del AUR para"
#: pkg/dep/dep_graph.go:182
msgid "No package found for"
msgstr "No se encontró un paquete para"
#: print.go:225
msgid "None"
msgstr "Ninguno"
#: print.go:39
msgid "Optional Deps"
msgstr "Dependencias opcionales"
#: pkg/query/aur_warnings.go:79
msgid "Orphan (unmaintained) AUR Packages:"
msgstr "Paquetes del AUR huérfanos (no mantenidos): "
#: print.go:53 print.go:55
msgid "Out-of-date"
msgstr "Desactualizado"
#: pkg/sync/srcinfo/pgp/keys.go:115
msgid "PGP keys need importing:"
msgstr "Llaves PGP a importar:"
#: pkg/sync/workdir/preparer.go:252
msgid "PKGBUILD up to date, skipping download: %s"
msgstr "PKGBUILD actualizado, omitiendo descarga: %s"
#: pkg/menus/edit_menu.go:130
msgid "PKGBUILDs to edit?"
msgstr "¿PKGBUILDs a editar?"
#: print.go:61
msgid "Package Base"
msgstr "Paquete base"
#: print.go:60
msgid "Package Base ID"
msgstr "ID de paquete base"
#: pkg/query/aur_warnings.go:71
msgid "Packages not in AUR:"
msgstr "Paquetes que no están en el AUR:"
#: pkg/menus/clean_menu.go:54
msgid "Packages to cleanBuild?"
msgstr "¿Paquetes a limpiar antes de compilar?"
#: pkg/dep/dep_graph.go:202
msgid "Packages to exclude"
msgstr "Paquetes a excluir"
#: pkg/upgrade/service.go:294
msgid "Packages to exclude: (eg: \"1 2 3\", \"1-3\", \"^4\" or repo name)"
msgstr ""
"Paquetes a excluir: (ej.: «1 2 3», «1-3», «^4» o nombre del repositorio)"
#: cmd.go:392
msgid "Packages to install (eg: 1 2 3, 1-3 or ^4)"
msgstr "Paquetes a instalar (ej..: 1 2 3, 1-3 or ^4)"
#: print.go:49
msgid "Popularity"
msgstr "Popularidad"
#: pkg/menus/diff_menu.go:172 pkg/menus/edit_menu.go:143
msgid "Proceed with install?"
msgstr "¿Proceder con la instalación?"
#: print.go:37
msgid "Provides"
msgstr "Provee"
#: pkg/sync/workdir/preparer.go:125
msgid "Remove make dependencies after install?"
msgstr "¿Borrar dependencias de instalación después de instalar?"
#: print.go:43
msgid "Replaces"
msgstr "Remplaza"
#: pkg/db/ialpm/alpm.go:191 print.go:30
msgid "Repository"
msgstr "Repositorio"
#: pkg/dep/dep_graph.go:730
msgid "Repository AUR"
msgstr "Repositorio AUR"
#: pkg/dep/dep_graph.go:78
msgid "SRCINFO"
msgstr "SRCINFO"
#: pkg/upgrade/service.go:71
msgid "Searching AUR for updates..."
msgstr "Buscando actualizaciones en el AUR..."
#: pkg/upgrade/service.go:159
msgid "Searching databases for updates..."
msgstr "Buscando actualizaciones en los repositorios..."
#: pkg/query/query_builder.go:214
msgid "Showing repo packages only"
msgstr "Mostrando solamente paquetes de los repositorios"
#: print.go:95
msgid "Size of pacman cache %s: %s"
msgstr "Tamaño de la cache de pacman %s:%s"
#: print.go:98
msgid "Size of yay cache %s: %s"
msgstr "Tamaño de la cache de yay %s:%s"
#: print.go:62
msgid "Snapshot URL"
msgstr "URL de snapshot"
#: pkg/dep/dep_graph.go:76
msgid "Sync"
msgstr "Sincronizar"
#: print.go:100
msgid "Ten biggest packages:"
msgstr "Diez paquetes más grandes:"
#: pkg/sync/sync.go:124
msgid "The following packages are not compatible with your architecture:"
msgstr "Los siguientes paquetes no son compatibles con su arquitectura:"
#: pkg/db/ialpm/alpm.go:179 pkg/dep/dep_graph.go:726
msgid "There are %[1]d providers available for %[2]s:"
msgstr "Existen %[1]d paquetes que proveen %[2]s:"
#: pkg/settings/exe/cmd_builder.go:258
msgid "There may be another Pacman instance running. Waiting..."
msgstr ""
"Es posible que exista otra instancia de Pacman en ejecución. Esperando..."
#: print.go:92
msgid "Total Size occupied by packages: %s"
msgstr "Tamaño total ocupado por los paquetes: %s"
#: print.go:89
msgid "Total installed packages: %s"
msgstr "Número de paquetes instalados: %s"
#: pkg/sync/sync.go:132
msgid "Try to build them anyway?"
msgstr "¿Intentar compilarlos de todas formas?"
#: print.go:34
msgid "URL"
msgstr "URL"
#: clean.go:194 pkg/menus/clean_menu.go:65 pkg/menus/clean_menu.go:71
msgid "Unable to clean:"
msgstr "No es posible limpiar:"
#: get.go:42 get.go:74
msgid "Unable to find the following packages:"
msgstr "No es posible encontrar los siguientes paquetes:"
#: vote.go:20
msgid "Unable to handle package vote for: %s. err: %s"
msgstr "No se ha podido gestionar el voto del paquete para: %s. error: %s"
#: clean.go:170
msgid "Unable to remove %s: %s"
msgstr "No se puede eliminar %s: %s"
#: print.go:32
msgid "Version"
msgstr "Versión"
#: print.go:50
msgid "Votes"
msgstr "Votos"
#: print.go:87
msgid "Yay version v%s"
msgstr "Versión de yay v%s"
#: pkg/menus/menu.go:49
msgid "[N]one"
msgstr "[N]inguno"
#: pkg/settings/errors.go:29
msgid "aborting due to user"
msgstr "abortando por el usuario"
#: pkg/settings/parser/parser.go:608
msgid "argument '-' specified without input on stdin"
msgstr "argumento «-» especificado sin entrada en stdin"
#: local_install.go:26
msgid "cannot find PKGBUILD and .SRCINFO in directory"
msgstr "no se pudo encontrar PKGBUILD y .SRCINFO en el directorio"
#: pkg/sync/build/pkg_archive.go:148
msgid "cannot find package name: %v"
msgstr "no es posible encontrar el paquete: %v"
#: pkg/sync/build/errors.go:30
msgid "could not find PKGDEST for: %s"
msgstr "no fue posible encontrar PKGDEST para: %s"
#: errors.go:9
msgid "could not find all required packages"
msgstr "no se han encontrado todos los paquetes necesarios"
#: pkg/sync/build/errors.go:61
msgid "could not find any package archives listed in %s"
msgstr "no pudo encontrar ningún archivo de paquetes listado en %s"
#: pkg/sync/build/errors.go:50 pkg/upgrade/service.go:286
msgid "dependency"
msgstr "dependencia"
#: pkg/vcs/vcs.go:100 pkg/vcs/vcs.go:96
msgid "devel check for package failed: '%s' encountered an error"
msgstr ""
"la verificación de desarrollo para el paquete ha fallado: «%s» se encontró "
"un error"
#: pkg/menus/edit_menu.go:110
msgid "editor did not exit successfully, aborting: %s"
msgstr "el editor no terminó correctamente, abortando: %s"
#: pkg/sync/workdir/aur_source.go:24
msgid "error downloading sources: %s"
msgstr "error descargando fuentes: %s"
#: pkg/download/errors.go:25
msgid "error fetching %s: %s"
msgstr "error descargando %s: %s"
#: pkg/sync/build/errors.go:9
msgid "error installing repo packages"
msgstr "error instalando paquetes del repositorio"
#: pkg/sync/build/installer.go:266 pkg/sync/build/installer.go:270
msgid "error installing:"
msgstr "error al instalar:"
#: pkg/sync/build/installer.go:233 pkg/sync/build/installer.go:237
msgid "error making: %s"
msgstr "error al compilar: %s"
#: pkg/sync/workdir/merge.go:24
msgid "error merging %s: %s"
msgstr "error al fusionar %s: %s"
#: pkg/download/unified.go:59
msgid "error reading %s"
msgstr "error leyendo %s"
#: sync.go:36
msgid "error refreshing databases"
msgstr "error refrescando las bases de datos"
#: pkg/sync/workdir/clean.go:51 pkg/sync/workdir/merge.go:17
msgid "error resetting %s: %s"
msgstr "error al reiniciar en %s: %s"
#: pkg/sync/build/errors.go:53
msgid "error updating package install reason to %s"
msgstr "error al actualizar el paquete motivo de instalación para %s"
#: pkg/sync/build/errors.go:48
msgid "explicit"
msgstr "explícito"
#: pkg/settings/errors.go:23
msgid "failed to create directory '%s': %s"
msgstr "no se pudo crear directorio «%s»: %s"
#: pkg/settings/config.go:281
msgid "failed to open config file '%s': %s"
msgstr "no se pudo al abrir archivo de configuración «%s»: %s"
#: pkg/sync/srcinfo/service.go:114
msgid "failed to parse %s -- skipping: %s"
msgstr "no se pudo analizar %s -- ignorando: %s"
#: pkg/sync/srcinfo/service.go:118
msgid "failed to parse %s: %s"
msgstr "no se pudo al analizar %s: %s"
#: local_install.go:77
msgid "failed to parse .SRCINFO"
msgstr "no se pudo analizar .SRCINFO"
#: pkg/settings/config.go:291
msgid "failed to read config file '%s': %s"
msgstr "no se pudo al leer archivo de configuración «%s»: %s"
#: pkg/cmd/graph/main.go:46 pkg/runtime/runtime.go:73
msgid "failed to retrieve aur Cache"
msgstr "no se pudo recuperar el aur Cache"
#: pkg/upgrade/sources.go:27
msgid "ignoring package devel upgrade (no AUR info found):"
msgstr ""
"ignorando la actualización del paquete de desarollo (no se ha encontrado "
"información en el AUR):"
#: pkg/text/errors.go:8
msgid "input too long"
msgstr "entrada demasiado larga"
#: pkg/db/ialpm/alpm.go:222 pkg/dep/dep_graph.go:761
msgid "invalid number: %s"
msgstr "número no válido: %s"
#: pkg/settings/parser/parser.go:174
msgid "invalid option '%s'"
msgstr "opción no válida «%s»"
#: cmd.go:197
msgid "invalid option: '--deps' and '--explicit' may not be used together"
msgstr "opción inválida: «--deps» y «--explicit» no deben usarse a la vez"
#: pkg/download/abs.go:22
msgid "invalid repository"
msgstr "repositorio inválido "
#: pkg/db/ialpm/alpm.go:227 pkg/dep/dep_graph.go:767
msgid "invalid value: %d is not between %d and %d"
msgstr "valor no válido: %d no está entre %d y %d"
#: pkg/text/input.go:48
msgid "no"
msgstr "no"
#: pkg/sync/srcinfo/pgp/keys.go:110
msgid "no keys to import"
msgstr "ninguna llave por importar"
#: pkg/query/errors.go:20
msgid "no query was executed"
msgstr "no se ha realizado ninguna consulta"
#: local_install.go:66
msgid "no target directories specified"
msgstr "no se han especificado directorios de destino"
#: pkg/sync/build/installer.go:242
msgid "nothing to install for %s"
msgstr "nada que instalar para %s"
#: pkg/settings/parser/parser.go:164
msgid "only one operation may be used at a time"
msgstr "sólo una operación se puede usar a la vez"
#: pkg/cmd/graph/main.go:70
msgid "only one target is allowed"
msgstr "sólo se permite un objetivo"
#: pkg/upgrade/service.go:291
msgid "package"
msgid_plural "packages"
msgstr[0] "paquete"
msgstr[1] "paquetes"
msgstr[2] "paquetes"
#: print.go:187
msgid "package '%s' was not found"
msgstr "paquete «%s» no fue encontrado"
#: pkg/download/errors.go:15
msgid "package not found in AUR"
msgstr "paquete no encontrado en el AUR"
#: pkg/download/abs.go:23
msgid "package not found in repos"
msgstr "paquete no encontrado en los repositorios "
#: pkg/sync/srcinfo/pgp/keys.go:100
msgid "problem importing keys"
msgstr "problema al importar llaves"
#: clean.go:105
msgid "removing AUR packages from cache..."
msgstr "borrando paquetes del AUR de la caché..."
#: clean.go:178 pkg/sync/workdir/clean.go:41
msgid "removing untracked AUR files from cache..."
msgstr "borrando archivos del AUR no rastreados de la caché..."
#: pkg/sync/build/errors.go:38
msgid "the PKGDEST for %s is listed by makepkg but does not exist: %s"
msgstr "el PKGDEST para %s está listado por makepkg pero no existe: %s"
#: pkg/sync/sync.go:45
msgid "there is nothing to do"
msgstr "no hay nada que hacer"
#: pkg/db/ialpm/alpm.go:247
msgid "unable to CreateHandle: %s"
msgstr "no fue posible ejecutar CreateHandle: %s"
#: cmd.go:186
msgid "unhandled operation"
msgstr "operación no implementada"
#: cmd.go:450
msgid "unknown-version"
msgstr "versión-desconocida"
#: pkg/text/input.go:47
msgid "yes"
msgstr "sí"
================================================
FILE: po/eu.po
================================================
#
# Translators:
# J G , 2021
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"PO-Revision-Date: 2021-08-13 22:55+0000\n"
"Last-Translator: J G , 2021\n"
"Language-Team: Basque (https://www.transifex.com/yay-1/teams/123732/eu/)\n"
"Language: eu\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: xgotext\n"
#: pkg/menus/menu.go:32
msgid " (Build Files Exist)"
msgstr " (Konpilazio fitxategiak existitzen dira)"
#: pkg/menus/menu.go:27
msgid " (Installed)"
msgstr " (Instalatuta)"
#: cmd.go:453
msgid " [Installed]"
msgstr " [Instalatuta]"
#: cmd.go:410 vote.go:36
msgid " there is nothing to do"
msgstr " ez dago ezer egiteko"
#: pkg/menus/menu.go:49
msgid "%s [A]ll [Ab]ort [I]nstalled [No]tInstalled or (1 2 3, 1-3, ^4)"
msgstr ""
"%s Guzti[A]k [Ab]ortatu [I]nstalatuak [No]Ez instalatuak edo (1 2 3, 1-3, ^4)"
#: pkg/sync/build/installer.go:308
msgid "%s already made -- skipping build"
msgstr "%s eginda dago jada -- konpilazioa alde batera uzten"
#: pkg/menus/edit_menu.go:57
msgid "%s is not set"
msgstr "%s ezarri gabe dago"
#: pkg/settings/exe/cmd_builder.go:257
msgid "%s is present."
msgstr "%s presente dago."
#: pkg/dep/dep_graph.go:460 pkg/sync/build/installer.go:305
msgid "%s is up to date -- skipping"
msgstr "%s eguneratuta dago - alde batera uzten"
#: pkg/upgrade/service.go:292
#, fuzzy
msgid "%s to upgrade/install."
msgstr "eguneratzeko pakete."
#: pkg/upgrade/service.go:286
msgid "%s will also be installed for this operation."
msgstr ""
#: pkg/sync/srcinfo/pgp/keys.go:124
msgid "%s, required by: %s"
msgstr "%s, honek beharrezkoa du: %s"
#: pkg/menus/diff_menu.go:49
msgid "%s: No changes -- skipping"
msgstr "%s: Aldaketarik ez -- alde batera uzten"
#: pkg/query/filter.go:22
msgid "%s: can't use target with option --aur -- skipping"
msgstr "%s: ezin da helburua --aur aukerarekin erabili -- alde batera uzten"
#: pkg/query/filter.go:17
msgid "%s: can't use target with option --repo -- skipping"
msgstr "%s: ezin da helburua --repo aukerarekin erabili -- alde batera uzten"
#: pkg/upgrade/sources.go:57
msgid "%s: ignoring package upgrade (%s => %s)"
msgstr "%s: paketearen eguneraketa alde batera uzten (%s => %s)"
#: pkg/query/aur_warnings.go:46
msgid "%s: local (%s) is newer than AUR (%s)"
msgstr "%s: pakete lokala (%s) AURekoa (%s) baino berriagoa da"
#: vote.go:51
msgid ""
"%s: please set AUR_USERNAME and AUR_PASSWORD environment variables for voting"
msgstr ""
#: pkg/download/unified.go:192
msgid "(%d/%d) Downloaded PKGBUILD from ABS: %s"
msgstr "(%d/%d) PKGBUILD ABStik deskargatuta: %s"
#: pkg/download/aur.go:92 pkg/download/unified.go:188
#, fuzzy
msgid "(%d/%d) Downloaded PKGBUILD: %s"
msgstr "(%d/%d) PKGBUILD ABStik deskargatuta: %s"
#: pkg/download/aur.go:82
#, fuzzy
msgid "(%d/%d) Failed to download PKGBUILD: %s"
msgstr "(%d/%d) PKGBUILD ABStik deskargatuta: %s"
#: pkg/sync/srcinfo/service.go:109
msgid "(%d/%d) Parsing SRCINFO: %s"
msgstr "(%d/%d) SRCINFO aztertzen: %s"
#: pkg/query/types.go:72 pkg/query/types.go:103
msgid "(Installed)"
msgstr "(Instalatuta)"
#: pkg/query/types.go:70 pkg/query/types.go:101
msgid "(Installed: %s)"
msgstr "(Instalatuta: %s)"
#: pkg/query/types.go:61
msgid "(Orphaned)"
msgstr "(Umezurtz)"
#: pkg/query/types.go:65
msgid "(Out-of-date: %s)"
msgstr "(Zaharkituta: %s)"
#: print.go:44
msgid "AUR URL"
msgstr "AUReko URLa"
#: pkg/dep/dep_graph.go:75
#, fuzzy
msgid "AUR"
msgstr "URLa"
#: pkg/menus/edit_menu.go:58
msgid "Add %s or %s to your environment variables"
msgstr "Gehitu %s edo %s zure ingurune-aldagaietara"
#: main.go:60
msgid "Avoid running yay as root/sudo."
msgstr "Saihestu yay root/sudo bezala exekutatzea."
#: pkg/dep/dep_graph.go:63
#, fuzzy
msgid "Check Dependency"
msgstr "Egiaztapen menpekotasunak"
#: print.go:41
msgid "Check Deps"
msgstr "Egiaztapen menpekotasunak"
#: pkg/upgrade/service.go:90
msgid "Checking development packages..."
msgstr "Garapen paketeak egiaztatzen..."
#: pkg/sync/workdir/clean.go:45
msgid "Cleaning (%d/%d): %s"
msgstr "(%d/%d) garbitzen: %s"
#: print.go:42
msgid "Conflicts With"
msgstr "Gatazkak hauekin"
#: pkg/menus/clean_menu.go:62
msgid "Deleting (%d/%d): %s"
msgstr "(%d/%d) ezabatzen: %s"
#: pkg/dep/dep_graph.go:61
#, fuzzy
msgid "Dependency"
msgstr "Behar du"
#: print.go:38
msgid "Depends On"
msgstr "Behar du"
#: print.go:33
msgid "Description"
msgstr "Deskribapena"
#: pkg/menus/diff_menu.go:160
msgid "Diffs to show?"
msgstr "Aldaketak erakutsi?"
#: pkg/settings/migrations.go:25
msgid "Disable 'provides' setting by default"
msgstr ""
#: clean.go:78
msgid "Do you want to remove ALL AUR packages from cache?"
msgstr "Cachean dauden AUR pakete GUZTIAK kendu nahi dituzu?"
#: clean.go:95
msgid "Do you want to remove ALL untracked AUR files?"
msgstr "Jarraipen gabeko AUR fitxategi GUZTIAK kendu nahi dituzu?"
#: clean.go:80
msgid "Do you want to remove all other AUR packages from cache?"
msgstr "Cachean dauden beste AUR paketeak kendu nahi dituzu?"
#: pkg/menus/edit_menu.go:61
msgid "Edit PKGBUILD with?"
msgstr "PKGBUILD zerekin editatu?"
#: pkg/query/errors.go:13
msgid "Error during AUR search: %s\n"
msgstr "Errorea AURen bilatzerakoan: %s\n"
#: pkg/upgrade/service.go:296
msgid "Excluding packages may cause partial upgrades and break systems"
msgstr ""
#: pkg/dep/dep_graph.go:60
msgid "Explicit"
msgstr ""
#: print.go:91
msgid "Explicitly installed packages: %s"
msgstr "Eskuz instalatutako paketeak: %s"
#: pkg/dep/dep_graph.go:437 pkg/dep/dep_graph.go:535
#, fuzzy
msgid "Failed to find AUR package for"
msgstr "Zaharkitu gisa markatutako AUR paketeak:"
#: pkg/sync/build/installer.go:120
msgid "Failed to install layer, rolling up to next layer."
msgstr ""
#: pkg/sync/build/errors.go:16
msgid ""
"Failed to install the following packages. Manual intervention is required:"
msgstr ""
#: print.go:45
msgid "First Submitted"
msgstr "Lehenengo bidalita"
#: pkg/query/aur_warnings.go:79
msgid "Flagged Out Of Date AUR Packages:"
msgstr "Zaharkitu gisa markatutako AUR paketeak:"
#: print.go:90
#, fuzzy
msgid "Foreign installed packages: %s"
msgstr "Kanpotik instalatu diren paketeak: %s"
#: pkg/vcs/vcs.go:144
msgid "Found git repo: %s"
msgstr "Git biltegia aurkitu da: %s"
#: vcs.go:72
msgid "GenDB finished. No packages were installed"
msgstr "GenDB amaitu da. Ez da paketerik instalatu"
#: print.go:36
msgid "Groups"
msgstr "Taldeak"
#: pkg/sync/srcinfo/pgp/keys.go:88
msgid "Import?"
msgstr "Inportatu?"
#: pkg/sync/srcinfo/pgp/keys.go:97
msgid "Importing keys with gpg..."
msgstr "Gakoak GPGrekin inportatzen..."
#: print.go:46
msgid "Keywords"
msgstr "Gako-hitzak"
#: print.go:47
msgid "Last Modified"
msgstr "Azken aldaketa"
#: print.go:35
msgid "Licenses"
msgstr "Lizentziak"
#: pkg/dep/dep_graph.go:77
msgid "Local"
msgstr ""
#: print.go:48
msgid "Maintainer"
msgstr "Arduraduna"
#: pkg/dep/dep_graph.go:62
#, fuzzy
msgid "Make Dependency"
msgstr "Menpekotasunak burutzea"
#: print.go:40
msgid "Make Deps"
msgstr "Menpekotasunak burutzea"
#: pkg/query/aur_warnings.go:71
#, fuzzy
msgid "Missing AUR Debug Packages:"
msgstr "Faltan dauden AUR paketeak:"
#: pkg/dep/dep_graph.go:79
msgid "Missing"
msgstr ""
#: print.go:31
msgid "Name"
msgstr "Izena"
#: pkg/dep/dep_graph.go:442 pkg/dep/dep_graph.go:548
msgid "No AUR package found for"
msgstr ""
#: pkg/dep/dep_graph.go:182
msgid "No package found for"
msgstr ""
#: print.go:225
msgid "None"
msgstr "Deus ez"
#: print.go:39
msgid "Optional Deps"
msgstr "Hautazko menpekotasunak"
#: pkg/query/aur_warnings.go:75
#, fuzzy
msgid "Orphan (unmaintained) AUR Packages:"
msgstr "Umezurtz geratu diren AUR paketeak:"
#: print.go:53 print.go:55
msgid "Out-of-date"
msgstr "Zaharkituta"
#: pkg/sync/srcinfo/pgp/keys.go:115
msgid "PGP keys need importing:"
msgstr "PGP gakoek inportatu behar dute:"
#: pkg/sync/workdir/preparer.go:252
#, fuzzy
msgid "PKGBUILD up to date, skipping download: %s"
msgstr "PKGBUILD eguneratuta, alde batera uzten (%d/%d): %s"
#: pkg/menus/edit_menu.go:130
msgid "PKGBUILDs to edit?"
msgstr "Editatzeko PKGBUILDak?"
#: print.go:60
msgid "Package Base ID"
msgstr "Pakete-oinarriaren IDa"
#: print.go:61
msgid "Package Base"
msgstr "Pakete-oinarria"
#: pkg/query/aur_warnings.go:67
msgid "Packages not in AUR:"
msgstr ""
#: pkg/menus/clean_menu.go:54
msgid "Packages to cleanBuild?"
msgstr "Konpilatu aurretik garbitzeko paketeak?"
#: pkg/dep/dep_graph.go:202
#, fuzzy
msgid "Packages to exclude"
msgstr "eguneratzeko pakete."
#: pkg/upgrade/service.go:295
msgid "Packages to exclude: (eg: \"1 2 3\", \"1-3\", \"^4\" or repo name)"
msgstr ""
"Kanpoan uzteko paketeak: (adb: \"1 2 3\", \"1-3\", \"^4\" edo biltegiaren "
"izena)"
#: cmd.go:392
msgid "Packages to install (eg: 1 2 3, 1-3 or ^4)"
msgstr "Instalatzeko paketeak (eg.: 1 2 3, 1-3 edo ^4)"
#: print.go:49
msgid "Popularity"
msgstr "Ospea"
#: pkg/menus/diff_menu.go:172 pkg/menus/edit_menu.go:143
msgid "Proceed with install?"
msgstr "Instalatu?"
#: print.go:37
msgid "Provides"
msgstr "Hornitzen du"
#: pkg/sync/workdir/preparer.go:125
msgid "Remove make dependencies after install?"
msgstr "Konpilatzeko menpekotasunak ezabatu instalatu ostean?"
#: print.go:43
msgid "Replaces"
msgstr ""
#: pkg/dep/dep_graph.go:730
msgid "Repository AUR"
msgstr "AUR biltegia"
#: print.go:30 pkg/db/ialpm/alpm.go:191
msgid "Repository"
msgstr "Biltegia"
#: pkg/dep/dep_graph.go:78
msgid "SRCINFO"
msgstr ""
#: pkg/upgrade/service.go:72
msgid "Searching AUR for updates..."
msgstr "Eguneraketak AURen bilatzen..."
#: pkg/upgrade/service.go:160
msgid "Searching databases for updates..."
msgstr "Datu-baseetan eguneraketak bilatzen..."
#: pkg/query/query_builder.go:214
msgid "Showing repo packages only"
msgstr "Biltegietako paketeak soilik erakusten"
#: print.go:95
#, fuzzy
msgid "Size of pacman cache %s: %s"
msgstr "%s analizatzeak huts egin du: %s"
#: print.go:98
#, fuzzy
msgid "Size of yay cache %s: %s"
msgstr "%s analizatzeak huts egin du: %s"
#: print.go:62
msgid "Snapshot URL"
msgstr "PKGBUILDaren argazkiaren URLa"
#: pkg/dep/dep_graph.go:76
msgid "Sync"
msgstr ""
#: print.go:100
msgid "Ten biggest packages:"
msgstr "Hamar pakete handienak:"
#: pkg/sync/sync.go:124
msgid "The following packages are not compatible with your architecture:"
msgstr "Ondorengo paketeak ez dira zure arkitekturarekin bateragarriak:"
#: pkg/db/ialpm/alpm.go:179 pkg/dep/dep_graph.go:726
#, fuzzy
msgid "There are %[1]d providers available for %[2]s:"
msgstr "%[1]d hornitzaile eskuragarri %[2]s paketearentzat:\n"
#: pkg/settings/exe/cmd_builder.go:258
msgid "There may be another Pacman instance running. Waiting..."
msgstr "Baliteke beste Pacman instantzia bat exekutzan aritzea. Zain..."
#: print.go:92
msgid "Total Size occupied by packages: %s"
msgstr "Pakete guztiek erabilitako biltegiratzea: %s"
#: print.go:89
msgid "Total installed packages: %s"
msgstr "Instalatu diren paketeak: %s"
#: pkg/sync/sync.go:132
msgid "Try to build them anyway?"
msgstr "Saiatu konpilatzen hala ere?"
#: print.go:34
msgid "URL"
msgstr "URLa"
#: clean.go:194 pkg/menus/clean_menu.go:65 pkg/menus/clean_menu.go:71
#, fuzzy
msgid "Unable to clean:"
msgstr "ezin da CreateHandle burutu: %s"
#: get.go:42 get.go:74
msgid "Unable to find the following packages:"
msgstr ""
#: vote.go:20
msgid "Unable to handle package vote for: %s. err: %s"
msgstr ""
#: clean.go:170
#, fuzzy
msgid "Unable to remove %s: %s"
msgstr "%s analizatzeak huts egin du: %s"
#: print.go:32
msgid "Version"
msgstr "Bertsioa"
#: print.go:50
msgid "Votes"
msgstr "Botoak"
#: print.go:87
msgid "Yay version v%s"
msgstr "Yay bertsioa v%s"
#: pkg/menus/menu.go:49
msgid "[N]one"
msgstr "I[N]or ez"
#: clean.go:83
msgid ""
"\n"
"Build directory:"
msgstr ""
"\n"
"Konpilazio direktorioa:"
#: pkg/db/ialpm/alpm.go:201 pkg/dep/dep_graph.go:740
msgid ""
"\n"
"Enter a number (default=1): "
msgstr ""
"\n"
"Idatzi zenbaki bat (lehenetsia=1): "
#: pkg/settings/errors.go:29
msgid "aborting due to user"
msgstr "erabiltzailearengatik bertan behera uzten"
#: pkg/settings/parser/parser.go:608
msgid "argument '-' specified without input on stdin"
msgstr ""
#: local_install.go:26
msgid "cannot find PKGBUILD and .SRCINFO in directory"
msgstr ""
#: pkg/sync/build/pkg_archive.go:148
msgid "cannot find package name: %v"
msgstr "ezin da paketearen izena aurkitu: %v"
#: pkg/sync/build/errors.go:30
msgid "could not find PKGDEST for: %s"
msgstr "ezin izan da aurkitu PKGDEST %s paketearentzat"
#: errors.go:9
#, fuzzy
msgid "could not find all required packages"
msgstr "Ez izan dira aurkitu beharrezko pakete guztiak:"
#: pkg/sync/build/errors.go:61
msgid "could not find any package archives listed in %s"
msgstr ""
#: pkg/sync/build/errors.go:50 pkg/upgrade/service.go:287
#, fuzzy
msgid "dependency"
msgstr "Behar du"
#: pkg/vcs/vcs.go:96 pkg/vcs/vcs.go:100
msgid "devel check for package failed: '%s' encountered an error"
msgstr ""
#: pkg/menus/edit_menu.go:110
msgid "editor did not exit successfully, aborting: %s"
msgstr "editorea ez da behar bezala irten, bertan behera uzten: %s"
#: pkg/sync/workdir/aur_source.go:24
msgid "error downloading sources: %s"
msgstr "errorea iturriak deskargatzerakoan: %s"
#: pkg/download/errors.go:25
msgid "error fetching %s: %s"
msgstr "errorea %s lortzerakoan: %s"
#: pkg/sync/build/errors.go:9
msgid "error installing repo packages"
msgstr "errorea biltegitako paketeak instalatzerakoan"
#: pkg/sync/build/installer.go:266 pkg/sync/build/installer.go:270
#, fuzzy
msgid "error installing:"
msgstr "errorea biltegitako paketeak instalatzerakoan"
#: pkg/sync/build/installer.go:233 pkg/sync/build/installer.go:237
msgid "error making: %s"
msgstr "konpilazio akatsa: %s"
#: pkg/sync/workdir/merge.go:24
msgid "error merging %s: %s"
msgstr "errorea %s bateratzerakoan: %s"
#: pkg/download/unified.go:59
msgid "error reading %s"
msgstr "errorea %s irakurtzerakoan"
#: sync.go:36
msgid "error refreshing databases"
msgstr "errorea datu-baseak freskatzerakoan"
#: pkg/sync/workdir/clean.go:51 pkg/sync/workdir/merge.go:17
msgid "error resetting %s: %s"
msgstr "errorea %s berrezartzerakoan: %s"
#: pkg/sync/build/errors.go:53
msgid "error updating package install reason to %s"
msgstr ""
#: pkg/sync/build/errors.go:48
msgid "explicit"
msgstr ""
#: pkg/settings/errors.go:23
msgid "failed to create directory '%s': %s"
msgstr "'%s' konfigurazio direktorioa sortzeak huts egin du: %s"
#: pkg/settings/config.go:281
msgid "failed to open config file '%s': %s"
msgstr "'%s' konfigurazio fitxategia irekitzeak huts egin du: %s"
#: pkg/sync/srcinfo/service.go:114
msgid "failed to parse %s -- skipping: %s"
msgstr "%s analizatzeak huts egin du -- alde batera uzten: %s"
#: pkg/sync/srcinfo/service.go:118
msgid "failed to parse %s: %s"
msgstr "%s analizatzeak huts egin du: %s"
#: local_install.go:77
#, fuzzy
msgid "failed to parse .SRCINFO"
msgstr "%s analizatzeak huts egin du: %s"
#: pkg/settings/config.go:291
msgid "failed to read config file '%s': %s"
msgstr "'%s' konfigurazio fitxategia irakurtzeak huts egin du: %s"
#: pkg/cmd/graph/main.go:46 pkg/runtime/runtime.go:73
msgid "failed to retrieve aur Cache"
msgstr ""
#: pkg/upgrade/sources.go:27
#, fuzzy
msgid "ignoring package devel upgrade (no AUR info found):"
msgstr "%s: paketearen eguneraketa alde batera uzten (%s => %s)"
#: pkg/text/errors.go:8
msgid "input too long"
msgstr "sarrera luzeegia"
#: pkg/db/ialpm/alpm.go:222 pkg/dep/dep_graph.go:761
msgid "invalid number: %s"
msgstr "zenbaki baliogabea: %s"
#: pkg/settings/parser/parser.go:174
msgid "invalid option '%s'"
msgstr "'%s' aukera baliogabea"
#: cmd.go:197
msgid "invalid option: '--deps' and '--explicit' may not be used together"
msgstr ""
#: pkg/download/abs.go:22
#, fuzzy
msgid "invalid repository"
msgstr "Biltegia"
#: pkg/db/ialpm/alpm.go:227 pkg/dep/dep_graph.go:767
msgid "invalid value: %d is not between %d and %d"
msgstr "balio baliogabea: %d ez dago %d eta %d artean"
#: pkg/sync/srcinfo/pgp/keys.go:110
msgid "no keys to import"
msgstr "inportatzeko gakorik ez"
#: pkg/query/errors.go:20
msgid "no query was executed"
msgstr ""
#: local_install.go:66
msgid "no target directories specified"
msgstr ""
#: pkg/text/input.go:48
msgid "no"
msgstr "ez"
#: pkg/sync/build/installer.go:242
msgid "nothing to install for %s"
msgstr ""
#: pkg/settings/parser/parser.go:164
msgid "only one operation may be used at a time"
msgstr "aldi berean operazio bakarra erabil daiteke"
#: pkg/cmd/graph/main.go:70
msgid "only one target is allowed"
msgstr ""
#: print.go:187
msgid "package '%s' was not found"
msgstr "ez da aurkitu '%s' paketea"
#: pkg/download/errors.go:15
#, fuzzy
msgid "package not found in AUR"
msgstr "ez da aurkitu '%s' paketea"
#: pkg/download/abs.go:23
#, fuzzy
msgid "package not found in repos"
msgstr "ez da aurkitu '%s' paketea"
#: pkg/upgrade/service.go:292
#, fuzzy
msgid "package"
msgid_plural "packages"
msgstr[0] "Pakete-oinarria"
msgstr[1] "Pakete-oinarria"
#: pkg/sync/srcinfo/pgp/keys.go:100
msgid "problem importing keys"
msgstr "arazoa gakoak inportatzerakoan"
#: clean.go:105
msgid "removing AUR packages from cache..."
msgstr "AUR paketeak cachetik kentzen..."
#: clean.go:178 pkg/sync/workdir/clean.go:41
msgid "removing untracked AUR files from cache..."
msgstr "jarraipen gabeko AUR fitxategiak cachetik kentzen..."
#: pkg/sync/build/errors.go:38
msgid "the PKGDEST for %s is listed by makepkg but does not exist: %s"
msgstr ""
"%s(e)rako PKGDESTa makepkg-ek zerrendatzen du, baina ez da existitzen: %s"
#: pkg/sync/sync.go:45
#, fuzzy
msgid "there is nothing to do"
msgstr " ez dago ezer egiteko"
#: pkg/db/ialpm/alpm.go:247
msgid "unable to CreateHandle: %s"
msgstr "ezin da CreateHandle burutu: %s"
#: cmd.go:186
msgid "unhandled operation"
msgstr "sostengurik gabeko eragiketa"
#: cmd.go:450
msgid "unknown-version"
msgstr "bertsio ezezaguna"
#: pkg/text/input.go:47
msgid "yes"
msgstr "bai"
#~ msgid " (Target"
#~ msgstr " (Helburua"
#~ msgid " (Wanted by: "
#~ msgstr " (Honek behar du: "
#~ msgid " Input too long"
#~ msgstr " Sarrera luzeegia"
#~ msgid "Installing %s will remove:"
#~ msgstr "%s instalatzeak ezabatuko du:"
#~ msgid "%s already downloaded -- use -f to overwrite"
#~ msgstr "%s deskargatuta dago -- erabili -f gainidazteko"
#~ msgid "%s and %s unset"
#~ msgstr "%s eta %s ezarri gabe"
#~ msgid "%s not satisfied, flushing install queue"
#~ msgstr "%s ez da betetzen, instalazio ilara ezabatzen"
#~ msgid "Checking for conflicts..."
#~ msgstr "Gatazkak bilatzen..."
#~ msgid "Checking for inner conflicts..."
#~ msgstr "Barneko gatazkak bilatzen..."
#~ msgid "Conflicting packages will have to be confirmed manually"
#~ msgstr "Pakete gatazkatsuak eskuz egiaztatu beharko dira"
#~ msgid "Downloaded PKGBUILD (%d/%d): %s"
#~ msgstr "(%d/%d) PKGBUILDak deskargatuta: %s"
#~ msgid "Missing ABS packages:"
#~ msgstr "Faltan dauden ABS paketeak:"
#~ msgid "Querying AUR..."
#~ msgstr "AURen bilatzen..."
#~ msgid ""
#~ "\n"
#~ "Inner conflicts found:"
#~ msgstr ""
#~ "\n"
#~ "Barne gatazkak aurkitu dira:"
#~ msgid ""
#~ "\n"
#~ "Package conflicts found:"
#~ msgstr ""
#~ "\n"
#~ "Pakete arteko gaztakak aurkitu dira:"
#~ msgid "error cloning %s: %s"
#~ msgstr "errorea %s klonatzerakoan: %s"
#~ msgid "error during AUR search: %s"
#~ msgstr "errorea AURen bilatzerakoan: %s"
#~ msgid "failed to create BuildDir directory '%s': %s"
#~ msgstr "'%s' BuildDir direktorioa sortzeak huts egin du: %s"
#~ msgid "failed to get pkgbuild: %s: %s"
#~ msgstr "PKGBUILD lortzeak huts egin du: %s: %s"
#~ msgid "failed to link %s: %s"
#~ msgstr "%s estekatzeak huts egin du: %s"
#~ msgid "failed to open vcs file '%s': %s"
#~ msgstr "'%s' VCS fitxategia irekitzeak huts egin du: %s"
#~ msgid "failed to read vcs file '%s': %s"
#~ msgstr "'%s' VCS fitxategia irakurtzeak huts egin du: %s"
#~ msgid "invalid sort mode. Fix with yay -Y --bottomup --save"
#~ msgstr ""
#~ "ordenatzeko modu baliogabea. Konpondu 'yay -Y --bottomup --save' "
#~ "exekutatzen"
#~ msgid "no packages match search"
#~ msgstr "paketerik ez dator bat bilaketarekin"
#~ msgid "package conflicts can not be resolved with noconfirm, aborting"
#~ msgstr ""
#~ "pakete arteko gatazkak ezin dira noconfirm-ekin konpondu, bertan behera "
#~ "uzten"
#~ msgid "refusing to install AUR packages as root, aborting"
#~ msgstr ""
#~ "AUR paketeak root gisa instalatzeari uko egiten, bertan behera uzten"
#~ msgid "failed to create cache directory '%s': %s"
#~ msgstr "'%s' cache direktorioa sortzeak huts egin du: %s"
================================================
FILE: po/fi.po
================================================
# Translators:
# Joonas Miettinen, 2026
#
msgid ""
msgstr ""
"Last-Translator: Joonas Miettinen, 2026\n"
"Language-Team: Finnish (https://app.transifex.com/yay-1/teams/123732/fi/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: fi\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: xgotext\n"
#: clean.go:83
msgid ""
"\n"
"Build directory:"
msgstr ""
"\n"
"Kääntöhakemisto:"
#: pkg/db/ialpm/alpm.go:201 pkg/dep/dep_graph.go:740
msgid ""
"\n"
"Enter a number (default=1): "
msgstr ""
"\n"
"Anna numero (oletusarvo=1):"
#: pkg/menus/menu.go:32
msgid " (Build Files Exist)"
msgstr "(Kääntötiedostot ovat olemassa)"
#: pkg/menus/menu.go:27
msgid " (Installed)"
msgstr "(Asennettu)"
#: cmd.go:453
msgid " [Installed]"
msgstr "[Asennettu]"
#: cmd.go:410 vote.go:36
msgid " there is nothing to do"
msgstr "ei tehtävää"
#: pkg/menus/menu.go:49
msgid "%s [A]ll [Ab]ort [I]nstalled [No]tInstalled or (1 2 3, 1-3, ^4)"
msgstr ""
"%s [A]Kaikki [Ab]Keskeytä [I]Asennettu [No]Ei asennettu tai (1 2 3, 1-3, ^4)"
" "
#: pkg/sync/build/installer.go:308
msgid "%s already made -- skipping build"
msgstr "%s on jo käännetty --- ei käännetä"
#: pkg/menus/edit_menu.go:57
msgid "%s is not set"
msgstr "asetusta %s ei ole asetettu"
#: pkg/settings/exe/cmd_builder.go:257
msgid "%s is present."
msgstr "%s on olemassa."
#: pkg/dep/dep_graph.go:460 pkg/sync/build/installer.go:305
msgid "%s is up to date -- skipping"
msgstr "Asennuskohdetta %s ei tarvitse päivittää --- jatketaan"
#: pkg/upgrade/service.go:291
msgid "%s to upgrade/install."
msgstr "%s päivittääksesi tai asentaaksesi."
#: pkg/upgrade/service.go:285
msgid "%s will also be installed for this operation."
msgstr "Myös %s asennetaan tätä toimenpidettä varten."
#: pkg/sync/srcinfo/pgp/keys.go:124
msgid "%s, required by: %s"
msgstr "%s, tarpeellinen riippuvuus seuraavalle: %s "
#: pkg/menus/diff_menu.go:49
msgid "%s: No changes -- skipping"
msgstr "%s: Ei muutoksia -- jatketaan"
#: pkg/query/filter.go:22
msgid "%s: can't use target with option --aur -- skipping"
msgstr "%s: kohdetta ei voi käyttää valinnalla --aur -- jätetään huomiotta"
#: pkg/query/filter.go:17
msgid "%s: can't use target with option --repo -- skipping"
msgstr "%s: kohdetta ei voi käyttää valinnalla --repo -- jätetään huomiotta"
#: pkg/upgrade/sources.go:57
msgid "%s: ignoring package upgrade (%s => %s)"
msgstr "%s: pakettia ei päivitetä (%s => %s) "
#: pkg/query/aur_warnings.go:46
msgid "%s: local (%s) is newer than AUR (%s)"
msgstr "%s: paikallinen (%s) on uudempi kuin AUR:ssa (%s) "
#: vote.go:51
msgid ""
"%s: please set AUR_USERNAME and AUR_PASSWORD environment variables for "
"voting"
msgstr ""
"%s: aseta ympäristömuuttujat AUR_USERNAME ja AUR_PASSWORD äänestääksesi "
#: pkg/download/unified.go:192
msgid "(%d/%d) Downloaded PKGBUILD from ABS: %s"
msgstr "(%d/%d) PKGBUILD ladattu ABS:stä: %s "
#: pkg/download/aur.go:92 pkg/download/unified.go:188
msgid "(%d/%d) Downloaded PKGBUILD: %s"
msgstr "(%d/%d) PKGBUILD ladattu: %s "
#: pkg/download/aur.go:82
msgid "(%d/%d) Failed to download PKGBUILD: %s"
msgstr "(%d/%d) PKGBUILDin lataaminen epäonnistui: %s "
#: pkg/sync/srcinfo/service.go:109
msgid "(%d/%d) Parsing SRCINFO: %s"
msgstr "(%d/%d) Jäsennetään SRCINFO: %s "
#: pkg/query/types.go:103 pkg/query/types.go:72
msgid "(Installed)"
msgstr "(Asennettu)"
#: pkg/query/types.go:101 pkg/query/types.go:70
msgid "(Installed: %s)"
msgstr "(Asennettu: %s) "
#: pkg/query/types.go:61
msgid "(Orphaned)"
msgstr "(Orvoksi jäänyt) "
#: pkg/query/types.go:65
msgid "(Out-of-date: %s)"
msgstr "(Vanhentunut: %s) "
#: pkg/dep/dep_graph.go:75
msgid "AUR"
msgstr "AUR "
#: print.go:44
msgid "AUR URL"
msgstr "AUR URL "
#: pkg/menus/edit_menu.go:58
msgid "Add %s or %s to your environment variables"
msgstr "Lisää %s tai %s ympäristömuuttujiisi"
#: main.go:60
msgid "Avoid running yay as root/sudo."
msgstr ""
"yay:ia ei suositella ajettavan pääkäyttäjänä tai sudo-komennon kanssa."
#: pkg/dep/dep_graph.go:63
msgid "Check Dependency"
msgstr "Tarkista riippuvuus"
#: print.go:41
msgid "Check Deps"
msgstr "Tarkista riippuvuudet"
#: pkg/upgrade/service.go:89
msgid "Checking development packages..."
msgstr "Tarkistetaan kehityspaketit..."
#: pkg/sync/workdir/clean.go:45
msgid "Cleaning (%d/%d): %s"
msgstr "Puhdistetaan (%d/%d): %s "
#: print.go:42
msgid "Conflicts With"
msgstr "Ei sovi seuraavan kanssa yhteen"
#: pkg/menus/clean_menu.go:62
msgid "Deleting (%d/%d): %s"
msgstr "Poistetaan (%d/%d): %s "
#: pkg/dep/dep_graph.go:61
msgid "Dependency"
msgstr "Riippuvuus"
#: print.go:38
msgid "Depends On"
msgstr "Riippuu seuraavasta"
#: print.go:33
msgid "Description"
msgstr "Kuvaus"
#: pkg/menus/diff_menu.go:160
msgid "Diffs to show?"
msgstr "Näytä erot?"
#: pkg/settings/migrations.go:25
msgid "Disable 'provides' setting by default"
msgstr "Poista asetus 'provides' käytöstä oletusarvoisesti"
#: clean.go:78
msgid "Do you want to remove ALL AUR packages from cache?"
msgstr "Haluatko poistaa KAIKKI AUR-paketit välimuistista?"
#: clean.go:95
msgid "Do you want to remove ALL untracked AUR files?"
msgstr "Haluatko poistaa KAIKKI seuraamattomat AUR-tiedostot?"
#: clean.go:80
msgid "Do you want to remove all other AUR packages from cache?"
msgstr "Haluatko poistaa kaikki muut AUR-paketit välimuistista?"
#: pkg/menus/edit_menu.go:61
msgid "Edit PKGBUILD with?"
msgstr "Millä haluat muokata PKGBUILD-tiedostoa?"
#: pkg/query/errors.go:13
msgid "Error during AUR search: %s\n"
msgstr "Virhe AUR-haun aikana: %s \n"
#: pkg/upgrade/service.go:295
msgid "Excluding packages may cause partial upgrades and break systems"
msgstr ""
"Pakettien jättäminen pois voi kohtaa osittaiseen päivitykseen ja voi rikkoa "
"järjestelmiä"
#: pkg/dep/dep_graph.go:60
msgid "Explicit"
msgstr "Nimellä pyydetty"
#: print.go:91
msgid "Explicitly installed packages: %s"
msgstr "Asennetut nimellä pyydetyt paketit: %s"
#: pkg/dep/dep_graph.go:437 pkg/dep/dep_graph.go:535
msgid "Failed to find AUR package for"
msgstr "AUR-paketteja ei löytynyt seuraavalle(ille)"
#: pkg/sync/build/installer.go:120
msgid "Failed to install layer, rolling up to next layer."
msgstr "Kerroksen asennus epäonnistui, siirrytään seuraavaan kerrokseen."
#: pkg/sync/build/errors.go:16
msgid ""
"Failed to install the following packages. Manual intervention is required:"
msgstr ""
"Seuraavien pakettien asennus epäonnistui. Käyttäjältä edellytetään "
"toimenpiteitä:"
#: print.go:45
msgid "First Submitted"
msgstr "Ladattiin palveluun ensimmäistä kertaa"
#: pkg/query/aur_warnings.go:83
msgid "Flagged Out Of Date AUR Packages:"
msgstr "Vanhentuneeksi merkityt AUR-paketit: "
#: print.go:90
msgid "Foreign installed packages: %s"
msgstr "Vieraat asennetut paketit: %s"
#: pkg/vcs/vcs.go:144
msgid "Found git repo: %s"
msgstr "Seuraava git-tietovarasto löytyi: %s "
#: vcs.go:72
msgid "GenDB finished. No packages were installed"
msgstr "GenDB valmistui. Yhtään pakettia ei asennettu"
#: print.go:36
msgid "Groups"
msgstr "Ryhmät"
#: pkg/sync/srcinfo/pgp/keys.go:88
msgid "Import?"
msgstr "Tuo?"
#: pkg/sync/srcinfo/pgp/keys.go:97
msgid "Importing keys with gpg..."
msgstr "Tuodaan avaimet gpg:in avulla..."
#: print.go:46
msgid "Keywords"
msgstr "Avainsanat"
#: print.go:47
msgid "Last Modified"
msgstr "Viimeksi muokattu"
#: print.go:35
msgid "Licenses"
msgstr "Lisenssit"
#: pkg/dep/dep_graph.go:77
msgid "Local"
msgstr "Paikallinen"
#: print.go:48
msgid "Maintainer"
msgstr "Ylläpitäjä"
#: pkg/dep/dep_graph.go:62
msgid "Make Dependency"
msgstr "Kääntöriippuvuus"
#: print.go:40
msgid "Make Deps"
msgstr "Kääntöriippuvuudet"
#: pkg/dep/dep_graph.go:79
msgid "Missing"
msgstr "Puuttuva(vat)"
#: pkg/query/aur_warnings.go:75
msgid "Missing AUR Debug Packages:"
msgstr "Puuttuvat AUR-virheenkorjauspaketit:"
#: print.go:31
msgid "Name"
msgstr "Nimi"
#: pkg/dep/dep_graph.go:442 pkg/dep/dep_graph.go:564
msgid "No AUR package found for"
msgstr "Seuraavalle(ville) ei löytynyt yhtään AUR-pakettia"
#: pkg/dep/dep_graph.go:564
msgid "required by"
msgstr "on seuraavan edellyttämä: "
#: pkg/dep/dep_graph.go:182
msgid "No package found for"
msgstr "Yhtään pakettia ei löytynyt seuraavalle(ville)"
#: print.go:225
msgid "None"
msgstr "Ei mitään/yhtään"
#: print.go:39
msgid "Optional Deps"
msgstr "Valinnaiset riippuvuudet"
#: pkg/query/aur_warnings.go:79
msgid "Orphan (unmaintained) AUR Packages:"
msgstr "Orvot (ei ylläpitäjää) AUR-paketit:"
#: print.go:53 print.go:55
msgid "Out-of-date"
msgstr "Vanhentunut"
#: pkg/sync/srcinfo/pgp/keys.go:115
msgid "PGP keys need importing:"
msgstr "PGP-avaimet täytyy tuoda:"
#: pkg/sync/workdir/preparer.go:252
msgid "PKGBUILD up to date, skipping download: %s"
msgstr "PKGBUILD on uusin, jätetään lataamatta: %s"
#: pkg/menus/edit_menu.go:130
msgid "PKGBUILDs to edit?"
msgstr "Muokattavat PKGBUILD-tiedostot?"
#: print.go:61
msgid "Package Base"
msgstr "Paketin perusta"
#: print.go:60
msgid "Package Base ID"
msgstr "Paketin perustan tunniste"
#: pkg/query/aur_warnings.go:71
msgid "Packages not in AUR:"
msgstr "Paketit joita ei löydy AUR:ista:"
#: pkg/menus/clean_menu.go:54
msgid "Packages to cleanBuild?"
msgstr "Paketit jotka haluat asentaa puhtaasti (cleanBuild)?"
#: pkg/dep/dep_graph.go:202
msgid "Packages to exclude"
msgstr "Pois jätettävät paketit"
#: pkg/upgrade/service.go:294
msgid "Packages to exclude: (eg: \"1 2 3\", \"1-3\", \"^4\" or repo name)"
msgstr "Pois jätettävät paketit (esim. \"1 2 3\", \"1-3\", \"^4\" tai tietovarasto nimi)"
#: cmd.go:392
msgid "Packages to install (eg: 1 2 3, 1-3 or ^4)"
msgstr "Asennettavat paketit (esim. 1 2 3, 1-3 tai ^4)"
#: print.go:49
msgid "Popularity"
msgstr "Suosio"
#: pkg/menus/diff_menu.go:172 pkg/menus/edit_menu.go:143
msgid "Proceed with install?"
msgstr "Jatka asennukseen?"
#: print.go:37
msgid "Provides"
msgstr "Mahdollistaa"
#: pkg/sync/workdir/preparer.go:125
msgid "Remove make dependencies after install?"
msgstr "Poista kääntöriippuvuudet asennuksen jälkeen?"
#: print.go:43
msgid "Replaces"
msgstr "Korvaa"
#: pkg/db/ialpm/alpm.go:191 print.go:30
msgid "Repository"
msgstr "Tietovarasto"
#: pkg/dep/dep_graph.go:730
msgid "Repository AUR"
msgstr "Tietovarasto AUR"
#: pkg/dep/dep_graph.go:78
msgid "SRCINFO"
msgstr "SRCINFO"
#: pkg/upgrade/service.go:71
msgid "Searching AUR for updates..."
msgstr "Etsitään päivityksitä AUR:ista..."
#: pkg/upgrade/service.go:159
msgid "Searching databases for updates..."
msgstr "Etsitään päivityksiä tietokannoista..."
#: pkg/query/query_builder.go:214
msgid "Showing repo packages only"
msgstr "Vain tietovarastopaketit näytetään"
#: print.go:95
msgid "Size of pacman cache %s: %s"
msgstr "pacman-välimuistin koko %s: %s"
#: print.go:98
msgid "Size of yay cache %s: %s"
msgstr "yay-välimuistin koko %s: %s"
#: print.go:62
msgid "Snapshot URL"
msgstr "Tilannekuvan URL"
#: pkg/dep/dep_graph.go:76
msgid "Sync"
msgstr "Synkronoi"
#: print.go:100
msgid "Ten biggest packages:"
msgstr "Kymmenen suurinta pakettia:"
#: pkg/sync/sync.go:124
msgid "The following packages are not compatible with your architecture:"
msgstr ""
"Seuraavat paketit eivät ole yhteensopivia tietokonearkkitehtuurisi kanssa:"
#: pkg/db/ialpm/alpm.go:179 pkg/dep/dep_graph.go:726
msgid "There are %[1]d providers available for %[2]s:"
msgstr "%s[1]d tarjoajaa saatavilla %[2]:lle:"
#: pkg/settings/exe/cmd_builder.go:258
msgid "There may be another Pacman instance running. Waiting..."
msgstr "Toinen pacman-prosessi saattaa olla käynnissä. Odotetaan..."
#: print.go:92
msgid "Total Size occupied by packages: %s"
msgstr "Paketit vievät muistia yhteensä: %s"
#: print.go:89
msgid "Total installed packages: %s"
msgstr "Yhteensä asennettuja paketteja: %s"
#: pkg/sync/sync.go:132
msgid "Try to build them anyway?"
msgstr "Yritä silti kääntää ne?"
#: print.go:34
msgid "URL"
msgstr "URL"
#: clean.go:194 pkg/menus/clean_menu.go:65 pkg/menus/clean_menu.go:71
msgid "Unable to clean:"
msgstr "Ei voida puhdistaa:"
#: get.go:42 get.go:74
msgid "Unable to find the following packages:"
msgstr "Seuraavia paketteja ei löytynyt:"
#: vote.go:20
msgid "Unable to handle package vote for: %s. err: %s"
msgstr "Ääntä paketille %s ei voitu käsitellä. Virheviesti: %s"
#: clean.go:170
msgid "Unable to remove %s: %s"
msgstr "Pakettia %s ei voitu poistaa: %s"
#: print.go:32
msgid "Version"
msgstr "Versio"
#: print.go:50
msgid "Votes"
msgstr "Äänet"
#: print.go:87
msgid "Yay version v%s"
msgstr "yay-versio v%s"
#: pkg/menus/menu.go:49
msgid "[N]one"
msgstr "[N]Ei yhtään"
#: pkg/settings/errors.go:29
msgid "aborting due to user"
msgstr "keskeytetään käyttäjän pyynnöstä"
#: pkg/settings/parser/parser.go:608
msgid "argument '-' specified without input on stdin"
msgstr "argumentti '-' annettu ilman stdin-syötettä"
#: local_install.go:26
msgid "cannot find PKGBUILD and .SRCINFO in directory"
msgstr "hakemistosta ei löydy PKGBUILD- ja .SRCINFO-tiedostoa"
#: pkg/sync/build/pkg_archive.go:148
msgid "cannot find package name: %v"
msgstr "paketin nimeä ei löydy: %v"
#: pkg/sync/build/errors.go:30
msgid "could not find PKGDEST for: %s"
msgstr "PKGDEST ei löytynyt paketille %s"
#: errors.go:9
msgid "could not find all required packages"
msgstr "kaikkia vaadittuja paketteja ei löytynyt"
#: pkg/sync/build/errors.go:61
msgid "could not find any package archives listed in %s"
msgstr "yhtään pakettiarkistoa ei löytynyt listasta %s"
#: pkg/sync/build/errors.go:50 pkg/upgrade/service.go:286
msgid "dependency"
msgstr "riippuvuus"
#: pkg/vcs/vcs.go:100 pkg/vcs/vcs.go:96
msgid "devel check for package failed: '%s' encountered an error"
msgstr "kehitystarkistus paketille '%s' kohtasi virheen"
#: pkg/menus/edit_menu.go:110
msgid "editor did not exit successfully, aborting: %s"
msgstr ""
"muokkausohjelma kaatui tai se suljettiin virheellisesti, keskeytetään: %s"
#: pkg/sync/workdir/aur_source.go:24
msgid "error downloading sources: %s"
msgstr "lähteiden lataaminen epäonnistui: %s"
#: pkg/download/errors.go:25
msgid "error fetching %s: %s"
msgstr "kohteen %s noutaminen epäonnistui: %s"
#: pkg/sync/build/errors.go:9
msgid "error installing repo packages"
msgstr "tietovarastopakettien asentaminen epäonnistui"
#: pkg/sync/build/installer.go:266 pkg/sync/build/installer.go:270
msgid "error installing:"
msgstr "seuraavan(ien) asentaminen epäonnistui:"
#: pkg/sync/build/installer.go:233 pkg/sync/build/installer.go:237
msgid "error making: %s"
msgstr "seuraavan(ien) kääntäminen epäonnistui: %s"
#: pkg/sync/workdir/merge.go:24
msgid "error merging %s: %s"
msgstr "kohteiden %s yhdistäminen epäonnistui: %s"
#: pkg/download/unified.go:59
msgid "error reading %s"
msgstr "kohdetta %s ei voinut lukea"
#: sync.go:36
msgid "error refreshing databases"
msgstr "tietokantojen päivittäminen epäonnistui"
#: pkg/sync/workdir/clean.go:51 pkg/sync/workdir/merge.go:17
msgid "error resetting %s: %s"
msgstr "kohteen %s nollaaminen epäonnistui: %s"
#: pkg/sync/build/errors.go:53
msgid "error updating package install reason to %s"
msgstr "paketin päivittäminen epäonnistui; riippuvuus/nimellä pyydetty: %s"
#: pkg/sync/build/errors.go:48
msgid "explicit"
msgstr "nimellä pyydetty"
#: pkg/settings/errors.go:23
msgid "failed to create directory '%s': %s"
msgstr "hakemistoa '%s' ei voitu luoda: %s"
#: pkg/settings/config.go:281
msgid "failed to open config file '%s': %s"
msgstr "asetustiedostoa '%s' ei voitu lukea: %s"
#: pkg/sync/srcinfo/service.go:114
msgid "failed to parse %s -- skipping: %s"
msgstr "kohteen '%s' jäsentäminen epäonnistui ja se jätetään välistä: %s"
#: pkg/sync/srcinfo/service.go:118
msgid "failed to parse %s: %s"
msgstr "kohteen '%s' jäsentäminen epäonnistui: %s"
#: local_install.go:77
msgid "failed to parse .SRCINFO"
msgstr ".SRCINFOn jäsentäminen epäonnistui"
#: pkg/settings/config.go:291
msgid "failed to read config file '%s': %s"
msgstr "asetustiedostoa '%s' ei voitu lukea: %s"
#: pkg/cmd/graph/main.go:46 pkg/runtime/runtime.go:73
msgid "failed to retrieve aur Cache"
msgstr "AUR-välimuistia ei voitu noutaa"
#: pkg/upgrade/sources.go:27
msgid "ignoring package devel upgrade (no AUR info found):"
msgstr "paketin kehityspäivitys jätetään huomiotta (AUR-tietoja ei löytynyt):"
#: pkg/text/errors.go:8
msgid "input too long"
msgstr "syöte on liian pitkä"
#: pkg/db/ialpm/alpm.go:222 pkg/dep/dep_graph.go:761
msgid "invalid number: %s"
msgstr "numero ei kelpaa: %s"
#: pkg/settings/parser/parser.go:174
msgid "invalid option '%s'"
msgstr "valinta ei kelpaa: '%s'"
#: cmd.go:197
msgid "invalid option: '--deps' and '--explicit' may not be used together"
msgstr ""
"valinta ei kelpaa: valintoja '--deps' ja '--explicit' ei voi tehdä samaan "
"aikaan"
#: pkg/download/abs.go:22
msgid "invalid repository"
msgstr "tietovarasto ei kelpaa"
#: pkg/db/ialpm/alpm.go:227 pkg/dep/dep_graph.go:767
msgid "invalid value: %d is not between %d and %d"
msgstr "arvo ei kelpaa: %d ei ole %d:n ja %d:n välillä"
#: pkg/text/input.go:48
msgid "no"
msgstr "ei"
#: pkg/sync/srcinfo/pgp/keys.go:110
msgid "no keys to import"
msgstr "ei tuotavia avaimia"
#: pkg/query/errors.go:20
msgid "no query was executed"
msgstr "yhtään kyselyä ei toteutettu"
#: local_install.go:66
msgid "no target directories specified"
msgstr "yhtään kohdehakemistoa ei määritetty"
#: pkg/sync/build/installer.go:242
msgid "nothing to install for %s"
msgstr "ei asennettavaa valinnalla(oilla) %s"
#: pkg/settings/parser/parser.go:164
msgid "only one operation may be used at a time"
msgstr "toimenpiteitä voi käyttää vain yhtä kerrallaan"
#: pkg/cmd/graph/main.go:70
msgid "only one target is allowed"
msgstr "vain yksi kohde sallitaan"
#: pkg/upgrade/service.go:291
msgid "package"
msgid_plural "packages"
msgstr[0] "paketit"
msgstr[1] "paketit"
#: print.go:187
msgid "package '%s' was not found"
msgstr "pakettia '%s' ei löytynyt"
#: pkg/download/errors.go:15
msgid "package not found in AUR"
msgstr "pakettia ei löytynyt AUR:ista"
#: pkg/download/abs.go:23
msgid "package not found in repos"
msgstr "pakettia ei löytynyt tietovarastoista"
#: pkg/sync/srcinfo/pgp/keys.go:100
msgid "problem importing keys"
msgstr "avainten tuomisessa esiintyi ongelma"
#: clean.go:105
msgid "removing AUR packages from cache..."
msgstr "poistetaan AUR-paketit välimuistista..."
#: clean.go:178 pkg/sync/workdir/clean.go:41
msgid "removing untracked AUR files from cache..."
msgstr "poistetaan seuraamattomat AUR-tiedostot välimuistista..."
#: pkg/sync/build/errors.go:38
msgid "the PKGDEST for %s is listed by makepkg but does not exist: %s"
msgstr ""
"paketin %s PKGDEST on makepkg:n listalla mutta sitä ei ole olemassa: %s"
#: pkg/sync/sync.go:45
msgid "there is nothing to do"
msgstr "ei tehtävää"
#: pkg/db/ialpm/alpm.go:247
msgid "unable to CreateHandle: %s"
msgstr "käsittelyn luonti (CreateHandle) epäonnistui: %s"
#: cmd.go:186
msgid "unhandled operation"
msgstr "käsittelemätön toimenpide"
#: cmd.go:450
msgid "unknown-version"
msgstr "tuntematon versio"
#: pkg/text/input.go:47
msgid "yes"
msgstr "kyllä"
================================================
FILE: po/fr.po
================================================
# Translators:
# Benjamin Chenebault, 2023
# anthony tonitch , 2024
# Edgar Fournival, 2024
# J G, 2024
# Sylvain Bx, 2024
# Mathias Brugger, 2024
# Léane GRASSER, 2024
# ariasuni , 2026
#
msgid ""
msgstr ""
"Last-Translator: ariasuni , 2026\n"
"Language-Team: French (https://app.transifex.com/yay-1/teams/123732/fr/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: fr\n"
"Plural-Forms: nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n"
"X-Generator: xgotext\n"
#: clean.go:83
msgid ""
"\n"
"Build directory:"
msgstr ""
"\n"
"Répertoire de compilation :"
#: pkg/db/ialpm/alpm.go:201 pkg/dep/dep_graph.go:740
msgid ""
"\n"
"Enter a number (default=1): "
msgstr ""
"\n"
"Saisissez un nombre (valeur par défaut : 1) :"
#: pkg/menus/menu.go:32
msgid " (Build Files Exist)"
msgstr " (Les fichiers de compilation existent)"
#: pkg/menus/menu.go:27
msgid " (Installed)"
msgstr " (Installé)"
#: cmd.go:453
msgid " [Installed]"
msgstr " [Installé]"
#: cmd.go:410 vote.go:36
msgid " there is nothing to do"
msgstr " il n'y a rien à faire"
#: pkg/menus/menu.go:49
msgid "%s [A]ll [Ab]ort [I]nstalled [No]tInstalled or (1 2 3, 1-3, ^4)"
msgstr "%s [A]Tous [Ab]Annuler [I]nstallés [No]nInstallés ou (1 2 3, 1-3, ^4)"
#: pkg/sync/build/installer.go:308
msgid "%s already made -- skipping build"
msgstr "%s déjà créé -- compilation ignorée"
#: pkg/menus/edit_menu.go:57
msgid "%s is not set"
msgstr "%s n'est pas définie"
#: pkg/settings/exe/cmd_builder.go:257
msgid "%s is present."
msgstr "%s existe."
#: pkg/dep/dep_graph.go:460 pkg/sync/build/installer.go:305
msgid "%s is up to date -- skipping"
msgstr "%s est à jour -- ignoré"
#: pkg/upgrade/service.go:291
msgid "%s to upgrade/install."
msgstr "%s à mettre à jour ou à installer."
#: pkg/upgrade/service.go:285
msgid "%s will also be installed for this operation."
msgstr "%s à installer pour effectuer cette opération."
#: pkg/sync/srcinfo/pgp/keys.go:124
msgid "%s, required by: %s"
msgstr "%s, nécessaire pour : %s"
#: pkg/menus/diff_menu.go:49
msgid "%s: No changes -- skipping"
msgstr "%s : Aucun changement -- ignoré"
#: pkg/query/filter.go:22
msgid "%s: can't use target with option --aur -- skipping"
msgstr "%s : la cible est incompatible avec l'option --aur -- ignoré"
#: pkg/query/filter.go:17
msgid "%s: can't use target with option --repo -- skipping"
msgstr "%s : la cible est incompatible avec l'option --repo -- ignoré"
#: pkg/upgrade/sources.go:57
msgid "%s: ignoring package upgrade (%s => %s)"
msgstr "%s : mise à jour du paquet ignorée (%s => %s)"
#: pkg/query/aur_warnings.go:46
msgid "%s: local (%s) is newer than AUR (%s)"
msgstr ""
"%s : le paquet local (%s) est plus récent que le paquet disponible sur l'AUR"
" (%s)"
#: vote.go:51
msgid ""
"%s: please set AUR_USERNAME and AUR_PASSWORD environment variables for "
"voting"
msgstr ""
"%s : définissez les variables d'environnement AUR_USERNAME et AUR_PASSWORD "
"avant de pouvoir voter"
#: pkg/download/unified.go:192
msgid "(%d/%d) Downloaded PKGBUILD from ABS: %s"
msgstr "(%d/%d) PKGBUILD téléchargé depuis l'ABS : %s"
#: pkg/download/aur.go:92 pkg/download/unified.go:188
msgid "(%d/%d) Downloaded PKGBUILD: %s"
msgstr "(%d/%d) PKGBUILD téléchargé : %s"
#: pkg/download/aur.go:82
msgid "(%d/%d) Failed to download PKGBUILD: %s"
msgstr "(%d/%d) Échec du téléchargement du PKGBUILD : %s"
#: pkg/sync/srcinfo/service.go:109
msgid "(%d/%d) Parsing SRCINFO: %s"
msgstr "(%d/%d) Analyse du SRCINFO : %s"
#: pkg/query/types.go:103 pkg/query/types.go:72
msgid "(Installed)"
msgstr "(Installé)"
#: pkg/query/types.go:101 pkg/query/types.go:70
msgid "(Installed: %s)"
msgstr "(Installé : %s)"
#: pkg/query/types.go:61
msgid "(Orphaned)"
msgstr "(Orphelin)"
#: pkg/query/types.go:65
msgid "(Out-of-date: %s)"
msgstr "(Obsolète : %s)"
#: pkg/dep/dep_graph.go:75
msgid "AUR"
msgstr "AUR"
#: print.go:44
msgid "AUR URL"
msgstr "URL sur l'AUR"
#: pkg/menus/edit_menu.go:58
msgid "Add %s or %s to your environment variables"
msgstr "Ajoutez %s ou %s à vos variables d'environnement"
#: main.go:60
msgid "Avoid running yay as root/sudo."
msgstr "Évitez d'exécuter yay en tant que root ou via sudo."
#: pkg/dep/dep_graph.go:63
msgid "Check Dependency"
msgstr "Dépendance de vérification"
#: print.go:41
msgid "Check Deps"
msgstr "Dépendances vérif."
#: pkg/upgrade/service.go:89
msgid "Checking development packages..."
msgstr "Vérification des paquets de développement…"
#: pkg/sync/workdir/clean.go:45
msgid "Cleaning (%d/%d): %s"
msgstr "Nettoyage (%d/%d) : %s"
#: print.go:42
msgid "Conflicts With"
msgstr "Est en conflit avec"
#: pkg/menus/clean_menu.go:62
msgid "Deleting (%d/%d): %s"
msgstr "Suppression (%d/%d) : %s"
#: pkg/dep/dep_graph.go:61
msgid "Dependency"
msgstr "Dépendance"
#: print.go:38
msgid "Depends On"
msgstr "Dépend de"
#: print.go:33
msgid "Description"
msgstr "Description"
#: pkg/menus/diff_menu.go:160
msgid "Diffs to show?"
msgstr "Diffs à afficher ?"
#: pkg/settings/migrations.go:25
msgid "Disable 'provides' setting by default"
msgstr "Désactiver par défaut le paramètre \"provides\""
#: clean.go:78
msgid "Do you want to remove ALL AUR packages from cache?"
msgstr "Voulez-vous supprimer TOUS les paquets de l'AUR dans le cache ?"
#: clean.go:95
msgid "Do you want to remove ALL untracked AUR files?"
msgstr ""
"Voulez-vous supprimer TOUS les fichiers non suivis des paquets de l'AUR ?"
#: clean.go:80
msgid "Do you want to remove all other AUR packages from cache?"
msgstr ""
"Voulez-vous supprimer tous les autres paquets de l'AUR dans le cache ?"
#: pkg/menus/edit_menu.go:61
msgid "Edit PKGBUILD with?"
msgstr "Modifier le PKGBUILD avec ?"
#: pkg/query/errors.go:13
msgid "Error during AUR search: %s\n"
msgstr "Erreur lors de la recherche dans l'AUR : %s\n"
#: pkg/upgrade/service.go:295
msgid "Excluding packages may cause partial upgrades and break systems"
msgstr ""
"L'exclusion de paquets peut mener à une mise à jour partielle de votre "
"système et pourrait le casser"
#: pkg/dep/dep_graph.go:60
msgid "Explicit"
msgstr "Explicite"
#: print.go:91
msgid "Explicitly installed packages: %s"
msgstr "Paquets installés explicitement : %s"
#: pkg/dep/dep_graph.go:437 pkg/dep/dep_graph.go:535
msgid "Failed to find AUR package for"
msgstr "Impossible de trouver un paquet sur l'AUR correspondant à"
#: pkg/sync/build/installer.go:120
msgid "Failed to install layer, rolling up to next layer."
msgstr "Échec de l'installation de la couche, passage à la couche suivante."
#: pkg/sync/build/errors.go:16
msgid ""
"Failed to install the following packages. Manual intervention is required:"
msgstr ""
"Échec de l'installation des paquets suivants. Une intervention manuelle est "
"requise :"
#: print.go:45
msgid "First Submitted"
msgstr "Envoyé le"
#: pkg/query/aur_warnings.go:83
msgid "Flagged Out Of Date AUR Packages:"
msgstr "Paquets de l'AUR marqués comme obsolètes :"
#: print.go:90
msgid "Foreign installed packages: %s"
msgstr "Paquets étrangers installés : %s"
#: pkg/vcs/vcs.go:144
msgid "Found git repo: %s"
msgstr "Dépôt Git trouvé : %s"
#: vcs.go:72
msgid "GenDB finished. No packages were installed"
msgstr "GenDB terminé. Aucun paquet n'a été installé"
#: print.go:36
msgid "Groups"
msgstr "Groupes"
#: pkg/sync/srcinfo/pgp/keys.go:88
msgid "Import?"
msgstr "Importer ?"
#: pkg/sync/srcinfo/pgp/keys.go:97
msgid "Importing keys with gpg..."
msgstr "Importation des clés avec gpg…"
#: print.go:46
msgid "Keywords"
msgstr "Mots-clés"
#: print.go:47
msgid "Last Modified"
msgstr "Modifié le"
#: print.go:35
msgid "Licenses"
msgstr "Licences"
#: pkg/dep/dep_graph.go:77
msgid "Local"
msgstr "Local"
#: print.go:48
msgid "Maintainer"
msgstr "Mainteneur"
#: pkg/dep/dep_graph.go:62
msgid "Make Dependency"
msgstr "Dépendance de création"
#: print.go:40
msgid "Make Deps"
msgstr "Dépendances créat."
#: pkg/dep/dep_graph.go:79
msgid "Missing"
msgstr "Manquant"
#: pkg/query/aur_warnings.go:75
msgid "Missing AUR Debug Packages:"
msgstr "Paquets de débogage de l'AUR manquants :"
#: print.go:31
msgid "Name"
msgstr "Nom"
#: pkg/dep/dep_graph.go:442 pkg/dep/dep_graph.go:564
msgid "No AUR package found for"
msgstr "Aucun paquet trouvé sur l'AUR correspondant à"
#: pkg/dep/dep_graph.go:564
msgid "required by"
msgstr "requis par"
#: pkg/dep/dep_graph.go:182
msgid "No package found for"
msgstr "Aucun paquet trouvé correspondant à"
#: print.go:225
msgid "None"
msgstr "--"
#: print.go:39
msgid "Optional Deps"
msgstr "Dépendances opt."
#: pkg/query/aur_warnings.go:79
msgid "Orphan (unmaintained) AUR Packages:"
msgstr "Paquets de l'AUR orphelins (non maintenus) :"
#: print.go:53 print.go:55
msgid "Out-of-date"
msgstr "Obsolète"
#: pkg/sync/srcinfo/pgp/keys.go:115
msgid "PGP keys need importing:"
msgstr "Des clés PGP doivent être importées :"
#: pkg/sync/workdir/preparer.go:252
msgid "PKGBUILD up to date, skipping download: %s"
msgstr "PKGBUILD à jour, téléchargement ignoré : %s"
#: pkg/menus/edit_menu.go:130
msgid "PKGBUILDs to edit?"
msgstr "PKGBUILDs à modifier ?"
#: print.go:61
msgid "Package Base"
msgstr "Paquet base"
#: print.go:60
msgid "Package Base ID"
msgstr "ID du paquet base"
#: pkg/query/aur_warnings.go:71
msgid "Packages not in AUR:"
msgstr "Paquets absents de l'AUR :"
#: pkg/menus/clean_menu.go:54
msgid "Packages to cleanBuild?"
msgstr "Paquets à cleanBuild ?"
#: pkg/dep/dep_graph.go:202
msgid "Packages to exclude"
msgstr "Paquets à exclure"
#: pkg/upgrade/service.go:294
msgid "Packages to exclude: (eg: \"1 2 3\", \"1-3\", \"^4\" or repo name)"
msgstr "Paquets à exclure : (ex. « 1 2 3 », « 1-3 », « ^4 » ou noms de dépôt)"
#: cmd.go:392
msgid "Packages to install (eg: 1 2 3, 1-3 or ^4)"
msgstr "Paquets à installer (ex. 1 2 3, 1-3 ou ^4)"
#: print.go:49
msgid "Popularity"
msgstr "Popularité"
#: pkg/menus/diff_menu.go:172 pkg/menus/edit_menu.go:143
msgid "Proceed with install?"
msgstr "Procéder à l'installation ?"
#: print.go:37
msgid "Provides"
msgstr "Fournit"
#: pkg/sync/workdir/preparer.go:125
msgid "Remove make dependencies after install?"
msgstr "Supprimer les dépendances de création après l'installation ?"
#: print.go:43
msgid "Replaces"
msgstr "Remplace"
#: pkg/db/ialpm/alpm.go:191 print.go:30
msgid "Repository"
msgstr "Dépôt"
#: pkg/dep/dep_graph.go:730
msgid "Repository AUR"
msgstr "Dépôt AUR"
#: pkg/dep/dep_graph.go:78
msgid "SRCINFO"
msgstr "SRCINFO"
#: pkg/upgrade/service.go:71
msgid "Searching AUR for updates..."
msgstr "Recherche des mises à jour sur l'AUR…"
#: pkg/upgrade/service.go:159
msgid "Searching databases for updates..."
msgstr "Recherche des mises à jour dans les bases de données…"
#: pkg/query/query_builder.go:214
msgid "Showing repo packages only"
msgstr "Affichage uniquement des paquets provenant des dépôts"
#: print.go:95
msgid "Size of pacman cache %s: %s"
msgstr "Taille du cache de pacman %s : %s"
#: print.go:98
msgid "Size of yay cache %s: %s"
msgstr "Taille du cache de yay %s : %s"
#: print.go:62
msgid "Snapshot URL"
msgstr "URL de l'instantané"
#: pkg/dep/dep_graph.go:76
msgid "Sync"
msgstr "Sync"
#: print.go:100
msgid "Ten biggest packages:"
msgstr "Les dix paquets les plus lourds :"
#: pkg/sync/sync.go:124
msgid "The following packages are not compatible with your architecture:"
msgstr "Les paquets suivants sont incompatibles avec votre architecture :"
#: pkg/db/ialpm/alpm.go:179 pkg/dep/dep_graph.go:726
msgid "There are %[1]d providers available for %[2]s:"
msgstr "%[1]d paquets fournissant %[2]s sont disponibles :"
#: pkg/settings/exe/cmd_builder.go:258
msgid "There may be another Pacman instance running. Waiting..."
msgstr ""
"Il semblerait qu'une autre instance de Pacman soit en cours d'exécution. En "
"attente…"
#: print.go:92
msgid "Total Size occupied by packages: %s"
msgstr "Taille totale occupée par les paquets : %s"
#: print.go:89
msgid "Total installed packages: %s"
msgstr "Nombre de paquets installés : %s"
#: pkg/sync/sync.go:132
msgid "Try to build them anyway?"
msgstr "Essayer quand même de les compiler ?"
#: print.go:34
msgid "URL"
msgstr "URL"
#: clean.go:194 pkg/menus/clean_menu.go:65 pkg/menus/clean_menu.go:71
msgid "Unable to clean:"
msgstr "Impossible de nettoyer :"
#: get.go:42 get.go:74
msgid "Unable to find the following packages:"
msgstr "Impossible de trouver les paquets suivants :"
#: vote.go:20
msgid "Unable to handle package vote for: %s. err: %s"
msgstr "Impossible de gérer le vote pour : %s. Erreur : %s"
#: clean.go:170
msgid "Unable to remove %s: %s"
msgstr "Impossible de supprimer %s : %s"
#: print.go:32
msgid "Version"
msgstr "Version"
#: print.go:50
msgid "Votes"
msgstr "Votes"
#: print.go:87
msgid "Yay version v%s"
msgstr "Yay version v%s"
#: pkg/menus/menu.go:49
msgid "[N]one"
msgstr "[N]Aucun"
#: pkg/settings/errors.go:29
msgid "aborting due to user"
msgstr "abandon dû à l'utilisateur"
#: pkg/settings/parser/parser.go:608
msgid "argument '-' specified without input on stdin"
msgstr "argument '-' spécifié et aucune entrée dans stdin"
#: local_install.go:26
msgid "cannot find PKGBUILD and .SRCINFO in directory"
msgstr ""
"impossible de trouver les fichiers PKGBUILD et .SRCINFO dans le répertoire"
#: pkg/sync/build/pkg_archive.go:148
msgid "cannot find package name: %v"
msgstr "impossible de trouver le nom de paquet : %v"
#: pkg/sync/build/errors.go:30
msgid "could not find PKGDEST for: %s"
msgstr "impossible de trouver le PKGDEST pour : %s"
#: errors.go:9
msgid "could not find all required packages"
msgstr "impossible de trouver tous les paquets requis"
#: pkg/sync/build/errors.go:61
msgid "could not find any package archives listed in %s"
msgstr "aucune archive de paquet trouvée parmi celles listées dans %s"
#: pkg/sync/build/errors.go:50 pkg/upgrade/service.go:286
msgid "dependency"
msgstr "dépendance"
#: pkg/vcs/vcs.go:100 pkg/vcs/vcs.go:96
msgid "devel check for package failed: '%s' encountered an error"
msgstr ""
"échec de la vérification du paquet de développement : '%s' a rencontré une "
"erreur"
#: pkg/menus/edit_menu.go:110
msgid "editor did not exit successfully, aborting: %s"
msgstr "l'éditeur ne s'est pas terminé avec succès, abandon : %s"
#: pkg/sync/workdir/aur_source.go:24
msgid "error downloading sources: %s"
msgstr "erreur lors du téléchargement des sources : %s"
#: pkg/download/errors.go:25
msgid "error fetching %s: %s"
msgstr "erreur lors de la récupération de %s : %s"
#: pkg/sync/build/errors.go:9
msgid "error installing repo packages"
msgstr "erreur lors de l'installation des paquets provenant des dépôts"
#: pkg/sync/build/installer.go:266 pkg/sync/build/installer.go:270
msgid "error installing:"
msgstr "erreur lors de l'installation de :"
#: pkg/sync/build/installer.go:233 pkg/sync/build/installer.go:237
msgid "error making: %s"
msgstr "erreur lors de la compilation de : %s"
#: pkg/sync/workdir/merge.go:24
msgid "error merging %s: %s"
msgstr "erreur lors de la fusion (%s) : %s"
#: pkg/download/unified.go:59
msgid "error reading %s"
msgstr "erreur lors de la lecture de %s"
#: sync.go:36
msgid "error refreshing databases"
msgstr "erreur lors de l'actualisation des bases de données"
#: pkg/sync/workdir/clean.go:51 pkg/sync/workdir/merge.go:17
msgid "error resetting %s: %s"
msgstr "erreur lors de la réinitialisation de %s : %s"
#: pkg/sync/build/errors.go:53
msgid "error updating package install reason to %s"
msgstr ""
"erreur lors de la modification du motif d'installation du paquet en « %s »"
#: pkg/sync/build/errors.go:48
msgid "explicit"
msgstr "explicite"
#: pkg/settings/errors.go:23
msgid "failed to create directory '%s': %s"
msgstr "échec de la création du répertoire '%s' : %s"
#: pkg/settings/config.go:281
msgid "failed to open config file '%s': %s"
msgstr "échec de l'ouverture du fichier de configuration '%s' : %s"
#: pkg/sync/srcinfo/service.go:114
msgid "failed to parse %s -- skipping: %s"
msgstr "échec de l'analyse de %s -- ignoré : %s"
#: pkg/sync/srcinfo/service.go:118
msgid "failed to parse %s: %s"
msgstr "échec de l'analyse de %s : %s"
#: local_install.go:77
msgid "failed to parse .SRCINFO"
msgstr "échec de l'analyse du .SRCINFO"
#: pkg/settings/config.go:291
msgid "failed to read config file '%s': %s"
msgstr "échec de la lecture du fichier de configuration '%s' : %s"
#: pkg/cmd/graph/main.go:46 pkg/runtime/runtime.go:73
msgid "failed to retrieve aur Cache"
msgstr "échec de la récupération du cache AUR"
#: pkg/upgrade/sources.go:27
msgid "ignoring package devel upgrade (no AUR info found):"
msgstr ""
"mise à jour du paquet de développement ignorée (infos AUR introuvables) :"
#: pkg/text/errors.go:8
msgid "input too long"
msgstr "entrée trop longue"
#: pkg/db/ialpm/alpm.go:222 pkg/dep/dep_graph.go:761
msgid "invalid number: %s"
msgstr "nombre invalide : %s"
#: pkg/settings/parser/parser.go:174
msgid "invalid option '%s'"
msgstr "option '%s' invalide"
#: cmd.go:197
msgid "invalid option: '--deps' and '--explicit' may not be used together"
msgstr ""
"option invalide : '--deps' et '--explicit' ne peuvent pas être utilisés "
"ensemble"
#: pkg/download/abs.go:22
msgid "invalid repository"
msgstr "dépôt invalide"
#: pkg/db/ialpm/alpm.go:227 pkg/dep/dep_graph.go:767
msgid "invalid value: %d is not between %d and %d"
msgstr "valeur invalide : %d n'est pas entre %d et %d"
#: pkg/text/input.go:48
msgid "no"
msgstr "non"
#: pkg/sync/srcinfo/pgp/keys.go:110
msgid "no keys to import"
msgstr "aucune clé à importer"
#: pkg/query/errors.go:20
msgid "no query was executed"
msgstr "aucune requête n'a été exécutée"
#: local_install.go:66
msgid "no target directories specified"
msgstr "aucun répertoire cible spécifié"
#: pkg/sync/build/installer.go:242
msgid "nothing to install for %s"
msgstr "rien à installer pour %s"
#: pkg/settings/parser/parser.go:164
msgid "only one operation may be used at a time"
msgstr "une seule opération peut être réalisée à la fois"
#: pkg/cmd/graph/main.go:70
msgid "only one target is allowed"
msgstr "une seule cible est autorisée"
#: pkg/upgrade/service.go:291
msgid "package"
msgid_plural "packages"
msgstr[0] "paquet"
msgstr[1] "paquets"
msgstr[2] "paquets"
#: print.go:187
msgid "package '%s' was not found"
msgstr "le paquet '%s' est introuvable"
#: pkg/download/errors.go:15
msgid "package not found in AUR"
msgstr "paquet introuvable dans l'AUR"
#: pkg/download/abs.go:23
msgid "package not found in repos"
msgstr "paquet introuvable dans les dépôts"
#: pkg/sync/srcinfo/pgp/keys.go:100
msgid "problem importing keys"
msgstr "erreur lors de l'importation des clés"
#: clean.go:105
msgid "removing AUR packages from cache..."
msgstr "suppression des paquets de l'AUR du cache…"
#: clean.go:178 pkg/sync/workdir/clean.go:41
msgid "removing untracked AUR files from cache..."
msgstr ""
"suppression des fichiers non suivis dans les paquets de l'AUR en cache…"
#: pkg/sync/build/errors.go:38
msgid "the PKGDEST for %s is listed by makepkg but does not exist: %s"
msgstr "le PKGDEST pour %s est listé par makepkg mais n'existe pas : %s"
#: pkg/sync/sync.go:45
msgid "there is nothing to do"
msgstr "il n'y a rien à faire"
#: pkg/db/ialpm/alpm.go:247
msgid "unable to CreateHandle: %s"
msgstr "impossible de CreateHandle : %s"
#: cmd.go:186
msgid "unhandled operation"
msgstr "opération non gérée"
#: cmd.go:450
msgid "unknown-version"
msgstr "version-inconnue"
#: pkg/text/input.go:47
msgid "yes"
msgstr "oui"
================================================
FILE: po/fr_FR.po
================================================
# Translators:
# J G, 2021
# Mr Strik3, 2022
# Maxime Demolin, 2023
# Mathias Brugger, 2023
# Sylvain Bx, 2023
# Bertrand Junqua, 2024
# Barsanuphe, 2024
# Léane GRASSER, 2024
# ariasuni , 2026
#
msgid ""
msgstr ""
"Last-Translator: ariasuni , 2026\n"
"Language-Team: French (France) (https://app.transifex.com/yay-1/teams/123732/fr_FR/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: fr_FR\n"
"Plural-Forms: nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n"
"X-Generator: xgotext\n"
#: clean.go:83
msgid ""
"\n"
"Build directory:"
msgstr ""
"\n"
"Répertoire de compilation :"
#: pkg/db/ialpm/alpm.go:201 pkg/dep/dep_graph.go:740
msgid ""
"\n"
"Enter a number (default=1): "
msgstr ""
"\n"
"Saisissez un nombre (valeur par défaut : 1) :"
#: pkg/menus/menu.go:32
msgid " (Build Files Exist)"
msgstr " (Les fichiers de compilation existent)"
#: pkg/menus/menu.go:27
msgid " (Installed)"
msgstr " (Installé)"
#: cmd.go:453
msgid " [Installed]"
msgstr " [Installé]"
#: cmd.go:410 vote.go:36
msgid " there is nothing to do"
msgstr " il n'y a rien à faire"
#: pkg/menus/menu.go:49
msgid "%s [A]ll [Ab]ort [I]nstalled [No]tInstalled or (1 2 3, 1-3, ^4)"
msgstr "%s [A]Tous [Ab]Annuler [I]nstallés [No]nInstallés ou (1 2 3, 1-3, ^4)"
#: pkg/sync/build/installer.go:308
msgid "%s already made -- skipping build"
msgstr "%s déjà créé -- compilation ignorée"
#: pkg/menus/edit_menu.go:57
msgid "%s is not set"
msgstr "%s n'est pas définie"
#: pkg/settings/exe/cmd_builder.go:257
msgid "%s is present."
msgstr "%s existe."
#: pkg/dep/dep_graph.go:460 pkg/sync/build/installer.go:305
msgid "%s is up to date -- skipping"
msgstr "%s est à jour -- ignoré"
#: pkg/upgrade/service.go:291
msgid "%s to upgrade/install."
msgstr "%s à mettre à jour ou à installer."
#: pkg/upgrade/service.go:285
msgid "%s will also be installed for this operation."
msgstr "%s à installer pour effectuer cette opération."
#: pkg/sync/srcinfo/pgp/keys.go:124
msgid "%s, required by: %s"
msgstr "%s, nécessaire pour : %s"
#: pkg/menus/diff_menu.go:49
msgid "%s: No changes -- skipping"
msgstr "%s : Aucun changement -- ignoré"
#: pkg/query/filter.go:22
msgid "%s: can't use target with option --aur -- skipping"
msgstr "%s : la cible est incompatible avec l'option --aur -- ignoré"
#: pkg/query/filter.go:17
msgid "%s: can't use target with option --repo -- skipping"
msgstr "%s : la cible est incompatible avec l'option --repo -- ignoré"
#: pkg/upgrade/sources.go:57
msgid "%s: ignoring package upgrade (%s => %s)"
msgstr "%s : mise à jour du paquet ignorée (%s => %s)"
#: pkg/query/aur_warnings.go:46
msgid "%s: local (%s) is newer than AUR (%s)"
msgstr ""
"%s : le paquet local (%s) est plus récent que le paquet disponible sur l'AUR"
" (%s)"
#: vote.go:51
msgid ""
"%s: please set AUR_USERNAME and AUR_PASSWORD environment variables for "
"voting"
msgstr ""
"%s : définissez les variables d'environnement AUR_USERNAME et AUR_PASSWORD "
"avant de pouvoir voter"
#: pkg/download/unified.go:192
msgid "(%d/%d) Downloaded PKGBUILD from ABS: %s"
msgstr "(%d/%d) PKGBUILD téléchargé depuis l'ABS : %s"
#: pkg/download/aur.go:92 pkg/download/unified.go:188
msgid "(%d/%d) Downloaded PKGBUILD: %s"
msgstr "(%d/%d) PKGBUILD téléchargé : %s"
#: pkg/download/aur.go:82
msgid "(%d/%d) Failed to download PKGBUILD: %s"
msgstr "(%d/%d) Échec du téléchargement du PKGBUILD : %s"
#: pkg/sync/srcinfo/service.go:109
msgid "(%d/%d) Parsing SRCINFO: %s"
msgstr "(%d/%d) Analyse du SRCINFO : %s"
#: pkg/query/types.go:103 pkg/query/types.go:72
msgid "(Installed)"
msgstr "(Installé)"
#: pkg/query/types.go:101 pkg/query/types.go:70
msgid "(Installed: %s)"
msgstr "(Installé : %s)"
#: pkg/query/types.go:61
msgid "(Orphaned)"
msgstr "(Orphelin)"
#: pkg/query/types.go:65
msgid "(Out-of-date: %s)"
msgstr "(Obsolète : %s)"
#: pkg/dep/dep_graph.go:75
msgid "AUR"
msgstr "AUR"
#: print.go:44
msgid "AUR URL"
msgstr "URL sur l'AUR"
#: pkg/menus/edit_menu.go:58
msgid "Add %s or %s to your environment variables"
msgstr "Ajoutez %s ou %s à vos variables d'environnement"
#: main.go:60
msgid "Avoid running yay as root/sudo."
msgstr "Évitez d'exécuter yay en tant que root ou via sudo."
#: pkg/dep/dep_graph.go:63
msgid "Check Dependency"
msgstr "Dépendance de vérification"
#: print.go:41
msgid "Check Deps"
msgstr "Dépendances vérif."
#: pkg/upgrade/service.go:89
msgid "Checking development packages..."
msgstr "Vérification des paquets de développement…"
#: pkg/sync/workdir/clean.go:45
msgid "Cleaning (%d/%d): %s"
msgstr "Nettoyage (%d/%d) : %s"
#: print.go:42
msgid "Conflicts With"
msgstr "Est en conflit avec"
#: pkg/menus/clean_menu.go:62
msgid "Deleting (%d/%d): %s"
msgstr "Suppression (%d/%d) : %s"
#: pkg/dep/dep_graph.go:61
msgid "Dependency"
msgstr "Dépendance"
#: print.go:38
msgid "Depends On"
msgstr "Dépend de"
#: print.go:33
msgid "Description"
msgstr "Description"
#: pkg/menus/diff_menu.go:160
msgid "Diffs to show?"
msgstr "Diffs à afficher ?"
#: pkg/settings/migrations.go:25
msgid "Disable 'provides' setting by default"
msgstr "Désactiver par défaut le paramètre \"provides\""
#: clean.go:78
msgid "Do you want to remove ALL AUR packages from cache?"
msgstr "Voulez-vous supprimer TOUS les paquets de l'AUR dans le cache ?"
#: clean.go:95
msgid "Do you want to remove ALL untracked AUR files?"
msgstr ""
"Voulez-vous supprimer TOUS les fichiers non suivis des paquets de l'AUR ?"
#: clean.go:80
msgid "Do you want to remove all other AUR packages from cache?"
msgstr ""
"Voulez-vous supprimer tous les autres paquets de l'AUR dans le cache ?"
#: pkg/menus/edit_menu.go:61
msgid "Edit PKGBUILD with?"
msgstr "Modifier le PKGBUILD avec ?"
#: pkg/query/errors.go:13
msgid "Error during AUR search: %s\n"
msgstr "Erreur lors de la recherche dans l'AUR : %s\n"
#: pkg/upgrade/service.go:295
msgid "Excluding packages may cause partial upgrades and break systems"
msgstr ""
"L'exclusion de paquets peut mener à une mise à jour partielle de votre "
"système et pourrait le casser"
#: pkg/dep/dep_graph.go:60
msgid "Explicit"
msgstr "Explicite"
#: print.go:91
msgid "Explicitly installed packages: %s"
msgstr "Paquets installés explicitement : %s"
#: pkg/dep/dep_graph.go:437 pkg/dep/dep_graph.go:535
msgid "Failed to find AUR package for"
msgstr "Impossible de trouver un paquet sur l'AUR correspondant à"
#: pkg/sync/build/installer.go:120
msgid "Failed to install layer, rolling up to next layer."
msgstr "Échec de l'installation de la couche, passage à la couche suivante."
#: pkg/sync/build/errors.go:16
msgid ""
"Failed to install the following packages. Manual intervention is required:"
msgstr ""
"Échec de l'installation des paquets suivants. Une intervention manuelle est "
"requise :"
#: print.go:45
msgid "First Submitted"
msgstr "Envoyé le"
#: pkg/query/aur_warnings.go:83
msgid "Flagged Out Of Date AUR Packages:"
msgstr "Paquets de l'AUR marqués comme obsolètes :"
#: print.go:90
msgid "Foreign installed packages: %s"
msgstr "Paquets étrangers installés : %s"
#: pkg/vcs/vcs.go:144
msgid "Found git repo: %s"
msgstr "Dépôt Git trouvé : %s"
#: vcs.go:72
msgid "GenDB finished. No packages were installed"
msgstr "GenDB terminé. Aucun paquet n'a été installé"
#: print.go:36
msgid "Groups"
msgstr "Groupes"
#: pkg/sync/srcinfo/pgp/keys.go:88
msgid "Import?"
msgstr "Importer ?"
#: pkg/sync/srcinfo/pgp/keys.go:97
msgid "Importing keys with gpg..."
msgstr "Importation des clés avec gpg…"
#: print.go:46
msgid "Keywords"
msgstr "Mots-clés"
#: print.go:47
msgid "Last Modified"
msgstr "Modifié le"
#: print.go:35
msgid "Licenses"
msgstr "Licences"
#: pkg/dep/dep_graph.go:77
msgid "Local"
msgstr "Local"
#: print.go:48
msgid "Maintainer"
msgstr "Mainteneur"
#: pkg/dep/dep_graph.go:62
msgid "Make Dependency"
msgstr "Dépendance de création"
#: print.go:40
msgid "Make Deps"
msgstr "Dépendances créat."
#: pkg/dep/dep_graph.go:79
msgid "Missing"
msgstr "Manquant"
#: pkg/query/aur_warnings.go:75
msgid "Missing AUR Debug Packages:"
msgstr "Paquets de débogage de l'AUR manquants :"
#: print.go:31
msgid "Name"
msgstr "Nom"
#: pkg/dep/dep_graph.go:442 pkg/dep/dep_graph.go:564
msgid "No AUR package found for"
msgstr "Aucun paquet trouvé sur l'AUR correspondant à"
#: pkg/dep/dep_graph.go:564
msgid "required by"
msgstr "requis par"
#: pkg/dep/dep_graph.go:182
msgid "No package found for"
msgstr "Aucun paquet trouvé correspondant à"
#: print.go:225
msgid "None"
msgstr "--"
#: print.go:39
msgid "Optional Deps"
msgstr "Dépendances opt."
#: pkg/query/aur_warnings.go:79
msgid "Orphan (unmaintained) AUR Packages:"
msgstr "Paquets de l'AUR orphelins (non maintenus) :"
#: print.go:53 print.go:55
msgid "Out-of-date"
msgstr "Obsolète"
#: pkg/sync/srcinfo/pgp/keys.go:115
msgid "PGP keys need importing:"
msgstr "Des clés PGP doivent être importées :"
#: pkg/sync/workdir/preparer.go:252
msgid "PKGBUILD up to date, skipping download: %s"
msgstr "PKGBUILD à jour, téléchargement ignoré : %s"
#: pkg/menus/edit_menu.go:130
msgid "PKGBUILDs to edit?"
msgstr "PKGBUILDs à modifier ?"
#: print.go:61
msgid "Package Base"
msgstr "Paquet base"
#: print.go:60
msgid "Package Base ID"
msgstr "ID du paquet base"
#: pkg/query/aur_warnings.go:71
msgid "Packages not in AUR:"
msgstr "Paquets absents de l'AUR :"
#: pkg/menus/clean_menu.go:54
msgid "Packages to cleanBuild?"
msgstr "Paquets à cleanBuild ?"
#: pkg/dep/dep_graph.go:202
msgid "Packages to exclude"
msgstr "Paquets à exclure"
#: pkg/upgrade/service.go:294
msgid "Packages to exclude: (eg: \"1 2 3\", \"1-3\", \"^4\" or repo name)"
msgstr "Paquets à exclure : (ex. « 1 2 3 », « 1-3 », « ^4 » ou noms de dépôt)"
#: cmd.go:392
msgid "Packages to install (eg: 1 2 3, 1-3 or ^4)"
msgstr "Paquets à installer (ex. 1 2 3, 1-3 ou ^4)"
#: print.go:49
msgid "Popularity"
msgstr "Popularité"
#: pkg/menus/diff_menu.go:172 pkg/menus/edit_menu.go:143
msgid "Proceed with install?"
msgstr "Procéder à l'installation ?"
#: print.go:37
msgid "Provides"
msgstr "Fournit"
#: pkg/sync/workdir/preparer.go:125
msgid "Remove make dependencies after install?"
msgstr "Supprimer les dépendances de création après l'installation ?"
#: print.go:43
msgid "Replaces"
msgstr "Remplace"
#: pkg/db/ialpm/alpm.go:191 print.go:30
msgid "Repository"
msgstr "Dépôt"
#: pkg/dep/dep_graph.go:730
msgid "Repository AUR"
msgstr "Dépôt AUR"
#: pkg/dep/dep_graph.go:78
msgid "SRCINFO"
msgstr "SRCINFO"
#: pkg/upgrade/service.go:71
msgid "Searching AUR for updates..."
msgstr "Recherche des mises à jour sur l'AUR…"
#: pkg/upgrade/service.go:159
msgid "Searching databases for updates..."
msgstr "Recherche des mises à jour dans les bases de données…"
#: pkg/query/query_builder.go:214
msgid "Showing repo packages only"
msgstr "Affichage uniquement des paquets provenant des dépôts"
#: print.go:95
msgid "Size of pacman cache %s: %s"
msgstr "Taille du cache de pacman %s : %s"
#: print.go:98
msgid "Size of yay cache %s: %s"
msgstr "Taille du cache de yay %s : %s"
#: print.go:62
msgid "Snapshot URL"
msgstr "URL de l'instantané"
#: pkg/dep/dep_graph.go:76
msgid "Sync"
msgstr "Sync"
#: print.go:100
msgid "Ten biggest packages:"
msgstr "Les dix paquets les plus lourds :"
#: pkg/sync/sync.go:124
msgid "The following packages are not compatible with your architecture:"
msgstr "Les paquets suivants sont incompatibles avec votre architecture :"
#: pkg/db/ialpm/alpm.go:179 pkg/dep/dep_graph.go:726
msgid "There are %[1]d providers available for %[2]s:"
msgstr "%[1]d paquets fournissant %[2]s sont disponibles :"
#: pkg/settings/exe/cmd_builder.go:258
msgid "There may be another Pacman instance running. Waiting..."
msgstr ""
"Il semblerait qu'une autre instance de Pacman soit en cours d'exécution. En "
"attente…"
#: print.go:92
msgid "Total Size occupied by packages: %s"
msgstr "Taille totale occupée par les paquets : %s"
#: print.go:89
msgid "Total installed packages: %s"
msgstr "Nombre de paquets installés : %s"
#: pkg/sync/sync.go:132
msgid "Try to build them anyway?"
msgstr "Essayer quand même de les compiler ?"
#: print.go:34
msgid "URL"
msgstr "URL"
#: clean.go:194 pkg/menus/clean_menu.go:65 pkg/menus/clean_menu.go:71
msgid "Unable to clean:"
msgstr "Impossible de nettoyer :"
#: get.go:42 get.go:74
msgid "Unable to find the following packages:"
msgstr "Impossible de trouver les paquets suivants :"
#: vote.go:20
msgid "Unable to handle package vote for: %s. err: %s"
msgstr "Impossible de gérer le vote pour : %s. Erreur : %s"
#: clean.go:170
msgid "Unable to remove %s: %s"
msgstr "Impossible de supprimer %s : %s"
#: print.go:32
msgid "Version"
msgstr "Version"
#: print.go:50
msgid "Votes"
msgstr "Votes"
#: print.go:87
msgid "Yay version v%s"
msgstr "Yay version v%s"
#: pkg/menus/menu.go:49
msgid "[N]one"
msgstr "[N]Aucun"
#: pkg/settings/errors.go:29
msgid "aborting due to user"
msgstr "abandon dû à l'utilisateur"
#: pkg/settings/parser/parser.go:608
msgid "argument '-' specified without input on stdin"
msgstr "argument '-' spécifié et aucune entrée dans stdin"
#: local_install.go:26
msgid "cannot find PKGBUILD and .SRCINFO in directory"
msgstr ""
"impossible de trouver les fichiers PKGBUILD et .SRCINFO dans le répertoire"
#: pkg/sync/build/pkg_archive.go:148
msgid "cannot find package name: %v"
msgstr "impossible de trouver le nom de paquet : %v"
#: pkg/sync/build/errors.go:30
msgid "could not find PKGDEST for: %s"
msgstr "impossible de trouver le PKGDEST pour : %s"
#: errors.go:9
msgid "could not find all required packages"
msgstr "impossible de trouver tous les paquets requis"
#: pkg/sync/build/errors.go:61
msgid "could not find any package archives listed in %s"
msgstr "aucune archive de paquet trouvée parmi celles listées dans %s"
#: pkg/sync/build/errors.go:50 pkg/upgrade/service.go:286
msgid "dependency"
msgstr "dépendance"
#: pkg/vcs/vcs.go:100 pkg/vcs/vcs.go:96
msgid "devel check for package failed: '%s' encountered an error"
msgstr ""
"échec de la vérification du paquet de développement : '%s' a rencontré une "
"erreur"
#: pkg/menus/edit_menu.go:110
msgid "editor did not exit successfully, aborting: %s"
msgstr "l'éditeur ne s'est pas terminé avec succès, abandon : %s"
#: pkg/sync/workdir/aur_source.go:24
msgid "error downloading sources: %s"
msgstr "erreur lors du téléchargement des sources : %s"
#: pkg/download/errors.go:25
msgid "error fetching %s: %s"
msgstr "erreur lors de la récupération de %s : %s"
#: pkg/sync/build/errors.go:9
msgid "error installing repo packages"
msgstr "erreur lors de l'installation des paquets provenant des dépôts"
#: pkg/sync/build/installer.go:266 pkg/sync/build/installer.go:270
msgid "error installing:"
msgstr "erreur lors de l'installation de :"
#: pkg/sync/build/installer.go:233 pkg/sync/build/installer.go:237
msgid "error making: %s"
msgstr "erreur lors de la compilation de : %s"
#: pkg/sync/workdir/merge.go:24
msgid "error merging %s: %s"
msgstr "erreur lors de la fusion (%s) : %s"
#: pkg/download/unified.go:59
msgid "error reading %s"
msgstr "erreur lors de la lecture de %s"
#: sync.go:36
msgid "error refreshing databases"
msgstr "erreur lors de l'actualisation des bases de données"
#: pkg/sync/workdir/clean.go:51 pkg/sync/workdir/merge.go:17
msgid "error resetting %s: %s"
msgstr "erreur lors de la réinitialisation de %s : %s"
#: pkg/sync/build/errors.go:53
msgid "error updating package install reason to %s"
msgstr ""
"erreur lors de la modification du motif d'installation du paquet en « %s »"
#: pkg/sync/build/errors.go:48
msgid "explicit"
msgstr "explicite"
#: pkg/settings/errors.go:23
msgid "failed to create directory '%s': %s"
msgstr "échec de la création du répertoire '%s' : %s"
#: pkg/settings/config.go:281
msgid "failed to open config file '%s': %s"
msgstr "échec de l'ouverture du fichier de configuration '%s' : %s"
#: pkg/sync/srcinfo/service.go:114
msgid "failed to parse %s -- skipping: %s"
msgstr "échec de l'analyse de %s -- ignoré : %s"
#: pkg/sync/srcinfo/service.go:118
msgid "failed to parse %s: %s"
msgstr "échec de l'analyse de %s : %s"
#: local_install.go:77
msgid "failed to parse .SRCINFO"
msgstr "échec de l'analyse du .SRCINFO"
#: pkg/settings/config.go:291
msgid "failed to read config file '%s': %s"
msgstr "échec de la lecture du fichier de configuration '%s' : %s"
#: pkg/cmd/graph/main.go:46 pkg/runtime/runtime.go:73
msgid "failed to retrieve aur Cache"
msgstr "échec de la récupération du cache AUR"
#: pkg/upgrade/sources.go:27
msgid "ignoring package devel upgrade (no AUR info found):"
msgstr ""
"mise à jour du paquet de développement ignorée (infos AUR introuvables) :"
#: pkg/text/errors.go:8
msgid "input too long"
msgstr "entrée trop longue"
#: pkg/db/ialpm/alpm.go:222 pkg/dep/dep_graph.go:761
msgid "invalid number: %s"
msgstr "nombre invalide : %s"
#: pkg/settings/parser/parser.go:174
msgid "invalid option '%s'"
msgstr "option '%s' invalide"
#: cmd.go:197
msgid "invalid option: '--deps' and '--explicit' may not be used together"
msgstr ""
"option invalide : '--deps' et '--explicit' ne peuvent pas être utilisés "
"ensemble"
#: pkg/download/abs.go:22
msgid "invalid repository"
msgstr "dépôt invalide"
#: pkg/db/ialpm/alpm.go:227 pkg/dep/dep_graph.go:767
msgid "invalid value: %d is not between %d and %d"
msgstr "valeur invalide : %d n'est pas entre %d et %d"
#: pkg/text/input.go:48
msgid "no"
msgstr "non"
#: pkg/sync/srcinfo/pgp/keys.go:110
msgid "no keys to import"
msgstr "aucune clé à importer"
#: pkg/query/errors.go:20
msgid "no query was executed"
msgstr "aucune requête n'a été exécutée"
#: local_install.go:66
msgid "no target directories specified"
msgstr "aucun répertoire cible spécifié"
#: pkg/sync/build/installer.go:242
msgid "nothing to install for %s"
msgstr "rien à installer pour %s"
#: pkg/settings/parser/parser.go:164
msgid "only one operation may be used at a time"
msgstr "une seule opération peut être réalisée à la fois"
#: pkg/cmd/graph/main.go:70
msgid "only one target is allowed"
msgstr "une seule cible est autorisée"
#: pkg/upgrade/service.go:291
msgid "package"
msgid_plural "packages"
msgstr[0] "paquet"
msgstr[1] "paquets"
msgstr[2] "paquets"
#: print.go:187
msgid "package '%s' was not found"
msgstr "le paquet '%s' est introuvable"
#: pkg/download/errors.go:15
msgid "package not found in AUR"
msgstr "paquet introuvable dans l'AUR"
#: pkg/download/abs.go:23
msgid "package not found in repos"
msgstr "paquet introuvable dans les dépôts"
#: pkg/sync/srcinfo/pgp/keys.go:100
msgid "problem importing keys"
msgstr "erreur lors de l'importation des clés"
#: clean.go:105
msgid "removing AUR packages from cache..."
msgstr "suppression des paquets de l'AUR du cache…"
#: clean.go:178 pkg/sync/workdir/clean.go:41
msgid "removing untracked AUR files from cache..."
msgstr ""
"suppression des fichiers non suivis dans les paquets de l'AUR en cache…"
#: pkg/sync/build/errors.go:38
msgid "the PKGDEST for %s is listed by makepkg but does not exist: %s"
msgstr "le PKGDEST pour %s est listé par makepkg mais n'existe pas : %s"
#: pkg/sync/sync.go:45
msgid "there is nothing to do"
msgstr "il n'y a rien à faire"
#: pkg/db/ialpm/alpm.go:247
msgid "unable to CreateHandle: %s"
msgstr "impossible de CreateHandle : %s"
#: cmd.go:186
msgid "unhandled operation"
msgstr "opération non gérée"
#: cmd.go:450
msgid "unknown-version"
msgstr "version-inconnue"
#: pkg/text/input.go:47
msgid "yes"
msgstr "oui"
================================================
FILE: po/he.po
================================================
#
# Translators:
# lavi landa, 2024
# Yaron Shahrabani , 2024
#
msgid ""
msgstr ""
"Last-Translator: Yaron Shahrabani , 2024\n"
"Language-Team: Hebrew (https://app.transifex.com/yay-1/teams/123732/he/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: he\n"
"Plural-Forms: nplurals=3; plural=(n == 1 && n % 1 == 0) ? 0 : (n == 2 && n % 1 == 0) ? 1: 2;\n"
"X-Generator: xgotext\n"
#: pkg/menus/menu.go:32
msgid " (Build Files Exist)"
msgstr " (קיימים קובצי בנייה)"
#: pkg/menus/menu.go:27
msgid " (Installed)"
msgstr " (מותקנת)"
#: cmd.go:453
msgid " [Installed]"
msgstr " [מותקנת]"
#: cmd.go:410 vote.go:36
msgid " there is nothing to do"
msgstr "אין מטרות לביצוע"
#: pkg/menus/menu.go:49
msgid "%s [A]ll [Ab]ort [I]nstalled [No]tInstalled or (1 2 3, 1-3, ^4)"
msgstr ""
"%s [A]-הכול [Ab]-ביטול [I]-מותקנות [No]-לא מותקנות או (1 2 3, 1-3, ^4)"
#: pkg/sync/build/installer.go:308
msgid "%s already made -- skipping build"
msgstr "%s כבר נבנתה -- לא תיבנה שוב"
#: pkg/menus/edit_menu.go:57
msgid "%s is not set"
msgstr "%s לא מוגדר"
#: pkg/settings/exe/cmd_builder.go:257
msgid "%s is present."
msgstr "%s קיימת."
#: pkg/dep/dep_graph.go:460 pkg/sync/build/installer.go:305
msgid "%s is up to date -- skipping"
msgstr "%s עדכנית -- מדלגים"
#: pkg/upgrade/service.go:292
msgid "%s to upgrade/install."
msgstr "%s לשדרוג/התקנה."
#: pkg/upgrade/service.go:286
msgid "%s will also be installed for this operation."
msgstr "גם %s תותקן לשם הפעולה הזאת."
#: pkg/sync/srcinfo/pgp/keys.go:124
msgid "%s, required by: %s"
msgstr "%s, נדרשה ע״י: %s"
#: pkg/menus/diff_menu.go:49
msgid "%s: No changes -- skipping"
msgstr "%s: אין שינויים -- מדלגים"
#: pkg/query/filter.go:22
msgid "%s: can't use target with option --aur -- skipping"
msgstr "%s: אי אפשר להשתמש ביעד עם האפשרות --aur -- מדלגים"
#: pkg/query/filter.go:17
msgid "%s: can't use target with option --repo -- skipping"
msgstr "%s: אי אפשר להשתמש ביעד עם האפשרות --repo -- מדלגים"
#: pkg/upgrade/sources.go:57
msgid "%s: ignoring package upgrade (%s => %s)"
msgstr "%s: מתעלמים משדרוג חבילה (%s => %s)"
#: pkg/query/aur_warnings.go:46
msgid "%s: local (%s) is newer than AUR (%s)"
msgstr "%s: מקומית (%s) חדשה יותר מ־AUR (%s)"
#: vote.go:51
msgid ""
"%s: please set AUR_USERNAME and AUR_PASSWORD environment variables for "
"voting"
msgstr "%s: נא להגדיר את משתני הסביבה AUR_USERNAME ו־AUR_PASSWORD כדי להצביע"
#: pkg/download/unified.go:192
msgid "(%d/%d) Downloaded PKGBUILD from ABS: %s"
msgstr "(%d/%d) PKGBUILD ירד מ־ABS: %s"
#: pkg/download/aur.go:92 pkg/download/unified.go:188
msgid "(%d/%d) Downloaded PKGBUILD: %s"
msgstr "(%d/%d) PKGBUILD שירד: %s"
#: pkg/download/aur.go:82
msgid "(%d/%d) Failed to download PKGBUILD: %s"
msgstr "(%d/%d) הורדת PKGBUILD נכשלה: %s"
#: pkg/sync/srcinfo/service.go:109
msgid "(%d/%d) Parsing SRCINFO: %s"
msgstr "(%d/%d) מתבצע פענוח של SRCINFO: %s"
#: pkg/query/types.go:72 pkg/query/types.go:103
msgid "(Installed)"
msgstr "(מותקנת)"
#: pkg/query/types.go:70 pkg/query/types.go:101
msgid "(Installed: %s)"
msgstr "(מותקנת: %s)"
#: pkg/query/types.go:61
msgid "(Orphaned)"
msgstr "(יתומה)"
#: pkg/query/types.go:65
msgid "(Out-of-date: %s)"
msgstr "(לא עדכנית: %s)"
#: print.go:44
msgid "AUR URL"
msgstr "כתובת AUR"
#: pkg/dep/dep_graph.go:75
msgid "AUR"
msgstr "AUR"
#: pkg/menus/edit_menu.go:58
msgid "Add %s or %s to your environment variables"
msgstr "יש להוסיף %s או %s למשתני הסביבה שלך"
#: main.go:60
msgid "Avoid running yay as root/sudo."
msgstr "כדאי להימנע מהפעלה תחת root/sudo."
#: pkg/dep/dep_graph.go:63
msgid "Check Dependency"
msgstr "בדיקת תלות"
#: print.go:41
msgid "Check Deps"
msgstr "בדיקת תלויות"
#: pkg/upgrade/service.go:90
msgid "Checking development packages..."
msgstr "חבילות הפיתוח נבדקות…"
#: pkg/sync/workdir/clean.go:45
msgid "Cleaning (%d/%d): %s"
msgstr "מתבצע ניקיון (%d/%d): %s"
#: print.go:42
msgid "Conflicts With"
msgstr "סותר את"
#: pkg/menus/clean_menu.go:62
msgid "Deleting (%d/%d): %s"
msgstr "מתבצעת מחיקה (%d/%d): %s"
#: pkg/dep/dep_graph.go:61
msgid "Dependency"
msgstr "תלות"
#: print.go:38
msgid "Depends On"
msgstr "תלויה ב־"
#: print.go:33
msgid "Description"
msgstr "תיאור"
#: pkg/menus/diff_menu.go:160
msgid "Diffs to show?"
msgstr "הבדלים להצגה?"
#: pkg/settings/migrations.go:25
msgid "Disable 'provides' setting by default"
msgstr "להשבית את הגדרת ‚provides’ (מספקת) כברירת מחדל"
#: clean.go:78
msgid "Do you want to remove ALL AUR packages from cache?"
msgstr "להסיר את כל חבילות ה־AUR מהמטמון?"
#: clean.go:95
msgid "Do you want to remove ALL untracked AUR files?"
msgstr "להסיר את כל קובצי ה־AUR שאינם במעקב?"
#: clean.go:80
msgid "Do you want to remove all other AUR packages from cache?"
msgstr "להסיר את כל חבילות ה־AUR האחרות מהמטמון?"
#: pkg/menus/edit_menu.go:61
msgid "Edit PKGBUILD with?"
msgstr "לערוך את PKGBUILD עם?"
#: pkg/query/errors.go:13
msgid "Error during AUR search: %s\n"
msgstr "שגיאה בחיפוש AUR: %s\n"
#: pkg/upgrade/service.go:296
msgid "Excluding packages may cause partial upgrades and break systems"
msgstr "החרגת חבילות עשויה להוביל לשדרוג חלקי ולפגוע במערכות"
#: pkg/dep/dep_graph.go:60
msgid "Explicit"
msgstr "מפורש"
#: print.go:91
msgid "Explicitly installed packages: %s"
msgstr "חבילות שהותקנו מפורשות: %s"
#: pkg/dep/dep_graph.go:437 pkg/dep/dep_graph.go:535
msgid "Failed to find AUR package for"
msgstr "נכשל האיתור בחבילת AUR עבור"
#: pkg/sync/build/installer.go:120
msgid "Failed to install layer, rolling up to next layer."
msgstr "התקנת השכבה נכשלה, מתבצעת התקדמות לשכבה הבאה."
#: pkg/sync/build/errors.go:16
msgid ""
"Failed to install the following packages. Manual intervention is required:"
msgstr "התקנת החבילות הבאות נכשלה. נדרשת התערבות ידנית:"
#: print.go:45
msgid "First Submitted"
msgstr "הוגשה לראשונה"
#: pkg/query/aur_warnings.go:79
msgid "Flagged Out Of Date AUR Packages:"
msgstr "חבילות AUR שסומנו כלא עדכניות:"
#: print.go:90
msgid "Foreign installed packages: %s"
msgstr "חבילות זרות מותקנות: %s"
#: pkg/vcs/vcs.go:144
msgid "Found git repo: %s"
msgstr "נמצא מאגר git: %s"
#: vcs.go:72
msgid "GenDB finished. No packages were installed"
msgstr "GenDB הסתיים. לא הותקנו חבילות"
#: print.go:36
msgid "Groups"
msgstr "קבוצות"
#: pkg/sync/srcinfo/pgp/keys.go:88
msgid "Import?"
msgstr "לייבא?"
#: pkg/sync/srcinfo/pgp/keys.go:97
msgid "Importing keys with gpg..."
msgstr "מתבצע ייבוא מפתחות עם gpg…"
#: print.go:46
msgid "Keywords"
msgstr "מילות מפתח"
#: print.go:47
msgid "Last Modified"
msgstr "שינוי אחרון"
#: print.go:35
msgid "Licenses"
msgstr "רישיונות"
#: pkg/dep/dep_graph.go:77
msgid "Local"
msgstr "מקומי"
#: print.go:48
msgid "Maintainer"
msgstr "תחזוקה"
#: pkg/dep/dep_graph.go:62
msgid "Make Dependency"
msgstr "תלות בנייה"
#: print.go:40
msgid "Make Deps"
msgstr "תלויות בנייה"
#: pkg/query/aur_warnings.go:71
msgid "Missing AUR Debug Packages:"
msgstr "חבילות ניפוי שגיאות חסרות ב־AUR:"
#: pkg/dep/dep_graph.go:79
msgid "Missing"
msgstr "חסרות"
#: print.go:31
msgid "Name"
msgstr "שם"
#: pkg/dep/dep_graph.go:442 pkg/dep/dep_graph.go:548
msgid "No AUR package found for"
msgstr "לא נמצאה חבילת AUR עבור"
#: pkg/dep/dep_graph.go:182
msgid "No package found for"
msgstr "לא נמצאה חבילה עבור"
#: print.go:225
msgid "None"
msgstr "ללא"
#: print.go:39
msgid "Optional Deps"
msgstr "תלויות רשות"
#: pkg/query/aur_warnings.go:75
msgid "Orphan (unmaintained) AUR Packages:"
msgstr "חבילות AUR יתומות (לא מתוחזקות):"
#: print.go:53 print.go:55
msgid "Out-of-date"
msgstr "לא עדכניות"
#: pkg/sync/srcinfo/pgp/keys.go:115
msgid "PGP keys need importing:"
msgstr "מפתחות PGP שיש לייבא:"
#: pkg/sync/workdir/preparer.go:252
msgid "PKGBUILD up to date, skipping download: %s"
msgstr "PKGBUILD עדכני, לא תתבצע הורדה: %s"
#: pkg/menus/edit_menu.go:130
msgid "PKGBUILDs to edit?"
msgstr "קובצי PKGBUILD לעריכה?"
#: print.go:60
msgid "Package Base ID"
msgstr "מזהה בסיס החבילה"
#: print.go:61
msgid "Package Base"
msgstr "בסיס חבילה"
#: pkg/query/aur_warnings.go:67
msgid "Packages not in AUR:"
msgstr "חבילות שאינן ב־AUR:"
#: pkg/menus/clean_menu.go:54
msgid "Packages to cleanBuild?"
msgstr "חבילות לבנייה מההתחלה (cleanBuild)?"
#: pkg/dep/dep_graph.go:202
msgid "Packages to exclude"
msgstr "חבילות להחרגה"
#: pkg/upgrade/service.go:295
msgid "Packages to exclude: (eg: \"1 2 3\", \"1-3\", \"^4\" or repo name)"
msgstr "חבילות שיוחרגו: (למשל: „1 2 3”, „1-3”, „^4” או שם המאגר)"
#: cmd.go:392
msgid "Packages to install (eg: 1 2 3, 1-3 or ^4)"
msgstr "חבילות להתקנה (למשל: 1 2 3, 1-3 או ^4)"
#: print.go:49
msgid "Popularity"
msgstr "פופולריות"
#: pkg/menus/diff_menu.go:172 pkg/menus/edit_menu.go:143
msgid "Proceed with install?"
msgstr "להמשיך בהתקנה?"
#: print.go:37
msgid "Provides"
msgstr "מספקת"
#: pkg/sync/workdir/preparer.go:125
msgid "Remove make dependencies after install?"
msgstr "להסיר תלויות בנייה לאחר ההתקנה?"
#: print.go:43
msgid "Replaces"
msgstr "מחליפה"
#: pkg/dep/dep_graph.go:730
msgid "Repository AUR"
msgstr "מאגר AUR"
#: print.go:30 pkg/db/ialpm/alpm.go:191
msgid "Repository"
msgstr "מאגר"
#: pkg/dep/dep_graph.go:78
msgid "SRCINFO"
msgstr "SRCINFO"
#: pkg/upgrade/service.go:72
msgid "Searching AUR for updates..."
msgstr "מתבצע חיפוש אחר עדכונים ב־AUR…"
#: pkg/upgrade/service.go:160
msgid "Searching databases for updates..."
msgstr "מתבצע חיפוש אחר עדכונים במסדי הנתונים…"
#: pkg/query/query_builder.go:214
msgid "Showing repo packages only"
msgstr "מוצגות חבילות מאגר בלבד"
#: print.go:95
msgid "Size of pacman cache %s: %s"
msgstr "גודל המטמון של pacman %s: %s"
#: print.go:98
msgid "Size of yay cache %s: %s"
msgstr "גודל המטמון של yay %s: %s"
#: print.go:62
msgid "Snapshot URL"
msgstr "כתובת לכידה"
#: pkg/dep/dep_graph.go:76
msgid "Sync"
msgstr "סנכרון"
#: print.go:100
msgid "Ten biggest packages:"
msgstr "עשרת החבילות הגדולות:"
#: pkg/sync/sync.go:124
msgid "The following packages are not compatible with your architecture:"
msgstr "החבילות הבאות אינן תואמות לארכיטקטורת המעבד שלך:"
#: pkg/db/ialpm/alpm.go:179 pkg/dep/dep_graph.go:726
msgid "There are %[1]d providers available for %[2]s:"
msgstr "יש %[1]d ספקים זמינים עבור %[2]s:"
#: pkg/settings/exe/cmd_builder.go:258
msgid "There may be another Pacman instance running. Waiting..."
msgstr "יכול להיות שעוד מופע של Pacman פעיל. ממתינים…"
#: print.go:92
msgid "Total Size occupied by packages: %s"
msgstr "הגודל הכולל שתופסות החבילות: %s"
#: print.go:89
msgid "Total installed packages: %s"
msgstr "סך כל החבילות המותקנות: %s"
#: pkg/sync/sync.go:132
msgid "Try to build them anyway?"
msgstr "לנסות לבנות אותן בכל מקרה?"
#: print.go:34
msgid "URL"
msgstr "כתובת"
#: clean.go:194 pkg/menus/clean_menu.go:65 pkg/menus/clean_menu.go:71
msgid "Unable to clean:"
msgstr "לא ניתן לפנות:"
#: get.go:42 get.go:74
msgid "Unable to find the following packages:"
msgstr "לא ניתן למצוא את החבילות הבאות:"
#: vote.go:20
msgid "Unable to handle package vote for: %s. err: %s"
msgstr "לא ניתן לטפל בהצבעות חבילה עבור: %s. שגיאה: %s"
#: clean.go:170
msgid "Unable to remove %s: %s"
msgstr "לא ניתן להסיר את %s: %s"
#: print.go:32
msgid "Version"
msgstr "גרסה"
#: print.go:50
msgid "Votes"
msgstr "הצבעות"
#: print.go:87
msgid "Yay version v%s"
msgstr "Yay גרסה v%s"
#: pkg/menus/menu.go:49
msgid "[N]one"
msgstr "[N]-כלום"
#: clean.go:83
msgid ""
"\n"
"Build directory:"
msgstr ""
"\n"
"תיקיית בנייה:"
#: pkg/db/ialpm/alpm.go:201 pkg/dep/dep_graph.go:740
msgid ""
"\n"
"Enter a number (default=1): "
msgstr ""
"\n"
"נא להקליד מספר (ברירת מחדל=1): "
#: pkg/settings/errors.go:29
msgid "aborting due to user"
msgstr "בוטל לבקשת המשתמש"
#: pkg/settings/parser/parser.go:608
msgid "argument '-' specified without input on stdin"
msgstr "סופק המשתנה ‚-’ ללא שום קלט ל־stdin"
#: local_install.go:26
msgid "cannot find PKGBUILD and .SRCINFO in directory"
msgstr "לא ניתן לאתר PKGBUILD ו־.SRCINFO בתיקייה"
#: pkg/sync/build/pkg_archive.go:148
msgid "cannot find package name: %v"
msgstr "לא ניתן למצוא את שם החבילה: %v"
#: pkg/sync/build/errors.go:30
msgid "could not find PKGDEST for: %s"
msgstr "לא ניתן למצוא PKGDEST עבור: %s"
#: errors.go:9
msgid "could not find all required packages"
msgstr "לא ניתן למצוא את כל החבילות הנחוצות"
#: pkg/sync/build/errors.go:61
msgid "could not find any package archives listed in %s"
msgstr "לא ניתן למצוא ארכיוני חבילות כלשהם שמופיעים תחת %s"
#: pkg/sync/build/errors.go:50 pkg/upgrade/service.go:287
msgid "dependency"
msgstr "תלות"
#: pkg/vcs/vcs.go:96 pkg/vcs/vcs.go:100
msgid "devel check for package failed: '%s' encountered an error"
msgstr "בדיקת פיתוח לחבילה נכשלה: ‚%s’ נתקל בשגיאה"
#: pkg/menus/edit_menu.go:110
msgid "editor did not exit successfully, aborting: %s"
msgstr "העורך לא יצא כמו שצריך, הפעולה מבוטלת: %s"
#: pkg/sync/workdir/aur_source.go:24
msgid "error downloading sources: %s"
msgstr "שגיאה בהורדת קוד מקור: %s"
#: pkg/download/errors.go:25
msgid "error fetching %s: %s"
msgstr "שגיאה במשיכת %s: %s"
#: pkg/sync/build/errors.go:9
msgid "error installing repo packages"
msgstr "שגיאה בהתקנת חבילות מאגר"
#: pkg/sync/build/installer.go:266 pkg/sync/build/installer.go:270
msgid "error installing:"
msgstr "שגיאה בהתקנה:"
#: pkg/sync/build/installer.go:233 pkg/sync/build/installer.go:237
msgid "error making: %s"
msgstr "שגיאה בבנייה: %s"
#: pkg/sync/workdir/merge.go:24
msgid "error merging %s: %s"
msgstr "שגיאה במיזוג %s: %s"
#: pkg/download/unified.go:59
msgid "error reading %s"
msgstr "שגיאה בקריאת %s"
#: sync.go:36
msgid "error refreshing databases"
msgstr "שגיאה ברענון מסדי נתונים"
#: pkg/sync/workdir/clean.go:51 pkg/sync/workdir/merge.go:17
msgid "error resetting %s: %s"
msgstr "שגיאה באיפוס %s: %s"
#: pkg/sync/build/errors.go:53
msgid "error updating package install reason to %s"
msgstr "שגיאה בעדכון סיבת התקנת החבילה לכדי %s"
#: pkg/sync/build/errors.go:48
msgid "explicit"
msgstr "מפורש"
#: pkg/settings/errors.go:23
msgid "failed to create directory '%s': %s"
msgstr "יצירת התיקייה ‚%s’ נכשלה: %s"
#: pkg/settings/config.go:281
msgid "failed to open config file '%s': %s"
msgstr "פתיחת קובץ ההגדרות ‚%s’ נכשלה: %s"
#: pkg/sync/srcinfo/service.go:114
msgid "failed to parse %s -- skipping: %s"
msgstr "הפענוח של %s נכשל -- מדלגים: %s"
#: pkg/sync/srcinfo/service.go:118
msgid "failed to parse %s: %s"
msgstr "הפענוח של %s נכשל: %s"
#: local_install.go:77
msgid "failed to parse .SRCINFO"
msgstr "פענוח ה־.SRCINFO נכשל"
#: pkg/settings/config.go:291
msgid "failed to read config file '%s': %s"
msgstr "קריאת קובץ ההגדרות ‚%s’ נכשלה: %s"
#: pkg/cmd/graph/main.go:46 pkg/runtime/runtime.go:73
msgid "failed to retrieve aur Cache"
msgstr "משיכת המטמון של ה־aur נכשלה"
#: pkg/upgrade/sources.go:27
msgid "ignoring package devel upgrade (no AUR info found):"
msgstr "התעלמות משדרוג חבילת פיתוח (devel - לא נמצאו פרטים ב־AUR):"
#: pkg/text/errors.go:8
msgid "input too long"
msgstr "הקלט ארוך מדי"
#: pkg/db/ialpm/alpm.go:222 pkg/dep/dep_graph.go:761
msgid "invalid number: %s"
msgstr "מס׳ שגוי: %s"
#: pkg/settings/parser/parser.go:174
msgid "invalid option '%s'"
msgstr "האפשרות ‚%s’ שגויה"
#: cmd.go:197
msgid "invalid option: '--deps' and '--explicit' may not be used together"
msgstr "אפשרות שגויה: אסור להשתמש ב־‚--deps' וב־‚--explicit’ יחד"
#: pkg/download/abs.go:22
msgid "invalid repository"
msgstr "מאגר שגוי"
#: pkg/db/ialpm/alpm.go:227 pkg/dep/dep_graph.go:767
msgid "invalid value: %d is not between %d and %d"
msgstr "ערך שגוי: %d לא בין %d לבין %d"
#: pkg/sync/srcinfo/pgp/keys.go:110
msgid "no keys to import"
msgstr "אין מפתחות לייבוא"
#: pkg/query/errors.go:20
msgid "no query was executed"
msgstr "לא הופעלה שאילתה"
#: local_install.go:66
msgid "no target directories specified"
msgstr "לא צוינו תיקיות יעד"
#: pkg/text/input.go:48
msgid "no"
msgstr "לא"
#: pkg/sync/build/installer.go:242
msgid "nothing to install for %s"
msgstr "אין מה להתקין עבור %s"
#: pkg/settings/parser/parser.go:164
msgid "only one operation may be used at a time"
msgstr "אפשר להשתמש בפעולה אחת בכל רגע נתון"
#: pkg/cmd/graph/main.go:70
msgid "only one target is allowed"
msgstr "מותר רק יעד אחד"
#: print.go:187
msgid "package '%s' was not found"
msgstr "החבילה ‚%s’ נכשלה"
#: pkg/download/errors.go:15
msgid "package not found in AUR"
msgstr "החבילה לא נמצאה ב־AUR"
#: pkg/download/abs.go:23
msgid "package not found in repos"
msgstr "החבילה לא נמצאה במאגרים"
#: pkg/upgrade/service.go:292
msgid "package"
msgid_plural "packages"
msgstr[0] "חבילה"
msgstr[1] "חבילות"
msgstr[2] "חבילות"
#: pkg/sync/srcinfo/pgp/keys.go:100
msgid "problem importing keys"
msgstr "תקלה בייבוא מפתחות"
#: clean.go:105
msgid "removing AUR packages from cache..."
msgstr "חבילות AUR נמחקות מהמטמון…"
#: clean.go:178 pkg/sync/workdir/clean.go:41
msgid "removing untracked AUR files from cache..."
msgstr "קובצי AUR שאינם במעקב נמחקים מהמטמון…"
#: pkg/sync/build/errors.go:38
msgid "the PKGDEST for %s is listed by makepkg but does not exist: %s"
msgstr "ה־PKGDEST עבור %s מוצג על ידי makepkg אך אינו קיים: %s"
#: pkg/sync/sync.go:45
msgid "there is nothing to do"
msgstr "אין מטרות לביצוע"
#: pkg/db/ialpm/alpm.go:247
msgid "unable to CreateHandle: %s"
msgstr "לא ניתן ליצור כינוי (CreateHandle): %s"
#: cmd.go:186
msgid "unhandled operation"
msgstr "פעולה שלא טופלה"
#: cmd.go:450
msgid "unknown-version"
msgstr "גרסה-לא-ידועה"
#: pkg/text/input.go:47
msgid "yes"
msgstr "כן"
================================================
FILE: po/he_IL.po
================================================
#
# Translators:
# Yaron Shahrabani , 2024
# lavi landa, 2024
#
msgid ""
msgstr ""
"Last-Translator: lavi landa, 2024\n"
"Language-Team: Hebrew (Israel) (https://app.transifex.com/yay-1/teams/123732/he_IL/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: he_IL\n"
"Plural-Forms: nplurals=3; plural=(n == 1 && n % 1 == 0) ? 0 : (n == 2 && n % 1 == 0) ? 1: 2;\n"
"X-Generator: xgotext\n"
#: pkg/menus/menu.go:32
msgid " (Build Files Exist)"
msgstr " (קיימים קובצי בנייה)"
#: pkg/menus/menu.go:27
msgid " (Installed)"
msgstr " (מותקנת)"
#: cmd.go:453
msgid " [Installed]"
msgstr " [מותקנת]"
#: cmd.go:410 vote.go:36
msgid " there is nothing to do"
msgstr "אין מטרות לביצוע"
#: pkg/menus/menu.go:49
msgid "%s [A]ll [Ab]ort [I]nstalled [No]tInstalled or (1 2 3, 1-3, ^4)"
msgstr ""
"%s [A]-הכול [Ab]-ביטול [I]-מותקנות [No]-לא מותקנות או (1 2 3, 1-3, ^4)"
#: pkg/sync/build/installer.go:308
msgid "%s already made -- skipping build"
msgstr "%s כבר נבנתה -- לא תיבנה שוב"
#: pkg/menus/edit_menu.go:57
msgid "%s is not set"
msgstr "%s לא מוגדר"
#: pkg/settings/exe/cmd_builder.go:257
msgid "%s is present."
msgstr "%s קיימת."
#: pkg/dep/dep_graph.go:460 pkg/sync/build/installer.go:305
msgid "%s is up to date -- skipping"
msgstr "%s עדכנית -- מדלגים"
#: pkg/upgrade/service.go:292
msgid "%s to upgrade/install."
msgstr "%s לשדרוג/התקנה."
#: pkg/upgrade/service.go:286
msgid "%s will also be installed for this operation."
msgstr "גם %s תותקן לשם הפעולה הזאת."
#: pkg/sync/srcinfo/pgp/keys.go:124
msgid "%s, required by: %s"
msgstr "%s, נדרשה ע״י: %s"
#: pkg/menus/diff_menu.go:49
msgid "%s: No changes -- skipping"
msgstr "%s: אין שינויים -- מדלגים"
#: pkg/query/filter.go:22
msgid "%s: can't use target with option --aur -- skipping"
msgstr "%s: אי אפשר להשתמש ביעד עם האפשרות --aur -- מדלגים"
#: pkg/query/filter.go:17
msgid "%s: can't use target with option --repo -- skipping"
msgstr "%s: אי אפשר להשתמש ביעד עם האפשרות --repo -- מדלגים"
#: pkg/upgrade/sources.go:57
msgid "%s: ignoring package upgrade (%s => %s)"
msgstr "%s: מתעלמים משדרוג חבילה (%s => %s)"
#: pkg/query/aur_warnings.go:46
msgid "%s: local (%s) is newer than AUR (%s)"
msgstr "%s: מקומית (%s) חדשה יותר מ־AUR (%s)"
#: vote.go:51
msgid ""
"%s: please set AUR_USERNAME and AUR_PASSWORD environment variables for "
"voting"
msgstr "%s: נא להגדיר את משתני הסביבה AUR_USERNAME ו־AUR_PASSWORD כדי להצביע"
#: pkg/download/unified.go:192
msgid "(%d/%d) Downloaded PKGBUILD from ABS: %s"
msgstr "(%d/%d) PKGBUILD ירד מ־ABS: %s"
#: pkg/download/aur.go:92 pkg/download/unified.go:188
msgid "(%d/%d) Downloaded PKGBUILD: %s"
msgstr "(%d/%d) PKGBUILD שירד: %s"
#: pkg/download/aur.go:82
msgid "(%d/%d) Failed to download PKGBUILD: %s"
msgstr "(%d/%d) לא הצליח להוריד PKGBUILD: %s"
#: pkg/sync/srcinfo/service.go:109
msgid "(%d/%d) Parsing SRCINFO: %s"
msgstr "(%d/%d) מתבצע פענוח של SRCINFO: %s"
#: pkg/query/types.go:72 pkg/query/types.go:103
msgid "(Installed)"
msgstr "(מותקנת)"
#: pkg/query/types.go:70 pkg/query/types.go:101
msgid "(Installed: %s)"
msgstr "(מותקנת: %s)"
#: pkg/query/types.go:61
msgid "(Orphaned)"
msgstr "(יתומה)"
#: pkg/query/types.go:65
msgid "(Out-of-date: %s)"
msgstr "(לא עדכנית: %s)"
#: print.go:44
msgid "AUR URL"
msgstr "כתובת AUR"
#: pkg/dep/dep_graph.go:75
msgid "AUR"
msgstr "AUR"
#: pkg/menus/edit_menu.go:58
msgid "Add %s or %s to your environment variables"
msgstr "יש להוסיף %s או %s למשתני הסביבה שלך"
#: main.go:60
msgid "Avoid running yay as root/sudo."
msgstr "כדאי להימנע מהפעלה תחת root/sudo."
#: pkg/dep/dep_graph.go:63
msgid "Check Dependency"
msgstr "בדיקת תלות"
#: print.go:41
msgid "Check Deps"
msgstr "בדיקת תלויות"
#: pkg/upgrade/service.go:90
msgid "Checking development packages..."
msgstr "חבילות הפיתוח נבדקות…"
#: pkg/sync/workdir/clean.go:45
msgid "Cleaning (%d/%d): %s"
msgstr "מתבצע ניקיון (%d/%d): %s"
#: print.go:42
msgid "Conflicts With"
msgstr "סותר את"
#: pkg/menus/clean_menu.go:62
msgid "Deleting (%d/%d): %s"
msgstr "מתבצעת מחיקה (%d/%d): %s"
#: pkg/dep/dep_graph.go:61
msgid "Dependency"
msgstr "תלות"
#: print.go:38
msgid "Depends On"
msgstr "תלויה ב־"
#: print.go:33
msgid "Description"
msgstr "תיאור"
#: pkg/menus/diff_menu.go:160
msgid "Diffs to show?"
msgstr "הבדלים להצגה?"
#: pkg/settings/migrations.go:25
msgid "Disable 'provides' setting by default"
msgstr "להשבית את הגדרת ‚provides’ (מספקת) כברירת מחדל"
#: clean.go:78
msgid "Do you want to remove ALL AUR packages from cache?"
msgstr "להסיר את כל חבילות ה־AUR מהמטמון?"
#: clean.go:95
msgid "Do you want to remove ALL untracked AUR files?"
msgstr "להסיר את כל קובצי ה־AUR שאינם במעקב?"
#: clean.go:80
msgid "Do you want to remove all other AUR packages from cache?"
msgstr "להסיר את כל חבילות ה־AUR האחרות מהמטמון?"
#: pkg/menus/edit_menu.go:61
msgid "Edit PKGBUILD with?"
msgstr "לערוך את PKGBUILD עם?"
#: pkg/query/errors.go:13
msgid "Error during AUR search: %s\n"
msgstr "שגיאה בחיפוש AUR: %s\n"
#: pkg/upgrade/service.go:296
msgid "Excluding packages may cause partial upgrades and break systems"
msgstr "החרגת חבילות עשויה להוביל לשדרוג חלקי ולפגוע במערכות"
#: pkg/dep/dep_graph.go:60
msgid "Explicit"
msgstr "מפורש"
#: print.go:91
msgid "Explicitly installed packages: %s"
msgstr "חבילות שהותקנו מפורשות: %s"
#: pkg/dep/dep_graph.go:437 pkg/dep/dep_graph.go:535
msgid "Failed to find AUR package for"
msgstr "נכשל האיתור בחבילת AUR עבור"
#: pkg/sync/build/installer.go:120
msgid "Failed to install layer, rolling up to next layer."
msgstr "התקנת השכבה נכשלה, מתבצעת התקדמות לשכבה הבאה."
#: pkg/sync/build/errors.go:16
msgid ""
"Failed to install the following packages. Manual intervention is required:"
msgstr "התקנת החבילות הבאות נכשלה. נדרשת התערבות ידנית:"
#: print.go:45
msgid "First Submitted"
msgstr "הוגשה לראשונה"
#: pkg/query/aur_warnings.go:79
msgid "Flagged Out Of Date AUR Packages:"
msgstr "חבילות AUR שסומנו כלא עדכניות:"
#: print.go:90
msgid "Foreign installed packages: %s"
msgstr "חבילות זרות מותקנות: %s"
#: pkg/vcs/vcs.go:144
msgid "Found git repo: %s"
msgstr "נמצא מאגר git: %s"
#: vcs.go:72
msgid "GenDB finished. No packages were installed"
msgstr "יצירת מסד הנתונים (GenDB) הסתיימה. לא הותקנו חבילות"
#: print.go:36
msgid "Groups"
msgstr "קבוצות"
#: pkg/sync/srcinfo/pgp/keys.go:88
msgid "Import?"
msgstr "לייבא?"
#: pkg/sync/srcinfo/pgp/keys.go:97
msgid "Importing keys with gpg..."
msgstr "מתבצע ייבוא מפתחות עם gpg…"
#: print.go:46
msgid "Keywords"
msgstr "מילות מפתח"
#: print.go:47
msgid "Last Modified"
msgstr "שינוי אחרון"
#: print.go:35
msgid "Licenses"
msgstr "רישיונות"
#: pkg/dep/dep_graph.go:77
msgid "Local"
msgstr "מקומי"
#: print.go:48
msgid "Maintainer"
msgstr "תחזוקה"
#: pkg/dep/dep_graph.go:62
msgid "Make Dependency"
msgstr "תלות בנייה"
#: print.go:40
msgid "Make Deps"
msgstr "תלויות בנייה"
#: pkg/query/aur_warnings.go:71
msgid "Missing AUR Debug Packages:"
msgstr "חבילות ניפוי שגיאות חסרות ב־AUR:"
#: pkg/dep/dep_graph.go:79
msgid "Missing"
msgstr "חסרות"
#: print.go:31
msgid "Name"
msgstr "שם"
#: pkg/dep/dep_graph.go:442 pkg/dep/dep_graph.go:548
msgid "No AUR package found for"
msgstr "לא נמצאה חבילת AUR עבור"
#: pkg/dep/dep_graph.go:182
msgid "No package found for"
msgstr "לא נמצאה חבילה עבור"
#: print.go:225
msgid "None"
msgstr "ללא"
#: print.go:39
msgid "Optional Deps"
msgstr "תלויות רשות"
#: pkg/query/aur_warnings.go:75
msgid "Orphan (unmaintained) AUR Packages:"
msgstr "חבילות AUR יתומות (לא מתוחזקות):"
#: print.go:53 print.go:55
msgid "Out-of-date"
msgstr "לא עדכניות"
#: pkg/sync/srcinfo/pgp/keys.go:115
msgid "PGP keys need importing:"
msgstr "מפתחות PGP שיש לייבא:"
#: pkg/sync/workdir/preparer.go:252
msgid "PKGBUILD up to date, skipping download: %s"
msgstr "PKGBUILD עדכני, לא תתבצע הורדה: %s"
#: pkg/menus/edit_menu.go:130
msgid "PKGBUILDs to edit?"
msgstr "קובצי PKGBUILD לעריכה?"
#: print.go:60
msgid "Package Base ID"
msgstr "מזהה בסיס החבילה"
#: print.go:61
msgid "Package Base"
msgstr "בסיס חבילה"
#: pkg/query/aur_warnings.go:67
msgid "Packages not in AUR:"
msgstr "חבילות שאינן ב־AUR:"
#: pkg/menus/clean_menu.go:54
msgid "Packages to cleanBuild?"
msgstr "חבילות לבנייה מההתחלה (cleanBuild)?"
#: pkg/dep/dep_graph.go:202
msgid "Packages to exclude"
msgstr "חבילות להחרגה"
#: pkg/upgrade/service.go:295
msgid "Packages to exclude: (eg: \"1 2 3\", \"1-3\", \"^4\" or repo name)"
msgstr "חבילות שיוחרגו: (למשל: „1 2 3”, „1-3”, „^4” או שם המאגר)"
#: cmd.go:392
msgid "Packages to install (eg: 1 2 3, 1-3 or ^4)"
msgstr "חבילות להתקנה (למשל: 1 2 3, 1-3 או ^4)"
#: print.go:49
msgid "Popularity"
msgstr "פופולריות"
#: pkg/menus/diff_menu.go:172 pkg/menus/edit_menu.go:143
msgid "Proceed with install?"
msgstr "להמשיך בהתקנה?"
#: print.go:37
msgid "Provides"
msgstr "מספקת"
#: pkg/sync/workdir/preparer.go:125
msgid "Remove make dependencies after install?"
msgstr "להסיר תלויות בנייה לאחר ההתקנה?"
#: print.go:43
msgid "Replaces"
msgstr "מחליף"
#: pkg/dep/dep_graph.go:730
msgid "Repository AUR"
msgstr "מאגר AUR"
#: print.go:30 pkg/db/ialpm/alpm.go:191
msgid "Repository"
msgstr "מאגר"
#: pkg/dep/dep_graph.go:78
msgid "SRCINFO"
msgstr "SRCINFO"
#: pkg/upgrade/service.go:72
msgid "Searching AUR for updates..."
msgstr "מתבצע חיפוש אחר עדכונים ב־AUR…"
#: pkg/upgrade/service.go:160
msgid "Searching databases for updates..."
msgstr "מתבצע חיפוש אחר עדכונים במסדי הנתונים…"
#: pkg/query/query_builder.go:214
msgid "Showing repo packages only"
msgstr "מוצגות חבילות מאגר בלבד"
#: print.go:95
msgid "Size of pacman cache %s: %s"
msgstr "גודל המטמון של pacman %s: %s"
#: print.go:98
msgid "Size of yay cache %s: %s"
msgstr "גודל המטמון של yay %s: %s"
#: print.go:62
msgid "Snapshot URL"
msgstr "כתובת לכידה"
#: pkg/dep/dep_graph.go:76
msgid "Sync"
msgstr "סנכרון"
#: print.go:100
msgid "Ten biggest packages:"
msgstr "עשרת החבילות הגדולות:"
#: pkg/sync/sync.go:124
msgid "The following packages are not compatible with your architecture:"
msgstr "החבילות הבאות אינן תואמות לארכיטקטורת המעבד שלך:"
#: pkg/db/ialpm/alpm.go:179 pkg/dep/dep_graph.go:726
msgid "There are %[1]d providers available for %[2]s:"
msgstr "יש %[1]d ספקים זמינים עבור %[2]s:"
#: pkg/settings/exe/cmd_builder.go:258
msgid "There may be another Pacman instance running. Waiting..."
msgstr "יכול להיות שעוד מופע של Pacman פעיל. ממתינים…"
#: print.go:92
msgid "Total Size occupied by packages: %s"
msgstr "הגודל הכולל שתופסות החבילות: %s"
#: print.go:89
msgid "Total installed packages: %s"
msgstr "סך כל החבילות המותקנות: %s"
#: pkg/sync/sync.go:132
msgid "Try to build them anyway?"
msgstr "לנסות לבנות אותן בכל מקרה?"
#: print.go:34
msgid "URL"
msgstr "כתובת"
#: clean.go:194 pkg/menus/clean_menu.go:65 pkg/menus/clean_menu.go:71
msgid "Unable to clean:"
msgstr "לא ניתן לפנות:"
#: get.go:42 get.go:74
msgid "Unable to find the following packages:"
msgstr "לא ניתן למצוא את החבילות הבאות:"
#: vote.go:20
msgid "Unable to handle package vote for: %s. err: %s"
msgstr "לא ניתן לטפל בהצבעות חבילה עבור: %s. שגיאה: %s"
#: clean.go:170
msgid "Unable to remove %s: %s"
msgstr "לא ניתן להסיר את %s: %s"
#: print.go:32
msgid "Version"
msgstr "גרסה"
#: print.go:50
msgid "Votes"
msgstr "הצבעות"
#: print.go:87
msgid "Yay version v%s"
msgstr "Yay גרסה v%s"
#: pkg/menus/menu.go:49
msgid "[N]one"
msgstr "[N]-כלום"
#: clean.go:83
msgid ""
"\n"
"Build directory:"
msgstr ""
"\n"
"תיקיית בנייה:"
#: pkg/db/ialpm/alpm.go:201 pkg/dep/dep_graph.go:740
msgid ""
"\n"
"Enter a number (default=1): "
msgstr ""
"\n"
"נא להקליד מספר (ברירת מחדל=1): "
#: pkg/settings/errors.go:29
msgid "aborting due to user"
msgstr "בוטל לבקשת המשתמש"
#: pkg/settings/parser/parser.go:608
msgid "argument '-' specified without input on stdin"
msgstr "סופק המשתנה ‚-’ ללא שום קלט ל־stdin"
#: local_install.go:26
msgid "cannot find PKGBUILD and .SRCINFO in directory"
msgstr "לא ניתן לאתר PKGBUILD ו־.SRCINFO בתיקייה"
#: pkg/sync/build/pkg_archive.go:148
msgid "cannot find package name: %v"
msgstr "לא ניתן למצוא את שם החבילה: %v"
#: pkg/sync/build/errors.go:30
msgid "could not find PKGDEST for: %s"
msgstr "לא ניתן למצוא PKGDEST עבור: %s"
#: errors.go:9
msgid "could not find all required packages"
msgstr "לא ניתן למצוא את כל החבילות הנחוצות"
#: pkg/sync/build/errors.go:61
msgid "could not find any package archives listed in %s"
msgstr "לא ניתן למצוא ארכיוני חבילות כלשהם שמופיעים תחת %s"
#: pkg/sync/build/errors.go:50 pkg/upgrade/service.go:287
msgid "dependency"
msgstr "תלות"
#: pkg/vcs/vcs.go:96 pkg/vcs/vcs.go:100
msgid "devel check for package failed: '%s' encountered an error"
msgstr "בדיקת פיתוח לחבילה נכשלה: ‚%s’ נתקל בשגיאה"
#: pkg/menus/edit_menu.go:110
msgid "editor did not exit successfully, aborting: %s"
msgstr "העורך לא יצא כמו שצריך, הפעולה מבוטלת: %s"
#: pkg/sync/workdir/aur_source.go:24
msgid "error downloading sources: %s"
msgstr "שגיאה בהורדת קוד מקור: %s"
#: pkg/download/errors.go:25
msgid "error fetching %s: %s"
msgstr "שגיאה במשיכת %s: %s"
#: pkg/sync/build/errors.go:9
msgid "error installing repo packages"
msgstr "שגיאה בהתקנת חבילות מאגר"
#: pkg/sync/build/installer.go:266 pkg/sync/build/installer.go:270
msgid "error installing:"
msgstr "שגיאה בהתקנה:"
#: pkg/sync/build/installer.go:233 pkg/sync/build/installer.go:237
msgid "error making: %s"
msgstr "שגיאה בבנייה: %s"
#: pkg/sync/workdir/merge.go:24
msgid "error merging %s: %s"
msgstr "שגיאה במיזוג %s: %s"
#: pkg/download/unified.go:59
msgid "error reading %s"
msgstr "שגיאה בקריאת %s"
#: sync.go:36
msgid "error refreshing databases"
msgstr "שגיאה ברענון מסדי נתונים"
#: pkg/sync/workdir/clean.go:51 pkg/sync/workdir/merge.go:17
msgid "error resetting %s: %s"
msgstr "שגיאה באיפוס %s: %s"
#: pkg/sync/build/errors.go:53
msgid "error updating package install reason to %s"
msgstr "שגיאה בעדכון סיבת התקנת החבילה לכדי %s"
#: pkg/sync/build/errors.go:48
msgid "explicit"
msgstr "מפורש"
#: pkg/settings/errors.go:23
msgid "failed to create directory '%s': %s"
msgstr "יצירת התיקייה ‚%s’ נכשלה: %s"
#: pkg/settings/config.go:281
msgid "failed to open config file '%s': %s"
msgstr "פתיחת קובץ ההגדרות ‚%s’ נכשלה: %s"
#: pkg/sync/srcinfo/service.go:114
msgid "failed to parse %s -- skipping: %s"
msgstr "הפענוח של %s נכשל -- מדלגים: %s"
#: pkg/sync/srcinfo/service.go:118
msgid "failed to parse %s: %s"
msgstr "הפענוח של %s נכשל: %s"
#: local_install.go:77
msgid "failed to parse .SRCINFO"
msgstr "פענוח ה־.SRCINFO נכשל"
#: pkg/settings/config.go:291
msgid "failed to read config file '%s': %s"
msgstr "קריאת קובץ ההגדרות ‚%s’ נכשלה: %s"
#: pkg/cmd/graph/main.go:46 pkg/runtime/runtime.go:73
msgid "failed to retrieve aur Cache"
msgstr "משיכת המטמון של ה־aur נכשלה"
#: pkg/upgrade/sources.go:27
msgid "ignoring package devel upgrade (no AUR info found):"
msgstr "התעלמות משדרוג חבילת פיתוח (devel - לא נמצאו פרטים ב־AUR):"
#: pkg/text/errors.go:8
msgid "input too long"
msgstr "הקלט ארוך מדי"
#: pkg/db/ialpm/alpm.go:222 pkg/dep/dep_graph.go:761
msgid "invalid number: %s"
msgstr "מס׳ שגוי: %s"
#: pkg/settings/parser/parser.go:174
msgid "invalid option '%s'"
msgstr "האפשרות ‚%s’ שגויה"
#: cmd.go:197
msgid "invalid option: '--deps' and '--explicit' may not be used together"
msgstr "אפשרות שגויה: אסור להשתמש ב־‚--deps' וב־‚--explicit’ יחד"
#: pkg/download/abs.go:22
msgid "invalid repository"
msgstr "מאגר שגוי"
#: pkg/db/ialpm/alpm.go:227 pkg/dep/dep_graph.go:767
msgid "invalid value: %d is not between %d and %d"
msgstr "ערך שגוי: %d לא בין %d לבין %d"
#: pkg/sync/srcinfo/pgp/keys.go:110
msgid "no keys to import"
msgstr "אין מפתחות לייבוא"
#: pkg/query/errors.go:20
msgid "no query was executed"
msgstr "לא הופעלה שאילתה"
#: local_install.go:66
msgid "no target directories specified"
msgstr "לא צוינו תיקיות יעד"
#: pkg/text/input.go:48
msgid "no"
msgstr "לא"
#: pkg/sync/build/installer.go:242
msgid "nothing to install for %s"
msgstr "אין מה להתקין עבור %s"
#: pkg/settings/parser/parser.go:164
msgid "only one operation may be used at a time"
msgstr "אפשר להשתמש בפעולה אחת בכל רגע נתון"
#: pkg/cmd/graph/main.go:70
msgid "only one target is allowed"
msgstr "מותר רק יעד אחד"
#: print.go:187
msgid "package '%s' was not found"
msgstr "החבילה ‚%s’ נכשלה"
#: pkg/download/errors.go:15
msgid "package not found in AUR"
msgstr "החבילה לא נמצאה ב־AUR"
#: pkg/download/abs.go:23
msgid "package not found in repos"
msgstr "החבילה לא נמצאה במאגרים"
#: pkg/upgrade/service.go:292
msgid "package"
msgid_plural "packages"
msgstr[0] "חבילה"
msgstr[1] "חבילות"
msgstr[2] "חבילות"
#: pkg/sync/srcinfo/pgp/keys.go:100
msgid "problem importing keys"
msgstr "תקלה בייבוא מפתחות"
#: clean.go:105
msgid "removing AUR packages from cache..."
msgstr "חבילות AUR נמחקות מהמטמון…"
#: clean.go:178 pkg/sync/workdir/clean.go:41
msgid "removing untracked AUR files from cache..."
msgstr "קובצי AUR שאינם במעקב נמחקים מהמטמון…"
#: pkg/sync/build/errors.go:38
msgid "the PKGDEST for %s is listed by makepkg but does not exist: %s"
msgstr "ה־PKGDEST עבור %s מוצג על ידי makepkg אך אינו קיים: %s"
#: pkg/sync/sync.go:45
msgid "there is nothing to do"
msgstr "אין מטרות לביצוע"
#: pkg/db/ialpm/alpm.go:247
msgid "unable to CreateHandle: %s"
msgstr "לא ניתן ליצור כינוי (CreateHandle): %s"
#: cmd.go:186
msgid "unhandled operation"
msgstr "פעולה שלא טופלה"
#: cmd.go:450
msgid "unknown-version"
msgstr "גרסה-לא-ידועה"
#: pkg/text/input.go:47
msgid "yes"
msgstr "כן"
================================================
FILE: po/hu.po
================================================
#
# Translators:
# Szigeti Péter, 2021
# summoner , 2025
#
msgid ""
msgstr ""
"Last-Translator: summoner , 2025\n"
"Language-Team: Hungarian (https://app.transifex.com/yay-1/teams/123732/hu/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: hu\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: xgotext\n"
#: clean.go:83
msgid ""
"\n"
"Build directory:"
msgstr ""
"\n"
"Összeállítási könyvtár:"
#: pkg/db/ialpm/alpm.go:201 pkg/dep/dep_graph.go:740
msgid ""
"\n"
"Enter a number (default=1): "
msgstr ""
"\n"
"Írjon be egy számot (alapértelmezett=1): "
#: pkg/menus/menu.go:32
msgid " (Build Files Exist)"
msgstr " (Összeállítási fájlok léteznek)"
#: pkg/menus/menu.go:27
msgid " (Installed)"
msgstr " (Telepítve)"
#: cmd.go:453
msgid " [Installed]"
msgstr " [Telepítve]"
#: cmd.go:410 vote.go:36
msgid " there is nothing to do"
msgstr " nincs tennivaló"
#: pkg/menus/menu.go:49
msgid "%s [A]ll [Ab]ort [I]nstalled [No]tInstalled or (1 2 3, 1-3, ^4)"
msgstr ""
"%s [A]Összes [Ab]Megszakítás [I]Telepített [No]Nem telepített, vagy (1 2 3, "
"1-3, ^4)"
#: pkg/sync/build/installer.go:308
msgid "%s already made -- skipping build"
msgstr "%s már kész -- összeállítás kihagyása"
#: pkg/menus/edit_menu.go:57
msgid "%s is not set"
msgstr "%s nincs beállítva"
#: pkg/settings/exe/cmd_builder.go:257
msgid "%s is present."
msgstr "%s létezik."
#: pkg/dep/dep_graph.go:460 pkg/sync/build/installer.go:305
msgid "%s is up to date -- skipping"
msgstr "%s naprakész -- kihagyás"
#: pkg/upgrade/service.go:291
msgid "%s to upgrade/install."
msgstr "%s a frissítéshez/telepítéshez."
#: pkg/upgrade/service.go:285
msgid "%s will also be installed for this operation."
msgstr "%s is telepítve lesz ehhez a művelethez."
#: pkg/sync/srcinfo/pgp/keys.go:124
msgid "%s, required by: %s"
msgstr "%s, szükséges a következő(k)höz: %s"
#: pkg/menus/diff_menu.go:49
msgid "%s: No changes -- skipping"
msgstr "%s: Nincs változtatás -- kihagyás"
#: pkg/query/filter.go:22
msgid "%s: can't use target with option --aur -- skipping"
msgstr "%s: nem használható a cél az --aur kapcsolóval -- kihagyás"
#: pkg/query/filter.go:17
msgid "%s: can't use target with option --repo -- skipping"
msgstr "%s: nem használható a cél a --repo kapcsolóval -- kihagyás"
#: pkg/upgrade/sources.go:57
msgid "%s: ignoring package upgrade (%s => %s)"
msgstr "%s: csomagfrissítés mellőzése (%s => %s)"
#: pkg/query/aur_warnings.go:46
msgid "%s: local (%s) is newer than AUR (%s)"
msgstr "%s: helyi (%s) frissebb mint az AUR-beli (%s)"
#: vote.go:51
msgid ""
"%s: please set AUR_USERNAME and AUR_PASSWORD environment variables for "
"voting"
msgstr ""
"%s: állítsa be az AUR_USERNAME és AUR_PASSWORD környezeti változókat a "
"szavazáshoz"
#: pkg/download/unified.go:192
msgid "(%d/%d) Downloaded PKGBUILD from ABS: %s"
msgstr "(%d/%d) PKGBUILD letöltve az ABS-ből: %s"
#: pkg/download/aur.go:92 pkg/download/unified.go:188
msgid "(%d/%d) Downloaded PKGBUILD: %s"
msgstr "(%d/%d) PKGBUILD letöltve: %s"
#: pkg/download/aur.go:82
msgid "(%d/%d) Failed to download PKGBUILD: %s"
msgstr "(%d/%d) Nem sikerült letölteni a PKGBUILD-et: %s"
#: pkg/sync/srcinfo/service.go:109
msgid "(%d/%d) Parsing SRCINFO: %s"
msgstr "(%d/%d) SRCINFO vizsgálata: %s"
#: pkg/query/types.go:103 pkg/query/types.go:72
msgid "(Installed)"
msgstr "(Telepítve)"
#: pkg/query/types.go:101 pkg/query/types.go:70
msgid "(Installed: %s)"
msgstr "(Telepítve: %s)"
#: pkg/query/types.go:61
msgid "(Orphaned)"
msgstr "(Elárvult)"
#: pkg/query/types.go:65
msgid "(Out-of-date: %s)"
msgstr "(Elavult: %s)"
#: pkg/dep/dep_graph.go:75
msgid "AUR"
msgstr "AUR"
#: print.go:44
msgid "AUR URL"
msgstr "AUR webcím"
#: pkg/menus/edit_menu.go:58
msgid "Add %s or %s to your environment variables"
msgstr "Állítsa be a(z) %s vagy a(z) %s környezeti változót"
#: main.go:60
msgid "Avoid running yay as root/sudo."
msgstr "Ne futtassa a yay-t root-ként/sudo-val."
#: pkg/dep/dep_graph.go:63
msgid "Check Dependency"
msgstr "Függőség ellenőrzése"
#: print.go:41
msgid "Check Deps"
msgstr "Függőségek ellenőrzése"
#: pkg/upgrade/service.go:89
msgid "Checking development packages..."
msgstr "Fejlesztői csomagok ellenőrzése…"
#: pkg/sync/workdir/clean.go:45
msgid "Cleaning (%d/%d): %s"
msgstr "Tisztítás (%d/%d): %s"
#: print.go:42
msgid "Conflicts With"
msgstr "Ütközik a következővel"
#: pkg/menus/clean_menu.go:62
msgid "Deleting (%d/%d): %s"
msgstr "Törlés (%d/%d): %s"
#: pkg/dep/dep_graph.go:61
msgid "Dependency"
msgstr "Függőség:"
#: print.go:38
msgid "Depends On"
msgstr "Függőség(ek)"
#: print.go:33
msgid "Description"
msgstr "Leírás"
#: pkg/menus/diff_menu.go:160
msgid "Diffs to show?"
msgstr "Összehasonlítások megjelenítése?"
#: pkg/settings/migrations.go:25
msgid "Disable 'provides' setting by default"
msgstr "A „provides” opció alapértelmezett kikapcsolása"
#: clean.go:78
msgid "Do you want to remove ALL AUR packages from cache?"
msgstr ""
"Biztosan el akarja távolítani az ÖSSZES AUR csomagot a gyorsítótárból?"
#: clean.go:95
msgid "Do you want to remove ALL untracked AUR files?"
msgstr "Biztosan el akarja távolítani az ÖSSZES nem követett AUR fájlt?"
#: clean.go:80
msgid "Do you want to remove all other AUR packages from cache?"
msgstr ""
"Biztosan el akarja távolítani az összes többi AUR csomagot a gyorsítótárból?"
#: pkg/menus/edit_menu.go:61
msgid "Edit PKGBUILD with?"
msgstr "Mivel szerkeszti a PKGBUILD-et?"
#: pkg/query/errors.go:13
msgid "Error during AUR search: %s\n"
msgstr "Hiba történt az AUR-ban való kereséskor: %s\n"
#: pkg/upgrade/service.go:295
msgid "Excluding packages may cause partial upgrades and break systems"
msgstr ""
"A csomagok kizárása részleges frissítéseket és rendszertöréseket okozhat"
#: pkg/dep/dep_graph.go:60
msgid "Explicit"
msgstr "Kifejezetten"
#: print.go:91
msgid "Explicitly installed packages: %s"
msgstr "Kifejezetten telepített csomagok: %s"
#: pkg/dep/dep_graph.go:437 pkg/dep/dep_graph.go:535
msgid "Failed to find AUR package for"
msgstr "Nem sikerült megtalálni a következőhöz tartozó AUR-csomagot:"
#: pkg/sync/build/installer.go:120
msgid "Failed to install layer, rolling up to next layer."
msgstr "Nem sikerült telepíteni a réteget, áttérés a következő rétegre."
#: pkg/sync/build/errors.go:16
msgid ""
"Failed to install the following packages. Manual intervention is required:"
msgstr ""
"Nem sikerült telepíteni a következő csomagokat. Kézi beavatkozás szükséges:"
#: print.go:45
msgid "First Submitted"
msgstr "Először beküldve"
#: pkg/query/aur_warnings.go:83
msgid "Flagged Out Of Date AUR Packages:"
msgstr "Elavultnak jelölt AUR-csomagok:"
#: print.go:90
msgid "Foreign installed packages: %s"
msgstr "Idegen telepített csomagok: %s"
#: pkg/vcs/vcs.go:144
msgid "Found git repo: %s"
msgstr "Git-tároló találat: %s"
#: vcs.go:72
msgid "GenDB finished. No packages were installed"
msgstr "GenDB végzett. Nem lett új csomag telepítve"
#: print.go:36
msgid "Groups"
msgstr "Csoportok"
#: pkg/sync/srcinfo/pgp/keys.go:88
msgid "Import?"
msgstr "Importálás?"
#: pkg/sync/srcinfo/pgp/keys.go:97
msgid "Importing keys with gpg..."
msgstr "Kulcsok importálása gpg-vel…"
#: print.go:46
msgid "Keywords"
msgstr "Kulcsszavak"
#: print.go:47
msgid "Last Modified"
msgstr "Legutóbb módosítva"
#: print.go:35
msgid "Licenses"
msgstr "Licencek"
#: pkg/dep/dep_graph.go:77
msgid "Local"
msgstr "Helyi"
#: print.go:48
msgid "Maintainer"
msgstr "Karbantartó"
#: pkg/dep/dep_graph.go:62
msgid "Make Dependency"
msgstr "Fordítási függőség"
#: print.go:40
msgid "Make Deps"
msgstr "Fordítási függőségek"
#: pkg/dep/dep_graph.go:79
msgid "Missing"
msgstr "Hiányzik"
#: pkg/query/aur_warnings.go:75
msgid "Missing AUR Debug Packages:"
msgstr "Hiányzó AUR-hibakereső-csomagok:"
#: print.go:31
msgid "Name"
msgstr "Név"
#: pkg/dep/dep_graph.go:442 pkg/dep/dep_graph.go:564
msgid "No AUR package found for"
msgstr "Nem található AUR-csomag a következőhöz:"
#: pkg/dep/dep_graph.go:564
msgid "required by"
msgstr "szükséges a következő(k)höz"
#: pkg/dep/dep_graph.go:182
msgid "No package found for"
msgstr "Nem található csomag a következőhöz:"
#: print.go:225
msgid "None"
msgstr "Semmi"
#: print.go:39
msgid "Optional Deps"
msgstr "Nem kötelező függőségek"
#: pkg/query/aur_warnings.go:79
msgid "Orphan (unmaintained) AUR Packages:"
msgstr "Árva (nem karbantartott) AUR-csomagok:"
#: print.go:53 print.go:55
msgid "Out-of-date"
msgstr "Elavult"
#: pkg/sync/srcinfo/pgp/keys.go:115
msgid "PGP keys need importing:"
msgstr "Importálandó PGP-kulcsok:"
#: pkg/sync/workdir/preparer.go:252
msgid "PKGBUILD up to date, skipping download: %s"
msgstr "A PKGBUILD naprakész, a letöltés kihagyása: %s"
#: pkg/menus/edit_menu.go:130
msgid "PKGBUILDs to edit?"
msgstr "PKGBUILD-ek szerkesztése?"
#: print.go:61
msgid "Package Base"
msgstr "Csomag alapja"
#: print.go:60
msgid "Package Base ID"
msgstr "Csomagazonosító-alap"
#: pkg/query/aur_warnings.go:71
msgid "Packages not in AUR:"
msgstr "A következő csomag nincs az AUR-ban:"
#: pkg/menus/clean_menu.go:54
msgid "Packages to cleanBuild?"
msgstr "Csomagok a tiszta összeállításhoz?"
#: pkg/dep/dep_graph.go:202
msgid "Packages to exclude"
msgstr "Kizárandó csomagok"
#: pkg/upgrade/service.go:294
msgid "Packages to exclude: (eg: \"1 2 3\", \"1-3\", \"^4\" or repo name)"
msgstr ""
"Kizárandó csomagok: (például: „1 2 3”, „1-3”, „^4”, vagy a tároló neve)"
#: cmd.go:392
msgid "Packages to install (eg: 1 2 3, 1-3 or ^4)"
msgstr "Telepítendő csomagok (például: „1 2 3”, „1-3” vagy „^4”)"
#: print.go:49
msgid "Popularity"
msgstr "Népszerűség"
#: pkg/menus/diff_menu.go:172 pkg/menus/edit_menu.go:143
msgid "Proceed with install?"
msgstr "Folytatja a telepítést?"
#: print.go:37
msgid "Provides"
msgstr "Szolgáltatók"
#: pkg/sync/workdir/preparer.go:125
msgid "Remove make dependencies after install?"
msgstr "Fordítási függőségek eltávolítása a telepítés után?"
#: print.go:43
msgid "Replaces"
msgstr "Cserék"
#: pkg/db/ialpm/alpm.go:191 print.go:30
msgid "Repository"
msgstr "Tároló"
#: pkg/dep/dep_graph.go:730
msgid "Repository AUR"
msgstr "AUR-tároló"
#: pkg/dep/dep_graph.go:78
msgid "SRCINFO"
msgstr "SRCINFO"
#: pkg/upgrade/service.go:71
msgid "Searching AUR for updates..."
msgstr "Frissítések keresése az AUR-ban…"
#: pkg/upgrade/service.go:159
msgid "Searching databases for updates..."
msgstr "Frissítések keresése az adatbázisokban…"
#: pkg/query/query_builder.go:214
msgid "Showing repo packages only"
msgstr "Csak a tároló csomagjainak megjelenítése"
#: print.go:95
msgid "Size of pacman cache %s: %s"
msgstr "A pacman gyorsítótárának mérete %s: %s"
#: print.go:98
msgid "Size of yay cache %s: %s"
msgstr "A yay gyorsítótárának mérete %s: %s"
#: print.go:62
msgid "Snapshot URL"
msgstr "Pillanatkép webcíme"
#: pkg/dep/dep_graph.go:76
msgid "Sync"
msgstr "Szinkronizálás"
#: print.go:100
msgid "Ten biggest packages:"
msgstr "A tíz legnagyobb csomag:"
#: pkg/sync/sync.go:124
msgid "The following packages are not compatible with your architecture:"
msgstr ""
"A következő csomagok nem kompatibilisek a processzor architektúrájával:"
#: pkg/db/ialpm/alpm.go:179 pkg/dep/dep_graph.go:726
msgid "There are %[1]d providers available for %[2]s:"
msgstr "%[1]d szolgáltató érhető el a következőhöz: %[2]d:"
#: pkg/settings/exe/cmd_builder.go:258
msgid "There may be another Pacman instance running. Waiting..."
msgstr "Lehet, hogy már fut a pacman. Kis türelmet…"
#: print.go:92
msgid "Total Size occupied by packages: %s"
msgstr "Csomagok által elfoglalt összes hely: %s"
#: print.go:89
msgid "Total installed packages: %s"
msgstr "Összes telepített csomag: %s"
#: pkg/sync/sync.go:132
msgid "Try to build them anyway?"
msgstr "Mégis megpróbálja összeállítani őket?"
#: print.go:34
msgid "URL"
msgstr "Webcím"
#: clean.go:194 pkg/menus/clean_menu.go:65 pkg/menus/clean_menu.go:71
msgid "Unable to clean:"
msgstr "Nem tisztítható:"
#: get.go:42 get.go:74
msgid "Unable to find the following packages:"
msgstr "Nem sikerült megtalálni a következő csomagokat:"
#: vote.go:20
msgid "Unable to handle package vote for: %s. err: %s"
msgstr ""
"Nem lehet feldolgozni a következő csomagra való szavazást: %s. hiba: %s"
#: clean.go:170
msgid "Unable to remove %s: %s"
msgstr "Nem lehet eltávolítani a következőt: %s: %s"
#: print.go:32
msgid "Version"
msgstr "Verzió"
#: print.go:50
msgid "Votes"
msgstr "Szavazatok"
#: print.go:87
msgid "Yay version v%s"
msgstr "Yay verziója: v%s"
#: pkg/menus/menu.go:49
msgid "[N]one"
msgstr "[N]semmi"
#: pkg/settings/errors.go:29
msgid "aborting due to user"
msgstr "a felhasználó kérésére megszakítva"
#: pkg/settings/parser/parser.go:608
msgid "argument '-' specified without input on stdin"
msgstr "„-” argumentum lett megadva az stdin bemenete nélkül"
#: local_install.go:26
msgid "cannot find PKGBUILD and .SRCINFO in directory"
msgstr "nem található a PKGBUILD és az .SRCINFO a könyvtárban"
#: pkg/sync/build/pkg_archive.go:148
msgid "cannot find package name: %v"
msgstr "nem található a csomagnév: %v"
#: pkg/sync/build/errors.go:30
msgid "could not find PKGDEST for: %s"
msgstr "nem található a csomaghoz PKGDEST: %s"
#: errors.go:9
msgid "could not find all required packages"
msgstr "nem sikerült megtalálni minden szükséges csomagot"
#: pkg/sync/build/errors.go:61
msgid "could not find any package archives listed in %s"
msgstr ""
"nem sikerült megtalálni egyetlen csomagarchívumot sem a(z) %s listában"
#: pkg/sync/build/errors.go:50 pkg/upgrade/service.go:286
msgid "dependency"
msgstr "függőség"
#: pkg/vcs/vcs.go:100 pkg/vcs/vcs.go:96
msgid "devel check for package failed: '%s' encountered an error"
msgstr "a csomag fejlesztői ellenőrzése nem sikerült: a(z) „%s” hibát észlelt"
#: pkg/menus/edit_menu.go:110
msgid "editor did not exit successfully, aborting: %s"
msgstr "nem lépett ki sikeresen a szerkesztő, megszakítva: %s"
#: pkg/sync/workdir/aur_source.go:24
msgid "error downloading sources: %s"
msgstr "hiba a következő forrásfájl(ok) letöltésekor: %s"
#: pkg/download/errors.go:25
msgid "error fetching %s: %s"
msgstr "hiba a következő letöltésekor: %s: %s"
#: pkg/sync/build/errors.go:9
msgid "error installing repo packages"
msgstr "hiba a tárolóból származó csomagok telepítésekor"
#: pkg/sync/build/installer.go:266 pkg/sync/build/installer.go:270
msgid "error installing:"
msgstr "hiba a következő telepítésekor:"
#: pkg/sync/build/installer.go:233 pkg/sync/build/installer.go:237
msgid "error making: %s"
msgstr "hiba a következő fordításakor: %s"
#: pkg/sync/workdir/merge.go:24
msgid "error merging %s: %s"
msgstr "hiba a következő beolvasztásakor: %s: %s"
#: pkg/download/unified.go:59
msgid "error reading %s"
msgstr "hiba a következő olvasásakor: %s"
#: sync.go:36
msgid "error refreshing databases"
msgstr "hiba az adatbázisok frissítésekor"
#: pkg/sync/workdir/clean.go:51 pkg/sync/workdir/merge.go:17
msgid "error resetting %s: %s"
msgstr "hiba a következő visszaállításakor: %s: %s"
#: pkg/sync/build/errors.go:53
msgid "error updating package install reason to %s"
msgstr "hiba a csomag telepítési okának frissítésekor a következőre: %s"
#: pkg/sync/build/errors.go:48
msgid "explicit"
msgstr "kifejezetten"
#: pkg/settings/errors.go:23
msgid "failed to create directory '%s': %s"
msgstr "nem sikerült létrehozni a következő könyvtárat: „%s”: %s"
#: pkg/settings/config.go:281
msgid "failed to open config file '%s': %s"
msgstr "nem sikerült megnyitni a következő könyvtárat: „%s”: %s"
#: pkg/sync/srcinfo/service.go:114
msgid "failed to parse %s -- skipping: %s"
msgstr "nem sikerült elemezni a következőt: %s -- kihagyás: %s"
#: pkg/sync/srcinfo/service.go:118
msgid "failed to parse %s: %s"
msgstr "nem sikerült elemezni a következőt: %s: %s"
#: local_install.go:77
msgid "failed to parse .SRCINFO"
msgstr "nem sikerült elemezni az .SRCINFO-t"
#: pkg/settings/config.go:291
msgid "failed to read config file '%s': %s"
msgstr "nem sikerült olvasni a következő konfigurációs fájlt: „%s”: %s"
#: pkg/cmd/graph/main.go:46 pkg/runtime/runtime.go:73
msgid "failed to retrieve aur Cache"
msgstr "nem sikerült lekérni az AUR-gyorsítótárát"
#: pkg/upgrade/sources.go:27
msgid "ignoring package devel upgrade (no AUR info found):"
msgstr ""
"fejlesztői csomag frissítésének mellőzése (nem található AUR-információ):"
#: pkg/text/errors.go:8
msgid "input too long"
msgstr "a bemenet túl hosszú"
#: pkg/db/ialpm/alpm.go:222 pkg/dep/dep_graph.go:761
msgid "invalid number: %s"
msgstr "érvénytelen szám: %s"
#: pkg/settings/parser/parser.go:174
msgid "invalid option '%s'"
msgstr "érvénytelen opció: „%s”"
#: cmd.go:197
msgid "invalid option: '--deps' and '--explicit' may not be used together"
msgstr "érvénytelen opció: „--deps” és „--explicit” nem használható egyszerre"
#: pkg/download/abs.go:22
msgid "invalid repository"
msgstr "érvénytelen tároló"
#: pkg/db/ialpm/alpm.go:227 pkg/dep/dep_graph.go:767
msgid "invalid value: %d is not between %d and %d"
msgstr "érvénytelen érték: %d, az érték nem %d és %d között van"
#: pkg/text/input.go:48
msgid "no"
msgstr "nem"
#: pkg/sync/srcinfo/pgp/keys.go:110
msgid "no keys to import"
msgstr "nincs importálandó kulcs"
#: pkg/query/errors.go:20
msgid "no query was executed"
msgstr "nem történt lekérdezés"
#: local_install.go:66
msgid "no target directories specified"
msgstr "nincs megadva célkönyvtár"
#: pkg/sync/build/installer.go:242
msgid "nothing to install for %s"
msgstr "nincs mit telepíteni a következőhöz: %s"
#: pkg/settings/parser/parser.go:164
msgid "only one operation may be used at a time"
msgstr "egyszerre csak egy művelet használható"
#: pkg/cmd/graph/main.go:70
msgid "only one target is allowed"
msgstr "csak egy cél engedélyezett"
#: pkg/upgrade/service.go:291
msgid "package"
msgid_plural "packages"
msgstr[0] "csomag"
msgstr[1] "csomagok"
#: print.go:187
msgid "package '%s' was not found"
msgstr "nem található a(z) „%s” csomag"
#: pkg/download/errors.go:15
msgid "package not found in AUR"
msgstr "a csomag nem található az AUR-ban"
#: pkg/download/abs.go:23
msgid "package not found in repos"
msgstr "a csomag nem található a tárolókban"
#: pkg/sync/srcinfo/pgp/keys.go:100
msgid "problem importing keys"
msgstr "hiba a kulcsok importálásakor"
#: clean.go:105
msgid "removing AUR packages from cache..."
msgstr "AUR-csomagok eltávolítása a gyorsítótárból…"
#: clean.go:178 pkg/sync/workdir/clean.go:41
msgid "removing untracked AUR files from cache..."
msgstr "nem követett AUR-fájlok eltávolítása a gyorsítótárból…"
#: pkg/sync/build/errors.go:38
msgid "the PKGDEST for %s is listed by makepkg but does not exist: %s"
msgstr "a(z) %s PKGDEST-jét a makepkg listázza, de nem létezik: %s"
#: pkg/sync/sync.go:45
msgid "there is nothing to do"
msgstr "nincs semmi tennivaló"
#: pkg/db/ialpm/alpm.go:247
msgid "unable to CreateHandle: %s"
msgstr "sikertelen CreateHandle: %s"
#: cmd.go:186
msgid "unhandled operation"
msgstr "nem kezelt művelet"
#: cmd.go:450
msgid "unknown-version"
msgstr "ismeretlen verzió"
#: pkg/text/input.go:47
msgid "yes"
msgstr "igen"
================================================
FILE: po/id.po
================================================
#
# Translators:
# Ludovico, 2022
# Alif Fathur, 2024
# Linerly , 2024
#
msgid ""
msgstr ""
"Last-Translator: Linerly , 2024\n"
"Language-Team: Indonesian (https://app.transifex.com/yay-1/teams/123732/id/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: id\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: xgotext\n"
#: clean.go:83
msgid ""
"\n"
"Build directory:"
msgstr ""
"\n"
"Direktori build:"
#: pkg/db/ialpm/alpm.go:201 pkg/dep/dep_graph.go:740
msgid ""
"\n"
"Enter a number (default=1): "
msgstr ""
"\n"
"Masukkan sebuah nomor (bawaan=1):"
#: pkg/menus/menu.go:32
msgid " (Build Files Exist)"
msgstr "(File build Sudah Ada)"
#: pkg/menus/menu.go:27
msgid " (Installed)"
msgstr "(Terpasang)"
#: cmd.go:453
msgid " [Installed]"
msgstr "[Terpasang]"
#: cmd.go:410 vote.go:36
msgid " there is nothing to do"
msgstr "tidak ada yang dapat dilakukan"
#: pkg/menus/menu.go:49
msgid "%s [A]ll [Ab]ort [I]nstalled [No]tInstalled or (1 2 3, 1-3, ^4)"
msgstr ""
"%s [A]Semua [Ab]Batalkan [I]Terpasang [No]Belum terpasang atau (1 2 3, 1-3, "
"^4)"
#: pkg/sync/build/installer.go:308
msgid "%s already made -- skipping build"
msgstr "%s sudah dibuat -- melewatkan proses build"
#: pkg/menus/edit_menu.go:57
msgid "%s is not set"
msgstr "%s belum diatur"
#: pkg/settings/exe/cmd_builder.go:257
msgid "%s is present."
msgstr "%s sudah ada."
#: pkg/dep/dep_graph.go:460 pkg/sync/build/installer.go:305
msgid "%s is up to date -- skipping"
msgstr "%s sudah yang terbaru -- melewatkan"
#: pkg/upgrade/service.go:291
msgid "%s to upgrade/install."
msgstr "%s untuk diupgrade/dipasang."
#: pkg/upgrade/service.go:285
msgid "%s will also be installed for this operation."
msgstr "%s juga akan dipasang untuk tindakan ini."
#: pkg/sync/srcinfo/pgp/keys.go:124
msgid "%s, required by: %s"
msgstr "%s, dibutuhkan oleh: %s"
#: pkg/menus/diff_menu.go:49
msgid "%s: No changes -- skipping"
msgstr "%s: Tidak ada perubahan -- melewatkan"
#: pkg/query/filter.go:22
msgid "%s: can't use target with option --aur -- skipping"
msgstr "%s: tidak dapat menggunakan target dengan --aur -- melewatkan"
#: pkg/query/filter.go:17
msgid "%s: can't use target with option --repo -- skipping"
msgstr "%s: tidak dapat menggunakan target dengan opsi --repo -- melewatkan"
#: pkg/upgrade/sources.go:57
msgid "%s: ignoring package upgrade (%s => %s)"
msgstr "%s: mengabaikan pengupgradean paket (%s => %s)"
#: pkg/query/aur_warnings.go:46
msgid "%s: local (%s) is newer than AUR (%s)"
msgstr "%s: lokal (%s) lebih baru dari yang AUR (%s)"
#: vote.go:51
msgid ""
"%s: please set AUR_USERNAME and AUR_PASSWORD environment variables for "
"voting"
msgstr ""
"%s: seilahkan atur variabel environment AUR_USERNAME dan AUR_PASSWORD untuk "
"pemungutan suara"
#: pkg/download/unified.go:192
msgid "(%d/%d) Downloaded PKGBUILD from ABS: %s"
msgstr "(%d/%d) PKGBUILD diunduh dari ABS: %s"
#: pkg/download/aur.go:92 pkg/download/unified.go:188
msgid "(%d/%d) Downloaded PKGBUILD: %s"
msgstr "(%d/%d) PKGBUILD terunduh: %s"
#: pkg/download/aur.go:82
msgid "(%d/%d) Failed to download PKGBUILD: %s"
msgstr "(%d/%d) Gagal mengunduh PKGBUILD: %s"
#: pkg/sync/srcinfo/service.go:109
msgid "(%d/%d) Parsing SRCINFO: %s"
msgstr "(%d/%d) Menguraikan SRCINFO: %s"
#: pkg/query/types.go:103 pkg/query/types.go:72
msgid "(Installed)"
msgstr "(Terpasang)"
#: pkg/query/types.go:101 pkg/query/types.go:70
msgid "(Installed: %s)"
msgstr "(Terpasang: %s)"
#: pkg/query/types.go:61
msgid "(Orphaned)"
msgstr "(Ditinggalkan)"
#: pkg/query/types.go:65
msgid "(Out-of-date: %s)"
msgstr "(Kedaluwarsa: %s)"
#: pkg/dep/dep_graph.go:75
msgid "AUR"
msgstr "AUR"
#: print.go:44
msgid "AUR URL"
msgstr "URL AUR"
#: pkg/menus/edit_menu.go:58
msgid "Add %s or %s to your environment variables"
msgstr "Tambahkan %s atau %s ke variabel environment Anda"
#: main.go:60
msgid "Avoid running yay as root/sudo."
msgstr "Hindari menjalankan yay sebagai root/sudo."
#: pkg/dep/dep_graph.go:63
msgid "Check Dependency"
msgstr "Periksa dependensi"
#: print.go:41
msgid "Check Deps"
msgstr "Periksa Ketergantungan"
#: pkg/upgrade/service.go:89
msgid "Checking development packages..."
msgstr "Memeriksa paket-paket pengembangan..."
#: pkg/sync/workdir/clean.go:45
msgid "Cleaning (%d/%d): %s"
msgstr "Membersihkan (%d/%d): %s"
#: print.go:42
msgid "Conflicts With"
msgstr "Berkonflik Dengan"
#: pkg/menus/clean_menu.go:62
msgid "Deleting (%d/%d): %s"
msgstr "Menghapus (%d/%d): %s"
#: pkg/dep/dep_graph.go:61
msgid "Dependency"
msgstr "Ketergantungan"
#: print.go:38
msgid "Depends On"
msgstr "Tergantung Pada"
#: print.go:33
msgid "Description"
msgstr "Deskripsi"
#: pkg/menus/diff_menu.go:160
msgid "Diffs to show?"
msgstr "Tampilkan perbedaan?"
#: pkg/settings/migrations.go:25
msgid "Disable 'provides' setting by default"
msgstr "Nonaktifkan pengaturan 'menyediakan' secara bawaan"
#: clean.go:78
msgid "Do you want to remove ALL AUR packages from cache?"
msgstr "Apakah Anda ingin menghapus SEMUA paket AUR dari cache?"
#: clean.go:95
msgid "Do you want to remove ALL untracked AUR files?"
msgstr "Apakah Anda ingin menghapus SEMUA file AUR yang tidak dilacak?"
#: clean.go:80
msgid "Do you want to remove all other AUR packages from cache?"
msgstr "Apakah Anda ingin menghapus semua paket AUR lainnya dari cache?"
#: pkg/menus/edit_menu.go:61
msgid "Edit PKGBUILD with?"
msgstr "Edit PKGBUILD dengan?"
#: pkg/query/errors.go:13
msgid "Error during AUR search: %s\n"
msgstr "Terjadi kesalahan melakukan pencarian AUR: %s\n"
#: pkg/upgrade/service.go:295
msgid "Excluding packages may cause partial upgrades and break systems"
msgstr ""
"Tidak menyertakan paket dapat menyebabkan peningkatan parsial dan merusak "
"sistem"
#: pkg/dep/dep_graph.go:60
msgid "Explicit"
msgstr "Eksplisit"
#: print.go:91
msgid "Explicitly installed packages: %s"
msgstr "Paket yang terpasang secara eksplisit: %s"
#: pkg/dep/dep_graph.go:437 pkg/dep/dep_graph.go:535
msgid "Failed to find AUR package for"
msgstr "Gagal mencari paket AUR untuk"
#: pkg/sync/build/installer.go:120
msgid "Failed to install layer, rolling up to next layer."
msgstr "Gagal memasang lapisan, menggulirkan ke lapisan berikutnya."
#: pkg/sync/build/errors.go:16
msgid ""
"Failed to install the following packages. Manual intervention is required:"
msgstr "Gagal memasang paket berikut. Intervensi manual dibutuhkan:"
#: print.go:45
msgid "First Submitted"
msgstr "Pengiriman Pertama"
#: pkg/query/aur_warnings.go:83
msgid "Flagged Out Of Date AUR Packages:"
msgstr "Paket AUR Yang Ditandai Sebagai Kedaluwarsa:"
#: print.go:90
msgid "Foreign installed packages: %s"
msgstr "Total paket-paket asing yang terpasang: %s"
#: pkg/vcs/vcs.go:144
msgid "Found git repo: %s"
msgstr "Ditemukan repositori git: %s"
#: vcs.go:72
msgid "GenDB finished. No packages were installed"
msgstr "GenDB selesai. Tidak ada paket yang terpasang"
#: print.go:36
msgid "Groups"
msgstr "Grup"
#: pkg/sync/srcinfo/pgp/keys.go:88
msgid "Import?"
msgstr "Impor?"
#: pkg/sync/srcinfo/pgp/keys.go:97
msgid "Importing keys with gpg..."
msgstr "Mengimpor kunci-kunci dengan gpg..."
#: print.go:46
msgid "Keywords"
msgstr "Kata Kunci"
#: print.go:47
msgid "Last Modified"
msgstr "Terakhir Diubah"
#: print.go:35
msgid "Licenses"
msgstr "Lisensi"
#: pkg/dep/dep_graph.go:77
msgid "Local"
msgstr "Lokal"
#: print.go:48
msgid "Maintainer"
msgstr "Pemelihara"
#: pkg/dep/dep_graph.go:62
msgid "Make Dependency"
msgstr "Ketergantungan Make"
#: print.go:40
msgid "Make Deps"
msgstr "Dependensi Make"
#: pkg/dep/dep_graph.go:79
msgid "Missing"
msgstr "Tidak Ditemukan"
#: pkg/query/aur_warnings.go:75
msgid "Missing AUR Debug Packages:"
msgstr "Paket Debug AUR Yang Tidak Ditemukan:"
#: print.go:31
msgid "Name"
msgstr "Nama"
#: pkg/dep/dep_graph.go:442 pkg/dep/dep_graph.go:548
msgid "No AUR package found for"
msgstr "Tidak ada paket AUR yang ditemukan untuk"
#: pkg/dep/dep_graph.go:182
msgid "No package found for"
msgstr "Tidak ada paket yang ditemukan untuk"
#: print.go:225
msgid "None"
msgstr "Tidak Ada"
#: print.go:39
msgid "Optional Deps"
msgstr "Dependensi Opsional"
#: pkg/query/aur_warnings.go:79
msgid "Orphan (unmaintained) AUR Packages:"
msgstr "Paket AUR Tersendiri (tidak dipelihara):"
#: print.go:53 print.go:55
msgid "Out-of-date"
msgstr "Kedaluwarsa"
#: pkg/sync/srcinfo/pgp/keys.go:115
msgid "PGP keys need importing:"
msgstr "Kunci-kunci PGP membutuhkan pengimporan:"
#: pkg/sync/workdir/preparer.go:252
msgid "PKGBUILD up to date, skipping download: %s"
msgstr "PKGBUILD sudah terbaru, melewati pengunduhan: %s"
#: pkg/menus/edit_menu.go:130
msgid "PKGBUILDs to edit?"
msgstr "PKGBUILD apa saja untuk diedit?"
#: print.go:61
msgid "Package Base"
msgstr "Basis Paket"
#: print.go:60
msgid "Package Base ID"
msgstr "ID Basis Paket"
#: pkg/query/aur_warnings.go:71
msgid "Packages not in AUR:"
msgstr "Paket yang tidak ada di AUR:"
#: pkg/menus/clean_menu.go:54
msgid "Packages to cleanBuild?"
msgstr "Paket-paket di bangunBersih?"
#: pkg/dep/dep_graph.go:202
msgid "Packages to exclude"
msgstr "Paket untuk tidak disertakan"
#: pkg/upgrade/service.go:294
msgid "Packages to exclude: (eg: \"1 2 3\", \"1-3\", \"^4\" or repo name)"
msgstr ""
"Paket-paket yang tidak disertakan: (mis: \"1 2 3\", \"1-3\", \"^4\", atau "
"nama repositori)"
#: cmd.go:392
msgid "Packages to install (eg: 1 2 3, 1-3 or ^4)"
msgstr "Paket-paket untuk dipasang (mis: 1 2 3, 1-3, atau ^4)"
#: print.go:49
msgid "Popularity"
msgstr "Popularitas"
#: pkg/menus/diff_menu.go:172 pkg/menus/edit_menu.go:143
msgid "Proceed with install?"
msgstr "Lanjutkan dengan instalasi?"
#: print.go:37
msgid "Provides"
msgstr "Menyediakan"
#: pkg/sync/workdir/preparer.go:125
msgid "Remove make dependencies after install?"
msgstr "Hapus dependensi make setelah pemasangan?"
#: print.go:43
msgid "Replaces"
msgstr "Menggantikan"
#: pkg/db/ialpm/alpm.go:191 print.go:30
msgid "Repository"
msgstr "Repositori"
#: pkg/dep/dep_graph.go:730
msgid "Repository AUR"
msgstr "Repositori AUR"
#: pkg/dep/dep_graph.go:78
msgid "SRCINFO"
msgstr "SRCINFO"
#: pkg/upgrade/service.go:71
msgid "Searching AUR for updates..."
msgstr "Mencari AUR untuk pembaruan..."
#: pkg/upgrade/service.go:159
msgid "Searching databases for updates..."
msgstr "Mencari database untuk pembaruan..."
#: pkg/query/query_builder.go:214
msgid "Showing repo packages only"
msgstr "Menampilkan paket-paket repositori saja"
#: print.go:95
msgid "Size of pacman cache %s: %s"
msgstr "Ukuran cache pacman %s: %s"
#: print.go:98
msgid "Size of yay cache %s: %s"
msgstr "Ukuran cache yay%s: %s"
#: print.go:62
msgid "Snapshot URL"
msgstr "URL Snapshot"
#: pkg/dep/dep_graph.go:76
msgid "Sync"
msgstr "Sinkron"
#: print.go:100
msgid "Ten biggest packages:"
msgstr "Sepuluh paket terbesar:"
#: pkg/sync/sync.go:124
msgid "The following packages are not compatible with your architecture:"
msgstr "Paket-paket berikut ini tidak kompatibel dengan arsitektur Anda:"
#: pkg/db/ialpm/alpm.go:179 pkg/dep/dep_graph.go:726
msgid "There are %[1]d providers available for %[2]s:"
msgstr "Tersedia %[1]d penyedia untuk %[2]s:"
#: pkg/settings/exe/cmd_builder.go:258
msgid "There may be another Pacman instance running. Waiting..."
msgstr "Mungkin ada instansi Pacman yang lain. Menunggu..."
#: print.go:92
msgid "Total Size occupied by packages: %s"
msgstr "Total Ukuran ditempati oleh paket-paket: %s"
#: print.go:89
msgid "Total installed packages: %s"
msgstr "Total paket-paket terpasang: %s"
#: pkg/sync/sync.go:132
msgid "Try to build them anyway?"
msgstr "Coba membangun saja?"
#: print.go:34
msgid "URL"
msgstr "URL"
#: clean.go:194 pkg/menus/clean_menu.go:65 pkg/menus/clean_menu.go:71
msgid "Unable to clean:"
msgstr "Tidak dapat membersihkan:"
#: get.go:42 get.go:74
msgid "Unable to find the following packages:"
msgstr "Tidak dapat mencari paket-paket berikut ini:"
#: vote.go:20
msgid "Unable to handle package vote for: %s. err: %s"
msgstr "Tidak dapat menangani pemungutan suara paket untuk: %s. kesalahan: %s"
#: clean.go:170
msgid "Unable to remove %s: %s"
msgstr "Tidak dapat menghapus %s: %s"
#: print.go:32
msgid "Version"
msgstr "Versi"
#: print.go:50
msgid "Votes"
msgstr "Suara"
#: print.go:87
msgid "Yay version v%s"
msgstr "Yay versi v%s"
#: pkg/menus/menu.go:49
msgid "[N]one"
msgstr "[T]idak Ada"
#: pkg/settings/errors.go:29
msgid "aborting due to user"
msgstr "membatalkan karena pengguna"
#: pkg/settings/parser/parser.go:608
msgid "argument '-' specified without input on stdin"
msgstr "argumen '-' ditentukan tanpa masukan pada stdin"
#: local_install.go:26
msgid "cannot find PKGBUILD and .SRCINFO in directory"
msgstr "tidak dapat menemukan PKGBUILD dan .SRCINFO di direktori"
#: pkg/sync/build/pkg_archive.go:148
msgid "cannot find package name: %v"
msgstr "tidak dapat menemukan nama paket: %v"
#: pkg/sync/build/errors.go:30
msgid "could not find PKGDEST for: %s"
msgstr "tidak dapat menemukan PKGDEST untuk: %s"
#: errors.go:9
msgid "could not find all required packages"
msgstr "tidak dapat menemukan semua paket yang dibutuhkan"
#: pkg/sync/build/errors.go:61
msgid "could not find any package archives listed in %s"
msgstr "tidak dapat mencari paket arsip apa pun yang terdaftar di %s"
#: pkg/sync/build/errors.go:50 pkg/upgrade/service.go:286
msgid "dependency"
msgstr "ketergantungan"
#: pkg/vcs/vcs.go:100 pkg/vcs/vcs.go:96
msgid "devel check for package failed: '%s' encountered an error"
msgstr "pemeriksaan devel untuk paket gagal: '%s' mengalami sebuah kesalahan"
#: pkg/menus/edit_menu.go:110
msgid "editor did not exit successfully, aborting: %s"
msgstr "editor tidak berhasil keluar, membatalkan: %s"
#: pkg/sync/workdir/aur_source.go:24
msgid "error downloading sources: %s"
msgstr "terjadi kesalahan mengunduh sumber-sumber: %s"
#: pkg/download/errors.go:25
msgid "error fetching %s: %s"
msgstr "terjadi kesalahan mendapatkan %s: %s"
#: pkg/sync/build/errors.go:9
msgid "error installing repo packages"
msgstr "terjadi kesalahan memasang paket-paket repositori"
#: pkg/sync/build/installer.go:266 pkg/sync/build/installer.go:270
msgid "error installing:"
msgstr "terjadi kesalahan memasang:"
#: pkg/sync/build/installer.go:233 pkg/sync/build/installer.go:237
msgid "error making: %s"
msgstr "terjadi kesalahan membuat: %s"
#: pkg/sync/workdir/merge.go:24
msgid "error merging %s: %s"
msgstr "terjadi kesalahan menggabungkan %s: %s"
#: pkg/download/unified.go:59
msgid "error reading %s"
msgstr "terjadi kesalahan membaca %s"
#: sync.go:36
msgid "error refreshing databases"
msgstr "terjadi kesalahan memuat ulang basis data"
#: pkg/sync/workdir/clean.go:51 pkg/sync/workdir/merge.go:17
msgid "error resetting %s: %s"
msgstr "terjadi kesalahan mengatur ulang %s: %s"
#: pkg/sync/build/errors.go:53
msgid "error updating package install reason to %s"
msgstr "terjadi kesalahan memperbarui alasan pemasangan paket ke %s"
#: pkg/sync/build/errors.go:48
msgid "explicit"
msgstr "eksplisit"
#: pkg/settings/errors.go:23
msgid "failed to create directory '%s': %s"
msgstr "gagal untuk membuat direktori '%s': %s"
#: pkg/settings/config.go:281
msgid "failed to open config file '%s': %s"
msgstr "gagal untuk membuka file konfigurasi '%s': %s"
#: pkg/sync/srcinfo/service.go:114
msgid "failed to parse %s -- skipping: %s"
msgstr "gagal untuk menguraikan %s -- melewatkan: %s"
#: pkg/sync/srcinfo/service.go:118
msgid "failed to parse %s: %s"
msgstr "gagal untuk menguraikan %s: %s"
#: local_install.go:77
msgid "failed to parse .SRCINFO"
msgstr "gagal mengurai .SRCINFO"
#: pkg/settings/config.go:291
msgid "failed to read config file '%s': %s"
msgstr "gagal untuk membaca file konfigurasi '%s': %s"
#: pkg/cmd/graph/main.go:46 pkg/runtime/runtime.go:73
msgid "failed to retrieve aur Cache"
msgstr "gagal mendapatkan Cache aur"
#: pkg/upgrade/sources.go:27
msgid "ignoring package devel upgrade (no AUR info found):"
msgstr ""
"mengabaikan pembaruan paket devel (tidak ada info AUR yang ditemukan):"
#: pkg/text/errors.go:8
msgid "input too long"
msgstr "input terlalu panjang"
#: pkg/db/ialpm/alpm.go:222 pkg/dep/dep_graph.go:761
msgid "invalid number: %s"
msgstr "Nomor tidak valid: %s"
#: pkg/settings/parser/parser.go:174
msgid "invalid option '%s'"
msgstr "opsi '%s' tidak valid"
#: cmd.go:197
msgid "invalid option: '--deps' and '--explicit' may not be used together"
msgstr ""
"opsi tidak valid: '--deps' dan '--explicit' tidak boleh digunakan bersama"
#: pkg/download/abs.go:22
msgid "invalid repository"
msgstr "repositori tidak valid"
#: pkg/db/ialpm/alpm.go:227 pkg/dep/dep_graph.go:767
msgid "invalid value: %d is not between %d and %d"
msgstr "nilai tidak valid: %d tidak di antara %d dan %d"
#: pkg/text/input.go:48
msgid "no"
msgstr "tidak"
#: pkg/sync/srcinfo/pgp/keys.go:110
msgid "no keys to import"
msgstr "tidak ada kunci-kunci untuk diimpor"
#: pkg/query/errors.go:20
msgid "no query was executed"
msgstr "tidak ada pencarian yang dilakukan"
#: local_install.go:66
msgid "no target directories specified"
msgstr "tidak ada sasaran direktori yang ditetapkan"
#: pkg/sync/build/installer.go:242
msgid "nothing to install for %s"
msgstr "tidka ada yang untuk dipasang untuk %s"
#: pkg/settings/parser/parser.go:164
msgid "only one operation may be used at a time"
msgstr "hanya satu operasi yang dapat digunakan"
#: pkg/cmd/graph/main.go:70
msgid "only one target is allowed"
msgstr "hanya satu target yang diperbolehkan"
#: pkg/upgrade/service.go:291
msgid "package"
msgid_plural "packages"
msgstr[0] "paket"
#: print.go:187
msgid "package '%s' was not found"
msgstr "paket '%s' tidak ditemukan"
#: pkg/download/errors.go:15
msgid "package not found in AUR"
msgstr "paket tidak ditemukan di AUR"
#: pkg/download/abs.go:23
msgid "package not found in repos"
msgstr "paket tidak ditemukan di repositori"
#: pkg/sync/srcinfo/pgp/keys.go:100
msgid "problem importing keys"
msgstr "terjadi masalah mengimpor kunci-kunci"
#: clean.go:105
msgid "removing AUR packages from cache..."
msgstr "menghapus paket-paket AUR dari cache..."
#: clean.go:178 pkg/sync/workdir/clean.go:41
msgid "removing untracked AUR files from cache..."
msgstr "menghapus file AUR yang tidak dilacak dari cache..."
#: pkg/sync/build/errors.go:38
msgid "the PKGDEST for %s is listed by makepkg but does not exist: %s"
msgstr "PKGDEST untuk %s didaftarkan oleh makepkg tetapi tidak ada: %s"
#: pkg/sync/sync.go:45
msgid "there is nothing to do"
msgstr "tidak ada yang dapat dilakukan"
#: pkg/db/ialpm/alpm.go:247
msgid "unable to CreateHandle: %s"
msgstr "tidak dapat CreateHandle: %s"
#: cmd.go:186
msgid "unhandled operation"
msgstr "operasi tidak diproses"
#: cmd.go:450
msgid "unknown-version"
msgstr "versi-tidak-diketahui"
#: pkg/text/input.go:47
msgid "yes"
msgstr "ya"
================================================
FILE: po/it_IT.po
================================================
#
# Translators:
# Cardellino, 2021
# Giulio Terigi, 2022
# Simone Dotto , 2022
# jheitz223, 2023
# Vincenzo Reale , 2025
#
msgid ""
msgstr ""
"Last-Translator: Vincenzo Reale , 2025\n"
"Language-Team: Italian (Italy) (https://app.transifex.com/yay-1/teams/123732/it_IT/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: it_IT\n"
"Plural-Forms: nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n"
"X-Generator: xgotext\n"
#: clean.go:83
msgid ""
"\n"
"Build directory:"
msgstr ""
"\n"
"Cartella di compilazione:"
#: pkg/db/ialpm/alpm.go:201 pkg/dep/dep_graph.go:740
msgid ""
"\n"
"Enter a number (default=1): "
msgstr ""
"\n"
"Inserisci un numero (predefinito=1) "
#: pkg/menus/menu.go:32
msgid " (Build Files Exist)"
msgstr " (I file di compilazione sono già presenti)"
#: pkg/menus/menu.go:27
msgid " (Installed)"
msgstr " (Installato)"
#: cmd.go:453
msgid " [Installed]"
msgstr " [Installato]"
#: cmd.go:410 vote.go:36
msgid " there is nothing to do"
msgstr " non c'è nulla da fare"
#: pkg/menus/menu.go:49
msgid "%s [A]ll [Ab]ort [I]nstalled [No]tInstalled or (1 2 3, 1-3, ^4)"
msgstr ""
"%s [A]Tutti [Ab]Annulla [I]nstallati [No]nInstallati oppure (1 2 3, 1-3, ^4)"
#: pkg/sync/build/installer.go:308
msgid "%s already made -- skipping build"
msgstr "%s già fatto -- compilazione saltata"
#: pkg/menus/edit_menu.go:57
msgid "%s is not set"
msgstr "%s non è impostato"
#: pkg/settings/exe/cmd_builder.go:257
msgid "%s is present."
msgstr "%s è presente."
#: pkg/dep/dep_graph.go:460 pkg/sync/build/installer.go:305
msgid "%s is up to date -- skipping"
msgstr "%s è aggiornato -- ignorato"
#: pkg/upgrade/service.go:291
msgid "%s to upgrade/install."
msgstr "%s da aggiornare/installare."
#: pkg/upgrade/service.go:285
msgid "%s will also be installed for this operation."
msgstr "%s sarà inoltre installato per questa operazione."
#: pkg/sync/srcinfo/pgp/keys.go:124
msgid "%s, required by: %s"
msgstr "%s, richiesto da: %s"
#: pkg/menus/diff_menu.go:49
msgid "%s: No changes -- skipping"
msgstr "%s: Nessuna modifica -- ignorato "
#: pkg/query/filter.go:22
msgid "%s: can't use target with option --aur -- skipping"
msgstr "%s: non si può usare il target con l'opzione --aur -- ignorato"
#: pkg/query/filter.go:17
msgid "%s: can't use target with option --repo -- skipping"
msgstr "%s: non si può usare il target con l'opzione --repo -- ignorato"
#: pkg/upgrade/sources.go:57
msgid "%s: ignoring package upgrade (%s => %s)"
msgstr "%s: aggiornamento dei pacchetti ignorato (%s => %s)"
#: pkg/query/aur_warnings.go:46
msgid "%s: local (%s) is newer than AUR (%s)"
msgstr "%s: il pacchetto locale (%s) è più nuovo del pacchetto AUR (%s)"
#: vote.go:51
msgid ""
"%s: please set AUR_USERNAME and AUR_PASSWORD environment variables for "
"voting"
msgstr ""
"%s: imposta le variabili d'ambiente AUR_USERNAME e AUR_PASSWORD prima di "
"votare"
#: pkg/download/unified.go:192
msgid "(%d/%d) Downloaded PKGBUILD from ABS: %s"
msgstr "(%d/%d) PKGBUILD scaricato da ABS: %s"
#: pkg/download/aur.go:92 pkg/download/unified.go:188
msgid "(%d/%d) Downloaded PKGBUILD: %s"
msgstr "(%d/%d) PKGBUILD scaricato: %s"
#: pkg/download/aur.go:82
msgid "(%d/%d) Failed to download PKGBUILD: %s"
msgstr "(%d/%d) Scaricamento PKGBUILD non riuscito: %s"
#: pkg/sync/srcinfo/service.go:109
msgid "(%d/%d) Parsing SRCINFO: %s"
msgstr "(%d/%d) Analisi SRCINFO: %s"
#: pkg/query/types.go:103 pkg/query/types.go:72
msgid "(Installed)"
msgstr "(Installato)"
#: pkg/query/types.go:101 pkg/query/types.go:70
msgid "(Installed: %s)"
msgstr "(Installato: %s)"
#: pkg/query/types.go:61
msgid "(Orphaned)"
msgstr "(Orfano)"
#: pkg/query/types.go:65
msgid "(Out-of-date: %s)"
msgstr "(Obsoleto: %s)"
#: pkg/dep/dep_graph.go:75
msgid "AUR"
msgstr "AUR"
#: print.go:44
msgid "AUR URL"
msgstr "URL di AUR"
#: pkg/menus/edit_menu.go:58
msgid "Add %s or %s to your environment variables"
msgstr "Aggiungi %s o %s alle tue variabili d'ambiente"
#: main.go:60
msgid "Avoid running yay as root/sudo."
msgstr "Evita di eseguire yay come root/sudo."
#: pkg/dep/dep_graph.go:63
msgid "Check Dependency"
msgstr "Controllo dipendenza"
#: print.go:41
msgid "Check Deps"
msgstr "Controllo dipendenze"
#: pkg/upgrade/service.go:89
msgid "Checking development packages..."
msgstr "Verifica dei pacchetti di sviluppo in corso..."
#: pkg/sync/workdir/clean.go:45
msgid "Cleaning (%d/%d): %s"
msgstr "Pulizia (%d/%d): %s"
#: print.go:42
msgid "Conflicts With"
msgstr "Conflitti con"
#: pkg/menus/clean_menu.go:62
msgid "Deleting (%d/%d): %s"
msgstr "Eliminazione (%d/%d): %s"
#: pkg/dep/dep_graph.go:61
msgid "Dependency"
msgstr "Dipendenza"
#: print.go:38
msgid "Depends On"
msgstr "Dipende da"
#: print.go:33
msgid "Description"
msgstr "Descrizione"
#: pkg/menus/diff_menu.go:160
msgid "Diffs to show?"
msgstr "Differenze da mostrare?"
#: pkg/settings/migrations.go:25
msgid "Disable 'provides' setting by default"
msgstr "Disabilita l'impostazione 'provides' in modo predefinito"
#: clean.go:78
msgid "Do you want to remove ALL AUR packages from cache?"
msgstr "Vuoi rimuovere TUTTI i pacchetti AUR dalla cache?"
#: clean.go:95
msgid "Do you want to remove ALL untracked AUR files?"
msgstr "Vuoi rimuovere TUTTI i file AUR non monitorati?"
#: clean.go:80
msgid "Do you want to remove all other AUR packages from cache?"
msgstr "Vuoi rimuovere tutti gli altri pacchetti AUR dalla cache?"
#: pkg/menus/edit_menu.go:61
msgid "Edit PKGBUILD with?"
msgstr "Con cosa modificare il PKGBUILD?"
#: pkg/query/errors.go:13
msgid "Error during AUR search: %s\n"
msgstr "Errore durante la ricerca in AUR: %s\n"
#: pkg/upgrade/service.go:295
msgid "Excluding packages may cause partial upgrades and break systems"
msgstr ""
"L'esclusione di pacchetti potrebbe causare aggiornamenti parziali e "
"danneggiare i sistemi"
#: pkg/dep/dep_graph.go:60
msgid "Explicit"
msgstr "Esplicito"
#: print.go:91
msgid "Explicitly installed packages: %s"
msgstr "Pacchetti installati esplicitamente: %s"
#: pkg/dep/dep_graph.go:437 pkg/dep/dep_graph.go:535
msgid "Failed to find AUR package for"
msgstr "Impossibile trovare il pacchetto AUR per"
#: pkg/sync/build/installer.go:120
msgid "Failed to install layer, rolling up to next layer."
msgstr ""
"Installazione del livello non riuscita, passaggio al livello successivo."
#: pkg/sync/build/errors.go:16
msgid ""
"Failed to install the following packages. Manual intervention is required:"
msgstr ""
"Installazione dei seguenti pacchetti non riuscita. È richiesto l'intervento "
"manuale:"
#: print.go:45
msgid "First Submitted"
msgstr "Primo invio"
#: pkg/query/aur_warnings.go:83
msgid "Flagged Out Of Date AUR Packages:"
msgstr "Pacchetti AUR con flag obsoleto:"
#: print.go:90
msgid "Foreign installed packages: %s"
msgstr "Totale dei pacchetti estranei installati: %s"
#: pkg/vcs/vcs.go:144
msgid "Found git repo: %s"
msgstr "Trovato repository git: %s"
#: vcs.go:72
msgid "GenDB finished. No packages were installed"
msgstr "GenDB terminato. Nessun pacchetto è stato installato"
#: print.go:36
msgid "Groups"
msgstr "Gruppi"
#: pkg/sync/srcinfo/pgp/keys.go:88
msgid "Import?"
msgstr "Importare?"
#: pkg/sync/srcinfo/pgp/keys.go:97
msgid "Importing keys with gpg..."
msgstr "Importazione delle chiavi con gpg in corso..."
#: print.go:46
msgid "Keywords"
msgstr "Parole chiave"
#: print.go:47
msgid "Last Modified"
msgstr "Ultima modifica"
#: print.go:35
msgid "Licenses"
msgstr "Licenze"
#: pkg/dep/dep_graph.go:77
msgid "Local"
msgstr "Locale"
#: print.go:48
msgid "Maintainer"
msgstr "Responsabile"
#: pkg/dep/dep_graph.go:62
msgid "Make Dependency"
msgstr "Dipendenza Make"
#: print.go:40
msgid "Make Deps"
msgstr "Make dipendenze"
#: pkg/dep/dep_graph.go:79
msgid "Missing"
msgstr "Mancante"
#: pkg/query/aur_warnings.go:75
msgid "Missing AUR Debug Packages:"
msgstr "Pacchetti AUR di debug mancanti:"
#: print.go:31
msgid "Name"
msgstr "Nome"
#: pkg/dep/dep_graph.go:442 pkg/dep/dep_graph.go:564
msgid "No AUR package found for"
msgstr "Nessun pacchetto AUR trovato per"
#: pkg/dep/dep_graph.go:564
msgid "required by"
msgstr "richiesto da"
#: pkg/dep/dep_graph.go:182
msgid "No package found for"
msgstr "Nessun pacchetto trovato per"
#: print.go:225
msgid "None"
msgstr "Nessuno"
#: print.go:39
msgid "Optional Deps"
msgstr "Dipendenze opzionali"
#: pkg/query/aur_warnings.go:79
msgid "Orphan (unmaintained) AUR Packages:"
msgstr "Pacchetti AUR orfani (non mantenuti):"
#: print.go:53 print.go:55
msgid "Out-of-date"
msgstr "Obsoleto"
#: pkg/sync/srcinfo/pgp/keys.go:115
msgid "PGP keys need importing:"
msgstr "Le chiavi PGP devono essere importate:"
#: pkg/sync/workdir/preparer.go:252
msgid "PKGBUILD up to date, skipping download: %s"
msgstr "PKGBUILD aggiornato, scaricamento ignorato: %s"
#: pkg/menus/edit_menu.go:130
msgid "PKGBUILDs to edit?"
msgstr "Quali PKGBUILD modificare?"
#: print.go:61
msgid "Package Base"
msgstr "Pacchetto base"
#: print.go:60
msgid "Package Base ID"
msgstr "ID pacchetto base"
#: pkg/query/aur_warnings.go:71
msgid "Packages not in AUR:"
msgstr "Pacchetti non in AUR:"
#: pkg/menus/clean_menu.go:54
msgid "Packages to cleanBuild?"
msgstr "Pacchetti da compilare in modo pulito?"
#: pkg/dep/dep_graph.go:202
msgid "Packages to exclude"
msgstr "Pacchetti da escludere"
#: pkg/upgrade/service.go:294
msgid "Packages to exclude: (eg: \"1 2 3\", \"1-3\", \"^4\" or repo name)"
msgstr "Pacchetti da escludere: (es: \"1 2 3\", \"1-3\", \"^4\" o il nome del repo)"
#: cmd.go:392
msgid "Packages to install (eg: 1 2 3, 1-3 or ^4)"
msgstr "Pacchetti da installare (es: 1 2 3, 1-3 o ^4)"
#: print.go:49
msgid "Popularity"
msgstr "Popolarità"
#: pkg/menus/diff_menu.go:172 pkg/menus/edit_menu.go:143
msgid "Proceed with install?"
msgstr "Procedere con l'installazione?"
#: print.go:37
msgid "Provides"
msgstr "Fornisce"
#: pkg/sync/workdir/preparer.go:125
msgid "Remove make dependencies after install?"
msgstr "Rimuovere le dipendenze di make dopo l'installazione?"
#: print.go:43
msgid "Replaces"
msgstr "Sostituisce"
#: pkg/db/ialpm/alpm.go:191 print.go:30
msgid "Repository"
msgstr "Repository"
#: pkg/dep/dep_graph.go:730
msgid "Repository AUR"
msgstr "Repository AUR"
#: pkg/dep/dep_graph.go:78
msgid "SRCINFO"
msgstr "SRCINFO"
#: pkg/upgrade/service.go:71
msgid "Searching AUR for updates..."
msgstr "Ricerca di aggiornamenti su AUR in corso..."
#: pkg/upgrade/service.go:159
msgid "Searching databases for updates..."
msgstr "Ricerca di aggiornamenti nei database in corso..."
#: pkg/query/query_builder.go:214
msgid "Showing repo packages only"
msgstr "Visualizzazione dei soli pacchetti del repo"
#: print.go:95
msgid "Size of pacman cache %s: %s"
msgstr "Dimensione della cache di pacman %s: %s"
#: print.go:98
msgid "Size of yay cache %s: %s"
msgstr "Dimensione della cache di yay %s: %s"
#: print.go:62
msgid "Snapshot URL"
msgstr "URL snapshot"
#: pkg/dep/dep_graph.go:76
msgid "Sync"
msgstr "Sincronizza"
#: print.go:100
msgid "Ten biggest packages:"
msgstr "I dieci pacchetti più grandi:"
#: pkg/sync/sync.go:124
msgid "The following packages are not compatible with your architecture:"
msgstr "I seguenti pacchetti non sono compatibili con la tua architettura:"
#: pkg/db/ialpm/alpm.go:179 pkg/dep/dep_graph.go:726
msgid "There are %[1]d providers available for %[2]s:"
msgstr "Ci sono %[1]d fornitori disponibili per %[2]s:"
#: pkg/settings/exe/cmd_builder.go:258
msgid "There may be another Pacman instance running. Waiting..."
msgstr ""
"Potrebbe esserci un'altra istanza di Pacman in esecuzione. In attesa... "
#: print.go:92
msgid "Total Size occupied by packages: %s"
msgstr "Dimensione totale occupata dai pacchetti: %s"
#: print.go:89
msgid "Total installed packages: %s"
msgstr "Totale dei pacchetti installati: %s"
#: pkg/sync/sync.go:132
msgid "Try to build them anyway?"
msgstr "Provare a compilarli comunque?"
#: print.go:34
msgid "URL"
msgstr "URL"
#: clean.go:194 pkg/menus/clean_menu.go:65 pkg/menus/clean_menu.go:71
msgid "Unable to clean:"
msgstr "Impossibile pulire:"
#: get.go:42 get.go:74
msgid "Unable to find the following packages:"
msgstr "Impossibile trovare i seguenti pacchetti:"
#: vote.go:20
msgid "Unable to handle package vote for: %s. err: %s"
msgstr "Impossibile gestire il voto per: %s. errore: %s"
#: clean.go:170
msgid "Unable to remove %s: %s"
msgstr "Impossibile rimuovere %s: %s"
#: print.go:32
msgid "Version"
msgstr "Versione"
#: print.go:50
msgid "Votes"
msgstr "Voti"
#: print.go:87
msgid "Yay version v%s"
msgstr "Versione di yay v%s"
#: pkg/menus/menu.go:49
msgid "[N]one"
msgstr "[N]essuno"
#: pkg/settings/errors.go:29
msgid "aborting due to user"
msgstr "interruzione su richiesta dell'utente in corso"
#: pkg/settings/parser/parser.go:608
msgid "argument '-' specified without input on stdin"
msgstr "argomento '-' specificato senza input su stdin"
#: local_install.go:26
msgid "cannot find PKGBUILD and .SRCINFO in directory"
msgstr "impossibile trovare PKGBUILD e .SRCINFO nella cartella"
#: pkg/sync/build/pkg_archive.go:148
msgid "cannot find package name: %v"
msgstr "impossibile trovare un pacchetto di nome: %v"
#: pkg/sync/build/errors.go:30
msgid "could not find PKGDEST for: %s"
msgstr "impossibile trovare il PKGDEST per: %s"
#: errors.go:9
msgid "could not find all required packages"
msgstr "impossibile trovare tutti i pacchetti richiesti"
#: pkg/sync/build/errors.go:61
msgid "could not find any package archives listed in %s"
msgstr "impossibile trovare alcun archivio di pacchetti elencato in %s"
#: pkg/sync/build/errors.go:50 pkg/upgrade/service.go:286
msgid "dependency"
msgstr "dipendenza"
#: pkg/vcs/vcs.go:100 pkg/vcs/vcs.go:96
msgid "devel check for package failed: '%s' encountered an error"
msgstr ""
"devel check non riuscito per il pacchetto: '%s' ha riscontrato un errore"
#: pkg/menus/edit_menu.go:110
msgid "editor did not exit successfully, aborting: %s"
msgstr "l'editor non è uscito correttamente, interruzione in corso: %s"
#: pkg/sync/workdir/aur_source.go:24
msgid "error downloading sources: %s"
msgstr "errore durante lo scaricamento dei sorgenti: %s"
#: pkg/download/errors.go:25
msgid "error fetching %s: %s"
msgstr "errore durante il recupero di %s: %s"
#: pkg/sync/build/errors.go:9
msgid "error installing repo packages"
msgstr "errore durante l'installazione dei pacchetti del repo"
#: pkg/sync/build/installer.go:266 pkg/sync/build/installer.go:270
msgid "error installing:"
msgstr "installazione non riuscita:"
#: pkg/sync/build/installer.go:233 pkg/sync/build/installer.go:237
msgid "error making: %s"
msgstr "errore durante la creazione: %s"
#: pkg/sync/workdir/merge.go:24
msgid "error merging %s: %s"
msgstr "errore durante l'unione di %s: %s "
#: pkg/download/unified.go:59
msgid "error reading %s"
msgstr "errore durante la lettura di %s"
#: sync.go:36
msgid "error refreshing databases"
msgstr "errore durante l'aggiornamento dei database"
#: pkg/sync/workdir/clean.go:51 pkg/sync/workdir/merge.go:17
msgid "error resetting %s: %s"
msgstr "errore durante il ripristino di %s: %s"
#: pkg/sync/build/errors.go:53
msgid "error updating package install reason to %s"
msgstr ""
"aggiornamento della ragione d'installazione del pacchetto a %s non è "
"riuscito"
#: pkg/sync/build/errors.go:48
msgid "explicit"
msgstr "esplicito"
#: pkg/settings/errors.go:23
msgid "failed to create directory '%s': %s"
msgstr "creazione della cartella '%s' non riuscita: %s"
#: pkg/settings/config.go:281
msgid "failed to open config file '%s': %s"
msgstr "apertura del file di configurazione '%s' non riuscita: %s"
#: pkg/sync/srcinfo/service.go:114
msgid "failed to parse %s -- skipping: %s"
msgstr "analisi di %s non riuscita -- ignorato: %s"
#: pkg/sync/srcinfo/service.go:118
msgid "failed to parse %s: %s"
msgstr "analisi di %s non riuscita: %s"
#: local_install.go:77
msgid "failed to parse .SRCINFO"
msgstr "analisi .SRCINFO non riuscita"
#: pkg/settings/config.go:291
msgid "failed to read config file '%s': %s"
msgstr "lettura del file di configurazione '%s' non riuscita: %s"
#: pkg/cmd/graph/main.go:46 pkg/runtime/runtime.go:73
msgid "failed to retrieve aur Cache"
msgstr "recupero della cache di AUR non riuscito"
#: pkg/upgrade/sources.go:27
msgid "ignoring package devel upgrade (no AUR info found):"
msgstr ""
"aggiornamento devel del pacchetto ignorato (nessuna informazione AUR "
"trovata):"
#: pkg/text/errors.go:8
msgid "input too long"
msgstr "input troppo lungo"
#: pkg/db/ialpm/alpm.go:222 pkg/dep/dep_graph.go:761
msgid "invalid number: %s"
msgstr "numero non valido: %s"
#: pkg/settings/parser/parser.go:174
msgid "invalid option '%s'"
msgstr "opzione non valida '%s'"
#: cmd.go:197
msgid "invalid option: '--deps' and '--explicit' may not be used together"
msgstr ""
"opzione non valida: '--deps' e '--explicit' non possono essere usate insieme"
#: pkg/download/abs.go:22
msgid "invalid repository"
msgstr "repository non valido"
#: pkg/db/ialpm/alpm.go:227 pkg/dep/dep_graph.go:767
msgid "invalid value: %d is not between %d and %d"
msgstr "valore non valido: %d non è compreso tra %d e %d"
#: pkg/text/input.go:48
msgid "no"
msgstr "no"
#: pkg/sync/srcinfo/pgp/keys.go:110
msgid "no keys to import"
msgstr "nessuna chiave da importare"
#: pkg/query/errors.go:20
msgid "no query was executed"
msgstr "nessuna richiesta è stata eseguita"
#: local_install.go:66
msgid "no target directories specified"
msgstr "nessuna cartella di destinazione specificata"
#: pkg/sync/build/installer.go:242
msgid "nothing to install for %s"
msgstr "nulla da installare per %s"
#: pkg/settings/parser/parser.go:164
msgid "only one operation may be used at a time"
msgstr "è possibile eseguire una sola operazione alla volta"
#: pkg/cmd/graph/main.go:70
msgid "only one target is allowed"
msgstr "è permessa solo una destinazione"
#: pkg/upgrade/service.go:291
msgid "package"
msgid_plural "packages"
msgstr[0] "pacchetto"
msgstr[1] "pacchetti"
msgstr[2] "pacchetti"
#: print.go:187
msgid "package '%s' was not found"
msgstr "il pacchetto '%s' non è stato trovato"
#: pkg/download/errors.go:15
msgid "package not found in AUR"
msgstr "pacchetto non trovato su AUR"
#: pkg/download/abs.go:23
msgid "package not found in repos"
msgstr "pacchetto non trovato nei repository"
#: pkg/sync/srcinfo/pgp/keys.go:100
msgid "problem importing keys"
msgstr "problema durante l'importazione delle chiavi"
#: clean.go:105
msgid "removing AUR packages from cache..."
msgstr "eliminazione dei pacchetti AUR dalla cache..."
#: clean.go:178 pkg/sync/workdir/clean.go:41
msgid "removing untracked AUR files from cache..."
msgstr "eliminazione dei file AUR non tracciati dalla cache..."
#: pkg/sync/build/errors.go:38
msgid "the PKGDEST for %s is listed by makepkg but does not exist: %s"
msgstr "il PKGDEST per %s è elencato da makepkg, ma non esiste: %s"
#: pkg/sync/sync.go:45
msgid "there is nothing to do"
msgstr " non c'è nulla da fare"
#: pkg/db/ialpm/alpm.go:247
msgid "unable to CreateHandle: %s"
msgstr "impossibile creare un handle: %s"
#: cmd.go:186
msgid "unhandled operation"
msgstr "operazione non gestita"
#: cmd.go:450
msgid "unknown-version"
msgstr "versione-sconosciuta"
#: pkg/text/input.go:47
msgid "yes"
msgstr "sì"
================================================
FILE: po/ja.po
================================================
#
# Translators:
# J G , 2021
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"PO-Revision-Date: 2021-08-13 22:55+0000\n"
"Last-Translator: J G , 2021\n"
"Language-Team: Japanese (https://www.transifex.com/yay-1/teams/123732/ja/)\n"
"Language: ja\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: xgotext\n"
#: pkg/menus/menu.go:32
msgid " (Build Files Exist)"
msgstr " (ビルドファイルが存在)"
#: pkg/menus/menu.go:27
msgid " (Installed)"
msgstr " (インストール済み)"
#: cmd.go:453
msgid " [Installed]"
msgstr " [インストール済み]"
#: cmd.go:410 vote.go:36
msgid " there is nothing to do"
msgstr " 何もすることがありません"
#: pkg/menus/menu.go:49
msgid "%s [A]ll [Ab]ort [I]nstalled [No]tInstalled or (1 2 3, 1-3, ^4)"
msgstr ""
"%s [A]全て [Ab]中止 [I]インストール済み [No]未インストール または (1 2 3, "
"1-3, ^4)"
#: pkg/sync/build/installer.go:308
msgid "%s already made -- skipping build"
msgstr "%s は既に作成済みです -- ビルドをスキップします"
#: pkg/menus/edit_menu.go:57
msgid "%s is not set"
msgstr "%s が設定されていません"
#: pkg/settings/exe/cmd_builder.go:257
msgid "%s is present."
msgstr "%s が存在します。"
#: pkg/dep/dep_graph.go:460 pkg/sync/build/installer.go:305
msgid "%s is up to date -- skipping"
msgstr "%s は最新です -- スキップ"
#: pkg/upgrade/service.go:292
#, fuzzy
msgid "%s to upgrade/install."
msgstr "アップグレードするパッケージ。"
#: pkg/upgrade/service.go:286
msgid "%s will also be installed for this operation."
msgstr ""
#: pkg/sync/srcinfo/pgp/keys.go:124
msgid "%s, required by: %s"
msgstr "%s、必要とするパッケージ: %s"
#: pkg/menus/diff_menu.go:49
msgid "%s: No changes -- skipping"
msgstr "%s: 変更なし -- スキップ"
#: pkg/query/filter.go:22
msgid "%s: can't use target with option --aur -- skipping"
msgstr "%s: 対象に --aur オプションを使うことができません -- スキップ"
#: pkg/query/filter.go:17
msgid "%s: can't use target with option --repo -- skipping"
msgstr "%s: 対象に --repo オプションを使うことができません -- スキップ"
#: pkg/upgrade/sources.go:57
msgid "%s: ignoring package upgrade (%s => %s)"
msgstr "%s: パッケージのアップグレードを無視 (%s => %s)"
#: pkg/query/aur_warnings.go:46
msgid "%s: local (%s) is newer than AUR (%s)"
msgstr "%s: ローカルのパッケージ (%s) は AUR (%s) よりも新しいバージョンです"
#: vote.go:51
msgid ""
"%s: please set AUR_USERNAME and AUR_PASSWORD environment variables for voting"
msgstr ""
#: pkg/download/unified.go:192
msgid "(%d/%d) Downloaded PKGBUILD from ABS: %s"
msgstr "(%d/%d) ABS から PKGBUILD をダウンロード: %s"
#: pkg/download/aur.go:92 pkg/download/unified.go:188
#, fuzzy
msgid "(%d/%d) Downloaded PKGBUILD: %s"
msgstr "(%d/%d) ABS から PKGBUILD をダウンロード: %s"
#: pkg/download/aur.go:82
#, fuzzy
msgid "(%d/%d) Failed to download PKGBUILD: %s"
msgstr "(%d/%d) ABS から PKGBUILD をダウンロード: %s"
#: pkg/sync/srcinfo/service.go:109
msgid "(%d/%d) Parsing SRCINFO: %s"
msgstr "(%d/%d) SRCINFO を解析中: %s"
#: pkg/query/types.go:72 pkg/query/types.go:103
msgid "(Installed)"
msgstr "(インストール済み)"
#: pkg/query/types.go:70 pkg/query/types.go:101
msgid "(Installed: %s)"
msgstr "(インストール済み: %s)"
#: pkg/query/types.go:61
msgid "(Orphaned)"
msgstr "(メンテナ不在)"
#: pkg/query/types.go:65
msgid "(Out-of-date: %s)"
msgstr "(古いバージョン: %s)"
#: print.go:44
msgid "AUR URL"
msgstr "AUR URL"
#: pkg/dep/dep_graph.go:75
#, fuzzy
msgid "AUR"
msgstr "URL"
#: pkg/menus/edit_menu.go:58
msgid "Add %s or %s to your environment variables"
msgstr "%s または %s を環境変数に追加してください"
#: main.go:60
msgid "Avoid running yay as root/sudo."
msgstr "yay を root や sudo で実行しないでください。"
#: pkg/dep/dep_graph.go:63
#, fuzzy
msgid "Check Dependency"
msgstr "チェック時の依存パッケージ"
#: print.go:41
msgid "Check Deps"
msgstr "チェック時の依存パッケージ"
#: pkg/upgrade/service.go:90
msgid "Checking development packages..."
msgstr "開発パッケージを確認..."
#: pkg/sync/workdir/clean.go:45
msgid "Cleaning (%d/%d): %s"
msgstr "消去 (%d/%d): %s"
#: print.go:42
msgid "Conflicts With"
msgstr "衝突するパッケージ"
#: pkg/menus/clean_menu.go:62
msgid "Deleting (%d/%d): %s"
msgstr "削除 (%d/%d): %s"
#: pkg/dep/dep_graph.go:61
#, fuzzy
msgid "Dependency"
msgstr "依存するパッケージ"
#: print.go:38
msgid "Depends On"
msgstr "依存するパッケージ"
#: print.go:33
msgid "Description"
msgstr "説明"
#: pkg/menus/diff_menu.go:160
msgid "Diffs to show?"
msgstr "差異を表示しますか?"
#: pkg/settings/migrations.go:25
msgid "Disable 'provides' setting by default"
msgstr ""
#: clean.go:78
msgid "Do you want to remove ALL AUR packages from cache?"
msgstr "キャッシュから全ての AUR パッケージを削除しますか?"
#: clean.go:95
msgid "Do you want to remove ALL untracked AUR files?"
msgstr "未追跡の AUR ファイルを全て削除しますか?"
#: clean.go:80
msgid "Do you want to remove all other AUR packages from cache?"
msgstr "キャッシュから他の全ての AUR パッケージを削除しますか?"
#: pkg/menus/edit_menu.go:61
msgid "Edit PKGBUILD with?"
msgstr "PKGBUILD をどのエディタで編集しますか?"
#: pkg/query/errors.go:13
msgid "Error during AUR search: %s\n"
msgstr "AUR 検索時のエラー: %s\n"
#: pkg/upgrade/service.go:296
msgid "Excluding packages may cause partial upgrades and break systems"
msgstr ""
#: pkg/dep/dep_graph.go:60
msgid "Explicit"
msgstr ""
#: print.go:91
msgid "Explicitly installed packages: %s"
msgstr "明示的にインストールしたパッケージ: %s"
#: pkg/dep/dep_graph.go:437 pkg/dep/dep_graph.go:535
#, fuzzy
msgid "Failed to find AUR package for"
msgstr "古いバージョンのフラグが立てられた AUR パッケージ:"
#: pkg/sync/build/installer.go:120
msgid "Failed to install layer, rolling up to next layer."
msgstr ""
#: pkg/sync/build/errors.go:16
msgid ""
"Failed to install the following packages. Manual intervention is required:"
msgstr ""
#: print.go:45
msgid "First Submitted"
msgstr "最初の投稿"
#: pkg/query/aur_warnings.go:79
msgid "Flagged Out Of Date AUR Packages:"
msgstr "古いバージョンのフラグが立てられた AUR パッケージ:"
#: print.go:90
#, fuzzy
msgid "Foreign installed packages: %s"
msgstr "全ての外部からインストールされたパッケージ: %s"
#: pkg/vcs/vcs.go:144
msgid "Found git repo: %s"
msgstr "git リポジトリを発見しました: %s"
#: vcs.go:72
msgid "GenDB finished. No packages were installed"
msgstr "GenDB が完了しました。パッケージのインストールは行われません"
#: print.go:36
msgid "Groups"
msgstr "グループ"
#: pkg/sync/srcinfo/pgp/keys.go:88
msgid "Import?"
msgstr "インポートしますか?"
#: pkg/sync/srcinfo/pgp/keys.go:97
msgid "Importing keys with gpg..."
msgstr "鍵を gpg でインポートします..."
#: print.go:46
msgid "Keywords"
msgstr "キーワード"
#: print.go:47
msgid "Last Modified"
msgstr "最終更新"
#: print.go:35
msgid "Licenses"
msgstr "ライセンス"
#: pkg/dep/dep_graph.go:77
msgid "Local"
msgstr ""
#: print.go:48
msgid "Maintainer"
msgstr "メンテナ"
#: pkg/dep/dep_graph.go:62
#, fuzzy
msgid "Make Dependency"
msgstr "ビルド時の依存パッケージ"
#: print.go:40
msgid "Make Deps"
msgstr "ビルド時の依存パッケージ"
#: pkg/query/aur_warnings.go:71
#, fuzzy
msgid "Missing AUR Debug Packages:"
msgstr "存在しない AUR パッケージ:"
#: pkg/dep/dep_graph.go:79
msgid "Missing"
msgstr ""
#: print.go:31
msgid "Name"
msgstr "名前"
#: pkg/dep/dep_graph.go:442 pkg/dep/dep_graph.go:548
msgid "No AUR package found for"
msgstr ""
#: pkg/dep/dep_graph.go:182
msgid "No package found for"
msgstr ""
#: print.go:225
msgid "None"
msgstr "なし"
#: print.go:39
msgid "Optional Deps"
msgstr "任意の依存パッケージ"
#: pkg/query/aur_warnings.go:75
#, fuzzy
msgid "Orphan (unmaintained) AUR Packages:"
msgstr "メンテナが存在しない AUR パッケージ:"
#: print.go:53 print.go:55
msgid "Out-of-date"
msgstr "古いバージョン"
#: pkg/sync/srcinfo/pgp/keys.go:115
msgid "PGP keys need importing:"
msgstr "PGP 鍵をインポートする必要があります:"
#: pkg/sync/workdir/preparer.go:252
#, fuzzy
msgid "PKGBUILD up to date, skipping download: %s"
msgstr "PKGBUILD は最新です、スキップ (%d/%d): %s"
#: pkg/menus/edit_menu.go:130
msgid "PKGBUILDs to edit?"
msgstr "PKGBUILD を編集しますか?"
#: print.go:60
msgid "Package Base ID"
msgstr "パッケージベース ID"
#: print.go:61
msgid "Package Base"
msgstr "パッケージベース"
#: pkg/query/aur_warnings.go:67
msgid "Packages not in AUR:"
msgstr ""
#: pkg/menus/clean_menu.go:54
msgid "Packages to cleanBuild?"
msgstr "パッケージをクリーンビルドしますか?"
#: pkg/dep/dep_graph.go:202
#, fuzzy
msgid "Packages to exclude"
msgstr "アップグレードするパッケージ。"
#: pkg/upgrade/service.go:295
msgid "Packages to exclude: (eg: \"1 2 3\", \"1-3\", \"^4\" or repo name)"
msgstr ""
"除外するパッケージ: (例: \"1 2 3\", \"1-3\", \"^4\" またはリポジトリ名)"
#: cmd.go:392
msgid "Packages to install (eg: 1 2 3, 1-3 or ^4)"
msgstr "インストールするパッケージ (例: 1 2 3, 1-3 または ^4)"
#: print.go:49
msgid "Popularity"
msgstr "人気度"
#: pkg/menus/diff_menu.go:172 pkg/menus/edit_menu.go:143
msgid "Proceed with install?"
msgstr "インストールを実行しますか?"
#: print.go:37
msgid "Provides"
msgstr "提供"
#: pkg/sync/workdir/preparer.go:125
msgid "Remove make dependencies after install?"
msgstr "ビルド時の依存パッケージをインストール後に削除しますか?"
#: print.go:43
msgid "Replaces"
msgstr ""
#: pkg/dep/dep_graph.go:730
msgid "Repository AUR"
msgstr "リポジトリ AUR"
#: print.go:30 pkg/db/ialpm/alpm.go:191
msgid "Repository"
msgstr "リポジトリ"
#: pkg/dep/dep_graph.go:78
msgid "SRCINFO"
msgstr ""
#: pkg/upgrade/service.go:72
msgid "Searching AUR for updates..."
msgstr "AUR からアップデートを検索..."
#: pkg/upgrade/service.go:160
msgid "Searching databases for updates..."
msgstr "データベースからアップデートを検索..."
#: pkg/query/query_builder.go:214
msgid "Showing repo packages only"
msgstr "リポジトリのパッケージだけを表示"
#: print.go:95
#, fuzzy
msgid "Size of pacman cache %s: %s"
msgstr "%s のパースに失敗: %s"
#: print.go:98
#, fuzzy
msgid "Size of yay cache %s: %s"
msgstr "%s のパースに失敗: %s"
#: print.go:62
msgid "Snapshot URL"
msgstr "スナップショット URL"
#: pkg/dep/dep_graph.go:76
msgid "Sync"
msgstr ""
#: print.go:100
msgid "Ten biggest packages:"
msgstr "最も巨大な10のパッケージ:"
#: pkg/sync/sync.go:124
msgid "The following packages are not compatible with your architecture:"
msgstr ""
"以下のパッケージはあなたの使っているアーキテクチャと互換性がありません:"
#: pkg/db/ialpm/alpm.go:179 pkg/dep/dep_graph.go:726
#, fuzzy
msgid "There are %[1]d providers available for %[2]s:"
msgstr "%[1]d 個のパッケージが %[2]s を提供しています:\n"
#: pkg/settings/exe/cmd_builder.go:258
msgid "There may be another Pacman instance running. Waiting..."
msgstr "他の Pacman インスタンスが実行中です。待機します..."
#: print.go:92
msgid "Total Size occupied by packages: %s"
msgstr "パッケージによって使用される合計容量: %s"
#: print.go:89
msgid "Total installed packages: %s"
msgstr "全てのインストールされたパッケージ: %s"
#: pkg/sync/sync.go:132
msgid "Try to build them anyway?"
msgstr "それでもパッケージをビルドしますか?"
#: print.go:34
msgid "URL"
msgstr "URL"
#: clean.go:194 pkg/menus/clean_menu.go:65 pkg/menus/clean_menu.go:71
#, fuzzy
msgid "Unable to clean:"
msgstr "ハンドルを作成できません: %s"
#: get.go:42 get.go:74
msgid "Unable to find the following packages:"
msgstr ""
#: vote.go:20
msgid "Unable to handle package vote for: %s. err: %s"
msgstr ""
#: clean.go:170
#, fuzzy
msgid "Unable to remove %s: %s"
msgstr "%s のパースに失敗: %s"
#: print.go:32
msgid "Version"
msgstr "バージョン"
#: print.go:50
msgid "Votes"
msgstr "投票"
#: print.go:87
msgid "Yay version v%s"
msgstr "Yay バージョン v%s"
#: pkg/menus/menu.go:49
msgid "[N]one"
msgstr "[N]なし"
#: clean.go:83
msgid ""
"\n"
"Build directory:"
msgstr ""
"\n"
"ビルドディレクトリ:"
#: pkg/db/ialpm/alpm.go:201 pkg/dep/dep_graph.go:740
msgid ""
"\n"
"Enter a number (default=1): "
msgstr ""
"\n"
"数字を入力してください (デフォルト=1): "
#: pkg/settings/errors.go:29
msgid "aborting due to user"
msgstr "ユーザーによって中止"
#: pkg/settings/parser/parser.go:608
msgid "argument '-' specified without input on stdin"
msgstr ""
#: local_install.go:26
msgid "cannot find PKGBUILD and .SRCINFO in directory"
msgstr ""
#: pkg/sync/build/pkg_archive.go:148
msgid "cannot find package name: %v"
msgstr "パッケージの名前を見つけることができません: %v"
#: pkg/sync/build/errors.go:30
msgid "could not find PKGDEST for: %s"
msgstr "PKGDEST を見つけることができません: %s"
#: errors.go:9
#, fuzzy
msgid "could not find all required packages"
msgstr "必要なパッケージを全て確認することができません:"
#: pkg/sync/build/errors.go:61
msgid "could not find any package archives listed in %s"
msgstr ""
#: pkg/sync/build/errors.go:50 pkg/upgrade/service.go:287
#, fuzzy
msgid "dependency"
msgstr "依存するパッケージ"
#: pkg/vcs/vcs.go:96 pkg/vcs/vcs.go:100
msgid "devel check for package failed: '%s' encountered an error"
msgstr ""
#: pkg/menus/edit_menu.go:110
msgid "editor did not exit successfully, aborting: %s"
msgstr "エディタが正しく終了しませんでした、中止: %s"
#: pkg/sync/workdir/aur_source.go:24
msgid "error downloading sources: %s"
msgstr "ソースのダウンロード時にエラー: %s"
#: pkg/download/errors.go:25
msgid "error fetching %s: %s"
msgstr "%s の取得時にエラー: %s"
#: pkg/sync/build/errors.go:9
msgid "error installing repo packages"
msgstr "リポジトリのパッケージのインストール時にエラー"
#: pkg/sync/build/installer.go:266 pkg/sync/build/installer.go:270
#, fuzzy
msgid "error installing:"
msgstr "リポジトリのパッケージのインストール時にエラー"
#: pkg/sync/build/installer.go:233 pkg/sync/build/installer.go:237
msgid "error making: %s"
msgstr "ビルド時にエラー: %s"
#: pkg/sync/workdir/merge.go:24
msgid "error merging %s: %s"
msgstr "%s のマージ時にエラー: %s"
#: pkg/download/unified.go:59
msgid "error reading %s"
msgstr "%s の読み取り時にエラー"
#: sync.go:36
msgid "error refreshing databases"
msgstr "データベースの更新時にエラー"
#: pkg/sync/workdir/clean.go:51 pkg/sync/workdir/merge.go:17
msgid "error resetting %s: %s"
msgstr "%s の再設定時にエラー: %s"
#: pkg/sync/build/errors.go:53
msgid "error updating package install reason to %s"
msgstr ""
#: pkg/sync/build/errors.go:48
msgid "explicit"
msgstr ""
#: pkg/settings/errors.go:23
msgid "failed to create directory '%s': %s"
msgstr "設定ディレクトリ '%s' の作成に失敗: %s"
#: pkg/settings/config.go:281
msgid "failed to open config file '%s': %s"
msgstr "設定ファイル '%s' のオープンに失敗: %s"
#: pkg/sync/srcinfo/service.go:114
msgid "failed to parse %s -- skipping: %s"
msgstr "%s のパースに失敗 -- スキップ: %s"
#: pkg/sync/srcinfo/service.go:118
msgid "failed to parse %s: %s"
msgstr "%s のパースに失敗: %s"
#: local_install.go:77
#, fuzzy
msgid "failed to parse .SRCINFO"
msgstr "%s のパースに失敗: %s"
#: pkg/settings/config.go:291
msgid "failed to read config file '%s': %s"
msgstr "設定ファイル '%s' の読み込みに失敗: %s"
#: pkg/cmd/graph/main.go:46 pkg/runtime/runtime.go:73
msgid "failed to retrieve aur Cache"
msgstr ""
#: pkg/upgrade/sources.go:27
#, fuzzy
msgid "ignoring package devel upgrade (no AUR info found):"
msgstr "%s: パッケージのアップグレードを無視 (%s => %s)"
#: pkg/text/errors.go:8
msgid "input too long"
msgstr "入力が長すぎます"
#: pkg/db/ialpm/alpm.go:222 pkg/dep/dep_graph.go:761
msgid "invalid number: %s"
msgstr "不正な数字: %s"
#: pkg/settings/parser/parser.go:174
msgid "invalid option '%s'"
msgstr "不正なオプション '%s'"
#: cmd.go:197
msgid "invalid option: '--deps' and '--explicit' may not be used together"
msgstr ""
#: pkg/download/abs.go:22
#, fuzzy
msgid "invalid repository"
msgstr "リポジトリ"
#: pkg/db/ialpm/alpm.go:227 pkg/dep/dep_graph.go:767
msgid "invalid value: %d is not between %d and %d"
msgstr "不正な値: %d は %d と %d の間にありません"
#: pkg/sync/srcinfo/pgp/keys.go:110
msgid "no keys to import"
msgstr "インポートする鍵がありません"
#: pkg/query/errors.go:20
msgid "no query was executed"
msgstr ""
#: local_install.go:66
msgid "no target directories specified"
msgstr ""
#: pkg/text/input.go:48
msgid "no"
msgstr "no"
#: pkg/sync/build/installer.go:242
msgid "nothing to install for %s"
msgstr ""
#: pkg/settings/parser/parser.go:164
msgid "only one operation may be used at a time"
msgstr "一度に使用できる操作はひとつだけです"
#: pkg/cmd/graph/main.go:70
msgid "only one target is allowed"
msgstr ""
#: print.go:187
msgid "package '%s' was not found"
msgstr "パッケージ '%s' が見つかりませんでした"
#: pkg/download/errors.go:15
#, fuzzy
msgid "package not found in AUR"
msgstr "パッケージ '%s' が見つかりませんでした"
#: pkg/download/abs.go:23
#, fuzzy
msgid "package not found in repos"
msgstr "パッケージ '%s' が見つかりませんでした"
#: pkg/upgrade/service.go:292
#, fuzzy
msgid "package"
msgid_plural "packages"
msgstr[0] "パッケージベース"
#: pkg/sync/srcinfo/pgp/keys.go:100
msgid "problem importing keys"
msgstr "鍵のインポート時にエラー"
#: clean.go:105
msgid "removing AUR packages from cache..."
msgstr "キャッシュから AUR パッケージを削除..."
#: clean.go:178 pkg/sync/workdir/clean.go:41
msgid "removing untracked AUR files from cache..."
msgstr "キャッシュから未追跡の AUR ファイルを削除..."
#: pkg/sync/build/errors.go:38
msgid "the PKGDEST for %s is listed by makepkg but does not exist: %s"
msgstr "%s の PKGDEST が makepkg によって指定されていますが存在しません: %s"
#: pkg/sync/sync.go:45
#, fuzzy
msgid "there is nothing to do"
msgstr " 何もすることがありません"
#: pkg/db/ialpm/alpm.go:247
msgid "unable to CreateHandle: %s"
msgstr "ハンドルを作成できません: %s"
#: cmd.go:186
msgid "unhandled operation"
msgstr "ハンドルが存在しない操作"
#: cmd.go:450
msgid "unknown-version"
msgstr "不明なバージョン"
#: pkg/text/input.go:47
msgid "yes"
msgstr "yes"
#~ msgid " (Target"
#~ msgstr " (対象"
#~ msgid " (Wanted by: "
#~ msgstr " (必要としているパッケージ: "
#~ msgid " Input too long"
#~ msgstr " 入力が長すぎます"
#~ msgid "Installing %s will remove:"
#~ msgstr "%s をインストールすることで削除されるパッケージ:"
#~ msgid "%s already downloaded -- use -f to overwrite"
#~ msgstr "%s は既にダウンロードされています -- -f で上書きできます"
#~ msgid "%s and %s unset"
#~ msgstr "%s と %s が設定されていません"
#~ msgid "%s not satisfied, flushing install queue"
#~ msgstr "%s が満たされていません、インストールキューを消去"
#~ msgid "Checking for conflicts..."
#~ msgstr "衝突を確認..."
#~ msgid "Checking for inner conflicts..."
#~ msgstr "内部衝突を確認..."
#~ msgid "Conflicting packages will have to be confirmed manually"
#~ msgstr "衝突するパッケージを手動で確認する必要があります"
#~ msgid "Downloaded PKGBUILD (%d/%d): %s"
#~ msgstr "PKGBUILD のダウンロード (%d/%d): %s"
#~ msgid "Missing ABS packages:"
#~ msgstr "存在しない ABS パッケージ:"
#~ msgid "Querying AUR..."
#~ msgstr "AUR を検索..."
#~ msgid ""
#~ "\n"
#~ "Inner conflicts found:"
#~ msgstr ""
#~ "\n"
#~ "内部衝突が存在します:"
#~ msgid ""
#~ "\n"
#~ "Package conflicts found:"
#~ msgstr ""
#~ "\n"
#~ "パッケージの衝突が存在します:"
#~ msgid "error cloning %s: %s"
#~ msgstr "%s の複製時にエラー: %s"
#~ msgid "error during AUR search: %s"
#~ msgstr "AUR の検索時にエラー: %s"
#~ msgid "failed to create BuildDir directory '%s': %s"
#~ msgstr "BuildDir ディレクトリ '%s' の作成に失敗: %s"
#~ msgid "failed to get pkgbuild: %s: %s"
#~ msgstr "pkgbuild の取得に失敗: %s: %s"
#~ msgid "failed to link %s: %s"
#~ msgstr "%s のリンクに失敗: %s"
#~ msgid "failed to open vcs file '%s': %s"
#~ msgstr "vcs ファイル '%s' のオープンに失敗: %s"
#~ msgid "failed to read vcs file '%s': %s"
#~ msgstr "vcs ファイル '%s' の読み込みに失敗: %s"
#~ msgid "invalid sort mode. Fix with yay -Y --bottomup --save"
#~ msgstr "不正なソートモードです。yay -Y --bottomup --save で修正してください"
#~ msgid "no packages match search"
#~ msgstr "検索にマッチするパッケージがありません"
#~ msgid "package conflicts can not be resolved with noconfirm, aborting"
#~ msgstr "パッケージの衝突は noconfirm で解決できません、中止"
#~ msgid "refusing to install AUR packages as root, aborting"
#~ msgstr "AUR パッケージの root によるインストールを拒否します、中止"
#~ msgid "failed to create cache directory '%s': %s"
#~ msgstr "キャッシュディレクトリ '%s' の作成に失敗: %s"
================================================
FILE: po/ko.po
================================================
#
# Translators:
# J G, 2021
# JungHee Lee , 2025
#
msgid ""
msgstr ""
"Last-Translator: JungHee Lee , 2025\n"
"Language-Team: Korean (https://app.transifex.com/yay-1/teams/123732/ko/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: ko\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: xgotext\n"
#: clean.go:83
msgid ""
"\n"
"Build directory:"
msgstr ""
"\n"
"빌드 디렉터리:"
#: pkg/db/ialpm/alpm.go:201 pkg/dep/dep_graph.go:740
msgid ""
"\n"
"Enter a number (default=1): "
msgstr ""
"\n"
"숫자를 입력하세요 (기본값=1): "
#: pkg/menus/menu.go:32
msgid " (Build Files Exist)"
msgstr " (빌드 파일 존재)"
#: pkg/menus/menu.go:27
msgid " (Installed)"
msgstr " (설치됨)"
#: cmd.go:453
msgid " [Installed]"
msgstr " [설치됨]"
#: cmd.go:410 vote.go:36
msgid " there is nothing to do"
msgstr " 할 작업 없음"
#: pkg/menus/menu.go:49
msgid "%s [A]ll [Ab]ort [I]nstalled [No]tInstalled or (1 2 3, 1-3, ^4)"
msgstr "%s [A]모두 [Ab]중단 [I]설치됨 [No]설치안함 / (1 2 3, 1-3, ^4)"
#: pkg/sync/build/installer.go:308
msgid "%s already made -- skipping build"
msgstr "%s 이미 빌드됨 -- 빌드 건너뛰는 중"
#: pkg/menus/edit_menu.go:57
msgid "%s is not set"
msgstr "%s 지정되지 않음"
#: pkg/settings/exe/cmd_builder.go:257
msgid "%s is present."
msgstr "%s가 존재합니다."
#: pkg/dep/dep_graph.go:460 pkg/sync/build/installer.go:305
msgid "%s is up to date -- skipping"
msgstr "%s 최신 버전임 -- 건너뛰는 중"
#: pkg/upgrade/service.go:291
msgid "%s to upgrade/install."
msgstr "업그레이드/설치하려면 %s 선택합니다."
#: pkg/upgrade/service.go:285
msgid "%s will also be installed for this operation."
msgstr "이 작업을 위해 %s 또한 설치됩니다."
#: pkg/sync/srcinfo/pgp/keys.go:124
msgid "%s, required by: %s"
msgstr "%s, 필요한 패키지: %s"
#: pkg/menus/diff_menu.go:49
msgid "%s: No changes -- skipping"
msgstr "%s: 변경사항 없음 -- 건너뛰는 중"
#: pkg/query/filter.go:22
msgid "%s: can't use target with option --aur -- skipping"
msgstr "%s: 대상에 --aur 옵션 사용할 수 없음 -- 건너뛰는 중"
#: pkg/query/filter.go:17
msgid "%s: can't use target with option --repo -- skipping"
msgstr "%s: 대상에 --repo 옵션 사용할 수 없음 -- 건너뛰는 중"
#: pkg/upgrade/sources.go:57
msgid "%s: ignoring package upgrade (%s => %s)"
msgstr "%s: 패키지 업그레이드 무시하는 중 (%s => %s)"
#: pkg/query/aur_warnings.go:46
msgid "%s: local (%s) is newer than AUR (%s)"
msgstr "%s: 로컬 (%s) 버전이 AUR (%s)보다 높음"
#: vote.go:51
msgid ""
"%s: please set AUR_USERNAME and AUR_PASSWORD environment variables for "
"voting"
msgstr "%s: 투표를 위해 AUR_USERNAME 및 AUR_PASSWORD 환경 변수를 설정하십시오"
#: pkg/download/unified.go:192
msgid "(%d/%d) Downloaded PKGBUILD from ABS: %s"
msgstr "(%d/%d) ABS에서 PKGBUILD 다운로드됨: %s"
#: pkg/download/aur.go:92 pkg/download/unified.go:188
msgid "(%d/%d) Downloaded PKGBUILD: %s"
msgstr "(%d/%d) PKGBUILD 다운로드됨: %s"
#: pkg/download/aur.go:82
msgid "(%d/%d) Failed to download PKGBUILD: %s"
msgstr "(%d/%d) PKGBUILD 다운로드에 실패함: %s"
#: pkg/sync/srcinfo/service.go:109
msgid "(%d/%d) Parsing SRCINFO: %s"
msgstr "(%d/%d) SRCINFO 분석하는 중: %s"
#: pkg/query/types.go:103 pkg/query/types.go:72
msgid "(Installed)"
msgstr "(설치됨)"
#: pkg/query/types.go:101 pkg/query/types.go:70
msgid "(Installed: %s)"
msgstr "(설치됨: %s)"
#: pkg/query/types.go:61
msgid "(Orphaned)"
msgstr "(고립됨)"
#: pkg/query/types.go:65
msgid "(Out-of-date: %s)"
msgstr "(오래됨: %s)"
#: pkg/dep/dep_graph.go:75
msgid "AUR"
msgstr "AUR"
#: print.go:44
msgid "AUR URL"
msgstr "AUR URL"
#: pkg/menus/edit_menu.go:58
msgid "Add %s or %s to your environment variables"
msgstr "%s 또는 %s를 환경 변수에 추가합니다"
#: main.go:60
msgid "Avoid running yay as root/sudo."
msgstr "root/sudo로 yay를 실행하지 마세요."
#: pkg/dep/dep_graph.go:63
msgid "Check Dependency"
msgstr "종속성 확인"
#: print.go:41
msgid "Check Deps"
msgstr "종속성 확인"
#: pkg/upgrade/service.go:89
msgid "Checking development packages..."
msgstr "개발 패키지 확인하는 중..."
#: pkg/sync/workdir/clean.go:45
msgid "Cleaning (%d/%d): %s"
msgstr "비우는 중 (%d/%d): %s"
#: print.go:42
msgid "Conflicts With"
msgstr "충돌하는 패키지"
#: pkg/menus/clean_menu.go:62
msgid "Deleting (%d/%d): %s"
msgstr "삭제하는 중 (%d/%d): %s"
#: pkg/dep/dep_graph.go:61
msgid "Dependency"
msgstr "종속성"
#: print.go:38
msgid "Depends On"
msgstr "종속하는 패키지"
#: print.go:33
msgid "Description"
msgstr "설명"
#: pkg/menus/diff_menu.go:160
msgid "Diffs to show?"
msgstr "표시할 차이점(Diff)이 있나요?"
#: pkg/settings/migrations.go:25
msgid "Disable 'provides' setting by default"
msgstr "기본적으로 '제공 패키지' 설정 비활성화"
#: clean.go:78
msgid "Do you want to remove ALL AUR packages from cache?"
msgstr "캐시에 있는 AUR 패키지를 모두 제거하시겠습니까?"
#: clean.go:95
msgid "Do you want to remove ALL untracked AUR files?"
msgstr "추척되지 않은 AUR 파일을 모두 제거하시겠습니까?"
#: clean.go:80
msgid "Do you want to remove all other AUR packages from cache?"
msgstr "캐시의 다른 AUR 패키지를 모두 제거하시겠습니까?"
#: pkg/menus/edit_menu.go:61
msgid "Edit PKGBUILD with?"
msgstr "다음으로 PKGBUILD를 편집하시겠습니까?"
#: pkg/query/errors.go:13
msgid "Error during AUR search: %s\n"
msgstr "AUR 검색 중 오류: %s\n"
#: pkg/upgrade/service.go:295
msgid "Excluding packages may cause partial upgrades and break systems"
msgstr "패키지를 제외하면 부분 업그레이드 및 시스템 중단이 발생할 수 있습니다."
#: pkg/dep/dep_graph.go:60
msgid "Explicit"
msgstr "명시적"
#: print.go:91
msgid "Explicitly installed packages: %s"
msgstr "명시적으로 설치된 패키지: %s"
#: pkg/dep/dep_graph.go:437 pkg/dep/dep_graph.go:535
msgid "Failed to find AUR package for"
msgstr "다음에 대한 AUR 패키지를 찾지 못함"
#: pkg/sync/build/installer.go:120
msgid "Failed to install layer, rolling up to next layer."
msgstr "레이어를 설치하지 못하여, 다음 레이어로 롤업합니다."
#: pkg/sync/build/errors.go:16
msgid ""
"Failed to install the following packages. Manual intervention is required:"
msgstr "다음 패키지를 설치하지 못했습니다. 수동 개입이 필요합니다:"
#: print.go:45
msgid "First Submitted"
msgstr "처음 제출됨"
#: pkg/query/aur_warnings.go:83
msgid "Flagged Out Of Date AUR Packages:"
msgstr "오래된 AUR 패키지로 플래그 지정됨:"
#: print.go:90
msgid "Foreign installed packages: %s"
msgstr "외부 설치된 패키지: %s"
#: pkg/vcs/vcs.go:144
msgid "Found git repo: %s"
msgstr "git 저장소 찾음: %s"
#: vcs.go:72
msgid "GenDB finished. No packages were installed"
msgstr "GenDB 완료됨. 패키지가 설치되지 않았습니다"
#: print.go:36
msgid "Groups"
msgstr "그룹"
#: pkg/sync/srcinfo/pgp/keys.go:88
msgid "Import?"
msgstr "가져올까요?"
#: pkg/sync/srcinfo/pgp/keys.go:97
msgid "Importing keys with gpg..."
msgstr "gpg로 키 가져오는 중..."
#: print.go:46
msgid "Keywords"
msgstr "키워드"
#: print.go:47
msgid "Last Modified"
msgstr "마지막 수정됨"
#: print.go:35
msgid "Licenses"
msgstr "라이선스"
#: pkg/dep/dep_graph.go:77
msgid "Local"
msgstr "로컬"
#: print.go:48
msgid "Maintainer"
msgstr "유지관리자"
#: pkg/dep/dep_graph.go:62
msgid "Make Dependency"
msgstr "종속성 만들기"
#: print.go:40
msgid "Make Deps"
msgstr "종속성 만들기"
#: pkg/dep/dep_graph.go:79
msgid "Missing"
msgstr "누락됨"
#: pkg/query/aur_warnings.go:75
msgid "Missing AUR Debug Packages:"
msgstr "누락된 AUR 디버그 패키지:"
#: print.go:31
msgid "Name"
msgstr "이름"
#: pkg/dep/dep_graph.go:442 pkg/dep/dep_graph.go:564
msgid "No AUR package found for"
msgstr "다음 AUR 패키지를 찾을 수 없음"
#: pkg/dep/dep_graph.go:564
msgid "required by"
msgstr "연관된 패키지"
#: pkg/dep/dep_graph.go:182
msgid "No package found for"
msgstr "다음 패키지를 찾을 수 없음"
#: print.go:225
msgid "None"
msgstr "없음"
#: print.go:39
msgid "Optional Deps"
msgstr "선택적 종속성"
#: pkg/query/aur_warnings.go:79
msgid "Orphan (unmaintained) AUR Packages:"
msgstr "고립 (유지 관리되지 않는) AUR 패키지:"
#: print.go:53 print.go:55
msgid "Out-of-date"
msgstr "오래됨"
#: pkg/sync/srcinfo/pgp/keys.go:115
msgid "PGP keys need importing:"
msgstr "가져와야 할 PGP 키:"
#: pkg/sync/workdir/preparer.go:252
msgid "PKGBUILD up to date, skipping download: %s"
msgstr "PKGBUILD 최신 버전임, 다운로드 건너뛰는 중: %s"
#: pkg/menus/edit_menu.go:130
msgid "PKGBUILDs to edit?"
msgstr "PKGBUILD를 편집하시겠습니까?"
#: print.go:61
msgid "Package Base"
msgstr "패키지 베이스"
#: print.go:60
msgid "Package Base ID"
msgstr "패키지 베이스 ID"
#: pkg/query/aur_warnings.go:71
msgid "Packages not in AUR:"
msgstr "AUR에 없는 패키지:"
#: pkg/menus/clean_menu.go:54
msgid "Packages to cleanBuild?"
msgstr "cleanBuild할 패키지는 무엇인가요?"
#: pkg/dep/dep_graph.go:202
msgid "Packages to exclude"
msgstr "제외할 패키지"
#: pkg/upgrade/service.go:294
msgid "Packages to exclude: (eg: \"1 2 3\", \"1-3\", \"^4\" or repo name)"
msgstr "제외할 패키지: (예: \"1 2 3\", \"1-3\", \"^4\" 혹은 저장소 이름)"
#: cmd.go:392
msgid "Packages to install (eg: 1 2 3, 1-3 or ^4)"
msgstr "설치할 패키지: (예: 1 2 3, 1-3 혹은 ^4)"
#: print.go:49
msgid "Popularity"
msgstr "인기순"
#: pkg/menus/diff_menu.go:172 pkg/menus/edit_menu.go:143
msgid "Proceed with install?"
msgstr "설치를 계속 진행하시겠습니까?"
#: print.go:37
msgid "Provides"
msgstr "제공 패키지"
#: pkg/sync/workdir/preparer.go:125
msgid "Remove make dependencies after install?"
msgstr "설치 후 make 종속성을 제거하시겠습니까?"
#: print.go:43
msgid "Replaces"
msgstr "교체하기"
#: pkg/db/ialpm/alpm.go:191 print.go:30
msgid "Repository"
msgstr "저장소"
#: pkg/dep/dep_graph.go:730
msgid "Repository AUR"
msgstr "저장소 AUR"
#: pkg/dep/dep_graph.go:78
msgid "SRCINFO"
msgstr "SRCINFO"
#: pkg/upgrade/service.go:71
msgid "Searching AUR for updates..."
msgstr "AUR에서 업데이트 검색하는 중..."
#: pkg/upgrade/service.go:159
msgid "Searching databases for updates..."
msgstr "데이터베이스에서 업데이트 검색하는 중..."
#: pkg/query/query_builder.go:214
msgid "Showing repo packages only"
msgstr "저장소 패키지만 표시하는 중"
#: print.go:95
msgid "Size of pacman cache %s: %s"
msgstr "pacman 캐시 %s의 크기: %s"
#: print.go:98
msgid "Size of yay cache %s: %s"
msgstr "yay 캐시 %s의 크기: %s"
#: print.go:62
msgid "Snapshot URL"
msgstr "스냅숏 URL"
#: pkg/dep/dep_graph.go:76
msgid "Sync"
msgstr "동기화"
#: print.go:100
msgid "Ten biggest packages:"
msgstr "가장 큰 10개 패키지:"
#: pkg/sync/sync.go:124
msgid "The following packages are not compatible with your architecture:"
msgstr "다음 패키지는 이 컴퓨터의 아키텍처와 호환되지 않음:"
#: pkg/db/ialpm/alpm.go:179 pkg/dep/dep_graph.go:726
msgid "There are %[1]d providers available for %[2]s:"
msgstr "%[2]s에 대해 %[1]d개의 의존성 충족 패키지가 있습니다:"
#: pkg/settings/exe/cmd_builder.go:258
msgid "There may be another Pacman instance running. Waiting..."
msgstr "다른 pacman 인스턴스가 실행 중입니다. 대기 중..."
#: print.go:92
msgid "Total Size occupied by packages: %s"
msgstr "패키지가 차지하는 전체 크기: %s"
#: print.go:89
msgid "Total installed packages: %s"
msgstr "설치된 전체 패키지: %s"
#: pkg/sync/sync.go:132
msgid "Try to build them anyway?"
msgstr "그래도 빌드할까요?"
#: print.go:34
msgid "URL"
msgstr "URL"
#: clean.go:194 pkg/menus/clean_menu.go:65 pkg/menus/clean_menu.go:71
msgid "Unable to clean:"
msgstr "비울 수 없음:"
#: get.go:42 get.go:74
msgid "Unable to find the following packages:"
msgstr "다음 패키지를 찾을 수 없음:"
#: vote.go:20
msgid "Unable to handle package vote for: %s. err: %s"
msgstr "다음의 패키지 투표를 처리할 수 없습니다: %s. 오류: %s"
#: clean.go:170
msgid "Unable to remove %s: %s"
msgstr "%s 제거할 수 없음: %s"
#: print.go:32
msgid "Version"
msgstr "버전"
#: print.go:50
msgid "Votes"
msgstr "투표"
#: print.go:87
msgid "Yay version v%s"
msgstr "Yay 버전 v%s"
#: pkg/menus/menu.go:49
msgid "[N]one"
msgstr "[N]안함"
#: pkg/settings/errors.go:29
msgid "aborting due to user"
msgstr "사용자에 의해 중단됨"
#: pkg/settings/parser/parser.go:608
msgid "argument '-' specified without input on stdin"
msgstr "stdin에 대한 입력없이 독립변수 '-' 지정됨"
#: local_install.go:26
msgid "cannot find PKGBUILD and .SRCINFO in directory"
msgstr "디렉터리에서 PKGBUILD 및 .SRCINFO를 찾을 수 없음"
#: pkg/sync/build/pkg_archive.go:148
msgid "cannot find package name: %v"
msgstr "패키지 이름을 찾을 수 없음: %v"
#: pkg/sync/build/errors.go:30
msgid "could not find PKGDEST for: %s"
msgstr "PKGDEST를 찾을 수 없음: %s"
#: errors.go:9
msgid "could not find all required packages"
msgstr "필요한 패키지를 모두 찾을 수 없음"
#: pkg/sync/build/errors.go:61
msgid "could not find any package archives listed in %s"
msgstr "%s에 나열된 패키지 아카이브를 찾을 수 없음"
#: pkg/sync/build/errors.go:50 pkg/upgrade/service.go:286
msgid "dependency"
msgstr "종속성"
#: pkg/vcs/vcs.go:100 pkg/vcs/vcs.go:96
msgid "devel check for package failed: '%s' encountered an error"
msgstr "패키지에 대한 devel 확인 실패함: '%s'에 하나의 오류가 발생했습니다"
#: pkg/menus/edit_menu.go:110
msgid "editor did not exit successfully, aborting: %s"
msgstr "편집기가 제대로 종료되지 않음, 중단하는 중: %s"
#: pkg/sync/workdir/aur_source.go:24
msgid "error downloading sources: %s"
msgstr "소스 다운로드 중 오류: %s"
#: pkg/download/errors.go:25
msgid "error fetching %s: %s"
msgstr "%s 가져오는 중 오류: %s"
#: pkg/sync/build/errors.go:9
msgid "error installing repo packages"
msgstr "저장소 패키지 설치 중 오류"
#: pkg/sync/build/installer.go:266 pkg/sync/build/installer.go:270
msgid "error installing:"
msgstr "설치 중 오류:"
#: pkg/sync/build/installer.go:233 pkg/sync/build/installer.go:237
msgid "error making: %s"
msgstr "빌드 중 오류: %s"
#: pkg/sync/workdir/merge.go:24
msgid "error merging %s: %s"
msgstr "%s 병합하는 중 오류: %s"
#: pkg/download/unified.go:59
msgid "error reading %s"
msgstr "%s 읽는 중 오류"
#: sync.go:36
msgid "error refreshing databases"
msgstr "데이터베이스 갱신하는 중 오류"
#: pkg/sync/workdir/clean.go:51 pkg/sync/workdir/merge.go:17
msgid "error resetting %s: %s"
msgstr "%s 재설정하는 중 오류: %s"
#: pkg/sync/build/errors.go:53
msgid "error updating package install reason to %s"
msgstr "패키지 설치 근거를 %s로 업데이트하는 중 오류"
#: pkg/sync/build/errors.go:48
msgid "explicit"
msgstr "명시적"
#: pkg/settings/errors.go:23
msgid "failed to create directory '%s': %s"
msgstr "'%s' 디렉터리 만들기 실패함: %s"
#: pkg/settings/config.go:281
msgid "failed to open config file '%s': %s"
msgstr " '%s' 구성 파일 열기 실패함: %s"
#: pkg/sync/srcinfo/service.go:114
msgid "failed to parse %s -- skipping: %s"
msgstr "%s 분석 실패함 -- 건너뜁니다: %s"
#: pkg/sync/srcinfo/service.go:118
msgid "failed to parse %s: %s"
msgstr "%s 분석 실패함: %s"
#: local_install.go:77
msgid "failed to parse .SRCINFO"
msgstr ".SRCINFO를 분석하지 못했습니다"
#: pkg/settings/config.go:291
msgid "failed to read config file '%s': %s"
msgstr "'%s' 구성 파일 읽기 실패함: %s"
#: pkg/cmd/graph/main.go:46 pkg/runtime/runtime.go:73
msgid "failed to retrieve aur Cache"
msgstr "AUR 캐시를 검색하지 못했습니다"
#: pkg/upgrade/sources.go:27
msgid "ignoring package devel upgrade (no AUR info found):"
msgstr "패키지 개발 업그레이드 무시하는 중 (AUR 정보를 찾을 수 없음):"
#: pkg/text/errors.go:8
msgid "input too long"
msgstr "너무 긴 입력"
#: pkg/db/ialpm/alpm.go:222 pkg/dep/dep_graph.go:761
msgid "invalid number: %s"
msgstr "잘못된 숫자: %s"
#: pkg/settings/parser/parser.go:174
msgid "invalid option '%s'"
msgstr "잘못된 옵션 '%s'"
#: cmd.go:197
msgid "invalid option: '--deps' and '--explicit' may not be used together"
msgstr "잘못된 옵션: '--deps'와 '--explicit'는 함께 사용할 수 없습니다"
#: pkg/download/abs.go:22
msgid "invalid repository"
msgstr "잘못된 저장소"
#: pkg/db/ialpm/alpm.go:227 pkg/dep/dep_graph.go:767
msgid "invalid value: %d is not between %d and %d"
msgstr "잘못된 값: %d가 %d와 %d 사이에 있지 않음"
#: pkg/text/input.go:48
msgid "no"
msgstr "아니요"
#: pkg/sync/srcinfo/pgp/keys.go:110
msgid "no keys to import"
msgstr "가져올 키 없음"
#: pkg/query/errors.go:20
msgid "no query was executed"
msgstr "실행된 쿼리가 없습니다"
#: local_install.go:66
msgid "no target directories specified"
msgstr "지정된 대상 디렉터리가 없습니다"
#: pkg/sync/build/installer.go:242
msgid "nothing to install for %s"
msgstr "%s 용으로 설치할 항목 없음"
#: pkg/settings/parser/parser.go:164
msgid "only one operation may be used at a time"
msgstr "한번에 한 작업만 쓸 수 있음"
#: pkg/cmd/graph/main.go:70
msgid "only one target is allowed"
msgstr "하나의 대상만 허용됩니다"
#: pkg/upgrade/service.go:291
msgid "package"
msgid_plural "packages"
msgstr[0] "패키지"
#: print.go:187
msgid "package '%s' was not found"
msgstr "'%s' 패키지를 찾지 못했습니다"
#: pkg/download/errors.go:15
msgid "package not found in AUR"
msgstr "AUR에서 패키지를 찾을 수 없습니다"
#: pkg/download/abs.go:23
msgid "package not found in repos"
msgstr "저장소에서 패키지를 찾을 수 없습니다"
#: pkg/sync/srcinfo/pgp/keys.go:100
msgid "problem importing keys"
msgstr "키 가져오는 중 오류"
#: clean.go:105
msgid "removing AUR packages from cache..."
msgstr "캐시에서 AUR 패키지 제거하는 중..."
#: clean.go:178 pkg/sync/workdir/clean.go:41
msgid "removing untracked AUR files from cache..."
msgstr "캐시에서 추적되지 않은 AUR 패키지 제거하는 중..."
#: pkg/sync/build/errors.go:38
msgid "the PKGDEST for %s is listed by makepkg but does not exist: %s"
msgstr "makepkg에 등록된 %s의 PKGDEST가 존재하지 않음: %s"
#: pkg/sync/sync.go:45
msgid "there is nothing to do"
msgstr "할 작업 없음"
#: pkg/db/ialpm/alpm.go:247
msgid "unable to CreateHandle: %s"
msgstr "CreateHandle 할 수 없음: %s"
#: cmd.go:186
msgid "unhandled operation"
msgstr "핸들되지 않은 작업"
#: cmd.go:450
msgid "unknown-version"
msgstr "알 수 없는 버전"
#: pkg/text/input.go:47
msgid "yes"
msgstr "예"
================================================
FILE: po/nl.po
================================================
#
# Translators:
# Jonathan van der Steege , 2023
# Loek Le Blansch , 2023
# Luke Rieff, 2023
# Ariejan de Vroom , 2024
# Heimen Stoffels , 2025
#
msgid ""
msgstr ""
"Last-Translator: Heimen Stoffels , 2025\n"
"Language-Team: Dutch (https://app.transifex.com/yay-1/teams/123732/nl/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: nl\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: xgotext\n"
#: clean.go:83
msgid ""
"\n"
"Build directory:"
msgstr ""
"\n"
"Bouwmap:"
#: pkg/db/ialpm/alpm.go:201 pkg/dep/dep_graph.go:740
msgid ""
"\n"
"Enter a number (default=1): "
msgstr ""
"\n"
"Voer een getal in (standaard=1):"
#: pkg/menus/menu.go:32
msgid " (Build Files Exist)"
msgstr "(Bouwbestanden zijn aanwezig)"
#: pkg/menus/menu.go:27
msgid " (Installed)"
msgstr "(Geïnstalleerd)"
#: cmd.go:453
msgid " [Installed]"
msgstr "[Geïnstalleerd]"
#: cmd.go:410 vote.go:36
msgid " there is nothing to do"
msgstr "er valt niets te doen"
#: pkg/menus/menu.go:49
msgid "%s [A]ll [Ab]ort [I]nstalled [No]tInstalled or (1 2 3, 1-3, ^4)"
msgstr ""
"[A]lle [An]nuleren [G]eïnstalleerd [N]iet geïnstalleerd of (1 2 3, 1-3, ^4)"
#: pkg/sync/build/installer.go:308
msgid "%s already made -- skipping build"
msgstr "%s is al gemaakt -- de bouw wordt overgeslagen"
#: pkg/menus/edit_menu.go:57
msgid "%s is not set"
msgstr "%s is niet ingesteld"
#: pkg/settings/exe/cmd_builder.go:257
msgid "%s is present."
msgstr "%s is aanwezig."
#: pkg/dep/dep_graph.go:460 pkg/sync/build/installer.go:305
msgid "%s is up to date -- skipping"
msgstr "%s is actueel -- wordt overgeslagen"
#: pkg/upgrade/service.go:291
msgid "%s to upgrade/install."
msgstr "%s op te waarderen/installeren."
#: pkg/upgrade/service.go:285
msgid "%s will also be installed for this operation."
msgstr "%s wordt ook geïnstalleerd bij het uitvoeren van deze actie."
#: pkg/sync/srcinfo/pgp/keys.go:124
msgid "%s, required by: %s"
msgstr "%s, benodigd door: %s"
#: pkg/menus/diff_menu.go:49
msgid "%s: No changes -- skipping"
msgstr "%s: Geen wijzigingen -- wordt overgeslagen"
#: pkg/query/filter.go:22
msgid "%s: can't use target with option --aur -- skipping"
msgstr "%s: kan doel niet gebruiken i.c.m. optie --aur -- wordt overgeslagen"
#: pkg/query/filter.go:17
msgid "%s: can't use target with option --repo -- skipping"
msgstr "%s: kan doel niet gebruiken i.c.m. optie --repo -- wordt overgeslagen"
#: pkg/upgrade/sources.go:57
msgid "%s: ignoring package upgrade (%s => %s)"
msgstr "%s: pakketupgrade wordt genegeerd (%s => %s)"
#: pkg/query/aur_warnings.go:46
msgid "%s: local (%s) is newer than AUR (%s)"
msgstr "%s: de lokale versie (%s) is nieuwer dan de AUR-versie (%s)"
#: vote.go:51
msgid ""
"%s: please set AUR_USERNAME and AUR_PASSWORD environment variables for "
"voting"
msgstr ""
"%s: gebruik de omgevingsvariabelen AUR_USERNAME en AUR_PASSWORD om te "
"stemmen"
#: pkg/download/unified.go:192
msgid "(%d/%d) Downloaded PKGBUILD from ABS: %s"
msgstr "(%d/%d) PKGBUILD gedownload van ABS: %s"
#: pkg/download/aur.go:92 pkg/download/unified.go:188
msgid "(%d/%d) Downloaded PKGBUILD: %s"
msgstr "(%d/%d) PKGBUILD gedownload: %s"
#: pkg/download/aur.go:82
msgid "(%d/%d) Failed to download PKGBUILD: %s"
msgstr "(%d/%d) De PKGBUILD kan niet worden gedownload: %s"
#: pkg/sync/srcinfo/service.go:109
msgid "(%d/%d) Parsing SRCINFO: %s"
msgstr "(%d/%d) Bezig met verwerken van SRCINFO: %s"
#: pkg/query/types.go:103 pkg/query/types.go:72
msgid "(Installed)"
msgstr "(Geïnstalleerd)"
#: pkg/query/types.go:101 pkg/query/types.go:70
msgid "(Installed: %s)"
msgstr "(Geïnstalleerd: %s)"
#: pkg/query/types.go:61
msgid "(Orphaned)"
msgstr "(Onteigend)"
#: pkg/query/types.go:65
msgid "(Out-of-date: %s)"
msgstr "(Verouderd: %s)"
#: pkg/dep/dep_graph.go:75
msgid "AUR"
msgstr "AUR"
#: print.go:44
msgid "AUR URL"
msgstr "AUR-url"
#: pkg/menus/edit_menu.go:58
msgid "Add %s or %s to your environment variables"
msgstr "Voeg %s of %s toe aan uw omgevingsvariabelen"
#: main.go:60
msgid "Avoid running yay as root/sudo."
msgstr "Voer yay niet uit als root/sudo."
#: pkg/dep/dep_graph.go:63
msgid "Check Dependency"
msgstr "Afhankelijkheid controleren"
#: print.go:41
msgid "Check Deps"
msgstr "Afhankelijkheden controleren"
#: pkg/upgrade/service.go:89
msgid "Checking development packages..."
msgstr "Bezig met controleren van ontwikkelpakketten…"
#: pkg/sync/workdir/clean.go:45
msgid "Cleaning (%d/%d): %s"
msgstr "Bezig met opruimen… (%d/%d): %s"
#: print.go:42
msgid "Conflicts With"
msgstr "Botst met"
#: pkg/menus/clean_menu.go:62
msgid "Deleting (%d/%d): %s"
msgstr "Bezig met verwijderen… (%d/%d): %s"
#: pkg/dep/dep_graph.go:61
msgid "Dependency"
msgstr "Afhankelijkheid"
#: print.go:38
msgid "Depends On"
msgstr "Afhankelijk van"
#: print.go:33
msgid "Description"
msgstr "Beschrijving"
#: pkg/menus/diff_menu.go:160
msgid "Diffs to show?"
msgstr "Verschillen tonen?"
#: pkg/settings/migrations.go:25
msgid "Disable 'provides' setting by default"
msgstr "Schakel de ‘biedt’-instelling standaard uit"
#: clean.go:78
msgid "Do you want to remove ALL AUR packages from cache?"
msgstr "Wilt u ALLE AUR-pakketten verwijderen uit de cache?"
#: clean.go:95
msgid "Do you want to remove ALL untracked AUR files?"
msgstr "Wilt u ALLE niet-gevolgde AUR-bestanden verwijderen?"
#: clean.go:80
msgid "Do you want to remove all other AUR packages from cache?"
msgstr "Wilt u alle andere AUR-pakketten verwijderen uit de cache?"
#: pkg/menus/edit_menu.go:61
msgid "Edit PKGBUILD with?"
msgstr "PKGBUILD bewerken met?"
#: pkg/query/errors.go:13
msgid "Error during AUR search: %s\n"
msgstr "Fout tijdens AUR-zoekactie: %s\n"
#: pkg/upgrade/service.go:295
msgid "Excluding packages may cause partial upgrades and break systems"
msgstr ""
"Pakketten uitsluiten kan mogelijk leiden tot onafgeronde upgrades en/of "
"beschadigde systemen"
#: pkg/dep/dep_graph.go:60
msgid "Explicit"
msgstr "Uitdrukkelijk"
#: print.go:91
msgid "Explicitly installed packages: %s"
msgstr "Uitdrukkelijk geïnstalleerde pakketten: %s"
#: pkg/dep/dep_graph.go:437 pkg/dep/dep_graph.go:535
msgid "Failed to find AUR package for"
msgstr "Er is geen AUR-pakket gevonden voor "
#: pkg/sync/build/installer.go:120
msgid "Failed to install layer, rolling up to next layer."
msgstr ""
"Het installeren van de laag is mislukt - de volgende laag wordt gebruikt"
#: pkg/sync/build/errors.go:16
msgid ""
"Failed to install the following packages. Manual intervention is required:"
msgstr ""
"De volgende pakketten konden niet worden geïnstalleerd. Handmatige "
"interventie is vereist:"
#: print.go:45
msgid "First Submitted"
msgstr "Eerst ingezonden op"
#: pkg/query/aur_warnings.go:83
msgid "Flagged Out Of Date AUR Packages:"
msgstr "Verouderde AUR-pakketten:"
#: print.go:90
msgid "Foreign installed packages: %s"
msgstr "Van buitenaf geïnstalleerde pakketten: %s"
#: pkg/vcs/vcs.go:144
msgid "Found git repo: %s"
msgstr "Git-repo aangetroffen: %s"
#: vcs.go:72
msgid "GenDB finished. No packages were installed"
msgstr "GenDB is afgerond. Er zijn geen pakketten zijn geïnstalleerd."
#: print.go:36
msgid "Groups"
msgstr "Groepen"
#: pkg/sync/srcinfo/pgp/keys.go:88
msgid "Import?"
msgstr "Importeren?"
#: pkg/sync/srcinfo/pgp/keys.go:97
msgid "Importing keys with gpg..."
msgstr "Bezig met importeren van sleutels…"
#: print.go:46
msgid "Keywords"
msgstr "Trefwoorden"
#: print.go:47
msgid "Last Modified"
msgstr "Laatst bewerkt op"
#: print.go:35
msgid "Licenses"
msgstr "Licenties"
#: pkg/dep/dep_graph.go:77
msgid "Local"
msgstr "Lokaal"
#: print.go:48
msgid "Maintainer"
msgstr "Onderhouder"
#: pkg/dep/dep_graph.go:62
msgid "Make Dependency"
msgstr "Make-afhankelijkheid"
#: print.go:40
msgid "Make Deps"
msgstr "Make-afhankelijkheden"
#: pkg/dep/dep_graph.go:79
msgid "Missing"
msgstr "Ontbrekend"
#: pkg/query/aur_warnings.go:75
msgid "Missing AUR Debug Packages:"
msgstr "Ontbrekende AUR-foutopsporingspakketten:"
#: print.go:31
msgid "Name"
msgstr "Naam"
#: pkg/dep/dep_graph.go:442 pkg/dep/dep_graph.go:564
msgid "No AUR package found for"
msgstr "Geen AUR-pakket gevonden voor "
#: pkg/dep/dep_graph.go:564
msgid "required by"
msgstr "benodigd door"
#: pkg/dep/dep_graph.go:182
msgid "No package found for"
msgstr "Geen AUR-pakket gevonden voor "
#: print.go:225
msgid "None"
msgstr "Geen"
#: print.go:39
msgid "Optional Deps"
msgstr "Optionele afhankelijkheden"
#: pkg/query/aur_warnings.go:79
msgid "Orphan (unmaintained) AUR Packages:"
msgstr "Onteigende (niet-onderhouden) AUR-pakketten:"
#: print.go:53 print.go:55
msgid "Out-of-date"
msgstr "Verouderd"
#: pkg/sync/srcinfo/pgp/keys.go:115
msgid "PGP keys need importing:"
msgstr "Te importeren pgp-sleutels:"
#: pkg/sync/workdir/preparer.go:252
msgid "PKGBUILD up to date, skipping download: %s"
msgstr ""
"De PKGBUILD is actueel, dus het downloaden van ‘%s’ wordt overgeslagen."
#: pkg/menus/edit_menu.go:130
msgid "PKGBUILDs to edit?"
msgstr "Te bewerken PKGBUILDs?"
#: print.go:61
msgid "Package Base"
msgstr "Pakketbasis"
#: print.go:60
msgid "Package Base ID"
msgstr "Basis-id van pakket"
#: pkg/query/aur_warnings.go:71
msgid "Packages not in AUR:"
msgstr "Pakketten niet in AUR:"
#: pkg/menus/clean_menu.go:54
msgid "Packages to cleanBuild?"
msgstr "Te herbouwen pakketten?"
#: pkg/dep/dep_graph.go:202
msgid "Packages to exclude"
msgstr "Uitgesloten pakketten"
#: pkg/upgrade/service.go:294
msgid "Packages to exclude: (eg: \"1 2 3\", \"1-3\", \"^4\" or repo name)"
msgstr ""
"Uit te sluiten pakketten: (bijv. ‘1 2 3’, ‘1-3’, ‘^4’ of pakketbronnaam)"
#: cmd.go:392
msgid "Packages to install (eg: 1 2 3, 1-3 or ^4)"
msgstr "Te installeren pakketten (bijv. 1 2 3, 1-3 of ^4)"
#: print.go:49
msgid "Popularity"
msgstr "Populariteit"
#: pkg/menus/diff_menu.go:172 pkg/menus/edit_menu.go:143
msgid "Proceed with install?"
msgstr "Doorgaan met installeren?"
#: print.go:37
msgid "Provides"
msgstr "Biedt"
#: pkg/sync/workdir/preparer.go:125
msgid "Remove make dependencies after install?"
msgstr "Make-afhankelijkheden verwijderen na installatie?"
#: print.go:43
msgid "Replaces"
msgstr "Vervangt"
#: pkg/db/ialpm/alpm.go:191 print.go:30
msgid "Repository"
msgstr "Pakketbron"
#: pkg/dep/dep_graph.go:730
msgid "Repository AUR"
msgstr "AUR-repo"
#: pkg/dep/dep_graph.go:78
msgid "SRCINFO"
msgstr "SRCINFO"
#: pkg/upgrade/service.go:71
msgid "Searching AUR for updates..."
msgstr "Bezig met zoeken naar AUR-updates…"
#: pkg/upgrade/service.go:159
msgid "Searching databases for updates..."
msgstr "Bezig met zoeken naar updates in databanken…"
#: pkg/query/query_builder.go:214
msgid "Showing repo packages only"
msgstr "Toont alleen repo-pakketten"
#: print.go:95
msgid "Size of pacman cache %s: %s"
msgstr "Omvang van pacman-cache %s: %s"
#: print.go:98
msgid "Size of yay cache %s: %s"
msgstr "Omvang van yay-cache %s: %s"
#: print.go:62
msgid "Snapshot URL"
msgstr "Momentopname-url"
#: pkg/dep/dep_graph.go:76
msgid "Sync"
msgstr "Synchroniseren"
#: print.go:100
msgid "Ten biggest packages:"
msgstr "Tien grootste pakketten:"
#: pkg/sync/sync.go:124
msgid "The following packages are not compatible with your architecture:"
msgstr "De volgende pakketten zijn niet compatibel met uw architectuur:"
#: pkg/db/ialpm/alpm.go:179 pkg/dep/dep_graph.go:726
msgid "There are %[1]d providers available for %[2]s:"
msgstr "Er zijn %[1]d bronnen die %[2]s aanbieden:"
#: pkg/settings/exe/cmd_builder.go:258
msgid "There may be another Pacman instance running. Waiting..."
msgstr "Wellicht is er een ander proces van Pacman bezig. Aan het wachten…"
#: print.go:92
msgid "Total Size occupied by packages: %s"
msgstr "Totale omvang van pakketten: %s"
#: print.go:89
msgid "Total installed packages: %s"
msgstr "Totaalaantal geïnstalleerde pakketten: %s"
#: pkg/sync/sync.go:132
msgid "Try to build them anyway?"
msgstr "Toch doorgaan met bouwen?"
#: print.go:34
msgid "URL"
msgstr "Url"
#: clean.go:194 pkg/menus/clean_menu.go:65 pkg/menus/clean_menu.go:71
msgid "Unable to clean:"
msgstr "Het opruimen is mislukt:"
#: get.go:42 get.go:74
msgid "Unable to find the following packages:"
msgstr "De volgende pakketten zijn niet aangetroffen:"
#: vote.go:20
msgid "Unable to handle package vote for: %s. err: %s"
msgstr "De stem op ‘%s’ kan niet worden verwerkt. Foutmelding: %s"
#: clean.go:170
msgid "Unable to remove %s: %s"
msgstr "‘%s’ kan niet worden verwijderd: %s"
#: print.go:32
msgid "Version"
msgstr "Versie"
#: print.go:50
msgid "Votes"
msgstr "Stemmen"
#: print.go:87
msgid "Yay version v%s"
msgstr "Yay-versie v%s"
#: pkg/menus/menu.go:49
msgid "[N]one"
msgstr "[G]een"
#: pkg/settings/errors.go:29
msgid "aborting due to user"
msgstr "afbreken vanwege gebruiker"
#: pkg/settings/parser/parser.go:608
msgid "argument '-' specified without input on stdin"
msgstr "‘-’ als optie ingevoerd zonder invoer op stdin"
#: local_install.go:26
msgid "cannot find PKGBUILD and .SRCINFO in directory"
msgstr "Er zijn geen PKGBUILD en .SRCINFO aangetroffen in de map"
#: pkg/sync/build/pkg_archive.go:148
msgid "cannot find package name: %v"
msgstr "Er is geen pakket met de naam “%v” aangetroffen"
#: pkg/sync/build/errors.go:30
msgid "could not find PKGDEST for: %s"
msgstr "Er is geen PKGDEST aangetroffen voor ‘%s’"
#: errors.go:9
msgid "could not find all required packages"
msgstr "Niet alle vereiste pakketten zijn aangetroffen"
#: pkg/sync/build/errors.go:61
msgid "could not find any package archives listed in %s"
msgstr "Er zijn geen pakketarchieven aangetroffen in %s"
#: pkg/sync/build/errors.go:50 pkg/upgrade/service.go:286
msgid "dependency"
msgstr "afhankelijkheid"
#: pkg/vcs/vcs.go:100 pkg/vcs/vcs.go:96
msgid "devel check for package failed: '%s' encountered an error"
msgstr "Ontwikkelcontrole van pakket mislukt: fout opgetreden in ‘%s’"
#: pkg/menus/edit_menu.go:110
msgid "editor did not exit successfully, aborting: %s"
msgstr "De bewerker is niet goed afgesloten - het proces wordt afgebroken: %s"
#: pkg/sync/workdir/aur_source.go:24
msgid "error downloading sources: %s"
msgstr "De bronnen kunnen niet worden opgehaald: %s"
#: pkg/download/errors.go:25
msgid "error fetching %s: %s"
msgstr "‘%s’ kan niet worden opgehaald: %s"
#: pkg/sync/build/errors.go:9
msgid "error installing repo packages"
msgstr "De pakketten kunnen niet worden geïnstalleerd"
#: pkg/sync/build/installer.go:266 pkg/sync/build/installer.go:270
msgid "error installing:"
msgstr "Het installeren van de volgende pakketten is mislukt:"
#: pkg/sync/build/installer.go:233 pkg/sync/build/installer.go:237
msgid "error making: %s"
msgstr "Het maken van de volgende pakketten is mislukt: %s"
#: pkg/sync/workdir/merge.go:24
msgid "error merging %s: %s"
msgstr "‘%s’ kan niet worden samengevoegd: %s"
#: pkg/download/unified.go:59
msgid "error reading %s"
msgstr "‘%s’ kan niet worden uitgelezen"
#: sync.go:36
msgid "error refreshing databases"
msgstr "De databanken kunnen niet worden vernieuwd"
#: pkg/sync/workdir/clean.go:51 pkg/sync/workdir/merge.go:17
msgid "error resetting %s: %s"
msgstr "‘%s’ kan niet worden hersteld: %s"
#: pkg/sync/build/errors.go:53
msgid "error updating package install reason to %s"
msgstr "Het bijwerken van de installatiereden naar %s is mislukt"
#: pkg/sync/build/errors.go:48
msgid "explicit"
msgstr "Uitdrukkelijk"
#: pkg/settings/errors.go:23
msgid "failed to create directory '%s': %s"
msgstr "‘%s’ kan niet worden aangemaakt: %s"
#: pkg/settings/config.go:281
msgid "failed to open config file '%s': %s"
msgstr "‘%s’ kan niet worden geopend: %s"
#: pkg/sync/srcinfo/service.go:114
msgid "failed to parse %s -- skipping: %s"
msgstr "‘%s’ kan niet worden verwerkt -- wordt overgeslagen: %s"
#: pkg/sync/srcinfo/service.go:118
msgid "failed to parse %s: %s"
msgstr "‘%s’ kan niet worden verwerkt: %s"
#: local_install.go:77
msgid "failed to parse .SRCINFO"
msgstr ".SRCINFO kan niet worden verwerkt"
#: pkg/settings/config.go:291
msgid "failed to read config file '%s': %s"
msgstr "‘%s’ kan niet worden uitgelezen: %s"
#: pkg/cmd/graph/main.go:46 pkg/runtime/runtime.go:73
msgid "failed to retrieve aur Cache"
msgstr "De AUR-cache kan niet worden opgehaald"
#: pkg/upgrade/sources.go:27
msgid "ignoring package devel upgrade (no AUR info found):"
msgstr "De ontwikkelupgrade is genegeerd (geen AUR-informatie aangetroffen):"
#: pkg/text/errors.go:8
msgid "input too long"
msgstr "De invoer is te lang"
#: pkg/db/ialpm/alpm.go:222 pkg/dep/dep_graph.go:761
msgid "invalid number: %s"
msgstr "Ongeldig getal: %s"
#: pkg/settings/parser/parser.go:174
msgid "invalid option '%s'"
msgstr "Ongeldige optie: ‘%s’"
#: cmd.go:197
msgid "invalid option: '--deps' and '--explicit' may not be used together"
msgstr ""
"Ongeldige optie: ‘--deps’ en ‘--explicit’ mogen niet gelijktijdig worden "
"gebruikt"
#: pkg/download/abs.go:22
msgid "invalid repository"
msgstr "Ongeldige pakketbron"
#: pkg/db/ialpm/alpm.go:227 pkg/dep/dep_graph.go:767
msgid "invalid value: %d is not between %d and %d"
msgstr "Ongeldige waarde: %d ligt niet tussen %d en %d"
#: pkg/text/input.go:48
msgid "no"
msgstr "nee"
#: pkg/sync/srcinfo/pgp/keys.go:110
msgid "no keys to import"
msgstr "Er zijn geen te importeren sleutels"
#: pkg/query/errors.go:20
msgid "no query was executed"
msgstr "Er is geen zoekactie uitgevoerd"
#: local_install.go:66
msgid "no target directories specified"
msgstr "Er zijn geen doelmappen opgegeven"
#: pkg/sync/build/installer.go:242
msgid "nothing to install for %s"
msgstr "Er is niets te installeren voor %s"
#: pkg/settings/parser/parser.go:164
msgid "only one operation may be used at a time"
msgstr "Er mag slechts één actie per keer worden uitgevoerd"
#: pkg/cmd/graph/main.go:70
msgid "only one target is allowed"
msgstr "Er is slechts één doel toegestaan"
#: pkg/upgrade/service.go:291
msgid "package"
msgid_plural "packages"
msgstr[0] "Pakket"
msgstr[1] "Pakketten"
#: print.go:187
msgid "package '%s' was not found"
msgstr "‘%s’ is niet aangetroffen"
#: pkg/download/errors.go:15
msgid "package not found in AUR"
msgstr "Het pakket is niet aangetroffen in de AUR"
#: pkg/download/abs.go:23
msgid "package not found in repos"
msgstr "Het pakket is niet aangetroffen in de pakketbronnen"
#: pkg/sync/srcinfo/pgp/keys.go:100
msgid "problem importing keys"
msgstr "De sleutels kunnen niet worden geïmporteerd"
#: clean.go:105
msgid "removing AUR packages from cache..."
msgstr "Bezig met verwijderen van AUR-pakketten uit cache…"
#: clean.go:178 pkg/sync/workdir/clean.go:41
msgid "removing untracked AUR files from cache..."
msgstr "Bezig met verwijderen van niet-gevolgde AUR-bestanden uit cache…"
#: pkg/sync/build/errors.go:38
msgid "the PKGDEST for %s is listed by makepkg but does not exist: %s"
msgstr "De PKGDEST van ‘%s’ wordt vermeld door makepkg, maar bestaat niet: %s"
#: pkg/sync/sync.go:45
msgid "there is nothing to do"
msgstr "Er is niets meer te doen"
#: pkg/db/ialpm/alpm.go:247
msgid "unable to CreateHandle: %s"
msgstr "CreateHandle kan niet worden uitgevoerd: %s"
#: cmd.go:186
msgid "unhandled operation"
msgstr "Onafgehandelde actie"
#: cmd.go:450
msgid "unknown-version"
msgstr "Onbekende versie"
#: pkg/text/input.go:47
msgid "yes"
msgstr "ja"
================================================
FILE: po/pl.po
================================================
#
# Translators:
# J G, 2021
# Oskar , 2022
# A_Salata, 2024
# SMT Broadcast, 2024
# Chair n, 2024
# Paweł Bernaciak, 2024
# Mariusz Bubak, 2024
# Bartłomiej Konecki, 2024
# Tadeusz Magura-Witkowski, 2024
# Tymon Marek, 2025
# iwo wolani, 2025
# Artur Wdowik, 2026
#
msgid ""
msgstr ""
"Last-Translator: Artur Wdowik, 2026\n"
"Language-Team: Polish (https://app.transifex.com/yay-1/teams/123732/pl/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: pl\n"
"Plural-Forms: nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n"
"X-Generator: xgotext\n"
#: clean.go:83
msgid ""
"\n"
"Build directory:"
msgstr ""
"\n"
"Folder kompilacji:"
#: pkg/db/ialpm/alpm.go:201 pkg/dep/dep_graph.go:740
msgid ""
"\n"
"Enter a number (default=1): "
msgstr ""
"\n"
"Wybierz wartość (domyślnie=1): "
#: pkg/menus/menu.go:32
msgid " (Build Files Exist)"
msgstr "(Pliki Budowy Istnieją)"
#: pkg/menus/menu.go:27
msgid " (Installed)"
msgstr " (zainstalowano)"
#: cmd.go:453
msgid " [Installed]"
msgstr " [zainstalowano]"
#: cmd.go:410 vote.go:36
msgid " there is nothing to do"
msgstr " nie ma nic do zrobienia"
#: pkg/menus/menu.go:49
msgid "%s [A]ll [Ab]ort [I]nstalled [No]tInstalled or (1 2 3, 1-3, ^4)"
msgstr ""
"%s [A]Wszystkie [Ab]Anuluj [I]Zainstalowane [No]Nie zainstalowane lub (1 2 "
"3, 1-3, ^4)"
#: pkg/sync/build/installer.go:308
msgid "%s already made -- skipping build"
msgstr "%s już jest zbudowany - pomijam budowanie"
#: pkg/menus/edit_menu.go:57
msgid "%s is not set"
msgstr "%s jest nieokreślony"
#: pkg/settings/exe/cmd_builder.go:257
msgid "%s is present."
msgstr "%s jest już obecny."
#: pkg/dep/dep_graph.go:460 pkg/sync/build/installer.go:305
msgid "%s is up to date -- skipping"
msgstr "%s jest aktualny - pomijam"
#: pkg/upgrade/service.go:291
msgid "%s to upgrade/install."
msgstr "%s do aktualizacji/instalacji."
#: pkg/upgrade/service.go:285
msgid "%s will also be installed for this operation."
msgstr "%s również zostanie zainstalowanie w ramach operacji."
#: pkg/sync/srcinfo/pgp/keys.go:124
msgid "%s, required by: %s"
msgstr "%s, wymagane przez: %s"
#: pkg/menus/diff_menu.go:49
msgid "%s: No changes -- skipping"
msgstr "%s: Bez zmian - pomijam"
#: pkg/query/filter.go:22
msgid "%s: can't use target with option --aur -- skipping"
msgstr "%s: nie jest dostępny z --aur - pomijam"
#: pkg/query/filter.go:17
msgid "%s: can't use target with option --repo -- skipping"
msgstr "%s: nie jest dostępny z --repo - pomijam"
#: pkg/upgrade/sources.go:57
msgid "%s: ignoring package upgrade (%s => %s)"
msgstr "%s: ignoruję aktualizację pakietu (%s => %s)"
#: pkg/query/aur_warnings.go:46
msgid "%s: local (%s) is newer than AUR (%s)"
msgstr "%s: wersja lokalna (%s) jest nowsza niż ta z AUR (%s)"
#: vote.go:51
msgid ""
"%s: please set AUR_USERNAME and AUR_PASSWORD environment variables for "
"voting"
msgstr ""
"%s: wprowadź proszę zmienne środowiskowe AUR_USERNAME oraz AUR_PASSWORD by "
"móc głosować"
#: pkg/download/unified.go:192
msgid "(%d/%d) Downloaded PKGBUILD from ABS: %s"
msgstr "(%d/%d) Pobrano PKGBUILD z ABS-u: %s"
#: pkg/download/aur.go:92 pkg/download/unified.go:188
msgid "(%d/%d) Downloaded PKGBUILD: %s"
msgstr "(%d/%d) Pobrany PKGBUILD: %s"
#: pkg/download/aur.go:82
msgid "(%d/%d) Failed to download PKGBUILD: %s"
msgstr "(%d/%d) Nie udało się pobrać PKGBUILD: %s"
#: pkg/sync/srcinfo/service.go:109
msgid "(%d/%d) Parsing SRCINFO: %s"
msgstr "(%d/%d) Przetwarzam SRCINFO: %s"
#: pkg/query/types.go:103 pkg/query/types.go:72
msgid "(Installed)"
msgstr "(Zainstalowana)"
#: pkg/query/types.go:101 pkg/query/types.go:70
msgid "(Installed: %s)"
msgstr "(Zainstalowano: %s)"
#: pkg/query/types.go:61
msgid "(Orphaned)"
msgstr "(Osierocona)"
#: pkg/query/types.go:65
msgid "(Out-of-date: %s)"
msgstr "(Nieaktualny od: %s)"
#: pkg/dep/dep_graph.go:75
msgid "AUR"
msgstr "AUR"
#: print.go:44
msgid "AUR URL"
msgstr "AUR URL"
#: pkg/menus/edit_menu.go:58
msgid "Add %s or %s to your environment variables"
msgstr "Dodaj %s lub %s do zmiennych środowiskowych"
#: main.go:60
msgid "Avoid running yay as root/sudo."
msgstr "Unikaj uruchamiania yay jako root lub z sudo."
#: pkg/dep/dep_graph.go:63
msgid "Check Dependency"
msgstr "Sprawdzanie Zależności"
#: print.go:41
msgid "Check Deps"
msgstr "Sprawdź Zależności"
#: pkg/upgrade/service.go:89
msgid "Checking development packages..."
msgstr "Sprawdzanie pakietów w wersjach rozwojowych..."
#: pkg/sync/workdir/clean.go:45
msgid "Cleaning (%d/%d): %s"
msgstr "Czyszczenie (%d/%d): %s"
#: print.go:42
msgid "Conflicts With"
msgstr "Koliduje z"
#: pkg/menus/clean_menu.go:62
msgid "Deleting (%d/%d): %s"
msgstr "Usuwanie (%d/%d): %s"
#: pkg/dep/dep_graph.go:61
msgid "Dependency"
msgstr "Zależność"
#: print.go:38
msgid "Depends On"
msgstr "Zależy od"
#: print.go:33
msgid "Description"
msgstr "Opis"
#: pkg/menus/diff_menu.go:160
msgid "Diffs to show?"
msgstr "Pokazać różnice?"
#: pkg/settings/migrations.go:25
msgid "Disable 'provides' setting by default"
msgstr "Domyślnie wyłącza ustawienie 'provides' "
#: clean.go:78
msgid "Do you want to remove ALL AUR packages from cache?"
msgstr "Czy chcesz usunąć WSZYSTKIE pakiety AUR z pamięci podręcznej?"
#: clean.go:95
msgid "Do you want to remove ALL untracked AUR files?"
msgstr "Czy chcesz usunąć WSZYSTKIE nieśledzone pliki AUR?"
#: clean.go:80
msgid "Do you want to remove all other AUR packages from cache?"
msgstr "Czy chcesz usunąć wszystkie inne pakiety AUR z pamięci podręczniej?"
#: pkg/menus/edit_menu.go:61
msgid "Edit PKGBUILD with?"
msgstr "Edytor PKGBUILD-ów?"
#: pkg/query/errors.go:13
msgid "Error during AUR search: %s\n"
msgstr "Błąd podczas wyszukiwania w AUR: %s\n"
#: pkg/upgrade/service.go:295
msgid "Excluding packages may cause partial upgrades and break systems"
msgstr ""
"Wykluczanie pakietów może spowodować częściową aktualizacje i uszkodzenie "
"systemu"
#: pkg/dep/dep_graph.go:60
msgid "Explicit"
msgstr "Jawne"
#: print.go:91
msgid "Explicitly installed packages: %s"
msgstr "Jawnie zainstalowane pakiety: %s"
#: pkg/dep/dep_graph.go:437 pkg/dep/dep_graph.go:535
msgid "Failed to find AUR package for"
msgstr "Nie znaleziono pakietu AUR dla"
#: pkg/sync/build/installer.go:120
msgid "Failed to install layer, rolling up to next layer."
msgstr "Niepowodzenie zainstalowania warstwy, przewijanie do następnej warstwy"
#: pkg/sync/build/errors.go:16
msgid ""
"Failed to install the following packages. Manual intervention is required:"
msgstr "Nie udało sie zainstalować następujących pakietów. Wymagana interwencja "
"użytkownika:"
#: print.go:45
msgid "First Submitted"
msgstr "Zamieszczono po raz pierwszy"
#: pkg/query/aur_warnings.go:83
msgid "Flagged Out Of Date AUR Packages:"
msgstr "Pakiety AUR oznaczone jako nieaktualne:"
#: print.go:90
msgid "Foreign installed packages: %s"
msgstr "Obce zainstalowane pakiety: %s"
#: pkg/vcs/vcs.go:144
msgid "Found git repo: %s"
msgstr "Znaleziono repozytorium git: %s"
#: vcs.go:72
msgid "GenDB finished. No packages were installed"
msgstr "Zakończono GenDB. Nie zainstalowano żadnych pakietów"
#: print.go:36
msgid "Groups"
msgstr "Grupy"
#: pkg/sync/srcinfo/pgp/keys.go:88
msgid "Import?"
msgstr "Zaimportować?"
#: pkg/sync/srcinfo/pgp/keys.go:97
msgid "Importing keys with gpg..."
msgstr "Importowanie kluczy przy użyciu gpg..."
#: print.go:46
msgid "Keywords"
msgstr "Słowa kluczowe"
#: print.go:47
msgid "Last Modified"
msgstr "Ostatnio zmodyfikowano"
#: print.go:35
msgid "Licenses"
msgstr "Licencje"
#: pkg/dep/dep_graph.go:77
msgid "Local"
msgstr "Lokalne"
#: print.go:48
msgid "Maintainer"
msgstr "Opiekun"
#: pkg/dep/dep_graph.go:62
msgid "Make Dependency"
msgstr "Zależność Budowania"
#: print.go:40
msgid "Make Deps"
msgstr "Zbuduj zależności"
#: pkg/dep/dep_graph.go:79
msgid "Missing"
msgstr "Zaginiony"
#: pkg/query/aur_warnings.go:75
msgid "Missing AUR Debug Packages:"
msgstr "Brakujące Pakiety Debugowania AUR:"
#: print.go:31
msgid "Name"
msgstr "Nazwa"
#: pkg/dep/dep_graph.go:442 pkg/dep/dep_graph.go:548
msgid "No AUR package found for"
msgstr "Nie znaleziono pakietu AUR dla"
#: pkg/dep/dep_graph.go:182
msgid "No package found for"
msgstr "Nie znaleziono pakietu dla"
#: print.go:225
msgid "None"
msgstr "Brak"
#: print.go:39
msgid "Optional Deps"
msgstr "Opcjonalne zależności"
#: pkg/query/aur_warnings.go:79
msgid "Orphan (unmaintained) AUR Packages:"
msgstr "Osierocone (nieutrzymywane) pakiety AUR:"
#: print.go:53 print.go:55
msgid "Out-of-date"
msgstr "Nieaktualne"
#: pkg/sync/srcinfo/pgp/keys.go:115
msgid "PGP keys need importing:"
msgstr "Klucze PGP wymagające zaimportowania:"
#: pkg/sync/workdir/preparer.go:252
msgid "PKGBUILD up to date, skipping download: %s"
msgstr "PKGBUILD jest aktualny, pomijam pobieranie: %s"
#: pkg/menus/edit_menu.go:130
msgid "PKGBUILDs to edit?"
msgstr "Edytować PKGBUILD-y?"
#: print.go:61
msgid "Package Base"
msgstr "Podstawa Pakietu"
#: print.go:60
msgid "Package Base ID"
msgstr "ID Podstawy Pakietu"
#: pkg/query/aur_warnings.go:71
msgid "Packages not in AUR:"
msgstr "Pakiety nie występujace w AUR:"
#: pkg/menus/clean_menu.go:54
msgid "Packages to cleanBuild?"
msgstr "Pakiety do zbudowania od zera?"
#: pkg/dep/dep_graph.go:202
msgid "Packages to exclude"
msgstr "Pakiety do wykluczenia"
#: pkg/upgrade/service.go:294
msgid "Packages to exclude: (eg: \"1 2 3\", \"1-3\", \"^4\" or repo name)"
msgstr "Wykluczone pakiety: (np.: \"1 2 3\", \"1-3\", \"^4\" lub nazwa repozytorium)"
#: cmd.go:392
msgid "Packages to install (eg: 1 2 3, 1-3 or ^4)"
msgstr "Pakiety do zainstalowania (np.: 1 2 3, 1-3 or ^4)"
#: print.go:49
msgid "Popularity"
msgstr "Popularność"
#: pkg/menus/diff_menu.go:172 pkg/menus/edit_menu.go:143
msgid "Proceed with install?"
msgstr "Kontynuować instalację?"
#: print.go:37
msgid "Provides"
msgstr "Dostarcza"
#: pkg/sync/workdir/preparer.go:125
msgid "Remove make dependencies after install?"
msgstr "Usunąć zależności potrzebne do zbudowania po instalacji?"
#: print.go:43
msgid "Replaces"
msgstr "Zastępuje"
#: pkg/db/ialpm/alpm.go:191 print.go:30
msgid "Repository"
msgstr "Repozytorium"
#: pkg/dep/dep_graph.go:730
msgid "Repository AUR"
msgstr "Repozytorium AUR"
#: pkg/dep/dep_graph.go:78
msgid "SRCINFO"
msgstr "SRCINFO"
#: pkg/upgrade/service.go:71
msgid "Searching AUR for updates..."
msgstr "Poszukuję aktualizacji w AUR..."
#: pkg/upgrade/service.go:159
msgid "Searching databases for updates..."
msgstr "Poszukuję aktualizacji w bazie danych..."
#: pkg/query/query_builder.go:214
msgid "Showing repo packages only"
msgstr "Pokazuję tylko pakiety z repozytoriów"
#: print.go:95
msgid "Size of pacman cache %s: %s"
msgstr "Rozmiar pamięci podręcznej Pacmana %s: %s"
#: print.go:98
msgid "Size of yay cache %s: %s"
msgstr "Rozmiar pamięci podręcznej yay %s: %s"
#: print.go:62
msgid "Snapshot URL"
msgstr "URL Snapshota"
#: pkg/dep/dep_graph.go:76
msgid "Sync"
msgstr "Synchronizacja"
#: print.go:100
msgid "Ten biggest packages:"
msgstr "Dziesięć największych pakietów:"
#: pkg/sync/sync.go:124
msgid "The following packages are not compatible with your architecture:"
msgstr "Te pakiety nie są kompatybilne z architekturą Twojego systemu:"
#: pkg/db/ialpm/alpm.go:179 pkg/dep/dep_graph.go:726
msgid "There are %[1]d providers available for %[2]s:"
msgstr "Jest %d możliwych źródeł dla %s:"
#: pkg/settings/exe/cmd_builder.go:258
msgid "There may be another Pacman instance running. Waiting..."
msgstr "Prawdopodobnie działa teraz inna instancja Pacmana. Czekam..."
#: print.go:92
msgid "Total Size occupied by packages: %s"
msgstr "Łączny rozmiar zajmowany przez pakiety: %s"
#: print.go:89
msgid "Total installed packages: %s"
msgstr "Łączna liczba zainstalowanych pakietów: %s"
#: pkg/sync/sync.go:132
msgid "Try to build them anyway?"
msgstr "Spróbować je zbudować mimo wszystko?"
#: print.go:34
msgid "URL"
msgstr "URL"
#: clean.go:194 pkg/menus/clean_menu.go:65 pkg/menus/clean_menu.go:71
msgid "Unable to clean:"
msgstr "Nie można sprzątnąć:"
#: get.go:42 get.go:74
msgid "Unable to find the following packages:"
msgstr "Nie udało się znaleźć następujących pakietów:"
#: vote.go:20
msgid "Unable to handle package vote for: %s. err: %s"
msgstr "Nie udało się obsłużyć oceny pakietu dla: %s. err: %s"
#: clean.go:170
msgid "Unable to remove %s: %s"
msgstr "Nie udało się usunąć %s: %s"
#: print.go:32
msgid "Version"
msgstr "Wersja"
#: print.go:50
msgid "Votes"
msgstr "Głosów"
#: print.go:87
msgid "Yay version v%s"
msgstr "Wersja yay v%s"
#: pkg/menus/menu.go:49
msgid "[N]one"
msgstr "[N]ic"
#: pkg/settings/errors.go:29
msgid "aborting due to user"
msgstr "przerwane przez użytkownika"
#: pkg/settings/parser/parser.go:608
msgid "argument '-' specified without input on stdin"
msgstr "argument '-' określony bez danych wejściowych"
#: local_install.go:26
msgid "cannot find PKGBUILD and .SRCINFO in directory"
msgstr "nie znaleziono PKGBUILD i .SRCINFO w katalogu"
#: pkg/sync/build/pkg_archive.go:148
msgid "cannot find package name: %v"
msgstr "nie odnalazłem pakietu: %v"
#: pkg/sync/build/errors.go:30
msgid "could not find PKGDEST for: %s"
msgstr "nie odnalazłem PKGDEST dla: %s"
#: errors.go:9
msgid "could not find all required packages"
msgstr "nie znaleziono wszystkich wymaganych pakietów"
#: pkg/sync/build/errors.go:61
msgid "could not find any package archives listed in %s"
msgstr "nie udało się znaleźć żadnych archiwów pakietów z listy %s"
#: pkg/sync/build/errors.go:50 pkg/upgrade/service.go:286
msgid "dependency"
msgstr "zależność"
#: pkg/vcs/vcs.go:100 pkg/vcs/vcs.go:96
msgid "devel check for package failed: '%s' encountered an error"
msgstr "błąd sprawdzania wersji rozwojowej: '%s' napotkało na błąd"
#: pkg/menus/edit_menu.go:110
msgid "editor did not exit successfully, aborting: %s"
msgstr "edytor nie zamknął się poprawnie, anuluję: %s"
#: pkg/sync/workdir/aur_source.go:24
msgid "error downloading sources: %s"
msgstr "błąd ściągania źródeł: %s"
#: pkg/download/errors.go:25
msgid "error fetching %s: %s"
msgstr "błąd podczas ściągania %s: %s"
#: pkg/sync/build/errors.go:9
msgid "error installing repo packages"
msgstr "błąd podczas instalowania pakietów z repozytorium"
#: pkg/sync/build/installer.go:266 pkg/sync/build/installer.go:270
msgid "error installing:"
msgstr "Błąd podczas instalacji:"
#: pkg/sync/build/installer.go:233 pkg/sync/build/installer.go:237
msgid "error making: %s"
msgstr "błąd podczas budowania: %s"
#: pkg/sync/workdir/merge.go:24
msgid "error merging %s: %s"
msgstr "błąd podczas scalania %s: %s"
#: pkg/download/unified.go:59
msgid "error reading %s"
msgstr "błąd podczas odczytywania %s"
#: sync.go:36
msgid "error refreshing databases"
msgstr "błąd odświeżania baz danych"
#: pkg/sync/workdir/clean.go:51 pkg/sync/workdir/merge.go:17
msgid "error resetting %s: %s"
msgstr "błąd podczas resetowania %s: %s"
#: pkg/sync/build/errors.go:53
msgid "error updating package install reason to %s"
msgstr "błąd aktualizacji pakietu z przyczyn %s"
#: pkg/sync/build/errors.go:48
msgid "explicit"
msgstr "jawne"
#: pkg/settings/errors.go:23
msgid "failed to create directory '%s': %s"
msgstr "nie udało się stworzyć katalogu '%s': %s"
#: pkg/settings/config.go:281
msgid "failed to open config file '%s': %s"
msgstr "nie udało się otworzyć pliku konfiguracyjnego '%s': %s"
#: pkg/sync/srcinfo/service.go:114
msgid "failed to parse %s -- skipping: %s"
msgstr "nie udało się przetworzyć %s - pomijam: %s"
#: pkg/sync/srcinfo/service.go:118
msgid "failed to parse %s: %s"
msgstr "nie udało się przetworzyć %s: %s"
#: local_install.go:77
msgid "failed to parse .SRCINFO"
msgstr "nie udało się przetworzyć .SRCINFO"
#: pkg/settings/config.go:291
msgid "failed to read config file '%s': %s"
msgstr "nie udało się odczytać pliku konfiguracyjnego '%s': %s"
#: pkg/cmd/graph/main.go:46 pkg/runtime/runtime.go:73
msgid "failed to retrieve aur Cache"
msgstr "Nie udało się pozyskać pamięci podręcznej AUR"
#: pkg/upgrade/sources.go:27
msgid "ignoring package devel upgrade (no AUR info found):"
msgstr "ignorowanie aktualizacji wersji rozwojowej (brak informacji w AUR):"
#: pkg/text/errors.go:8
msgid "input too long"
msgstr "zbyt długie dane wejściowe"
#: pkg/db/ialpm/alpm.go:222 pkg/dep/dep_graph.go:761
msgid "invalid number: %s"
msgstr "niepoprawna liczba: %s"
#: pkg/settings/parser/parser.go:174
msgid "invalid option '%s'"
msgstr "niepoprawna opcja '%s'"
#: cmd.go:197
msgid "invalid option: '--deps' and '--explicit' may not be used together"
msgstr "niepoprawna opcja: '--deps' i '--explicit' nie mogą być razem używane"
#: pkg/download/abs.go:22
msgid "invalid repository"
msgstr "Nieprawidłowe repozytorium"
#: pkg/db/ialpm/alpm.go:227 pkg/dep/dep_graph.go:767
msgid "invalid value: %d is not between %d and %d"
msgstr "niepoprawna wartość: %d nie jest pomiędzy %d a %d"
#: pkg/text/input.go:48
msgid "no"
msgstr "nie"
#: pkg/sync/srcinfo/pgp/keys.go:110
msgid "no keys to import"
msgstr "brak kluczy do zaimportowania"
#: pkg/query/errors.go:20
msgid "no query was executed"
msgstr "nie uruchomiono żadnego polecenia"
#: local_install.go:66
msgid "no target directories specified"
msgstr "Nie określono folderu docelowego"
#: pkg/sync/build/installer.go:242
msgid "nothing to install for %s"
msgstr "Nic do instalowania dla %s"
#: pkg/settings/parser/parser.go:164
msgid "only one operation may be used at a time"
msgstr "tylko jedna operacja może być użyta na raz"
#: pkg/cmd/graph/main.go:70
msgid "only one target is allowed"
msgstr "dozwolony jest tylko jeden cel"
#: pkg/upgrade/service.go:291
msgid "package"
msgid_plural "packages"
msgstr[0] "pakiet"
msgstr[1] "pakietów"
msgstr[2] "pakietów"
msgstr[3] "pakiety"
#: print.go:187
msgid "package '%s' was not found"
msgstr "pakiet '%s' nie został odnaleziony"
#: pkg/download/errors.go:15
msgid "package not found in AUR"
msgstr "Nie znaleziono pakietu w AUR"
#: pkg/download/abs.go:23
msgid "package not found in repos"
msgstr "Nie znaleziono pakietu w repozytoriach"
#: pkg/sync/srcinfo/pgp/keys.go:100
msgid "problem importing keys"
msgstr "błąd importowania kluczy"
#: clean.go:105
msgid "removing AUR packages from cache..."
msgstr "usuwanie z pamięci podręcznej pakietów z AUR..."
#: clean.go:178 pkg/sync/workdir/clean.go:41
msgid "removing untracked AUR files from cache..."
msgstr "usuwanie z pamięci podręcznej nieśledzonych plików z AUR..."
#: pkg/sync/build/errors.go:38
msgid "the PKGDEST for %s is listed by makepkg but does not exist: %s"
msgstr "PKGDEST dla %s jest wyszczególniony w makepkg, ale nie istnieje: %s"
#: pkg/sync/sync.go:45
msgid "there is nothing to do"
msgstr "Nie ma nic do zrobienia"
#: pkg/db/ialpm/alpm.go:247
msgid "unable to CreateHandle: %s"
msgstr "błąd w CreateHandle: %s"
#: cmd.go:186
msgid "unhandled operation"
msgstr "nieobsłużona operacja"
#: cmd.go:450
msgid "unknown-version"
msgstr "nieznana wersja"
#: pkg/text/input.go:47
msgid "yes"
msgstr "tak"
================================================
FILE: po/pl_PL.po
================================================
#
# Translators:
# J G, 2021
# Oskar , 2022
# A_Salata, 2024
# SMT Broadcast, 2024
# Chair n, 2024
# Paweł Bernaciak, 2024
# Mariusz Bubak, 2024
# Bartłomiej Konecki, 2024
# Tadeusz Magura-Witkowski, 2024
# Tymon Marek, 2025
# iwo wolani, 2025
# Artur Wdowik, 2026
#
msgid ""
msgstr ""
"Last-Translator: Artur Wdowik, 2026\n"
"Language-Team: Polish (https://app.transifex.com/yay-1/teams/123732/pl/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: pl\n"
"Plural-Forms: nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n"
"X-Generator: xgotext\n"
#: clean.go:83
msgid ""
"\n"
"Build directory:"
msgstr ""
"\n"
"Folder kompilacji:"
#: pkg/db/ialpm/alpm.go:201 pkg/dep/dep_graph.go:740
msgid ""
"\n"
"Enter a number (default=1): "
msgstr ""
"\n"
"Wybierz wartość (domyślnie=1): "
#: pkg/menus/menu.go:32
msgid " (Build Files Exist)"
msgstr "(Pliki Budowy Istnieją)"
#: pkg/menus/menu.go:27
msgid " (Installed)"
msgstr " (zainstalowano)"
#: cmd.go:453
msgid " [Installed]"
msgstr " [zainstalowano]"
#: cmd.go:410 vote.go:36
msgid " there is nothing to do"
msgstr " nie ma nic do zrobienia"
#: pkg/menus/menu.go:49
msgid "%s [A]ll [Ab]ort [I]nstalled [No]tInstalled or (1 2 3, 1-3, ^4)"
msgstr ""
"%s [A]Wszystkie [Ab]Anuluj [I]Zainstalowane [No]Nie zainstalowane lub (1 2 "
"3, 1-3, ^4)"
#: pkg/sync/build/installer.go:308
msgid "%s already made -- skipping build"
msgstr "%s już jest zbudowany - pomijam budowanie"
#: pkg/menus/edit_menu.go:57
msgid "%s is not set"
msgstr "%s jest nieokreślony"
#: pkg/settings/exe/cmd_builder.go:257
msgid "%s is present."
msgstr "%s jest już obecny."
#: pkg/dep/dep_graph.go:460 pkg/sync/build/installer.go:305
msgid "%s is up to date -- skipping"
msgstr "%s jest aktualny - pomijam"
#: pkg/upgrade/service.go:291
msgid "%s to upgrade/install."
msgstr "%s do aktualizacji/instalacji."
#: pkg/upgrade/service.go:285
msgid "%s will also be installed for this operation."
msgstr "%s również zostanie zainstalowanie w ramach operacji."
#: pkg/sync/srcinfo/pgp/keys.go:124
msgid "%s, required by: %s"
msgstr "%s, wymagane przez: %s"
#: pkg/menus/diff_menu.go:49
msgid "%s: No changes -- skipping"
msgstr "%s: Bez zmian - pomijam"
#: pkg/query/filter.go:22
msgid "%s: can't use target with option --aur -- skipping"
msgstr "%s: nie jest dostępny z --aur - pomijam"
#: pkg/query/filter.go:17
msgid "%s: can't use target with option --repo -- skipping"
msgstr "%s: nie jest dostępny z --repo - pomijam"
#: pkg/upgrade/sources.go:57
msgid "%s: ignoring package upgrade (%s => %s)"
msgstr "%s: ignoruję aktualizację pakietu (%s => %s)"
#: pkg/query/aur_warnings.go:46
msgid "%s: local (%s) is newer than AUR (%s)"
msgstr "%s: wersja lokalna (%s) jest nowsza niż ta z AUR (%s)"
#: vote.go:51
msgid ""
"%s: please set AUR_USERNAME and AUR_PASSWORD environment variables for "
"voting"
msgstr ""
"%s: wprowadź proszę zmienne środowiskowe AUR_USERNAME oraz AUR_PASSWORD by "
"móc głosować"
#: pkg/download/unified.go:192
msgid "(%d/%d) Downloaded PKGBUILD from ABS: %s"
msgstr "(%d/%d) Pobrano PKGBUILD z ABS-u: %s"
#: pkg/download/aur.go:92 pkg/download/unified.go:188
msgid "(%d/%d) Downloaded PKGBUILD: %s"
msgstr "(%d/%d) Pobrany PKGBUILD: %s"
#: pkg/download/aur.go:82
msgid "(%d/%d) Failed to download PKGBUILD: %s"
msgstr "(%d/%d) Nie udało się pobrać PKGBUILD: %s"
#: pkg/sync/srcinfo/service.go:109
msgid "(%d/%d) Parsing SRCINFO: %s"
msgstr "(%d/%d) Przetwarzam SRCINFO: %s"
#: pkg/query/types.go:103 pkg/query/types.go:72
msgid "(Installed)"
msgstr "(Zainstalowana)"
#: pkg/query/types.go:101 pkg/query/types.go:70
msgid "(Installed: %s)"
msgstr "(Zainstalowano: %s)"
#: pkg/query/types.go:61
msgid "(Orphaned)"
msgstr "(Osierocona)"
#: pkg/query/types.go:65
msgid "(Out-of-date: %s)"
msgstr "(Nieaktualny od: %s)"
#: pkg/dep/dep_graph.go:75
msgid "AUR"
msgstr "AUR"
#: print.go:44
msgid "AUR URL"
msgstr "AUR URL"
#: pkg/menus/edit_menu.go:58
msgid "Add %s or %s to your environment variables"
msgstr "Dodaj %s lub %s do zmiennych środowiskowych"
#: main.go:60
msgid "Avoid running yay as root/sudo."
msgstr "Unikaj uruchamiania yay jako root lub z sudo."
#: pkg/dep/dep_graph.go:63
msgid "Check Dependency"
msgstr "Sprawdzanie Zależności"
#: print.go:41
msgid "Check Deps"
msgstr "Sprawdź Zależności"
#: pkg/upgrade/service.go:89
msgid "Checking development packages..."
msgstr "Sprawdzanie pakietów w wersjach rozwojowych..."
#: pkg/sync/workdir/clean.go:45
msgid "Cleaning (%d/%d): %s"
msgstr "Czyszczenie (%d/%d): %s"
#: print.go:42
msgid "Conflicts With"
msgstr "Koliduje z"
#: pkg/menus/clean_menu.go:62
msgid "Deleting (%d/%d): %s"
msgstr "Usuwanie (%d/%d): %s"
#: pkg/dep/dep_graph.go:61
msgid "Dependency"
msgstr "Zależność"
#: print.go:38
msgid "Depends On"
msgstr "Zależy od"
#: print.go:33
msgid "Description"
msgstr "Opis"
#: pkg/menus/diff_menu.go:160
msgid "Diffs to show?"
msgstr "Pokazać różnice?"
#: pkg/settings/migrations.go:25
msgid "Disable 'provides' setting by default"
msgstr "Domyślnie wyłącza ustawienie 'provides' "
#: clean.go:78
msgid "Do you want to remove ALL AUR packages from cache?"
msgstr "Czy chcesz usunąć WSZYSTKIE pakiety AUR z pamięci podręcznej?"
#: clean.go:95
msgid "Do you want to remove ALL untracked AUR files?"
msgstr "Czy chcesz usunąć WSZYSTKIE nieśledzone pliki AUR?"
#: clean.go:80
msgid "Do you want to remove all other AUR packages from cache?"
msgstr "Czy chcesz usunąć wszystkie inne pakiety AUR z pamięci podręczniej?"
#: pkg/menus/edit_menu.go:61
msgid "Edit PKGBUILD with?"
msgstr "Edytor PKGBUILD-ów?"
#: pkg/query/errors.go:13
msgid "Error during AUR search: %s\n"
msgstr "Błąd podczas wyszukiwania w AUR: %s\n"
#: pkg/upgrade/service.go:295
msgid "Excluding packages may cause partial upgrades and break systems"
msgstr ""
"Wykluczanie pakietów może spowodować częściową aktualizacje i uszkodzenie "
"systemu"
#: pkg/dep/dep_graph.go:60
msgid "Explicit"
msgstr "Jawne"
#: print.go:91
msgid "Explicitly installed packages: %s"
msgstr "Jawnie zainstalowane pakiety: %s"
#: pkg/dep/dep_graph.go:437 pkg/dep/dep_graph.go:535
msgid "Failed to find AUR package for"
msgstr "Nie znaleziono pakietu AUR dla"
#: pkg/sync/build/installer.go:120
msgid "Failed to install layer, rolling up to next layer."
msgstr "Niepowodzenie zainstalowania warstwy, przewijanie do następnej warstwy"
#: pkg/sync/build/errors.go:16
msgid ""
"Failed to install the following packages. Manual intervention is required:"
msgstr "Nie udało sie zainstalować następujących pakietów. Wymagana interwencja "
"użytkownika:"
#: print.go:45
msgid "First Submitted"
msgstr "Zamieszczono po raz pierwszy"
#: pkg/query/aur_warnings.go:83
msgid "Flagged Out Of Date AUR Packages:"
msgstr "Pakiety AUR oznaczone jako nieaktualne:"
#: print.go:90
msgid "Foreign installed packages: %s"
msgstr "Obce zainstalowane pakiety: %s"
#: pkg/vcs/vcs.go:144
msgid "Found git repo: %s"
msgstr "Znaleziono repozytorium git: %s"
#: vcs.go:72
msgid "GenDB finished. No packages were installed"
msgstr "Zakończono GenDB. Nie zainstalowano żadnych pakietów"
#: print.go:36
msgid "Groups"
msgstr "Grupy"
#: pkg/sync/srcinfo/pgp/keys.go:88
msgid "Import?"
msgstr "Zaimportować?"
#: pkg/sync/srcinfo/pgp/keys.go:97
msgid "Importing keys with gpg..."
msgstr "Importowanie kluczy przy użyciu gpg..."
#: print.go:46
msgid "Keywords"
msgstr "Słowa kluczowe"
#: print.go:47
msgid "Last Modified"
msgstr "Ostatnio zmodyfikowano"
#: print.go:35
msgid "Licenses"
msgstr "Licencje"
#: pkg/dep/dep_graph.go:77
msgid "Local"
msgstr "Lokalne"
#: print.go:48
msgid "Maintainer"
msgstr "Opiekun"
#: pkg/dep/dep_graph.go:62
msgid "Make Dependency"
msgstr "Zależność Budowania"
#: print.go:40
msgid "Make Deps"
msgstr "Zbuduj zależności"
#: pkg/dep/dep_graph.go:79
msgid "Missing"
msgstr "Zaginiony"
#: pkg/query/aur_warnings.go:75
msgid "Missing AUR Debug Packages:"
msgstr "Brakujące Pakiety Debugowania AUR:"
#: print.go:31
msgid "Name"
msgstr "Nazwa"
#: pkg/dep/dep_graph.go:442 pkg/dep/dep_graph.go:548
msgid "No AUR package found for"
msgstr "Nie znaleziono pakietu AUR dla"
#: pkg/dep/dep_graph.go:182
msgid "No package found for"
msgstr "Nie znaleziono pakietu dla"
#: print.go:225
msgid "None"
msgstr "Brak"
#: print.go:39
msgid "Optional Deps"
msgstr "Opcjonalne zależności"
#: pkg/query/aur_warnings.go:79
msgid "Orphan (unmaintained) AUR Packages:"
msgstr "Osierocone (nieutrzymywane) pakiety AUR:"
#: print.go:53 print.go:55
msgid "Out-of-date"
msgstr "Nieaktualne"
#: pkg/sync/srcinfo/pgp/keys.go:115
msgid "PGP keys need importing:"
msgstr "Klucze PGP wymagające zaimportowania:"
#: pkg/sync/workdir/preparer.go:252
msgid "PKGBUILD up to date, skipping download: %s"
msgstr "PKGBUILD jest aktualny, pomijam pobieranie: %s"
#: pkg/menus/edit_menu.go:130
msgid "PKGBUILDs to edit?"
msgstr "Edytować PKGBUILD-y?"
#: print.go:61
msgid "Package Base"
msgstr "Podstawa Pakietu"
#: print.go:60
msgid "Package Base ID"
msgstr "ID Podstawy Pakietu"
#: pkg/query/aur_warnings.go:71
msgid "Packages not in AUR:"
msgstr "Pakiety nie występujace w AUR:"
#: pkg/menus/clean_menu.go:54
msgid "Packages to cleanBuild?"
msgstr "Pakiety do zbudowania od zera?"
#: pkg/dep/dep_graph.go:202
msgid "Packages to exclude"
msgstr "Pakiety do wykluczenia"
#: pkg/upgrade/service.go:294
msgid "Packages to exclude: (eg: \"1 2 3\", \"1-3\", \"^4\" or repo name)"
msgstr "Wykluczone pakiety: (np.: \"1 2 3\", \"1-3\", \"^4\" lub nazwa repozytorium)"
#: cmd.go:392
msgid "Packages to install (eg: 1 2 3, 1-3 or ^4)"
msgstr "Pakiety do zainstalowania (np.: 1 2 3, 1-3 or ^4)"
#: print.go:49
msgid "Popularity"
msgstr "Popularność"
#: pkg/menus/diff_menu.go:172 pkg/menus/edit_menu.go:143
msgid "Proceed with install?"
msgstr "Kontynuować instalację?"
#: print.go:37
msgid "Provides"
msgstr "Dostarcza"
#: pkg/sync/workdir/preparer.go:125
msgid "Remove make dependencies after install?"
msgstr "Usunąć zależności potrzebne do zbudowania po instalacji?"
#: print.go:43
msgid "Replaces"
msgstr "Zastępuje"
#: pkg/db/ialpm/alpm.go:191 print.go:30
msgid "Repository"
msgstr "Repozytorium"
#: pkg/dep/dep_graph.go:730
msgid "Repository AUR"
msgstr "Repozytorium AUR"
#: pkg/dep/dep_graph.go:78
msgid "SRCINFO"
msgstr "SRCINFO"
#: pkg/upgrade/service.go:71
msgid "Searching AUR for updates..."
msgstr "Poszukuję aktualizacji w AUR..."
#: pkg/upgrade/service.go:159
msgid "Searching databases for updates..."
msgstr "Poszukuję aktualizacji w bazie danych..."
#: pkg/query/query_builder.go:214
msgid "Showing repo packages only"
msgstr "Pokazuję tylko pakiety z repozytoriów"
#: print.go:95
msgid "Size of pacman cache %s: %s"
msgstr "Rozmiar pamięci podręcznej Pacmana %s: %s"
#: print.go:98
msgid "Size of yay cache %s: %s"
msgstr "Rozmiar pamięci podręcznej yay %s: %s"
#: print.go:62
msgid "Snapshot URL"
msgstr "URL Snapshota"
#: pkg/dep/dep_graph.go:76
msgid "Sync"
msgstr "Synchronizacja"
#: print.go:100
msgid "Ten biggest packages:"
msgstr "Dziesięć największych pakietów:"
#: pkg/sync/sync.go:124
msgid "The following packages are not compatible with your architecture:"
msgstr "Te pakiety nie są kompatybilne z architekturą Twojego systemu:"
#: pkg/db/ialpm/alpm.go:179 pkg/dep/dep_graph.go:726
msgid "There are %[1]d providers available for %[2]s:"
msgstr "Jest %d możliwych źródeł dla %s:"
#: pkg/settings/exe/cmd_builder.go:258
msgid "There may be another Pacman instance running. Waiting..."
msgstr "Prawdopodobnie działa teraz inna instancja Pacmana. Czekam..."
#: print.go:92
msgid "Total Size occupied by packages: %s"
msgstr "Łączny rozmiar zajmowany przez pakiety: %s"
#: print.go:89
msgid "Total installed packages: %s"
msgstr "Łączna liczba zainstalowanych pakietów: %s"
#: pkg/sync/sync.go:132
msgid "Try to build them anyway?"
msgstr "Spróbować je zbudować mimo wszystko?"
#: print.go:34
msgid "URL"
msgstr "URL"
#: clean.go:194 pkg/menus/clean_menu.go:65 pkg/menus/clean_menu.go:71
msgid "Unable to clean:"
msgstr "Nie można sprzątnąć:"
#: get.go:42 get.go:74
msgid "Unable to find the following packages:"
msgstr "Nie udało się znaleźć następujących pakietów:"
#: vote.go:20
msgid "Unable to handle package vote for: %s. err: %s"
msgstr "Nie udało się obsłużyć oceny pakietu dla: %s. err: %s"
#: clean.go:170
msgid "Unable to remove %s: %s"
msgstr "Nie udało się usunąć %s: %s"
#: print.go:32
msgid "Version"
msgstr "Wersja"
#: print.go:50
msgid "Votes"
msgstr "Głosów"
#: print.go:87
msgid "Yay version v%s"
msgstr "Wersja yay v%s"
#: pkg/menus/menu.go:49
msgid "[N]one"
msgstr "[N]ic"
#: pkg/settings/errors.go:29
msgid "aborting due to user"
msgstr "przerwane przez użytkownika"
#: pkg/settings/parser/parser.go:608
msgid "argument '-' specified without input on stdin"
msgstr "argument '-' określony bez danych wejściowych"
#: local_install.go:26
msgid "cannot find PKGBUILD and .SRCINFO in directory"
msgstr "nie znaleziono PKGBUILD i .SRCINFO w katalogu"
#: pkg/sync/build/pkg_archive.go:148
msgid "cannot find package name: %v"
msgstr "nie odnalazłem pakietu: %v"
#: pkg/sync/build/errors.go:30
msgid "could not find PKGDEST for: %s"
msgstr "nie odnalazłem PKGDEST dla: %s"
#: errors.go:9
msgid "could not find all required packages"
msgstr "nie znaleziono wszystkich wymaganych pakietów"
#: pkg/sync/build/errors.go:61
msgid "could not find any package archives listed in %s"
msgstr "nie udało się znaleźć żadnych archiwów pakietów z listy %s"
#: pkg/sync/build/errors.go:50 pkg/upgrade/service.go:286
msgid "dependency"
msgstr "zależność"
#: pkg/vcs/vcs.go:100 pkg/vcs/vcs.go:96
msgid "devel check for package failed: '%s' encountered an error"
msgstr "błąd sprawdzania wersji rozwojowej: '%s' napotkało na błąd"
#: pkg/menus/edit_menu.go:110
msgid "editor did not exit successfully, aborting: %s"
msgstr "edytor nie zamknął się poprawnie, anuluję: %s"
#: pkg/sync/workdir/aur_source.go:24
msgid "error downloading sources: %s"
msgstr "błąd ściągania źródeł: %s"
#: pkg/download/errors.go:25
msgid "error fetching %s: %s"
msgstr "błąd podczas ściągania %s: %s"
#: pkg/sync/build/errors.go:9
msgid "error installing repo packages"
msgstr "błąd podczas instalowania pakietów z repozytorium"
#: pkg/sync/build/installer.go:266 pkg/sync/build/installer.go:270
msgid "error installing:"
msgstr "Błąd podczas instalacji:"
#: pkg/sync/build/installer.go:233 pkg/sync/build/installer.go:237
msgid "error making: %s"
msgstr "błąd podczas budowania: %s"
#: pkg/sync/workdir/merge.go:24
msgid "error merging %s: %s"
msgstr "błąd podczas scalania %s: %s"
#: pkg/download/unified.go:59
msgid "error reading %s"
msgstr "błąd podczas odczytywania %s"
#: sync.go:36
msgid "error refreshing databases"
msgstr "błąd odświeżania baz danych"
#: pkg/sync/workdir/clean.go:51 pkg/sync/workdir/merge.go:17
msgid "error resetting %s: %s"
msgstr "błąd podczas resetowania %s: %s"
#: pkg/sync/build/errors.go:53
msgid "error updating package install reason to %s"
msgstr "błąd aktualizacji pakietu z przyczyn %s"
#: pkg/sync/build/errors.go:48
msgid "explicit"
msgstr "jawne"
#: pkg/settings/errors.go:23
msgid "failed to create directory '%s': %s"
msgstr "nie udało się stworzyć katalogu '%s': %s"
#: pkg/settings/config.go:281
msgid "failed to open config file '%s': %s"
msgstr "nie udało się otworzyć pliku konfiguracyjnego '%s': %s"
#: pkg/sync/srcinfo/service.go:114
msgid "failed to parse %s -- skipping: %s"
msgstr "nie udało się przetworzyć %s - pomijam: %s"
#: pkg/sync/srcinfo/service.go:118
msgid "failed to parse %s: %s"
msgstr "nie udało się przetworzyć %s: %s"
#: local_install.go:77
msgid "failed to parse .SRCINFO"
msgstr "nie udało się przetworzyć .SRCINFO"
#: pkg/settings/config.go:291
msgid "failed to read config file '%s': %s"
msgstr "nie udało się odczytać pliku konfiguracyjnego '%s': %s"
#: pkg/cmd/graph/main.go:46 pkg/runtime/runtime.go:73
msgid "failed to retrieve aur Cache"
msgstr "Nie udało się pozyskać pamięci podręcznej AUR"
#: pkg/upgrade/sources.go:27
msgid "ignoring package devel upgrade (no AUR info found):"
msgstr "ignorowanie aktualizacji wersji rozwojowej (brak informacji w AUR):"
#: pkg/text/errors.go:8
msgid "input too long"
msgstr "zbyt długie dane wejściowe"
#: pkg/db/ialpm/alpm.go:222 pkg/dep/dep_graph.go:761
msgid "invalid number: %s"
msgstr "niepoprawna liczba: %s"
#: pkg/settings/parser/parser.go:174
msgid "invalid option '%s'"
msgstr "niepoprawna opcja '%s'"
#: cmd.go:197
msgid "invalid option: '--deps' and '--explicit' may not be used together"
msgstr "niepoprawna opcja: '--deps' i '--explicit' nie mogą być razem używane"
#: pkg/download/abs.go:22
msgid "invalid repository"
msgstr "Nieprawidłowe repozytorium"
#: pkg/db/ialpm/alpm.go:227 pkg/dep/dep_graph.go:767
msgid "invalid value: %d is not between %d and %d"
msgstr "niepoprawna wartość: %d nie jest pomiędzy %d a %d"
#: pkg/text/input.go:48
msgid "no"
msgstr "nie"
#: pkg/sync/srcinfo/pgp/keys.go:110
msgid "no keys to import"
msgstr "brak kluczy do zaimportowania"
#: pkg/query/errors.go:20
msgid "no query was executed"
msgstr "nie uruchomiono żadnego polecenia"
#: local_install.go:66
msgid "no target directories specified"
msgstr "Nie określono folderu docelowego"
#: pkg/sync/build/installer.go:242
msgid "nothing to install for %s"
msgstr "Nic do instalowania dla %s"
#: pkg/settings/parser/parser.go:164
msgid "only one operation may be used at a time"
msgstr "tylko jedna operacja może być użyta na raz"
#: pkg/cmd/graph/main.go:70
msgid "only one target is allowed"
msgstr "dozwolony jest tylko jeden cel"
#: pkg/upgrade/service.go:291
msgid "package"
msgid_plural "packages"
msgstr[0] "pakiet"
msgstr[1] "pakietów"
msgstr[2] "pakietów"
msgstr[3] "pakiety"
#: print.go:187
msgid "package '%s' was not found"
msgstr "pakiet '%s' nie został odnaleziony"
#: pkg/download/errors.go:15
msgid "package not found in AUR"
msgstr "Nie znaleziono pakietu w AUR"
#: pkg/download/abs.go:23
msgid "package not found in repos"
msgstr "Nie znaleziono pakietu w repozytoriach"
#: pkg/sync/srcinfo/pgp/keys.go:100
msgid "problem importing keys"
msgstr "błąd importowania kluczy"
#: clean.go:105
msgid "removing AUR packages from cache..."
msgstr "usuwanie z pamięci podręcznej pakietów z AUR..."
#: clean.go:178 pkg/sync/workdir/clean.go:41
msgid "removing untracked AUR files from cache..."
msgstr "usuwanie z pamięci podręcznej nieśledzonych plików z AUR..."
#: pkg/sync/build/errors.go:38
msgid "the PKGDEST for %s is listed by makepkg but does not exist: %s"
msgstr "PKGDEST dla %s jest wyszczególniony w makepkg, ale nie istnieje: %s"
#: pkg/sync/sync.go:45
msgid "there is nothing to do"
msgstr "Nie ma nic do zrobienia"
#: pkg/db/ialpm/alpm.go:247
msgid "unable to CreateHandle: %s"
msgstr "błąd w CreateHandle: %s"
#: cmd.go:186
msgid "unhandled operation"
msgstr "nieobsłużona operacja"
#: cmd.go:450
msgid "unknown-version"
msgstr "nieznana wersja"
#: pkg/text/input.go:47
msgid "yes"
msgstr "tak"
================================================
FILE: po/pt.po
================================================
#
# Translators:
# J G, 2021
# Eduardo Ervideira, 2023
# Hugo Carvalho , 2023
# Matheus Calegaro , 2024
#
msgid ""
msgstr ""
"Last-Translator: Matheus Calegaro , 2024\n"
"Language-Team: Portuguese (https://app.transifex.com/yay-1/teams/123732/pt/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: pt\n"
"Plural-Forms: nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n"
"X-Generator: xgotext\n"
#: clean.go:83
msgid ""
"\n"
"Build directory:"
msgstr ""
"\n"
"Diretório de compilação:"
#: pkg/db/ialpm/alpm.go:201 pkg/dep/dep_graph.go:740
msgid ""
"\n"
"Enter a number (default=1): "
msgstr ""
"\n"
"Introduza um número (por padrão=1): "
#: pkg/menus/menu.go:32
msgid " (Build Files Exist)"
msgstr " (Existem ficheiros de compilação)"
#: pkg/menus/menu.go:27
msgid " (Installed)"
msgstr " (Instalado)"
#: cmd.go:453
msgid " [Installed]"
msgstr " [Instalado]"
#: cmd.go:410 vote.go:36
msgid " there is nothing to do"
msgstr " não há nada a fazer"
#: pkg/menus/menu.go:49
msgid "%s [A]ll [Ab]ort [I]nstalled [No]tInstalled or (1 2 3, 1-3, ^4)"
msgstr ""
"%s [A]Todos [Ab]ortar [I]nstalado [No]Não instalado or (1 2 3, 1-3, ^4)"
#: pkg/sync/build/installer.go:308
msgid "%s already made -- skipping build"
msgstr "%s já efetuado -- a ignorar compilação"
#: pkg/menus/edit_menu.go:57
msgid "%s is not set"
msgstr "%s não está definido"
#: pkg/settings/exe/cmd_builder.go:257
msgid "%s is present."
msgstr "%s está presente."
#: pkg/dep/dep_graph.go:460 pkg/sync/build/installer.go:305
msgid "%s is up to date -- skipping"
msgstr "%s está atualizado -- a ignorar"
#: pkg/upgrade/service.go:291
msgid "%s to upgrade/install."
msgstr "%s a atualizar/instalar."
#: pkg/upgrade/service.go:285
msgid "%s will also be installed for this operation."
msgstr "%s também será instalado para esta operação."
#: pkg/sync/srcinfo/pgp/keys.go:124
msgid "%s, required by: %s"
msgstr "%s, necessário para: %s"
#: pkg/menus/diff_menu.go:49
msgid "%s: No changes -- skipping"
msgstr "%s: Sem modificações -- a ignorar"
#: pkg/query/filter.go:22
msgid "%s: can't use target with option --aur -- skipping"
msgstr ""
"%s: não é possível utilizar a opção --aur com este pacote -- a ignorar"
#: pkg/query/filter.go:17
msgid "%s: can't use target with option --repo -- skipping"
msgstr ""
"%s: não é possível utilizar a opção --repo com este pacote -- a ignorar"
#: pkg/upgrade/sources.go:57
msgid "%s: ignoring package upgrade (%s => %s)"
msgstr "%s: a ignorar atualização de pacote (%s => %s)"
#: pkg/query/aur_warnings.go:46
msgid "%s: local (%s) is newer than AUR (%s)"
msgstr "%s: local (%s) é mais recente que AUR (%s)"
#: vote.go:51
msgid ""
"%s: please set AUR_USERNAME and AUR_PASSWORD environment variables for "
"voting"
msgstr ""
"%s: definir variáveis de ambiente AUR_USERNAME e AUR_PASSWORD para votação"
#: pkg/download/unified.go:192
msgid "(%d/%d) Downloaded PKGBUILD from ABS: %s"
msgstr "(%d/%d) PKGBUILD ABS transferido: %s"
#: pkg/download/aur.go:92 pkg/download/unified.go:188
msgid "(%d/%d) Downloaded PKGBUILD: %s"
msgstr "(%d/%d) PKGBUILD transferido: %s"
#: pkg/download/aur.go:82
msgid "(%d/%d) Failed to download PKGBUILD: %s"
msgstr "(%d/%d) Falha ao transferir PKGBUILD: %s"
#: pkg/sync/srcinfo/service.go:109
msgid "(%d/%d) Parsing SRCINFO: %s"
msgstr "(%d/%d) Analisando SRCINFO: %s"
#: pkg/query/types.go:103 pkg/query/types.go:72
msgid "(Installed)"
msgstr "(Instalado)"
#: pkg/query/types.go:101 pkg/query/types.go:70
msgid "(Installed: %s)"
msgstr "(Instalado: %s)"
#: pkg/query/types.go:61
msgid "(Orphaned)"
msgstr "(Orfão)"
#: pkg/query/types.go:65
msgid "(Out-of-date: %s)"
msgstr "(Desatualizado: %s)"
#: pkg/dep/dep_graph.go:75
msgid "AUR"
msgstr "AUR"
#: print.go:44
msgid "AUR URL"
msgstr "URL AUR"
#: pkg/menus/edit_menu.go:58
msgid "Add %s or %s to your environment variables"
msgstr "Adicione %s ou %s às suas variáveis de ambiente"
#: main.go:60
msgid "Avoid running yay as root/sudo."
msgstr "Evite executar o yay como root/sudo."
#: pkg/dep/dep_graph.go:63
msgid "Check Dependency"
msgstr "Verificar dependências"
#: print.go:41
msgid "Check Deps"
msgstr "Dependências de verificação"
#: pkg/upgrade/service.go:89
msgid "Checking development packages..."
msgstr "A verificar pacotes de desenvolvimento..."
#: pkg/sync/workdir/clean.go:45
msgid "Cleaning (%d/%d): %s"
msgstr "A limpar (%d/%d): %s"
#: print.go:42
msgid "Conflicts With"
msgstr "Em conflito com"
#: pkg/menus/clean_menu.go:62
msgid "Deleting (%d/%d): %s"
msgstr "A eliminar (%d/%d): %s"
#: pkg/dep/dep_graph.go:61
msgid "Dependency"
msgstr "Dependências"
#: print.go:38
msgid "Depends On"
msgstr "Depende de"
#: print.go:33
msgid "Description"
msgstr "Descrição"
#: pkg/menus/diff_menu.go:160
msgid "Diffs to show?"
msgstr "Diffs a mostrar?"
#: pkg/settings/migrations.go:25
msgid "Disable 'provides' setting by default"
msgstr "Desativar a definição 'fornece' por defeito"
#: clean.go:78
msgid "Do you want to remove ALL AUR packages from cache?"
msgstr "Quer remover todos os pacotes AUR da cache?"
#: clean.go:95
msgid "Do you want to remove ALL untracked AUR files?"
msgstr "Quer remover todos os ficheiros AUR não rastreados?"
#: clean.go:80
msgid "Do you want to remove all other AUR packages from cache?"
msgstr "Quer remover todos os outros pacotes AUR da cache?"
#: pkg/menus/edit_menu.go:61
msgid "Edit PKGBUILD with?"
msgstr "Editar PKGBUILD com?"
#: pkg/query/errors.go:13
msgid "Error during AUR search: %s\n"
msgstr "Erro durante a pesquisa AUR: %s\n"
#: pkg/upgrade/service.go:295
msgid "Excluding packages may cause partial upgrades and break systems"
msgstr ""
"A exclusão de pacotes pode causar atualizações parciais e quebra de sistemas"
#: pkg/dep/dep_graph.go:60
msgid "Explicit"
msgstr "Explícito"
#: print.go:91
msgid "Explicitly installed packages: %s"
msgstr "Pacotes explicitamente instalados: %s"
#: pkg/dep/dep_graph.go:437 pkg/dep/dep_graph.go:535
msgid "Failed to find AUR package for"
msgstr "Falha ao localizar pacote AUR para"
#: pkg/sync/build/installer.go:120
msgid "Failed to install layer, rolling up to next layer."
msgstr "Falha ao instalar camada, indo até à camada seguinte."
#: pkg/sync/build/errors.go:16
msgid ""
"Failed to install the following packages. Manual intervention is required:"
msgstr ""
"Falha ao instalar os seguintes pacotes. É necessária a intervenção manual:"
#: print.go:45
msgid "First Submitted"
msgstr "Primeira submissão"
#: pkg/query/aur_warnings.go:83
msgid "Flagged Out Of Date AUR Packages:"
msgstr "Pacotes AUR marcados como desatualizados:"
#: print.go:90
msgid "Foreign installed packages: %s"
msgstr "Pacotes externos instalados: %s"
#: pkg/vcs/vcs.go:144
msgid "Found git repo: %s"
msgstr "Repositório git encontrado: %s"
#: vcs.go:72
msgid "GenDB finished. No packages were installed"
msgstr "GenDB terminado. Nenhum pacote foi instalado"
#: print.go:36
msgid "Groups"
msgstr "Grupos"
#: pkg/sync/srcinfo/pgp/keys.go:88
msgid "Import?"
msgstr "Importar?"
#: pkg/sync/srcinfo/pgp/keys.go:97
msgid "Importing keys with gpg..."
msgstr "A importar chaves com gpg..."
#: print.go:46
msgid "Keywords"
msgstr "Palavras-chave"
#: print.go:47
msgid "Last Modified"
msgstr "Última Modificação"
#: print.go:35
msgid "Licenses"
msgstr "Licenças"
#: pkg/dep/dep_graph.go:77
msgid "Local"
msgstr "Local"
#: print.go:48
msgid "Maintainer"
msgstr "Responsável pela manutenção"
#: pkg/dep/dep_graph.go:62
msgid "Make Dependency"
msgstr "Criar dependências"
#: print.go:40
msgid "Make Deps"
msgstr "Dependências Make"
#: pkg/dep/dep_graph.go:79
msgid "Missing"
msgstr "Em falta"
#: pkg/query/aur_warnings.go:75
msgid "Missing AUR Debug Packages:"
msgstr "Pacotes de depuração AUR em falta:"
#: print.go:31
msgid "Name"
msgstr "Nome"
#: pkg/dep/dep_graph.go:442 pkg/dep/dep_graph.go:548
msgid "No AUR package found for"
msgstr "Nenhum pacote AUR encontrado para"
#: pkg/dep/dep_graph.go:182
msgid "No package found for"
msgstr "Nenhum pacote encontrado para"
#: print.go:225
msgid "None"
msgstr "Nenhum"
#: print.go:39
msgid "Optional Deps"
msgstr "Dependências opcionais"
#: pkg/query/aur_warnings.go:79
msgid "Orphan (unmaintained) AUR Packages:"
msgstr "Pacotes AUR órfãos (não mantidos):"
#: print.go:53 print.go:55
msgid "Out-of-date"
msgstr "Desatualizado"
#: pkg/sync/srcinfo/pgp/keys.go:115
msgid "PGP keys need importing:"
msgstr "Chaves PGP a importar:"
#: pkg/sync/workdir/preparer.go:252
msgid "PKGBUILD up to date, skipping download: %s"
msgstr "PKGBUILD atualizado, a ignorar a transferência: %s"
#: pkg/menus/edit_menu.go:130
msgid "PKGBUILDs to edit?"
msgstr "PKGBUILDs a editar?"
#: print.go:61
msgid "Package Base"
msgstr "Pacote Base"
#: print.go:60
msgid "Package Base ID"
msgstr "ID do Pacote Base"
#: pkg/query/aur_warnings.go:71
msgid "Packages not in AUR:"
msgstr "Pacotes que não estão no AUR:"
#: pkg/menus/clean_menu.go:54
msgid "Packages to cleanBuild?"
msgstr "Pacotes a compilar a limpo?"
#: pkg/dep/dep_graph.go:202
msgid "Packages to exclude"
msgstr "Pacotes a excluir"
#: pkg/upgrade/service.go:294
msgid "Packages to exclude: (eg: \"1 2 3\", \"1-3\", \"^4\" or repo name)"
msgstr "Pacotes a excluir: (eg: \"1 2 3\", \"1-3\", \"^4\" or repo name)"
#: cmd.go:392
msgid "Packages to install (eg: 1 2 3, 1-3 or ^4)"
msgstr "Pacotes a instalar (eg: 1 2 3, 1-3 or ^4)"
#: print.go:49
msgid "Popularity"
msgstr "Popularidade"
#: pkg/menus/diff_menu.go:172 pkg/menus/edit_menu.go:143
msgid "Proceed with install?"
msgstr "Proceder com a instalação?"
#: print.go:37
msgid "Provides"
msgstr "Fornece"
#: pkg/sync/workdir/preparer.go:125
msgid "Remove make dependencies after install?"
msgstr "Remover as dependências de make pós-instalação?"
#: print.go:43
msgid "Replaces"
msgstr "Substitui"
#: pkg/db/ialpm/alpm.go:191 print.go:30
msgid "Repository"
msgstr "Repositório"
#: pkg/dep/dep_graph.go:730
msgid "Repository AUR"
msgstr "Repositório AUR"
#: pkg/dep/dep_graph.go:78
msgid "SRCINFO"
msgstr "SRCINFO"
#: pkg/upgrade/service.go:71
msgid "Searching AUR for updates..."
msgstr "A procurar atualizações na AUR..."
#: pkg/upgrade/service.go:159
msgid "Searching databases for updates..."
msgstr "A procurar atualizações nos repositórios..."
#: pkg/query/query_builder.go:214
msgid "Showing repo packages only"
msgstr "Mostrando apenas pacotes do repositório"
#: print.go:95
msgid "Size of pacman cache %s: %s"
msgstr "Tamanho da cache do pacman %s: %s"
#: print.go:98
msgid "Size of yay cache %s: %s"
msgstr "Tamanho da cache do yay %s: %s"
#: print.go:62
msgid "Snapshot URL"
msgstr "URL Snapshot"
#: pkg/dep/dep_graph.go:76
msgid "Sync"
msgstr "Sincronizar"
#: print.go:100
msgid "Ten biggest packages:"
msgstr "Dez maiores pacotes:"
#: pkg/sync/sync.go:124
msgid "The following packages are not compatible with your architecture:"
msgstr "Os seguintes pacotes não são compatíveis com a sua arquitetura:"
#: pkg/db/ialpm/alpm.go:179 pkg/dep/dep_graph.go:726
msgid "There are %[1]d providers available for %[2]s:"
msgstr "Existem %[1]d provedores disponíveis para %[2]s:"
#: pkg/settings/exe/cmd_builder.go:258
msgid "There may be another Pacman instance running. Waiting..."
msgstr "Poderá haver outro Pacman em execução. Aguardar..."
#: print.go:92
msgid "Total Size occupied by packages: %s"
msgstr "Tamanho total ocupado por pacotes: %s"
#: print.go:89
msgid "Total installed packages: %s"
msgstr "Total instalado de pacotes: %s"
#: pkg/sync/sync.go:132
msgid "Try to build them anyway?"
msgstr "Tentar compilar mesmo assim?"
#: print.go:34
msgid "URL"
msgstr "URL"
#: clean.go:194 pkg/menus/clean_menu.go:65 pkg/menus/clean_menu.go:71
msgid "Unable to clean:"
msgstr "Não foi possível limpar:"
#: get.go:42 get.go:74
msgid "Unable to find the following packages:"
msgstr "Não foi possível encontrar os seguintes pacotes:"
#: vote.go:20
msgid "Unable to handle package vote for: %s. err: %s"
msgstr "Não foi possível gerir votação de pacote para: %s err: %s"
#: clean.go:170
msgid "Unable to remove %s: %s"
msgstr "Não foi possível remover %s: %s"
#: print.go:32
msgid "Version"
msgstr "Versão"
#: print.go:50
msgid "Votes"
msgstr "Votos"
#: print.go:87
msgid "Yay version v%s"
msgstr "Versão Yay v%s"
#: pkg/menus/menu.go:49
msgid "[N]one"
msgstr "[N]enhum"
#: pkg/settings/errors.go:29
msgid "aborting due to user"
msgstr "a abortar por opção do utilizador"
#: pkg/settings/parser/parser.go:608
msgid "argument '-' specified without input on stdin"
msgstr "argumento '-' especificado sem entrada em stdin"
#: local_install.go:26
msgid "cannot find PKGBUILD and .SRCINFO in directory"
msgstr "não foi possível localizar PKGBUILD e .SRCINFO no diretório"
#: pkg/sync/build/pkg_archive.go:148
msgid "cannot find package name: %v"
msgstr "nome de pacote não encontrado: %v"
#: pkg/sync/build/errors.go:30
msgid "could not find PKGDEST for: %s"
msgstr "não encontrado PKGDEST para: %s"
#: errors.go:9
msgid "could not find all required packages"
msgstr "não foi possível localizar todos os pacotes necessários"
#: pkg/sync/build/errors.go:61
msgid "could not find any package archives listed in %s"
msgstr "não foi possível localizar nenhum arquivo de pacotes listados em %s"
#: pkg/sync/build/errors.go:50 pkg/upgrade/service.go:286
msgid "dependency"
msgstr "dependência"
#: pkg/vcs/vcs.go:100 pkg/vcs/vcs.go:96
msgid "devel check for package failed: '%s' encountered an error"
msgstr ""
"falha na pesquisa por pacotes de desenvolvimento: '%s' encontrou um erro"
#: pkg/menus/edit_menu.go:110
msgid "editor did not exit successfully, aborting: %s"
msgstr "editor não terminou com sucesso, abortando: %s"
#: pkg/sync/workdir/aur_source.go:24
msgid "error downloading sources: %s"
msgstr "erro ao descarregar fontes: %s"
#: pkg/download/errors.go:25
msgid "error fetching %s: %s"
msgstr "erro ao buscar %s: %s"
#: pkg/sync/build/errors.go:9
msgid "error installing repo packages"
msgstr "erro ao instalar pacotes de repositório"
#: pkg/sync/build/installer.go:266 pkg/sync/build/installer.go:270
msgid "error installing:"
msgstr "erro ao instalar:"
#: pkg/sync/build/installer.go:233 pkg/sync/build/installer.go:237
msgid "error making: %s"
msgstr "erro ao compilar: %s"
#: pkg/sync/workdir/merge.go:24
msgid "error merging %s: %s"
msgstr "erro ao fundir %s: %s"
#: pkg/download/unified.go:59
msgid "error reading %s"
msgstr "erro ao ler %s"
#: sync.go:36
msgid "error refreshing databases"
msgstr "erro ao recarregar base de dados"
#: pkg/sync/workdir/clean.go:51 pkg/sync/workdir/merge.go:17
msgid "error resetting %s: %s"
msgstr "erro ao repor %s: %s"
#: pkg/sync/build/errors.go:53
msgid "error updating package install reason to %s"
msgstr "erro ao atualizar instalação do pacote motivo para %s"
#: pkg/sync/build/errors.go:48
msgid "explicit"
msgstr "explícito"
#: pkg/settings/errors.go:23
msgid "failed to create directory '%s': %s"
msgstr "falha ao criar pasta '%s': %s"
#: pkg/settings/config.go:281
msgid "failed to open config file '%s': %s"
msgstr "falha ao abrir arquivo de configuração '%s': %s"
#: pkg/sync/srcinfo/service.go:114
msgid "failed to parse %s -- skipping: %s"
msgstr "falha ao analisar %s -- ignorando: %s"
#: pkg/sync/srcinfo/service.go:118
msgid "failed to parse %s: %s"
msgstr "falha ao analisar %s: %s"
#: local_install.go:77
msgid "failed to parse .SRCINFO"
msgstr "falha ao analisar .SRCINFO"
#: pkg/settings/config.go:291
msgid "failed to read config file '%s': %s"
msgstr "falha ao ler ficheiro de configuração '%s': %s"
#: pkg/cmd/graph/main.go:46 pkg/runtime/runtime.go:73
msgid "failed to retrieve aur Cache"
msgstr "falha ao recuperar cache aur"
#: pkg/upgrade/sources.go:27
msgid "ignoring package devel upgrade (no AUR info found):"
msgstr ""
"a ignorar a atualização de desenvolvimento do pacote (não foi encontrada "
"informação AUR):"
#: pkg/text/errors.go:8
msgid "input too long"
msgstr "input demasiado longo"
#: pkg/db/ialpm/alpm.go:222 pkg/dep/dep_graph.go:761
msgid "invalid number: %s"
msgstr "número inválido: %s"
#: pkg/settings/parser/parser.go:174
msgid "invalid option '%s'"
msgstr "opção inválida '%s'"
#: cmd.go:197
msgid "invalid option: '--deps' and '--explicit' may not be used together"
msgstr ""
"opção inválida: '--deps' e '--explicit' não podem ser usados em conjunto"
#: pkg/download/abs.go:22
msgid "invalid repository"
msgstr "repositório inválido"
#: pkg/db/ialpm/alpm.go:227 pkg/dep/dep_graph.go:767
msgid "invalid value: %d is not between %d and %d"
msgstr "valor inválido: %d não está entre %d e %d"
#: pkg/text/input.go:48
msgid "no"
msgstr "não"
#: pkg/sync/srcinfo/pgp/keys.go:110
msgid "no keys to import"
msgstr "nenhuma chave a importar"
#: pkg/query/errors.go:20
msgid "no query was executed"
msgstr "nenhuma análise foi executada"
#: local_install.go:66
msgid "no target directories specified"
msgstr "nenhum diretório de destino especificado"
#: pkg/sync/build/installer.go:242
msgid "nothing to install for %s"
msgstr "nada a instalar para %s"
#: pkg/settings/parser/parser.go:164
msgid "only one operation may be used at a time"
msgstr "apenas uma operação pode ser utilizada ao mesmo tempo"
#: pkg/cmd/graph/main.go:70
msgid "only one target is allowed"
msgstr "apenas um destino é permitido"
#: pkg/upgrade/service.go:291
msgid "package"
msgid_plural "packages"
msgstr[0] "pacote"
msgstr[1] "pacotes"
msgstr[2] "pacotes"
#: print.go:187
msgid "package '%s' was not found"
msgstr "pacote '%s' não foi encontrado"
#: pkg/download/errors.go:15
msgid "package not found in AUR"
msgstr "pacote não encontrado no AUR"
#: pkg/download/abs.go:23
msgid "package not found in repos"
msgstr "pacote não encontrado nos repositórios"
#: pkg/sync/srcinfo/pgp/keys.go:100
msgid "problem importing keys"
msgstr "problema ao importar chaves"
#: clean.go:105
msgid "removing AUR packages from cache..."
msgstr "a remover pacotes AUR da cache..."
#: clean.go:178 pkg/sync/workdir/clean.go:41
msgid "removing untracked AUR files from cache..."
msgstr "a remover ficheiros AUR não rastreados da cache..."
#: pkg/sync/build/errors.go:38
msgid "the PKGDEST for %s is listed by makepkg but does not exist: %s"
msgstr "o PKGDEST para %s é listado pelo makepkg mas não existe: %s"
#: pkg/sync/sync.go:45
msgid "there is nothing to do"
msgstr "não há nada a fazer"
#: pkg/db/ialpm/alpm.go:247
msgid "unable to CreateHandle: %s"
msgstr "não é possível executar CreateHandle: %s"
#: cmd.go:186
msgid "unhandled operation"
msgstr "operação não implementada"
#: cmd.go:450
msgid "unknown-version"
msgstr "versão-desconhecida"
#: pkg/text/input.go:47
msgid "yes"
msgstr "sim"
================================================
FILE: po/pt_BR.po
================================================
#
# Translators:
# J G, 2021
# Otto Micheletti , 2021
# Harrison Ferreira, 2022
# Zisa, 2022
# Fernando Macedo, 2023
# Felipe Avelar, 2023
# Lucas Miranda , 2023
# Matheus Calegaro, 2024
# Mateus Eduardo, 2025
#
msgid ""
msgstr ""
"Last-Translator: Mateus Eduardo, 2025\n"
"Language-Team: Portuguese (Brazil) (https://app.transifex.com/yay-1/teams/123732/pt_BR/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: pt_BR\n"
"Plural-Forms: nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n"
"X-Generator: xgotext\n"
#: clean.go:83
msgid ""
"\n"
"Build directory:"
msgstr ""
"\n"
"Diretório de compilação:"
#: pkg/db/ialpm/alpm.go:201 pkg/dep/dep_graph.go:740
msgid ""
"\n"
"Enter a number (default=1): "
msgstr ""
"\n"
"Digite um número (padrão=1): "
#: pkg/menus/menu.go:32
msgid " (Build Files Exist)"
msgstr " (Arquivos de Build Existem)"
#: pkg/menus/menu.go:27
msgid " (Installed)"
msgstr " (Instalado)"
#: cmd.go:453
msgid " [Installed]"
msgstr " [Instalado]"
#: cmd.go:410 vote.go:36
msgid " there is nothing to do"
msgstr " não há nada a ser feito"
#: pkg/menus/menu.go:49
msgid "%s [A]ll [Ab]ort [I]nstalled [No]tInstalled or (1 2 3, 1-3, ^4)"
msgstr ""
"%s [A]Todos [Ab]Abortar [I]Instalados [No]Não Instalados ou (1 2 3, 1-3, ^4)"
#: pkg/sync/build/installer.go:308
msgid "%s already made -- skipping build"
msgstr "%s já construído -- pulando build"
#: pkg/menus/edit_menu.go:57
msgid "%s is not set"
msgstr "%s não está definido"
#: pkg/settings/exe/cmd_builder.go:257
msgid "%s is present."
msgstr "%s está presente."
#: pkg/dep/dep_graph.go:460 pkg/sync/build/installer.go:305
msgid "%s is up to date -- skipping"
msgstr "%s está atualizado -- pulando"
#: pkg/upgrade/service.go:291
msgid "%s to upgrade/install."
msgstr "%s para atualizar/instalar."
#: pkg/upgrade/service.go:285
msgid "%s will also be installed for this operation."
msgstr "%s também será instalado para essa operação."
#: pkg/sync/srcinfo/pgp/keys.go:124
msgid "%s, required by: %s"
msgstr "%s, requeridos por: %s"
#: pkg/menus/diff_menu.go:49
msgid "%s: No changes -- skipping"
msgstr "%s: Sem mudanças -- pulando"
#: pkg/query/filter.go:22
msgid "%s: can't use target with option --aur -- skipping"
msgstr ""
"%s: não é possível utilizar o argumento '--aur' com este pacote -- pulando"
#: pkg/query/filter.go:17
msgid "%s: can't use target with option --repo -- skipping"
msgstr ""
"%s: não é possível utilizar o argumento '--repo' com este pacote -- pulando"
#: pkg/upgrade/sources.go:57
msgid "%s: ignoring package upgrade (%s => %s)"
msgstr "%s: ignorando atualização do pacote (%s => %s)"
#: pkg/query/aur_warnings.go:46
msgid "%s: local (%s) is newer than AUR (%s)"
msgstr "%s: o pacote local (%s) é mais recente que o do AUR (%s)"
#: vote.go:51
msgid ""
"%s: please set AUR_USERNAME and AUR_PASSWORD environment variables for "
"voting"
msgstr ""
"%s: por favor, configure as variáveis de ambiente AUR_USERNAME e "
"AUR_PASSWORD para votar"
#: pkg/download/unified.go:192
msgid "(%d/%d) Downloaded PKGBUILD from ABS: %s"
msgstr "(%d/%d) PKGBUILD baixado do ABS: %s"
#: pkg/download/aur.go:92 pkg/download/unified.go:188
msgid "(%d/%d) Downloaded PKGBUILD: %s"
msgstr "(%d/%d) PKGBUILD transferido: %s"
#: pkg/download/aur.go:82
msgid "(%d/%d) Failed to download PKGBUILD: %s"
msgstr "(%d/%d) Falha ao baixar PKGBUILD: %s"
#: pkg/sync/srcinfo/service.go:109
msgid "(%d/%d) Parsing SRCINFO: %s"
msgstr "(%d/%d) Analisando SRCINFO: %s"
#: pkg/query/types.go:103 pkg/query/types.go:72
msgid "(Installed)"
msgstr "(Instalado)"
#: pkg/query/types.go:101 pkg/query/types.go:70
msgid "(Installed: %s)"
msgstr "(Instalado: %s)"
#: pkg/query/types.go:61
msgid "(Orphaned)"
msgstr "(Órfão)"
#: pkg/query/types.go:65
msgid "(Out-of-date: %s)"
msgstr "(Desatualizado: %s)"
#: pkg/dep/dep_graph.go:75
msgid "AUR"
msgstr "AUR"
#: print.go:44
msgid "AUR URL"
msgstr "URL do AUR"
#: pkg/menus/edit_menu.go:58
msgid "Add %s or %s to your environment variables"
msgstr "Adicione %s ou %s às suas variáveis de ambiente"
#: main.go:60
msgid "Avoid running yay as root/sudo."
msgstr "Evite executar o yay como root/sudo."
#: pkg/dep/dep_graph.go:63
msgid "Check Dependency"
msgstr "Verificar dependências"
#: print.go:41
msgid "Check Deps"
msgstr "Dependências de verificação"
#: pkg/upgrade/service.go:89
msgid "Checking development packages..."
msgstr "Verificando pacotes em desenvolvimento..."
#: pkg/sync/workdir/clean.go:45
msgid "Cleaning (%d/%d): %s"
msgstr "Limpando (%d/%d): %s"
#: print.go:42
msgid "Conflicts With"
msgstr "Em conflito com"
#: pkg/menus/clean_menu.go:62
msgid "Deleting (%d/%d): %s"
msgstr "Removendo (%d/%d): %s"
#: pkg/dep/dep_graph.go:61
msgid "Dependency"
msgstr "Dependências"
#: print.go:38
msgid "Depends On"
msgstr "Depende de"
#: print.go:33
msgid "Description"
msgstr "Descrição"
#: pkg/menus/diff_menu.go:160
msgid "Diffs to show?"
msgstr "Exibir diffs?"
#: pkg/settings/migrations.go:25
msgid "Disable 'provides' setting by default"
msgstr "Desativar a definição 'fornece' por padrão"
#: clean.go:78
msgid "Do you want to remove ALL AUR packages from cache?"
msgstr "Você deseja remover TODOS os pacotes AUR do cache?"
#: clean.go:95
msgid "Do you want to remove ALL untracked AUR files?"
msgstr "Você deseja remover TODOS os pacotes AUR não monitorados?"
#: clean.go:80
msgid "Do you want to remove all other AUR packages from cache?"
msgstr "Você deseja remover todos os outros pacotes AUR do cache?"
#: pkg/menus/edit_menu.go:61
msgid "Edit PKGBUILD with?"
msgstr "Editar PKGBUILD com?"
#: pkg/query/errors.go:13
msgid "Error during AUR search: %s\n"
msgstr "Erro durante a busca no AUR: %s\n"
#: pkg/upgrade/service.go:295
msgid "Excluding packages may cause partial upgrades and break systems"
msgstr ""
"A exclusão de pacotes pode causar atualizações parciais e quebrar sistemas"
#: pkg/dep/dep_graph.go:60
msgid "Explicit"
msgstr "Explícito"
#: print.go:91
msgid "Explicitly installed packages: %s"
msgstr "Pacotes explicitamente instalados: %s"
#: pkg/dep/dep_graph.go:437 pkg/dep/dep_graph.go:535
msgid "Failed to find AUR package for"
msgstr "Falha ao localizar pacote AUR para"
#: pkg/sync/build/installer.go:120
msgid "Failed to install layer, rolling up to next layer."
msgstr "Falha ao instalar camada, indo até à camada seguinte."
#: pkg/sync/build/errors.go:16
msgid ""
"Failed to install the following packages. Manual intervention is required:"
msgstr ""
"Falha ao instalar os seguintes pacotes. É necessária a intervenção manual:"
#: print.go:45
msgid "First Submitted"
msgstr "Primeira submissão"
#: pkg/query/aur_warnings.go:83
msgid "Flagged Out Of Date AUR Packages:"
msgstr "Pacotes AUR marcados como desatualizados:"
#: print.go:90
msgid "Foreign installed packages: %s"
msgstr "Pacotes externos instalados: %s"
#: pkg/vcs/vcs.go:144
msgid "Found git repo: %s"
msgstr "Repositório git encontrado: %s"
#: vcs.go:72
msgid "GenDB finished. No packages were installed"
msgstr "GenDB finalizado. Nenhum pacote foi instalado"
#: print.go:36
msgid "Groups"
msgstr "Grupos"
#: pkg/sync/srcinfo/pgp/keys.go:88
msgid "Import?"
msgstr "Importar?"
#: pkg/sync/srcinfo/pgp/keys.go:97
msgid "Importing keys with gpg..."
msgstr "Importando chaves com gpg..."
#: print.go:46
msgid "Keywords"
msgstr "Palavras-chave"
#: print.go:47
msgid "Last Modified"
msgstr "Última modificação"
#: print.go:35
msgid "Licenses"
msgstr "Licenças"
#: pkg/dep/dep_graph.go:77
msgid "Local"
msgstr "Local"
#: print.go:48
msgid "Maintainer"
msgstr "Mantenedor"
#: pkg/dep/dep_graph.go:62
msgid "Make Dependency"
msgstr "Criar dependências"
#: print.go:40
msgid "Make Deps"
msgstr "Dependências Make"
#: pkg/dep/dep_graph.go:79
msgid "Missing"
msgstr "Em falta"
#: pkg/query/aur_warnings.go:75
msgid "Missing AUR Debug Packages:"
msgstr "Pacotes de debug do AUR em falta:"
#: print.go:31
msgid "Name"
msgstr "Nome"
#: pkg/dep/dep_graph.go:442 pkg/dep/dep_graph.go:564
msgid "No AUR package found for"
msgstr "Nenhum pacote AUR localizado para"
#: pkg/dep/dep_graph.go:564
msgid "required by"
msgstr "exigido por"
#: pkg/dep/dep_graph.go:182
msgid "No package found for"
msgstr "Nenhum pacote encontrado para"
#: print.go:225
msgid "None"
msgstr "Nenhum"
#: print.go:39
msgid "Optional Deps"
msgstr "Depêndencias Opcionais"
#: pkg/query/aur_warnings.go:79
msgid "Orphan (unmaintained) AUR Packages:"
msgstr "Pacotes AUR órfãos (não mantidos):"
#: print.go:53 print.go:55
msgid "Out-of-date"
msgstr "Desatualizado"
#: pkg/sync/srcinfo/pgp/keys.go:115
msgid "PGP keys need importing:"
msgstr "Chaves PGP que precisam ser importadas:"
#: pkg/sync/workdir/preparer.go:252
msgid "PKGBUILD up to date, skipping download: %s"
msgstr "PKGBUILD atualizado, ignorando a transferência: %s"
#: pkg/menus/edit_menu.go:130
msgid "PKGBUILDs to edit?"
msgstr "PKGBUILDs a serem editados?"
#: print.go:61
msgid "Package Base"
msgstr "Pacotes Base"
#: print.go:60
msgid "Package Base ID"
msgstr "ID do Pacote Base"
#: pkg/query/aur_warnings.go:71
msgid "Packages not in AUR:"
msgstr "Pacotes que não estão no AUR:"
#: pkg/menus/clean_menu.go:54
msgid "Packages to cleanBuild?"
msgstr "Limpar e construir quais pacotes?"
#: pkg/dep/dep_graph.go:202
msgid "Packages to exclude"
msgstr "Pacotes a excluir"
#: pkg/upgrade/service.go:294
msgid "Packages to exclude: (eg: \"1 2 3\", \"1-3\", \"^4\" or repo name)"
msgstr "Pacotes a excluir: (ex: \"1 2 3\", \"1-3\", \"^4\" ou nome do repositório)"
#: cmd.go:392
msgid "Packages to install (eg: 1 2 3, 1-3 or ^4)"
msgstr "Pacotes a instalar (ex: 1 2 3, 1-3 ou ^4)"
#: print.go:49
msgid "Popularity"
msgstr "Popularidade"
#: pkg/menus/diff_menu.go:172 pkg/menus/edit_menu.go:143
msgid "Proceed with install?"
msgstr "Prosseguir com a instalação?"
#: print.go:37
msgid "Provides"
msgstr "Fornece"
#: pkg/sync/workdir/preparer.go:125
msgid "Remove make dependencies after install?"
msgstr "Remover dependências make após a instalação?"
#: print.go:43
msgid "Replaces"
msgstr "Substitui"
#: pkg/db/ialpm/alpm.go:191 print.go:30
msgid "Repository"
msgstr "Repositório"
#: pkg/dep/dep_graph.go:730
msgid "Repository AUR"
msgstr "Repositório AUR"
#: pkg/dep/dep_graph.go:78
msgid "SRCINFO"
msgstr "SRCINFO"
#: pkg/upgrade/service.go:71
msgid "Searching AUR for updates..."
msgstr "Procurando atualizações no AUR..."
#: pkg/upgrade/service.go:159
msgid "Searching databases for updates..."
msgstr "Procurando atualizações nos bancos de dados..."
#: pkg/query/query_builder.go:214
msgid "Showing repo packages only"
msgstr "Mostrando somente pacotes do repositório"
#: print.go:95
msgid "Size of pacman cache %s: %s"
msgstr "Tamanho da cache do pacman %s: %s"
#: print.go:98
msgid "Size of yay cache %s: %s"
msgstr "Tamanho da cache do yay %s: %s"
#: print.go:62
msgid "Snapshot URL"
msgstr "URL para Snapshot"
#: pkg/dep/dep_graph.go:76
msgid "Sync"
msgstr "Sincronizar"
#: print.go:100
msgid "Ten biggest packages:"
msgstr "Dez maiores pacotes:"
#: pkg/sync/sync.go:124
msgid "The following packages are not compatible with your architecture:"
msgstr "Os seguintes pacotes não são compatíveis com a sua arquitetura:"
#: pkg/db/ialpm/alpm.go:179 pkg/dep/dep_graph.go:726
msgid "There are %[1]d providers available for %[2]s:"
msgstr "Existem %[1]d provedores disponíveis para %[2]s:"
#: pkg/settings/exe/cmd_builder.go:258
msgid "There may be another Pacman instance running. Waiting..."
msgstr "Pode haver outra instância do Pacman em execução. Aguardando..."
#: print.go:92
msgid "Total Size occupied by packages: %s"
msgstr "Espaço total ocupado por pacotes: %s"
#: print.go:89
msgid "Total installed packages: %s"
msgstr "Total de pacotes instalados: %s"
#: pkg/sync/sync.go:132
msgid "Try to build them anyway?"
msgstr "Tentar construí-los mesmo assim?"
#: print.go:34
msgid "URL"
msgstr "URL"
#: clean.go:194 pkg/menus/clean_menu.go:65 pkg/menus/clean_menu.go:71
msgid "Unable to clean:"
msgstr "Não foi possível limpar:"
#: get.go:42 get.go:74
msgid "Unable to find the following packages:"
msgstr "Não foi possível encontrar os seguintes pacotes:"
#: vote.go:20
msgid "Unable to handle package vote for: %s. err: %s"
msgstr "Não foi possível gerir votação de pacote para: %s err: %s"
#: clean.go:170
msgid "Unable to remove %s: %s"
msgstr "Não foi possível remover %s: %s"
#: print.go:32
msgid "Version"
msgstr "Versão"
#: print.go:50
msgid "Votes"
msgstr "Votos"
#: print.go:87
msgid "Yay version v%s"
msgstr "Versão do Yay v%s"
#: pkg/menus/menu.go:49
msgid "[N]one"
msgstr "[N]Nenhum"
#: pkg/settings/errors.go:29
msgid "aborting due to user"
msgstr "abortando devido ao usuário"
#: pkg/settings/parser/parser.go:608
msgid "argument '-' specified without input on stdin"
msgstr "argumento '-' especificado sem entrada em stdin"
#: local_install.go:26
msgid "cannot find PKGBUILD and .SRCINFO in directory"
msgstr "não foi possível localizar PKGBUILD e .SRCINFO no diretório"
#: pkg/sync/build/pkg_archive.go:148
msgid "cannot find package name: %v"
msgstr "não foi possível encontrar o nome do pacote: %v"
#: pkg/sync/build/errors.go:30
msgid "could not find PKGDEST for: %s"
msgstr "não foi possível encontrar PKGDEST para: %s"
#: errors.go:9
msgid "could not find all required packages"
msgstr "não foi possível localizar todos os pacotes necessários"
#: pkg/sync/build/errors.go:61
msgid "could not find any package archives listed in %s"
msgstr "não foi possível localizar nenhum arquivo de pacotes listados em %s"
#: pkg/sync/build/errors.go:50 pkg/upgrade/service.go:286
msgid "dependency"
msgstr "dependência"
#: pkg/vcs/vcs.go:100 pkg/vcs/vcs.go:96
msgid "devel check for package failed: '%s' encountered an error"
msgstr "a busca por pacotes devel falhou: '%s' encontrou um erro"
#: pkg/menus/edit_menu.go:110
msgid "editor did not exit successfully, aborting: %s"
msgstr "editor não finalizou com sucesso, abortando: %s"
#: pkg/sync/workdir/aur_source.go:24
msgid "error downloading sources: %s"
msgstr "erro ao descarregar as fontes: %s"
#: pkg/download/errors.go:25
msgid "error fetching %s: %s"
msgstr "erro ao buscar %s: %s"
#: pkg/sync/build/errors.go:9
msgid "error installing repo packages"
msgstr "erro ao instalar pacotes do repositório"
#: pkg/sync/build/installer.go:266 pkg/sync/build/installer.go:270
msgid "error installing:"
msgstr "erro ao instalar:"
#: pkg/sync/build/installer.go:233 pkg/sync/build/installer.go:237
msgid "error making: %s"
msgstr "erro ao construir: %s"
#: pkg/sync/workdir/merge.go:24
msgid "error merging %s: %s"
msgstr "erro ao mesclar %s: %s"
#: pkg/download/unified.go:59
msgid "error reading %s"
msgstr "erro ao ler %s"
#: sync.go:36
msgid "error refreshing databases"
msgstr "erro ao recarregar base de dados"
#: pkg/sync/workdir/clean.go:51 pkg/sync/workdir/merge.go:17
msgid "error resetting %s: %s"
msgstr "erro ao resetar %s: %s"
#: pkg/sync/build/errors.go:53
msgid "error updating package install reason to %s"
msgstr "erro ao atualizar instalação do pacote motivo para %s"
#: pkg/sync/build/errors.go:48
msgid "explicit"
msgstr "explícito"
#: pkg/settings/errors.go:23
msgid "failed to create directory '%s': %s"
msgstr "falha ao criar diretório '%s':%s"
#: pkg/settings/config.go:281
msgid "failed to open config file '%s': %s"
msgstr "falha ao abrir o arquivo de configuração '%s': %s"
#: pkg/sync/srcinfo/service.go:114
msgid "failed to parse %s -- skipping: %s"
msgstr "falha ao analisar %s -- pulando: %s"
#: pkg/sync/srcinfo/service.go:118
msgid "failed to parse %s: %s"
msgstr "falha ao analisar %s: %s"
#: local_install.go:77
msgid "failed to parse .SRCINFO"
msgstr "falha ao analisar .SRCINFO"
#: pkg/settings/config.go:291
msgid "failed to read config file '%s': %s"
msgstr "falha ao ler o arquivo de configuração '%s': %s"
#: pkg/cmd/graph/main.go:46 pkg/runtime/runtime.go:73
msgid "failed to retrieve aur Cache"
msgstr "falha ao recuperar cache aur"
#: pkg/upgrade/sources.go:27
msgid "ignoring package devel upgrade (no AUR info found):"
msgstr ""
"ignorando a atualização do desenvolvimento do pacote (não foi localizada "
"informação AUR):"
#: pkg/text/errors.go:8
msgid "input too long"
msgstr "input muito longo"
#: pkg/db/ialpm/alpm.go:222 pkg/dep/dep_graph.go:761
msgid "invalid number: %s"
msgstr "número inválido: %s"
#: pkg/settings/parser/parser.go:174
msgid "invalid option '%s'"
msgstr "argumento inválido '%s'"
#: cmd.go:197
msgid "invalid option: '--deps' and '--explicit' may not be used together"
msgstr ""
"opção inválida: os argumentos '--deps' e '--explicit' não podem ser usados "
"juntos "
#: pkg/download/abs.go:22
msgid "invalid repository"
msgstr "repositório inválido"
#: pkg/db/ialpm/alpm.go:227 pkg/dep/dep_graph.go:767
msgid "invalid value: %d is not between %d and %d"
msgstr "valor inválido: %d não está entre %d e %d"
#: pkg/text/input.go:48
msgid "no"
msgstr "não"
#: pkg/sync/srcinfo/pgp/keys.go:110
msgid "no keys to import"
msgstr "nenhuma chave para ser importada"
#: pkg/query/errors.go:20
msgid "no query was executed"
msgstr "nenhuma busca foi executada"
#: local_install.go:66
msgid "no target directories specified"
msgstr "nenhum diretório de destino especificado"
#: pkg/sync/build/installer.go:242
msgid "nothing to install for %s"
msgstr "nada a instalar para %s"
#: pkg/settings/parser/parser.go:164
msgid "only one operation may be used at a time"
msgstr "somente uma operação pode ser utilizada de cada vez"
#: pkg/cmd/graph/main.go:70
msgid "only one target is allowed"
msgstr "apenas um destino é permitido"
#: pkg/upgrade/service.go:291
msgid "package"
msgid_plural "packages"
msgstr[0] "pacote"
msgstr[1] "pacotes"
msgstr[2] "pacotes"
#: print.go:187
msgid "package '%s' was not found"
msgstr "pacote '%s' não foi encontrado"
#: pkg/download/errors.go:15
msgid "package not found in AUR"
msgstr "pacote não encontrado no AUR"
#: pkg/download/abs.go:23
msgid "package not found in repos"
msgstr "pacote não encontrado nos repositórios"
#: pkg/sync/srcinfo/pgp/keys.go:100
msgid "problem importing keys"
msgstr "problema ao importar as chaves"
#: clean.go:105
msgid "removing AUR packages from cache..."
msgstr "removendo pacotes AUR do cache..."
#: clean.go:178 pkg/sync/workdir/clean.go:41
msgid "removing untracked AUR files from cache..."
msgstr "removendo arquivos do AUR não rastreados do cache..."
#: pkg/sync/build/errors.go:38
msgid "the PKGDEST for %s is listed by makepkg but does not exist: %s"
msgstr "o PKGDEST para %s está listado pelo makepkg porém não existe: %s"
#: pkg/sync/sync.go:45
msgid "there is nothing to do"
msgstr " não há nada a ser feito"
#: pkg/db/ialpm/alpm.go:247
msgid "unable to CreateHandle: %s"
msgstr "não foi possível executar CreateHandle: %s"
#: cmd.go:186
msgid "unhandled operation"
msgstr "operação sem manuseio"
#: cmd.go:450
msgid "unknown-version"
msgstr "versão-desconhecida"
#: pkg/text/input.go:47
msgid "yes"
msgstr "sim"
================================================
FILE: po/ru.po
================================================
#
# Translators:
# Vladislav Zenkov, 2022
# Kira Malinova, 2023
# Victor Golovanenko , 2023
# Dancheg97 F, 2023
# falixfresh, 2023
# Ravenso BlacK, 2023
# Vladislav Grechannik, 2024
# Vladimir Yerilov, 2025
# Alexandra Harbar, 2025
# Maxim Tyuterev, 2026
#
msgid ""
msgstr ""
"Last-Translator: Maxim Tyuterev, 2026\n"
"Language-Team: Russian (https://app.transifex.com/yay-1/teams/123732/ru/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: ru\n"
"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n"
"X-Generator: xgotext\n"
#: clean.go:83
msgid ""
"\n"
"Build directory:"
msgstr ""
"\n"
"Каталог сборки:"
#: pkg/db/ialpm/alpm.go:201 pkg/dep/dep_graph.go:740
msgid ""
"\n"
"Enter a number (default=1): "
msgstr ""
"\n"
"Введите номер (по умолчанию=1):"
#: pkg/menus/menu.go:32
msgid " (Build Files Exist)"
msgstr " (Файлы сборки существуют)"
#: pkg/menus/menu.go:27
msgid " (Installed)"
msgstr "(Установлено)"
#: cmd.go:453
msgid " [Installed]"
msgstr "[Установлено]"
#: cmd.go:410 vote.go:36
msgid " there is nothing to do"
msgstr "делать больше нечего"
#: pkg/menus/menu.go:49
msgid "%s [A]ll [Ab]ort [I]nstalled [No]tInstalled or (1 2 3, 1-3, ^4)"
msgstr ""
"%s [В]се [От]менить [У]становленные [Не]установленные или (1 2 3, 1-3, ^4)"
#: pkg/sync/build/installer.go:308
msgid "%s already made -- skipping build"
msgstr "%s уже собран -- сборка пропускается"
#: pkg/menus/edit_menu.go:57
msgid "%s is not set"
msgstr "%s не задан"
#: pkg/settings/exe/cmd_builder.go:257
msgid "%s is present."
msgstr "%s уже существует."
#: pkg/dep/dep_graph.go:460 pkg/sync/build/installer.go:305
msgid "%s is up to date -- skipping"
msgstr "%s обновлён -- пропуск"
#: pkg/upgrade/service.go:291
msgid "%s to upgrade/install."
msgstr "обновить/установить."
#: pkg/upgrade/service.go:285
msgid "%s will also be installed for this operation."
msgstr "также будет установлен для этой операции."
#: pkg/sync/srcinfo/pgp/keys.go:124
msgid "%s, required by: %s"
msgstr "%s, требуется пакету: %s"
#: pkg/menus/diff_menu.go:49
msgid "%s: No changes -- skipping"
msgstr "%s: Нет изменений -- пропуск"
#: pkg/query/filter.go:22
msgid "%s: can't use target with option --aur -- skipping"
msgstr "%s: невозможно использовать цель с параметром --aur -- skipping"
#: pkg/query/filter.go:17
msgid "%s: can't use target with option --repo -- skipping"
msgstr "%s: невозможно использовать цель с параметром --repo -- skipping"
#: pkg/upgrade/sources.go:57
msgid "%s: ignoring package upgrade (%s => %s)"
msgstr "%s: игнорирование обновления пакета (%s => %s)"
#: pkg/query/aur_warnings.go:46
msgid "%s: local (%s) is newer than AUR (%s)"
msgstr "%s: локальный пакет (%s) новее, чем в AUR (%s)"
#: vote.go:51
msgid ""
"%s: please set AUR_USERNAME and AUR_PASSWORD environment variables for "
"voting"
msgstr ""
"%s: пожалуйста, укажите переменные среды AUR_USERNAME и AUR_PASSWORD для "
"голосования"
#: pkg/download/unified.go:192
msgid "(%d/%d) Downloaded PKGBUILD from ABS: %s"
msgstr "(%d/%d) Загружен PKGBUILD из ABS: %s"
#: pkg/download/aur.go:92 pkg/download/unified.go:188
msgid "(%d/%d) Downloaded PKGBUILD: %s"
msgstr "(%d/%d) Загружен PKGBUILD: %s"
#: pkg/download/aur.go:82
msgid "(%d/%d) Failed to download PKGBUILD: %s"
msgstr "(%d/%d) Не удалось загрузить PKGBUILD:"
#: pkg/sync/srcinfo/service.go:109
msgid "(%d/%d) Parsing SRCINFO: %s"
msgstr "(%d/%d) Анализ SRCINFO: %s"
#: pkg/query/types.go:103 pkg/query/types.go:72
msgid "(Installed)"
msgstr "(Установлено)"
#: pkg/query/types.go:101 pkg/query/types.go:70
msgid "(Installed: %s)"
msgstr "(Установлено: %s)"
#: pkg/query/types.go:61
msgid "(Orphaned)"
msgstr "(Осиротевший)"
#: pkg/query/types.go:65
msgid "(Out-of-date: %s)"
msgstr "(Устарел: %s)"
#: pkg/dep/dep_graph.go:75
msgid "AUR"
msgstr "AUR"
#: print.go:44
msgid "AUR URL"
msgstr "Ссылка на AUR"
#: pkg/menus/edit_menu.go:58
msgid "Add %s or %s to your environment variables"
msgstr "Добавьте %s или %s в переменные среды"
#: main.go:60
msgid "Avoid running yay as root/sudo."
msgstr "Избегайте запуска yay от имени root/sudo."
#: pkg/dep/dep_graph.go:63
msgid "Check Dependency"
msgstr "Проверка зависимости"
#: print.go:41
msgid "Check Deps"
msgstr "Зависимости, требуемые для проверки"
#: pkg/upgrade/service.go:89
msgid "Checking development packages..."
msgstr "Проверка пакетов разработки..."
#: pkg/sync/workdir/clean.go:45
msgid "Cleaning (%d/%d): %s"
msgstr "Очистка (%d/%d): %s"
#: print.go:42
msgid "Conflicts With"
msgstr "Конфликтует с"
#: pkg/menus/clean_menu.go:62
msgid "Deleting (%d/%d): %s"
msgstr "Удаление (%d/%d): %s"
#: pkg/dep/dep_graph.go:61
msgid "Dependency"
msgstr "Зависимость"
#: print.go:38
msgid "Depends On"
msgstr "Зависит от"
#: print.go:33
msgid "Description"
msgstr "Описание"
#: pkg/menus/diff_menu.go:160
msgid "Diffs to show?"
msgstr "Показать изменения?"
#: pkg/settings/migrations.go:25
msgid "Disable 'provides' setting by default"
msgstr "Отключите настройку 'обеспечивает' по умолчанию"
#: clean.go:78
msgid "Do you want to remove ALL AUR packages from cache?"
msgstr "Вы хотите удалить ВСЕ пакеты AUR из кэша?"
#: clean.go:95
msgid "Do you want to remove ALL untracked AUR files?"
msgstr "Вы хотите удалить ВСЕ неотслеживаемые файлы AUR?"
#: clean.go:80
msgid "Do you want to remove all other AUR packages from cache?"
msgstr "Вы хотите удалить все остальные пакеты AUR из кэша?"
#: pkg/menus/edit_menu.go:61
msgid "Edit PKGBUILD with?"
msgstr "Отредактировать PKGBUILD?"
#: pkg/query/errors.go:13
msgid "Error during AUR search: %s\n"
msgstr "Ошибка поиска в AUR: %s\n"
#: pkg/upgrade/service.go:295
msgid "Excluding packages may cause partial upgrades and break systems"
msgstr ""
"Исключение пакетов может привести к частичному обновлению и сломать систему"
#: pkg/dep/dep_graph.go:60
msgid "Explicit"
msgstr "Явно"
#: print.go:91
msgid "Explicitly installed packages: %s"
msgstr "Явно установленные пакеты: %s"
#: pkg/dep/dep_graph.go:437 pkg/dep/dep_graph.go:535
msgid "Failed to find AUR package for"
msgstr "Не удалось найти пакет AUR для"
#: pkg/sync/build/installer.go:120
msgid "Failed to install layer, rolling up to next layer."
msgstr "Ошибка установки слоя, переход на следующий слой."
#: pkg/sync/build/errors.go:16
msgid ""
"Failed to install the following packages. Manual intervention is required:"
msgstr ""
"Не удалось установить следующие пакеты — требуется ручное вмешательство:"
#: print.go:45
msgid "First Submitted"
msgstr "Впервые представленный"
#: pkg/query/aur_warnings.go:83
msgid "Flagged Out Of Date AUR Packages:"
msgstr "Пакеты AUR, помеченные как устаревшие:"
#: print.go:90
msgid "Foreign installed packages: %s"
msgstr "Cторонних пакетов установлено: "
#: pkg/vcs/vcs.go:144
msgid "Found git repo: %s"
msgstr "Найден git репозиторий: %s"
#: vcs.go:72
msgid "GenDB finished. No packages were installed"
msgstr "Генерирование БД завершено. Никакие пакеты не были установлены"
#: print.go:36
msgid "Groups"
msgstr "Группы"
#: pkg/sync/srcinfo/pgp/keys.go:88
msgid "Import?"
msgstr "Импортировать?"
#: pkg/sync/srcinfo/pgp/keys.go:97
msgid "Importing keys with gpg..."
msgstr "Импортирование ключей с помощью GPG..."
#: print.go:46
msgid "Keywords"
msgstr "Ключевые слова"
#: print.go:47
msgid "Last Modified"
msgstr "Последнее изменение"
#: print.go:35
msgid "Licenses"
msgstr "Лицензии"
#: pkg/dep/dep_graph.go:77
msgid "Local"
msgstr "Локальный"
#: print.go:48
msgid "Maintainer"
msgstr "Сопровождающий"
#: pkg/dep/dep_graph.go:62
msgid "Make Dependency"
msgstr "Создание зависимости"
#: print.go:40
msgid "Make Deps"
msgstr "Зависимости, требуемые для сборки"
#: pkg/dep/dep_graph.go:79
msgid "Missing"
msgstr "Отсутствующие"
#: pkg/query/aur_warnings.go:75
msgid "Missing AUR Debug Packages:"
msgstr "Отсутствующие в AUR пакеты для отладки:"
#: print.go:31
msgid "Name"
msgstr "Название"
#: pkg/dep/dep_graph.go:442 pkg/dep/dep_graph.go:564
msgid "No AUR package found for"
msgstr "Не найден пакет AUR для"
#: pkg/dep/dep_graph.go:564
msgid "required by"
msgstr "нужен для"
#: pkg/dep/dep_graph.go:182
msgid "No package found for"
msgstr "Не найден пакет для"
#: print.go:225
msgid "None"
msgstr "Нет"
#: print.go:39
msgid "Optional Deps"
msgstr "Дополнительные зависимости"
#: pkg/query/aur_warnings.go:79
msgid "Orphan (unmaintained) AUR Packages:"
msgstr "Осиротевшие (неподдерживаемые) пакеты AUR:"
#: print.go:53 print.go:55
msgid "Out-of-date"
msgstr "Устарел"
#: pkg/sync/srcinfo/pgp/keys.go:115
msgid "PGP keys need importing:"
msgstr "Ключи PGP, требующие импорта:"
#: pkg/sync/workdir/preparer.go:252
msgid "PKGBUILD up to date, skipping download: %s"
msgstr "PKGBUILD находится в актуальном состоянии, пропускается загрузка: %s"
#: pkg/menus/edit_menu.go:130
msgid "PKGBUILDs to edit?"
msgstr "Отредактировать PKGBUILD?"
#: print.go:61
msgid "Package Base"
msgstr "Базовый пакет"
#: print.go:60
msgid "Package Base ID"
msgstr "Идентификатор пакета"
#: pkg/query/aur_warnings.go:71
msgid "Packages not in AUR:"
msgstr "Пакет не найден в AUR:"
#: pkg/menus/clean_menu.go:54
msgid "Packages to cleanBuild?"
msgstr "Пакеты для чистой сборки?"
#: pkg/dep/dep_graph.go:202
msgid "Packages to exclude"
msgstr "Пакеты для исключения"
#: pkg/upgrade/service.go:294
msgid "Packages to exclude: (eg: \"1 2 3\", \"1-3\", \"^4\" or repo name)"
msgstr "Исключить пакеты: (напр.: \"1 2 3\", \"1-3\", \"^4\" или название репозитория)"
#: cmd.go:392
msgid "Packages to install (eg: 1 2 3, 1-3 or ^4)"
msgstr "Пакеты для установки: (напр.: 1 2 3, 1-3 or ^4)"
#: print.go:49
msgid "Popularity"
msgstr "Популярность"
#: pkg/menus/diff_menu.go:172 pkg/menus/edit_menu.go:143
msgid "Proceed with install?"
msgstr "Продолжить установку?"
#: print.go:37
msgid "Provides"
msgstr "Предоставляет"
#: pkg/sync/workdir/preparer.go:125
msgid "Remove make dependencies after install?"
msgstr "Удалить зависимости сборки после установки?"
#: print.go:43
msgid "Replaces"
msgstr "Заменяет"
#: pkg/db/ialpm/alpm.go:191 print.go:30
msgid "Repository"
msgstr "Репозиторий"
#: pkg/dep/dep_graph.go:730
msgid "Repository AUR"
msgstr "Репозиторий AUR"
#: pkg/dep/dep_graph.go:78
msgid "SRCINFO"
msgstr "SRCINFO"
#: pkg/upgrade/service.go:71
msgid "Searching AUR for updates..."
msgstr "Поиск обновлений пакетов AUR..."
#: pkg/upgrade/service.go:159
msgid "Searching databases for updates..."
msgstr "Поиск обновлений в базах данных..."
#: pkg/query/query_builder.go:214
msgid "Showing repo packages only"
msgstr "Показываются только пакеты из репозиториев"
#: print.go:95
msgid "Size of pacman cache %s: %s"
msgstr "Размер кэша pacman %s: %s"
#: print.go:98
msgid "Size of yay cache %s: %s"
msgstr "Размер кэша yay %s: %s"
#: print.go:62
msgid "Snapshot URL"
msgstr "Ссылка снимка"
#: pkg/dep/dep_graph.go:76
msgid "Sync"
msgstr "Синхронизация"
#: print.go:100
msgid "Ten biggest packages:"
msgstr "Десять самых больших пакетов:"
#: pkg/sync/sync.go:124
msgid "The following packages are not compatible with your architecture:"
msgstr "Следующие пакеты несовместимы с вашей архитектурой:"
#: pkg/db/ialpm/alpm.go:179 pkg/dep/dep_graph.go:726
msgid "There are %[1]d providers available for %[2]s:"
msgstr "%[2]s доступен в %[1]d источниках:"
#: pkg/settings/exe/cmd_builder.go:258
msgid "There may be another Pacman instance running. Waiting..."
msgstr "Возможно, запущен другой процесс Pacman. Ожидание..."
#: print.go:92
msgid "Total Size occupied by packages: %s"
msgstr "Суммарный размер, занятый пакетами: %s"
#: print.go:89
msgid "Total installed packages: %s"
msgstr "Всего установлено пакетов: %s"
#: pkg/sync/sync.go:132
msgid "Try to build them anyway?"
msgstr "Попытаться собрать, несмотря на несовместимость?"
#: print.go:34
msgid "URL"
msgstr "URL"
#: clean.go:194 pkg/menus/clean_menu.go:65 pkg/menus/clean_menu.go:71
msgid "Unable to clean:"
msgstr "Невозможно очистить:"
#: get.go:42 get.go:74
msgid "Unable to find the following packages:"
msgstr "Не удалось найти следующие пакеты:"
#: vote.go:20
msgid "Unable to handle package vote for: %s. err: %s"
msgstr "Не удалось обработать голосование за: %s. ошибка: %s"
#: clean.go:170
msgid "Unable to remove %s: %s"
msgstr "Не удается удалить %s: %s"
#: print.go:32
msgid "Version"
msgstr "Версия"
#: print.go:50
msgid "Votes"
msgstr "Голосов"
#: print.go:87
msgid "Yay version v%s"
msgstr "Версия Yay: v%s"
#: pkg/menus/menu.go:49
msgid "[N]one"
msgstr "[Н]ет"
#: pkg/settings/errors.go:29
msgid "aborting due to user"
msgstr "прервано пользователем"
#: pkg/settings/parser/parser.go:608
msgid "argument '-' specified without input on stdin"
msgstr "аргумент '-' задан без ввода из stdin"
#: local_install.go:26
msgid "cannot find PKGBUILD and .SRCINFO in directory"
msgstr "не удалось найти PKGBUILD и .SRCINFO в каталоге"
#: pkg/sync/build/pkg_archive.go:148
msgid "cannot find package name: %v"
msgstr "Неудалось найти пакет с названием: %v"
#: pkg/sync/build/errors.go:30
msgid "could not find PKGDEST for: %s"
msgstr "не удалось найти PKGDEST для: %s"
#: errors.go:9
msgid "could not find all required packages"
msgstr "не удалось найти все необходимые пакеты"
#: pkg/sync/build/errors.go:61
msgid "could not find any package archives listed in %s"
msgstr "Не удалось найти архивы пакетов в %s"
#: pkg/sync/build/errors.go:50 pkg/upgrade/service.go:286
msgid "dependency"
msgstr "зависимости"
#: pkg/vcs/vcs.go:100 pkg/vcs/vcs.go:96
msgid "devel check for package failed: '%s' encountered an error"
msgstr "проверка пакета '%s' не завершена из-за ошибки"
#: pkg/menus/edit_menu.go:110
msgid "editor did not exit successfully, aborting: %s"
msgstr "неудачный выход из редактора, отмена: %s"
#: pkg/sync/workdir/aur_source.go:24
msgid "error downloading sources: %s"
msgstr "ошибка загрузки исходников: %s"
#: pkg/download/errors.go:25
msgid "error fetching %s: %s"
msgstr "ошибка скачивания %s: %s"
#: pkg/sync/build/errors.go:9
msgid "error installing repo packages"
msgstr "ошибка установки пакетов из репозиториев"
#: pkg/sync/build/installer.go:266 pkg/sync/build/installer.go:270
msgid "error installing:"
msgstr "ошибка установки:"
#: pkg/sync/build/installer.go:233 pkg/sync/build/installer.go:237
msgid "error making: %s"
msgstr "ошибка сборки: %s"
#: pkg/sync/workdir/merge.go:24
msgid "error merging %s: %s"
msgstr "ошибка объединения %s: %s"
#: pkg/download/unified.go:59
msgid "error reading %s"
msgstr "ошибка чтения %s"
#: sync.go:36
msgid "error refreshing databases"
msgstr "ошибка обновления базы данных"
#: pkg/sync/workdir/clean.go:51 pkg/sync/workdir/merge.go:17
msgid "error resetting %s: %s"
msgstr "ошибка сброса %s: %s"
#: pkg/sync/build/errors.go:53
msgid "error updating package install reason to %s"
msgstr "ошибка изменения причины установки пакета на %s"
#: pkg/sync/build/errors.go:48
msgid "explicit"
msgstr "явно"
#: pkg/settings/errors.go:23
msgid "failed to create directory '%s': %s"
msgstr "ошибка создания каталога '%s': %s"
#: pkg/settings/config.go:281
msgid "failed to open config file '%s': %s"
msgstr "ошибка открытия файла конфигурации '%s': %s"
#: pkg/sync/srcinfo/service.go:114
msgid "failed to parse %s -- skipping: %s"
msgstr "ошибка при анализе %s --- пропуск: %s"
#: pkg/sync/srcinfo/service.go:118
msgid "failed to parse %s: %s"
msgstr "ошибка при анализе %s: %s"
#: local_install.go:77
msgid "failed to parse .SRCINFO"
msgstr "ошибка при анализе .SRCINFO"
#: pkg/settings/config.go:291
msgid "failed to read config file '%s': %s"
msgstr "не удалось прочитать файл конфигурации '%s': %s"
#: pkg/cmd/graph/main.go:46 pkg/runtime/runtime.go:73
msgid "failed to retrieve aur Cache"
msgstr "не удалось получить кэш AUR"
#: pkg/upgrade/sources.go:27
msgid "ignoring package devel upgrade (no AUR info found):"
msgstr "Игнорирование обновления пакета devel (информация об AUR не найдена):"
#: pkg/text/errors.go:8
msgid "input too long"
msgstr "ввод слишком длинный"
#: pkg/db/ialpm/alpm.go:222 pkg/dep/dep_graph.go:761
msgid "invalid number: %s"
msgstr "неверное число: %s"
#: pkg/settings/parser/parser.go:174
msgid "invalid option '%s'"
msgstr "неверная опция '%s'"
#: cmd.go:197
msgid "invalid option: '--deps' and '--explicit' may not be used together"
msgstr ""
"неверная опция: невозможно использовать опции '--deps' и '--explicit' "
"одновременно"
#: pkg/download/abs.go:22
msgid "invalid repository"
msgstr "недействительный репозиторий"
#: pkg/db/ialpm/alpm.go:227 pkg/dep/dep_graph.go:767
msgid "invalid value: %d is not between %d and %d"
msgstr "неверное значение: %d не в промежутке между %d и %d "
#: pkg/text/input.go:48
msgid "no"
msgstr "нет"
#: pkg/sync/srcinfo/pgp/keys.go:110
msgid "no keys to import"
msgstr "нет ключей для импорта"
#: pkg/query/errors.go:20
msgid "no query was executed"
msgstr "запрос не был выполнен"
#: local_install.go:66
msgid "no target directories specified"
msgstr "не выбраны целевые директории"
#: pkg/sync/build/installer.go:242
msgid "nothing to install for %s"
msgstr "нечего установить из %s"
#: pkg/settings/parser/parser.go:164
msgid "only one operation may be used at a time"
msgstr "только одна операция может быть вызвана за один раз"
#: pkg/cmd/graph/main.go:70
msgid "only one target is allowed"
msgstr "разрешена только одна цель"
#: pkg/upgrade/service.go:291
msgid "package"
msgid_plural "packages"
msgstr[0] "пакет"
msgstr[1] "пакеты"
msgstr[2] "пакеты"
msgstr[3] "пакеты"
#: print.go:187
msgid "package '%s' was not found"
msgstr "пакет '%s' не найден"
#: pkg/download/errors.go:15
msgid "package not found in AUR"
msgstr "пакет не найден в AUR"
#: pkg/download/abs.go:23
msgid "package not found in repos"
msgstr "пакет не найден в репозиториях"
#: pkg/sync/srcinfo/pgp/keys.go:100
msgid "problem importing keys"
msgstr "проблема импортирования ключей"
#: clean.go:105
msgid "removing AUR packages from cache..."
msgstr "удаление пакетов AUR из кэша..."
#: clean.go:178 pkg/sync/workdir/clean.go:41
msgid "removing untracked AUR files from cache..."
msgstr "удаление неотслеживаемых файлов AUR из кэша..."
#: pkg/sync/build/errors.go:38
msgid "the PKGDEST for %s is listed by makepkg but does not exist: %s"
msgstr "файл PKGDEST для %s указан в выводе makepkg, но не существует: %s"
#: pkg/sync/sync.go:45
msgid "there is nothing to do"
msgstr "делать больше нечего"
#: pkg/db/ialpm/alpm.go:247
msgid "unable to CreateHandle: %s"
msgstr "невозможно выполнить CreateHandle: %s"
#: cmd.go:186
msgid "unhandled operation"
msgstr "необработанная операция"
#: cmd.go:450
msgid "unknown-version"
msgstr "неизвестная версия"
#: pkg/text/input.go:47
msgid "yes"
msgstr "да"
================================================
FILE: po/ru_RU.po
================================================
#
# Translators:
# J G, 2021
# makvasm, 2021
# antsif.a, 2022
# Demir Yerli, 2022
# Антон Карасев , 2023
# Ravenso BlacK, 2024
# Victor Golovanenko , 2024
# Vladislav Zenkov, 2024
# falixfresh, 2024
# Kira Malinova, 2024
# Dancheg97 F, 2024
# Vladislav Grechannik, 2024
# ratijas, 2024
# Vladimir Yerilov, 2025
# Maxim Tyuterev, 2026
#
msgid ""
msgstr ""
"Last-Translator: Maxim Tyuterev, 2026\n"
"Language-Team: Russian (Russia) (https://app.transifex.com/yay-1/teams/123732/ru_RU/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: ru_RU\n"
"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n"
"X-Generator: xgotext\n"
#: clean.go:83
msgid ""
"\n"
"Build directory:"
msgstr ""
"\n"
"Каталог сборки:"
#: pkg/db/ialpm/alpm.go:201 pkg/dep/dep_graph.go:740
msgid ""
"\n"
"Enter a number (default=1): "
msgstr ""
"\n"
"Введите номер (по умолчанию = 1): "
#: pkg/menus/menu.go:32
msgid " (Build Files Exist)"
msgstr " (файлы сборки существуют)"
#: pkg/menus/menu.go:27
msgid " (Installed)"
msgstr " (Установлено)"
#: cmd.go:453
msgid " [Installed]"
msgstr " [Установлено]"
#: cmd.go:410 vote.go:36
msgid " there is nothing to do"
msgstr "делать нечего"
#: pkg/menus/menu.go:49
msgid "%s [A]ll [Ab]ort [I]nstalled [No]tInstalled or (1 2 3, 1-3, ^4)"
msgstr ""
"%s [A]Все [Ab]Прервать [I]Установленные [No]Неустановленные или (1 2 3, 1-3,"
" ^4)"
#: pkg/sync/build/installer.go:308
msgid "%s already made -- skipping build"
msgstr "%s уже собран --- сборка пропускается"
#: pkg/menus/edit_menu.go:57
msgid "%s is not set"
msgstr "%s не задан"
#: pkg/settings/exe/cmd_builder.go:257
msgid "%s is present."
msgstr "%s уже существует."
#: pkg/dep/dep_graph.go:460 pkg/sync/build/installer.go:305
msgid "%s is up to date -- skipping"
msgstr "%sобновлён до последней версии --- пропуск"
#: pkg/upgrade/service.go:291
msgid "%s to upgrade/install."
msgstr "%s обновить/установить."
#: pkg/upgrade/service.go:285
msgid "%s will also be installed for this operation."
msgstr "%s также будет установлен для этой операции."
#: pkg/sync/srcinfo/pgp/keys.go:124
msgid "%s, required by: %s"
msgstr "%s, требуется пакету: %s"
#: pkg/menus/diff_menu.go:49
msgid "%s: No changes -- skipping"
msgstr "%s: Нет изменений --- пропуск"
#: pkg/query/filter.go:22
msgid "%s: can't use target with option --aur -- skipping"
msgstr "%s: невозможно использовать цель с опцией --aur --- пропуск"
#: pkg/query/filter.go:17
msgid "%s: can't use target with option --repo -- skipping"
msgstr "%s: невозможно использовать цель с опцией --repo --- пропуск"
#: pkg/upgrade/sources.go:57
msgid "%s: ignoring package upgrade (%s => %s)"
msgstr "%s: пропуск обновления пакета (%s => %s)"
#: pkg/query/aur_warnings.go:46
msgid "%s: local (%s) is newer than AUR (%s)"
msgstr "%s: локальная версия (%s) новее, чем в AUR (%s)"
#: vote.go:51
msgid ""
"%s: please set AUR_USERNAME and AUR_PASSWORD environment variables for "
"voting"
msgstr ""
"пожалуйста, укажите AUR_USERNAME и AUR_PASSWORD в переменных среды для "
"возможности голосовать"
#: pkg/download/unified.go:192
msgid "(%d/%d) Downloaded PKGBUILD from ABS: %s"
msgstr "(%d/%d) Скачан PKGBUILD из ABS: %s"
#: pkg/download/aur.go:92 pkg/download/unified.go:188
msgid "(%d/%d) Downloaded PKGBUILD: %s"
msgstr "(%d/%d) Скачан PKGBUILD: %s"
#: pkg/download/aur.go:82
msgid "(%d/%d) Failed to download PKGBUILD: %s"
msgstr "(%d/%d) Не удалось загрузить PKGBUILD:"
#: pkg/sync/srcinfo/service.go:109
msgid "(%d/%d) Parsing SRCINFO: %s"
msgstr "(%d/%d) Анализ SRCINFO: %s"
#: pkg/query/types.go:103 pkg/query/types.go:72
msgid "(Installed)"
msgstr "(установлено)"
#: pkg/query/types.go:101 pkg/query/types.go:70
msgid "(Installed: %s)"
msgstr "(установлено: %s)"
#: pkg/query/types.go:61
msgid "(Orphaned)"
msgstr "(сирота в AUR)"
#: pkg/query/types.go:65
msgid "(Out-of-date: %s)"
msgstr "(устарел: %s)"
#: pkg/dep/dep_graph.go:75
msgid "AUR"
msgstr "AUR"
#: print.go:44
msgid "AUR URL"
msgstr "AUR URL"
#: pkg/menus/edit_menu.go:58
msgid "Add %s or %s to your environment variables"
msgstr "Добавьте %s или %s в переменные окружения"
#: main.go:60
msgid "Avoid running yay as root/sudo."
msgstr "Не запускайте yay от имени root/sudo."
#: pkg/dep/dep_graph.go:63
msgid "Check Dependency"
msgstr "Проверка зависимости"
#: print.go:41
msgid "Check Deps"
msgstr "Зависимости проверки"
#: pkg/upgrade/service.go:89
msgid "Checking development packages..."
msgstr "Проверка пакетов в разработке (-git, -svn и т.п.)..."
#: pkg/sync/workdir/clean.go:45
msgid "Cleaning (%d/%d): %s"
msgstr "Очистка (%d/%d): %s"
#: print.go:42
msgid "Conflicts With"
msgstr "Конфликтует с"
#: pkg/menus/clean_menu.go:62
msgid "Deleting (%d/%d): %s"
msgstr "Удаление (%d/%d): %s"
#: pkg/dep/dep_graph.go:61
msgid "Dependency"
msgstr "Зависимость"
#: print.go:38
msgid "Depends On"
msgstr "Зависит от"
#: print.go:33
msgid "Description"
msgstr "Описание"
#: pkg/menus/diff_menu.go:160
msgid "Diffs to show?"
msgstr "Показать изменения?"
#: pkg/settings/migrations.go:25
msgid "Disable 'provides' setting by default"
msgstr "Отключите настройку 'обеспечивает' по умолчанию"
#: clean.go:78
msgid "Do you want to remove ALL AUR packages from cache?"
msgstr "Вы хотите удалить все пакеты AUR из кэша?"
#: clean.go:95
msgid "Do you want to remove ALL untracked AUR files?"
msgstr "Вы хотите удалить все неотслеживаемые файлы AUR?"
#: clean.go:80
msgid "Do you want to remove all other AUR packages from cache?"
msgstr "Вы хотите удалить все остальные пакеты AUR из кэша?"
#: pkg/menus/edit_menu.go:61
msgid "Edit PKGBUILD with?"
msgstr "Отредактировать PKGBUILD?"
#: pkg/query/errors.go:13
msgid "Error during AUR search: %s\n"
msgstr "Ошибка поиска в AUR: %s\n"
#: pkg/upgrade/service.go:295
msgid "Excluding packages may cause partial upgrades and break systems"
msgstr ""
"Исключение пакетов может привести к частичным обновлениям и поломкам системы"
#: pkg/dep/dep_graph.go:60
msgid "Explicit"
msgstr "Явно"
#: print.go:91
msgid "Explicitly installed packages: %s"
msgstr "Пакеты, установленные по запросу пользователя: %s"
#: pkg/dep/dep_graph.go:437 pkg/dep/dep_graph.go:535
msgid "Failed to find AUR package for"
msgstr "Не удалось найти пакет AUR для"
#: pkg/sync/build/installer.go:120
msgid "Failed to install layer, rolling up to next layer."
msgstr "Ошибка установки слоя, переход на следующий слой."
#: pkg/sync/build/errors.go:16
msgid ""
"Failed to install the following packages. Manual intervention is required:"
msgstr ""
"Не удалось установить следующие пакеты. Необходимо ручное вмешательство:"
#: print.go:45
msgid "First Submitted"
msgstr "Впервые загружен на AUR"
#: pkg/query/aur_warnings.go:83
msgid "Flagged Out Of Date AUR Packages:"
msgstr "Пакеты AUR, помеченные как устаревшие:"
#: print.go:90
msgid "Foreign installed packages: %s"
msgstr "Установлено сторонних пакетов: %s"
#: pkg/vcs/vcs.go:144
msgid "Found git repo: %s"
msgstr "Найден git-репозиторий: %s"
#: vcs.go:72
msgid "GenDB finished. No packages were installed"
msgstr "GenDB завершён без установки пакетов"
#: print.go:36
msgid "Groups"
msgstr "Группы"
#: pkg/sync/srcinfo/pgp/keys.go:88
msgid "Import?"
msgstr "Импортировать?"
#: pkg/sync/srcinfo/pgp/keys.go:97
msgid "Importing keys with gpg..."
msgstr "Импортирование ключей с помощью gpg..."
#: print.go:46
msgid "Keywords"
msgstr "Ключевые слова"
#: print.go:47
msgid "Last Modified"
msgstr "Последнее изменение"
#: print.go:35
msgid "Licenses"
msgstr "Лицензии"
#: pkg/dep/dep_graph.go:77
msgid "Local"
msgstr "Локальный"
#: print.go:48
msgid "Maintainer"
msgstr "Сопровождающий"
#: pkg/dep/dep_graph.go:62
msgid "Make Dependency"
msgstr "Зависимость сборки"
#: print.go:40
msgid "Make Deps"
msgstr "Зависимости сборки"
#: pkg/dep/dep_graph.go:79
msgid "Missing"
msgstr "Отсутствующие"
#: pkg/query/aur_warnings.go:75
msgid "Missing AUR Debug Packages:"
msgstr "Отсутствующие в AUR пакеты для отладки:"
#: print.go:31
msgid "Name"
msgstr "Название"
#: pkg/dep/dep_graph.go:442 pkg/dep/dep_graph.go:564
msgid "No AUR package found for"
msgstr "Не найден пакет AUR для"
#: pkg/dep/dep_graph.go:564
msgid "required by"
msgstr "нужен для"
#: pkg/dep/dep_graph.go:182
msgid "No package found for"
msgstr "Не найден пакет для"
#: print.go:225
msgid "None"
msgstr "Нет"
#: print.go:39
msgid "Optional Deps"
msgstr "Факультативные зависимости"
#: pkg/query/aur_warnings.go:79
msgid "Orphan (unmaintained) AUR Packages:"
msgstr "Брошенные (без сопровождающего) пакеты AUR:"
#: print.go:53 print.go:55
msgid "Out-of-date"
msgstr "Устарел"
#: pkg/sync/srcinfo/pgp/keys.go:115
msgid "PGP keys need importing:"
msgstr "Ключи PGP, требующие импорта:"
#: pkg/sync/workdir/preparer.go:252
msgid "PKGBUILD up to date, skipping download: %s"
msgstr "Актуальный PKGBUILD, пропуск загрузки"
#: pkg/menus/edit_menu.go:130
msgid "PKGBUILDs to edit?"
msgstr "Отредактировать PKGBUILD?"
#: print.go:61
msgid "Package Base"
msgstr "Группа пакетов"
#: print.go:60
msgid "Package Base ID"
msgstr "ID группы пакетов"
#: pkg/query/aur_warnings.go:71
msgid "Packages not in AUR:"
msgstr "Пакет не найден в AUR:"
#: pkg/menus/clean_menu.go:54
msgid "Packages to cleanBuild?"
msgstr "Пакеты, для которых требуется очистить кэш сборки?"
#: pkg/dep/dep_graph.go:202
msgid "Packages to exclude"
msgstr "Пакеты для исключения"
#: pkg/upgrade/service.go:294
msgid "Packages to exclude: (eg: \"1 2 3\", \"1-3\", \"^4\" or repo name)"
msgstr "Пакеты для исключения: (пример: \"1 2 3\", \"1-3\", \"^4\" или имя в репозитории)"
#: cmd.go:392
msgid "Packages to install (eg: 1 2 3, 1-3 or ^4)"
msgstr "Пакеты для установки (пример: 1 2 3, 1-3 или ^4)"
#: print.go:49
msgid "Popularity"
msgstr "Популярность"
#: pkg/menus/diff_menu.go:172 pkg/menus/edit_menu.go:143
msgid "Proceed with install?"
msgstr "Продолжить установку?"
#: print.go:37
msgid "Provides"
msgstr "Обеспечивает"
#: pkg/sync/workdir/preparer.go:125
msgid "Remove make dependencies after install?"
msgstr "Удалить зависимости для сборки после установки?"
#: print.go:43
msgid "Replaces"
msgstr "Заменяет"
#: pkg/db/ialpm/alpm.go:191 print.go:30
msgid "Repository"
msgstr "Репозиторий"
#: pkg/dep/dep_graph.go:730
msgid "Repository AUR"
msgstr "Репозиторий AUR"
#: pkg/dep/dep_graph.go:78
msgid "SRCINFO"
msgstr "SRCINFO"
#: pkg/upgrade/service.go:71
msgid "Searching AUR for updates..."
msgstr "Поиск обновлений в AUR..."
#: pkg/upgrade/service.go:159
msgid "Searching databases for updates..."
msgstr "Поиск обновлений в базе данных..."
#: pkg/query/query_builder.go:214
msgid "Showing repo packages only"
msgstr "Показываются только пакеты из репозиториев"
#: print.go:95
msgid "Size of pacman cache %s: %s"
msgstr "Размер кэша \"pacman\""
#: print.go:98
msgid "Size of yay cache %s: %s"
msgstr "Размер кэша \"yay\""
#: print.go:62
msgid "Snapshot URL"
msgstr "URL архива"
#: pkg/dep/dep_graph.go:76
msgid "Sync"
msgstr "Синхронизация"
#: print.go:100
msgid "Ten biggest packages:"
msgstr "10 самых объёмных пакетов:"
#: pkg/sync/sync.go:124
msgid "The following packages are not compatible with your architecture:"
msgstr "Следующие пакеты несовместимы с архитектурой вашего процессора:"
#: pkg/db/ialpm/alpm.go:179 pkg/dep/dep_graph.go:726
msgid "There are %[1]d providers available for %[2]s:"
msgstr "%[2]s доступен в %[1]d источниках:"
#: pkg/settings/exe/cmd_builder.go:258
msgid "There may be another Pacman instance running. Waiting..."
msgstr "Возможно, запущен другой экземпляр Pacman. Ожидание..."
#: print.go:92
msgid "Total Size occupied by packages: %s"
msgstr "Суммарный размер, занятый пакетами: %s"
#: print.go:89
msgid "Total installed packages: %s"
msgstr "Всего установлено пакетов: %s"
#: pkg/sync/sync.go:132
msgid "Try to build them anyway?"
msgstr "Попытаться собрать их, несмотря на несовместимость?"
#: print.go:34
msgid "URL"
msgstr "URL"
#: clean.go:194 pkg/menus/clean_menu.go:65 pkg/menus/clean_menu.go:71
msgid "Unable to clean:"
msgstr "Невозможно очистить:"
#: get.go:42 get.go:74
msgid "Unable to find the following packages:"
msgstr "Не удалось найти следующие пакеты:"
#: vote.go:20
msgid "Unable to handle package vote for: %s. err: %s"
msgstr "Не удалось обработать голосование за: %s. ошибка: %s"
#: clean.go:170
msgid "Unable to remove %s: %s"
msgstr "Не удается удалить %s: %s"
#: print.go:32
msgid "Version"
msgstr "Версия"
#: print.go:50
msgid "Votes"
msgstr "Голосов"
#: print.go:87
msgid "Yay version v%s"
msgstr "Yay версии v%s"
#: pkg/menus/menu.go:49
msgid "[N]one"
msgstr "[N]Нет"
#: pkg/settings/errors.go:29
msgid "aborting due to user"
msgstr "прерывание по запросу пользователя"
#: pkg/settings/parser/parser.go:608
msgid "argument '-' specified without input on stdin"
msgstr "аргумент '-' задан без ввода из stdin"
#: local_install.go:26
msgid "cannot find PKGBUILD and .SRCINFO in directory"
msgstr "не удалось найти PKGBUILD и .SRCINFO в каталоге"
#: pkg/sync/build/pkg_archive.go:148
msgid "cannot find package name: %v"
msgstr "невозможно найти пакет: %v"
#: pkg/sync/build/errors.go:30
msgid "could not find PKGDEST for: %s"
msgstr "невозможно найти файл PKGDEST для: %s"
#: errors.go:9
msgid "could not find all required packages"
msgstr "не удалось найти все необходимые пакеты"
#: pkg/sync/build/errors.go:61
msgid "could not find any package archives listed in %s"
msgstr "Не удалось найти архивы пакетов в %s"
#: pkg/sync/build/errors.go:50 pkg/upgrade/service.go:286
msgid "dependency"
msgstr "зависимость"
#: pkg/vcs/vcs.go:100 pkg/vcs/vcs.go:96
msgid "devel check for package failed: '%s' encountered an error"
msgstr "Проверка для пакета '%s' не закончилась успехом"
#: pkg/menus/edit_menu.go:110
msgid "editor did not exit successfully, aborting: %s"
msgstr "выход из редактора был неуспешным, прерывание: %s"
#: pkg/sync/workdir/aur_source.go:24
msgid "error downloading sources: %s"
msgstr "ошибка загрузки исходников: %s"
#: pkg/download/errors.go:25
msgid "error fetching %s: %s"
msgstr "ошибка получения %s: %s"
#: pkg/sync/build/errors.go:9
msgid "error installing repo packages"
msgstr "ошибка установки пакетов из репозиториев"
#: pkg/sync/build/installer.go:266 pkg/sync/build/installer.go:270
msgid "error installing:"
msgstr "ошибка установки:"
#: pkg/sync/build/installer.go:233 pkg/sync/build/installer.go:237
msgid "error making: %s"
msgstr "ошибка сборки: %s"
#: pkg/sync/workdir/merge.go:24
msgid "error merging %s: %s"
msgstr "ошибка объединения %s: %s"
#: pkg/download/unified.go:59
msgid "error reading %s"
msgstr "ошибка чтения %s"
#: sync.go:36
msgid "error refreshing databases"
msgstr "ошибка обновления базы данных"
#: pkg/sync/workdir/clean.go:51 pkg/sync/workdir/merge.go:17
msgid "error resetting %s: %s"
msgstr "ошибка сброса %s: %s"
#: pkg/sync/build/errors.go:53
msgid "error updating package install reason to %s"
msgstr "ошибка изменения причины установки пакета на %s"
#: pkg/sync/build/errors.go:48
msgid "explicit"
msgstr "явно"
#: pkg/settings/errors.go:23
msgid "failed to create directory '%s': %s"
msgstr "ошибка создания каталога '%s': %s"
#: pkg/settings/config.go:281
msgid "failed to open config file '%s': %s"
msgstr "ошибка открытия файла конфигурации '%s': %s"
#: pkg/sync/srcinfo/service.go:114
msgid "failed to parse %s -- skipping: %s"
msgstr "ошибка при анализе %s --- пропуск: %s"
#: pkg/sync/srcinfo/service.go:118
msgid "failed to parse %s: %s"
msgstr "ошибка при анализе %s: %s"
#: local_install.go:77
msgid "failed to parse .SRCINFO"
msgstr "ошибка при анализе .SRCINFO"
#: pkg/settings/config.go:291
msgid "failed to read config file '%s': %s"
msgstr "ошибка чтения файла конфигурации '%s': %s"
#: pkg/cmd/graph/main.go:46 pkg/runtime/runtime.go:73
msgid "failed to retrieve aur Cache"
msgstr "не удалось получить кэш AUR"
#: pkg/upgrade/sources.go:27
msgid "ignoring package devel upgrade (no AUR info found):"
msgstr "Игнорирование обновления пакета devel (информация об AUR не найдена):"
#: pkg/text/errors.go:8
msgid "input too long"
msgstr "ввод слишком длинный"
#: pkg/db/ialpm/alpm.go:222 pkg/dep/dep_graph.go:761
msgid "invalid number: %s"
msgstr "некорректный номер: %s"
#: pkg/settings/parser/parser.go:174
msgid "invalid option '%s'"
msgstr "некорректный параметр '%s'"
#: cmd.go:197
msgid "invalid option: '--deps' and '--explicit' may not be used together"
msgstr ""
"некорректный параметр: '--deps' и '--explicit' не могут быть использованы "
"вместе"
#: pkg/download/abs.go:22
msgid "invalid repository"
msgstr "недействительный репозиторий"
#: pkg/db/ialpm/alpm.go:227 pkg/dep/dep_graph.go:767
msgid "invalid value: %d is not between %d and %d"
msgstr "некорректное значение: %d не лежит между %d и %d"
#: pkg/text/input.go:48
msgid "no"
msgstr "нет"
#: pkg/sync/srcinfo/pgp/keys.go:110
msgid "no keys to import"
msgstr "нет ключей для импорта"
#: pkg/query/errors.go:20
msgid "no query was executed"
msgstr "запрос не был выполнен"
#: local_install.go:66
msgid "no target directories specified"
msgstr "не выбраны целевые директории"
#: pkg/sync/build/installer.go:242
msgid "nothing to install for %s"
msgstr "нечего установить из %s"
#: pkg/settings/parser/parser.go:164
msgid "only one operation may be used at a time"
msgstr "только одна операция может быть вызвана за один раз"
#: pkg/cmd/graph/main.go:70
msgid "only one target is allowed"
msgstr "разрешена только одна цель"
#: pkg/upgrade/service.go:291
msgid "package"
msgid_plural "packages"
msgstr[0] "пакет"
msgstr[1] "пакеты"
msgstr[2] "пакеты"
msgstr[3] "пакеты"
#: print.go:187
msgid "package '%s' was not found"
msgstr "пакет '%s' не найден"
#: pkg/download/errors.go:15
msgid "package not found in AUR"
msgstr "пакет не найден в AUR"
#: pkg/download/abs.go:23
msgid "package not found in repos"
msgstr "пакет не найден в репозиториях"
#: pkg/sync/srcinfo/pgp/keys.go:100
msgid "problem importing keys"
msgstr "проблема импортирования ключей"
#: clean.go:105
msgid "removing AUR packages from cache..."
msgstr "удаление пакетов AUR из кэша..."
#: clean.go:178 pkg/sync/workdir/clean.go:41
msgid "removing untracked AUR files from cache..."
msgstr "удаление неотслеживаемых файлов AUR из кэша..."
#: pkg/sync/build/errors.go:38
msgid "the PKGDEST for %s is listed by makepkg but does not exist: %s"
msgstr "файл PKGDEST для %s означен в выводе makepkg, но не существует: %s"
#: pkg/sync/sync.go:45
msgid "there is nothing to do"
msgstr "делать больше нечего"
#: pkg/db/ialpm/alpm.go:247
msgid "unable to CreateHandle: %s"
msgstr "невозможно выполнить CreateHandle: %s"
#: cmd.go:186
msgid "unhandled operation"
msgstr "неизвестная операция"
#: cmd.go:450
msgid "unknown-version"
msgstr "неизвестная версия"
#: pkg/text/input.go:47
msgid "yes"
msgstr "да"
================================================
FILE: po/sk.po
================================================
#
# Translators:
# Matej Mrenica, 2022
# Peter Cyprich, 2024
# Michal Fusatý, 2024
#
msgid ""
msgstr ""
"Last-Translator: Michal Fusatý, 2024\n"
"Language-Team: Slovak (https://app.transifex.com/yay-1/teams/123732/sk/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: sk\n"
"Plural-Forms: nplurals=4; plural=(n % 1 == 0 && n == 1 ? 0 : n % 1 == 0 && n >= 2 && n <= 4 ? 1 : n % 1 != 0 ? 2: 3);\n"
"X-Generator: xgotext\n"
#: clean.go:83
msgid ""
"\n"
"Build directory:"
msgstr ""
"\n"
"Adresár zostavenia:"
#: pkg/db/ialpm/alpm.go:201 pkg/dep/dep_graph.go:740
msgid ""
"\n"
"Enter a number (default=1): "
msgstr ""
"\n"
"Zadajte číslo (predvolené=1):"
#: pkg/menus/menu.go:32
msgid " (Build Files Exist)"
msgstr "(Súbory na zostavenie existujú)"
#: pkg/menus/menu.go:27
msgid " (Installed)"
msgstr "(Nainštalované)"
#: cmd.go:453
msgid " [Installed]"
msgstr "[Nainštalované]"
#: cmd.go:410 vote.go:36
msgid " there is nothing to do"
msgstr "nie je čo robiť"
#: pkg/menus/menu.go:49
msgid "%s [A]ll [Ab]ort [I]nstalled [No]tInstalled or (1 2 3, 1-3, ^4)"
msgstr ""
"%s [A]Všetko [Ab]Zrušiť [I]Nainštalované [No]Nenainštalované alebo (1 2 3, "
"1-3, ^4)"
#: pkg/sync/build/installer.go:308
msgid "%s already made -- skipping build"
msgstr "%s už vytvorené -- preskočenie zostavenia"
#: pkg/menus/edit_menu.go:57
msgid "%s is not set"
msgstr "%s nie je nastavené"
#: pkg/settings/exe/cmd_builder.go:257
msgid "%s is present."
msgstr "%s je prítomný."
#: pkg/dep/dep_graph.go:460 pkg/sync/build/installer.go:305
msgid "%s is up to date -- skipping"
msgstr "%s je aktuálny -- preskakujem"
#: pkg/upgrade/service.go:291
msgid "%s to upgrade/install."
msgstr "%s na aktualizáciu/inštaláciu."
#: pkg/upgrade/service.go:285
msgid "%s will also be installed for this operation."
msgstr "%sbude tiež nainštalovaný pre túto operáciu."
#: pkg/sync/srcinfo/pgp/keys.go:124
msgid "%s, required by: %s"
msgstr "%s, vyžaduje: %s"
#: pkg/menus/diff_menu.go:49
msgid "%s: No changes -- skipping"
msgstr "%s Žiadne zmeny -- preskakujem"
#: pkg/query/filter.go:22
msgid "%s: can't use target with option --aur -- skipping"
msgstr "%snemožno použiť cieľ s voľbou --aur -- preskakujem"
#: pkg/query/filter.go:17
msgid "%s: can't use target with option --repo -- skipping"
msgstr "%s nemožno použiť cieľ s voľbou --repo -- preskakujem"
#: pkg/upgrade/sources.go:57
msgid "%s: ignoring package upgrade (%s => %s)"
msgstr "%s ignorujem aktualizáciu balíčka (%s => %s)"
#: pkg/query/aur_warnings.go:46
msgid "%s: local (%s) is newer than AUR (%s)"
msgstr "%s lokálne (%s) je novšie ako AUR (%s)"
#: vote.go:51
msgid ""
"%s: please set AUR_USERNAME and AUR_PASSWORD environment variables for "
"voting"
msgstr ""
"%s prosím nastavte premenné prostredia AUR_USERNAME (používateľské meno AUR)"
" a AUR_PASSWORD (heslo AUR) pre hlasovanie"
#: pkg/download/unified.go:192
msgid "(%d/%d) Downloaded PKGBUILD from ABS: %s"
msgstr "(%d/%d) Stiahnuté PKGBUILD z ABS: %s"
#: pkg/download/aur.go:92 pkg/download/unified.go:188
msgid "(%d/%d) Downloaded PKGBUILD: %s"
msgstr "(%d/%d) Stiahnuté PKGBUILD: %s"
#: pkg/download/aur.go:82
msgid "(%d/%d) Failed to download PKGBUILD: %s"
msgstr "(%d/%d) Nepodarilo sa stiahnuť PKGBUILD: %s"
#: pkg/sync/srcinfo/service.go:109
msgid "(%d/%d) Parsing SRCINFO: %s"
msgstr "(%d/%d) Analýza SRCINFO: %s"
#: pkg/query/types.go:103 pkg/query/types.go:72
msgid "(Installed)"
msgstr "(Nainštalované)"
#: pkg/query/types.go:101 pkg/query/types.go:70
msgid "(Installed: %s)"
msgstr "(Nainštalované: %s)"
#: pkg/query/types.go:61
msgid "(Orphaned)"
msgstr "(Osirotené)"
#: pkg/query/types.go:65
msgid "(Out-of-date: %s)"
msgstr "(Zastarané: %s)"
#: pkg/dep/dep_graph.go:75
msgid "AUR"
msgstr "AUR"
#: print.go:44
msgid "AUR URL"
msgstr "AUR URL"
#: pkg/menus/edit_menu.go:58
msgid "Add %s or %s to your environment variables"
msgstr "Pridajte %s or %s do vašich premenných prostredia"
#: main.go:60
msgid "Avoid running yay as root/sudo."
msgstr "Vyhnite sa spúšťaniu yay ako root/sudo."
#: pkg/dep/dep_graph.go:63
msgid "Check Dependency"
msgstr "Skontrolovať závislosti"
#: print.go:41
msgid "Check Deps"
msgstr "Skontrolovať závislosti"
#: pkg/upgrade/service.go:89
msgid "Checking development packages..."
msgstr "Kontrolujem balíčky vývoja..."
#: pkg/sync/workdir/clean.go:45
msgid "Cleaning (%d/%d): %s"
msgstr "Čiestenie (%d/%d): %s"
#: print.go:42
msgid "Conflicts With"
msgstr "Konflikty s"
#: pkg/menus/clean_menu.go:62
msgid "Deleting (%d/%d): %s"
msgstr "Mazanie (%d/%d): %s"
#: pkg/dep/dep_graph.go:61
msgid "Dependency"
msgstr "Závislosť"
#: print.go:38
msgid "Depends On"
msgstr "Závisí na"
#: print.go:33
msgid "Description"
msgstr "Popis"
#: pkg/menus/diff_menu.go:160
msgid "Diffs to show?"
msgstr "Rozdiely na zobrazenie?"
#: pkg/settings/migrations.go:25
msgid "Disable 'provides' setting by default"
msgstr "Predvolene zrušiť nastavenie 'provides'"
#: clean.go:78
msgid "Do you want to remove ALL AUR packages from cache?"
msgstr "Chcete odstrániť VŠETKY balíky AUR z vyrovnávacej pamäte?"
#: clean.go:95
msgid "Do you want to remove ALL untracked AUR files?"
msgstr "Chcete odstrániť VŠETKY nesledované súbory AUR?"
#: clean.go:80
msgid "Do you want to remove all other AUR packages from cache?"
msgstr "Chcete odstrániť všetky ostatné balíky AUR z vyrovnávacej pamäte?"
#: pkg/menus/edit_menu.go:61
msgid "Edit PKGBUILD with?"
msgstr "Upraviť PKGBUILD s?"
#: pkg/query/errors.go:13
msgid "Error during AUR search: %s\n"
msgstr "Chyba počas vyhľadávania AUR: %s\n"
#: pkg/upgrade/service.go:295
msgid "Excluding packages may cause partial upgrades and break systems"
msgstr ""
"Vynechanie balíčkov môže spôsobiť čiastočné vylepšienia a zničiť systém"
#: pkg/dep/dep_graph.go:60
msgid "Explicit"
msgstr "Explicitne"
#: print.go:91
msgid "Explicitly installed packages: %s"
msgstr "Explicitne nainštalované balíky: %s"
#: pkg/dep/dep_graph.go:437 pkg/dep/dep_graph.go:535
msgid "Failed to find AUR package for"
msgstr "Nepodarilo sa nájsť AUR balíček "
#: pkg/sync/build/installer.go:120
msgid "Failed to install layer, rolling up to next layer."
msgstr "Nepodarilo sa nainštalovať vrstvu, presúvam sa na ďalšiu vrstvu"
#: pkg/sync/build/errors.go:16
msgid ""
"Failed to install the following packages. Manual intervention is required:"
msgstr ""
"Nepodarilo sa nainštalovať nasledovné balíčky. Je potrebná manuálna "
"interakcia: "
#: print.go:45
msgid "First Submitted"
msgstr "Prvý krát predložené"
#: pkg/query/aur_warnings.go:83
msgid "Flagged Out Of Date AUR Packages:"
msgstr "Balíčky AUR označené ako neaktuálne:"
#: print.go:90
msgid "Foreign installed packages: %s"
msgstr "Cudzie nainšťalované balíčky: %s"
#: pkg/vcs/vcs.go:144
msgid "Found git repo: %s"
msgstr "Nájdený git repozitár: %s"
#: vcs.go:72
msgid "GenDB finished. No packages were installed"
msgstr "GenDB dokončené. Žiadne balíčky neboli nainštalované"
#: print.go:36
msgid "Groups"
msgstr "Skupiny"
#: pkg/sync/srcinfo/pgp/keys.go:88
msgid "Import?"
msgstr "Importovať?"
#: pkg/sync/srcinfo/pgp/keys.go:97
msgid "Importing keys with gpg..."
msgstr "Importovanie kľúčov pomocou gpg..."
#: print.go:46
msgid "Keywords"
msgstr "Kľúčové slová"
#: print.go:47
msgid "Last Modified"
msgstr "Naposledy upravené"
#: print.go:35
msgid "Licenses"
msgstr "Licencie"
#: pkg/dep/dep_graph.go:77
msgid "Local"
msgstr "Lokálne"
#: print.go:48
msgid "Maintainer"
msgstr "Správca"
#: pkg/dep/dep_graph.go:62
msgid "Make Dependency"
msgstr "Spraviť závislosť"
#: print.go:40
msgid "Make Deps"
msgstr "Spraviť závislosť"
#: pkg/dep/dep_graph.go:79
msgid "Missing"
msgstr "Chýbajúce"
#: pkg/query/aur_warnings.go:75
msgid "Missing AUR Debug Packages:"
msgstr "Chýbajúce AUR balíčky na ladenie: "
#: print.go:31
msgid "Name"
msgstr "Názov"
#: pkg/dep/dep_graph.go:442 pkg/dep/dep_graph.go:548
msgid "No AUR package found for"
msgstr "Nenašiel sa AUR balíček pre "
#: pkg/dep/dep_graph.go:182
msgid "No package found for"
msgstr "Nenašiel sa balíček pre "
#: print.go:225
msgid "None"
msgstr "Žiadne"
#: print.go:39
msgid "Optional Deps"
msgstr "Voliteľné závislosti"
#: pkg/query/aur_warnings.go:79
msgid "Orphan (unmaintained) AUR Packages:"
msgstr "Osirotené (neudržiavané) balíčky AUR: "
#: print.go:53 print.go:55
msgid "Out-of-date"
msgstr "Zastarané"
#: pkg/sync/srcinfo/pgp/keys.go:115
msgid "PGP keys need importing:"
msgstr "PGP kľúče potrebujú importovať:"
#: pkg/sync/workdir/preparer.go:252
msgid "PKGBUILD up to date, skipping download: %s"
msgstr "PKGBUILD aktuálne, preskakujem sťahovanie: %s"
#: pkg/menus/edit_menu.go:130
msgid "PKGBUILDs to edit?"
msgstr "PKGBUILDy na editáciu?"
#: print.go:61
msgid "Package Base"
msgstr "Základ balíčka"
#: print.go:60
msgid "Package Base ID"
msgstr "ID Základu balíčka"
#: pkg/query/aur_warnings.go:71
msgid "Packages not in AUR:"
msgstr "Balíčky, ktoré nie sú v AUR:"
#: pkg/menus/clean_menu.go:54
msgid "Packages to cleanBuild?"
msgstr "Balíčky, ktoré vybudovať nanovo?"
#: pkg/dep/dep_graph.go:202
msgid "Packages to exclude"
msgstr "Balíčky, ktoré vynechať"
#: pkg/upgrade/service.go:294
msgid "Packages to exclude: (eg: \"1 2 3\", \"1-3\", \"^4\" or repo name)"
msgstr ""
"Balíčky, ktoré vynechať: (napr.: \"1 2 3\", \"1-3\" \"^4\" alebo názov "
"repozitára)"
#: cmd.go:392
msgid "Packages to install (eg: 1 2 3, 1-3 or ^4)"
msgstr "Balíčky na inštaláciu (napr: 1 2 3, 1-3 or ^4)"
#: print.go:49
msgid "Popularity"
msgstr "Popularita"
#: pkg/menus/diff_menu.go:172 pkg/menus/edit_menu.go:143
msgid "Proceed with install?"
msgstr "Pokračovať s inštaláciou?"
#: print.go:37
msgid "Provides"
msgstr "Poskytuje"
#: pkg/sync/workdir/preparer.go:125
msgid "Remove make dependencies after install?"
msgstr "Odstrániť make závislosti po inštalácií?"
#: print.go:43
msgid "Replaces"
msgstr "Nahrádza"
#: pkg/db/ialpm/alpm.go:191 print.go:30
msgid "Repository"
msgstr "Repozitár"
#: pkg/dep/dep_graph.go:730
msgid "Repository AUR"
msgstr "Repozitár AUR"
#: pkg/dep/dep_graph.go:78
msgid "SRCINFO"
msgstr "SRCINFO"
#: pkg/upgrade/service.go:71
msgid "Searching AUR for updates..."
msgstr "Vyhľadávanie aktualizácií v AUR..."
#: pkg/upgrade/service.go:159
msgid "Searching databases for updates..."
msgstr "Vyhľadávanie aktualizácií v databázach..."
#: pkg/query/query_builder.go:214
msgid "Showing repo packages only"
msgstr "Zobrazenie iba repo balíčkov"
#: print.go:95
msgid "Size of pacman cache %s: %s"
msgstr "Veľkosť pacman cache %s: %s"
#: print.go:98
msgid "Size of yay cache %s: %s"
msgstr "Veľkosť yay cache %s: %s"
#: print.go:62
msgid "Snapshot URL"
msgstr "URL Snapshotu"
#: pkg/dep/dep_graph.go:76
msgid "Sync"
msgstr "Synchronizácia"
#: print.go:100
msgid "Ten biggest packages:"
msgstr "Desať najväčších balíčkov:"
#: pkg/sync/sync.go:124
msgid "The following packages are not compatible with your architecture:"
msgstr "Nasledovné balíčky nie sú kompatibilné s vašou architektúrou: "
#: pkg/db/ialpm/alpm.go:179 pkg/dep/dep_graph.go:726
msgid "There are %[1]d providers available for %[2]s:"
msgstr ""
"Existuje/existujú %d poskytovateľ/poskytovatelia/poskytovateľov pre %s: "
#: pkg/settings/exe/cmd_builder.go:258
msgid "There may be another Pacman instance running. Waiting..."
msgstr "Môže byť spustená ďalšia inštancia Pacmana. Čakanie..."
#: print.go:92
msgid "Total Size occupied by packages: %s"
msgstr "Celková veľkosť obsadená balíčkami: %s"
#: print.go:89
msgid "Total installed packages: %s"
msgstr "Celkový počet nainštalovaných balíčkov: %s"
#: pkg/sync/sync.go:132
msgid "Try to build them anyway?"
msgstr "Pokúsiť sa ich zostaviť aj tak?"
#: print.go:34
msgid "URL"
msgstr "URL"
#: clean.go:194 pkg/menus/clean_menu.go:65 pkg/menus/clean_menu.go:71
msgid "Unable to clean:"
msgstr "Nepodarilo sa vyčistiť:"
#: get.go:42 get.go:74
msgid "Unable to find the following packages:"
msgstr "Nie je možné nájsť nasledujúce balíčky:"
#: vote.go:20
msgid "Unable to handle package vote for: %s. err: %s"
msgstr "Nie je možné spracovať hlasovanie o balíku pre: %s. err: %s"
#: clean.go:170
msgid "Unable to remove %s: %s"
msgstr "Nepodarilo sa odstrániť %s: %s"
#: print.go:32
msgid "Version"
msgstr "Verzia"
#: print.go:50
msgid "Votes"
msgstr "Hlasy"
#: print.go:87
msgid "Yay version v%s"
msgstr "Verzia Yay v%s"
#: pkg/menus/menu.go:49
msgid "[N]one"
msgstr "[N]Žiadne"
#: pkg/settings/errors.go:29
msgid "aborting due to user"
msgstr "prerušenie kvôli používateľovi"
#: pkg/settings/parser/parser.go:608
msgid "argument '-' specified without input on stdin"
msgstr "argument \"-\" špecifikovaný bez vstupu alebo štandardného vstupu"
#: local_install.go:26
msgid "cannot find PKGBUILD and .SRCINFO in directory"
msgstr "nepodarilo sa nájsť PKGBUILD a .SRCINFO v adresári"
#: pkg/sync/build/pkg_archive.go:148
msgid "cannot find package name: %v"
msgstr "nepodarilo sa nájsť názov balíčku: %v"
#: pkg/sync/build/errors.go:30
msgid "could not find PKGDEST for: %s"
msgstr "nemožno nájsť PKGDEST pre: %s"
#: errors.go:9
msgid "could not find all required packages"
msgstr "nemožno nájsť všetky potrebné balíčky"
#: pkg/sync/build/errors.go:61
msgid "could not find any package archives listed in %s"
msgstr "nemožno nájst žiadne archívy balíčkov uvedené v %s"
#: pkg/sync/build/errors.go:50 pkg/upgrade/service.go:286
msgid "dependency"
msgstr "závislosť"
#: pkg/vcs/vcs.go:100 pkg/vcs/vcs.go:96
msgid "devel check for package failed: '%s' encountered an error"
msgstr "nepodarilo sa skontrolovať vývoj pre balíček: '%s' postihla chyba"
#: pkg/menus/edit_menu.go:110
msgid "editor did not exit successfully, aborting: %s"
msgstr "editor nebol úspešne ukončený, ruším: %s"
#: pkg/sync/workdir/aur_source.go:24
msgid "error downloading sources: %s"
msgstr "chyba pri sťahovaní zdrojov: %s"
#: pkg/download/errors.go:25
msgid "error fetching %s: %s"
msgstr "načítanie chýb %s: %s"
#: pkg/sync/build/errors.go:9
msgid "error installing repo packages"
msgstr "chyba pri inštalácií balíčkov z repozitára"
#: pkg/sync/build/installer.go:266 pkg/sync/build/installer.go:270
msgid "error installing:"
msgstr "chyba pri inštalovaní:"
#: pkg/sync/build/installer.go:233 pkg/sync/build/installer.go:237
msgid "error making: %s"
msgstr "chyba vytvárania: %s"
#: pkg/sync/workdir/merge.go:24
msgid "error merging %s: %s"
msgstr "chyba pri zlúčení %s: %s"
#: pkg/download/unified.go:59
msgid "error reading %s"
msgstr "chyba čítania %s"
#: sync.go:36
msgid "error refreshing databases"
msgstr "chyba obnovenia databáz"
#: pkg/sync/workdir/clean.go:51 pkg/sync/workdir/merge.go:17
msgid "error resetting %s: %s"
msgstr "chyba resetovania %s: %s"
#: pkg/sync/build/errors.go:53
msgid "error updating package install reason to %s"
msgstr "chyba pri aktualizácii dôvodu inštalácie balíka na %s"
#: pkg/sync/build/errors.go:48
msgid "explicit"
msgstr "explicitné"
#: pkg/settings/errors.go:23
msgid "failed to create directory '%s': %s"
msgstr "nepodarilo sa vytvoriť adresár '%s': %s"
#: pkg/settings/config.go:281
msgid "failed to open config file '%s': %s"
msgstr "nepodarilo sa otvoriť konfiguračný súbor '%s': %s"
#: pkg/sync/srcinfo/service.go:114
msgid "failed to parse %s -- skipping: %s"
msgstr "nepodarilo sa analyzovať %s -- preskakujem: %s"
#: pkg/sync/srcinfo/service.go:118
msgid "failed to parse %s: %s"
msgstr "nepodarilo sa analyzovať %s: %s"
#: local_install.go:77
msgid "failed to parse .SRCINFO"
msgstr "nepodarilo sa analyzovať .SRCINFO"
#: pkg/settings/config.go:291
msgid "failed to read config file '%s': %s"
msgstr "nepodarilo sa prečítať konfiguračný súbor '%s': %s"
#: pkg/cmd/graph/main.go:46 pkg/runtime/runtime.go:73
msgid "failed to retrieve aur Cache"
msgstr "nepodarilo sa získať aur cache"
#: pkg/upgrade/sources.go:27
msgid "ignoring package devel upgrade (no AUR info found):"
msgstr "ignorujem aktualizáciu balíčka vývoja (nenašli sa AUR informácie):"
#: pkg/text/errors.go:8
msgid "input too long"
msgstr "vstup príliš dlhý"
#: pkg/db/ialpm/alpm.go:222 pkg/dep/dep_graph.go:761
msgid "invalid number: %s"
msgstr "neplatné číslo: %s"
#: pkg/settings/parser/parser.go:174
msgid "invalid option '%s'"
msgstr "neplatná možnosť '%s'"
#: cmd.go:197
msgid "invalid option: '--deps' and '--explicit' may not be used together"
msgstr "neplatná možnosť: '--deps' a '--explicit' nemôže byť použité spolu"
#: pkg/download/abs.go:22
msgid "invalid repository"
msgstr "neplatný repozitár"
#: pkg/db/ialpm/alpm.go:227 pkg/dep/dep_graph.go:767
msgid "invalid value: %d is not between %d and %d"
msgstr "neplatná hodnota: %d nie je mezdi %d a %d"
#: pkg/text/input.go:48
msgid "no"
msgstr "nie"
#: pkg/sync/srcinfo/pgp/keys.go:110
msgid "no keys to import"
msgstr "žiadne kľúče na importovanie"
#: pkg/query/errors.go:20
msgid "no query was executed"
msgstr "nebola vykonaná žiadna požiadavka"
#: local_install.go:66
msgid "no target directories specified"
msgstr "nie sú špecifikované žiadne adresáre"
#: pkg/sync/build/installer.go:242
msgid "nothing to install for %s"
msgstr "nie je čo inštalovať pre %s"
#: pkg/settings/parser/parser.go:164
msgid "only one operation may be used at a time"
msgstr "naraz môže byť použitá iba jedna operácia"
#: pkg/cmd/graph/main.go:70
msgid "only one target is allowed"
msgstr "je povolený iba jeden cieľ"
#: pkg/upgrade/service.go:291
msgid "package"
msgid_plural "packages"
msgstr[0] "balíček"
msgstr[1] "balíčkov"
msgstr[2] "balíčkov"
msgstr[3] "balíčky"
#: print.go:187
msgid "package '%s' was not found"
msgstr "balíček '%s' sa nenašiel"
#: pkg/download/errors.go:15
msgid "package not found in AUR"
msgstr "balíček sa nenašiel v AUR"
#: pkg/download/abs.go:23
msgid "package not found in repos"
msgstr "balíček sa nenašiel v repozitároch"
#: pkg/sync/srcinfo/pgp/keys.go:100
msgid "problem importing keys"
msgstr "problém s importom kľúčov"
#: clean.go:105
msgid "removing AUR packages from cache..."
msgstr "odstraňujem balíčky AUR z cache..."
#: clean.go:178 pkg/sync/workdir/clean.go:41
msgid "removing untracked AUR files from cache..."
msgstr "odstraňujem nesledované AUR balíčky z cache..."
#: pkg/sync/build/errors.go:38
msgid "the PKGDEST for %s is listed by makepkg but does not exist: %s"
msgstr "PKGDEST pre %s je uvedený v makepkg, ale neexistuje %s"
#: pkg/sync/sync.go:45
msgid "there is nothing to do"
msgstr "nie je čo urobiť"
#: pkg/db/ialpm/alpm.go:247
msgid "unable to CreateHandle: %s"
msgstr "nepodarilo sa CreateHandle: %s"
#: cmd.go:186
msgid "unhandled operation"
msgstr "neošetrená operácia"
#: cmd.go:450
msgid "unknown-version"
msgstr "neznáma-verzia"
#: pkg/text/input.go:47
msgid "yes"
msgstr "áno"
================================================
FILE: po/sv.po
================================================
#
# Translators:
# J G, 2021
# August Wikerfors, 2023
# Luna Jernberg , 2026
#
msgid ""
msgstr ""
"Last-Translator: Luna Jernberg , 2026\n"
"Language-Team: Swedish (https://app.transifex.com/yay-1/teams/123732/sv/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: sv\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: xgotext\n"
#: clean.go:83
msgid ""
"\n"
"Build directory:"
msgstr ""
"\n"
"Byggkatalog:"
#: pkg/db/ialpm/alpm.go:201 pkg/dep/dep_graph.go:740
msgid ""
"\n"
"Enter a number (default=1): "
msgstr ""
"\n"
"Ange ett nummer: (standard = 1): "
#: pkg/menus/menu.go:32
msgid " (Build Files Exist)"
msgstr " (Byggfiler finns)"
#: pkg/menus/menu.go:27
msgid " (Installed)"
msgstr " (Installerad)"
#: cmd.go:453
msgid " [Installed]"
msgstr " [Installerad]"
#: cmd.go:410 vote.go:36
msgid " there is nothing to do"
msgstr " inget behöver göras"
#: pkg/menus/menu.go:49
msgid "%s [A]ll [Ab]ort [I]nstalled [No]tInstalled or (1 2 3, 1-3, ^4)"
msgstr ""
"%s [A]lla [Ab]Avbryt [I]nstallerade [No]EjInstallerade eller (1 2 3, 1-3, "
"^4)"
#: pkg/sync/build/installer.go:308
msgid "%s already made -- skipping build"
msgstr "%s redan byggt -- hoppar över bygge"
#: pkg/menus/edit_menu.go:57
msgid "%s is not set"
msgstr "%s är inte inställd"
#: pkg/settings/exe/cmd_builder.go:257
msgid "%s is present."
msgstr "%s finns."
#: pkg/dep/dep_graph.go:460 pkg/sync/build/installer.go:305
msgid "%s is up to date -- skipping"
msgstr "%s är ajour -- hoppar över"
#: pkg/upgrade/service.go:291
msgid "%s to upgrade/install."
msgstr "%s att uppgradera/installera."
#: pkg/upgrade/service.go:285
msgid "%s will also be installed for this operation."
msgstr "%s kommer också att installeras för denna operation."
#: pkg/sync/srcinfo/pgp/keys.go:124
msgid "%s, required by: %s"
msgstr "%s, krävs av: %s"
#: pkg/menus/diff_menu.go:49
msgid "%s: No changes -- skipping"
msgstr "%s: Inga ändringar -- hoppar över"
#: pkg/query/filter.go:22
msgid "%s: can't use target with option --aur -- skipping"
msgstr "%s: kan inte använda mål med växeln --aur -- hoppar över"
#: pkg/query/filter.go:17
msgid "%s: can't use target with option --repo -- skipping"
msgstr "%s: kan inte använda mål med växeln --repo -- hoppar över"
#: pkg/upgrade/sources.go:57
msgid "%s: ignoring package upgrade (%s => %s)"
msgstr "%s: ignorerar paketuppgradering (%s => %s)"
#: pkg/query/aur_warnings.go:46
msgid "%s: local (%s) is newer than AUR (%s)"
msgstr "%s: lokalt paket (%s) är nyare än AUR (%s)"
#: vote.go:51
msgid ""
"%s: please set AUR_USERNAME and AUR_PASSWORD environment variables for "
"voting"
msgstr ""
"%s: vänligen ställ in miljövariablerna AUR_USERNAME och AUR_PASSWORD för att"
" rösta"
#: pkg/download/unified.go:192
msgid "(%d/%d) Downloaded PKGBUILD from ABS: %s"
msgstr "(%d/%d) Hämtat PKGBUILD från ABS: %s"
#: pkg/download/aur.go:92 pkg/download/unified.go:188
msgid "(%d/%d) Downloaded PKGBUILD: %s"
msgstr "(%d/%d) Hämtat PKGBUILD: %s"
#: pkg/download/aur.go:82
msgid "(%d/%d) Failed to download PKGBUILD: %s"
msgstr "(%d/%d) Misslyckades att ladda ner PKGBUILD: %s"
#: pkg/sync/srcinfo/service.go:109
msgid "(%d/%d) Parsing SRCINFO: %s"
msgstr "(%d/%d) Tolkar SRCINFO: %s"
#: pkg/query/types.go:103 pkg/query/types.go:72
msgid "(Installed)"
msgstr "(Installerad)"
#: pkg/query/types.go:101 pkg/query/types.go:70
msgid "(Installed: %s)"
msgstr "(Installerade: %s)"
#: pkg/query/types.go:61
msgid "(Orphaned)"
msgstr "(Övergiven)"
#: pkg/query/types.go:65
msgid "(Out-of-date: %s)"
msgstr "(Föråldrade: %s)"
#: pkg/dep/dep_graph.go:75
msgid "AUR"
msgstr "AUR"
#: print.go:44
msgid "AUR URL"
msgstr "AUR-webbadress"
#: pkg/menus/edit_menu.go:58
msgid "Add %s or %s to your environment variables"
msgstr "Lägg till %s eller %s bland dina miljövariabler"
#: main.go:60
msgid "Avoid running yay as root/sudo."
msgstr "Undvik att köra yay som root/sudo."
#: pkg/dep/dep_graph.go:63
msgid "Check Dependency"
msgstr "Kontrollberoende"
#: print.go:41
msgid "Check Deps"
msgstr "Kontrollberoenden"
#: pkg/upgrade/service.go:89
msgid "Checking development packages..."
msgstr "Kontrollerar utvecklingspaket..."
#: pkg/sync/workdir/clean.go:45
msgid "Cleaning (%d/%d): %s"
msgstr "Rensar (%d/%d): %s"
#: print.go:42
msgid "Conflicts With"
msgstr "Krockar med"
#: pkg/menus/clean_menu.go:62
msgid "Deleting (%d/%d): %s"
msgstr "Tar bort (%d/%d): %s"
#: pkg/dep/dep_graph.go:61
msgid "Dependency"
msgstr "Beroende"
#: print.go:38
msgid "Depends On"
msgstr "Beror av"
#: print.go:33
msgid "Description"
msgstr "Beskrivning"
#: pkg/menus/diff_menu.go:160
msgid "Diffs to show?"
msgstr "Visa diff?"
#: pkg/settings/migrations.go:25
msgid "Disable 'provides' setting by default"
msgstr "Inaktivera \"tillhandahåller\" inställning som standard"
#: clean.go:78
msgid "Do you want to remove ALL AUR packages from cache?"
msgstr "Vill du verkligen ta bort ALLA AUR-paket från cachen?"
#: clean.go:95
msgid "Do you want to remove ALL untracked AUR files?"
msgstr "Vill du verkligen ta bort ALLA ospårade AUR-filer?"
#: clean.go:80
msgid "Do you want to remove all other AUR packages from cache?"
msgstr "Vill du verkligen ta bort alla andra AUR-paket från cachen?"
#: pkg/menus/edit_menu.go:61
msgid "Edit PKGBUILD with?"
msgstr "Redigera PKGBUILD med?"
#: pkg/query/errors.go:13
msgid "Error during AUR search: %s\n"
msgstr "Fel vid AUR-sökning: %s\n"
#: pkg/upgrade/service.go:295
msgid "Excluding packages may cause partial upgrades and break systems"
msgstr ""
"Att exkludera paket kan orsaka partiella uppgraderingar och ha sönder system"
#: pkg/dep/dep_graph.go:60
msgid "Explicit"
msgstr "Uttryckligen"
#: print.go:91
msgid "Explicitly installed packages: %s"
msgstr "Uttryckligen installerade paket: %s"
#: pkg/dep/dep_graph.go:437 pkg/dep/dep_graph.go:535
msgid "Failed to find AUR package for"
msgstr "Misslyckades att hitta AUR paket för"
#: pkg/sync/build/installer.go:120
msgid "Failed to install layer, rolling up to next layer."
msgstr "Misslyckades att installera lager, rullar upp till nästa lager."
#: pkg/sync/build/errors.go:16
msgid ""
"Failed to install the following packages. Manual intervention is required:"
msgstr "Misslyckades att bygga följande paket. Manuell intervention krävs:"
#: print.go:45
msgid "First Submitted"
msgstr "Först skapad"
#: pkg/query/aur_warnings.go:83
msgid "Flagged Out Of Date AUR Packages:"
msgstr "AUR-paket markerade som föråldrade:"
#: print.go:90
msgid "Foreign installed packages: %s"
msgstr "Okända installerade paket: %s"
#: pkg/vcs/vcs.go:144
msgid "Found git repo: %s"
msgstr "Hittade git-arkiv: %s"
#: vcs.go:72
msgid "GenDB finished. No packages were installed"
msgstr "GenDB klar. Inga paket installerades"
#: print.go:36
msgid "Groups"
msgstr "Grupper"
#: pkg/sync/srcinfo/pgp/keys.go:88
msgid "Import?"
msgstr "Importera?"
#: pkg/sync/srcinfo/pgp/keys.go:97
msgid "Importing keys with gpg..."
msgstr "Importerar nycklar med gpg..."
#: print.go:46
msgid "Keywords"
msgstr "Nyckelord"
#: print.go:47
msgid "Last Modified"
msgstr "Senast ändrad"
#: print.go:35
msgid "Licenses"
msgstr "Licenser"
#: pkg/dep/dep_graph.go:77
msgid "Local"
msgstr "Lokal"
#: print.go:48
msgid "Maintainer"
msgstr "Underhållare"
#: pkg/dep/dep_graph.go:62
msgid "Make Dependency"
msgstr "Byggberoende"
#: print.go:40
msgid "Make Deps"
msgstr "Byggberoenden"
#: pkg/dep/dep_graph.go:79
msgid "Missing"
msgstr "Saknas"
#: pkg/query/aur_warnings.go:75
msgid "Missing AUR Debug Packages:"
msgstr "Saknar AUR-felsökningspaket:"
#: print.go:31
msgid "Name"
msgstr "Namn"
#: pkg/dep/dep_graph.go:442 pkg/dep/dep_graph.go:564
msgid "No AUR package found for"
msgstr "Inga AUR paket hittades för"
#: pkg/dep/dep_graph.go:564
msgid "required by"
msgstr "krävs av:"
#: pkg/dep/dep_graph.go:182
msgid "No package found for"
msgstr "Inget paket hittades för"
#: print.go:225
msgid "None"
msgstr "Inga"
#: print.go:39
msgid "Optional Deps"
msgstr "Valfria beroenden"
#: pkg/query/aur_warnings.go:79
msgid "Orphan (unmaintained) AUR Packages:"
msgstr "Föräldralösa (icke underhållna) AUR paket:"
#: print.go:53 print.go:55
msgid "Out-of-date"
msgstr "Föråldrat"
#: pkg/sync/srcinfo/pgp/keys.go:115
msgid "PGP keys need importing:"
msgstr "PGP-nycklar kräver import:"
#: pkg/sync/workdir/preparer.go:252
msgid "PKGBUILD up to date, skipping download: %s"
msgstr "PKGBUILD uppdaterad, hoppar över nedladdning: %s"
#: pkg/menus/edit_menu.go:130
msgid "PKGBUILDs to edit?"
msgstr "Redigera PKGBUILD-filer?"
#: print.go:61
msgid "Package Base"
msgstr "Grundpaket"
#: print.go:60
msgid "Package Base ID"
msgstr "Grundpakets-ID"
#: pkg/query/aur_warnings.go:71
msgid "Packages not in AUR:"
msgstr "Paket som inte finns i AUR:"
#: pkg/menus/clean_menu.go:54
msgid "Packages to cleanBuild?"
msgstr "Bygg paket rent (cleanBuild)?"
#: pkg/dep/dep_graph.go:202
msgid "Packages to exclude"
msgstr "Paket att exkludera"
#: pkg/upgrade/service.go:294
msgid "Packages to exclude: (eg: \"1 2 3\", \"1-3\", \"^4\" or repo name)"
msgstr ""
"Paket att hoppa över: (t.ex: \"1 2 3\", \"1-3\", \"^4\" eller "
"centralkatalogsnamn)"
#: cmd.go:392
msgid "Packages to install (eg: 1 2 3, 1-3 or ^4)"
msgstr "Paket att installera (t.ex: 1 2 3, 1-3 eller ^4)"
#: print.go:49
msgid "Popularity"
msgstr "Popularitet"
#: pkg/menus/diff_menu.go:172 pkg/menus/edit_menu.go:143
msgid "Proceed with install?"
msgstr "Fortsätt med installation?"
#: print.go:37
msgid "Provides"
msgstr "Tillhandahåller"
#: pkg/sync/workdir/preparer.go:125
msgid "Remove make dependencies after install?"
msgstr "Ta bort byggberoenden efter installation?"
#: print.go:43
msgid "Replaces"
msgstr "Ersätter"
#: pkg/db/ialpm/alpm.go:191 print.go:30
msgid "Repository"
msgstr "Centralkatalog"
#: pkg/dep/dep_graph.go:730
msgid "Repository AUR"
msgstr "Centralkatalog AUR"
#: pkg/dep/dep_graph.go:78
msgid "SRCINFO"
msgstr "SRCINFO"
#: pkg/upgrade/service.go:71
msgid "Searching AUR for updates..."
msgstr "Söker efter uppdateringar på AUR..."
#: pkg/upgrade/service.go:159
msgid "Searching databases for updates..."
msgstr "Söker efter uppdateringar i databaserna..."
#: pkg/query/query_builder.go:214
msgid "Showing repo packages only"
msgstr "Visar endast centralkatalogspaket"
#: print.go:95
msgid "Size of pacman cache %s: %s"
msgstr "Storlek på pacman cache %s: %s"
#: print.go:98
msgid "Size of yay cache %s: %s"
msgstr "Storlek på yay cache %s: %s"
#: print.go:62
msgid "Snapshot URL"
msgstr "Snapshot-URL"
#: pkg/dep/dep_graph.go:76
msgid "Sync"
msgstr "Synk"
#: print.go:100
msgid "Ten biggest packages:"
msgstr "Tio största paketen:"
#: pkg/sync/sync.go:124
msgid "The following packages are not compatible with your architecture:"
msgstr "Följande paket stöds inte av din arkitektur:"
#: pkg/db/ialpm/alpm.go:179 pkg/dep/dep_graph.go:726
msgid "There are %[1]d providers available for %[2]s:"
msgstr "Det finns %[1]d tillhandahållare tillgängliga för %[2]s:"
#: pkg/settings/exe/cmd_builder.go:258
msgid "There may be another Pacman instance running. Waiting..."
msgstr "Det kan finns en till instans av pacman som kör. Väntar..."
#: print.go:92
msgid "Total Size occupied by packages: %s"
msgstr "Total paketstorlek: %s"
#: print.go:89
msgid "Total installed packages: %s"
msgstr "Antal installerade paket: %s"
#: pkg/sync/sync.go:132
msgid "Try to build them anyway?"
msgstr "Försök att bygga dem ändå?"
#: print.go:34
msgid "URL"
msgstr "Webbadress"
#: clean.go:194 pkg/menus/clean_menu.go:65 pkg/menus/clean_menu.go:71
msgid "Unable to clean:"
msgstr "Kunde inte rensa:"
#: get.go:42 get.go:74
msgid "Unable to find the following packages:"
msgstr "Det gick inte att hitta följande paket:"
#: vote.go:20
msgid "Unable to handle package vote for: %s. err: %s"
msgstr "Kan inte hantera paketröstning för: %s . fel: %s"
#: clean.go:170
msgid "Unable to remove %s: %s"
msgstr "Kunde inte ta bort %s: %s"
#: print.go:32
msgid "Version"
msgstr "Version"
#: print.go:50
msgid "Votes"
msgstr "Röster"
#: print.go:87
msgid "Yay version v%s"
msgstr "Yay-version v%s"
#: pkg/menus/menu.go:49
msgid "[N]one"
msgstr "[N]Inga"
#: pkg/settings/errors.go:29
msgid "aborting due to user"
msgstr "avbryter på grund av användare"
#: pkg/settings/parser/parser.go:608
msgid "argument '-' specified without input on stdin"
msgstr "argument '-' specificerat utan indata på stdin"
#: local_install.go:26
msgid "cannot find PKGBUILD and .SRCINFO in directory"
msgstr "kan inte hitta PKGBUILD eller .SRCINFO i katalog"
#: pkg/sync/build/pkg_archive.go:148
msgid "cannot find package name: %v"
msgstr "kunde inte hitta paketet: %v"
#: pkg/sync/build/errors.go:30
msgid "could not find PKGDEST for: %s"
msgstr "kunde inte hitta PKGDEST för: %s"
#: errors.go:9
msgid "could not find all required packages"
msgstr "kunde inte hitta alla paket som krävs"
#: pkg/sync/build/errors.go:61
msgid "could not find any package archives listed in %s"
msgstr "kunde inte hitta några paketarkiv listade i %s"
#: pkg/sync/build/errors.go:50 pkg/upgrade/service.go:286
msgid "dependency"
msgstr "beroende"
#: pkg/vcs/vcs.go:100 pkg/vcs/vcs.go:96
msgid "devel check for package failed: '%s' encountered an error"
msgstr "Utvecklings koll för paket misslyckades '%s' stötte på ett fel"
#: pkg/menus/edit_menu.go:110
msgid "editor did not exit successfully, aborting: %s"
msgstr "redigeraren avslutades inte korrekt, avbryter: %s"
#: pkg/sync/workdir/aur_source.go:24
msgid "error downloading sources: %s"
msgstr "fel vid nedladdning av källfiler: %s"
#: pkg/download/errors.go:25
msgid "error fetching %s: %s"
msgstr "fel vid hämtning av %s: %s"
#: pkg/sync/build/errors.go:9
msgid "error installing repo packages"
msgstr "fel vid installation av centralkatalogspaket"
#: pkg/sync/build/installer.go:266 pkg/sync/build/installer.go:270
msgid "error installing:"
msgstr "fel vid installation:"
#: pkg/sync/build/installer.go:233 pkg/sync/build/installer.go:237
msgid "error making: %s"
msgstr "fel vid bygge: %s"
#: pkg/sync/workdir/merge.go:24
msgid "error merging %s: %s"
msgstr "fel vid sammanslagning av %s: %s"
#: pkg/download/unified.go:59
msgid "error reading %s"
msgstr "fel vid läsning av %s"
#: sync.go:36
msgid "error refreshing databases"
msgstr "fel vid uppdatering av databaserna"
#: pkg/sync/workdir/clean.go:51 pkg/sync/workdir/merge.go:17
msgid "error resetting %s: %s"
msgstr "fel vid återställning av %s: %s"
#: pkg/sync/build/errors.go:53
msgid "error updating package install reason to %s"
msgstr "fel vid uppdatering av paketinstallations anledning till %s"
#: pkg/sync/build/errors.go:48
msgid "explicit"
msgstr "uttryckligen"
#: pkg/settings/errors.go:23
msgid "failed to create directory '%s': %s"
msgstr "misslyckades med att skapa katalog '%s': %s"
#: pkg/settings/config.go:281
msgid "failed to open config file '%s': %s"
msgstr "misslyckades med att öppna konfigurationsfilen '%s': %s"
#: pkg/sync/srcinfo/service.go:114
msgid "failed to parse %s -- skipping: %s"
msgstr "misslyckades med att tolka %s -- hoppar över: %s"
#: pkg/sync/srcinfo/service.go:118
msgid "failed to parse %s: %s"
msgstr "misslyckades med att tolka %s: %s"
#: local_install.go:77
msgid "failed to parse .SRCINFO"
msgstr "misslyckades med att tolka .SRCINFO"
#: pkg/settings/config.go:291
msgid "failed to read config file '%s': %s"
msgstr "misslyckades med att läsa konfigurationsfilen '%s': %s"
#: pkg/cmd/graph/main.go:46 pkg/runtime/runtime.go:73
msgid "failed to retrieve aur Cache"
msgstr "misslyckades med att hämta aur Cache"
#: pkg/upgrade/sources.go:27
msgid "ignoring package devel upgrade (no AUR info found):"
msgstr "ignorerar paket devel uppgradering (ingen AUR info hittades):"
#: pkg/text/errors.go:8
msgid "input too long"
msgstr "för lång indata"
#: pkg/db/ialpm/alpm.go:222 pkg/dep/dep_graph.go:761
msgid "invalid number: %s"
msgstr "ogiltigt nummer: %s"
#: pkg/settings/parser/parser.go:174
msgid "invalid option '%s'"
msgstr "ogiltig växel '%s'"
#: cmd.go:197
msgid "invalid option: '--deps' and '--explicit' may not be used together"
msgstr ""
"ogiltigt alternativ: '--deps' och '--explicit' kan inte användas tillsammans"
#: pkg/download/abs.go:22
msgid "invalid repository"
msgstr "ogiltig centralkatalog"
#: pkg/db/ialpm/alpm.go:227 pkg/dep/dep_graph.go:767
msgid "invalid value: %d is not between %d and %d"
msgstr "ogiltigt värde: %d är inte mellan %d och %d"
#: pkg/text/input.go:48
msgid "no"
msgstr "nej"
#: pkg/sync/srcinfo/pgp/keys.go:110
msgid "no keys to import"
msgstr "inga nycklar att importera"
#: pkg/query/errors.go:20
msgid "no query was executed"
msgstr "ingen fråga utfördes"
#: local_install.go:66
msgid "no target directories specified"
msgstr "inga målkataloger specificerade"
#: pkg/sync/build/installer.go:242
msgid "nothing to install for %s"
msgstr "inget att installera för %s"
#: pkg/settings/parser/parser.go:164
msgid "only one operation may be used at a time"
msgstr "bara en operation går att utföra åt gången"
#: pkg/cmd/graph/main.go:70
msgid "only one target is allowed"
msgstr "endast ett mål är tillåtet"
#: pkg/upgrade/service.go:291
msgid "package"
msgid_plural "packages"
msgstr[0] "paket"
msgstr[1] "paket"
#: print.go:187
msgid "package '%s' was not found"
msgstr "paketet '%s' kunde inte hittas"
#: pkg/download/errors.go:15
msgid "package not found in AUR"
msgstr "paketet hittades inte i AUR"
#: pkg/download/abs.go:23
msgid "package not found in repos"
msgstr "paketet hittades inte i centralkatalogerna"
#: pkg/sync/srcinfo/pgp/keys.go:100
msgid "problem importing keys"
msgstr "ett problem uppstod vid nyckelimporten"
#: clean.go:105
msgid "removing AUR packages from cache..."
msgstr "tar bort AUR-paket från cachen..."
#: clean.go:178 pkg/sync/workdir/clean.go:41
msgid "removing untracked AUR files from cache..."
msgstr "tar bort ospårade AUR-filer från cachen..."
#: pkg/sync/build/errors.go:38
msgid "the PKGDEST for %s is listed by makepkg but does not exist: %s"
msgstr "den PKGDEST för %s som står med i makepkg finns ej: %s"
#: pkg/sync/sync.go:45
msgid "there is nothing to do"
msgstr "det finns ingenting att göra"
#: pkg/db/ialpm/alpm.go:247
msgid "unable to CreateHandle: %s"
msgstr "misslyckades med att skapa handtag (CreateHandle): %s"
#: cmd.go:186
msgid "unhandled operation"
msgstr "okänd operation"
#: cmd.go:450
msgid "unknown-version"
msgstr "okänd-version"
#: pkg/text/input.go:47
msgid "yes"
msgstr "ja"
================================================
FILE: po/tr.po
================================================
#
# Translators:
# Ahmet Arda Kavakcı, 2022
# Mehmet Özgür Bayhan , 2023
# can avar, 2023
# yiğit yeten, 2023
#
msgid ""
msgstr ""
"Last-Translator: yiğit yeten, 2023\n"
"Language-Team: Turkish (https://app.transifex.com/yay-1/teams/123732/tr/)\n"
"Language: tr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"X-Generator: xgotext\n"
#: pkg/menus/menu.go:32
msgid " (Build Files Exist)"
msgstr "(Kurulum Dosyaları Var)"
#: pkg/menus/menu.go:27
msgid " (Installed)"
msgstr "(Kuruldu)"
#: cmd.go:453
msgid " [Installed]"
msgstr "[Kuruldu]"
#: cmd.go:410 vote.go:36
msgid " there is nothing to do"
msgstr "yapılacak bir şey yok"
#: pkg/menus/menu.go:49
msgid "%s [A]ll [Ab]ort [I]nstalled [No]tInstalled or (1 2 3, 1-3, ^4)"
msgstr "%s [A]Hepsi [Ab]İptal [I]Kurulmuş [No]Kurulmamış veya (1 2 3, 1-3, ^4)"
#: pkg/sync/build/installer.go:308
msgid "%s already made -- skipping build"
msgstr "%s çoktan yapıldı -- yapılandırma atlanıyor"
#: pkg/menus/edit_menu.go:57
msgid "%s is not set"
msgstr "%s belirlenmiş değil"
#: pkg/settings/exe/cmd_builder.go:257
msgid "%s is present."
msgstr "%s hâlihazırda bulunuyor."
#: pkg/dep/dep_graph.go:460 pkg/sync/build/installer.go:305
msgid "%s is up to date -- skipping"
msgstr "%s güncel -- atlanıyor"
#: pkg/upgrade/service.go:292
#, fuzzy
msgid "%s to upgrade/install."
msgstr "Yükseltilecek/yüklenecek paketler."
#: pkg/upgrade/service.go:286
msgid "%s will also be installed for this operation."
msgstr ""
#: pkg/sync/srcinfo/pgp/keys.go:124
msgid "%s, required by: %s"
msgstr "%s, ihtiyaç duyan: %s"
#: pkg/menus/diff_menu.go:49
msgid "%s: No changes -- skipping"
msgstr "%s: Değişiklik yok -- atlanıyor"
#: pkg/query/filter.go:22
msgid "%s: can't use target with option --aur -- skipping"
msgstr "%s: hedef --aur seçeneği ile kullanılamaz -- atlanıyor"
#: pkg/query/filter.go:17
msgid "%s: can't use target with option --repo -- skipping"
msgstr "%s: hedef --repo seçeneği ile kullanılamaz -- atlanıyor"
#: pkg/upgrade/sources.go:57
msgid "%s: ignoring package upgrade (%s => %s)"
msgstr "%s: paket güncellemesi göz ardı ediliyor (%s => %s)"
#: pkg/query/aur_warnings.go:46
msgid "%s: local (%s) is newer than AUR (%s)"
msgstr "%s: yerel (%s) AUR'dan (%s) daha güncel"
#: vote.go:51
msgid ""
"%s: please set AUR_USERNAME and AUR_PASSWORD environment variables for voting"
msgstr ""
"Oy verebilmek için AUR_USERNAME ve AUR_PASSWORD ortam değişkenlerini "
"ayarlayın"
#: pkg/download/unified.go:192
msgid "(%d/%d) Downloaded PKGBUILD from ABS: %s"
msgstr "(%d/%d) PKGBUILD, ABS'den indirildi: %s"
#: pkg/download/aur.go:92 pkg/download/unified.go:188
msgid "(%d/%d) Downloaded PKGBUILD: %s"
msgstr "(%d/%d) PKGBUILD indirildi: %s"
#: pkg/download/aur.go:82
#, fuzzy
msgid "(%d/%d) Failed to download PKGBUILD: %s"
msgstr "(%d/%d) PKGBUILD indirildi: %s"
#: pkg/sync/srcinfo/service.go:109
msgid "(%d/%d) Parsing SRCINFO: %s"
msgstr "(%d/%d) SRCINFO çözümleniyor: %s"
#: pkg/query/types.go:72 pkg/query/types.go:103
msgid "(Installed)"
msgstr "(Kuruldu)"
#: pkg/query/types.go:70 pkg/query/types.go:101
msgid "(Installed: %s)"
msgstr "(Kuruldu: %s)"
#: pkg/query/types.go:61
msgid "(Orphaned)"
msgstr "(Öksüz bırakıldı)"
#: pkg/query/types.go:65
msgid "(Out-of-date: %s)"
msgstr "(Güncel değil: %s)"
#: print.go:44
msgid "AUR URL"
msgstr "AUR URL"
#: pkg/dep/dep_graph.go:75
msgid "AUR"
msgstr "AUR"
#: pkg/menus/edit_menu.go:58
msgid "Add %s or %s to your environment variables"
msgstr "Ortam değişkenlerinize %s veya %s seçeneklerini ekleyin"
#: main.go:60
msgid "Avoid running yay as root/sudo."
msgstr "Yay'ı root/sudo olarak çalıştırmaktan kaçının"
#: pkg/dep/dep_graph.go:63
msgid "Check Dependency"
msgstr "Bağımlılık Kontrolü"
#: print.go:41
msgid "Check Deps"
msgstr "Bağımlıkları Kontrol Et"
#: pkg/upgrade/service.go:90
msgid "Checking development packages..."
msgstr "Geliştirme paketleri kontrol ediliyor..."
#: pkg/sync/workdir/clean.go:45
msgid "Cleaning (%d/%d): %s"
msgstr "Temizleniyor (%d/%d): %s"
#: print.go:42
msgid "Conflicts With"
msgstr "Çakışıyor:"
#: pkg/menus/clean_menu.go:62
msgid "Deleting (%d/%d): %s"
msgstr "Siliniyor (%d/%d): %s"
#: pkg/dep/dep_graph.go:61
msgid "Dependency"
msgstr "Bağımlılık"
#: print.go:38
msgid "Depends On"
msgstr "Bağımlı:"
#: print.go:33
msgid "Description"
msgstr "Açıklama"
#: pkg/menus/diff_menu.go:160
msgid "Diffs to show?"
msgstr "Gösterilecek olan değişiklikler (diff)?"
#: pkg/settings/migrations.go:25
msgid "Disable 'provides' setting by default"
msgstr "Varsayılan olarak 'provides' ayarını geçersiz kıl"
#: clean.go:78
msgid "Do you want to remove ALL AUR packages from cache?"
msgstr "AUR paketlerinin TÜMÜNÜ önbellekten silmek istiyor musun?"
#: clean.go:95
msgid "Do you want to remove ALL untracked AUR files?"
msgstr "İzlenmemiş AUR paketlerinin TÜM dosyalarını silmek istiyor musun?"
#: clean.go:80
msgid "Do you want to remove all other AUR packages from cache?"
msgstr "Tüm diğer AUR paketlerini önbellekten silmek istiyor musun?"
#: pkg/menus/edit_menu.go:61
msgid "Edit PKGBUILD with?"
msgstr "PKGBUILD'i neyle düzenleyeceksin?"
#: pkg/query/errors.go:13
msgid "Error during AUR search: %s\n"
msgstr "AUR araması yaparken hata: %s\n"
#: pkg/upgrade/service.go:296
msgid "Excluding packages may cause partial upgrades and break systems"
msgstr ""
"Paketlerin dışlanması, kısmi yükseltmelere ve sistemlerin bozulmasına neden "
"olabilir"
#: pkg/dep/dep_graph.go:60
msgid "Explicit"
msgstr "Açık"
#: print.go:91
msgid "Explicitly installed packages: %s"
msgstr "Doğrudan kurulan paketler: %s"
#: pkg/dep/dep_graph.go:437 pkg/dep/dep_graph.go:535
msgid "Failed to find AUR package for"
msgstr "Şunun için AUR paketi bulunamadı:"
#: pkg/sync/build/installer.go:120
msgid "Failed to install layer, rolling up to next layer."
msgstr "Katman yüklenemedi, sonraki katmana geçiliyor."
#: pkg/sync/build/errors.go:16
msgid ""
"Failed to install the following packages. Manual intervention is required:"
msgstr "İstenen paketler yüklenemedi. Manüel müdahale gerekli:"
#: print.go:45
msgid "First Submitted"
msgstr "İlk İletilen"
#: pkg/query/aur_warnings.go:79
msgid "Flagged Out Of Date AUR Packages:"
msgstr "Eski Olarak İşaretlenmiş AUR Paketleri:"
#: print.go:90
msgid "Foreign installed packages: %s"
msgstr "Yabancı kurulan paketler: %s"
#: pkg/vcs/vcs.go:144
msgid "Found git repo: %s"
msgstr "Git deposu bulundu: %s"
#: vcs.go:72
msgid "GenDB finished. No packages were installed"
msgstr "GenDB tamamlandı. Hiçbir paket kurulmadı"
#: print.go:36
msgid "Groups"
msgstr "Gruplar"
#: pkg/sync/srcinfo/pgp/keys.go:88
msgid "Import?"
msgstr "İçe Aktar?"
#: pkg/sync/srcinfo/pgp/keys.go:97
msgid "Importing keys with gpg..."
msgstr "GPG anahtarları içe aktarılıyor..."
#: print.go:46
msgid "Keywords"
msgstr "Anahtar Kelimeler"
#: print.go:47
msgid "Last Modified"
msgstr "Son Değiştirme"
#: print.go:35
msgid "Licenses"
msgstr "Lisanslar"
#: pkg/dep/dep_graph.go:77
msgid "Local"
msgstr "Yerel"
#: print.go:48
msgid "Maintainer"
msgstr "Bakımcı:"
#: pkg/dep/dep_graph.go:62
msgid "Make Dependency"
msgstr "Make Bağımlılığı"
#: print.go:40
msgid "Make Deps"
msgstr "Make Bağımlılıkları"
#: pkg/query/aur_warnings.go:71
msgid "Missing AUR Debug Packages:"
msgstr "Eksik AUR Hata Ayıklama Paketleri:"
#: pkg/dep/dep_graph.go:79
msgid "Missing"
msgstr "Kayıp"
#: print.go:31
msgid "Name"
msgstr "İsim"
#: pkg/dep/dep_graph.go:442 pkg/dep/dep_graph.go:548
msgid "No AUR package found for"
msgstr "Şunun için AUR paketi bulunamadı: "
#: pkg/dep/dep_graph.go:182
#, fuzzy
msgid "No package found for"
msgstr "Şunun için AUR paketi bulunamadı: "
#: print.go:225
msgid "None"
msgstr "Yok"
#: print.go:39
msgid "Optional Deps"
msgstr "İsteğe Bağlı Bağımlılıklar"
#: pkg/query/aur_warnings.go:75
msgid "Orphan (unmaintained) AUR Packages:"
msgstr "Orphan (bakımsız) AUR Paketleri:"
#: print.go:53 print.go:55
msgid "Out-of-date"
msgstr "Eski"
#: pkg/sync/srcinfo/pgp/keys.go:115
msgid "PGP keys need importing:"
msgstr "İçe aktarılması gereken PGP anahtarları:"
#: pkg/sync/workdir/preparer.go:252
msgid "PKGBUILD up to date, skipping download: %s"
msgstr "PKGBUILD güncel, indirme geçiliyor: %s"
#: pkg/menus/edit_menu.go:130
msgid "PKGBUILDs to edit?"
msgstr "Düzenlenecek PKGBUILD'ler?"
#: print.go:60
msgid "Package Base ID"
msgstr "Paket Temel Kimliği (ID):"
#: print.go:61
msgid "Package Base"
msgstr "Paket Temeli"
#: pkg/query/aur_warnings.go:67
msgid "Packages not in AUR:"
msgstr "AUR'da bulunmayan paketler:"
#: pkg/menus/clean_menu.go:54
msgid "Packages to cleanBuild?"
msgstr "cleanBuild yapılacak paketler?"
#: pkg/dep/dep_graph.go:202
msgid "Packages to exclude"
msgstr "Dışlanacak paketler"
#: pkg/upgrade/service.go:295
msgid "Packages to exclude: (eg: \"1 2 3\", \"1-3\", \"^4\" or repo name)"
msgstr "Dışlanacak paketler: (ör. \"1 2 3\", \"1-3\", \"^4\" veya depo ismi)"
#: cmd.go:392
msgid "Packages to install (eg: 1 2 3, 1-3 or ^4)"
msgstr "Kurulacak paketler (ör. 1 2 3, 1-3 veya ^4)"
#: print.go:49
msgid "Popularity"
msgstr "Popülerlik"
#: pkg/menus/diff_menu.go:172 pkg/menus/edit_menu.go:143
msgid "Proceed with install?"
msgstr "Kurmaya devam et?"
#: print.go:37
msgid "Provides"
msgstr "Sağlar:"
#: pkg/sync/workdir/preparer.go:125
msgid "Remove make dependencies after install?"
msgstr "Make bağımlılıkları kurulum sonrası silinsin mi?"
#: print.go:43
msgid "Replaces"
msgstr ""
#: pkg/dep/dep_graph.go:730
msgid "Repository AUR"
msgstr "AUR Deposu"
#: print.go:30 pkg/db/ialpm/alpm.go:191
msgid "Repository"
msgstr "Depo"
#: pkg/dep/dep_graph.go:78
msgid "SRCINFO"
msgstr "SRCINFO"
#: pkg/upgrade/service.go:72
msgid "Searching AUR for updates..."
msgstr "AUR güncellemeleri aranıyor..."
#: pkg/upgrade/service.go:160
msgid "Searching databases for updates..."
msgstr "Veritabanlarında güncellemeler aranıyor..."
#: pkg/query/query_builder.go:214
msgid "Showing repo packages only"
msgstr "Sadece depo paketleri gösteriliyor"
#: print.go:95
msgid "Size of pacman cache %s: %s"
msgstr "pacman önbelleğinin boyutu: %s: %s"
#: print.go:98
msgid "Size of yay cache %s: %s"
msgstr "yay önbelleğinin boyutu: %s: %s"
#: print.go:62
msgid "Snapshot URL"
msgstr "Anlık görüntü URL'si"
#: pkg/dep/dep_graph.go:76
msgid "Sync"
msgstr "Senkronize"
#: print.go:100
msgid "Ten biggest packages:"
msgstr "En büyük 10 paket:"
#: pkg/sync/sync.go:124
msgid "The following packages are not compatible with your architecture:"
msgstr "Aşağıdaki paketler sistem mimarin ile uyumlu değil:"
#: pkg/db/ialpm/alpm.go:179 pkg/dep/dep_graph.go:726
msgid "There are %[1]d providers available for %[2]s:"
msgstr "%[2]s için %[1]d sağlayıcı bulunuyor:"
#: pkg/settings/exe/cmd_builder.go:258
msgid "There may be another Pacman instance running. Waiting..."
msgstr "Başka bir Pacman talebi çalışıyor olabilir. Bekleniyor..."
#: print.go:92
msgid "Total Size occupied by packages: %s"
msgstr "Paketlerin tuttuğu Toplam Boyut: %s"
#: print.go:89
msgid "Total installed packages: %s"
msgstr "İndirilen toplam paketler: %s"
#: pkg/sync/sync.go:132
msgid "Try to build them anyway?"
msgstr "Yine de yapılandırmaya çalış?"
#: print.go:34
msgid "URL"
msgstr "URL"
#: clean.go:194 pkg/menus/clean_menu.go:65 pkg/menus/clean_menu.go:71
msgid "Unable to clean:"
msgstr "Temizlenemiyor:"
#: get.go:42 get.go:74
msgid "Unable to find the following packages:"
msgstr "Aşağıdaki paketler bulunamadı:"
#: vote.go:20
msgid "Unable to handle package vote for: %s. err: %s"
msgstr "Şunlar için paket oylaması işlenemiyor:%s. hata: %s"
#: clean.go:170
msgid "Unable to remove %s: %s"
msgstr "Kaldırılamıyor %s: %s"
#: print.go:32
msgid "Version"
msgstr "Sürüm"
#: print.go:50
msgid "Votes"
msgstr "Oylar"
#: print.go:87
msgid "Yay version v%s"
msgstr "Yay sürümü v%s"
#: pkg/menus/menu.go:49
msgid "[N]one"
msgstr "[N]Hiçbiri"
#: clean.go:83
msgid ""
"\n"
"Build directory:"
msgstr ""
"\n"
"Yapım dizini:"
#: pkg/db/ialpm/alpm.go:201 pkg/dep/dep_graph.go:740
msgid ""
"\n"
"Enter a number (default=1): "
msgstr ""
"\n"
"Bir sayı girin (varsayılan=1):"
#: pkg/settings/errors.go:29
msgid "aborting due to user"
msgstr "kullanıcı nedeniyle iptal ediliyor"
#: pkg/settings/parser/parser.go:608
msgid "argument '-' specified without input on stdin"
msgstr "stdin üzerinde girdi olmadan '-' argümanı kullanıldı"
#: local_install.go:26
msgid "cannot find PKGBUILD and .SRCINFO in directory"
msgstr "dizin içerisinde PKGBUILD ve .SRCINFO bulunamadı"
#: pkg/sync/build/pkg_archive.go:148
msgid "cannot find package name: %v"
msgstr "paket ismi bulunamadı: %v"
#: pkg/sync/build/errors.go:30
msgid "could not find PKGDEST for: %s"
msgstr "PKGDEST dizini bulunamadı: %s"
#: errors.go:9
msgid "could not find all required packages"
msgstr "tüm gereken paketler bulunamadı"
#: pkg/sync/build/errors.go:61
msgid "could not find any package archives listed in %s"
msgstr "arşivde listelenmiş paketler bulunamadı %s"
#: pkg/sync/build/errors.go:50 pkg/upgrade/service.go:287
msgid "dependency"
msgstr "bağımlılık"
#: pkg/vcs/vcs.go:96 pkg/vcs/vcs.go:100
msgid "devel check for package failed: '%s' encountered an error"
msgstr "Paket gelişim kontrolü başarısız: '%s' hatayla karşılaştı"
#: pkg/menus/edit_menu.go:110
msgid "editor did not exit successfully, aborting: %s"
msgstr "düzenleyici başarılı bir şekilde çıkış yapmadı, iptal ediliyor: %s"
#: pkg/sync/workdir/aur_source.go:24
msgid "error downloading sources: %s"
msgstr "kaynaklar indirilirken hata: %s"
#: pkg/download/errors.go:25
msgid "error fetching %s: %s"
msgstr "%s getirilirken hata: %s"
#: pkg/sync/build/errors.go:9
msgid "error installing repo packages"
msgstr "depo paketleri indirilirken hata"
#: pkg/sync/build/installer.go:266 pkg/sync/build/installer.go:270
msgid "error installing:"
msgstr "kurarken hata:"
#: pkg/sync/build/installer.go:233 pkg/sync/build/installer.go:237
msgid "error making: %s"
msgstr "yapılırken hata: %s"
#: pkg/sync/workdir/merge.go:24
msgid "error merging %s: %s"
msgstr "%s birleştirilirken hata: %s"
#: pkg/download/unified.go:59
msgid "error reading %s"
msgstr "%s okunurken hata"
#: sync.go:36
msgid "error refreshing databases"
msgstr "veritabanlarını yenilenirken hata"
#: pkg/sync/workdir/clean.go:51 pkg/sync/workdir/merge.go:17
msgid "error resetting %s: %s"
msgstr "%s yenilenirken hata: %s"
#: pkg/sync/build/errors.go:53
msgid "error updating package install reason to %s"
msgstr "paket yükleme nedeni güncellenirken hata oluştu %s"
#: pkg/sync/build/errors.go:48
msgid "explicit"
msgstr "açık"
#: pkg/settings/errors.go:23
msgid "failed to create directory '%s': %s"
msgstr "'%s' dizini oluşturulamadı: %s"
#: pkg/settings/config.go:281
msgid "failed to open config file '%s': %s"
msgstr "'%s' yapılandırma dosyası açılamadı: %s"
#: pkg/sync/srcinfo/service.go:114
msgid "failed to parse %s -- skipping: %s"
msgstr "%s çözümlenirken hata -- atlanıyor: %s"
#: pkg/sync/srcinfo/service.go:118
msgid "failed to parse %s: %s"
msgstr "%s çözümlenirken hata: %s"
#: local_install.go:77
msgid "failed to parse .SRCINFO"
msgstr " .SRCINFO ayrıştırılamadı"
#: pkg/settings/config.go:291
msgid "failed to read config file '%s': %s"
msgstr "'%s' yapılandırma dosyası okunurken hata: %s"
#: pkg/cmd/graph/main.go:46 pkg/runtime/runtime.go:73
msgid "failed to retrieve aur Cache"
msgstr "aur Cache alınamadı"
#: pkg/upgrade/sources.go:27
msgid "ignoring package devel upgrade (no AUR info found):"
msgstr "devel paketi yükseltmesi yok sayılıyor (AUR bilgisi bulunamadı):"
#: pkg/text/errors.go:8
msgid "input too long"
msgstr "girdi çok uzun"
#: pkg/db/ialpm/alpm.go:222 pkg/dep/dep_graph.go:761
msgid "invalid number: %s"
msgstr "geçersiz sayı: %s"
#: pkg/settings/parser/parser.go:174
msgid "invalid option '%s'"
msgstr "geçersiz seçenek '%s'"
#: cmd.go:197
msgid "invalid option: '--deps' and '--explicit' may not be used together"
msgstr "geçersiz seçenek: '--deps' ve '--explicit' beraber kullanılamaz"
#: pkg/download/abs.go:22
msgid "invalid repository"
msgstr "geçersiz depo"
#: pkg/db/ialpm/alpm.go:227 pkg/dep/dep_graph.go:767
msgid "invalid value: %d is not between %d and %d"
msgstr "geçersiz değer: %d, %d ile %d arasında değil."
#: pkg/sync/srcinfo/pgp/keys.go:110
msgid "no keys to import"
msgstr "aktarılacak anahtar yok"
#: pkg/query/errors.go:20
msgid "no query was executed"
msgstr "hiçbir sorgu çalıştırılmadı"
#: local_install.go:66
msgid "no target directories specified"
msgstr "hiç bir hedef dizin belirtilmedi"
#: pkg/text/input.go:48
msgid "no"
msgstr "hayır"
#: pkg/sync/build/installer.go:242
msgid "nothing to install for %s"
msgstr "indirelecek bir şey yok %s"
#: pkg/settings/parser/parser.go:164
msgid "only one operation may be used at a time"
msgstr "bir seferde sadece bir işlem kullanılabilir"
#: pkg/cmd/graph/main.go:70
msgid "only one target is allowed"
msgstr ""
#: print.go:187
msgid "package '%s' was not found"
msgstr "'%s' paketi bulunamadı"
#: pkg/download/errors.go:15
msgid "package not found in AUR"
msgstr "paket AUR'da bulunamadı"
#: pkg/download/abs.go:23
msgid "package not found in repos"
msgstr "paket depolarda bulunamadı"
#: pkg/upgrade/service.go:292
#, fuzzy
msgid "package"
msgid_plural "packages"
msgstr[0] "Paket Temeli"
msgstr[1] "Paket Temeli"
#: pkg/sync/srcinfo/pgp/keys.go:100
msgid "problem importing keys"
msgstr "anahtarları aktarırken sorun"
#: clean.go:105
msgid "removing AUR packages from cache..."
msgstr "AUR paketleri önbellekten siliniyor..."
#: clean.go:178 pkg/sync/workdir/clean.go:41
msgid "removing untracked AUR files from cache..."
msgstr "izlenmemiş AUR paketleri önbellekten siliniyor..."
#: pkg/sync/build/errors.go:38
msgid "the PKGDEST for %s is listed by makepkg but does not exist: %s"
msgstr "%s için olan PKGDEST, makepkg'da listelenmiş ancak bulunmuyor: %s"
#: pkg/sync/sync.go:45
msgid "there is nothing to do"
msgstr "yapılacak bir şey yok"
#: pkg/db/ialpm/alpm.go:247
msgid "unable to CreateHandle: %s"
msgstr "CreateHandle yapılamıyor: %s"
#: cmd.go:186
msgid "unhandled operation"
msgstr "beklenmeyen işlem"
#: cmd.go:450
msgid "unknown-version"
msgstr "bilinmeyen sürüm"
#: pkg/text/input.go:47
msgid "yes"
msgstr "evet"
#~ msgid " (Target"
#~ msgstr "(Hedef"
#~ msgid " (Wanted by: "
#~ msgstr "(Tarafından isteniyor:"
#~ msgid "%s not satisfied, flushing install queue"
#~ msgstr "%s sağlanmamış, indirme kuyruğu boşaltılıyor"
#~ msgid "Checking for conflicts..."
#~ msgstr "Çakışmalar kontrol ediliyor..."
#~ msgid "Checking for inner conflicts..."
#~ msgstr "İç çakışmalar kontrol ediliyor..."
#~ msgid "Conflicting packages will have to be confirmed manually"
#~ msgstr "Çakışan paketlerin elle onaylanması gerekecek"
#~ msgid "Inner conflicts found:"
#~ msgstr "İç çakışmalar bulundu:"
#~ msgid "Installing %s will remove:"
#~ msgstr "%s paketini kurmak şunları kaldıracak:"
#~ msgid "PKGBUILD up to date, Skipping (%d/%d): %s"
#~ msgstr "PKGBUILD güncel, Atlanıyor (%d/%d): %s"
#~ msgid "Package conflicts found:"
#~ msgstr "Paket çakışmaları bulundu:"
#~ msgid "Packages to upgrade."
#~ msgstr "Yükseltilecek paketler."
#~ msgid "Querying AUR..."
#~ msgstr "AUR sorgulanıyor..."
#~ msgid "could not find all required packages:"
#~ msgstr "tüm gereken paketler bulunamadı:"
#~ msgid "could not find srcinfo for: %s"
#~ msgstr "%s : İçin scrinfo bulunamadı"
#~ msgid "package conflicts can not be resolved with noconfirm, aborting"
#~ msgstr "paket çakışmaları noconfirm ile çözümlenemedi, iptal ediliyor"
================================================
FILE: po/uk.po
================================================
#
# Translators:
# Volodymyr Markiv , 2021
# Igor Lukyanov, 2023
# Andrii Lytvyn, 2023
# null null, 2023
# Andrii Yermak, 2024
#
msgid ""
msgstr ""
"Last-Translator: Andrii Yermak, 2024\n"
"Language-Team: Ukrainian (https://app.transifex.com/yay-1/teams/123732/uk/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: uk\n"
"Plural-Forms: nplurals=4; plural=(n % 1 == 0 && n % 10 == 1 && n % 100 != 11 ? 0 : n % 1 == 0 && n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14) ? 1 : n % 1 == 0 && (n % 10 ==0 || (n % 10 >=5 && n % 10 <=9) || (n % 100 >=11 && n % 100 <=14 )) ? 2: 3);\n"
"X-Generator: xgotext\n"
#: clean.go:83
msgid ""
"\n"
"Build directory:"
msgstr ""
"\n"
"Каталог збірки:"
#: pkg/db/ialpm/alpm.go:201 pkg/dep/dep_graph.go:740
msgid ""
"\n"
"Enter a number (default=1): "
msgstr ""
"\n"
"Введіть число (типово=1):"
#: pkg/menus/menu.go:32
msgid " (Build Files Exist)"
msgstr "(Файли Збірки Існують)"
#: pkg/menus/menu.go:27
msgid " (Installed)"
msgstr "(Встановлено)"
#: cmd.go:453
msgid " [Installed]"
msgstr "[Встановлено]"
#: cmd.go:410 vote.go:36
msgid " there is nothing to do"
msgstr "тут нічого робити"
#: pkg/menus/menu.go:49
msgid "%s [A]ll [Ab]ort [I]nstalled [No]tInstalled or (1 2 3, 1-3, ^4)"
msgstr ""
"%s [A]Всі [Ab]Скасувати [I]Встановлені [No]Невстановлені чи (1 2 3, 1-3, ^4)"
#: pkg/sync/build/installer.go:308
msgid "%s already made -- skipping build"
msgstr "%s вже зроблено -- пропуск збірки"
#: pkg/menus/edit_menu.go:57
msgid "%s is not set"
msgstr "%s не задано"
#: pkg/settings/exe/cmd_builder.go:257
msgid "%s is present."
msgstr "%s вже існує."
#: pkg/dep/dep_graph.go:460 pkg/sync/build/installer.go:305
msgid "%s is up to date -- skipping"
msgstr "%s є актуальним -- пропуск"
#: pkg/upgrade/service.go:291
msgid "%s to upgrade/install."
msgstr "%s для оновлення/встановлення."
#: pkg/upgrade/service.go:285
msgid "%s will also be installed for this operation."
msgstr "%s також буде встановлено для цієї операції."
#: pkg/sync/srcinfo/pgp/keys.go:124
msgid "%s, required by: %s"
msgstr "%s потребується для: %s"
#: pkg/menus/diff_menu.go:49
msgid "%s: No changes -- skipping"
msgstr "%s: Нема змін -- пропуск"
#: pkg/query/filter.go:22
msgid "%s: can't use target with option --aur -- skipping"
msgstr "%s: не вдалося застосувати ціль з параметром --aur -- пропуск"
#: pkg/query/filter.go:17
msgid "%s: can't use target with option --repo -- skipping"
msgstr "%s: не вдалося застосувати ціль з параметром --repo -- пропуск"
#: pkg/upgrade/sources.go:57
msgid "%s: ignoring package upgrade (%s => %s)"
msgstr "%s: ігнорування оновлення пакунка ( %s => %s)"
#: pkg/query/aur_warnings.go:46
msgid "%s: local (%s) is newer than AUR (%s)"
msgstr "%s: встановлений пакунок (%s) новіший ніж у AUR (%s)"
#: vote.go:51
msgid ""
"%s: please set AUR_USERNAME and AUR_PASSWORD environment variables for "
"voting"
msgstr ""
"%s: будь ласка встановіть змінні середовища AUR_USERNAME та AUR_PASSWORD для"
" цього голосування"
#: pkg/download/unified.go:192
msgid "(%d/%d) Downloaded PKGBUILD from ABS: %s"
msgstr "(%d/%d) PKGBUILD завантажено з ABS: %s"
#: pkg/download/aur.go:92 pkg/download/unified.go:188
msgid "(%d/%d) Downloaded PKGBUILD: %s"
msgstr "(%d/%d) Завантажено PKGBUILD: %s"
#: pkg/download/aur.go:82
msgid "(%d/%d) Failed to download PKGBUILD: %s"
msgstr "(%d/%d) Не вдалося завантажити PKGBUILD: %s"
#: pkg/sync/srcinfo/service.go:109
msgid "(%d/%d) Parsing SRCINFO: %s"
msgstr "(%d/%d) Аналіз SRCINFO: %s"
#: pkg/query/types.go:103 pkg/query/types.go:72
msgid "(Installed)"
msgstr "(Встановлено)"
#: pkg/query/types.go:101 pkg/query/types.go:70
msgid "(Installed: %s)"
msgstr "(Встановлено: %s)"
#: pkg/query/types.go:61
msgid "(Orphaned)"
msgstr "(Осиротілий)"
#: pkg/query/types.go:65
msgid "(Out-of-date: %s)"
msgstr "(Застарілий: %s)"
#: pkg/dep/dep_graph.go:75
msgid "AUR"
msgstr "AUR"
#: print.go:44
msgid "AUR URL"
msgstr "AUR URL"
#: pkg/menus/edit_menu.go:58
msgid "Add %s or %s to your environment variables"
msgstr "Додайте %s або %s до змінних середовища"
#: main.go:60
msgid "Avoid running yay as root/sudo."
msgstr "Не запускайте yay від імені root/sudo."
#: pkg/dep/dep_graph.go:63
msgid "Check Dependency"
msgstr "Перевірити залежності"
#: print.go:41
msgid "Check Deps"
msgstr "Залежності Перевірки"
#: pkg/upgrade/service.go:89
msgid "Checking development packages..."
msgstr "Перевірка пакунків розробки..."
#: pkg/sync/workdir/clean.go:45
msgid "Cleaning (%d/%d): %s"
msgstr "Очистка (%d/%d): %s"
#: print.go:42
msgid "Conflicts With"
msgstr "Конфліктує з"
#: pkg/menus/clean_menu.go:62
msgid "Deleting (%d/%d): %s"
msgstr "Видалення (%d/%d): %s"
#: pkg/dep/dep_graph.go:61
msgid "Dependency"
msgstr "Залежність"
#: print.go:38
msgid "Depends On"
msgstr "Залежить Від"
#: print.go:33
msgid "Description"
msgstr "Опис"
#: pkg/menus/diff_menu.go:160
msgid "Diffs to show?"
msgstr "Показати зміни?"
#: pkg/settings/migrations.go:25
msgid "Disable 'provides' setting by default"
msgstr "Вимкнути параметр 'provides' за замовчуванням"
#: clean.go:78
msgid "Do you want to remove ALL AUR packages from cache?"
msgstr "Ви бажаєте видалити ВСІ пакунки AUR з кешу?"
#: clean.go:95
msgid "Do you want to remove ALL untracked AUR files?"
msgstr "Ви бажаєте видалити ВСІ невідстежувані файли AUR?"
#: clean.go:80
msgid "Do you want to remove all other AUR packages from cache?"
msgstr "Ви бажаєте видалити всі інші пакунки AUR з кешу?"
#: pkg/menus/edit_menu.go:61
msgid "Edit PKGBUILD with?"
msgstr "Редагувати PKGBUILD за допомогою?"
#: pkg/query/errors.go:13
msgid "Error during AUR search: %s\n"
msgstr "Помилка під час пошуку у AUR: %s\n"
#: pkg/upgrade/service.go:295
msgid "Excluding packages may cause partial upgrades and break systems"
msgstr ""
"Виключення пакетів може привести до часткового оновлення та зламати систему "
#: pkg/dep/dep_graph.go:60
msgid "Explicit"
msgstr "Явно"
#: print.go:91
msgid "Explicitly installed packages: %s"
msgstr "Явно встановлені пакунки: %s"
#: pkg/dep/dep_graph.go:437 pkg/dep/dep_graph.go:535
msgid "Failed to find AUR package for"
msgstr "Не вдалося знайти AUR пакет для"
#: pkg/sync/build/installer.go:120
msgid "Failed to install layer, rolling up to next layer."
msgstr "Не вдалося встановити шар, перекочуємось до наступного шару."
#: pkg/sync/build/errors.go:16
msgid ""
"Failed to install the following packages. Manual intervention is required:"
msgstr "Не вдалося встановити наступні пакети. Необхідне ручне втручання:"
#: print.go:45
msgid "First Submitted"
msgstr "Вперше Завантажений"
#: pkg/query/aur_warnings.go:83
msgid "Flagged Out Of Date AUR Packages:"
msgstr "Пакунки з AUR, що позначені як застарілі:"
#: print.go:90
msgid "Foreign installed packages: %s"
msgstr "Сторонні встановлені пакунки: %s"
#: pkg/vcs/vcs.go:144
msgid "Found git repo: %s"
msgstr "Знайдено git-репозиторій: %s"
#: vcs.go:72
msgid "GenDB finished. No packages were installed"
msgstr "GenDB завершено. Пакунки не встановлено"
#: print.go:36
msgid "Groups"
msgstr "Групи"
#: pkg/sync/srcinfo/pgp/keys.go:88
msgid "Import?"
msgstr "Імпортувати?"
#: pkg/sync/srcinfo/pgp/keys.go:97
msgid "Importing keys with gpg..."
msgstr "Імпортування ключів за допомогою gpg..."
#: print.go:46
msgid "Keywords"
msgstr "Ключові слова"
#: print.go:47
msgid "Last Modified"
msgstr "Остання зміна"
#: print.go:35
msgid "Licenses"
msgstr "Ліцензії"
#: pkg/dep/dep_graph.go:77
msgid "Local"
msgstr "Локальний"
#: print.go:48
msgid "Maintainer"
msgstr "Супроводжувач"
#: pkg/dep/dep_graph.go:62
msgid "Make Dependency"
msgstr "Зібрати залежність"
#: print.go:40
msgid "Make Deps"
msgstr "Залежності Збірки"
#: pkg/dep/dep_graph.go:79
msgid "Missing"
msgstr "Нестачає"
#: pkg/query/aur_warnings.go:75
msgid "Missing AUR Debug Packages:"
msgstr "Відсутні налагоджувальні пакунки з AUR:"
#: print.go:31
msgid "Name"
msgstr "Ім'я"
#: pkg/dep/dep_graph.go:442 pkg/dep/dep_graph.go:548
msgid "No AUR package found for"
msgstr "Не знайдено AUR пакету для"
#: pkg/dep/dep_graph.go:182
msgid "No package found for"
msgstr "Пакунок не знайдено для"
#: print.go:225
msgid "None"
msgstr "Ні"
#: print.go:39
msgid "Optional Deps"
msgstr "Необов'язкові Залежності"
#: pkg/query/aur_warnings.go:79
msgid "Orphan (unmaintained) AUR Packages:"
msgstr "Сирі (що не супроводжуються) AUR Пакети:"
#: print.go:53 print.go:55
msgid "Out-of-date"
msgstr "Застарілий"
#: pkg/sync/srcinfo/pgp/keys.go:115
msgid "PGP keys need importing:"
msgstr "Необхідно імпортувати ключі PGP:"
#: pkg/sync/workdir/preparer.go:252
msgid "PKGBUILD up to date, skipping download: %s"
msgstr "PKGBUILD оновлений, пропускаємо завантаження: %s"
#: pkg/menus/edit_menu.go:130
msgid "PKGBUILDs to edit?"
msgstr "Редагувати PKGBUILDs?"
#: print.go:61
msgid "Package Base"
msgstr "Група Пакунків"
#: print.go:60
msgid "Package Base ID"
msgstr "ID Групи Пакунків"
#: pkg/query/aur_warnings.go:71
msgid "Packages not in AUR:"
msgstr "Пакети не в AUR:"
#: pkg/menus/clean_menu.go:54
msgid "Packages to cleanBuild?"
msgstr "Пакунки для чистої збірки?"
#: pkg/dep/dep_graph.go:202
msgid "Packages to exclude"
msgstr "Пакети для виключення"
#: pkg/upgrade/service.go:294
msgid "Packages to exclude: (eg: \"1 2 3\", \"1-3\", \"^4\" or repo name)"
msgstr ""
"Пакунки, які потрібно виключити: (наприклад, \"1 2 3\", \"1-3\", \"^4\" або "
"назва репозиторію)"
#: cmd.go:392
msgid "Packages to install (eg: 1 2 3, 1-3 or ^4)"
msgstr "Пакунки для встановлення (наприклад: 1 2 3, 1-3 або ^4)"
#: print.go:49
msgid "Popularity"
msgstr "Популярність"
#: pkg/menus/diff_menu.go:172 pkg/menus/edit_menu.go:143
msgid "Proceed with install?"
msgstr "Продовжити встановлення?"
#: print.go:37
msgid "Provides"
msgstr "Забезпечує"
#: pkg/sync/workdir/preparer.go:125
msgid "Remove make dependencies after install?"
msgstr "Видалити залежності збірки після встановлення?"
#: print.go:43
msgid "Replaces"
msgstr "Замінює"
#: pkg/db/ialpm/alpm.go:191 print.go:30
msgid "Repository"
msgstr "Репозиторій"
#: pkg/dep/dep_graph.go:730
msgid "Repository AUR"
msgstr "Репозиторій AUR"
#: pkg/dep/dep_graph.go:78
msgid "SRCINFO"
msgstr "SRCINFO"
#: pkg/upgrade/service.go:71
msgid "Searching AUR for updates..."
msgstr "Пошук оновлень у AUR..."
#: pkg/upgrade/service.go:159
msgid "Searching databases for updates..."
msgstr "Пошук оновлень у базах даних..."
#: pkg/query/query_builder.go:214
msgid "Showing repo packages only"
msgstr "Показано лише пакунки з репозиторіїв"
#: print.go:95
msgid "Size of pacman cache %s: %s"
msgstr "Розмір кешу pacman %s: %s"
#: print.go:98
msgid "Size of yay cache %s: %s"
msgstr "Розмір кешу yay %s: %s"
#: print.go:62
msgid "Snapshot URL"
msgstr "URL знімка"
#: pkg/dep/dep_graph.go:76
msgid "Sync"
msgstr "Синхронізувати"
#: print.go:100
msgid "Ten biggest packages:"
msgstr "Десять найбільших пакунків:"
#: pkg/sync/sync.go:124
msgid "The following packages are not compatible with your architecture:"
msgstr "Наступні пакунки не сумісні з вашою архітектурою:"
#: pkg/db/ialpm/alpm.go:179 pkg/dep/dep_graph.go:726
msgid "There are %[1]d providers available for %[2]s:"
msgstr "Існує %[1]d постачальників для %[2]s:"
#: pkg/settings/exe/cmd_builder.go:258
msgid "There may be another Pacman instance running. Waiting..."
msgstr "Можливо, запущено інший екземпляр Pacman. Очікування..."
#: print.go:92
msgid "Total Size occupied by packages: %s"
msgstr "Загальний розмір, зайнятий пакунками: %s"
#: print.go:89
msgid "Total installed packages: %s"
msgstr "Загальна кількість встановлених пакунків: %s"
#: pkg/sync/sync.go:132
msgid "Try to build them anyway?"
msgstr "Все одно спробувати їх побудувати?"
#: print.go:34
msgid "URL"
msgstr "URL"
#: clean.go:194 pkg/menus/clean_menu.go:65 pkg/menus/clean_menu.go:71
msgid "Unable to clean:"
msgstr "Неможливо очистити:"
#: get.go:42 get.go:74
msgid "Unable to find the following packages:"
msgstr "Не вдалося знайти наступні пакунки:"
#: vote.go:20
msgid "Unable to handle package vote for: %s. err: %s"
msgstr "Неможливо обробити голос за пакет: %s. помилка: %s"
#: clean.go:170
msgid "Unable to remove %s: %s"
msgstr "Неможливо видалити %s: %s"
#: print.go:32
msgid "Version"
msgstr "Версія"
#: print.go:50
msgid "Votes"
msgstr "Голоси"
#: print.go:87
msgid "Yay version v%s"
msgstr "Версія Yay v%s"
#: pkg/menus/menu.go:49
msgid "[N]one"
msgstr "[N]Ні"
#: pkg/settings/errors.go:29
msgid "aborting due to user"
msgstr "зупинка через користувача"
#: pkg/settings/parser/parser.go:608
msgid "argument '-' specified without input on stdin"
msgstr "аргумент '-' вказаний без вводу у stdin"
#: local_install.go:26
msgid "cannot find PKGBUILD and .SRCINFO in directory"
msgstr "не вдалося знайти PKGBUILD та .SRCINFO в директорії"
#: pkg/sync/build/pkg_archive.go:148
msgid "cannot find package name: %v"
msgstr "не вдається знайти пакунок: %v"
#: pkg/sync/build/errors.go:30
msgid "could not find PKGDEST for: %s"
msgstr "не вдалося знайти PKGDEST для: %s"
#: errors.go:9
msgid "could not find all required packages"
msgstr "не знайдені всі необхідні пакунки"
#: pkg/sync/build/errors.go:61
msgid "could not find any package archives listed in %s"
msgstr "не знайдено жодного пакунку з перелічених у %s"
#: pkg/sync/build/errors.go:50 pkg/upgrade/service.go:286
msgid "dependency"
msgstr "залежність"
#: pkg/vcs/vcs.go:100 pkg/vcs/vcs.go:96
msgid "devel check for package failed: '%s' encountered an error"
msgstr "розробницька перевірка пакунку не вдалася: '%s' сталася помилка"
#: pkg/menus/edit_menu.go:110
msgid "editor did not exit successfully, aborting: %s"
msgstr "вихід з редактора був успішним, переривання: %s"
#: pkg/sync/workdir/aur_source.go:24
msgid "error downloading sources: %s"
msgstr "помилка завантаження вихідного коду:"
#: pkg/download/errors.go:25
msgid "error fetching %s: %s"
msgstr "помилка отримання %s: %s"
#: pkg/sync/build/errors.go:9
msgid "error installing repo packages"
msgstr "помилка встановлення пакунку з репозиторіїв"
#: pkg/sync/build/installer.go:266 pkg/sync/build/installer.go:270
msgid "error installing:"
msgstr "помилка встановлення:"
#: pkg/sync/build/installer.go:233 pkg/sync/build/installer.go:237
msgid "error making: %s"
msgstr "помилка збірки: %s"
#: pkg/sync/workdir/merge.go:24
msgid "error merging %s: %s"
msgstr "помилка об'єднання %s: %s"
#: pkg/download/unified.go:59
msgid "error reading %s"
msgstr "помилка читання %s"
#: sync.go:36
msgid "error refreshing databases"
msgstr "помилка оновлення баз даних"
#: pkg/sync/workdir/clean.go:51 pkg/sync/workdir/merge.go:17
msgid "error resetting %s: %s"
msgstr "помилка скидання %s: %s"
#: pkg/sync/build/errors.go:53
msgid "error updating package install reason to %s"
msgstr "помилка оновлення причини встановлення пакунка для %s"
#: pkg/sync/build/errors.go:48
msgid "explicit"
msgstr "явно"
#: pkg/settings/errors.go:23
msgid "failed to create directory '%s': %s"
msgstr "не вдалося створити каталог '%s': %s"
#: pkg/settings/config.go:281
msgid "failed to open config file '%s': %s"
msgstr "не вдалося відкрити файл конфігурації '%s': %s"
#: pkg/sync/srcinfo/service.go:114
msgid "failed to parse %s -- skipping: %s"
msgstr "не вдалося проаналізувати %s -- пропуск: %s"
#: pkg/sync/srcinfo/service.go:118
msgid "failed to parse %s: %s"
msgstr "не вдалося проаналізувати %s: %s"
#: local_install.go:77
msgid "failed to parse .SRCINFO"
msgstr "не вдалося проаналізувати .SRCINFO"
#: pkg/settings/config.go:291
msgid "failed to read config file '%s': %s"
msgstr "не вдалося прочитати конфігураційний файл '%s': %s"
#: pkg/cmd/graph/main.go:46 pkg/runtime/runtime.go:73
msgid "failed to retrieve aur Cache"
msgstr "не вдалося отримати кеш AUR"
#: pkg/upgrade/sources.go:27
msgid "ignoring package devel upgrade (no AUR info found):"
msgstr "ігноруємо оновлення розробки пакунка (нема інформації в AUR):"
#: pkg/text/errors.go:8
msgid "input too long"
msgstr "введення занадто довге"
#: pkg/db/ialpm/alpm.go:222 pkg/dep/dep_graph.go:761
msgid "invalid number: %s"
msgstr "некоректний номер: %s"
#: pkg/settings/parser/parser.go:174
msgid "invalid option '%s'"
msgstr "некоректний параметр '%s'"
#: cmd.go:197
msgid "invalid option: '--deps' and '--explicit' may not be used together"
msgstr ""
"некоректний параметр: '--deps' і '--explicit' не можна використовувати разом"
#: pkg/download/abs.go:22
msgid "invalid repository"
msgstr "недійсний репозиторій"
#: pkg/db/ialpm/alpm.go:227 pkg/dep/dep_graph.go:767
msgid "invalid value: %d is not between %d and %d"
msgstr "некоректне значення: %d не знаходиться між %d та %d"
#: pkg/text/input.go:48
msgid "no"
msgstr "ні"
#: pkg/sync/srcinfo/pgp/keys.go:110
msgid "no keys to import"
msgstr "нема ключів для імпорту"
#: pkg/query/errors.go:20
msgid "no query was executed"
msgstr "запит не виконано"
#: local_install.go:66
msgid "no target directories specified"
msgstr "цільова директорія не вказана"
#: pkg/sync/build/installer.go:242
msgid "nothing to install for %s"
msgstr "нема чого встановити для %s"
#: pkg/settings/parser/parser.go:164
msgid "only one operation may be used at a time"
msgstr "одночасно можна використовувати лише одну операцію"
#: pkg/cmd/graph/main.go:70
msgid "only one target is allowed"
msgstr "дозволена лише одна ціль"
#: pkg/upgrade/service.go:291
msgid "package"
msgid_plural "packages"
msgstr[0] "пакунок"
msgstr[1] "пакунки"
msgstr[2] "пакунків"
msgstr[3] "пакунків"
#: print.go:187
msgid "package '%s' was not found"
msgstr "пакунок '%s' не знайдено"
#: pkg/download/errors.go:15
msgid "package not found in AUR"
msgstr "пакунок не знайдено в AUR"
#: pkg/download/abs.go:23
msgid "package not found in repos"
msgstr "пакунок не знайдено в репозиторіях"
#: pkg/sync/srcinfo/pgp/keys.go:100
msgid "problem importing keys"
msgstr "проблема імпортування ключів"
#: clean.go:105
msgid "removing AUR packages from cache..."
msgstr "видалення пакунків AUR з кешу..."
#: clean.go:178 pkg/sync/workdir/clean.go:41
msgid "removing untracked AUR files from cache..."
msgstr "видалення невідстежених файлів AUR з кешу..."
#: pkg/sync/build/errors.go:38
msgid "the PKGDEST for %s is listed by makepkg but does not exist: %s"
msgstr "PKGDEST для %s перераховано makepkg, але не існує: %s"
#: pkg/sync/sync.go:45
msgid "there is nothing to do"
msgstr "нема чого робити"
#: pkg/db/ialpm/alpm.go:247
msgid "unable to CreateHandle: %s"
msgstr "неможливо створити Handle:"
#: cmd.go:186
msgid "unhandled operation"
msgstr "необроблена операція"
#: cmd.go:450
msgid "unknown-version"
msgstr "невідома версія"
#: pkg/text/input.go:47
msgid "yes"
msgstr "так"
================================================
FILE: po/vi.po
================================================
#
# Translators:
# Tín Nguyễn, 2022
# Katoji Rikito, 2023
# Nguyễn Tri Phương, 2024
# Phong Phạm Thanh, 2025
# Nguyễn Hoàng Minh , 2025
#
msgid ""
msgstr ""
"Last-Translator: Nguyễn Hoàng Minh , 2025\n"
"Language-Team: Vietnamese (https://app.transifex.com/yay-1/teams/123732/vi/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: vi\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: xgotext\n"
#: clean.go:83
msgid ""
"\n"
"Build directory:"
msgstr ""
"\n"
"Vị trí lưu tệp xây dựng:"
#: pkg/db/ialpm/alpm.go:201 pkg/dep/dep_graph.go:740
msgid ""
"\n"
"Enter a number (default=1): "
msgstr ""
"\n"
"Hãy nhập số (mặc định = 1)"
#: pkg/menus/menu.go:32
msgid " (Build Files Exist)"
msgstr "(Tệp Xây Dựng Đã Tồn Tại)"
#: pkg/menus/menu.go:27
msgid " (Installed)"
msgstr "(Đã cài đặt)"
#: cmd.go:453
msgid " [Installed]"
msgstr "[Đã cài đặt]"
#: cmd.go:410 vote.go:36
msgid " there is nothing to do"
msgstr "không có tác vụ cần làm"
#: pkg/menus/menu.go:49
msgid "%s [A]ll [Ab]ort [I]nstalled [No]tInstalled or (1 2 3, 1-3, ^4)"
msgstr ""
"%s [A] Tất cả [Ab] Hủy [I] Đã được cài đặt [No] Chưa được cài đặt hoặc (1"
" 2 3, 1-3, ^4)"
#: pkg/sync/build/installer.go:308
msgid "%s already made -- skipping build"
msgstr "%s đã được xây dựng -- đang bỏ qua"
#: pkg/menus/edit_menu.go:57
msgid "%s is not set"
msgstr "%s chưa được đặt"
#: pkg/settings/exe/cmd_builder.go:257
msgid "%s is present."
msgstr "%s có tồn tại"
#: pkg/dep/dep_graph.go:460 pkg/sync/build/installer.go:305
msgid "%s is up to date -- skipping"
msgstr "%s đã là phiên bản mới nhất -- đang bỏ qua"
#: pkg/upgrade/service.go:291
msgid "%s to upgrade/install."
msgstr "%s để nâng cấp/cài đặt"
#: pkg/upgrade/service.go:285
msgid "%s will also be installed for this operation."
msgstr "%s cũng sẽ được cài đặt cho hoạt động này."
#: pkg/sync/srcinfo/pgp/keys.go:124
msgid "%s, required by: %s"
msgstr "%s, cần thiết cho: %s"
#: pkg/menus/diff_menu.go:49
msgid "%s: No changes -- skipping"
msgstr "%s: Không có thay đổi nào -- đang bỏ qua"
#: pkg/query/filter.go:22
msgid "%s: can't use target with option --aur -- skipping"
msgstr "%s: không thể sử dụng mục tiêu với lựa chọn --aur -- đang bỏ qua"
#: pkg/query/filter.go:17
msgid "%s: can't use target with option --repo -- skipping"
msgstr "%s: không thể sử dụng mục tiêu với lựa chọn --repo -- đang bỏ qua"
#: pkg/upgrade/sources.go:57
msgid "%s: ignoring package upgrade (%s => %s)"
msgstr "%s: đang bỏ qua cập nhật gói (%s => %s)"
#: pkg/query/aur_warnings.go:46
msgid "%s: local (%s) is newer than AUR (%s)"
msgstr "%s: phiên bản cục bộ (%s) mới hơn phiên bản từ AUR (%s)"
#: vote.go:51
msgid ""
"%s: please set AUR_USERNAME and AUR_PASSWORD environment variables for "
"voting"
msgstr "%s: hãy đặt biến AUR_USERNAME và AUR_PASSWORD cho việc bỏ phiếu"
#: pkg/download/unified.go:192
msgid "(%d/%d) Downloaded PKGBUILD from ABS: %s"
msgstr "(%d/%d) Đã tải PKGBUILD từ ABS: %s"
#: pkg/download/aur.go:92 pkg/download/unified.go:188
msgid "(%d/%d) Downloaded PKGBUILD: %s"
msgstr "(%d/%d) Đã tải PKGBUILD: %s"
#: pkg/download/aur.go:82
msgid "(%d/%d) Failed to download PKGBUILD: %s"
msgstr "(%d/%d) Tải PKGBUILD thất bại: %s"
#: pkg/sync/srcinfo/service.go:109
msgid "(%d/%d) Parsing SRCINFO: %s"
msgstr "(%d/%d) Đang đọc SRCINFO: %s"
#: pkg/query/types.go:103 pkg/query/types.go:72
msgid "(Installed)"
msgstr "(Đã cài đặt)"
#: pkg/query/types.go:101 pkg/query/types.go:70
msgid "(Installed: %s)"
msgstr "(Đã cài đặt: %s)"
#: pkg/query/types.go:61
msgid "(Orphaned)"
msgstr "(Đơn lẻ)"
#: pkg/query/types.go:65
msgid "(Out-of-date: %s)"
msgstr "(Lỗi thời: %s)"
#: pkg/dep/dep_graph.go:75
msgid "AUR"
msgstr "AUR"
#: print.go:44
msgid "AUR URL"
msgstr "URL của AUR"
#: pkg/menus/edit_menu.go:58
msgid "Add %s or %s to your environment variables"
msgstr "Thêm %s hoặc %s làm vào các biến môi trường của bạn"
#: main.go:60
msgid "Avoid running yay as root/sudo."
msgstr "Tránh chạy yay khi là root hoặc dùng sudo."
#: pkg/dep/dep_graph.go:63
msgid "Check Dependency"
msgstr "Kiểm tra gói phụ thuộc"
#: print.go:41
msgid "Check Deps"
msgstr "K.tra ph.thuộc"
#: pkg/upgrade/service.go:89
msgid "Checking development packages..."
msgstr "Đang kiểm tra các gói tự xây dựng..."
#: pkg/sync/workdir/clean.go:45
msgid "Cleaning (%d/%d): %s"
msgstr "Đang dọn (%d/%d): %s"
#: print.go:42
msgid "Conflicts With"
msgstr "Mâu thuẫn với"
#: pkg/menus/clean_menu.go:62
msgid "Deleting (%d/%d): %s"
msgstr "Đang xóa (%d/%d): %s"
#: pkg/dep/dep_graph.go:61
msgid "Dependency"
msgstr "Gói phụ thuộc"
#: print.go:38
msgid "Depends On"
msgstr "Phụ thuộc vào"
#: print.go:33
msgid "Description"
msgstr "Mô tả"
#: pkg/menus/diff_menu.go:160
msgid "Diffs to show?"
msgstr "Hiện ra các thay đổi?"
#: pkg/settings/migrations.go:25
msgid "Disable 'provides' setting by default"
msgstr "Tắt cài đặt 'provides' theo mặc định"
#: clean.go:78
msgid "Do you want to remove ALL AUR packages from cache?"
msgstr "Bạn có muốn loại bỏ TOÀN BỘ các gói từ AUR ra khỏi bộ nhớ không?"
#: clean.go:95
msgid "Do you want to remove ALL untracked AUR files?"
msgstr "Bạn có muốn loại bỏ TOÀN BỘ các tệp từ AUR không được theo dõi không?"
#: clean.go:80
msgid "Do you want to remove all other AUR packages from cache?"
msgstr ""
"Bạn có muốn loại bỏ toàn bộ các tệp AUR còn lại trong kho nhớ tạm không?"
#: pkg/menus/edit_menu.go:61
msgid "Edit PKGBUILD with?"
msgstr "Chỉnh sửa PKGBUILD bằng?"
#: pkg/query/errors.go:13
msgid "Error during AUR search: %s\n"
msgstr "Lỗi khi thực hiện tìm kiếm trong AUR: %s\n"
#: pkg/upgrade/service.go:295
msgid "Excluding packages may cause partial upgrades and break systems"
msgstr ""
"Việc bỏ qua các gói có thể khiến cho bản cập nhật không đầy đủ và làm hỏng "
"hệ thống"
#: pkg/dep/dep_graph.go:60
msgid "Explicit"
msgstr "Cụ thể"
#: print.go:91
msgid "Explicitly installed packages: %s"
msgstr "Các gói được cài đặt cụ thể: %s"
#: pkg/dep/dep_graph.go:437 pkg/dep/dep_graph.go:535
msgid "Failed to find AUR package for"
msgstr "Không thể tìm gói AUR cho"
#: pkg/sync/build/installer.go:120
msgid "Failed to install layer, rolling up to next layer."
msgstr "Cài đặt lớp thất bại, đang đến lớp tiếp theo."
#: pkg/sync/build/errors.go:16
msgid ""
"Failed to install the following packages. Manual intervention is required:"
msgstr "Đã thất bại khi cài đặt những gói sau. Yêu cầu can thiệp thủ công:"
#: print.go:45
msgid "First Submitted"
msgstr "Lần nộp đầu tiên"
#: pkg/query/aur_warnings.go:83
msgid "Flagged Out Of Date AUR Packages:"
msgstr "Các gói AUR bị đánh dấu lỗi thời:"
#: print.go:90
msgid "Foreign installed packages: %s"
msgstr "Gói ngoại lai đã được cài đặt: %s"
#: pkg/vcs/vcs.go:144
msgid "Found git repo: %s"
msgstr "Tìm thấy kho chứa git: %s"
#: vcs.go:72
msgid "GenDB finished. No packages were installed"
msgstr "Hoàn thành GenDB. Không có gói được cài đặt"
#: print.go:36
msgid "Groups"
msgstr "Nhóm"
#: pkg/sync/srcinfo/pgp/keys.go:88
msgid "Import?"
msgstr "Nhập?"
#: pkg/sync/srcinfo/pgp/keys.go:97
msgid "Importing keys with gpg..."
msgstr "Đang nhập khóa với gpg..."
#: print.go:46
msgid "Keywords"
msgstr "Từ khóa"
#: print.go:47
msgid "Last Modified"
msgstr "Lần sửa cuối"
#: print.go:35
msgid "Licenses"
msgstr "Giấy phép"
#: pkg/dep/dep_graph.go:77
msgid "Local"
msgstr "Cục bộ"
#: print.go:48
msgid "Maintainer"
msgstr "Người bảo trì"
#: pkg/dep/dep_graph.go:62
msgid "Make Dependency"
msgstr "Gói phụ thuộc cho make"
#: print.go:40
msgid "Make Deps"
msgstr "Gói ph.thuộc make"
#: pkg/dep/dep_graph.go:79
msgid "Missing"
msgstr "Đang thiếu"
#: pkg/query/aur_warnings.go:75
msgid "Missing AUR Debug Packages:"
msgstr "Đang thiếu gói AUR gỡ lỗi:"
#: print.go:31
msgid "Name"
msgstr "Tên"
#: pkg/dep/dep_graph.go:442 pkg/dep/dep_graph.go:548
msgid "No AUR package found for"
msgstr "Không tìm thấy gói AUR cho"
#: pkg/dep/dep_graph.go:182
msgid "No package found for"
msgstr "Không tìm thấy gói cho"
#: print.go:225
msgid "None"
msgstr "Không có"
#: print.go:39
msgid "Optional Deps"
msgstr "Gói phụ thuộc không bắt buộc"
#: pkg/query/aur_warnings.go:79
msgid "Orphan (unmaintained) AUR Packages:"
msgstr "Gói AUR đơn lẻ (không được bảo trì):"
#: print.go:53 print.go:55
msgid "Out-of-date"
msgstr "Lỗi thời"
#: pkg/sync/srcinfo/pgp/keys.go:115
msgid "PGP keys need importing:"
msgstr "Khóa PGP cần nhập:"
#: pkg/sync/workdir/preparer.go:252
msgid "PKGBUILD up to date, skipping download: %s"
msgstr "Tệp PKGBUILD đã ở bản mới nhất, đang bỏ qua: %s"
#: pkg/menus/edit_menu.go:130
msgid "PKGBUILDs to edit?"
msgstr "Những tệp PKGBUILD cần chỉnh sửa? "
#: print.go:61
msgid "Package Base"
msgstr "Gói gốc"
#: print.go:60
msgid "Package Base ID"
msgstr "ID của gói gốc"
#: pkg/query/aur_warnings.go:71
msgid "Packages not in AUR:"
msgstr "Gói không ở trong AUR:"
#: pkg/menus/clean_menu.go:54
msgid "Packages to cleanBuild?"
msgstr "Những gói để chạy cleanBuild (xây dựng từ nguồn)?"
#: pkg/dep/dep_graph.go:202
msgid "Packages to exclude"
msgstr "Những gói bỏ lại"
#: pkg/upgrade/service.go:294
msgid "Packages to exclude: (eg: \"1 2 3\", \"1-3\", \"^4\" or repo name)"
msgstr "Những gói bỏ lại (v.d.: \"1 2 3\", \"1-3\", \"^4\" hay tên kho chứa)"
#: cmd.go:392
msgid "Packages to install (eg: 1 2 3, 1-3 or ^4)"
msgstr "Những gói cài đặt (vd: 1 2 3, 1-3 hoặc ^4)"
#: print.go:49
msgid "Popularity"
msgstr "Độ phổ biến"
#: pkg/menus/diff_menu.go:172 pkg/menus/edit_menu.go:143
msgid "Proceed with install?"
msgstr "Tiếp tục cài đặt?"
#: print.go:37
msgid "Provides"
msgstr "Cung cấp"
#: pkg/sync/workdir/preparer.go:125
msgid "Remove make dependencies after install?"
msgstr "Gỡ phụ thuộc xây dựng gói sau khi cài đặt?"
#: print.go:43
msgid "Replaces"
msgstr "Thay thế"
#: pkg/db/ialpm/alpm.go:191 print.go:30
msgid "Repository"
msgstr "Kho chứa"
#: pkg/dep/dep_graph.go:730
msgid "Repository AUR"
msgstr "Kho chứa AUR"
#: pkg/dep/dep_graph.go:78
msgid "SRCINFO"
msgstr "SRCINFO"
#: pkg/upgrade/service.go:71
msgid "Searching AUR for updates..."
msgstr "Đang tìm kiếm phiên bản cập nhật từ AUR..."
#: pkg/upgrade/service.go:159
msgid "Searching databases for updates..."
msgstr "Đang tìm kiếm phiên bản cập nhật từ cơ sở dữ liệu..."
#: pkg/query/query_builder.go:214
msgid "Showing repo packages only"
msgstr "Chỉ hiện gói từ kho chứa"
#: print.go:95
msgid "Size of pacman cache %s: %s"
msgstr "Kích cỡ bộ nhớ tạm pacman %s: %s"
#: print.go:98
msgid "Size of yay cache %s: %s"
msgstr "Kích cỡ bộ nhớ tạm yay %s: %s"
#: print.go:62
msgid "Snapshot URL"
msgstr "URL Snapshot"
#: pkg/dep/dep_graph.go:76
msgid "Sync"
msgstr "Đồng bộ hóa"
#: print.go:100
msgid "Ten biggest packages:"
msgstr "Mười gói lớn nhất:"
#: pkg/sync/sync.go:124
msgid "The following packages are not compatible with your architecture:"
msgstr "Những gói sau đây không phù hợp với kiến trúc máy tính của bạn:"
#: pkg/db/ialpm/alpm.go:179 pkg/dep/dep_graph.go:726
msgid "There are %[1]d providers available for %[2]s:"
msgstr "Có %[1]d nhà phát hành cho %[2]s:"
#: pkg/settings/exe/cmd_builder.go:258
msgid "There may be another Pacman instance running. Waiting..."
msgstr "Có thể đang có một tiến trình Pacman khác đang chạy. Đang đợi..."
#: print.go:92
msgid "Total Size occupied by packages: %s"
msgstr "Tổng kích cỡ chiếm dụng bởi các gói: %s"
#: print.go:89
msgid "Total installed packages: %s"
msgstr "Tổng các gói đã cài đặt: %s"
#: pkg/sync/sync.go:132
msgid "Try to build them anyway?"
msgstr "Vẫn xây dựng gói?"
#: print.go:34
msgid "URL"
msgstr "URL"
#: clean.go:194 pkg/menus/clean_menu.go:65 pkg/menus/clean_menu.go:71
msgid "Unable to clean:"
msgstr "Không thể dọn:"
#: get.go:42 get.go:74
msgid "Unable to find the following packages:"
msgstr "Không thể tìm thấy những gói sau:"
#: vote.go:20
msgid "Unable to handle package vote for: %s. err: %s"
msgstr "Không thể xử lý phiếu bầu cho gói: %s. Lỗi: %s"
#: clean.go:170
msgid "Unable to remove %s: %s"
msgstr "Không thể xóa %s:%s"
#: print.go:32
msgid "Version"
msgstr "Phiên bản"
#: print.go:50
msgid "Votes"
msgstr "Số lượng bầu chọn"
#: print.go:87
msgid "Yay version v%s"
msgstr "Phiên bản yay số v%s"
#: pkg/menus/menu.go:49
msgid "[N]one"
msgstr "[N] Không có"
#: pkg/settings/errors.go:29
msgid "aborting due to user"
msgstr "hủy bởi người dùng"
#: pkg/settings/parser/parser.go:608
msgid "argument '-' specified without input on stdin"
msgstr "đối số '-' được dùng mà không có đầu vào từ stdin"
#: local_install.go:26
msgid "cannot find PKGBUILD and .SRCINFO in directory"
msgstr "không tìm được PKGBUILD và .SRCINFO trong thư mục"
#: pkg/sync/build/pkg_archive.go:148
msgid "cannot find package name: %v"
msgstr "không thể tìm thấy gói: %v"
#: pkg/sync/build/errors.go:30
msgid "could not find PKGDEST for: %s"
msgstr "không thể tìm thấy PKGDEST cho: %s"
#: errors.go:9
msgid "could not find all required packages"
msgstr "không thể tìm thấy các gói được yêu cầu"
#: pkg/sync/build/errors.go:61
msgid "could not find any package archives listed in %s"
msgstr "không thể tìm thấy các gói lưu trữ được liệt kê ở %s"
#: pkg/sync/build/errors.go:50 pkg/upgrade/service.go:286
msgid "dependency"
msgstr "phần phụ thuộc"
#: pkg/vcs/vcs.go:100 pkg/vcs/vcs.go:96
msgid "devel check for package failed: '%s' encountered an error"
msgstr "thất bại khi kiểm tra gói tự xây dựng: %s đã bị lỗi"
#: pkg/menus/edit_menu.go:110
msgid "editor did not exit successfully, aborting: %s"
msgstr "phần mềm chỉnh sửa không thoát thành công, đang ngắt: %s"
#: pkg/sync/workdir/aur_source.go:24
msgid "error downloading sources: %s"
msgstr "có lỗi trong khi đang tải nguồn: %s"
#: pkg/download/errors.go:25
msgid "error fetching %s: %s"
msgstr "có lỗi trong khi thu thập %s: %s"
#: pkg/sync/build/errors.go:9
msgid "error installing repo packages"
msgstr "có lỗi trong khi đang tải gói từ kho mã nguồn (repo)"
#: pkg/sync/build/installer.go:266 pkg/sync/build/installer.go:270
msgid "error installing:"
msgstr "có lỗi trong khi cài đặt:"
#: pkg/sync/build/installer.go:233 pkg/sync/build/installer.go:237
msgid "error making: %s"
msgstr "có lỗi trong khi tạo: %s"
#: pkg/sync/workdir/merge.go:24
msgid "error merging %s: %s"
msgstr "có lỗi trong khi hợp nhất %s: %s"
#: pkg/download/unified.go:59
msgid "error reading %s"
msgstr "có lỗi trong khi đọc tệp %s"
#: sync.go:36
msgid "error refreshing databases"
msgstr "có lỗi trong khi cập nhật cơ sở dữ liệu"
#: pkg/sync/workdir/clean.go:51 pkg/sync/workdir/merge.go:17
msgid "error resetting %s: %s"
msgstr "có lỗi trong khi đưa về trạng thái mặc định %s: %s"
#: pkg/sync/build/errors.go:53
msgid "error updating package install reason to %s"
msgstr "có lỗi trong khi đang cập nhật lý do tải gói sang %s"
#: pkg/sync/build/errors.go:48
msgid "explicit"
msgstr "cụ thể"
#: pkg/settings/errors.go:23
msgid "failed to create directory '%s': %s"
msgstr "tạo thư mục '%s' thất bại: %s"
#: pkg/settings/config.go:281
msgid "failed to open config file '%s': %s"
msgstr "mở tệp cài đặt '%s' thất bại: %s"
#: pkg/sync/srcinfo/service.go:114
msgid "failed to parse %s -- skipping: %s"
msgstr "thất bị khi đọc %s -- bỏ qua: %s"
#: pkg/sync/srcinfo/service.go:118
msgid "failed to parse %s: %s"
msgstr "thất bại khi đọc %s: %s"
#: local_install.go:77
msgid "failed to parse .SRCINFO"
msgstr "thất bại khi đọc .SRCINFO"
#: pkg/settings/config.go:291
msgid "failed to read config file '%s': %s"
msgstr "thất bại khi đọc tệp cài đặt '%s': %s"
#: pkg/cmd/graph/main.go:46 pkg/runtime/runtime.go:73
msgid "failed to retrieve aur Cache"
msgstr "tải kho nhớ AUR thất bại"
#: pkg/upgrade/sources.go:27
msgid "ignoring package devel upgrade (no AUR info found):"
msgstr ""
"bỏ qua cập nhật phần mềm tự xây dựng (không tìm thấy thông tin trên AUR):"
#: pkg/text/errors.go:8
msgid "input too long"
msgstr "nhập liệu quá dài"
#: pkg/db/ialpm/alpm.go:222 pkg/dep/dep_graph.go:761
msgid "invalid number: %s"
msgstr "số không hợp lệ: %s"
#: pkg/settings/parser/parser.go:174
msgid "invalid option '%s'"
msgstr "lựa chọn '%s' không hợp lệ"
#: cmd.go:197
msgid "invalid option: '--deps' and '--explicit' may not be used together"
msgstr ""
"lựa chọn không hợp lệ: '--deps' và '--explicit' không thể sử dụng cùng nhau"
#: pkg/download/abs.go:22
msgid "invalid repository"
msgstr "kho không hợp lệ"
#: pkg/db/ialpm/alpm.go:227 pkg/dep/dep_graph.go:767
msgid "invalid value: %d is not between %d and %d"
msgstr "giá trị không hợp lệ: %d không nằm giữa %d và %d"
#: pkg/text/input.go:48
msgid "no"
msgstr "không"
#: pkg/sync/srcinfo/pgp/keys.go:110
msgid "no keys to import"
msgstr "không có keys để thêm vào"
#: pkg/query/errors.go:20
msgid "no query was executed"
msgstr "không có truy vấn được thực hành"
#: local_install.go:66
msgid "no target directories specified"
msgstr "không có thư mục được xác định"
#: pkg/sync/build/installer.go:242
msgid "nothing to install for %s"
msgstr "không có gì để tải cho %s"
#: pkg/settings/parser/parser.go:164
msgid "only one operation may be used at a time"
msgstr "chỉ được sử dụng một lệnh trong một lần gọi"
#: pkg/cmd/graph/main.go:70
msgid "only one target is allowed"
msgstr "chỉ cho phép 1 đối tượng duy nhất"
#: pkg/upgrade/service.go:291
msgid "package"
msgid_plural "packages"
msgstr[0] "các gói"
#: print.go:187
msgid "package '%s' was not found"
msgstr "không tìm thấy gói '%s'"
#: pkg/download/errors.go:15
msgid "package not found in AUR"
msgstr "không tìm được gói trong AUR"
#: pkg/download/abs.go:23
msgid "package not found in repos"
msgstr "không tìm được gói trong các kho"
#: pkg/sync/srcinfo/pgp/keys.go:100
msgid "problem importing keys"
msgstr "có vấn đề trong khi thêm keys"
#: clean.go:105
msgid "removing AUR packages from cache..."
msgstr "đang xóa các gói AUR khỏi bộ nhớ đệm..."
#: clean.go:178 pkg/sync/workdir/clean.go:41
msgid "removing untracked AUR files from cache..."
msgstr "đang loại bỏ các tệp AUR không được theo dõi khỏi bộ nhớ"
#: pkg/sync/build/errors.go:38
msgid "the PKGDEST for %s is listed by makepkg but does not exist: %s"
msgstr ""
"tệp PKGDEST của %s được gọi trong lệnh makepkg nhưng không tồn tại: %s"
#: pkg/sync/sync.go:45
msgid "there is nothing to do"
msgstr "không thể làm được gì cả"
#: pkg/db/ialpm/alpm.go:247
msgid "unable to CreateHandle: %s"
msgstr "không sử dùng được hàm CreateHandle: %s"
#: cmd.go:186
msgid "unhandled operation"
msgstr "lệnh không được xử lý"
#: cmd.go:450
msgid "unknown-version"
msgstr "phiên-bản-không-xác-định"
#: pkg/text/input.go:47
msgid "yes"
msgstr "có"
================================================
FILE: po/vi_VN.po
================================================
#
# Translators:
# su va, 2023
# Tín Nguyễn, 2024
# Nguyễn Tri Phương, 2024
# Phong Phạm Thanh, 2025
# Minh Nguyen , 2025
#
msgid ""
msgstr ""
"Last-Translator: Minh Nguyen , 2025\n"
"Language-Team: Vietnamese (Viet Nam) (https://app.transifex.com/yay-1/teams/123732/vi_VN/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: vi_VN\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: xgotext\n"
#: clean.go:83
msgid ""
"\n"
"Build directory:"
msgstr ""
"\n"
"Vị trí lưu tệp dựng:"
#: pkg/db/ialpm/alpm.go:201 pkg/dep/dep_graph.go:740
msgid ""
"\n"
"Enter a number (default=1): "
msgstr ""
"\n"
"Hãy nhập số (Mặc định = 1)"
#: pkg/menus/menu.go:32
msgid " (Build Files Exist)"
msgstr "(File để Build có sẵn)"
#: pkg/menus/menu.go:27
msgid " (Installed)"
msgstr "(Đã cài đặt)"
#: cmd.go:453
msgid " [Installed]"
msgstr "[Đã cài đặt]"
#: cmd.go:410 vote.go:36
msgid " there is nothing to do"
msgstr "không còn gì "
#: pkg/menus/menu.go:49
msgid "%s [A]ll [Ab]ort [I]nstalled [No]tInstalled or (1 2 3, 1-3, ^4)"
msgstr "%s [A] Tất cả [Ab] Hủy [I] Đã cài [No] Chưa cài hoặc (1 2 3, 1-3, ^4)"
#: pkg/sync/build/installer.go:308
msgid "%s already made -- skipping build"
msgstr "%s đã make -- bỏ qua bước "
#: pkg/menus/edit_menu.go:57
msgid "%s is not set"
msgstr "%s chưa được đặt"
#: pkg/settings/exe/cmd_builder.go:257
msgid "%s is present."
msgstr "%s đã có sẵn."
#: pkg/dep/dep_graph.go:460 pkg/sync/build/installer.go:305
msgid "%s is up to date -- skipping"
msgstr "%s đã cập nhật -- bỏ qua"
#: pkg/upgrade/service.go:291
msgid "%s to upgrade/install."
msgstr "%s để nâng cấp/cài đặt."
#: pkg/upgrade/service.go:285
msgid "%s will also be installed for this operation."
msgstr "%s cũng sẽ được cài đặt để thực hiện việc này."
#: pkg/sync/srcinfo/pgp/keys.go:124
msgid "%s, required by: %s"
msgstr "%s, bắt buộc cho:%s"
#: pkg/menus/diff_menu.go:49
msgid "%s: No changes -- skipping"
msgstr "%s: Không thay đổi -- bỏ qua"
#: pkg/query/filter.go:22
msgid "%s: can't use target with option --aur -- skipping"
msgstr "%s: không thể dùng target với lựa chọn --aur -- bỏ qua"
#: pkg/query/filter.go:17
msgid "%s: can't use target with option --repo -- skipping"
msgstr "%s: không thể dùng đối tượng với lựa chọn --repo -- bỏ qua"
#: pkg/upgrade/sources.go:57
msgid "%s: ignoring package upgrade (%s => %s)"
msgstr "%s: bỏ qua việc nâng cấp gói (%s => %s)"
#: pkg/query/aur_warnings.go:46
msgid "%s: local (%s) is newer than AUR (%s)"
msgstr "%s: bản trên máy (%s) mới hơn bản trên AUR (%s)"
#: vote.go:51
msgid ""
"%s: please set AUR_USERNAME and AUR_PASSWORD environment variables for "
"voting"
msgstr "%s: set các biến môi trường AUR_USERNAME và AUR_PASSWORD để bầu chọn"
#: pkg/download/unified.go:192
msgid "(%d/%d) Downloaded PKGBUILD from ABS: %s"
msgstr "(%d/%d) Đã tải xuống PKGBUILD từ ABS: %s"
#: pkg/download/aur.go:92 pkg/download/unified.go:188
msgid "(%d/%d) Downloaded PKGBUILD: %s"
msgstr "(%d/%d) Đã tải xuống PKGBUILD: %s"
#: pkg/download/aur.go:82
msgid "(%d/%d) Failed to download PKGBUILD: %s"
msgstr "(%d/%d) Tải xuống PKGBUILD thất bại: %s"
#: pkg/sync/srcinfo/service.go:109
msgid "(%d/%d) Parsing SRCINFO: %s"
msgstr "(%d/%d) Đang đọc SRCINFO: %s"
#: pkg/query/types.go:103 pkg/query/types.go:72
msgid "(Installed)"
msgstr "(Đã cài)"
#: pkg/query/types.go:101 pkg/query/types.go:70
msgid "(Installed: %s)"
msgstr "(Đã cài: %s)"
#: pkg/query/types.go:61
msgid "(Orphaned)"
msgstr "(Đã bị bỏ)"
#: pkg/query/types.go:65
msgid "(Out-of-date: %s)"
msgstr "(Chưa được cập nhật: %s)"
#: pkg/dep/dep_graph.go:75
msgid "AUR"
msgstr "AUR"
#: print.go:44
msgid "AUR URL"
msgstr "AUR "
#: pkg/menus/edit_menu.go:58
msgid "Add %s or %s to your environment variables"
msgstr "Thêm %s hoặc %s vào các biến môi trường của bạn"
#: main.go:60
msgid "Avoid running yay as root/sudo."
msgstr "Tránh chạy yay bằng root/sudo."
#: pkg/dep/dep_graph.go:63
msgid "Check Dependency"
msgstr "Kiểm tra gói phụ thuộc"
#: print.go:41
msgid "Check Deps"
msgstr "Kiểm tra các gói phụ thuộc"
#: pkg/upgrade/service.go:89
msgid "Checking development packages..."
msgstr "Đang kiểm tra các gói dev"
#: pkg/sync/workdir/clean.go:45
msgid "Cleaning (%d/%d): %s"
msgstr "Đang dọn (%d/%d): %s"
#: print.go:42
msgid "Conflicts With"
msgstr "Xung đột với"
#: pkg/menus/clean_menu.go:62
msgid "Deleting (%d/%d): %s"
msgstr "Đang xóa (%d/%d): %s"
#: pkg/dep/dep_graph.go:61
msgid "Dependency"
msgstr "Gói phụ thuộc"
#: print.go:38
msgid "Depends On"
msgstr "Phụ thuộc vào"
#: print.go:33
msgid "Description"
msgstr "Mô tả"
#: pkg/menus/diff_menu.go:160
msgid "Diffs to show?"
msgstr "Hiện ra sự khác nhau?"
#: pkg/settings/migrations.go:25
msgid "Disable 'provides' setting by default"
msgstr "Tắt cài đặt 'provides' theo mặc định"
#: clean.go:78
msgid "Do you want to remove ALL AUR packages from cache?"
msgstr "Bạn có muốn loại bỏ TOÀN BỘ gói AUR ra khỏi kho nhớ tạm không?"
#: clean.go:95
msgid "Do you want to remove ALL untracked AUR files?"
msgstr "Bạn có muốn loại bỏ TOÀN BỘ các tệp AUR chưa được theo dõi không?"
#: clean.go:80
msgid "Do you want to remove all other AUR packages from cache?"
msgstr ""
"Bạn có muốn loại bỏ toàn bộ các tệp AUR còn lại trong kho nhớ tạm không?"
#: pkg/menus/edit_menu.go:61
msgid "Edit PKGBUILD with?"
msgstr "Chỉnh sửa PKGBUILD bằng?"
#: pkg/query/errors.go:13
msgid "Error during AUR search: %s\n"
msgstr "Lỗi khi thực hiện tìm kiếm AUR: %s\n"
#: pkg/upgrade/service.go:295
msgid "Excluding packages may cause partial upgrades and break systems"
msgstr ""
"Bỏ lại các gói này có thể khiến cho bản cập nhật không đầy đủ và làm hỏng hệ"
" thống"
#: pkg/dep/dep_graph.go:60
msgid "Explicit"
msgstr "Cụ thể"
#: print.go:91
msgid "Explicitly installed packages: %s"
msgstr "Các gói được cài đặt cụ thể:"
#: pkg/dep/dep_graph.go:437 pkg/dep/dep_graph.go:535
msgid "Failed to find AUR package for"
msgstr "Không thể tìm gói AUR cho"
#: pkg/sync/build/installer.go:120
msgid "Failed to install layer, rolling up to next layer."
msgstr "Cài đặt layer thất bại, quay trở lại layer tiếp theo"
#: pkg/sync/build/errors.go:16
msgid ""
"Failed to install the following packages. Manual intervention is required:"
msgstr "Cài đặt những gói dưới đây thất bại. Yêu cầu can thiệp thủ công:"
#: print.go:45
msgid "First Submitted"
msgstr "Bản Nộp Đầu Tiên"
#: pkg/query/aur_warnings.go:83
msgid "Flagged Out Of Date AUR Packages:"
msgstr "Gói AUR Đã Bị Đánh Dấu Lỗi Thời"
#: print.go:90
msgid "Foreign installed packages: %s"
msgstr "Gói ngoại lai đã được cài đặt: %s"
#: pkg/vcs/vcs.go:144
msgid "Found git repo: %s"
msgstr "Tìm thấy kho chứa git: %s"
#: vcs.go:72
msgid "GenDB finished. No packages were installed"
msgstr "Hoàn thành GenDB. Không cài đặt gói nào."
#: print.go:36
msgid "Groups"
msgstr "Nhóm"
#: pkg/sync/srcinfo/pgp/keys.go:88
msgid "Import?"
msgstr "Nhập?"
#: pkg/sync/srcinfo/pgp/keys.go:97
msgid "Importing keys with gpg..."
msgstr "Đang nhập khóa với gpg..."
#: print.go:46
msgid "Keywords"
msgstr "Từ khóa"
#: print.go:47
msgid "Last Modified"
msgstr "Sửa Lần Cuối"
#: print.go:35
msgid "Licenses"
msgstr "Giấy Phép"
#: pkg/dep/dep_graph.go:77
msgid "Local"
msgstr "Cục Bộ"
#: print.go:48
msgid "Maintainer"
msgstr "Người Bảo Trì"
#: pkg/dep/dep_graph.go:62
msgid "Make Dependency"
msgstr "Yêu Cầu Make"
#: print.go:40
msgid "Make Deps"
msgstr "Y/c Make"
#: pkg/dep/dep_graph.go:79
msgid "Missing"
msgstr "Thiếu"
#: pkg/query/aur_warnings.go:75
msgid "Missing AUR Debug Packages:"
msgstr "Thiếu gói AUR gỡ lỗi:"
#: print.go:31
msgid "Name"
msgstr "Tên"
#: pkg/dep/dep_graph.go:442 pkg/dep/dep_graph.go:548
msgid "No AUR package found for"
msgstr "Không tìm thấy gói AUR cho"
#: pkg/dep/dep_graph.go:182
msgid "No package found for"
msgstr "Không tìm thấy gói cho"
#: print.go:225
msgid "None"
msgstr "Không Có"
#: print.go:39
msgid "Optional Deps"
msgstr "Phụ Thuộc Không Bắt Buộc"
#: pkg/query/aur_warnings.go:79
msgid "Orphan (unmaintained) AUR Packages:"
msgstr "Gói AUR Orphan (không được bảo trì)"
#: print.go:53 print.go:55
msgid "Out-of-date"
msgstr "Lỗi thời"
#: pkg/sync/srcinfo/pgp/keys.go:115
msgid "PGP keys need importing:"
msgstr "Khóa PGP cần nhập:"
#: pkg/sync/workdir/preparer.go:252
msgid "PKGBUILD up to date, skipping download: %s"
msgstr "PKGBUILD đã ở bản mới nhất, bỏ qua tải xuống: %s"
#: pkg/menus/edit_menu.go:130
msgid "PKGBUILDs to edit?"
msgstr "Những PKGBUILD cần chỉnh sửa? "
#: print.go:61
msgid "Package Base"
msgstr "Base Của Gói"
#: print.go:60
msgid "Package Base ID"
msgstr "Base ID Của Gói"
#: pkg/query/aur_warnings.go:71
msgid "Packages not in AUR:"
msgstr "Gói không ở trong AUR:"
#: pkg/menus/clean_menu.go:54
msgid "Packages to cleanBuild?"
msgstr "Những gói để chạy cleanBuild?"
#: pkg/dep/dep_graph.go:202
msgid "Packages to exclude"
msgstr "Những gói bỏ lại"
#: pkg/upgrade/service.go:294
msgid "Packages to exclude: (eg: \"1 2 3\", \"1-3\", \"^4\" or repo name)"
msgstr "Những gói bỏ lại (ví dụ: \"1 2 3\", \"1-3\", \"^4\" hay tên kho chứa)"
#: cmd.go:392
msgid "Packages to install (eg: 1 2 3, 1-3 or ^4)"
msgstr "Những gói cài đặt (vd: 1 2 3, 1-3 hoặc ^4)"
#: print.go:49
msgid "Popularity"
msgstr "Độ Phổ Biến"
#: pkg/menus/diff_menu.go:172 pkg/menus/edit_menu.go:143
msgid "Proceed with install?"
msgstr "Tiếp tục cài đặt?"
#: print.go:37
msgid "Provides"
msgstr "Cung Cấp"
#: pkg/sync/workdir/preparer.go:125
msgid "Remove make dependencies after install?"
msgstr "Gỡ phụ thuộc xây dựng gói sau khi cài đặt?"
#: print.go:43
msgid "Replaces"
msgstr "Thay Thế"
#: pkg/db/ialpm/alpm.go:191 print.go:30
msgid "Repository"
msgstr "Kho Chứa"
#: pkg/dep/dep_graph.go:730
msgid "Repository AUR"
msgstr "Kho Chứa AUR"
#: pkg/dep/dep_graph.go:78
msgid "SRCINFO"
msgstr "SRCINFO"
#: pkg/upgrade/service.go:71
msgid "Searching AUR for updates..."
msgstr "Đang tìm kiếm cập nhật từ AUR..."
#: pkg/upgrade/service.go:159
msgid "Searching databases for updates..."
msgstr "Đang tìm kiếm cập nhật từ cơ sở dữ liệu..."
#: pkg/query/query_builder.go:214
msgid "Showing repo packages only"
msgstr "Chỉ hiện gói từ kho chứa"
#: print.go:95
msgid "Size of pacman cache %s: %s"
msgstr "Kích cỡ kho nhớ tạm pacman %s: %s"
#: print.go:98
msgid "Size of yay cache %s: %s"
msgstr "Kích cỡ kho nhớ tạm yay %s: %s"
#: print.go:62
msgid "Snapshot URL"
msgstr "URL Snapshot"
#: pkg/dep/dep_graph.go:76
msgid "Sync"
msgstr "Đồng Bộ Hóa"
#: print.go:100
msgid "Ten biggest packages:"
msgstr "Mười gói lớn nhất:"
#: pkg/sync/sync.go:124
msgid "The following packages are not compatible with your architecture:"
msgstr "Những gói sau đây không phù hợp với kiến trúc máy tính của bạn:"
#: pkg/db/ialpm/alpm.go:179 pkg/dep/dep_graph.go:726
msgid "There are %[1]d providers available for %[2]s:"
msgstr "Có %[1]d nhà phát hành cho %[2]d:"
#: pkg/settings/exe/cmd_builder.go:258
msgid "There may be another Pacman instance running. Waiting..."
msgstr "Có thể đang có một tiến trình Pacman khác đang chạy. Đang đợi..."
#: print.go:92
msgid "Total Size occupied by packages: %s"
msgstr "Tổng Kích Cỡ chiếm dụng bởi các gói:"
#: print.go:89
msgid "Total installed packages: %s"
msgstr "Tổng các gói đã cài đặt: %s"
#: pkg/sync/sync.go:132
msgid "Try to build them anyway?"
msgstr "Vẫn xây dựng gói?"
#: print.go:34
msgid "URL"
msgstr "URL"
#: clean.go:194 pkg/menus/clean_menu.go:65 pkg/menus/clean_menu.go:71
msgid "Unable to clean:"
msgstr "Không thể dọn:"
#: get.go:42 get.go:74
msgid "Unable to find the following packages:"
msgstr "Không thể tìm thấy những gói sau:"
#: vote.go:20
msgid "Unable to handle package vote for: %s. err: %s"
msgstr "Không thể xử lý phiếu bầu cho gói: %s. Lỗi: %s"
#: clean.go:170
msgid "Unable to remove %s: %s"
msgstr "Không thể xóa %s:%s"
#: print.go:32
msgid "Version"
msgstr "Phiên bản"
#: print.go:50
msgid "Votes"
msgstr "Bầu chọn"
#: print.go:87
msgid "Yay version v%s"
msgstr "Phiên bản yay số %s"
#: pkg/menus/menu.go:49
msgid "[N]one"
msgstr "[N] Không có"
#: pkg/settings/errors.go:29
msgid "aborting due to user"
msgstr "hủy bởi người dùng"
#: pkg/settings/parser/parser.go:608
msgid "argument '-' specified without input on stdin"
msgstr "đối số '-' được dùng mà không có đầu vào từ stdin"
#: local_install.go:26
msgid "cannot find PKGBUILD and .SRCINFO in directory"
msgstr "không tìm được PKGBUILD và .SRCINFO trong thư mục"
#: pkg/sync/build/pkg_archive.go:148
msgid "cannot find package name: %v"
msgstr "không thể tìm thấy gói: %v"
#: pkg/sync/build/errors.go:30
msgid "could not find PKGDEST for: %s"
msgstr "không thể tìm thấy PKGDEST cho: %s"
#: errors.go:9
msgid "could not find all required packages"
msgstr "không thể tìm thấy các gói yêu cầu"
#: pkg/sync/build/errors.go:61
msgid "could not find any package archives listed in %s"
msgstr "không thể tìm thấy các gói lưu trữ được liệt kê ở %s"
#: pkg/sync/build/errors.go:50 pkg/upgrade/service.go:286
msgid "dependency"
msgstr "phần phụ thuộc"
#: pkg/vcs/vcs.go:100 pkg/vcs/vcs.go:96
msgid "devel check for package failed: '%s' encountered an error"
msgstr "không kiểm tra được gói tự xây dựng: %s đã bị lỗi"
#: pkg/menus/edit_menu.go:110
msgid "editor did not exit successfully, aborting: %s"
msgstr "phần mềm chỉnh sửa không thoát thành công, đang ngắt: %s"
#: pkg/sync/workdir/aur_source.go:24
msgid "error downloading sources: %s"
msgstr "lỗi khi đang tải nguồn: %s"
#: pkg/download/errors.go:25
msgid "error fetching %s: %s"
msgstr "lỗi khi thu thập %s: %s"
#: pkg/sync/build/errors.go:9
msgid "error installing repo packages"
msgstr "lỗi khi đang tải gói từ kho mã nguồn (repo)"
#: pkg/sync/build/installer.go:266 pkg/sync/build/installer.go:270
msgid "error installing:"
msgstr "có lỗi trong khi cài đặt:"
#: pkg/sync/build/installer.go:233 pkg/sync/build/installer.go:237
msgid "error making: %s"
msgstr "có lỗi trong khi tạo tệp:"
#: pkg/sync/workdir/merge.go:24
msgid "error merging %s: %s"
msgstr "có lỗi trong khi hợp nhất %s: %s"
#: pkg/download/unified.go:59
msgid "error reading %s"
msgstr "có lỗi trong khi đọc tệp %s"
#: sync.go:36
msgid "error refreshing databases"
msgstr "có lỗi trong khi làm mới dữ liệu"
#: pkg/sync/workdir/clean.go:51 pkg/sync/workdir/merge.go:17
msgid "error resetting %s: %s"
msgstr "lỗi khi đưa về trạng thái mặc định %s: %s"
#: pkg/sync/build/errors.go:53
msgid "error updating package install reason to %s"
msgstr "lỗi khi đang cập nhật lý do tải gói sang %s"
#: pkg/sync/build/errors.go:48
msgid "explicit"
msgstr "cụ thể"
#: pkg/settings/errors.go:23
msgid "failed to create directory '%s': %s"
msgstr "tạo thư mục '%s' thất bại: %s"
#: pkg/settings/config.go:281
msgid "failed to open config file '%s': %s"
msgstr "mở tệp cài đặt `%s` thất bại: %s"
#: pkg/sync/srcinfo/service.go:114
msgid "failed to parse %s -- skipping: %s"
msgstr "thất bị khi đọc %s -- sẽ bỏ qua: %s"
#: pkg/sync/srcinfo/service.go:118
msgid "failed to parse %s: %s"
msgstr "thất bại khi đọc %s: %s"
#: local_install.go:77
msgid "failed to parse .SRCINFO"
msgstr "thất bại khi đọc .SRCINFO"
#: pkg/settings/config.go:291
msgid "failed to read config file '%s': %s"
msgstr "thất bại khi đọc tệp cài đặt '%s': %s"
#: pkg/cmd/graph/main.go:46 pkg/runtime/runtime.go:73
msgid "failed to retrieve aur Cache"
msgstr "tải kho nhớ AUR thất bại"
#: pkg/upgrade/sources.go:27
msgid "ignoring package devel upgrade (no AUR info found):"
msgstr ""
"bỏ qua cập nhật phần mềm tự xây dựng (không tìm thấy thông tin trên AUR):"
#: pkg/text/errors.go:8
msgid "input too long"
msgstr "nhập liệu quá dài"
#: pkg/db/ialpm/alpm.go:222 pkg/dep/dep_graph.go:761
msgid "invalid number: %s"
msgstr "số không hợp lệ: %s"
#: pkg/settings/parser/parser.go:174
msgid "invalid option '%s'"
msgstr "lựa chọn '%s' không hợp lệ"
#: cmd.go:197
msgid "invalid option: '--deps' and '--explicit' may not be used together"
msgstr ""
"lựa chọn không hợp lệ: '--deps' và '--explicit' không thể sử dụng cùng nhau"
#: pkg/download/abs.go:22
msgid "invalid repository"
msgstr "kho không khả dụng"
#: pkg/db/ialpm/alpm.go:227 pkg/dep/dep_graph.go:767
msgid "invalid value: %d is not between %d and %d"
msgstr "giá trị không hợp lệ: %d không nằm giữa %d và %d"
#: pkg/text/input.go:48
msgid "no"
msgstr "không"
#: pkg/sync/srcinfo/pgp/keys.go:110
msgid "no keys to import"
msgstr "không có keys để thêm vào"
#: pkg/query/errors.go:20
msgid "no query was executed"
msgstr "không có truy vấn được thực hành"
#: local_install.go:66
msgid "no target directories specified"
msgstr "không có thư mục được xác định"
#: pkg/sync/build/installer.go:242
msgid "nothing to install for %s"
msgstr "không có gì để tải cho %s"
#: pkg/settings/parser/parser.go:164
msgid "only one operation may be used at a time"
msgstr "chỉ được sử dụng một lệnh trong một lần gọi"
#: pkg/cmd/graph/main.go:70
msgid "only one target is allowed"
msgstr "chỉ cho phép 1 đối tượng duy nhất"
#: pkg/upgrade/service.go:291
msgid "package"
msgid_plural "packages"
msgstr[0] "các gói"
#: print.go:187
msgid "package '%s' was not found"
msgstr "không tìm thấy gói'%s'"
#: pkg/download/errors.go:15
msgid "package not found in AUR"
msgstr "không tìm được gói trong AUR"
#: pkg/download/abs.go:23
msgid "package not found in repos"
msgstr "không tìm được gói trong các kho"
#: pkg/sync/srcinfo/pgp/keys.go:100
msgid "problem importing keys"
msgstr "có vấn đề trong khi thêm keys"
#: clean.go:105
msgid "removing AUR packages from cache..."
msgstr "đang xóa các gói AUR khỏi bộ nhớ đệm..."
#: clean.go:178 pkg/sync/workdir/clean.go:41
msgid "removing untracked AUR files from cache..."
msgstr "đang loại bỏ các tệp AUR không được theo dõi khỏi bộ nhớ"
#: pkg/sync/build/errors.go:38
msgid "the PKGDEST for %s is listed by makepkg but does not exist: %s"
msgstr ""
"tệp PKGDEST của %s được gọi trong lệnh makepkg nhưng không tồn tại: %s"
#: pkg/sync/sync.go:45
msgid "there is nothing to do"
msgstr "không thể làm được gì cả"
#: pkg/db/ialpm/alpm.go:247
msgid "unable to CreateHandle: %s"
msgstr "không sử dùng được hàm CreateHandle: %s"
#: cmd.go:186
msgid "unhandled operation"
msgstr "không thể xử lý hoạt động"
#: cmd.go:450
msgid "unknown-version"
msgstr "phiên-bản-không-xác-định"
#: pkg/text/input.go:47
msgid "yes"
msgstr "có"
================================================
FILE: po/zh_CN.po
================================================
#
# Translators:
# J G, 2022
# Moon Xanadu , 2023
# kanna 5, 2023
# Songer Hy, 2023
# Celestially H., 2023
# lakejason0 , 2023
# Alex Wang, 2023
# Xuekai Deng, 2024
# CloverGit, 2024
# qsdwindows <2687267056@qq.com>, 2025
# Haowen Shi, 2025
#
msgid ""
msgstr ""
"Last-Translator: Haowen Shi, 2025\n"
"Language-Team: Chinese (China) (https://app.transifex.com/yay-1/teams/123732/zh_CN/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: zh_CN\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: xgotext\n"
#: clean.go:83
msgid ""
"\n"
"Build directory:"
msgstr ""
"\n"
"构建目录:"
#: pkg/db/ialpm/alpm.go:201 pkg/dep/dep_graph.go:740
msgid ""
"\n"
"Enter a number (default=1): "
msgstr ""
"\n"
"输入数字 (默认=1): "
#: pkg/menus/menu.go:32
msgid " (Build Files Exist)"
msgstr " (构建文件已存在)"
#: pkg/menus/menu.go:27
msgid " (Installed)"
msgstr " (已安装)"
#: cmd.go:453
msgid " [Installed]"
msgstr " [已安装]"
#: cmd.go:410 vote.go:36
msgid " there is nothing to do"
msgstr " 今日无事可做"
#: pkg/menus/menu.go:49
msgid "%s [A]ll [Ab]ort [I]nstalled [No]tInstalled or (1 2 3, 1-3, ^4)"
msgstr "%s [A]全部 [Ab]中止 [I]已安装 [No]未安装 或 (1 2 3, 1-3, ^4)"
#: pkg/sync/build/installer.go:308
msgid "%s already made -- skipping build"
msgstr "%s 已生成 -- 跳过构建"
#: pkg/menus/edit_menu.go:57
msgid "%s is not set"
msgstr "未设置 %s"
#: pkg/settings/exe/cmd_builder.go:257
msgid "%s is present."
msgstr "%s 存在。"
#: pkg/dep/dep_graph.go:460 pkg/sync/build/installer.go:305
msgid "%s is up to date -- skipping"
msgstr "%s 是最新的 -- 跳过"
#: pkg/upgrade/service.go:291
msgid "%s to upgrade/install."
msgstr "%s 将被升级/安装。"
#: pkg/upgrade/service.go:285
msgid "%s will also be installed for this operation."
msgstr "%s 也会为此操作而被安装。"
#: pkg/sync/srcinfo/pgp/keys.go:124
msgid "%s, required by: %s"
msgstr "%s, 被以下需要: %s"
#: pkg/menus/diff_menu.go:49
msgid "%s: No changes -- skipping"
msgstr "%s: 没有更改 -- 跳过"
#: pkg/query/filter.go:22
msgid "%s: can't use target with option --aur -- skipping"
msgstr "%s: 不能将目标与选项 --aur 一起使用 -- 跳过"
#: pkg/query/filter.go:17
msgid "%s: can't use target with option --repo -- skipping"
msgstr "%s: 不能将目标与选项 --repo 一起使用 -- 跳过"
#: pkg/upgrade/sources.go:57
msgid "%s: ignoring package upgrade (%s => %s)"
msgstr "%s: 忽略包升级 (%s => %s)"
#: pkg/query/aur_warnings.go:46
msgid "%s: local (%s) is newer than AUR (%s)"
msgstr "%s: 本地 (%s) 比 AUR (%s) 更新"
#: vote.go:51
msgid ""
"%s: please set AUR_USERNAME and AUR_PASSWORD environment variables for "
"voting"
msgstr "%s: 请设置 AUR_USERNAME 与 AUR_PASSWORD 环境变量以投票"
#: pkg/download/unified.go:192
msgid "(%d/%d) Downloaded PKGBUILD from ABS: %s"
msgstr "(%d/%d) 从 ABS 下载了 PKGBUILD: %s"
#: pkg/download/aur.go:92 pkg/download/unified.go:188
msgid "(%d/%d) Downloaded PKGBUILD: %s"
msgstr "(%d/%d) 下载了 PKGBUILD: %s"
#: pkg/download/aur.go:82
msgid "(%d/%d) Failed to download PKGBUILD: %s"
msgstr "(%d/%d) 下载PKGBUILD失败"
#: pkg/sync/srcinfo/service.go:109
msgid "(%d/%d) Parsing SRCINFO: %s"
msgstr "(%d/%d) 正在解析 SRCINFO: %s"
#: pkg/query/types.go:103 pkg/query/types.go:72
msgid "(Installed)"
msgstr "(已安装)"
#: pkg/query/types.go:101 pkg/query/types.go:70
msgid "(Installed: %s)"
msgstr "(已安装: %s)"
#: pkg/query/types.go:61
msgid "(Orphaned)"
msgstr "(孤包)"
#: pkg/query/types.go:65
msgid "(Out-of-date: %s)"
msgstr "(过时的: %s)"
#: pkg/dep/dep_graph.go:75
msgid "AUR"
msgstr "AUR"
#: print.go:44
msgid "AUR URL"
msgstr "AUR URL"
#: pkg/menus/edit_menu.go:58
msgid "Add %s or %s to your environment variables"
msgstr "将 %s 或 %s 添加到你的环境变量"
#: main.go:60
msgid "Avoid running yay as root/sudo."
msgstr "避免以 root/sudo 运行 yay。"
#: pkg/dep/dep_graph.go:63
msgid "Check Dependency"
msgstr "作为检查依赖安装"
#: print.go:41
msgid "Check Deps"
msgstr "检查依赖"
#: pkg/upgrade/service.go:89
msgid "Checking development packages..."
msgstr "正在检查开发包..."
#: pkg/sync/workdir/clean.go:45
msgid "Cleaning (%d/%d): %s"
msgstr "正在清理 (%d/%d): %s"
#: print.go:42
msgid "Conflicts With"
msgstr "与它冲突"
#: pkg/menus/clean_menu.go:62
msgid "Deleting (%d/%d): %s"
msgstr "正在删除 (%d/%d): %s"
#: pkg/dep/dep_graph.go:61
msgid "Dependency"
msgstr "作为依赖安装"
#: print.go:38
msgid "Depends On"
msgstr "依赖以下包"
#: print.go:33
msgid "Description"
msgstr "描述"
#: pkg/menus/diff_menu.go:160
msgid "Diffs to show?"
msgstr "显示哪些包的差异?"
#: pkg/settings/migrations.go:25
msgid "Disable 'provides' setting by default"
msgstr "默认禁用 'provides' 设置"
#: clean.go:78
msgid "Do you want to remove ALL AUR packages from cache?"
msgstr "你是否要从缓存中删除所有 AUR 软件包?"
#: clean.go:95
msgid "Do you want to remove ALL untracked AUR files?"
msgstr "你是否要删除所有未跟踪的 AUR 文件?"
#: clean.go:80
msgid "Do you want to remove all other AUR packages from cache?"
msgstr "你是否要从缓存中删除所有其他 AUR 软件包?"
#: pkg/menus/edit_menu.go:61
msgid "Edit PKGBUILD with?"
msgstr "使用什么来编辑 PKGBUILD?"
#: pkg/query/errors.go:13
msgid "Error during AUR search: %s\n"
msgstr "搜索 AUR 时出错: %s\n"
#: pkg/upgrade/service.go:295
msgid "Excluding packages may cause partial upgrades and break systems"
msgstr "排除软件包可能会导致不完整的升级并破坏系统"
#: pkg/dep/dep_graph.go:60
msgid "Explicit"
msgstr "单独指定安装"
#: print.go:91
msgid "Explicitly installed packages: %s"
msgstr "单独指定安装的软件包: %s"
#: pkg/dep/dep_graph.go:437 pkg/dep/dep_graph.go:535
msgid "Failed to find AUR package for"
msgstr "查找 AUR 软件包失败:"
#: pkg/sync/build/installer.go:120
msgid "Failed to install layer, rolling up to next layer."
msgstr "层级安装失败,正在合并到下一个层级。"
#: pkg/sync/build/errors.go:16
msgid ""
"Failed to install the following packages. Manual intervention is required:"
msgstr "无法安装以下软件包, 需要手动介入处理:"
#: print.go:45
msgid "First Submitted"
msgstr "首次提交"
#: pkg/query/aur_warnings.go:83
msgid "Flagged Out Of Date AUR Packages:"
msgstr "标记为过时的 AUR 软件包:"
#: print.go:90
msgid "Foreign installed packages: %s"
msgstr "已安装的外部软件包: %s"
#: pkg/vcs/vcs.go:144
msgid "Found git repo: %s"
msgstr "已找到 git 仓库: %s"
#: vcs.go:72
msgid "GenDB finished. No packages were installed"
msgstr "GenDB 完成。 没有安装任何软件包"
#: print.go:36
msgid "Groups"
msgstr "组"
#: pkg/sync/srcinfo/pgp/keys.go:88
msgid "Import?"
msgstr "导入?"
#: pkg/sync/srcinfo/pgp/keys.go:97
msgid "Importing keys with gpg..."
msgstr "正在导入 GPG 密钥..."
#: print.go:46
msgid "Keywords"
msgstr "关键字"
#: print.go:47
msgid "Last Modified"
msgstr "最后修改"
#: print.go:35
msgid "Licenses"
msgstr "许可证"
#: pkg/dep/dep_graph.go:77
msgid "Local"
msgstr "本地"
#: print.go:48
msgid "Maintainer"
msgstr "维护者"
#: pkg/dep/dep_graph.go:62
msgid "Make Dependency"
msgstr "作为构建依赖安装"
#: print.go:40
msgid "Make Deps"
msgstr "构建依赖"
#: pkg/dep/dep_graph.go:79
msgid "Missing"
msgstr "缺少"
#: pkg/query/aur_warnings.go:75
msgid "Missing AUR Debug Packages:"
msgstr "缺少 AUR 调试包:"
#: print.go:31
msgid "Name"
msgstr "名称"
#: pkg/dep/dep_graph.go:442 pkg/dep/dep_graph.go:548
msgid "No AUR package found for"
msgstr "找不到 AUR 包"
#: pkg/dep/dep_graph.go:182
msgid "No package found for"
msgstr "找不到包"
#: print.go:225
msgid "None"
msgstr "没有"
#: print.go:39
msgid "Optional Deps"
msgstr "可选依赖"
#: pkg/query/aur_warnings.go:79
msgid "Orphan (unmaintained) AUR Packages:"
msgstr "孤包 (无人维护) 的 AUR 软件包:"
#: print.go:53 print.go:55
msgid "Out-of-date"
msgstr "过时"
#: pkg/sync/srcinfo/pgp/keys.go:115
msgid "PGP keys need importing:"
msgstr "需要导入的 PGP 密钥:"
#: pkg/sync/workdir/preparer.go:252
msgid "PKGBUILD up to date, skipping download: %s"
msgstr "PKGBUILD 是最新的,跳过下载: %s"
#: pkg/menus/edit_menu.go:130
msgid "PKGBUILDs to edit?"
msgstr "要编辑哪些 PKGBUILD?"
#: print.go:61
msgid "Package Base"
msgstr "基本包"
#: print.go:60
msgid "Package Base ID"
msgstr "基本包 ID"
#: pkg/query/aur_warnings.go:71
msgid "Packages not in AUR:"
msgstr "不在 AUR 中的软件包:"
#: pkg/menus/clean_menu.go:54
msgid "Packages to cleanBuild?"
msgstr "清理哪些包的构建文件?"
#: pkg/dep/dep_graph.go:202
msgid "Packages to exclude"
msgstr "要排除的软件包"
#: pkg/upgrade/service.go:294
msgid "Packages to exclude: (eg: \"1 2 3\", \"1-3\", \"^4\" or repo name)"
msgstr "要排除的包: (示例: \"1 2 3\", \"1-3\", \"^4\" 或软件库名称)"
#: cmd.go:392
msgid "Packages to install (eg: 1 2 3, 1-3 or ^4)"
msgstr "要安装的包 (示例: 1 2 3, 1-3 或 ^4)"
#: print.go:49
msgid "Popularity"
msgstr "受欢迎度"
#: pkg/menus/diff_menu.go:172 pkg/menus/edit_menu.go:143
msgid "Proceed with install?"
msgstr "继续安装?"
#: print.go:37
msgid "Provides"
msgstr "提供"
#: pkg/sync/workdir/preparer.go:125
msgid "Remove make dependencies after install?"
msgstr "安装后删除构建依赖?"
#: print.go:43
msgid "Replaces"
msgstr "替换"
#: pkg/db/ialpm/alpm.go:191 print.go:30
msgid "Repository"
msgstr "软件库"
#: pkg/dep/dep_graph.go:730
msgid "Repository AUR"
msgstr "AUR 软件库"
#: pkg/dep/dep_graph.go:78
msgid "SRCINFO"
msgstr "SRCINFO"
#: pkg/upgrade/service.go:71
msgid "Searching AUR for updates..."
msgstr "正在搜索 AUR 更新..."
#: pkg/upgrade/service.go:159
msgid "Searching databases for updates..."
msgstr "正在搜索数据库更新..."
#: pkg/query/query_builder.go:214
msgid "Showing repo packages only"
msgstr "仅显示软件库软件包"
#: print.go:95
msgid "Size of pacman cache %s: %s"
msgstr "pacman 缓存大小 %s: %s"
#: print.go:98
msgid "Size of yay cache %s: %s"
msgstr "yay 缓存大小 %s: %s"
#: print.go:62
msgid "Snapshot URL"
msgstr "快照 URL"
#: pkg/dep/dep_graph.go:76
msgid "Sync"
msgstr "同步"
#: print.go:100
msgid "Ten biggest packages:"
msgstr "十个最大的软件包:"
#: pkg/sync/sync.go:124
msgid "The following packages are not compatible with your architecture:"
msgstr "下列软件包与你的系统架构不兼容:"
#: pkg/db/ialpm/alpm.go:179 pkg/dep/dep_graph.go:726
msgid "There are %[1]d providers available for %[2]s:"
msgstr "对于 %[2] 有 %[1]d 个结果:"
#: pkg/settings/exe/cmd_builder.go:258
msgid "There may be another Pacman instance running. Waiting..."
msgstr "可能还有另一个 Pacman 实例正在运行。等待中..."
#: print.go:92
msgid "Total Size occupied by packages: %s"
msgstr "包占用的总大小: %s"
#: print.go:89
msgid "Total installed packages: %s"
msgstr "已安装的软件包总数: %s"
#: pkg/sync/sync.go:132
msgid "Try to build them anyway?"
msgstr "依然尝试构建吗?"
#: print.go:34
msgid "URL"
msgstr "URL"
#: clean.go:194 pkg/menus/clean_menu.go:65 pkg/menus/clean_menu.go:71
msgid "Unable to clean:"
msgstr "无法清理:"
#: get.go:42 get.go:74
msgid "Unable to find the following packages:"
msgstr "找不到下列软件包:"
#: vote.go:20
msgid "Unable to handle package vote for: %s. err: %s"
msgstr "无法处理软件包的投票: %s。错误: %s"
#: clean.go:170
msgid "Unable to remove %s: %s"
msgstr "无法删除 %s: %s"
#: print.go:32
msgid "Version"
msgstr "版本"
#: print.go:50
msgid "Votes"
msgstr "得票"
#: print.go:87
msgid "Yay version v%s"
msgstr "Yay 版本 v%s"
#: pkg/menus/menu.go:49
msgid "[N]one"
msgstr "[N]没有"
#: pkg/settings/errors.go:29
msgid "aborting due to user"
msgstr "用户自行终止"
#: pkg/settings/parser/parser.go:608
msgid "argument '-' specified without input on stdin"
msgstr "指定了参数“-”,但未在 stdin 输入任何内容"
#: local_install.go:26
msgid "cannot find PKGBUILD and .SRCINFO in directory"
msgstr "在目录中找不到 PKGBUILD 和 .SRCINFO 文件"
#: pkg/sync/build/pkg_archive.go:148
msgid "cannot find package name: %v"
msgstr "找不到包名: %v"
#: pkg/sync/build/errors.go:30
msgid "could not find PKGDEST for: %s"
msgstr "找不到 PKGDEST: %s"
#: errors.go:9
msgid "could not find all required packages"
msgstr "无法找到所有必需的软件包"
#: pkg/sync/build/errors.go:61
msgid "could not find any package archives listed in %s"
msgstr "在 %s 中无法找到任何软件包存档"
#: pkg/sync/build/errors.go:50 pkg/upgrade/service.go:286
msgid "dependency"
msgstr "作为依赖安装"
#: pkg/vcs/vcs.go:100 pkg/vcs/vcs.go:96
msgid "devel check for package failed: '%s' encountered an error"
msgstr "软件包的开发检查失败: '%s' 遇到了错误"
#: pkg/menus/edit_menu.go:110
msgid "editor did not exit successfully, aborting: %s"
msgstr "编辑器未成功退出,正在中止: %s"
#: pkg/sync/workdir/aur_source.go:24
msgid "error downloading sources: %s"
msgstr "下载源文件时出错: %s"
#: pkg/download/errors.go:25
msgid "error fetching %s: %s"
msgstr "获取 %s 时出错: %s"
#: pkg/sync/build/errors.go:9
msgid "error installing repo packages"
msgstr "安装软件库软件包时出错"
#: pkg/sync/build/installer.go:266 pkg/sync/build/installer.go:270
msgid "error installing:"
msgstr "安装时出错: "
#: pkg/sync/build/installer.go:233 pkg/sync/build/installer.go:237
msgid "error making: %s"
msgstr "生成时出错: %s"
#: pkg/sync/workdir/merge.go:24
msgid "error merging %s: %s"
msgstr "合并 %s 时出错: %s"
#: pkg/download/unified.go:59
msgid "error reading %s"
msgstr "读取 %s 时出错"
#: sync.go:36
msgid "error refreshing databases"
msgstr "刷新数据库时出错"
#: pkg/sync/workdir/clean.go:51 pkg/sync/workdir/merge.go:17
msgid "error resetting %s: %s"
msgstr "重置 %s 时出错: %s"
#: pkg/sync/build/errors.go:53
msgid "error updating package install reason to %s"
msgstr "更新软件包时因 %s 出错"
#: pkg/sync/build/errors.go:48
msgid "explicit"
msgstr "单独指定安装"
#: pkg/settings/errors.go:23
msgid "failed to create directory '%s': %s"
msgstr "无法创建目录 '%s': %s"
#: pkg/settings/config.go:281
msgid "failed to open config file '%s': %s"
msgstr "无法打开配置文件 '%s': %s"
#: pkg/sync/srcinfo/service.go:114
msgid "failed to parse %s -- skipping: %s"
msgstr "无法解析 %s -- 跳过: %s"
#: pkg/sync/srcinfo/service.go:118
msgid "failed to parse %s: %s"
msgstr "无法解析 %s: %s"
#: local_install.go:77
msgid "failed to parse .SRCINFO"
msgstr "无法解析 .SRCINFO"
#: pkg/settings/config.go:291
msgid "failed to read config file '%s': %s"
msgstr "无法读取配置文件 '%s': %s"
#: pkg/cmd/graph/main.go:46 pkg/runtime/runtime.go:73
msgid "failed to retrieve aur Cache"
msgstr "无法获取 AUR 缓存"
#: pkg/upgrade/sources.go:27
msgid "ignoring package devel upgrade (no AUR info found):"
msgstr "忽略软件包开发更新(未找到 AUR 信息):"
#: pkg/text/errors.go:8
msgid "input too long"
msgstr "输入过长"
#: pkg/db/ialpm/alpm.go:222 pkg/dep/dep_graph.go:761
msgid "invalid number: %s"
msgstr "无效数字: %s"
#: pkg/settings/parser/parser.go:174
msgid "invalid option '%s'"
msgstr "无效选项 '%s'"
#: cmd.go:197
msgid "invalid option: '--deps' and '--explicit' may not be used together"
msgstr "无效选项: '--deps' 和 '--explicit' 不能同时指定"
#: pkg/download/abs.go:22
msgid "invalid repository"
msgstr "无效软件库"
#: pkg/db/ialpm/alpm.go:227 pkg/dep/dep_graph.go:767
msgid "invalid value: %d is not between %d and %d"
msgstr "无效值: %d 不在 %d 和 %d 之间"
#: pkg/text/input.go:48
msgid "no"
msgstr "no"
#: pkg/sync/srcinfo/pgp/keys.go:110
msgid "no keys to import"
msgstr "没有要导入的密钥"
#: pkg/query/errors.go:20
msgid "no query was executed"
msgstr "没有查询被执行"
#: local_install.go:66
msgid "no target directories specified"
msgstr "未指定目标目录"
#: pkg/sync/build/installer.go:242
msgid "nothing to install for %s"
msgstr "%s 没有需要安装的依赖软件包"
#: pkg/settings/parser/parser.go:164
msgid "only one operation may be used at a time"
msgstr "一次只能使用一项操作"
#: pkg/cmd/graph/main.go:70
msgid "only one target is allowed"
msgstr "仅允许一个目标"
#: pkg/upgrade/service.go:291
msgid "package"
msgid_plural "packages"
msgstr[0] "软件包"
#: print.go:187
msgid "package '%s' was not found"
msgstr "找不到软件包 '%s'"
#: pkg/download/errors.go:15
msgid "package not found in AUR"
msgstr "AUR 中找不到软件包"
#: pkg/download/abs.go:23
msgid "package not found in repos"
msgstr "软件库中找不到软件包"
#: pkg/sync/srcinfo/pgp/keys.go:100
msgid "problem importing keys"
msgstr "导入密钥时出现问题"
#: clean.go:105
msgid "removing AUR packages from cache..."
msgstr "正在从缓存中删除 AUR 软件包..."
#: clean.go:178 pkg/sync/workdir/clean.go:41
msgid "removing untracked AUR files from cache..."
msgstr "正在从缓存中删除未跟踪的 AUR 文件..."
#: pkg/sync/build/errors.go:38
msgid "the PKGDEST for %s is listed by makepkg but does not exist: %s"
msgstr "%s 的 PKGDEST 由 makepkg 列出,但不存在: %s"
#: pkg/sync/sync.go:45
msgid "there is nothing to do"
msgstr "今日无事可做"
#: pkg/db/ialpm/alpm.go:247
msgid "unable to CreateHandle: %s"
msgstr "无法创建句柄: %s"
#: cmd.go:186
msgid "unhandled operation"
msgstr "未处理的操作"
#: cmd.go:450
msgid "unknown-version"
msgstr "未知版本"
#: pkg/text/input.go:47
msgid "yes"
msgstr "yes"
================================================
FILE: po/zh_TW.po
================================================
#
# Translators:
# J G, 2022
# lakejason0 , 2023
# Oliver Tzeng, 2025
# zangmen hsu(neko_0xff) , 2025
# Kisaragi Hiu , 2025
#
msgid ""
msgstr ""
"Last-Translator: Kisaragi Hiu , 2025\n"
"Language-Team: Chinese (Taiwan) (https://app.transifex.com/yay-1/teams/123732/zh_TW/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: zh_TW\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: xgotext\n"
#: clean.go:83
msgid ""
"\n"
"Build directory:"
msgstr ""
"\n"
"編譯資料夾:"
#: pkg/db/ialpm/alpm.go:201 pkg/dep/dep_graph.go:740
msgid ""
"\n"
"Enter a number (default=1): "
msgstr ""
"\n"
"輸入數字 (預設=1): "
#: pkg/menus/menu.go:32
msgid " (Build Files Exist)"
msgstr " (編譯檔案已存在)"
#: pkg/menus/menu.go:27
msgid " (Installed)"
msgstr " (已安裝)"
#: cmd.go:453
msgid " [Installed]"
msgstr " [已安裝]"
#: cmd.go:410 vote.go:36
msgid " there is nothing to do"
msgstr " 無事可做"
#: pkg/menus/menu.go:49
msgid "%s [A]ll [Ab]ort [I]nstalled [No]tInstalled or (1 2 3, 1-3, ^4)"
msgstr "%s [A]全部 [Ab]中止 [I]已安裝 [No]未安裝 或 (1 2 3, 1-3, ^4)"
#: pkg/sync/build/installer.go:308
msgid "%s already made -- skipping build"
msgstr "%s 已編譯 -- 跳過編譯"
#: pkg/menus/edit_menu.go:57
msgid "%s is not set"
msgstr "未設定 %s"
#: pkg/settings/exe/cmd_builder.go:257
msgid "%s is present."
msgstr "%s 存在。"
#: pkg/dep/dep_graph.go:460 pkg/sync/build/installer.go:305
msgid "%s is up to date -- skipping"
msgstr "%s 是最新的 -- 跳過"
#: pkg/upgrade/service.go:291
msgid "%s to upgrade/install."
msgstr "%s 個要升級/安裝的套件。"
#: pkg/upgrade/service.go:285
msgid "%s will also be installed for this operation."
msgstr "%s 也會為此操作被安裝。 "
#: pkg/sync/srcinfo/pgp/keys.go:124
msgid "%s, required by: %s"
msgstr "%s, 需要: %s"
#: pkg/menus/diff_menu.go:49
msgid "%s: No changes -- skipping"
msgstr "%s: 沒有更改 -- 跳過"
#: pkg/query/filter.go:22
msgid "%s: can't use target with option --aur -- skipping"
msgstr "%s: 不能將目標與選項 --aur 一起使用 -- 跳過"
#: pkg/query/filter.go:17
msgid "%s: can't use target with option --repo -- skipping"
msgstr "%s: 不能將目標與選項 --repo 一起使用 -- 跳過"
#: pkg/upgrade/sources.go:57
msgid "%s: ignoring package upgrade (%s => %s)"
msgstr "%s: 忽略套件包升級 (%s => %s)"
#: pkg/query/aur_warnings.go:46
msgid "%s: local (%s) is newer than AUR (%s)"
msgstr "%s: 本機 (%s) 比 AUR (%s) 更新"
#: vote.go:51
msgid ""
"%s: please set AUR_USERNAME and AUR_PASSWORD environment variables for "
"voting"
msgstr "%s: 請設定 AUR_USERNAME 與 AUR_PASSWORD 環境變數以進行投票"
#: pkg/download/unified.go:192
msgid "(%d/%d) Downloaded PKGBUILD from ABS: %s"
msgstr "(%d/%d) 從 ABS 下載了 PKGBUILD:%s"
#: pkg/download/aur.go:92 pkg/download/unified.go:188
msgid "(%d/%d) Downloaded PKGBUILD: %s"
msgstr "(%d/%d) 下載了 PKGBUILD:%s"
#: pkg/download/aur.go:82
msgid "(%d/%d) Failed to download PKGBUILD: %s"
msgstr "(%d/%d)下載 PKGBUILD 時失敗:%s"
#: pkg/sync/srcinfo/service.go:109
msgid "(%d/%d) Parsing SRCINFO: %s"
msgstr "(%d/%d) 正在解析 SRCINFO:%s"
#: pkg/query/types.go:103 pkg/query/types.go:72
msgid "(Installed)"
msgstr "(已安裝)"
#: pkg/query/types.go:101 pkg/query/types.go:70
msgid "(Installed: %s)"
msgstr "(已安裝: %s)"
#: pkg/query/types.go:61
msgid "(Orphaned)"
msgstr "(棄置)"
#: pkg/query/types.go:65
msgid "(Out-of-date: %s)"
msgstr "(過期的: %s)"
#: pkg/dep/dep_graph.go:75
msgid "AUR"
msgstr "AUR"
#: print.go:44
msgid "AUR URL"
msgstr "AUR 網址"
#: pkg/menus/edit_menu.go:58
msgid "Add %s or %s to your environment variables"
msgstr "將 %s 或 %s 添加到環境變數中"
#: main.go:60
msgid "Avoid running yay as root/sudo."
msgstr "避免以 root/sudo 執行 yay。"
#: pkg/dep/dep_graph.go:63
msgid "Check Dependency"
msgstr "作為檢查依賴安裝"
#: print.go:41
msgid "Check Deps"
msgstr "檢查依賴"
#: pkg/upgrade/service.go:89
msgid "Checking development packages..."
msgstr "正在檢查開發套件包..."
#: pkg/sync/workdir/clean.go:45
msgid "Cleaning (%d/%d): %s"
msgstr "正在清理 (%d/%d): %s"
#: print.go:42
msgid "Conflicts With"
msgstr "與它衝突"
#: pkg/menus/clean_menu.go:62
msgid "Deleting (%d/%d): %s"
msgstr "正在刪除 (%d/%d): %s"
#: pkg/dep/dep_graph.go:61
msgid "Dependency"
msgstr "作為依賴安裝"
#: print.go:38
msgid "Depends On"
msgstr "依賴於"
#: print.go:33
msgid "Description"
msgstr "描述"
#: pkg/menus/diff_menu.go:160
msgid "Diffs to show?"
msgstr "顯示差異?"
#: pkg/settings/migrations.go:25
msgid "Disable 'provides' setting by default"
msgstr "預設停用「provides」設定"
#: clean.go:78
msgid "Do you want to remove ALL AUR packages from cache?"
msgstr "您是否要從快取中刪除所有 AUR 套件包?"
#: clean.go:95
msgid "Do you want to remove ALL untracked AUR files?"
msgstr "您是否要刪除所有未使用的 AUR 檔案?"
#: clean.go:80
msgid "Do you want to remove all other AUR packages from cache?"
msgstr "您是否要從快取中刪除所有其他 AUR 套件包?"
#: pkg/menus/edit_menu.go:61
msgid "Edit PKGBUILD with?"
msgstr "使用什麼來編輯 PKGBUILD?"
#: pkg/query/errors.go:13
msgid "Error during AUR search: %s\n"
msgstr "搜尋 AUR 時出錯: %s\n"
#: pkg/upgrade/service.go:295
msgid "Excluding packages may cause partial upgrades and break systems"
msgstr "排除套件包則會造成部分功能更新而使系統無法正常運作"
#: pkg/dep/dep_graph.go:60
msgid "Explicit"
msgstr "單獨指定安裝"
#: print.go:91
msgid "Explicitly installed packages: %s"
msgstr "單獨指定安装的套件包: %s"
#: pkg/dep/dep_graph.go:437 pkg/dep/dep_graph.go:535
msgid "Failed to find AUR package for"
msgstr "尋找 AUR 套件包時失敗"
#: pkg/sync/build/installer.go:120
msgid "Failed to install layer, rolling up to next layer."
msgstr "層級安裝失敗,將合併到下一個層級。"
#: pkg/sync/build/errors.go:16
msgid ""
"Failed to install the following packages. Manual intervention is required:"
msgstr "無法安裝以下的套件包。需要自行手動處理:"
#: print.go:45
msgid "First Submitted"
msgstr "首次提交"
#: pkg/query/aur_warnings.go:83
msgid "Flagged Out Of Date AUR Packages:"
msgstr "標記為過期的 AUR 套件包:"
#: print.go:90
msgid "Foreign installed packages: %s"
msgstr "已安裝的外部套件包: %s"
#: pkg/vcs/vcs.go:144
msgid "Found git repo: %s"
msgstr "已找到 git 倉庫源: %s"
#: vcs.go:72
msgid "GenDB finished. No packages were installed"
msgstr "GenDB 完成。沒有安裝任何套件包"
#: print.go:36
msgid "Groups"
msgstr "群組別"
#: pkg/sync/srcinfo/pgp/keys.go:88
msgid "Import?"
msgstr "匯入?"
#: pkg/sync/srcinfo/pgp/keys.go:97
msgid "Importing keys with gpg..."
msgstr "正在匯入 gpg 金鑰..."
#: print.go:46
msgid "Keywords"
msgstr "關鍵字"
#: print.go:47
msgid "Last Modified"
msgstr "最後修改"
#: print.go:35
msgid "Licenses"
msgstr "授權條款"
#: pkg/dep/dep_graph.go:77
msgid "Local"
msgstr "本機"
#: print.go:48
msgid "Maintainer"
msgstr "維護者"
#: pkg/dep/dep_graph.go:62
msgid "Make Dependency"
msgstr "作為編譯依賴安裝"
#: print.go:40
msgid "Make Deps"
msgstr "編譯依賴"
#: pkg/dep/dep_graph.go:79
msgid "Missing"
msgstr "缺少"
#: pkg/query/aur_warnings.go:75
msgid "Missing AUR Debug Packages:"
msgstr "缺少 AUR 除錯套件包:"
#: print.go:31
msgid "Name"
msgstr "名稱"
#: pkg/dep/dep_graph.go:442 pkg/dep/dep_graph.go:548
msgid "No AUR package found for"
msgstr "沒有找到 AUR 套件包"
#: pkg/dep/dep_graph.go:182
msgid "No package found for"
msgstr "未找到套件包"
#: print.go:225
msgid "None"
msgstr "不做任何動作"
#: print.go:39
msgid "Optional Deps"
msgstr "可選擇的依賴選項"
#: pkg/query/aur_warnings.go:79
msgid "Orphan (unmaintained) AUR Packages:"
msgstr "孤立 (不受維護) 的 AUR 套件:"
#: print.go:53 print.go:55
msgid "Out-of-date"
msgstr "過期"
#: pkg/sync/srcinfo/pgp/keys.go:115
msgid "PGP keys need importing:"
msgstr "需要匯入的 PGP 金鑰:"
#: pkg/sync/workdir/preparer.go:252
msgid "PKGBUILD up to date, skipping download: %s"
msgstr "PKGBUILD 是最新的,跳過下載: %s"
#: pkg/menus/edit_menu.go:130
msgid "PKGBUILDs to edit?"
msgstr "要編輯哪些 PKGBUILD 檔案?"
#: print.go:61
msgid "Package Base"
msgstr "套件包基礎"
#: print.go:60
msgid "Package Base ID"
msgstr "套件包基礎編號"
#: pkg/query/aur_warnings.go:71
msgid "Packages not in AUR:"
msgstr "不在 AUR 裡的套件包:"
#: pkg/menus/clean_menu.go:54
msgid "Packages to cleanBuild?"
msgstr "清理哪些套件包的編譯檔案?"
#: pkg/dep/dep_graph.go:202
msgid "Packages to exclude"
msgstr "要排除的套件包"
#: pkg/upgrade/service.go:294
msgid "Packages to exclude: (eg: \"1 2 3\", \"1-3\", \"^4\" or repo name)"
msgstr "要排除的套件包:(例如: \"1 2 3\", \"1-3\", \"^4\" 或倉庫源名稱)"
#: cmd.go:392
msgid "Packages to install (eg: 1 2 3, 1-3 or ^4)"
msgstr "要安裝的套件包 (例如: 1 2 3, 1-3 或 ^4)"
#: print.go:49
msgid "Popularity"
msgstr "熱門度"
#: pkg/menus/diff_menu.go:172 pkg/menus/edit_menu.go:143
msgid "Proceed with install?"
msgstr "繼續安裝?"
#: print.go:37
msgid "Provides"
msgstr "提供"
#: pkg/sync/workdir/preparer.go:125
msgid "Remove make dependencies after install?"
msgstr "安裝後刪除編譯依賴?"
#: print.go:43
msgid "Replaces"
msgstr "替換"
#: pkg/db/ialpm/alpm.go:191 print.go:30
msgid "Repository"
msgstr "軟體源"
#: pkg/dep/dep_graph.go:730
msgid "Repository AUR"
msgstr "AUR 軟體源"
#: pkg/dep/dep_graph.go:78
msgid "SRCINFO"
msgstr "SRCINFO"
#: pkg/upgrade/service.go:71
msgid "Searching AUR for updates..."
msgstr "搜尋 AUR 來獲取更新..."
#: pkg/upgrade/service.go:159
msgid "Searching databases for updates..."
msgstr "搜尋資料庫來獲取更新..."
#: pkg/query/query_builder.go:214
msgid "Showing repo packages only"
msgstr "僅顯示軟體源中的套件包"
#: print.go:95
msgid "Size of pacman cache %s: %s"
msgstr "pacman 快取大小 %s: %s"
#: print.go:98
msgid "Size of yay cache %s: %s"
msgstr "yay 快取大小 %s: %s"
#: print.go:62
msgid "Snapshot URL"
msgstr "快照網址"
#: pkg/dep/dep_graph.go:76
msgid "Sync"
msgstr "同步"
#: print.go:100
msgid "Ten biggest packages:"
msgstr "前十大的套件包:"
#: pkg/sync/sync.go:124
msgid "The following packages are not compatible with your architecture:"
msgstr "以下套件不支援您的系統架構:"
#: pkg/db/ialpm/alpm.go:179 pkg/dep/dep_graph.go:726
msgid "There are %[1]d providers available for %[2]s:"
msgstr "對於 %[2]s 有 %[1]d 個結果:"
#: pkg/settings/exe/cmd_builder.go:258
msgid "There may be another Pacman instance running. Waiting..."
msgstr "可能還有另一個 pacman 程序正在執行。等待中..."
#: print.go:92
msgid "Total Size occupied by packages: %s"
msgstr "套件包佔用的空間: %s"
#: print.go:89
msgid "Total installed packages: %s"
msgstr "已安裝的套件包總數: %s"
#: pkg/sync/sync.go:132
msgid "Try to build them anyway?"
msgstr "依然嘗試編譯它們?"
#: print.go:34
msgid "URL"
msgstr "網址"
#: clean.go:194 pkg/menus/clean_menu.go:65 pkg/menus/clean_menu.go:71
msgid "Unable to clean:"
msgstr "無法清理:"
#: get.go:42 get.go:74
msgid "Unable to find the following packages:"
msgstr "找不到下列套件包:"
#: vote.go:20
msgid "Unable to handle package vote for: %s. err: %s"
msgstr "無法處理套件包的投票: %s。錯誤: %s"
#: clean.go:170
msgid "Unable to remove %s: %s"
msgstr "無法移除 %s: %s"
#: print.go:32
msgid "Version"
msgstr "版本"
#: print.go:50
msgid "Votes"
msgstr "得到票數"
#: print.go:87
msgid "Yay version v%s"
msgstr "Yay 版本 v%s"
#: pkg/menus/menu.go:49
msgid "[N]one"
msgstr "[N]不做任何動作"
#: pkg/settings/errors.go:29
msgid "aborting due to user"
msgstr "使用者已自行中止 yay"
#: pkg/settings/parser/parser.go:608
msgid "argument '-' specified without input on stdin"
msgstr "指定了參數 '-' 但沒有 stdin 或其他輸入值"
#: local_install.go:26
msgid "cannot find PKGBUILD and .SRCINFO in directory"
msgstr "目錄內找不到 PKGBUILD 與 .SRCINFO 檔案"
#: pkg/sync/build/pkg_archive.go:148
msgid "cannot find package name: %v"
msgstr "找不到套件名稱:%v"
#: pkg/sync/build/errors.go:30
msgid "could not find PKGDEST for: %s"
msgstr "找不到 PKGDEST:%s"
#: errors.go:9
msgid "could not find all required packages"
msgstr "找不到所有需要的套件包"
#: pkg/sync/build/errors.go:61
msgid "could not find any package archives listed in %s"
msgstr "找不到任何 %s 中列舉的套件封存檔"
#: pkg/sync/build/errors.go:50 pkg/upgrade/service.go:286
msgid "dependency"
msgstr "作為依賴安裝"
#: pkg/vcs/vcs.go:100 pkg/vcs/vcs.go:96
msgid "devel check for package failed: '%s' encountered an error"
msgstr "開發套件包檢查失敗:「%s」 遇到了錯誤"
#: pkg/menus/edit_menu.go:110
msgid "editor did not exit successfully, aborting: %s"
msgstr "編輯器未成功退出,正在中止:%s"
#: pkg/sync/workdir/aur_source.go:24
msgid "error downloading sources: %s"
msgstr "下載套件包檔案時出錯:%s"
#: pkg/download/errors.go:25
msgid "error fetching %s: %s"
msgstr "取得 %s 時出錯:%s"
#: pkg/sync/build/errors.go:9
msgid "error installing repo packages"
msgstr "安裝套件包時出錯"
#: pkg/sync/build/installer.go:266 pkg/sync/build/installer.go:270
msgid "error installing:"
msgstr "安裝時出錯:"
#: pkg/sync/build/installer.go:233 pkg/sync/build/installer.go:237
msgid "error making: %s"
msgstr "編譯時出錯:%s"
#: pkg/sync/workdir/merge.go:24
msgid "error merging %s: %s"
msgstr "合併 %s 時出錯:%s"
#: pkg/download/unified.go:59
msgid "error reading %s"
msgstr "讀取 %s 時出錯"
#: sync.go:36
msgid "error refreshing databases"
msgstr "重新整理資料庫時出錯"
#: pkg/sync/workdir/clean.go:51 pkg/sync/workdir/merge.go:17
msgid "error resetting %s: %s"
msgstr "重置 %s 時出錯:%s"
#: pkg/sync/build/errors.go:53
msgid "error updating package install reason to %s"
msgstr "更新套件安裝原因至 %s 時出錯"
#: pkg/sync/build/errors.go:48
msgid "explicit"
msgstr "單獨指定安裝"
#: pkg/settings/errors.go:23
msgid "failed to create directory '%s': %s"
msgstr "無法建立資料夾 '%s':%s"
#: pkg/settings/config.go:281
msgid "failed to open config file '%s': %s"
msgstr "無法開啟設定檔案 '%s':%s"
#: pkg/sync/srcinfo/service.go:114
msgid "failed to parse %s -- skipping: %s"
msgstr "無法解析 %s -- 跳過:%s"
#: pkg/sync/srcinfo/service.go:118
msgid "failed to parse %s: %s"
msgstr "無法解析 %s:%s"
#: local_install.go:77
msgid "failed to parse .SRCINFO"
msgstr "無法解析 .SRCINFO"
#: pkg/settings/config.go:291
msgid "failed to read config file '%s': %s"
msgstr "無法讀取設定檔案 '%s':%s"
#: pkg/cmd/graph/main.go:46 pkg/runtime/runtime.go:73
msgid "failed to retrieve aur Cache"
msgstr "無法取得 AUR 快取"
#: pkg/upgrade/sources.go:27
msgid "ignoring package devel upgrade (no AUR info found):"
msgstr "忽略軟體包開發更新 (未找到 AUR 資訊):"
#: pkg/text/errors.go:8
msgid "input too long"
msgstr "輸入太長"
#: pkg/db/ialpm/alpm.go:222 pkg/dep/dep_graph.go:761
msgid "invalid number: %s"
msgstr "無效數字:%s"
#: pkg/settings/parser/parser.go:174
msgid "invalid option '%s'"
msgstr "無效選項「%s」"
#: cmd.go:197
msgid "invalid option: '--deps' and '--explicit' may not be used together"
msgstr "無效選項:不能同時指定「--deps」與「--explicit」"
#: pkg/download/abs.go:22
msgid "invalid repository"
msgstr "無效軟體源"
#: pkg/db/ialpm/alpm.go:227 pkg/dep/dep_graph.go:767
msgid "invalid value: %d is not between %d and %d"
msgstr "無效值: %d 不在 %d 和 %d 之間"
#: pkg/text/input.go:48
msgid "no"
msgstr "否"
#: pkg/sync/srcinfo/pgp/keys.go:110
msgid "no keys to import"
msgstr "沒有要匯入的金鑰"
#: pkg/query/errors.go:20
msgid "no query was executed"
msgstr "未進行任何查詢"
#: local_install.go:66
msgid "no target directories specified"
msgstr "沒有指定目標目錄"
#: pkg/sync/build/installer.go:242
msgid "nothing to install for %s"
msgstr "沒有要為 %s 安裝的東西"
#: pkg/settings/parser/parser.go:164
msgid "only one operation may be used at a time"
msgstr "一次只能使用一項操作"
#: pkg/cmd/graph/main.go:70
msgid "only one target is allowed"
msgstr "只能有一個目標"
#: pkg/upgrade/service.go:291
msgid "package"
msgid_plural "packages"
msgstr[0] "套件包"
#: print.go:187
msgid "package '%s' was not found"
msgstr "找不到套件包「%s」"
#: pkg/download/errors.go:15
msgid "package not found in AUR"
msgstr "AUR 中找不到套件包"
#: pkg/download/abs.go:23
msgid "package not found in repos"
msgstr "倉庫源中找不到套件包"
#: pkg/sync/srcinfo/pgp/keys.go:100
msgid "problem importing keys"
msgstr "匯入金鑰時出錯"
#: clean.go:105
msgid "removing AUR packages from cache..."
msgstr "正在從快取中刪除 AUR 套件包..."
#: clean.go:178 pkg/sync/workdir/clean.go:41
msgid "removing untracked AUR files from cache..."
msgstr "正在從暫存中刪除未追蹤的 AUR 檔案..."
#: pkg/sync/build/errors.go:38
msgid "the PKGDEST for %s is listed by makepkg but does not exist: %s"
msgstr "%s 的 PKGDEST 由 makepkg 列出,但不存在:%s"
#: pkg/sync/sync.go:45
msgid "there is nothing to do"
msgstr "無事可做"
#: pkg/db/ialpm/alpm.go:247
msgid "unable to CreateHandle: %s"
msgstr "無法 CreateHandle:%s"
#: cmd.go:186
msgid "unhandled operation"
msgstr "未處理的操作"
#: cmd.go:450
msgid "unknown-version"
msgstr "未知版本"
#: pkg/text/input.go:47
msgid "yes"
msgstr "是"
================================================
FILE: print.go
================================================
package main
import (
"context"
"fmt"
"io"
"os"
"strconv"
"strings"
"syscall"
"unicode"
aur "github.com/Jguer/aur"
mapset "github.com/deckarep/golang-set/v2"
"github.com/leonelquinteros/gotext"
"golang.org/x/sys/unix"
"github.com/Jguer/yay/v12/pkg/db"
"github.com/Jguer/yay/v12/pkg/dep"
"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/parser"
"github.com/Jguer/yay/v12/pkg/text"
"github.com/Jguer/yay/v12/pkg/upgrade"
)
// printInfo prints package info like pacman -Si.
func printInfo(logger *text.Logger, config *settings.Configuration, a *aur.Pkg, extendedInfo bool) {
printInfoValue(logger, gotext.Get("Repository"), "aur")
printInfoValue(logger, gotext.Get("Name"), a.Name)
printInfoValue(logger, gotext.Get("Version"), a.Version)
printInfoValue(logger, gotext.Get("Description"), a.Description)
printInfoValue(logger, gotext.Get("URL"), a.URL)
printInfoValue(logger, gotext.Get("Licenses"), a.License...)
printInfoValue(logger, gotext.Get("Groups"), a.Groups...)
printInfoValue(logger, gotext.Get("Provides"), a.Provides...)
printInfoValue(logger, gotext.Get("Depends On"), a.Depends...)
printInfoValue(logger, gotext.Get("Optional Deps"), a.OptDepends...)
printInfoValue(logger, gotext.Get("Make Deps"), a.MakeDepends...)
printInfoValue(logger, gotext.Get("Check Deps"), a.CheckDepends...)
printInfoValue(logger, gotext.Get("Conflicts With"), a.Conflicts...)
printInfoValue(logger, gotext.Get("Replaces"), a.Replaces...)
printInfoValue(logger, gotext.Get("AUR URL"), config.AURURL+"/packages/"+a.Name)
printInfoValue(logger, gotext.Get("First Submitted"), text.FormatTimeQuery(a.FirstSubmitted))
printInfoValue(logger, gotext.Get("Keywords"), a.Keywords...)
printInfoValue(logger, gotext.Get("Last Modified"), text.FormatTimeQuery(a.LastModified))
printInfoValue(logger, gotext.Get("Maintainer"), a.Maintainer)
printInfoValue(logger, gotext.Get("Popularity"), fmt.Sprintf("%f", a.Popularity))
printInfoValue(logger, gotext.Get("Votes"), fmt.Sprintf("%d", a.NumVotes))
if a.OutOfDate != 0 {
printInfoValue(logger, gotext.Get("Out-of-date"), text.FormatTimeQuery(a.OutOfDate))
} else {
printInfoValue(logger, gotext.Get("Out-of-date"), "No")
}
if extendedInfo {
printInfoValue(logger, "ID", fmt.Sprintf("%d", a.ID))
printInfoValue(logger, gotext.Get("Package Base ID"), fmt.Sprintf("%d", a.PackageBaseID))
printInfoValue(logger, gotext.Get("Package Base"), a.PackageBase)
printInfoValue(logger, gotext.Get("Snapshot URL"), config.AURURL+a.URLPath)
}
logger.Println()
}
// BiggestPackages prints the name of the ten biggest packages in the system.
func biggestPackages(logger *text.Logger, dbExecutor db.Executor) {
pkgS := dbExecutor.BiggestPackages()
if len(pkgS) < 10 {
return
}
for i := range 10 {
logger.Printf("%s: %s\n", text.Bold(pkgS[i].Name()), text.Cyan(text.Human(pkgS[i].ISize())))
}
}
// localStatistics prints installed packages statistics.
func localStatistics(ctx context.Context, run *runtime.Runtime, dbExecutor db.Executor) error {
info := statistics(run, dbExecutor)
remoteNames := dbExecutor.InstalledRemotePackageNames()
remote := dbExecutor.InstalledRemotePackages()
run.Logger.Infoln(gotext.Get("Yay version v%s", yayVersion))
run.Logger.Println(text.Bold(text.Cyan("===========================================")))
run.Logger.Infoln(gotext.Get("Total installed packages: %s", text.Cyan(strconv.Itoa(info.Totaln))))
run.Logger.Infoln(gotext.Get("Foreign installed packages: %s", text.Cyan(strconv.Itoa(len(remoteNames)))))
run.Logger.Infoln(gotext.Get("Explicitly installed packages: %s", text.Cyan(strconv.Itoa(info.Expln))))
run.Logger.Infoln(gotext.Get("Total Size occupied by packages: %s", text.Cyan(text.Human(info.TotalSize))))
for path, size := range info.pacmanCaches {
run.Logger.Infoln(gotext.Get("Size of pacman cache %s: %s", path, text.Cyan(text.Human(size))))
}
run.Logger.Infoln(gotext.Get("Size of yay cache %s: %s", run.Cfg.BuildDir, text.Cyan(text.Human(info.yayCache))))
run.Logger.Println(text.Bold(text.Cyan("===========================================")))
run.Logger.Infoln(gotext.Get("Ten biggest packages:"))
biggestPackages(run.Logger, dbExecutor)
run.Logger.Println(text.Bold(text.Cyan("===========================================")))
aurData, err := run.AURClient.Get(ctx, &aur.Query{
Needles: remoteNames,
By: aur.Name,
})
if err != nil {
return err
}
warnings := query.NewWarnings(run.Logger.Child("warnings"))
for i := range aurData {
warnings.AddToWarnings(remote, &aurData[i])
}
warnings.Print()
return nil
}
func printUpdateList(ctx context.Context, run *runtime.Runtime, cmdArgs *parser.Arguments,
dbExecutor db.Executor, enableDowngrade bool, filter upgrade.Filter,
) error {
quietMode := cmdArgs.ExistsArg("q", "quiet")
// TODO: handle quiet mode in a better way
logger := text.NewLogger(io.Discard, os.Stderr, os.Stdin, run.Cfg.Debug, "update-list")
dbExecutor.SetLogger(logger.Child("db"))
oldNoConfirm := settings.NoConfirm
settings.NoConfirm = true
// restoring global NoConfirm to make tests work properly
defer func() { settings.NoConfirm = oldNoConfirm }()
targets := mapset.NewThreadUnsafeSet(cmdArgs.Targets...)
grapher := dep.NewGrapher(dbExecutor, run.AURClient, false, true,
false, false, cmdArgs.ExistsArg("needed"), logger.Child("grapher"))
upService := upgrade.NewUpgradeService(
grapher, run.AURClient, dbExecutor, run.VCSStore,
run.Cfg, true, logger.Child("upgrade"))
graph, errSysUp := upService.GraphUpgrades(ctx, nil,
enableDowngrade, filter)
if errSysUp != nil {
return errSysUp
}
if graph.Len() == 0 {
return fmt.Errorf("")
}
noTargets := targets.Cardinality() == 0
foreignFilter := cmdArgs.ExistsArg("m", "foreign")
nativeFilter := cmdArgs.ExistsArg("n", "native")
noUpdates := true
_ = graph.ForEach(func(pkgName string, ii *dep.InstallInfo) error {
if !ii.Upgrade {
return nil
}
if noTargets || targets.Contains(pkgName) {
if ii.Source == dep.Sync && foreignFilter {
return nil
} else if ii.Source == dep.AUR && nativeFilter {
return nil
}
if quietMode {
run.Logger.Printf("%s\n", pkgName)
} else {
run.Logger.Printf("%s %s -> %s\n", text.Bold(pkgName), text.Bold(text.Green(ii.LocalVersion)),
text.Bold(text.Green(ii.Version)))
}
targets.Remove(pkgName)
noUpdates = false
}
return nil
})
missing := false
targets.Each(func(pkgName string) bool {
if dbExecutor.LocalPackage(pkgName) == nil {
run.Logger.Errorln(gotext.Get("package '%s' was not found", pkgName))
missing = true
}
return false
})
if missing || noUpdates {
return fmt.Errorf("")
}
return nil
}
func printInfoValue(logger *text.Logger, key string, values ...string) {
const (
keyLength = 32
delimCount = 2
)
specialWordsCount := 0
for _, runeValue := range key {
// CJK handling: the character 'ー' is Katakana
// but if use unicode.Katakana, it will return false
if unicode.IsOneOf([]*unicode.RangeTable{
unicode.Han,
unicode.Hiragana,
unicode.Katakana,
unicode.Hangul,
}, runeValue) || runeValue == 'ー' {
specialWordsCount++
}
}
keyTextCount := specialWordsCount - keyLength + delimCount
str := fmt.Sprintf(text.Bold("%-*s: "), keyTextCount, key)
if len(values) == 0 || (len(values) == 1 && values[0] == "") {
logger.Printf("%s%s\n", str, gotext.Get("None"))
return
}
maxCols := getColumnCount()
cols := keyLength + len(values[0])
str += values[0]
for _, value := range values[1:] {
if maxCols > keyLength && cols+len(value)+delimCount >= maxCols {
cols = keyLength
str += "\n" + strings.Repeat(" ", keyLength)
} else if cols != keyLength {
str += strings.Repeat(" ", delimCount)
cols += delimCount
}
str += value
cols += len(value)
}
logger.Println(str)
}
var cachedColumnCount = -1
func getColumnCount() int {
if cachedColumnCount > 0 {
return cachedColumnCount
}
if count, err := strconv.Atoi(os.Getenv("COLUMNS")); err == nil {
cachedColumnCount = count
return cachedColumnCount
}
if ws, err := unix.IoctlGetWinsize(syscall.Stdout, unix.TIOCGWINSZ); err == nil {
cachedColumnCount = int(ws.Col)
return cachedColumnCount
}
return 80
}
================================================
FILE: print_test.go
================================================
//go:build !integration
// +build !integration
package main
import (
"context"
"fmt"
"io"
"os"
"strings"
"testing"
"github.com/Jguer/aur"
alpm "github.com/Jguer/dyalpm"
"github.com/stretchr/testify/assert"
"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"
"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 TestPrintUpdateList(t *testing.T) {
// The current method of capturing os.Stdout hinders parallelization.
// Setting of global settings.NoConfirm in printUpdateList also hinders parallelization.
// t.Parallel()
pacmanBin := t.TempDir() + "/pacman"
f, err := os.OpenFile(pacmanBin, os.O_RDONLY|os.O_CREATE, 0o755)
require.NoError(t, err)
require.NoError(t, f.Close())
mockDBName := mock.NewDB("core")
mockDB := &mock.DBExecutor{
AlpmArchitecturesFn: func() ([]string, error) {
return []string{"x86_64"}, nil
},
RefreshHandleFn: func() error {
return nil
},
ReposFn: func() []string {
return []string{"core"}
},
InstalledRemotePackagesFn: func() map[string]alpm.Package {
return map[string]alpm.Package{
"vosk-api": &mock.Package{
PName: "vosk-api",
PVersion: "0.3.43-1",
PBase: "vosk-api",
PReason: alpm.PkgReasonExplicit,
},
}
},
InstalledRemotePackageNamesFn: func() []string {
return []string{"vosk-api"}
},
SyncUpgradesFn: func(
bool,
) (map[string]db.SyncUpgrade, error) {
return map[string]db.SyncUpgrade{
"linux": {
Package: &mock.Package{
PName: "linux",
PVersion: "5.10.0",
PDB: mockDBName,
},
LocalVersion: "4.3.0",
Reason: alpm.PkgReasonExplicit,
},
"go": {
Package: &mock.Package{
PName: "go",
PVersion: "2:1.20.4-1",
PDB: mockDBName,
},
LocalVersion: "2:1.20.3-1",
Reason: alpm.PkgReasonExplicit,
},
}, nil
},
LocalPackageFn: func(s string) mock.IPackage {
if s == "no-update-pkg" {
return &mock.Package{
PName: "no-update-pkg",
PVersion: "3.3.3",
PDB: mockDBName,
}
}
return nil
},
SetLoggerFn: func(logger *text.Logger) {},
}
mockDBNoUpdates := &mock.DBExecutor{
AlpmArchitecturesFn: func() ([]string, error) {
return []string{"x86_64"}, nil
},
RefreshHandleFn: func() error {
return nil
},
ReposFn: func() []string {
return []string{"core"}
},
InstalledRemotePackagesFn: func() map[string]alpm.Package {
return map[string]alpm.Package{
"vosk-api": &mock.Package{
PName: "vosk-api",
PVersion: "0.3.43-1",
PBase: "vosk-api",
PReason: alpm.PkgReasonExplicit,
},
}
},
InstalledRemotePackageNamesFn: func() []string {
return []string{"vosk-api"}
},
SyncUpgradesFn: func(
bool,
) (map[string]db.SyncUpgrade, error) {
return map[string]db.SyncUpgrade{}, nil
},
LocalPackageFn: func(s string) mock.IPackage {
return nil
},
SetLoggerFn: func(logger *text.Logger) {},
}
mockAUR := &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return []aur.Pkg{
{
Name: "vosk-api",
PackageBase: "vosk-api",
Version: "0.3.45-1",
},
}, nil
},
}
mockAURNoUpdates := &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return []aur.Pkg{
{
Name: "vosk-api",
PackageBase: "vosk-api",
Version: "0.3.43-1",
},
}, nil
},
}
type mockData struct {
db *mock.DBExecutor
aurCache *mockaur.MockAUR
}
testCases := []struct {
name string
mockData mockData
args []string
targets []string
wantPkgs []string
wantErr bool
}{
{
name: "Qu",
mockData: mockData{mockDB, mockAUR},
args: []string{"Q", "u"},
targets: []string{},
wantPkgs: []string{
fmt.Sprintf("%s %s -> %s",
text.Bold("linux"),
text.Bold(text.Green("4.3.0")),
text.Bold(text.Green("5.10.0")),
),
fmt.Sprintf("%s %s -> %s",
text.Bold("go"),
text.Bold(text.Green("2:1.20.3-1")),
text.Bold(text.Green("2:1.20.4-1")),
),
fmt.Sprintf("%s %s -> %s",
text.Bold("vosk-api"),
text.Bold(text.Green("0.3.43-1")),
text.Bold(text.Green("0.3.45-1")),
),
},
},
{
name: "Quq",
mockData: mockData{mockDB, mockAUR},
args: []string{"Q", "u", "q"},
targets: []string{},
wantPkgs: []string{"linux", "go", "vosk-api"},
},
{
name: "Quq linux",
mockData: mockData{mockDB, mockAUR},
args: []string{"Q", "u", "q"},
targets: []string{"linux"},
wantPkgs: []string{"linux"},
},
{
name: "Qunq",
mockData: mockData{mockDB, mockAUR},
args: []string{"Q", "u", "n", "q"},
targets: []string{},
wantPkgs: []string{"linux", "go"},
},
{
name: "Qumq",
mockData: mockData{mockDB, mockAUR},
args: []string{"Q", "u", "m", "q"},
targets: []string{},
wantPkgs: []string{"vosk-api"},
},
{
name: "Quq no-update-pkg",
mockData: mockData{mockDB, mockAUR},
args: []string{"Q", "u", "q"},
targets: []string{"no-update-pkg"},
wantPkgs: []string{},
wantErr: true,
},
{
name: "Quq non-existent-pkg",
mockData: mockData{mockDB, mockAUR},
args: []string{"Q", "u", "q"},
targets: []string{"non-existent-pkg"},
wantPkgs: []string{},
wantErr: true,
},
{
name: "Qu no-updates-any",
mockData: mockData{mockDBNoUpdates, mockAURNoUpdates},
args: []string{"Q", "u"},
targets: []string{},
wantPkgs: []string{},
wantErr: true,
},
{
name: "Qun no-updates-native",
mockData: mockData{mockDBNoUpdates, mockAUR},
args: []string{"Q", "u", "n"},
targets: []string{},
wantPkgs: []string{},
wantErr: true,
},
{
name: "Qum no-updates-foreign",
mockData: mockData{mockDB, mockAURNoUpdates},
args: []string{"Q", "u", "m"},
targets: []string{},
wantPkgs: []string{},
wantErr: true,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
cmdBuilder := &exe.CmdBuilder{
SudoBin: "su",
PacmanBin: pacmanBin,
PacmanConfigPath: "/etc/pacman.conf",
Runner: &exe.MockRunner{},
SudoLoopEnabled: false,
}
r, w, _ := os.Pipe()
logger := text.NewLogger(w, io.Discard, strings.NewReader(""), true, "test")
run := &runtime.Runtime{
Cfg: &settings.Configuration{
RemoveMake: "no",
},
Logger: logger,
CmdBuilder: cmdBuilder,
VCSStore: &vcs.Mock{},
AURClient: tc.mockData.aurCache,
}
cmdArgs := parser.MakeArguments()
cmdArgs.AddArg(tc.args...)
cmdArgs.AddTarget(tc.targets...)
err = handleCmd(context.Background(), run, cmdArgs, tc.mockData.db)
w.Close()
out, _ := io.ReadAll(r)
if tc.wantErr {
require.Error(t, err)
assert.EqualError(t, err, "")
} else {
require.NoError(t, err)
}
outStr := string(out)
outPkgs := make([]string, 0)
if outStr != "" {
outPkgs = strings.Split(strings.TrimSuffix(outStr, "\n"), "\n")
}
assert.ElementsMatch(t, outPkgs, tc.wantPkgs, "Lists of packages should match")
})
}
}
================================================
FILE: query.go
================================================
package main
import (
"context"
"fmt"
"io/fs"
"path/filepath"
aur "github.com/Jguer/aur"
alpm "github.com/Jguer/dyalpm"
mapset "github.com/deckarep/golang-set/v2"
"github.com/leonelquinteros/gotext"
"github.com/Jguer/yay/v12/pkg/db"
"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/parser"
"github.com/Jguer/yay/v12/pkg/text"
)
// SyncSearch presents a query to the local repos and to the AUR.
func syncSearch(ctx context.Context, pkgS []string,
dbExecutor db.Executor, queryBuilder query.Builder, verbose bool,
) error {
queryBuilder.Execute(ctx, dbExecutor, pkgS)
searchMode := query.Minimal
if verbose {
searchMode = query.Detailed
}
return queryBuilder.Results(dbExecutor, searchMode)
}
// SyncInfo serves as a pacman -Si for repo packages and AUR packages.
func syncInfo(ctx context.Context, run *runtime.Runtime,
cmdArgs *parser.Arguments, pkgS []string, dbExecutor db.Executor,
) error {
var (
remoteAurPkgs []aur.Pkg
err error
missing = false
)
pkgS = query.RemoveInvalidTargets(run.Logger, pkgS, run.Cfg.Mode)
expandedPackages := []string{}
for _, pkg := range pkgS {
groupPackages := dbExecutor.PackagesFromGroup(pkg)
if len(groupPackages) > 0 {
for _, p := range groupPackages {
expandedPackages = append(expandedPackages, p.Name())
}
} else {
expandedPackages = append(expandedPackages, pkg)
}
}
pkgS = expandedPackages
aurS, repoS := packageSlices(pkgS, run.Cfg, dbExecutor)
if len(repoS) == 0 && len(aurS) == 0 {
if run.Cfg.Mode != parser.ModeRepo {
aurS = dbExecutor.InstalledRemotePackageNames()
}
if run.Cfg.Mode != parser.ModeAUR {
repoS = dbExecutor.InstalledSyncPackageNames()
}
}
if len(aurS) != 0 {
noDB := make([]string, 0, len(aurS))
for _, pkg := range aurS {
_, name := text.SplitDBFromName(pkg)
noDB = append(noDB, name)
}
remoteAurPkgs, err = run.AURClient.Get(ctx, &aur.Query{
Needles: noDB,
By: aur.Name,
})
if err != nil {
run.Logger.Errorln(err)
}
// Check for any missing packages and print errors for any not found
found := mapset.NewThreadUnsafeSet[string]()
for i := range remoteAurPkgs {
found.Add(remoteAurPkgs[i].Name)
}
for _, name := range noDB {
if !found.Contains(name) {
missing = true
run.Logger.Errorln(gotext.Get("No AUR package found for"), " ", name)
}
}
}
if len(repoS) != 0 || (len(aurS) == 0 && len(repoS) == 0) {
arguments := cmdArgs.Copy()
arguments.ClearTargets()
arguments.AddTarget(repoS...)
err = run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx,
arguments, run.Cfg.Mode, settings.NoConfirm))
if err != nil {
return err
}
}
for i := range remoteAurPkgs {
printInfo(run.Logger, run.Cfg, &remoteAurPkgs[i], cmdArgs.ExistsDouble("i"))
}
if missing {
err = fmt.Errorf("")
}
return err
}
// PackageSlices separates an input slice into aur and repo slices.
func packageSlices(toCheck []string, config *settings.Configuration, dbExecutor db.Executor) (aurNames, repoNames []string) {
for _, _pkg := range toCheck {
dbName, name := text.SplitDBFromName(_pkg)
if dbName == "aur" || config.Mode == parser.ModeAUR {
aurNames = append(aurNames, _pkg)
continue
} else if dbName != "" || config.Mode == parser.ModeRepo {
repoNames = append(repoNames, _pkg)
continue
}
if dbExecutor.SyncSatisfierExists(name) ||
len(dbExecutor.PackagesFromGroup(name)) != 0 {
repoNames = append(repoNames, _pkg)
} else {
aurNames = append(aurNames, _pkg)
}
}
return aurNames, repoNames
}
// MapSetMap is a Map of Sets.
type mapSetMap[T comparable] map[T]mapset.Set[T]
// Add adds a new value to the Map.
// If n is already in the map, then v is appended to the StringSet under that key.
// Otherwise a new Set is created containing v.
func (mss mapSetMap[T]) Add(n, v T) {
if _, ok := mss[n]; !ok {
mss[n] = mapset.NewSet[T]()
}
mss[n].Add(v)
}
// HangingPackages returns a list of packages installed as deps
// and unneeded by the system
// removeOptional decides whether optional dependencies are counted or not.
func hangingPackages(removeOptional bool, dbExecutor db.Executor) (hanging []string) {
// safePackages represents every package in the system in one of 3 states
// State = 0 - Remove package from the system
// State = 1 - Keep package in the system; need to iterate over dependencies
// State = 2 - Keep package and have iterated over dependencies
safePackages := make(map[string]uint8)
// provides stores a mapping from the provides name back to the original package name
provides := make(mapSetMap[string])
packages := dbExecutor.LocalPackages()
// Mark explicit dependencies and enumerate the provides list
for _, pkg := range packages {
if pkg.Reason() == alpm.PkgReasonExplicit {
safePackages[pkg.Name()] = 1
} else {
safePackages[pkg.Name()] = 0
}
for _, dep := range dbExecutor.PackageProvides(pkg) {
provides.Add(dep.Name, pkg.Name())
}
}
iterateAgain := true
for iterateAgain {
iterateAgain = false
for _, pkg := range packages {
if state := safePackages[pkg.Name()]; state == 0 || state == 2 {
continue
}
safePackages[pkg.Name()] = 2
deps := dbExecutor.PackageDepends(pkg)
if !removeOptional {
deps = append(deps, dbExecutor.PackageOptionalDepends(pkg)...)
}
// Update state for dependencies
for _, dep := range deps {
// Don't assume a dependency is installed
state, ok := safePackages[dep.Name]
if !ok {
// Check if dep is a provides rather than actual package name
if pset, ok2 := provides[dep.Name]; ok2 {
for p := range pset.Iter() {
if safePackages[p] == 0 {
iterateAgain = true
safePackages[p] = 1
}
}
}
continue
}
if state == 0 {
iterateAgain = true
safePackages[dep.Name] = 1
}
}
}
}
// Build list of packages to be removed
for _, pkg := range packages {
if safePackages[pkg.Name()] == 0 {
hanging = append(hanging, pkg.Name())
}
}
return hanging
}
func getFolderSize(path string) (size int64) {
_ = filepath.WalkDir(path, func(p string, entry fs.DirEntry, err error) error {
if err != nil || entry == nil {
return nil
}
info, infoErr := entry.Info()
if infoErr != nil || info == nil {
return nil
}
size += info.Size()
return nil
})
return size
}
// Statistics returns statistics about packages installed in system.
func statistics(run *runtime.Runtime, dbExecutor db.Executor) (res struct {
Totaln int
Expln int
TotalSize int64
pacmanCaches map[string]int64
yayCache int64
},
) {
for _, pkg := range dbExecutor.LocalPackages() {
res.TotalSize += pkg.ISize()
res.Totaln++
if pkg.Reason() == alpm.PkgReasonExplicit {
res.Expln++
}
}
res.pacmanCaches = make(map[string]int64)
for _, path := range run.PacmanConf.CacheDir {
res.pacmanCaches[path] = getFolderSize(path)
}
res.yayCache = getFolderSize(run.Cfg.BuildDir)
return
}
================================================
FILE: query_test.go
================================================
//go:build !integration
// +build !integration
package main
import (
"context"
"encoding/json"
"fmt"
"io"
"os"
"os/exec"
"strings"
"testing"
"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/aur"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
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 *aur.Query) ([]aur.Pkg, error) {
return pkgs, nil
}
}
func TestSyncInfo(t *testing.T) {
t.Parallel()
pacmanBin := t.TempDir() + "/pacman"
testCases := []struct {
name string
args []string
targets []string
wantShow []string
wantErr bool
}{
{
name: "Si linux",
args: []string{"S", "i"},
targets: []string{"linux"},
wantShow: []string{"pacman", "-S", "-i", "--config", "/etc/pacman.conf", "--", "linux"},
},
{
name: "Si jellyfin",
args: []string{"S", "i"},
targets: []string{"jellyfin"},
wantShow: []string{},
},
{
name: "Si linux jellyfin",
args: []string{"S", "i"},
targets: []string{"linux", "jellyfin"},
wantShow: []string{"pacman", "-S", "-i", "--config", "/etc/pacman.conf", "--", "linux"},
},
{
name: "Si jellyfin",
args: []string{"S", "i"},
targets: []string{"jellyfin"},
wantShow: []string{},
},
{
name: "Si missing",
args: []string{"S", "i"},
targets: []string{"missing"},
wantShow: []string{},
wantErr: true,
},
{
name: "Si arduino",
args: []string{"S", "i"},
targets: []string{"arduino"},
wantShow: []string{"pacman", "-S", "-i", "--config", "/etc/pacman.conf", "--", "arduino-cli"},
},
}
dbExc := &mock.DBExecutor{
SyncSatisfierFn: func(s string) mock.IPackage {
if s == "linux" {
return &mock.Package{
PName: "linux",
PBase: "linux",
}
}
if s == "arduino-cli" {
return &mock.Package{
PName: "arduino-cli",
PBase: "arduino-cli",
}
}
return nil
},
PackagesFromGroupFn: func(s string) []mock.IPackage {
if s == "arduino" {
return []mock.IPackage{
&mock.Package{PName: "arduino-cli"},
}
}
return nil
},
}
mockAUR := &mockaur.MockAUR{GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
if query.Needles[0] == "jellyfin" {
jfinFn := getFromFile(t, "pkg/dep/testdata/jellyfin.json")
return jfinFn(ctx, query)
}
return nil, fmt.Errorf("not found")
}}
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,
AURClient: mockAUR,
Logger: newTestLogger(),
Cfg: &settings.Configuration{},
}
cmdArgs := parser.MakeArguments()
cmdArgs.AddArg(tc.args...)
cmdArgs.AddTarget(tc.targets...)
err := handleCmd(context.Background(),
run, cmdArgs, dbExc,
)
if tc.wantErr {
require.Error(t, err)
assert.EqualError(t, err, "")
} else {
require.NoError(t, err)
}
if len(tc.wantShow) == 0 {
assert.Empty(t, mockRunner.ShowCalls)
return
}
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))
}
})
}
}
// Should not error when there is a DB called aur
func TestSyncSearchAURDB(t *testing.T) {
t.Parallel()
pacmanBin := t.TempDir() + "/pacman"
testCases := []struct {
name string
args []string
targets []string
wantShow []string
wantErr bool
bottomUp bool
singleLine bool
mixed bool
}{
{
name: "Ss jellyfin false false",
args: []string{"S", "s"},
targets: []string{"jellyfin"},
wantShow: []string{},
},
{
name: "Ss jellyfin true false",
args: []string{"S", "s"},
targets: []string{"jellyfin"},
wantShow: []string{},
singleLine: true,
},
{
name: "Ss jellyfin true true",
args: []string{"S", "s"},
targets: []string{"jellyfin"},
wantShow: []string{},
singleLine: true,
mixed: true,
},
{
name: "Ss jellyfin false true",
args: []string{"S", "s"},
targets: []string{"jellyfin"},
wantShow: []string{},
singleLine: false,
mixed: true,
},
{
name: "Ss jellyfin true true - bottomup",
args: []string{"S", "s"},
targets: []string{"jellyfin"},
wantShow: []string{},
singleLine: true,
mixed: true,
bottomUp: true,
},
}
dbExc := &mock.DBExecutor{
SyncPackagesFn: func(s ...string) []mock.IPackage {
return []mock.IPackage{
&mock.Package{
PName: "jellyfin",
PBase: "jellyfin",
PDB: mock.NewDB("aur"),
},
}
},
LocalPackageFn: func(s string) mock.IPackage {
return &mock.Package{
PName: "jellyfin",
PBase: "jellyfin",
PDB: mock.NewDB("aur"),
}
},
PackagesFromGroupFn: func(s string) []mock.IPackage {
return nil
},
}
mockAUR := &mockaur.MockAUR{GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
if query.Needles[0] == "jellyfin" {
jfinFn := getFromFile(t, "pkg/dep/testdata/jellyfin.json")
return jfinFn(ctx, query)
}
return nil, fmt.Errorf("not found")
}}
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,
AURClient: mockAUR,
QueryBuilder: query.NewSourceQueryBuilder(mockAUR, newTestLogger(), "votes", parser.ModeAny, "name",
tc.bottomUp, tc.singleLine, tc.mixed),
Logger: newTestLogger(),
Cfg: &settings.Configuration{},
}
cmdArgs := parser.MakeArguments()
cmdArgs.AddArg(tc.args...)
cmdArgs.AddTarget(tc.targets...)
err := handleCmd(context.Background(),
run, cmdArgs, dbExc,
)
if tc.wantErr {
require.Error(t, err)
assert.EqualError(t, err, "")
} else {
require.NoError(t, err)
}
if len(tc.wantShow) == 0 {
assert.Empty(t, mockRunner.ShowCalls)
return
}
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))
}
})
}
}
================================================
FILE: sync.go
================================================
package main
import (
"context"
"fmt"
"strings"
"github.com/leonelquinteros/gotext"
"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"
"github.com/Jguer/yay/v12/pkg/upgrade"
)
func syncInstall(ctx context.Context,
run *runtime.Runtime,
cmdArgs *parser.Arguments,
dbExecutor db.Executor,
) error {
aurCache := run.AURClient
refreshArg := cmdArgs.ExistsArg("y", "refresh")
noDeps := cmdArgs.ExistsArg("d", "nodeps")
noCheck := strings.Contains(run.Cfg.MFlags, "--nocheck")
if noDeps {
run.CmdBuilder.AddMakepkgFlag("-d")
}
if refreshArg && run.Cfg.Mode.AtLeastRepo() {
if errR := earlyRefresh(ctx, run.Cfg, run.CmdBuilder, cmdArgs); errR != nil {
return fmt.Errorf("%s - %w", gotext.Get("error refreshing databases"), errR)
}
// we may have done -Sy, our handle now has an old
// database.
if errRefresh := dbExecutor.RefreshHandle(); errRefresh != nil {
return errRefresh
}
}
grapher := dep.NewGrapher(dbExecutor, aurCache, false, settings.NoConfirm,
noDeps, noCheck, cmdArgs.ExistsArg("needed"), run.Logger.Child("grapher"))
graph, err := grapher.GraphFromTargets(ctx, nil, cmdArgs.Targets)
if err != nil {
return err
}
excluded := []string{}
if cmdArgs.ExistsArg("u", "sysupgrade") {
var errSysUp error
upService := upgrade.NewUpgradeService(
grapher, aurCache, dbExecutor, run.VCSStore,
run.Cfg, settings.NoConfirm, run.Logger.Child("upgrade"))
graph, errSysUp = upService.GraphUpgrades(ctx,
graph, cmdArgs.ExistsDouble("u", "sysupgrade"),
func(*upgrade.Upgrade) bool { return true })
if errSysUp != nil {
return errSysUp
}
upService.AURWarnings.Print()
excluded, errSysUp = upService.UserExcludeUpgrades(graph)
if errSysUp != nil {
return errSysUp
}
}
opService := sync.NewOperationService(ctx, dbExecutor, run)
multiErr := &multierror.MultiError{}
targets := graph.TopoSortedLayers(func(s string, ii *dep.InstallInfo) error {
if ii.Source == dep.Missing {
multiErr.Add(fmt.Errorf("%w: %s %s", ErrPackagesNotFound, s, ii.Version))
}
return nil
})
if err := multiErr.Return(); err != nil {
return err
}
return opService.Run(ctx, run, cmdArgs, targets, excluded)
}
func earlyRefresh(ctx context.Context, cfg *settings.Configuration, cmdBuilder exe.ICmdBuilder, cmdArgs *parser.Arguments) error {
arguments := cmdArgs.Copy()
if cfg.CombinedUpgrade {
arguments.DelArg("u", "sysupgrade")
}
arguments.DelArg("s", "search")
arguments.DelArg("i", "info")
arguments.DelArg("l", "list")
arguments.ClearTargets()
return cmdBuilder.Show(cmdBuilder.BuildPacmanCmd(ctx,
arguments, cfg.Mode, settings.NoConfirm))
}
================================================
FILE: sync_test.go
================================================
//go:build !integration
// +build !integration
package main
import (
"context"
"errors"
"fmt"
"io"
"os"
"os/exec"
"strings"
"sync"
"testing"
"github.com/Jguer/aur"
alpm "github.com/Jguer/dyalpm"
"github.com/stretchr/testify/assert"
"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"
"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 TestSyncUpgrade(t *testing.T) {
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("S")
cmdArgs.AddArg("y")
cmdArgs.AddArg("u")
dbName := mock.NewDB("core")
db := &mock.DBExecutor{
AlpmArchitecturesFn: func() ([]string, error) {
return []string{"x86_64"}, nil
},
RefreshHandleFn: func() error {
return nil
},
ReposFn: func() []string {
return []string{"core"}
},
InstalledRemotePackagesFn: func() map[string]alpm.Package {
return map[string]alpm.Package{}
},
InstalledRemotePackageNamesFn: func() []string {
return []string{}
},
SyncUpgradesFn: func(
bool,
) (map[string]db.SyncUpgrade, error) {
return map[string]db.SyncUpgrade{
"linux": {
Package: &mock.Package{
PName: "linux",
PVersion: "5.10.0",
PDB: dbName,
},
LocalVersion: "4.3.0",
Reason: alpm.PkgReasonExplicit,
},
}, nil
},
}
run := &runtime.Runtime{
Cfg: &settings.Configuration{
RemoveMake: "no",
},
Logger: text.NewLogger(io.Discard, os.Stderr, strings.NewReader("\n"), true, "test"),
CmdBuilder: cmdBuilder,
VCSStore: &vcs.Mock{},
AURClient: &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return []aur.Pkg{}, nil
},
},
}
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, "git")
// 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))
}
}
func TestSyncUpgrade_IgnoreAll(t *testing.T) {
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("S")
cmdArgs.AddArg("y")
cmdArgs.AddArg("u")
dbName := mock.NewDB("core")
db := &mock.DBExecutor{
AlpmArchitecturesFn: func() ([]string, error) {
return []string{"x86_64"}, nil
},
RefreshHandleFn: func() error {
return nil
},
ReposFn: func() []string {
return []string{"core"}
},
InstalledRemotePackagesFn: func() map[string]alpm.Package {
return map[string]alpm.Package{}
},
InstalledRemotePackageNamesFn: func() []string {
return []string{}
},
SyncUpgradesFn: func(
bool,
) (map[string]db.SyncUpgrade, error) {
return map[string]db.SyncUpgrade{
"linux": {
Package: &mock.Package{
PName: "linux",
PVersion: "5.10.0",
PDB: dbName,
},
LocalVersion: "4.3.0",
Reason: alpm.PkgReasonExplicit,
},
}, nil
},
}
run := &runtime.Runtime{
Cfg: &settings.Configuration{
RemoveMake: "no",
},
Logger: text.NewLogger(io.Discard, os.Stderr, strings.NewReader("1\n"), true, "test"),
CmdBuilder: cmdBuilder,
VCSStore: &vcs.Mock{},
AURClient: &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return []aur.Pkg{}, nil
},
},
}
err = handleCmd(context.Background(), run, cmdArgs, db)
require.NoError(t, err)
wantCapture := []string{}
wantShow := []string{
"pacman -S -y --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, "git")
// 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))
}
}
func TestSyncUpgrade_IgnoreOne(t *testing.T) {
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("S")
cmdArgs.AddArg("y")
cmdArgs.AddArg("u")
dbName := mock.NewDB("core")
db := &mock.DBExecutor{
AlpmArchitecturesFn: func() ([]string, error) {
return []string{"x86_64"}, nil
},
RefreshHandleFn: func() error {
return nil
},
ReposFn: func() []string {
return []string{"core"}
},
InstalledRemotePackagesFn: func() map[string]alpm.Package {
return map[string]alpm.Package{}
},
InstalledRemotePackageNamesFn: func() []string {
return []string{}
},
SyncUpgradesFn: func(
bool,
) (map[string]db.SyncUpgrade, error) {
return map[string]db.SyncUpgrade{
"gcc": {
Package: &mock.Package{
PName: "gcc",
PVersion: "6.0.0",
PDB: dbName,
},
LocalVersion: "5.0.0",
Reason: alpm.PkgReasonExplicit,
},
"linux": {
Package: &mock.Package{
PName: "linux",
PVersion: "5.10.0",
PDB: dbName,
},
LocalVersion: "4.3.0",
Reason: alpm.PkgReasonExplicit,
},
"linux-headers": {
Package: &mock.Package{
PName: "linux-headers",
PVersion: "5.10.0",
PDB: dbName,
},
LocalVersion: "4.3.0",
Reason: alpm.PkgReasonDepend,
},
}, nil
},
}
run := &runtime.Runtime{
Cfg: &settings.Configuration{
RemoveMake: "no",
},
Logger: text.NewLogger(io.Discard, os.Stderr, strings.NewReader("1\n"), true, "test"),
CmdBuilder: cmdBuilder,
VCSStore: &vcs.Mock{},
AURClient: &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return []aur.Pkg{}, nil
},
},
}
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 --ignore linux-headers --",
}
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, "git")
// 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))
}
}
// Pinned deps with rollup
func TestSyncUpgradeAURPinnedSplitPackage(t *testing.T) {
t.Parallel()
makepkgBin := t.TempDir() + "/makepkg"
pacmanBin := t.TempDir() + "/pacman"
tmpDir := t.TempDir()
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())
pkgBuildDir := tmpDir + "/vosk-api"
os.Mkdir(pkgBuildDir, 0o755)
fSource, err := os.OpenFile(pkgBuildDir+"/.SRCINFO", os.O_RDWR|os.O_CREATE, 0o666)
require.NoError(t, err)
n, errF := fSource.WriteString(`pkgbase = vosk-api
pkgdesc = Offline speech recognition toolkit
pkgver = 0.3.45
pkgrel = 1
url = https://alphacephei.com/vosk/
arch = x86_64
license = Apache
pkgname = vosk-api
pkgdesc = vosk-api
pkgname = python-vosk
pkgdesc = Python module for vosk-api
depends = vosk-api=0.3.45`)
require.NoError(t, errF)
require.Greater(t, n, 0)
require.NoError(t, fSource.Close())
tars := []string{
tmpDir + "/vosk-api-0.3.45-1-x86_64.pkg.tar.zst",
tmpDir + "/python-vosk-0.3.45-1-x86_64.pkg.tar.zst",
}
captureOverride := func(cmd *exec.Cmd) (stdout string, stderr string, err error) {
return strings.Join(tars, "\n"), "", nil
}
once := sync.Once{}
showOverride := func(cmd *exec.Cmd) error {
once.Do(func() {
for _, tar := range tars {
f, err := os.OpenFile(tar, os.O_RDONLY|os.O_CREATE, 0o666)
require.NoError(t, err)
require.NoError(t, f.Close())
}
})
if sanitizeCall(cmd.String(), tmpDir, makepkgBin,
pacmanBin, gitBin) == "pacman -U --config /etc/pacman.conf -- /testdir/vosk-api-0.3.45-1-x86_64.pkg.tar.zst" {
return errors.New("Unsatisfied dependency")
}
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("S")
cmdArgs.AddArg("y")
cmdArgs.AddArg("u")
db := &mock.DBExecutor{
AlpmArchitecturesFn: func() ([]string, error) {
return []string{"x86_64"}, nil
},
RefreshHandleFn: func() error {
return nil
},
ReposFn: func() []string {
return []string{"core"}
},
SyncSatisfierFn: func(s string) mock.IPackage {
return nil
},
InstalledRemotePackagesFn: func() map[string]alpm.Package {
return map[string]alpm.Package{
"vosk-api": &mock.Package{
PName: "vosk-api",
PVersion: "0.3.43-1",
PBase: "vosk-api",
PReason: alpm.PkgReasonDepend,
},
"python-vosk": &mock.Package{
PName: "python-vosk",
PVersion: "0.3.43-1",
PBase: "python-vosk",
PReason: alpm.PkgReasonExplicit,
// TODO: fix mock Depends
},
}
},
InstalledRemotePackageNamesFn: func() []string {
return []string{"vosk-api", "python-vosk"}
},
LocalSatisfierExistsFn: func(s string) bool {
return false
},
SyncUpgradesFn: func(
bool,
) (map[string]db.SyncUpgrade, error) {
return map[string]db.SyncUpgrade{}, nil
},
}
run := &runtime.Runtime{
Cfg: &settings.Configuration{
DoubleConfirm: true,
RemoveMake: "no",
BuildDir: tmpDir,
},
Logger: text.NewLogger(io.Discard, os.Stderr, strings.NewReader("\n\n\n\n"), true, "test"),
CmdBuilder: cmdBuilder,
VCSStore: &vcs.Mock{},
AURClient: &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return []aur.Pkg{
{
Name: "vosk-api",
PackageBase: "vosk-api",
Version: "0.3.45-1",
},
{
Name: "python-vosk",
PackageBase: "vosk-api",
Version: "0.3.45-1",
Depends: []string{
"vosk-api=0.3.45",
},
},
}, nil
},
},
}
err = handleCmd(context.Background(), run, cmdArgs, db)
require.NoError(t, err)
wantCapture := []string{
"/usr/bin/git -C /testdir/vosk-api reset --hard HEAD",
"/usr/bin/git -C /testdir/vosk-api merge --no-edit --ff",
"makepkg --packagelist",
"makepkg --packagelist",
}
wantShow := []string{
"pacman -S -y --config /etc/pacman.conf --",
"makepkg --verifysource --skippgpcheck -f -Cc", "makepkg --nobuild -f -C --ignorearch",
"makepkg -c --nobuild --noextract --ignorearch",
"pacman -U --config /etc/pacman.conf -- /testdir/vosk-api-0.3.45-1-x86_64.pkg.tar.zst",
"makepkg --nobuild -f -C --ignorearch", "makepkg -c --nobuild --noextract --ignorearch",
"pacman -U --config /etc/pacman.conf -- /testdir/vosk-api-0.3.45-1-x86_64.pkg.tar.zst /testdir/python-vosk-0.3.45-1-x86_64.pkg.tar.zst",
"pacman -D -q --asdeps --config /etc/pacman.conf -- vosk-api",
"pacman -D -q --asexplicit --config /etc/pacman.conf -- python-vosk",
}
require.Len(t, mockRunner.ShowCalls, len(wantShow),
fmt.Sprintf("%#v", sanitizeCalls(mockRunner.ShowCalls, tmpDir, makepkgBin, pacmanBin, gitBin)))
require.Len(t, mockRunner.CaptureCalls, len(wantCapture),
fmt.Sprintf("%#v", sanitizeCalls(mockRunner.CaptureCalls, tmpDir, makepkgBin, pacmanBin, gitBin)))
for i, call := range mockRunner.ShowCalls {
show := call.Args[0].(*exec.Cmd).String()
show = strings.ReplaceAll(show, tmpDir, "/testdir") // replace the temp dir with a static path
show = strings.ReplaceAll(show, makepkgBin, "makepkg")
show = strings.ReplaceAll(show, pacmanBin, "pacman")
show = strings.ReplaceAll(show, gitBin, "git")
// 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))
}
}
func sanitizeCalls(calls []exe.Call, tmpDir, makepkg, pacman, git string) []string {
san := make([]string, 0, len(calls))
for _, c := range calls {
s := c.Args[0].(*exec.Cmd).String()
san = append(san, sanitizeCall(s, tmpDir, makepkg, pacman, git))
}
return san
}
func sanitizeCall(s, tmpDir, makepkg, pacman, git string) string {
_, after, found := strings.Cut(s, makepkg)
if found {
s = "makepkg" + after
}
_, after, found = strings.Cut(s, pacman)
if found {
s = "pacman" + after
}
_, after, found = strings.Cut(s, git)
if found {
s = "git" + after
}
s = strings.ReplaceAll(s, tmpDir, "/testdir")
return s
}
func TestSyncUpgrade_NoCombinedUpgrade(t *testing.T) {
t.Parallel()
testCases := []struct {
name string
combinedUpgrade bool
want []string
}{
{
name: "combined upgrade",
combinedUpgrade: true,
want: []string{"pacman -S -y -u --config /etc/pacman.conf --"},
},
{
name: "no combined upgrade",
combinedUpgrade: false,
want: []string{"pacman -S -y --config /etc/pacman.conf --"},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
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("S")
cmdArgs.AddArg("y")
cmdArgs.AddArg("u")
db := &mock.DBExecutor{
AlpmArchitecturesFn: func() ([]string, error) {
return []string{"x86_64"}, nil
},
RefreshHandleFn: func() error {
return nil
},
ReposFn: func() []string {
return []string{"core"}
},
InstalledRemotePackagesFn: func() map[string]alpm.Package {
return map[string]alpm.Package{}
},
InstalledRemotePackageNamesFn: func() []string {
return []string{}
},
SyncUpgradesFn: func(
bool,
) (map[string]db.SyncUpgrade, error) {
return map[string]db.SyncUpgrade{}, nil
},
}
run := &runtime.Runtime{
Cfg: &settings.Configuration{
RemoveMake: "no",
CombinedUpgrade: false,
},
Logger: text.NewLogger(io.Discard, os.Stderr, strings.NewReader("1\n"), true, "test"),
CmdBuilder: cmdBuilder,
VCSStore: &vcs.Mock{},
AURClient: &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return []aur.Pkg{}, nil
},
},
}
err = handleCmd(context.Background(), run, cmdArgs, db)
require.NoError(t, err)
require.Len(t, mockRunner.ShowCalls, len(tc.want))
require.Len(t, mockRunner.CaptureCalls, 0)
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.want[i], " "), fmt.Sprintf("%d - %s", i, show))
}
})
}
}
================================================
FILE: testdata/cephbin/.SRCINFO
================================================
pkgbase = ceph-bin
pkgdesc = Distributed, fault-tolerant storage platform delivering object, block, and file system
pkgver = 17.2.6
pkgrel = 2
url = https://ceph.com/
arch = x86_64
license = GPL
noextract = ceph-17.2.6-2.tar.zst
noextract = ceph-libs-17.2.6-2.tar.zst
options = emptydirs
source = ceph-17.2.6-2.tar.zst::https://github.com/bazaah/aur-ceph/releases/download/v17.2.6-2/ceph_linux_x86_64.tar.zstd
source = ceph-libs-17.2.6-2.tar.zst::https://github.com/bazaah/aur-ceph/releases/download/v17.2.6-2/ceph_libs_linux_x86_64.tar.zstd
sha512sums = db38d454ef9fda99fbe9a0954ccb4a8a7b66537f5cb1998dd552161049a6750a82a218d6e95f2bbc2a501637bf13f2f2d0fd6a12b367733cd294364722ba236c
sha512sums = 2218b8d81ce8b94daa63ebd7674bd6d8e3da047ad46bbde1e1666eaa10d429a329ad1e6fa43d9f9d07ba5ce06b57dcc442ca3269eb482bffbb1c25d247d2ba1b
sha512sums = 029f5fd11f30800a78fdc1e4c6541c413be498b284271ef93886268fd47002f6505df698c64a8118f1c2dcef22081c4fcc79e6047db7202d66f2e3e132830742
pkgname = ceph-bin
depends = ceph-libs=17.2.6-2
depends = dep1
depends = dep2
provides = ceph=17.2.6-2
conflicts = ceph
pkgname = ceph-libs-bin
depends = dep1
depends = curl
provides = ceph-libs=17.2.6-2
conflicts = ceph-libs
================================================
FILE: testdata/cephbin/PKGBUILD
================================================
================================================
FILE: testdata/gourou/.SRCINFO
================================================
pkgbase = gourou
pkgdesc = Download and decrypt adobe encrypted (acsm) pdf and epub files
pkgver = 0.8.1
pkgrel = 4
url = https://indefero.soutade.fr/p/libgourou
arch = x86_64
license = LGPL3
depends = glibc
depends = gcc-libs
depends = zlib
depends = libzip
depends = openssl
depends = pugixml
depends = curl
provides = gourou=0.8.1
provides = libgourou=0.8.1
conflicts = gourou-git
conflicts = gourou-bin
options = strip
source = git://soutade.fr/libgourou.git#tag=v0.8.1
source = git://soutade.fr/updfparser.git#commit=a421098092ba600fb1686a7df8fc58cd67429f59
source = build.patch
sha512sums = SKIP
sha512sums = SKIP
sha512sums = 768e49fddcabe8b4c6f771ebbddf2618ab59e7b1a399d99aa9a9881f932e092210878ef576144593684b4c3a763218c5b546dbe19fdbadeff13995245bffda19
pkgname = gourou
================================================
FILE: testdata/gourou/PKGBUILD
================================================
================================================
FILE: testdata/jfin/.SRCINFO
================================================
pkgbase = jellyfin
pkgdesc = The Free Software Media System
pkgver = 10.8.4
pkgrel = 1
url = https://github.com/jellyfin/jellyfin
arch = i686
arch = x86_64
arch = armv6h
license = GPL2
makedepends = dotnet-sdk>=6
makedepends = dotnet-sdk<7
makedepends = nodejs
makedepends = npm
makedepends = git
source = jellyfin-10.8.4.tar.gz::https://github.com/jellyfin/jellyfin/archive/v10.8.4.tar.gz
source = jellyfin-web-10.8.4.tar.gz::https://github.com/jellyfin/jellyfin-web/archive/v10.8.4.tar.gz
source = jellyfin.conf
source = jellyfin.service
source = jellyfin.sysusers
source = jellyfin.tmpfiles
sha512sums = cf472f36a759a7eb3724dac79d3bd2d6c9c58fc375293ad6eb8b5ce1ea1a8f6dd296cc36113b80b1c705a99eafb2bd9ffd9381fd52fa19aa12018d50656c9bde
sha512sums = 21983940689475de7f9d37a1016fb2dd740986ac27ffa2e0eac0bc9c84d68ac557fdc8afb64ca70b867af2d1e438293b98d5c155da402d3e985ab831042ba176
sha512sums = 2aa97a1a7a8a447171b59be3e93183e09cbbc32c816843cc47c6777b9aec48bd9c1d9d354f166e0b000ad8d2e94e6e4b0559aa52e5c159abbc103ed2c5afa3f0
sha512sums = 99d02080b1b92e731250f39ddd13ceca7129d69d0c05e0939620cbc3f499a9574668c63fa889704a4905560888131e980d7ab1fbcc5837b04d33ce26daa9d42b
sha512sums = 6fc2638e6ec4b1ee0240e17815c91107b694e5fde72c1bc7956c83067bbeacb632de899b86837e47a0ec04288131b15c20746373b45e0669c8976069a55d627a
sha512sums = 45a62b62d97b9a83289d4dfde684163b1bcf340c1921fb958e5a701812c61b392901841940c67e5fa5148783277d5b4dc65ba01d3a22e8f855ea62154ad9be33
pkgname = jellyfin
depends = jellyfin-web=10.8.4
depends = jellyfin-server=10.8.4
pkgname = jellyfin-web
pkgdesc = Jellyfin web client
pkgname = jellyfin-server
pkgdesc = Jellyfin server component
depends = dotnet-runtime>=6
depends = dotnet-runtime<7
depends = aspnet-runtime>=6
depends = aspnet-runtime<7
depends = ffmpeg
depends = sqlite
backup = etc/conf.d/jellyfin
================================================
FILE: testdata/jfin/PKGBUILD
================================================
================================================
FILE: testdata/libzip-git/.SRCINFO
================================================
pkgbase = libzip-git
pkgdesc = C library for reading, creating, and modifying zip archives
pkgver = 1.9.2.r144.gdadb14d5
pkgrel = 1
url = https://libzip.org/
arch = i686
arch = x86_64
license = BSD
makedepends = git
makedepends = cmake
depends = glibc
depends = bzip2
depends = gnutls
depends = openssl
depends = xz
depends = zlib
depends = zstd
provides = libzip=1.9.2.r144.gdadb14d5
conflicts = libzip
source = git+https://github.com/nih-at/libzip.git
sha256sums = SKIP
pkgname = libzip-git
================================================
FILE: testdata/libzip-git/PKGBUILD
================================================
================================================
FILE: testdata/pacman.conf
================================================
#
# /etc/pacman.conf
#
# See the pacman.conf(5) manpage for option and repository directives
#
# GENERAL OPTIONS
#
[options]
# The following paths are commented out with their default values listed.
# If you wish to use different paths, uncomment and update the paths.
RootDir = /
DBPath = /var/lib/pacman/
CacheDir = /var/cache/pacman/pkg/
LogFile = /var/log/pacman.log
GPGDir = /etc/pacman.d/gnupg/
HookDir = /etc/pacman.d/hooks/
HoldPkg = pacman glibc
#XferCommand = /usr/bin/curl -L -C - -f -o %o %u
XferCommand = /usr/bin/wget --passive-ftp -c -O %o %u
CleanMethod = KeepInstalled
Architecture = auto
# Pacman won't upgrade packages listed in IgnorePkg and members of IgnoreGroup
IgnorePkg = xorm
IgnoreGroup = yorm
#NoUpgrade =
#NoExtract =
# Misc options
UseSyslog
Color
NoProgressBar
CheckSpace
VerbosePkgLists
ParallelDownloads = 5
# By default, pacman accepts packages signed by keys that its local keyring
# trusts (see pacman-key and its man page), as well as unsigned packages.
SigLevel = Required DatabaseOptional
LocalFileSigLevel = Optional
RemoteFileSigLevel = Required
# NOTE: You must run `pacman-key --init` before first using pacman; the local
# keyring can then be populated with the keys of all official Arch Linux
# packagers with `pacman-key --populate archlinux`.
#
# REPOSITORIES
# - can be defined here or included from another file
# - pacman will search repositories in the order defined here
# - local/custom mirrors can be added here or in separate files
# - repositories listed first will take precedence when packages
# have identical names, regardless of version number
# - URLs will have $repo replaced by the name of the current repo
# - URLs will have $arch replaced by the name of the architecture
#
# Repository entries are of the format:
# [repo-name]
# Server = ServerName
# Include = IncludePath
#
# The header [repo-name] is crucial - it must be present and
# uncommented to enable the repo.
#
# The testing repositories are disabled by default. To enable, uncomment the
# repo name header and Include lines. You can add preferred servers immediately
# after the header, and they will be used before the default mirrors.
#[core-testing]
#Include = /etc/pacman.d/mirrorlist
[core]
Server = Core
#[extra-testing]
#Include = /etc/pacman.d/mirrorlist
[extra]
Server = Extra
# If you want to run 32 bit applications on your x86_64 system,
# enable the multilib repositories as required here.
#[multilib-testing]
#Include = /etc/pacman.d/mirrorlist
[multilib]
Server = repo3
Server = multilib
# An example of a custom package repository. See the pacman manpage for
# tips on creating your own repositories.
#[custom]
#SigLevel = Optional TrustAll
#Server = file:///home/custompkgs
================================================
FILE: vcs.go
================================================
package main
import (
"context"
"sync"
"github.com/Jguer/aur"
"github.com/leonelquinteros/gotext"
"github.com/Jguer/yay/v12/pkg/db"
"github.com/Jguer/yay/v12/pkg/dep"
"github.com/Jguer/yay/v12/pkg/runtime"
"github.com/Jguer/yay/v12/pkg/sync/srcinfo"
"github.com/Jguer/yay/v12/pkg/sync/workdir"
)
func infoToInstallInfo(info []aur.Pkg) []map[string]*dep.InstallInfo {
installInfo := make([]map[string]*dep.InstallInfo, 1)
installInfo[0] = map[string]*dep.InstallInfo{}
for i := range info {
pkg := &info[i]
installInfo[0][pkg.Name] = &dep.InstallInfo{
AURBase: &pkg.PackageBase,
Source: dep.AUR,
}
}
return installInfo
}
// createDevelDB forces yay to create a DB of the existing development packages.
func createDevelDB(ctx context.Context, run *runtime.Runtime, dbExecutor db.Executor) error {
remoteNames := dbExecutor.InstalledRemotePackageNames()
run.QueryBuilder.Execute(ctx, dbExecutor, remoteNames)
info, err := run.AURClient.Get(ctx, &aur.Query{
Needles: remoteNames,
By: aur.Name,
Contains: false,
})
if err != nil {
return err
}
preper := workdir.NewPreparerWithoutHooks(dbExecutor, run.CmdBuilder, run.Cfg, run.Logger.Child("workdir"), false)
mapInfo := infoToInstallInfo(info)
pkgBuildDirsByBase, err := preper.Run(ctx, run, mapInfo)
if err != nil {
return err
}
srcinfos, err := srcinfo.ParseSrcinfoFilesByBase(run.Logger.Child("srcinfo"), pkgBuildDirsByBase, false)
if err != nil {
return err
}
var wg sync.WaitGroup
for i := range srcinfos {
for iP := range srcinfos[i].Packages {
wg.Add(1)
go func(baseIndex string, packageIndex int) {
run.VCSStore.Update(ctx, srcinfos[baseIndex].Packages[packageIndex].Pkgname, srcinfos[baseIndex].Source)
wg.Done()
}(i, iP)
}
}
wg.Wait()
run.Logger.OperationInfoln(gotext.Get("GenDB finished. No packages were installed"))
return nil
}
================================================
FILE: vote.go
================================================
package main
import (
"context"
"errors"
"github.com/Jguer/aur"
"github.com/Jguer/votar/pkg/vote"
"github.com/leonelquinteros/gotext"
"github.com/Jguer/yay/v12/pkg/text"
)
type ErrAURVote struct {
inner error
pkgName string
}
func (e *ErrAURVote) Error() string {
return gotext.Get("Unable to handle package vote for: %s. err: %s", e.pkgName, e.inner.Error())
}
func handlePackageVote(ctx context.Context,
targets []string, aurClient aur.QueryClient, logger *text.Logger,
voteClient *vote.Client, upvote bool,
) error {
infos, err := aurClient.Get(ctx, &aur.Query{
Needles: targets,
By: aur.Name,
})
if err != nil {
return err
}
if len(infos) == 0 {
logger.Println(gotext.Get(" there is nothing to do"))
return nil
}
for i := range infos {
var err error
if upvote {
err = voteClient.Vote(ctx, infos[i].PackageBase)
} else {
err = voteClient.Unvote(ctx, infos[i].PackageBase)
}
if err != nil {
if errors.Is(err, vote.ErrNoCredentials) {
return errors.New(
gotext.Get("%s: please set AUR_USERNAME and AUR_PASSWORD environment variables for voting",
err.Error()))
}
return &ErrAURVote{inner: err, pkgName: infos[i].Name}
}
}
return nil
}