Compare commits

..

No commits in common. "next" and "v12.1.0" have entirely different histories.

144 changed files with 8057 additions and 14188 deletions

View File

@ -1,26 +0,0 @@
# Use the jguer/yay-builder image as a parent image with archlinux
FROM docker.io/jguer/yay-builder
# Install extra packages (pacman-contrib and fish)
RUN sudo pacman -Syu --noconfirm pacman-contrib fish git-delta openssh bat go
# Set passwordless sudo for the docker user
RUN echo "docker ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/docker
# Create a non-root user and switch to it
USER docker
# Install xgotext
RUN go install github.com/leonelquinteros/gotext/cli/xgotext@latest
# Add /app/bin to the PATH
ENV PATH="/app/bin:$PATH"
# add /home/docker/go/bin to the PATH
ENV PATH="/home/docker/go/bin:$PATH"
# Set the working directory
WORKDIR /workspace
# Command to run when starting the container
CMD ["bash"]

View File

@ -1,14 +0,0 @@
{
"name": "Existing Dockerfile",
"build": {
"context": "..",
"dockerfile": "../.devcontainer/Dockerfile"
},
"customizations": {
"vscode": {
"extensions": [
"golang.go"
]
}
}
}

View File

@ -1,15 +0,0 @@
# 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:
- '*'

View File

@ -1,143 +1,40 @@
name: Builder Image name: Builder image
on: on:
schedule: schedule:
- cron: "0 3 * * 1" # Every Monday at 3 AM - cron: "0 3 * * 1"
push: push:
paths: paths:
- "ci.Dockerfile" - "ci.Dockerfile"
- ".github/workflows/builder-image.yml" - "**/builder-image.yml"
env:
REGISTRY_IMAGE: jguer/yay-builder
jobs: jobs:
build: build:
name: Push builder image to Docker Hub
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
platform:
- linux/amd64
- linux/arm/v7
- linux/arm64
steps: steps:
- name: Checkout repository - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v2
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v3 uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v1
- name: Login to DockerHub
- name: Login to Docker Hub uses: docker/login-action@v1
uses: docker/login-action@v3
with: with:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKER_PASSWORD }}
- name: Push to Docker Hub
- name: Login to GitHub Container Registry uses: docker/build-push-action@v2
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: |
${{ env.REGISTRY_IMAGE }}
ghcr.io/${{ env.REGISTRY_IMAGE }}
tags: |
type=raw,value=latest
type=sha,format=long
- name: Build and push by digest
id: build
uses: docker/build-push-action@v5
with:
context: .
file: ci.Dockerfile
platforms: ${{ matrix.platform }}
labels: ${{ steps.meta.outputs.labels }}
outputs: type=image,name=${{ env.REGISTRY_IMAGE }},push-by-digest=true,name-canonical=true,push=true
- name: Export digest
run: |
mkdir -p /tmp/digests
digest="${{ steps.build.outputs.digest }}"
echo -n "$digest" > "/tmp/digests/$(echo "${{ matrix.platform }}" | tr '/' '_')"
- name: Upload digest
uses: actions/upload-artifact@v4
with:
name: digest-${{ matrix.platform == 'linux/amd64' && 'amd64' || matrix.platform == 'linux/arm/v7' && 'armv7' || 'arm64' }}
path: /tmp/digests/*
if-no-files-found: error
retention-days: 1
merge:
needs: [build]
runs-on: ubuntu-latest
steps:
- name: Download digests
uses: actions/download-artifact@v4
with:
pattern: digest-*
merge-multiple: true
path: /tmp/digests
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: |
${{ env.REGISTRY_IMAGE }}
ghcr.io/${{ env.REGISTRY_IMAGE }}
tags: |
type=raw,value=latest
type=sha,format=short
- name: Create and push manifest list
env: env:
DOCKER_CLI_EXPERIMENTAL: enabled DOCKER_BUILDKIT: 0
run: | COMPOSE_DOCKER_CLI_BUILD: 0
# Extract Docker Hub tags with:
DH_TAGS=$(echo '${{ steps.meta.outputs.tags }}' | grep -v "^ghcr.io" | xargs -I {} echo "-t {}") platforms: linux/amd64,linux/arm/v7,linux/arm64
file: ci.Dockerfile
# Extract GitHub Container Registry tags push: true
GHCR_TAGS=$(echo '${{ steps.meta.outputs.tags }}' | grep "^ghcr.io" | xargs -I {} echo "-t {}") tags: jguer/yay-builder:latest
secrets: |
# Create a manifest list using the image digests from /tmp/digests/* DOCKER_BUILDKIT=0
DIGESTS=$(for file in /tmp/digests/*; do COMPOSE_DOCKER_CLI_BUILD=0
echo -n "${{ env.REGISTRY_IMAGE }}@$(cat $file) " cache-from: type=registry,ref=jguer/yay-builder:latest
done) cache-to: type=inline
# Create the manifest list for Docker Hub
docker buildx imagetools create $DH_TAGS $DIGESTS
# Create the manifest list for GitHub Container Registry
docker buildx imagetools create $GHCR_TAGS $DIGESTS
- name: Inspect image
run: |
docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:latest

View File

@ -1,5 +1,4 @@
name: Build Release name: Build Release
on: on:
push: push:
tags: tags:
@ -9,36 +8,31 @@ jobs:
build-releases: build-releases:
strategy: strategy:
matrix: matrix:
arch: ["linux/amd64 x86_64", "linux/arm/v7 armv7h", "linux/arm64 aarch64"] arch:
["linux/amd64 x86_64", "linux/arm/v7 armv7h", "linux/arm64 aarch64"]
name: Build ${{ matrix.arch }} name: Build ${{ matrix.arch }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v3 uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with: with:
username: ${{ secrets.DOCKERHUB_USERNAME }} platforms: all
password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v1
with:
version: latest
- name: Read info - name: Read info
id: tags id: tags
shell: bash
run: | run: |
echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\/v/}
echo "TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT echo ::set-output name=TAG::${GITHUB_REF/refs\/tags\//}
arch="${{ matrix.arch }}" arch="${{ matrix.arch }}"
echo "PLATFORM=${arch%% *}" >> $GITHUB_OUTPUT echo ::set-output name=PLATFORM::${arch%% *}
echo "ARCH=${arch##* }" >> $GITHUB_OUTPUT echo ::set-output name=ARCH::${arch##* }
- name: Build ${{ matrix.arch }} release - name: Build ${{ matrix.arch }} release
run: | run: |
mkdir artifacts mkdir artifacts
@ -49,44 +43,74 @@ jobs:
-t yay:${{ steps.tags.outputs.arch }} . --load -t yay:${{ steps.tags.outputs.arch }} . --load
make docker-release ARCH=${{ steps.tags.outputs.arch }} VERSION=${{ steps.tags.outputs.version }} PREFIX="/usr" make docker-release ARCH=${{ steps.tags.outputs.arch }} VERSION=${{ steps.tags.outputs.version }} PREFIX="/usr"
mv *.tar.gz artifacts mv *.tar.gz artifacts
- uses: actions/upload-artifact@v2
- uses: actions/upload-artifact@v4
with: with:
name: yay_${{ steps.tags.outputs.arch }} name: yay_${{ steps.tags.outputs.arch }}
path: artifacts path: artifacts
create_release: create_release:
name: Create release from this build name: Create release from this build
needs: [build-releases] needs: [build-releases]
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions:
contents: write
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v2
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Read info - name: Read info
id: tags id: tags
shell: bash
run: | run: |
echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\/v/}
echo "TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT echo ::set-output name=TAG::${GITHUB_REF/refs\/tags\//}
- uses: actions/download-artifact@v2
- uses: actions/download-artifact@v4
with: with:
pattern: yay_* name: yay_x86_64
merge-multiple: true - uses: actions/download-artifact@v2
with:
name: yay_armv7h
- uses: actions/download-artifact@v2
with:
name: yay_aarch64
- name: Create Release - name: Create Release
id: create_release
uses: actions/create-release@master
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: | with:
gh release create ${{ steps.tags.outputs.tag }} \ tag_name: ${{ steps.tags.outputs.tag }}
--title "${{ steps.tags.outputs.tag }}" \ release_name: ${{ steps.tags.outputs.tag }}
--generate-notes \ draft: false
./yay_${{ steps.tags.outputs.version }}_*.tar.gz prerelease: false
- name: Upload x86_64 asset
id: upload-release-asset-x86_64
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./yay_${{ steps.tags.outputs.version }}_x86_64.tar.gz
asset_name: yay_${{ steps.tags.outputs.version }}_x86_64.tar.gz
asset_content_type: application/tar+gzip
- name: Upload armv7h asset
id: upload-release-asset-armv7h
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./yay_${{ steps.tags.outputs.version }}_armv7h.tar.gz
asset_name: yay_${{ steps.tags.outputs.version }}_armv7h.tar.gz
asset_content_type: application/tar+gzip
- name: Upload aarch64 asset
id: upload-release-asset-aarch64
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./yay_${{ steps.tags.outputs.version }}_aarch64.tar.gz
asset_name: yay_${{ steps.tags.outputs.version }}_aarch64.tar.gz
asset_content_type: application/tar+gzip
- name: Release Notary Action - name: Release Notary Action
uses: docker://aevea/release-notary:latest uses: docker://aevea/release-notary:latest
env: env:

View File

@ -12,9 +12,9 @@ jobs:
name: Lint and test yay (-git) name: Lint and test yay (-git)
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: container:
image: ghcr.io/jguer/yay-builder:latest image: jguer/yay-builder:latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
- uses: actions/cache@v3 - uses: actions/cache@v3
with: with:
path: ~/go/pkg/mod path: ~/go/pkg/mod
@ -35,5 +35,4 @@ jobs:
chmod -R 777 pacman-git chmod -R 777 pacman-git
su github -c 'cd pacman-git; yes | makepkg -i --nocheck' su github -c 'cd pacman-git; yes | makepkg -i --nocheck'
- name: Run Build and Tests with pacman-git - name: Run Build and Tests with pacman-git
run: | run: make test
make test

View File

@ -7,9 +7,9 @@ jobs:
name: Lint and test yay name: Lint and test yay
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: container:
image: ghcr.io/jguer/yay-builder:latest image: jguer/yay-builder:latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
- uses: actions/cache@v3 - uses: actions/cache@v3
with: with:
path: ~/go/pkg/mod path: ~/go/pkg/mod
@ -17,12 +17,9 @@ jobs:
restore-keys: | restore-keys: |
${{ runner.os }}-go- ${{ runner.os }}-go-
- name: Lint - name: Lint
env: run: /app/bin/golangci-lint run ./...
GOFLAGS: -buildvcs=false -tags=next
run: /app/bin/golangci-lint run -v ./...
- name: Run Build and Tests - name: Run Build and Tests
run: make test run: make test
- name: Run Integration Tests - name: Run Integration Tests
continue-on-error: true continue-on-error: true
run: | run: |
@ -30,15 +27,4 @@ jobs:
chown -R yay:yay . && chown -R yay:yay . &&
cp -r ~/go/ /home/yay/go/ && cp -r ~/go/ /home/yay/go/ &&
chown -R yay:yay /home/yay/go/ && chown -R yay:yay /home/yay/go/ &&
su yay -c "make test-integration" 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

4
.gitignore vendored
View File

@ -28,7 +28,3 @@ qemu-*
*.pot *.pot
*.po~ *.po~
*.pprof *.pprof
node_modules/
xgotext
.devcontainer/

View File

@ -1,18 +1,70 @@
version: "2" linters-settings:
run: dupl:
go: "1.20" 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
goimports:
local-prefixes: github.com/Jguer/yay/v12
gomnd:
checks:
- argument
- case
- condition
- return
ignored-numbers:
- "0"
- "1"
- "2"
- "3"
ignored-functions:
- strings.SplitN
govet:
check-shadowing: true
lll:
line-length: 140
misspell:
locale: US
nolintlint:
allow-unused: false # report any unused nolint directives
require-explanation: false # don't require an explanation for nolint directives
require-specific: false # don't require nolint directives to be specific about which linter is being skipped
linters: linters:
default: none # please, do not use `enable-all`: it's deprecated and will be removed soon.
# inverted configuration with `enable-all` and `disable` is not scalable during updates of golangci-lint
disable-all: true
enable: enable:
- bodyclose - bodyclose
- depguard
- dogsled - dogsled
- dupl - dupl
- errcheck
- errorlint - errorlint
- errcheck
- exportloopref
# - funlen # TOFIX
- gochecknoinits - gochecknoinits
# - goconst # TOFIX
- gocritic - gocritic
# - gocyclo # TOFIX
- gofmt
- goimports
# - gomnd # TOFIX
- goprintffuncname - goprintffuncname
- gosec - gosec
- gosimple
- govet - govet
- ineffassign - ineffassign
- lll - lll
@ -21,74 +73,32 @@ linters:
- noctx - noctx
- nolintlint - nolintlint
- staticcheck - staticcheck
- stylecheck
- typecheck
- unconvert - unconvert
- unparam - unparam
- unused - unused
- whitespace - whitespace
settings:
dupl: run:
threshold: 100 go: "1.18"
funlen: timeout: "10m"
lines: 100
statements: 50 issues:
goconst: exclude-rules:
min-len: 3 - path: (.+)_test.go
min-occurrences: 4 linters:
gocritic: - lll
enabled-tags: - revive
- diagnostic - wsl
- experimental - govet
- opinionated - godot
- performance - errcheck
- style - stylecheck
gocyclo: - dupl
min-complexity: 15 - gocritic
lll: - gochecknoinits
line-length: 140 - errorlint
misspell:
locale: US exclude:
nolintlint: - G204
require-explanation: false
require-specific: false
allow-unused: false
exclusions:
generated: lax
presets:
- comments
- common-false-positives
- legacy
- std-error-handling
rules:
- linters:
- dupl
- errcheck
- errorlint
- gochecknoinits
- gocritic
- godot
- govet
- lll
- revive
- staticcheck
- wsl
path: (.+)_test.go
- path: (.+)\.go$
text: G204
paths:
- third_party$
- builtin$
- examples$
formatters:
enable:
- gofmt
- goimports
settings:
goimports:
local-prefixes:
- github.com/Jguer/yay/v12
exclusions:
generated: lax
paths:
- third_party$
- builtin$
- examples$

View File

@ -5,17 +5,19 @@ repos:
rev: v0.5.1 rev: v0.5.1
hooks: hooks:
- id: go-fmt - id: go-fmt
- id: go-imports
args: [-local=github.com/Jguer/yay/v12/]
- id: golangci-lint - id: golangci-lint
- id: go-unit-tests - id: go-unit-tests
- id: go-build - id: go-build
- repo: https://github.com/pre-commit/mirrors-prettier - repo: https://github.com/pre-commit/mirrors-prettier
rev: v4.0.0-alpha.8 # Use the sha or tag you want to point at rev: v3.0.0-alpha.4 # Use the sha or tag you want to point at
hooks: hooks:
- id: prettier - id: prettier
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0 # Use the ref you want to point at rev: v4.4.0 # Use the ref you want to point at
hooks: hooks:
- id: trailing-whitespace - id: trailing-whitespace
- id: check-json - id: check-json
@ -23,7 +25,7 @@ repos:
- id: check-added-large-files - id: check-added-large-files
- repo: https://github.com/commitizen-tools/commitizen - repo: https://github.com/commitizen-tools/commitizen
rev: v3.15.0 rev: v2.38.0
hooks: hooks:
- id: commitizen - id: commitizen
stages: [commit-msg] stages: [commit-msg]

View File

@ -1,7 +0,0 @@
{
"go.lintTool": "golangci-lint",
"gopls": {
"formatting.gofumpt": true,
"formatting.local": "github.com/Jguer/yay/v12"
}
}

View File

@ -1,4 +1,4 @@
FROM ghcr.io/jguer/yay-builder:latest FROM docker.io/jguer/yay-builder:latest
LABEL maintainer="Jguer,docker@jguer.space" LABEL maintainer="Jguer,docker@jguer.space"
ARG VERSION ARG VERSION

View File

@ -26,7 +26,8 @@ MOFILES := $(POFILES:.po=.mo)
FLAGS ?= -trimpath -mod=readonly -modcacherw FLAGS ?= -trimpath -mod=readonly -modcacherw
EXTRA_FLAGS ?= -buildmode=pie EXTRA_FLAGS ?= -buildmode=pie
LDFLAGS := -X "main.yayVersion=${VERSION}" -X "main.localePath=${SYSTEMLOCALEPATH}" -linkmode=external -compressdwarf=false LDFLAGS := -X "main.yayVersion=${VERSION}" -X "main.localePath=${SYSTEMLOCALEPATH}" -linkmode=external
FLAGS += $(shell pacman -T 'pacman-git' >/dev/null 2>&1 && echo "-tags next")
RELEASE_DIR := ${PKGNAME}_${VERSION}_${ARCH} RELEASE_DIR := ${PKGNAME}_${VERSION}_${ARCH}
PACKAGE := $(RELEASE_DIR).tar.gz PACKAGE := $(RELEASE_DIR).tar.gz
@ -69,7 +70,7 @@ docker-release-all:
make docker-release-aarch64 ARCH=aarch64 make docker-release-aarch64 ARCH=aarch64
docker-release: docker-release:
docker create --name yay-$(ARCH) yay:${ARCH} /bin/sh docker create --name yay-$(ARCH) yay:${ARCH}
docker cp yay-$(ARCH):/app/${PACKAGE} $(PACKAGE) docker cp yay-$(ARCH):/app/${PACKAGE} $(PACKAGE)
docker container rm yay-$(ARCH) docker container rm yay-$(ARCH)
@ -82,7 +83,9 @@ docker-build:
.PHONY: lint .PHONY: lint
lint: lint:
GOFLAGS="$(FLAGS)" golangci-lint run ./... $(GO) vet $(FLAGS) ./...
@test -z "$$(gofmt -l $(SOURCES))" || (echo "Files need to be linted. Use make fmt" && false)
golangci-lint run ./...
.PHONY: fmt .PHONY: fmt
fmt: fmt:
@ -123,9 +126,8 @@ $(PACKAGE): $(BIN) $(RELEASE_DIR) ${MOFILES}
locale: locale:
xgotext -in . -out po xgotext -in . -out po
mv po/default.pot po/en.po
for lang in ${LANGS}; do \ for lang in ${LANGS}; do \
test -f po/$$lang.po || msginit --no-translator -l po/$$lang.po -i po/${POTFILE} -o po/$$lang.po; \ test -f po/$$lang.po || msginit -l po/$$lang.po -i po/${POTFILE} -o po/$$lang.po \
msgmerge -U po/$$lang.po po/${POTFILE}; \ msgmerge -U po/$$lang.po po/${POTFILE}; \
touch po/$$lang.po; \ touch po/$$lang.po; \
done done

View File

@ -30,18 +30,15 @@ Yet Another Yogurt - An AUR Helper Written in Go
If you are migrating from another AUR helper, you can simply install Yay with that helper. 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 ### Source
The initial installation of Yay can be done by cloning the PKGBUILD and The initial installation of Yay can be done by cloning the PKGBUILD and
building with makepkg: building with makepkg:
We make sure we have the `base-devel` package group installed. Before you begin, make sure you have the `base-devel` package group installed.
```sh ```sh
sudo pacman -S --needed git base-devel pacman -S --needed git base-devel
git clone https://aur.archlinux.org/yay.git git clone https://aur.archlinux.org/yay.git
cd yay cd yay
makepkg -si makepkg -si
@ -50,7 +47,7 @@ makepkg -si
If you want to do all of this at once, we can chain the commands like so: If you want to do all of this at once, we can chain the commands like so:
```sh ```sh
sudo pacman -S --needed git base-devel && git clone https://aur.archlinux.org/yay.git && cd yay && makepkg -si pacman -S --needed git base-devel && git clone https://aur.archlinux.org/yay.git && cd yay && makepkg -si
``` ```
### Binary ### Binary
@ -59,18 +56,12 @@ If you do not want to compile yay yourself you can use the builds generated by
GitHub Actions. GitHub Actions.
```sh ```sh
sudo pacman -S --needed git base-devel pacman -S --needed git base-devel
git clone https://aur.archlinux.org/yay-bin.git git clone https://aur.archlinux.org/yay-bin.git
cd yay-bin cd yay-bin
makepkg -si 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 ### Other distributions
If you're using Manjaro or [another distribution that packages `yay`](https://repology.org/project/yay/versions) If you're using Manjaro or [another distribution that packages `yay`](https://repology.org/project/yay/versions)
@ -79,8 +70,8 @@ you can simply install yay using pacman (as root):
```sh ```sh
pacman -S --needed git base-devel yay pacman -S --needed git base-devel yay
``` ```
> [!WARNING]
> distributions sometimes lag updating yay on their repositories. ⚠️ distributions sometimes lag updating yay on their repositories.
## First Use ## First Use
@ -120,6 +111,17 @@ pacman -S --needed git base-devel yay
Make sure you have the `Color` option in your `/etc/pacman.conf` Make sure you have the `Color` option in your `/etc/pacman.conf`
(see issue [#123](https://github.com/Jguer/yay/issues/123)). (see issue [#123](https://github.com/Jguer/yay/issues/123)).
- **Yay is not prompting to skip packages during system upgrade.**
The default behavior was changed after
[v8.918](https://github.com/Jguer/yay/releases/tag/v8.918)
(see [3bdb534](https://github.com/Jguer/yay/commit/3bdb5343218d99d40f8a449b887348611f6bdbfc)
and issue [#554](https://github.com/Jguer/yay/issues/554)).
To restore the package-skip behavior use `--combinedupgrade` (make
it permanent by appending `--save`). Note: skipping packages will leave your
system in a
[partially-upgraded state](https://wiki.archlinux.org/index.php/System_maintenance#Partial_upgrades_are_unsupported).
- **Sometimes diffs are printed to the terminal, and other times they are paged via less. How do I fix this?** - **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 Yay uses `git diff` to display diffs, which by default tells less not to
@ -128,14 +130,14 @@ pacman -S --needed git base-devel yay
- **Yay is not asking me to edit PKGBUILDS, and I don't like the diff menu! What can I do?** - **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` `yay --editmenu --nodiffmenu --save`
- **How can I tell Yay to act only on AUR packages, or only on repo packages?** - **How can I tell Yay to act only on AUR packages, or only on repo packages?**
`yay -{OPERATION} --aur` `yay -{OPERATION} --aur`
`yay -{OPERATION} --repo` `yay -{OPERATION} --repo`
- **A `Flagged Out Of Date AUR Packages` message is displayed. Why doesn't Yay update them?** - **An `Out Of Date AUR Packages` message is displayed. Why doesn't Yay update them?**
This message does not mean that updated AUR packages are available. It means 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 the packages have been flagged out of date on the AUR, but
@ -150,13 +152,28 @@ pacman -S --needed git base-devel yay
- **I know my `-git` package has updates but yay doesn't offer to update it** - **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`. Yay uses an hash cache for development packages. Normally it is updated at the end of the package install with the message `Found git repo`.
If you transition between aur helpers and did not install the devel package using yay at some point, it is possible it never got added to the cache. `yay -Y --gendb` will fix the current version of every devel package and start checking from there. 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!** - **I want to help out!**
Check [CONTRIBUTING.md](./CONTRIBUTING.md) for more information. Check [CONTRIBUTING.md](./CONTRIBUTING.md) for more information.
- **What settings do you use?**
```sh
yay -Y --devel --combinedupgrade --batchinstall --save
```
Pacman conf options:
```conf
UseSyslog
Color
CheckSpace
VerbosePkgLists
```
## Support ## Support
All support related to Yay should be requested via GitHub issues. Since Yay is not All support related to Yay should be requested via GitHub issues. Since Yay is not
@ -172,14 +189,14 @@ tools.
## Images ## Images
<p align="center"> <p float="left">
<img src="https://raw.githubusercontent.com/Jguer/jguer.github.io/refs/heads/master/yay/yay.png" width="42%"> <img src="https://rawcdn.githack.com/Jguer/jguer.github.io/77647f396cb7156fd32e30970dbeaf6d6dc7f983/yay/yay.png" width="42%"/>
<img src="https://raw.githubusercontent.com/Jguer/jguer.github.io/refs/heads/master/yay/yay-s.png" width="42%"> <img src="https://rawcdn.githack.com/Jguer/jguer.github.io/77647f396cb7156fd32e30970dbeaf6d6dc7f983/yay/yay-s.png" width="42%"/>
</p> </p>
<p align="center"> <p float="left">
<img src="https://raw.githubusercontent.com/Jguer/jguer.github.io/refs/heads/master/yay/yay-y.png" width="42%"> <img src="https://rawcdn.githack.com/Jguer/jguer.github.io/77647f396cb7156fd32e30970dbeaf6d6dc7f983/yay/yay-y.png" width="42%"/>
<img src="https://raw.githubusercontent.com/Jguer/jguer.github.io/refs/heads/master/yay/yay-ps.png" width="42%"> <img src="https://rawcdn.githack.com/Jguer/jguer.github.io/77647f396cb7156fd32e30970dbeaf6d6dc7f983/yay/yay-ps.png" width="42%"/>
</p> </p>
### Other AUR helpers/tools ### Other AUR helpers/tools

View File

@ -1,13 +0,0 @@
# Security Policy
Thank you for helping keep yay secure!
## Supported Versions
We only provide security updates and support for the latest released version of yay. Please ensure you are using the most up-to-date version before reporting vulnerabilities.
## Reporting a Vulnerability
If you discover a security vulnerability, please email us at [security@jguer.space](mailto:security@jguer.space). We will respond as quickly as possible and coordinate a fix.
We appreciate responsible disclosure and your help in making this project safe for everyone.

View File

@ -1,4 +1,4 @@
package build package main
import ( import (
"context" "context"
@ -54,12 +54,12 @@ func NewInstaller(dbExecutor db.Executor,
} }
} }
func (installer *Installer) CompileFailedAndIgnored() (map[string]error, error) { func (installer *Installer) CompileFailedAndIgnored() error {
if len(installer.failedAndIgnored) == 0 { if len(installer.failedAndIgnored) == 0 {
return installer.failedAndIgnored, nil return nil
} }
return installer.failedAndIgnored, &FailedIgnoredPkgError{ return &FailedIgnoredPkgError{
pkgErrors: installer.failedAndIgnored, pkgErrors: installer.failedAndIgnored,
} }
} }
@ -145,20 +145,16 @@ func (installer *Installer) handleLayer(ctx context.Context,
excluded []string, excluded []string,
) error { ) error {
// Install layer // Install layer
nameToBaseMap := make(map[string]string, len(layer)) nameToBaseMap := make(map[string]string, 0)
syncDeps, syncExp, syncGroups := mapset.NewThreadUnsafeSet[string](), syncDeps, syncExp, syncGroups := mapset.NewThreadUnsafeSet[string](),
mapset.NewThreadUnsafeSet[string](), mapset.NewThreadUnsafeSet[string]() mapset.NewThreadUnsafeSet[string](), mapset.NewThreadUnsafeSet[string]()
aurDeps, aurExp, aurOrigTargetBases := mapset.NewThreadUnsafeSet[string](), aurDeps, aurExp := mapset.NewThreadUnsafeSet[string](), mapset.NewThreadUnsafeSet[string]()
mapset.NewThreadUnsafeSet[string](), mapset.NewThreadUnsafeSet[string]()
upgradeSync := false upgradeSync := false
for name, info := range layer { for name, info := range layer {
switch info.Source { switch info.Source {
case dep.AUR, dep.SrcInfo: case dep.AUR, dep.SrcInfo:
nameToBaseMap[name] = *info.AURBase nameToBaseMap[name] = *info.AURBase
if installer.origTargets.Contains(name) {
aurOrigTargetBases.Add(*info.AURBase)
}
switch info.Reason { switch info.Reason {
case dep.Explicit: case dep.Explicit:
@ -205,15 +201,14 @@ func (installer *Installer) handleLayer(ctx context.Context,
} }
errAur := installer.installAURPackages(ctx, cmdArgs, aurDeps, aurExp, errAur := installer.installAURPackages(ctx, cmdArgs, aurDeps, aurExp,
aurOrigTargetBases, nameToBaseMap, pkgBuildDirs, true, lastLayer, nameToBaseMap, pkgBuildDirs, true, lastLayer, installer.appendNoConfirm())
installer.appendNoConfirm())
return errAur return errAur
} }
func (installer *Installer) installAURPackages(ctx context.Context, func (installer *Installer) installAURPackages(ctx context.Context,
cmdArgs *parser.Arguments, cmdArgs *parser.Arguments,
aurDepNames, aurExpNames, aurOrigTargetBases mapset.Set[string], aurDepNames, aurExpNames mapset.Set[string],
nameToBase, pkgBuildDirsByBase map[string]string, nameToBase, pkgBuildDirsByBase map[string]string,
installIncompatible bool, installIncompatible bool,
lastLayer bool, lastLayer bool,
@ -224,37 +219,27 @@ func (installer *Installer) installAURPackages(ctx context.Context,
return nil return nil
} }
builtPkgDests := make(map[string]map[string]string) deps, exps := make([]string, 0, aurDepNames.Cardinality()), make([]string, 0, aurExpNames.Cardinality())
deps := make([]string, 0, aurDepNames.Cardinality()) pkgArchives := make([]string, 0, len(exps)+len(deps))
exps := make([]string, 0, aurExpNames.Cardinality())
pkgArchives := make([]string, 0, len(all))
for _, name := range all { for _, name := range all {
base := nameToBase[name] base := nameToBase[name]
dir := pkgBuildDirsByBase[base] dir := pkgBuildDirsByBase[base]
pkgdests, ok := builtPkgDests[base] pkgdests, errMake := installer.buildPkg(ctx, dir, base,
if ok { installIncompatible, cmdArgs.ExistsArg("needed"), installer.origTargets.Contains(name))
installer.log.Debugln("skipping built pkgbase", base, "package", name) if errMake != nil {
} else { if !lastLayer {
var errMake error return fmt.Errorf("%s - %w", gotext.Get("error making: %s", base), errMake)
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
} }
installer.failedAndIgnored[name] = errMake
text.Errorln(gotext.Get("error making: %s", base), "-", errMake)
continue
} }
if len(pkgdests) == 0 { if len(pkgdests) == 0 {
installer.log.Warnln(gotext.Get("nothing to install for %s", text.Cyan(base))) text.Warnln(gotext.Get("nothing to install for %s", text.Cyan(base)))
continue continue
} }
@ -292,11 +277,7 @@ func (installer *Installer) buildPkg(ctx context.Context,
dir, base string, dir, base string,
installIncompatible, needed, isTarget bool, installIncompatible, needed, isTarget bool,
) (map[string]string, error) { ) (map[string]string, error) {
args := []string{"--nobuild", "-f"} args := []string{"--nobuild", "-fC"}
if !installer.exeCmd.GetKeepSrc() {
args = append(args, "-C")
}
if installIncompatible { if installIncompatible {
args = append(args, "--ignorearch") args = append(args, "--ignorearch")
@ -315,23 +296,19 @@ func (installer *Installer) buildPkg(ctx context.Context,
switch { switch {
case needed && installer.pkgsAreAlreadyInstalled(pkgdests, pkgVersion) || installer.downloadOnly: case needed && installer.pkgsAreAlreadyInstalled(pkgdests, pkgVersion) || installer.downloadOnly:
args = []string{"--nobuild", "--noextract", "--ignorearch"} args = []string{"-c", "--nobuild", "--noextract", "--ignorearch"}
pkgdests = map[string]string{} pkgdests = map[string]string{}
installer.log.Warnln(gotext.Get("%s is up to date -- skipping", text.Cyan(base+"-"+pkgVersion))) text.Warnln(gotext.Get("%s is up to date -- skipping", text.Cyan(base+"-"+pkgVersion)))
case installer.skipAlreadyBuiltPkg(isTarget, pkgdests): case installer.skipAlreadyBuiltPkg(isTarget, pkgdests):
args = []string{"--nobuild", "--noextract", "--ignorearch"} args = []string{"-c", "--nobuild", "--noextract", "--ignorearch"}
installer.log.Warnln(gotext.Get("%s already made -- skipping build", text.Cyan(base+"-"+pkgVersion))) text.Warnln(gotext.Get("%s already made -- skipping build", text.Cyan(base+"-"+pkgVersion)))
default: default:
args = []string{"-f", "--noconfirm", "--noextract", "--noprepare", "--holdver"} args = []string{"-cf", "--noconfirm", "--noextract", "--noprepare", "--holdver"}
if installIncompatible { if installIncompatible {
args = append(args, "--ignorearch") args = append(args, "--ignorearch")
} }
} }
if !installer.exeCmd.GetKeepSrc() {
args = append(args, "-c")
}
errMake := installer.exeCmd.Show( errMake := installer.exeCmd.Show(
installer.exeCmd.BuildMakepkgCmd(ctx, installer.exeCmd.BuildMakepkgCmd(ctx,
dir, args...)) dir, args...))
@ -356,10 +333,10 @@ func (installer *Installer) pkgsAreAlreadyInstalled(pkgdests map[string]string,
return true return true
} }
func pkgsAreBuilt(logger *text.Logger, pkgdests map[string]string) bool { func pkgsAreBuilt(pkgdests map[string]string) bool {
for _, pkgdest := range pkgdests { for _, pkgdest := range pkgdests {
if _, err := os.Stat(pkgdest); err != nil { if _, err := os.Stat(pkgdest); err != nil {
logger.Debugln("pkgIsBuilt:", pkgdest, "does not exist") text.Debugln("pkgIsBuilt:", pkgdest, "does not exist")
return false return false
} }
} }
@ -370,14 +347,14 @@ func pkgsAreBuilt(logger *text.Logger, pkgdests map[string]string) bool {
func (installer *Installer) skipAlreadyBuiltPkg(isTarget bool, pkgdests map[string]string) bool { func (installer *Installer) skipAlreadyBuiltPkg(isTarget bool, pkgdests map[string]string) bool {
switch installer.rebuildMode { switch installer.rebuildMode {
case parser.RebuildModeNo: case parser.RebuildModeNo:
return pkgsAreBuilt(installer.log, pkgdests) return pkgsAreBuilt(pkgdests)
case parser.RebuildModeYes: case parser.RebuildModeYes:
return !isTarget && pkgsAreBuilt(installer.log, pkgdests) return !isTarget && pkgsAreBuilt(pkgdests)
// case parser.RebuildModeTree: // TODO // case parser.RebuildModeTree: // TODO
// case parser.RebuildModeAll: // TODO // case parser.RebuildModeAll: // TODO
default: default:
// same as RebuildModeNo // same as RebuildModeNo
return pkgsAreBuilt(installer.log, pkgdests) return pkgsAreBuilt(pkgdests)
} }
} }
@ -443,12 +420,6 @@ func (installer *Installer) installSyncPackages(ctx context.Context, cmdArgs *pa
arguments.Op = "S" arguments.Op = "S"
arguments.ClearTargets() arguments.ClearTargets()
arguments.AddTarget(repoTargets...) 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 { if len(excluded) > 0 {
arguments.CreateOrAppendOption("ignore", excluded...) arguments.CreateOrAppendOption("ignore", excluded...)
} }

View File

@ -1,4 +1,4 @@
package build package main
import ( import (
"context" "context"
@ -21,7 +21,7 @@ import (
"github.com/Jguer/yay/v12/pkg/vcs" "github.com/Jguer/yay/v12/pkg/vcs"
) )
func newTestLogger() *text.Logger { func NewTestLogger() *text.Logger {
return text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), true, "test") return text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), true, "test")
} }
@ -56,8 +56,8 @@ func TestInstaller_InstallNeeded(t *testing.T) {
isInstalled: false, isInstalled: false,
isBuilt: false, isBuilt: false,
wantShow: []string{ wantShow: []string{
"makepkg --nobuild -f -C --ignorearch", "makepkg --nobuild -fC --ignorearch",
"makepkg -f -c --noconfirm --noextract --noprepare --holdver --ignorearch", "makepkg -cf --noconfirm --noextract --noprepare --holdver --ignorearch",
"pacman -U --needed --config -- /testdir/yay-91.0.0-1-x86_64.pkg.tar.zst", "pacman -U --needed --config -- /testdir/yay-91.0.0-1-x86_64.pkg.tar.zst",
"pacman -D -q --asexplicit --config -- yay", "pacman -D -q --asexplicit --config -- yay",
}, },
@ -68,7 +68,7 @@ func TestInstaller_InstallNeeded(t *testing.T) {
isInstalled: false, isInstalled: false,
isBuilt: true, isBuilt: true,
wantShow: []string{ wantShow: []string{
"makepkg --nobuild -f -C --ignorearch", "makepkg --nobuild -fC --ignorearch",
"makepkg -c --nobuild --noextract --ignorearch", "makepkg -c --nobuild --noextract --ignorearch",
"pacman -U --needed --config -- /testdir/yay-91.0.0-1-x86_64.pkg.tar.zst", "pacman -U --needed --config -- /testdir/yay-91.0.0-1-x86_64.pkg.tar.zst",
"pacman -D -q --asexplicit --config -- yay", "pacman -D -q --asexplicit --config -- yay",
@ -80,7 +80,7 @@ func TestInstaller_InstallNeeded(t *testing.T) {
isInstalled: true, isInstalled: true,
isBuilt: false, isBuilt: false,
wantShow: []string{ wantShow: []string{
"makepkg --nobuild -f -C --ignorearch", "makepkg --nobuild -fC --ignorearch",
"makepkg -c --nobuild --noextract --ignorearch", "makepkg -c --nobuild --noextract --ignorearch",
}, },
wantCapture: []string{"makepkg --packagelist"}, wantCapture: []string{"makepkg --packagelist"},
@ -134,7 +134,7 @@ func TestInstaller_InstallNeeded(t *testing.T) {
cmdBuilder.Runner = mockRunner cmdBuilder.Runner = mockRunner
installer := NewInstaller(mockDB, cmdBuilder, &vcs.Mock{}, parser.ModeAny, installer := NewInstaller(mockDB, cmdBuilder, &vcs.Mock{}, parser.ModeAny,
parser.RebuildModeNo, false, newTestLogger()) parser.RebuildModeNo, false, NewTestLogger())
cmdArgs := parser.MakeArguments() cmdArgs := parser.MakeArguments()
cmdArgs.AddArg("needed") cmdArgs.AddArg("needed")
@ -212,8 +212,8 @@ func TestInstaller_InstallMixedSourcesAndLayers(t *testing.T) {
wantShow: []string{ wantShow: []string{
"pacman -S --config /etc/pacman.conf -- core/linux", "pacman -S --config /etc/pacman.conf -- core/linux",
"pacman -D -q --asdeps --config /etc/pacman.conf -- linux", "pacman -D -q --asdeps --config /etc/pacman.conf -- linux",
"makepkg --nobuild -f -C --ignorearch", "makepkg --nobuild -fC --ignorearch",
"makepkg -f -c --noconfirm --noextract --noprepare --holdver --ignorearch", "makepkg -cf --noconfirm --noextract --noprepare --holdver --ignorearch",
"pacman -U --config /etc/pacman.conf -- /testdir/yay-91.0.0-1-x86_64.pkg.tar.zst", "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", "pacman -D -q --asexplicit --config /etc/pacman.conf -- yay",
}, },
@ -241,8 +241,8 @@ func TestInstaller_InstallMixedSourcesAndLayers(t *testing.T) {
wantShow: []string{ wantShow: []string{
"pacman -S --config /etc/pacman.conf -- core/linux", "pacman -S --config /etc/pacman.conf -- core/linux",
"pacman -D -q --asdeps --config /etc/pacman.conf -- linux", "pacman -D -q --asdeps --config /etc/pacman.conf -- linux",
"makepkg --nobuild -f -C --ignorearch", "makepkg --nobuild -fC --ignorearch",
"makepkg -f -c --noconfirm --noextract --noprepare --holdver --ignorearch", "makepkg -cf --noconfirm --noextract --noprepare --holdver --ignorearch",
"pacman -U --config /etc/pacman.conf -- /testdir/yay-91.0.0-1-x86_64.pkg.tar.zst", "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", "pacman -D -q --asexplicit --config /etc/pacman.conf -- yay",
}, },
@ -293,10 +293,10 @@ func TestInstaller_InstallMixedSourcesAndLayers(t *testing.T) {
{ {
desc: "same layer -- aur", desc: "same layer -- aur",
wantShow: []string{ wantShow: []string{
"makepkg --nobuild -f -C --ignorearch", "makepkg --nobuild -fC --ignorearch",
"makepkg -f -c --noconfirm --noextract --noprepare --holdver --ignorearch", "makepkg -cf --noconfirm --noextract --noprepare --holdver --ignorearch",
"makepkg --nobuild -f -C --ignorearch", "makepkg --nobuild -fC --ignorearch",
"makepkg -f -c --noconfirm --noextract --noprepare --holdver --ignorearch", "makepkg -cf --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 -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", "pacman -D -q --asexplicit --config /etc/pacman.conf -- yay",
}, },
@ -323,12 +323,12 @@ func TestInstaller_InstallMixedSourcesAndLayers(t *testing.T) {
{ {
desc: "different layer -- aur", desc: "different layer -- aur",
wantShow: []string{ wantShow: []string{
"makepkg --nobuild -f -C --ignorearch", "makepkg --nobuild -fC --ignorearch",
"makepkg -f -c --noconfirm --noextract --noprepare --holdver --ignorearch", "makepkg -cf --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 -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", "pacman -D -q --asdeps --config /etc/pacman.conf -- jellyfin-server",
"makepkg --nobuild -f -C --ignorearch", "makepkg --nobuild -fC --ignorearch",
"makepkg -f -c --noconfirm --noextract --noprepare --holdver --ignorearch", "makepkg -cf --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 -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", "pacman -D -q --asexplicit --config /etc/pacman.conf -- yay",
}, },
@ -374,13 +374,13 @@ func TestInstaller_InstallMixedSourcesAndLayers(t *testing.T) {
} }
showOverride := func(cmd *exec.Cmd) error { showOverride := func(cmd *exec.Cmd) error {
if strings.Contains(cmd.String(), "makepkg -f --noconfirm") && cmd.Dir == tmpDir { if strings.Contains(cmd.String(), "makepkg -cf --noconfirm") && cmd.Dir == tmpDir {
f, err := os.OpenFile(pkgTar, os.O_RDONLY|os.O_CREATE, 0o666) f, err := os.OpenFile(pkgTar, os.O_RDONLY|os.O_CREATE, 0o666)
require.NoError(td, err) require.NoError(td, err)
require.NoError(td, f.Close()) require.NoError(td, f.Close())
} }
if strings.Contains(cmd.String(), "makepkg -f --noconfirm") && cmd.Dir == tmpDirJfin { if strings.Contains(cmd.String(), "makepkg -cf --noconfirm") && cmd.Dir == tmpDirJfin {
f, err := os.OpenFile(jfinPkgTar, os.O_RDONLY|os.O_CREATE, 0o666) f, err := os.OpenFile(jfinPkgTar, os.O_RDONLY|os.O_CREATE, 0o666)
require.NoError(td, err) require.NoError(td, err)
require.NoError(td, f.Close()) require.NoError(td, f.Close())
@ -408,7 +408,7 @@ func TestInstaller_InstallMixedSourcesAndLayers(t *testing.T) {
cmdBuilder.Runner = mockRunner cmdBuilder.Runner = mockRunner
installer := NewInstaller(mockDB, cmdBuilder, &vcs.Mock{}, parser.ModeAny, parser.RebuildModeNo, false, newTestLogger()) installer := NewInstaller(mockDB, cmdBuilder, &vcs.Mock{}, parser.ModeAny, parser.RebuildModeNo, false, NewTestLogger())
cmdArgs := parser.MakeArguments() cmdArgs := parser.MakeArguments()
cmdArgs.AddTarget("yay") cmdArgs.AddTarget("yay")
@ -462,7 +462,7 @@ func TestInstaller_RunPostHooks(t *testing.T) {
cmdBuilder.Runner = mockRunner cmdBuilder.Runner = mockRunner
installer := NewInstaller(mockDB, cmdBuilder, &vcs.Mock{}, parser.ModeAny, installer := NewInstaller(mockDB, cmdBuilder, &vcs.Mock{}, parser.ModeAny,
parser.RebuildModeNo, false, newTestLogger()) parser.RebuildModeNo, false, NewTestLogger())
called := false called := false
hook := func(ctx context.Context) error { hook := func(ctx context.Context) error {
@ -570,7 +570,7 @@ func TestInstaller_CompileFailed(t *testing.T) {
} }
showOverride := func(cmd *exec.Cmd) error { showOverride := func(cmd *exec.Cmd) error {
if tc.failBuild && strings.Contains(cmd.String(), "makepkg -f --noconfirm") && cmd.Dir == tmpDir { if tc.failBuild && strings.Contains(cmd.String(), "makepkg -cf --noconfirm") && cmd.Dir == tmpDir {
return errors.New("makepkg failed") return errors.New("makepkg failed")
} }
return nil return nil
@ -593,7 +593,7 @@ func TestInstaller_CompileFailed(t *testing.T) {
cmdBuilder.Runner = mockRunner cmdBuilder.Runner = mockRunner
installer := NewInstaller(mockDB, cmdBuilder, &vcs.Mock{}, parser.ModeAny, installer := NewInstaller(mockDB, cmdBuilder, &vcs.Mock{}, parser.ModeAny,
parser.RebuildModeNo, false, newTestLogger()) parser.RebuildModeNo, false, NewTestLogger())
cmdArgs := parser.MakeArguments() cmdArgs := parser.MakeArguments()
cmdArgs.AddArg("needed") cmdArgs.AddArg("needed")
@ -609,21 +609,10 @@ func TestInstaller_CompileFailed(t *testing.T) {
} else { } else {
require.NoError(td, errI) require.NoError(td, errI)
} }
failed, err := installer.CompileFailedAndIgnored() err := installer.CompileFailedAndIgnored()
if tc.wantErrCompile { if tc.wantErrCompile {
require.Error(td, err) require.Error(td, err)
for key := range failed { assert.ErrorContains(td, err, "yay")
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 { } else {
require.NoError(td, err) require.NoError(td, err)
} }
@ -704,16 +693,18 @@ func TestInstaller_InstallSplitPackage(t *testing.T) {
wantShow: []string{ wantShow: []string{
"pacman -S --config /etc/pacman.conf -- community/dotnet-runtime-6.0 community/aspnet-runtime community/dotnet-sdk-6.0", "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", "pacman -D -q --asdeps --config /etc/pacman.conf -- dotnet-runtime-6.0 aspnet-runtime dotnet-sdk-6.0",
"makepkg --nobuild -f -C --ignorearch", "makepkg --nobuild -fC --ignorearch",
"makepkg -f -c --noconfirm --noextract --noprepare --holdver --ignorearch", "makepkg -cf --noconfirm --noextract --noprepare --holdver --ignorearch",
"makepkg --nobuild -fC --ignorearch",
"makepkg -c --nobuild --noextract --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 -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", "pacman -D -q --asdeps --config /etc/pacman.conf -- jellyfin-server jellyfin-web",
"makepkg --nobuild -f -C --ignorearch", "makepkg --nobuild -fC --ignorearch",
"makepkg -c --nobuild --noextract --ignorearch", "makepkg -c --nobuild --noextract --ignorearch",
"pacman -U --config /etc/pacman.conf -- /testdir/jellyfin-10.8.4-1-x86_64.pkg.tar.zst", "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", "pacman -D -q --asexplicit --config /etc/pacman.conf -- jellyfin",
}, },
wantCapture: []string{"makepkg --packagelist", "makepkg --packagelist"}, wantCapture: []string{"makepkg --packagelist", "makepkg --packagelist", "makepkg --packagelist"},
}, },
} }
@ -761,7 +752,7 @@ func TestInstaller_InstallSplitPackage(t *testing.T) {
cmdBuilder.Runner = mockRunner cmdBuilder.Runner = mockRunner
installer := NewInstaller(mockDB, cmdBuilder, &vcs.Mock{}, parser.ModeAny, installer := NewInstaller(mockDB, cmdBuilder, &vcs.Mock{}, parser.ModeAny,
parser.RebuildModeNo, false, newTestLogger()) parser.RebuildModeNo, false, NewTestLogger())
cmdArgs := parser.MakeArguments() cmdArgs := parser.MakeArguments()
cmdArgs.AddTarget("jellyfin") cmdArgs.AddTarget("jellyfin")
@ -826,7 +817,7 @@ func TestInstaller_InstallDownloadOnly(t *testing.T) {
isInstalled: false, isInstalled: false,
isBuilt: false, isBuilt: false,
wantShow: []string{ wantShow: []string{
"makepkg --nobuild -f -C --ignorearch", "makepkg --nobuild -fC --ignorearch",
"makepkg -c --nobuild --noextract --ignorearch", "makepkg -c --nobuild --noextract --ignorearch",
}, },
wantCapture: []string{"makepkg --packagelist"}, wantCapture: []string{"makepkg --packagelist"},
@ -836,7 +827,7 @@ func TestInstaller_InstallDownloadOnly(t *testing.T) {
isInstalled: false, isInstalled: false,
isBuilt: true, isBuilt: true,
wantShow: []string{ wantShow: []string{
"makepkg --nobuild -f -C --ignorearch", "makepkg --nobuild -fC --ignorearch",
"makepkg -c --nobuild --noextract --ignorearch", "makepkg -c --nobuild --noextract --ignorearch",
}, },
wantCapture: []string{"makepkg --packagelist"}, wantCapture: []string{"makepkg --packagelist"},
@ -846,7 +837,7 @@ func TestInstaller_InstallDownloadOnly(t *testing.T) {
isInstalled: true, isInstalled: true,
isBuilt: false, isBuilt: false,
wantShow: []string{ wantShow: []string{
"makepkg --nobuild -f -C --ignorearch", "makepkg --nobuild -fC --ignorearch",
"makepkg -c --nobuild --noextract --ignorearch", "makepkg -c --nobuild --noextract --ignorearch",
}, },
wantCapture: []string{"makepkg --packagelist"}, wantCapture: []string{"makepkg --packagelist"},
@ -900,7 +891,7 @@ func TestInstaller_InstallDownloadOnly(t *testing.T) {
cmdBuilder.Runner = mockRunner cmdBuilder.Runner = mockRunner
installer := NewInstaller(mockDB, cmdBuilder, &vcs.Mock{}, parser.ModeAny, installer := NewInstaller(mockDB, cmdBuilder, &vcs.Mock{}, parser.ModeAny,
parser.RebuildModeNo, true, newTestLogger()) parser.RebuildModeNo, true, NewTestLogger())
cmdArgs := parser.MakeArguments() cmdArgs := parser.MakeArguments()
cmdArgs.AddTarget("yay") cmdArgs.AddTarget("yay")
@ -1004,7 +995,7 @@ func TestInstaller_InstallGroup(t *testing.T) {
cmdBuilder.Runner = mockRunner cmdBuilder.Runner = mockRunner
installer := NewInstaller(mockDB, cmdBuilder, &vcs.Mock{}, parser.ModeAny, installer := NewInstaller(mockDB, cmdBuilder, &vcs.Mock{}, parser.ModeAny,
parser.RebuildModeNo, true, newTestLogger()) parser.RebuildModeNo, true, NewTestLogger())
cmdArgs := parser.MakeArguments() cmdArgs := parser.MakeArguments()
cmdArgs.AddTarget("kubernetes-tools") cmdArgs.AddTarget("kubernetes-tools")
@ -1083,7 +1074,7 @@ func TestInstaller_InstallRebuild(t *testing.T) {
isBuilt: true, isBuilt: true,
isInstalled: false, isInstalled: false,
wantShow: []string{ wantShow: []string{
"makepkg --nobuild -f -C --ignorearch", "makepkg --nobuild -fC --ignorearch",
"makepkg -c --nobuild --noextract --ignorearch", "makepkg -c --nobuild --noextract --ignorearch",
"pacman -U --config -- /testdir/yay-91.0.0-1-x86_64.pkg.tar.zst", "pacman -U --config -- /testdir/yay-91.0.0-1-x86_64.pkg.tar.zst",
"pacman -D -q --asexplicit --config -- yay", "pacman -D -q --asexplicit --config -- yay",
@ -1107,8 +1098,8 @@ func TestInstaller_InstallRebuild(t *testing.T) {
isBuilt: true, isBuilt: true,
isInstalled: false, isInstalled: false,
wantShow: []string{ wantShow: []string{
"makepkg --nobuild -f -C --ignorearch", "makepkg --nobuild -fC --ignorearch",
"makepkg -f -c --noconfirm --noextract --noprepare --holdver --ignorearch", "makepkg -cf --noconfirm --noextract --noprepare --holdver --ignorearch",
"pacman -U --config -- /testdir/yay-91.0.0-1-x86_64.pkg.tar.zst", "pacman -U --config -- /testdir/yay-91.0.0-1-x86_64.pkg.tar.zst",
"pacman -D -q --asexplicit --config -- yay", "pacman -D -q --asexplicit --config -- yay",
}, },
@ -1131,8 +1122,8 @@ func TestInstaller_InstallRebuild(t *testing.T) {
isInstalled: true, isInstalled: true,
isBuilt: true, isBuilt: true,
wantShow: []string{ wantShow: []string{
"makepkg --nobuild -f -C --ignorearch", "makepkg --nobuild -fC --ignorearch",
"makepkg -f -c --noconfirm --noextract --noprepare --holdver --ignorearch", "makepkg -cf --noconfirm --noextract --noprepare --holdver --ignorearch",
"pacman -U --config -- /testdir/yay-91.0.0-1-x86_64.pkg.tar.zst", "pacman -U --config -- /testdir/yay-91.0.0-1-x86_64.pkg.tar.zst",
"pacman -D -q --asexplicit --config -- yay", "pacman -D -q --asexplicit --config -- yay",
}, },
@ -1155,8 +1146,8 @@ func TestInstaller_InstallRebuild(t *testing.T) {
isInstalled: true, isInstalled: true,
isBuilt: true, isBuilt: true,
wantShow: []string{ wantShow: []string{
"makepkg --nobuild -f -C --ignorearch", "makepkg --nobuild -fC --ignorearch",
"makepkg -f -c --noconfirm --noextract --noprepare --holdver --ignorearch", "makepkg -cf --noconfirm --noextract --noprepare --holdver --ignorearch",
"pacman -U --config -- /testdir/yay-91.0.0-1-x86_64.pkg.tar.zst", "pacman -U --config -- /testdir/yay-91.0.0-1-x86_64.pkg.tar.zst",
"pacman -D -q --asdeps --config -- yay", "pacman -D -q --asdeps --config -- yay",
}, },
@ -1222,7 +1213,7 @@ func TestInstaller_InstallRebuild(t *testing.T) {
cmdBuilder.Runner = mockRunner cmdBuilder.Runner = mockRunner
installer := NewInstaller(mockDB, cmdBuilder, &vcs.Mock{}, parser.ModeAny, installer := NewInstaller(mockDB, cmdBuilder, &vcs.Mock{}, parser.ModeAny,
tc.rebuildOption, false, newTestLogger()) tc.rebuildOption, false, NewTestLogger())
cmdArgs := parser.MakeArguments() cmdArgs := parser.MakeArguments()
cmdArgs.AddTarget("yay") cmdArgs.AddTarget("yay")
@ -1257,195 +1248,3 @@ func TestInstaller_InstallRebuild(t *testing.T) {
}) })
} }
} }
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 {
tc := tc
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 {
tc := tc
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")
}
}
})
}
}

View File

@ -1,4 +1,4 @@
package workdir package main
import ( import (
"context" "context"
@ -32,11 +32,7 @@ func (e *ErrDownloadSource) Unwrap() error {
func downloadPKGBUILDSource(ctx context.Context, func downloadPKGBUILDSource(ctx context.Context,
cmdBuilder exe.ICmdBuilder, pkgBuildDir string, installIncompatible bool, cmdBuilder exe.ICmdBuilder, pkgBuildDir string, installIncompatible bool,
) error { ) error {
args := []string{"--verifysource", "--skippgpcheck", "-f"} args := []string{"--verifysource", "-Ccf"}
if !cmdBuilder.GetKeepSrc() {
args = append(args, "-Cc")
}
if installIncompatible { if installIncompatible {
args = append(args, "--ignorearch") args = append(args, "--ignorearch")

View File

@ -1,7 +1,7 @@
//go:build !integration //go:build !integration
// +build !integration // +build !integration
package workdir package main
import ( import (
"context" "context"
@ -33,10 +33,6 @@ func (z *TestMakepkgBuilder) BuildMakepkgCmd(ctx context.Context, dir string, ex
assert.Contains(z.test, cmd.String(), z.want) assert.Contains(z.test, cmd.String(), z.want)
} }
if z.GetKeepSrc() {
assert.NotContains(z.test, cmd.String(), "-Cc")
}
if z.wantDir != "" { if z.wantDir != "" {
assert.Equal(z.test, z.wantDir, cmd.Dir) assert.Equal(z.test, z.wantDir, cmd.Dir)
} }
@ -50,54 +46,20 @@ func (z *TestMakepkgBuilder) Show(cmd *exec.Cmd) error {
return z.showError return z.showError
} }
func (z *TestMakepkgBuilder) GetKeepSrc() bool {
return z.parentBuilder.KeepSrc
}
// GIVEN 1 package // GIVEN 1 package
// WHEN downloadPKGBUILDSource is called // WHEN downloadPKGBUILDSource is called
// THEN 1 call should be made to makepkg with the specified parameters and dir // THEN 1 call should be made to makepkg with the specified parameters and dir
func Test_downloadPKGBUILDSource(t *testing.T) { func Test_downloadPKGBUILDSource(t *testing.T) {
t.Parallel() t.Parallel()
cmdBuilder := &TestMakepkgBuilder{
type testCase struct { parentBuilder: &exe.CmdBuilder{MakepkgConfPath: "/etc/not.conf", MakepkgFlags: []string{"--nocheck"}, MakepkgBin: "makepkg"},
desc string test: t,
keepSrc bool want: "makepkg --nocheck --config /etc/not.conf --verifysource -Ccf",
want string wantDir: "/tmp/yay-bin",
}
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 {
tc := tc
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))
})
} }
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 // GIVEN 1 package
@ -108,7 +70,7 @@ func Test_downloadPKGBUILDSourceError(t *testing.T) {
cmdBuilder := &TestMakepkgBuilder{ cmdBuilder := &TestMakepkgBuilder{
parentBuilder: &exe.CmdBuilder{MakepkgConfPath: "/etc/not.conf", MakepkgFlags: []string{"--nocheck"}, MakepkgBin: "makepkg"}, parentBuilder: &exe.CmdBuilder{MakepkgConfPath: "/etc/not.conf", MakepkgFlags: []string{"--nocheck"}, MakepkgBin: "makepkg"},
test: t, test: t,
want: "makepkg --nocheck --config /etc/not.conf --verifysource --skippgpcheck -f -Cc", want: "makepkg --nocheck --config /etc/not.conf --verifysource -Ccf",
wantDir: "/tmp/yay-bin", wantDir: "/tmp/yay-bin",
showError: &exec.ExitError{}, showError: &exec.ExitError{},
} }

View File

@ -1,15 +1,13 @@
FROM docker.io/ljmf00/archlinux:devel FROM docker.io/jguer/yay-builder:latest
LABEL maintainer="Jguer,docker@jguer.space" LABEL maintainer="Jguer,docker@jguer.space"
ENV GO111MODULE=on ENV GO111MODULE=on
WORKDIR /app WORKDIR /app
RUN sed -i '/^\[community\]/,/^\[/ s/^/#/' /etc/pacman.conf
COPY go.mod . COPY go.mod .
RUN pacman-key --init && pacman -Sy && pacman -S --overwrite=* --noconfirm archlinux-keyring && \ RUN pacman-key --init && pacman -Sy && pacman -S --overwrite=* --noconfirm archlinux-keyring && \
pacman -Su --overwrite=* --needed --noconfirm pacman doxygen meson asciidoc go git gcc make sudo base-devel && \ pacman -Su --overwrite=* --needed --noconfirm doxygen meson asciidoc go git gcc make sudo base-devel && \
rm -rfv /var/cache/pacman/* /var/lib/pacman/sync/* && \ rm -rfv /var/cache/pacman/* /var/lib/pacman/sync/* && \
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s v2.1.5 && \ curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s v1.52.2 && \
go mod download go mod download

101
clean.go
View File

@ -2,18 +2,19 @@ package main
import ( import (
"context" "context"
"fmt"
"os" "os"
"path/filepath" "path/filepath"
"github.com/Jguer/aur" "github.com/Jguer/aur"
mapset "github.com/deckarep/golang-set/v2"
"github.com/leonelquinteros/gotext" "github.com/leonelquinteros/gotext"
"github.com/Jguer/yay/v12/pkg/db" "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"
"github.com/Jguer/yay/v12/pkg/settings/exe" "github.com/Jguer/yay/v12/pkg/settings/exe"
"github.com/Jguer/yay/v12/pkg/settings/parser" "github.com/Jguer/yay/v12/pkg/settings/parser"
"github.com/Jguer/yay/v12/pkg/stringset"
"github.com/Jguer/yay/v12/pkg/text"
) )
// CleanDependencies removes all dangling dependencies in system. // CleanDependencies removes all dangling dependencies in system.
@ -48,29 +49,28 @@ func cleanRemove(ctx context.Context, cfg *settings.Configuration,
arguments, cfg.Mode, settings.NoConfirm)) arguments, cfg.Mode, settings.NoConfirm))
} }
func syncClean(ctx context.Context, run *runtime.Runtime, cmdArgs *parser.Arguments, dbExecutor db.Executor) error { func syncClean(ctx context.Context, cfg *settings.Configuration, cmdArgs *parser.Arguments, dbExecutor db.Executor) error {
keepInstalled := false keepInstalled := false
keepCurrent := false keepCurrent := false
_, removeAll, _ := cmdArgs.GetArg("c", "clean") _, removeAll, _ := cmdArgs.GetArg("c", "clean")
for _, v := range run.PacmanConf.CleanMethod { for _, v := range cfg.Runtime.PacmanConf.CleanMethod {
switch v { if v == "KeepInstalled" {
case "KeepInstalled":
keepInstalled = true keepInstalled = true
case "KeepCurrent": } else if v == "KeepCurrent" {
keepCurrent = true keepCurrent = true
} }
} }
if run.Cfg.Mode.AtLeastRepo() { if cfg.Mode.AtLeastRepo() {
if err := run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx, if err := cfg.Runtime.CmdBuilder.Show(cfg.Runtime.CmdBuilder.BuildPacmanCmd(ctx,
cmdArgs, run.Cfg.Mode, settings.NoConfirm)); err != nil { cmdArgs, cfg.Mode, settings.NoConfirm)); err != nil {
return err return err
} }
} }
if !run.Cfg.Mode.AtLeastAUR() { if !cfg.Mode.AtLeastAUR() {
return nil return nil
} }
@ -81,10 +81,10 @@ func syncClean(ctx context.Context, run *runtime.Runtime, cmdArgs *parser.Argume
question = gotext.Get("Do you want to remove all other AUR packages from cache?") question = gotext.Get("Do you want to remove all other AUR packages from cache?")
} }
run.Logger.Println(gotext.Get("\nBuild directory:"), run.Cfg.BuildDir) fmt.Println(gotext.Get("\nBuild directory:"), cfg.BuildDir)
if run.Logger.ContinueTask(question, true, settings.NoConfirm) { if text.ContinueTask(os.Stdin, question, true, settings.NoConfirm) {
if err := cleanAUR(ctx, run, keepInstalled, keepCurrent, removeAll, dbExecutor); err != nil { if err := cleanAUR(ctx, cfg, keepInstalled, keepCurrent, removeAll, dbExecutor); err != nil {
return err return err
} }
} }
@ -93,24 +93,24 @@ func syncClean(ctx context.Context, run *runtime.Runtime, cmdArgs *parser.Argume
return nil return nil
} }
if run.Logger.ContinueTask(gotext.Get("Do you want to remove ALL untracked AUR files?"), true, settings.NoConfirm) { if text.ContinueTask(os.Stdin, gotext.Get("Do you want to remove ALL untracked AUR files?"), true, settings.NoConfirm) {
return cleanUntracked(ctx, run) return cleanUntracked(ctx, cfg)
} }
return nil return nil
} }
func cleanAUR(ctx context.Context, run *runtime.Runtime, func cleanAUR(ctx context.Context, cfg *settings.Configuration,
keepInstalled, keepCurrent, removeAll bool, dbExecutor db.Executor, keepInstalled, keepCurrent, removeAll bool, dbExecutor db.Executor,
) error { ) error {
run.Logger.Println(gotext.Get("removing AUR packages from cache...")) cfg.Runtime.Logger.Println(gotext.Get("removing AUR packages from cache..."))
installedBases := mapset.NewThreadUnsafeSet[string]() installedBases := make(stringset.StringSet)
inAURBases := mapset.NewThreadUnsafeSet[string]() inAURBases := make(stringset.StringSet)
remotePackages := dbExecutor.InstalledRemotePackages() remotePackages := dbExecutor.InstalledRemotePackages()
files, err := os.ReadDir(run.Cfg.BuildDir) files, err := os.ReadDir(cfg.BuildDir)
if err != nil { if err != nil {
return err return err
} }
@ -130,7 +130,7 @@ func cleanAUR(ctx context.Context, run *runtime.Runtime,
// Querying the AUR is slow and needs internet so don't do it if we // Querying the AUR is slow and needs internet so don't do it if we
// don't need to. // don't need to.
if keepCurrent { if keepCurrent {
info, errInfo := run.AURClient.Get(ctx, &aur.Query{ info, errInfo := cfg.Runtime.AURClient.Get(ctx, &aur.Query{
Needles: cachedPackages, Needles: cachedPackages,
}) })
if errInfo != nil { if errInfo != nil {
@ -138,15 +138,15 @@ func cleanAUR(ctx context.Context, run *runtime.Runtime,
} }
for i := range info { for i := range info {
inAURBases.Add(info[i].PackageBase) inAURBases.Set(info[i].PackageBase)
} }
} }
for _, pkg := range remotePackages { for _, pkg := range remotePackages {
if pkg.Base() != "" { if pkg.Base() != "" {
installedBases.Add(pkg.Base()) installedBases.Set(pkg.Base())
} else { } else {
installedBases.Add(pkg.Name()) installedBases.Set(pkg.Name())
} }
} }
@ -156,29 +156,29 @@ func cleanAUR(ctx context.Context, run *runtime.Runtime,
} }
if !removeAll { if !removeAll {
if keepInstalled && installedBases.Contains(file.Name()) { if keepInstalled && installedBases.Get(file.Name()) {
continue continue
} }
if keepCurrent && inAURBases.Contains(file.Name()) { if keepCurrent && inAURBases.Get(file.Name()) {
continue continue
} }
} }
dir := filepath.Join(run.Cfg.BuildDir, file.Name()) dir := filepath.Join(cfg.BuildDir, file.Name())
run.Logger.Debugln("removing", dir) cfg.Runtime.Logger.Debugln("removing", dir)
if err = os.RemoveAll(dir); err != nil { if err = os.RemoveAll(dir); err != nil {
run.Logger.Warnln(gotext.Get("Unable to remove %s: %s", dir, err)) cfg.Runtime.Logger.Warnln(gotext.Get("Unable to remove %s: %s", dir, err))
} }
} }
return nil return nil
} }
func cleanUntracked(ctx context.Context, run *runtime.Runtime) error { func cleanUntracked(ctx context.Context, cfg *settings.Configuration) error {
run.Logger.Println(gotext.Get("removing untracked AUR files from cache...")) cfg.Runtime.Logger.Println(gotext.Get("removing untracked AUR files from cache..."))
files, err := os.ReadDir(run.Cfg.BuildDir) files, err := os.ReadDir(cfg.BuildDir)
if err != nil { if err != nil {
return err return err
} }
@ -188,11 +188,12 @@ func cleanUntracked(ctx context.Context, run *runtime.Runtime) error {
continue continue
} }
dir := filepath.Join(run.Cfg.BuildDir, file.Name()) dir := filepath.Join(cfg.BuildDir, file.Name())
run.Logger.Debugln("cleaning", dir) cfg.Runtime.Logger.Debugln("cleaning", dir)
if isGitRepository(dir) { if isGitRepository(dir) {
if err := run.CmdBuilder.Show(run.CmdBuilder.BuildGitCmd(ctx, dir, "clean", "-fx")); err != nil { if err := cfg.Runtime.CmdBuilder.Show(cfg.Runtime.CmdBuilder.BuildGitCmd(ctx, dir, "clean", "-fx")); err != nil {
run.Logger.Warnln(gotext.Get("Unable to clean:"), dir) cfg.Runtime.Logger.Warnln(gotext.Get("Unable to clean:"), dir)
return err return err
} }
} }
@ -205,3 +206,29 @@ func isGitRepository(dir string) bool {
_, err := os.Stat(filepath.Join(dir, ".git")) _, err := os.Stat(filepath.Join(dir, ".git"))
return !os.IsNotExist(err) return !os.IsNotExist(err)
} }
func cleanAfter(ctx context.Context, config *settings.Configuration,
cmdBuilder exe.ICmdBuilder, pkgbuildDirs map[string]string,
) {
fmt.Println(gotext.Get("removing untracked AUR files from cache..."))
i := 0
for _, dir := range pkgbuildDirs {
text.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 {
text.Errorln(gotext.Get("error resetting %s: %s", dir, stderr))
}
if err := config.Runtime.CmdBuilder.Show(
config.Runtime.CmdBuilder.BuildGitCmd(
ctx, dir, "clean", "-fx", "--exclude", "*.pkg.*")); err != nil {
fmt.Fprintln(os.Stderr, err)
}
i++
}
}

View File

@ -15,7 +15,6 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/Jguer/yay/v12/pkg/db/mock" "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"
"github.com/Jguer/yay/v12/pkg/settings/exe" "github.com/Jguer/yay/v12/pkg/settings/exe"
"github.com/Jguer/yay/v12/pkg/settings/parser" "github.com/Jguer/yay/v12/pkg/settings/parser"
@ -91,13 +90,15 @@ func TestCleanHanging(t *testing.T) {
Runner: mockRunner, Runner: mockRunner,
SudoLoopEnabled: false, SudoLoopEnabled: false,
} }
cfg := &settings.Configuration{
Runtime: &settings.Runtime{CmdBuilder: cmdBuilder},
}
run := &runtime.Runtime{CmdBuilder: cmdBuilder, Cfg: &settings.Configuration{}}
cmdArgs := parser.MakeArguments() cmdArgs := parser.MakeArguments()
cmdArgs.AddArg(tc.args...) cmdArgs.AddArg(tc.args...)
err := handleCmd(context.Background(), err := handleCmd(context.Background(),
run, cmdArgs, dbExc, cfg, cmdArgs, dbExc,
) )
require.NoError(t, err) require.NoError(t, err)

200
cmd.go
View File

@ -17,7 +17,6 @@ import (
"github.com/Jguer/yay/v12/pkg/intrange" "github.com/Jguer/yay/v12/pkg/intrange"
"github.com/Jguer/yay/v12/pkg/news" "github.com/Jguer/yay/v12/pkg/news"
"github.com/Jguer/yay/v12/pkg/query" "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"
"github.com/Jguer/yay/v12/pkg/settings/exe" "github.com/Jguer/yay/v12/pkg/settings/exe"
"github.com/Jguer/yay/v12/pkg/settings/parser" "github.com/Jguer/yay/v12/pkg/settings/parser"
@ -26,8 +25,8 @@ import (
"github.com/Jguer/yay/v12/pkg/vcs" "github.com/Jguer/yay/v12/pkg/vcs"
) )
func usage(logger *text.Logger) { func usage() {
logger.Println(`Usage: fmt.Println(`Usage:
yay yay
yay <operation> [...] yay <operation> [...]
yay <package(s)> yay <package(s)>
@ -54,7 +53,7 @@ If no operation is specified 'yay -Syu' will be performed
If no operation is specified and targets are provided -Y will be assumed If no operation is specified and targets are provided -Y will be assumed
New options: New options:
-N --repo Assume targets are from the repositories --repo Assume targets are from the repositories
-a --aur Assume targets are from the AUR -a --aur Assume targets are from the AUR
Permanent configuration options: Permanent configuration options:
@ -92,19 +91,23 @@ Permanent configuration options:
--cleanmenu Give the option to clean build PKGBUILDS --cleanmenu Give the option to clean build PKGBUILDS
--diffmenu Give the option to show diffs for build files --diffmenu Give the option to show diffs for build files
--editmenu Give the option to edit/view PKGBUILDS --editmenu Give the option to edit/view PKGBUILDS
--nocleanmenu Don't clean build PKGBUILDS
--nodiffmenu Don't show diffs for build files
--noeditmenu Don't edit/view PKGBUILDS
--askremovemake Ask to remove makedepends after install --askremovemake Ask to remove makedepends after install
--askyesremovemake Ask to remove makedepends after install("Y" as default) --askyesremovemake Ask to remove makedepends after install("Y" as default)
--removemake Remove makedepends after install --removemake Remove makedepends after install
--noremovemake Don't remove makedepends after install --noremovemake Don't remove makedepends after install
--cleanafter Remove package sources after successful install --cleanafter Remove package sources after successful install
--keepsrc Keep pkg/ and src/ after building packages --nocleanafter Do not remove package sources after successful build
--bottomup Shows AUR's packages first and then repository's --bottomup Shows AUR's packages first and then repository's
--topdown Shows repository's packages first and then AUR's --topdown Shows repository's packages first and then AUR's
--singlelineresults List each search result on its own line --singlelineresults List each search result on its own line
--doublelineresults List each search result on two lines, like pacman --doublelineresults List each search result on two lines, like pacman
--devel Check development packages during sysupgrade --devel Check development packages during sysupgrade
--nodevel Do not check development packages
--rebuild Always build target packages --rebuild Always build target packages
--rebuildall Always build all AUR packages --rebuildall Always build all AUR packages
--norebuild Skip package build if in cache and up to date --norebuild Skip package build if in cache and up to date
@ -113,14 +116,19 @@ Permanent configuration options:
--noredownload Skip pkgbuild download if in cache and up to date --noredownload Skip pkgbuild download if in cache and up to date
--redownloadall Always download pkgbuilds of all AUR packages --redownloadall Always download pkgbuilds of all AUR packages
--provides Look for matching providers when searching for packages --provides Look for matching providers when searching for packages
--noprovides Just look for packages by pkgname
--pgpfetch Prompt to import PGP keys from PKGBUILDs --pgpfetch Prompt to import PGP keys from PKGBUILDs
--nopgpfetch Don't prompt to import PGP keys
--useask Automatically resolve conflicts using pacman's ask flag --useask Automatically resolve conflicts using pacman's ask flag
--nouseask Confirm conflicts manually during the install
--sudo <file> sudo command to use --sudo <file> sudo command to use
--sudoflags <flags> Pass arguments to sudo --sudoflags <flags> Pass arguments to sudo
--sudoloop Loop sudo calls in the background to avoid timeout --sudoloop Loop sudo calls in the background to avoid timeout
--nosudoloop Do not loop sudo calls in the background
--timeupdate Check packages' AUR page for changes during sysupgrade --timeupdate Check packages' AUR page for changes during sysupgrade
--notimeupdate Do not check packages' AUR page for changes
show specific options: show specific options:
-c --complete Used for completions -c --complete Used for completions
@ -130,7 +138,7 @@ show specific options:
-w --news Print arch news -w --news Print arch news
yay specific options: yay specific options:
-c --clean Remove unneeded dependencies (-cc to ignore optdepends) -c --clean Remove unneeded dependencies
--gendb Generates development package DB used for updating --gendb Generates development package DB used for updating
getpkgbuild specific options: getpkgbuild specific options:
@ -138,49 +146,50 @@ getpkgbuild specific options:
-p --print Print pkgbuild of packages`) -p --print Print pkgbuild of packages`)
} }
func handleCmd(ctx context.Context, run *runtime.Runtime, func handleCmd(ctx context.Context, cfg *settings.Configuration,
cmdArgs *parser.Arguments, dbExecutor db.Executor, cmdArgs *parser.Arguments, dbExecutor db.Executor,
) error { ) error {
if cmdArgs.ExistsArg("h", "help") { if cmdArgs.ExistsArg("h", "help") {
return handleHelp(ctx, run, cmdArgs) return handleHelp(ctx, cfg, cmdArgs)
} }
if run.Cfg.SudoLoop && cmdArgs.NeedRoot(run.Cfg.Mode) { if cfg.SudoLoop && cmdArgs.NeedRoot(cfg.Mode) {
run.CmdBuilder.SudoLoop() cfg.Runtime.CmdBuilder.SudoLoop()
} }
switch cmdArgs.Op { switch cmdArgs.Op {
case "V", "version": case "V", "version":
handleVersion(run.Logger) handleVersion()
return nil return nil
case "D", "database": case "D", "database":
return run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx, return cfg.Runtime.CmdBuilder.Show(cfg.Runtime.CmdBuilder.BuildPacmanCmd(ctx,
cmdArgs, run.Cfg.Mode, settings.NoConfirm)) cmdArgs, cfg.Mode, settings.NoConfirm))
case "F", "files": case "F", "files":
return run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx, return cfg.Runtime.CmdBuilder.Show(cfg.Runtime.CmdBuilder.BuildPacmanCmd(ctx,
cmdArgs, run.Cfg.Mode, settings.NoConfirm)) cmdArgs, cfg.Mode, settings.NoConfirm))
case "Q", "query": case "Q", "query":
return handleQuery(ctx, run, cmdArgs, dbExecutor) return handleQuery(ctx, cfg, cmdArgs, dbExecutor)
case "R", "remove": case "R", "remove":
return handleRemove(ctx, run, cmdArgs, run.VCSStore) return handleRemove(ctx, cfg, cmdArgs, cfg.Runtime.VCSStore)
case "S", "sync": case "S", "sync":
return handleSync(ctx, run, cmdArgs, dbExecutor) return handleSync(ctx, cfg, cmdArgs, dbExecutor)
case "T", "deptest": case "T", "deptest":
return run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx, return cfg.Runtime.CmdBuilder.Show(cfg.Runtime.CmdBuilder.BuildPacmanCmd(ctx,
cmdArgs, run.Cfg.Mode, settings.NoConfirm)) cmdArgs, cfg.Mode, settings.NoConfirm))
case "U", "upgrade": case "U", "upgrade":
return handleUpgrade(ctx, run, cmdArgs) return handleUpgrade(ctx, cfg, cmdArgs)
case "B", "build": case "B", "build":
return handleBuild(ctx, run, dbExecutor, cmdArgs) return handleBuild(ctx, cfg, dbExecutor, cmdArgs)
case "G", "getpkgbuild": case "G", "getpkgbuild":
return handleGetpkgbuild(ctx, run, cmdArgs, dbExecutor) return handleGetpkgbuild(ctx, cfg, cmdArgs, dbExecutor)
case "P", "show": case "P", "show":
return handlePrint(ctx, run, cmdArgs, dbExecutor) return handlePrint(ctx, cfg, cmdArgs, dbExecutor)
case "Y", "yay": case "Y", "yay":
return handleYay(ctx, run, cmdArgs, run.CmdBuilder, return handleYay(ctx, cfg, cmdArgs, cfg.Runtime.CmdBuilder,
dbExecutor, run.QueryBuilder) dbExecutor, cfg.Runtime.QueryBuilder)
case "W", "web": case "W", "web":
return handleWeb(ctx, run, cmdArgs) return handleWeb(ctx, cfg, cmdArgs)
} }
return errors.New(gotext.Get("unhandled operation")) return errors.New(gotext.Get("unhandled operation"))
@ -210,19 +219,19 @@ func getFilter(cmdArgs *parser.Arguments) (upgrade.Filter, error) {
}, nil }, nil
} }
func handleQuery(ctx context.Context, run *runtime.Runtime, cmdArgs *parser.Arguments, dbExecutor db.Executor) error { func handleQuery(ctx context.Context, cfg *settings.Configuration, cmdArgs *parser.Arguments, dbExecutor db.Executor) error {
if cmdArgs.ExistsArg("u", "upgrades") { if cmdArgs.ExistsArg("u", "upgrades") {
filter, err := getFilter(cmdArgs) filter, err := getFilter(cmdArgs)
if err != nil { if err != nil {
return err return err
} }
return printUpdateList(ctx, run, cmdArgs, dbExecutor, return printUpdateList(ctx, cfg, cmdArgs, dbExecutor,
cmdArgs.ExistsDouble("u", "sysupgrade"), filter) cmdArgs.ExistsDouble("u", "sysupgrade"), filter)
} }
if err := run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx, if err := cfg.Runtime.CmdBuilder.Show(cfg.Runtime.CmdBuilder.BuildPacmanCmd(ctx,
cmdArgs, run.Cfg.Mode, settings.NoConfirm)); err != nil { cmdArgs, cfg.Mode, settings.NoConfirm)); err != nil {
if str := err.Error(); strings.Contains(str, "exit status") { if str := err.Error(); strings.Contains(str, "exit status") {
// yay -Qdt should not output anything in case of error // yay -Qdt should not output anything in case of error
return fmt.Errorf("") return fmt.Errorf("")
@ -234,139 +243,138 @@ func handleQuery(ctx context.Context, run *runtime.Runtime, cmdArgs *parser.Argu
return nil return nil
} }
func handleHelp(ctx context.Context, run *runtime.Runtime, cmdArgs *parser.Arguments) error { func handleHelp(ctx context.Context, cfg *settings.Configuration, cmdArgs *parser.Arguments) error {
usage(run.Logger) usage()
switch cmdArgs.Op { switch cmdArgs.Op {
case "Y", "yay", "G", "getpkgbuild", "P", "show", "W", "web", "B", "build": case "Y", "yay", "G", "getpkgbuild", "P", "show", "W", "web", "B", "build":
return nil return nil
} }
run.Logger.Println("\npacman operation specific options:") cfg.Runtime.Logger.Println("\npacman operation specific options:")
return run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx, return cfg.Runtime.CmdBuilder.Show(cfg.Runtime.CmdBuilder.BuildPacmanCmd(ctx,
cmdArgs, run.Cfg.Mode, settings.NoConfirm)) cmdArgs, cfg.Mode, settings.NoConfirm))
} }
func handleVersion(logger *text.Logger) { func handleVersion() {
logger.Printf("yay v%s - libalpm v%s\n", yayVersion, alpm.Version()) fmt.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 { func handlePrint(ctx context.Context, cfg *settings.Configuration, cmdArgs *parser.Arguments, dbExecutor db.Executor) error {
switch { switch {
case cmdArgs.ExistsArg("d", "defaultconfig"): case cmdArgs.ExistsArg("d", "defaultconfig"):
tmpConfig := settings.DefaultConfig(yayVersion) tmpConfig := settings.DefaultConfig(yayVersion)
run.Logger.Printf("%v", tmpConfig) fmt.Printf("%v", tmpConfig)
return nil return nil
case cmdArgs.ExistsArg("g", "currentconfig"): case cmdArgs.ExistsArg("g", "currentconfig"):
run.Logger.Printf("%v", run.Cfg) fmt.Printf("%v", cfg)
return nil return nil
case cmdArgs.ExistsArg("w", "news"): case cmdArgs.ExistsArg("w", "news"):
double := cmdArgs.ExistsDouble("w", "news") double := cmdArgs.ExistsDouble("w", "news")
quiet := cmdArgs.ExistsArg("q", "quiet") quiet := cmdArgs.ExistsArg("q", "quiet")
return news.PrintNewsFeed(ctx, run.HTTPClient, run.Logger, return news.PrintNewsFeed(ctx, cfg.Runtime.HTTPClient, dbExecutor.LastBuildTime(), cfg.BottomUp, double, quiet)
dbExecutor.LastBuildTime(), run.Cfg.BottomUp, double, quiet)
case cmdArgs.ExistsArg("c", "complete"): case cmdArgs.ExistsArg("c", "complete"):
return completion.Show(ctx, run.HTTPClient, dbExecutor, return completion.Show(ctx, cfg.Runtime.HTTPClient, dbExecutor,
run.Cfg.AURURL, run.Cfg.CompletionPath, run.Cfg.CompletionInterval, cmdArgs.ExistsDouble("c", "complete")) cfg.AURURL, cfg.CompletionPath, cfg.CompletionInterval, cmdArgs.ExistsDouble("c", "complete"))
case cmdArgs.ExistsArg("s", "stats"): case cmdArgs.ExistsArg("s", "stats"):
return localStatistics(ctx, run, dbExecutor) return localStatistics(ctx, cfg, dbExecutor)
} }
return nil return nil
} }
func handleYay(ctx context.Context, run *runtime.Runtime, func handleYay(ctx context.Context, cfg *settings.Configuration,
cmdArgs *parser.Arguments, cmdBuilder exe.ICmdBuilder, cmdArgs *parser.Arguments, cmdBuilder exe.ICmdBuilder,
dbExecutor db.Executor, queryBuilder query.Builder, dbExecutor db.Executor, queryBuilder query.Builder,
) error { ) error {
switch { switch {
case cmdArgs.ExistsArg("gendb"): case cmdArgs.ExistsArg("gendb"):
return createDevelDB(ctx, run, dbExecutor) return createDevelDB(ctx, cfg, dbExecutor)
case cmdArgs.ExistsDouble("c"): case cmdArgs.ExistsDouble("c"):
return cleanDependencies(ctx, run.Cfg, cmdBuilder, cmdArgs, dbExecutor, true) return cleanDependencies(ctx, cfg, cmdBuilder, cmdArgs, dbExecutor, true)
case cmdArgs.ExistsArg("c", "clean"): case cmdArgs.ExistsArg("c", "clean"):
return cleanDependencies(ctx, run.Cfg, cmdBuilder, cmdArgs, dbExecutor, false) return cleanDependencies(ctx, cfg, cmdBuilder, cmdArgs, dbExecutor, false)
case len(cmdArgs.Targets) > 0: case len(cmdArgs.Targets) > 0:
return displayNumberMenu(ctx, run, cmdArgs.Targets, dbExecutor, queryBuilder, cmdArgs) return displayNumberMenu(ctx, cfg, cmdArgs.Targets, dbExecutor, queryBuilder, cmdArgs)
} }
return nil return nil
} }
func handleWeb(ctx context.Context, run *runtime.Runtime, cmdArgs *parser.Arguments) error { func handleWeb(ctx context.Context, cfg *settings.Configuration, cmdArgs *parser.Arguments) error {
switch { switch {
case cmdArgs.ExistsArg("v", "vote"): case cmdArgs.ExistsArg("v", "vote"):
return handlePackageVote(ctx, cmdArgs.Targets, run.AURClient, run.Logger, return handlePackageVote(ctx, cmdArgs.Targets, cfg.Runtime.AURClient,
run.VoteClient, true) cfg.Runtime.VoteClient, true)
case cmdArgs.ExistsArg("u", "unvote"): case cmdArgs.ExistsArg("u", "unvote"):
return handlePackageVote(ctx, cmdArgs.Targets, run.AURClient, run.Logger, return handlePackageVote(ctx, cmdArgs.Targets, cfg.Runtime.AURClient,
run.VoteClient, false) cfg.Runtime.VoteClient, false)
} }
return nil return nil
} }
func handleGetpkgbuild(ctx context.Context, run *runtime.Runtime, cmdArgs *parser.Arguments, dbExecutor download.DBSearcher) error { func handleGetpkgbuild(ctx context.Context, cfg *settings.Configuration, cmdArgs *parser.Arguments, dbExecutor download.DBSearcher) error {
if cmdArgs.ExistsArg("p", "print") { if cmdArgs.ExistsArg("p", "print") {
return printPkgbuilds(dbExecutor, run.AURClient, return printPkgbuilds(dbExecutor, cfg.Runtime.AURClient,
run.HTTPClient, run.Logger, cmdArgs.Targets, run.Cfg.Mode, run.Cfg.AURURL) cfg.Runtime.HTTPClient, cmdArgs.Targets, cfg.Mode, cfg.AURURL)
} }
return getPkgbuilds(ctx, dbExecutor, run.AURClient, run, return getPkgbuilds(ctx, dbExecutor, cfg.Runtime.AURClient, cfg,
cmdArgs.Targets, cmdArgs.ExistsArg("f", "force")) cmdArgs.Targets, cmdArgs.ExistsArg("f", "force"))
} }
func handleUpgrade(ctx context.Context, func handleUpgrade(ctx context.Context,
run *runtime.Runtime, cmdArgs *parser.Arguments, config *settings.Configuration, cmdArgs *parser.Arguments,
) error { ) error {
return run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx, return config.Runtime.CmdBuilder.Show(config.Runtime.CmdBuilder.BuildPacmanCmd(ctx,
cmdArgs, run.Cfg.Mode, settings.NoConfirm)) cmdArgs, config.Mode, settings.NoConfirm))
} }
// -B* options // -B* options
func handleBuild(ctx context.Context, func handleBuild(ctx context.Context,
run *runtime.Runtime, dbExecutor db.Executor, cmdArgs *parser.Arguments, config *settings.Configuration, dbExecutor db.Executor, cmdArgs *parser.Arguments,
) error { ) error {
if cmdArgs.ExistsArg("i", "install") { if cmdArgs.ExistsArg("i", "install") {
return installLocalPKGBUILD(ctx, run, cmdArgs, dbExecutor) return installLocalPKGBUILD(ctx, config, cmdArgs, dbExecutor)
} }
return nil return nil
} }
func handleSync(ctx context.Context, run *runtime.Runtime, cmdArgs *parser.Arguments, dbExecutor db.Executor) error { func handleSync(ctx context.Context, cfg *settings.Configuration, cmdArgs *parser.Arguments, dbExecutor db.Executor) error {
targets := cmdArgs.Targets targets := cmdArgs.Targets
switch { switch {
case cmdArgs.ExistsArg("s", "search"): case cmdArgs.ExistsArg("s", "search"):
return syncSearch(ctx, targets, dbExecutor, run.QueryBuilder, !cmdArgs.ExistsArg("q", "quiet")) return syncSearch(ctx, targets, dbExecutor, cfg.Runtime.QueryBuilder, !cmdArgs.ExistsArg("q", "quiet"))
case cmdArgs.ExistsArg("p", "print", "print-format"): case cmdArgs.ExistsArg("p", "print", "print-format"):
return run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx, return cfg.Runtime.CmdBuilder.Show(cfg.Runtime.CmdBuilder.BuildPacmanCmd(ctx,
cmdArgs, run.Cfg.Mode, settings.NoConfirm)) cmdArgs, cfg.Mode, settings.NoConfirm))
case cmdArgs.ExistsArg("c", "clean"): case cmdArgs.ExistsArg("c", "clean"):
return syncClean(ctx, run, cmdArgs, dbExecutor) return syncClean(ctx, cfg, cmdArgs, dbExecutor)
case cmdArgs.ExistsArg("l", "list"): case cmdArgs.ExistsArg("l", "list"):
return syncList(ctx, run, run.HTTPClient, cmdArgs, dbExecutor) return syncList(ctx, cfg, cfg.Runtime.HTTPClient, cmdArgs, dbExecutor)
case cmdArgs.ExistsArg("g", "groups"): case cmdArgs.ExistsArg("g", "groups"):
return run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx, return cfg.Runtime.CmdBuilder.Show(cfg.Runtime.CmdBuilder.BuildPacmanCmd(ctx,
cmdArgs, run.Cfg.Mode, settings.NoConfirm)) cmdArgs, cfg.Mode, settings.NoConfirm))
case cmdArgs.ExistsArg("i", "info"): case cmdArgs.ExistsArg("i", "info"):
return syncInfo(ctx, run, cmdArgs, targets, dbExecutor) return syncInfo(ctx, cfg, cmdArgs, targets, dbExecutor)
case cmdArgs.ExistsArg("u", "sysupgrade") || len(cmdArgs.Targets) > 0: case cmdArgs.ExistsArg("u", "sysupgrade") || len(cmdArgs.Targets) > 0:
return syncInstall(ctx, run, cmdArgs, dbExecutor) return syncInstall(ctx, cfg, cmdArgs, dbExecutor)
case cmdArgs.ExistsArg("y", "refresh"): case cmdArgs.ExistsArg("y", "refresh"):
return run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx, return cfg.Runtime.CmdBuilder.Show(cfg.Runtime.CmdBuilder.BuildPacmanCmd(ctx,
cmdArgs, run.Cfg.Mode, settings.NoConfirm)) cmdArgs, cfg.Mode, settings.NoConfirm))
} }
return nil return nil
} }
func handleRemove(ctx context.Context, run *runtime.Runtime, cmdArgs *parser.Arguments, localCache vcs.Store) error { func handleRemove(ctx context.Context, cfg *settings.Configuration, cmdArgs *parser.Arguments, localCache vcs.Store) error {
err := run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx, err := cfg.Runtime.CmdBuilder.Show(cfg.Runtime.CmdBuilder.BuildPacmanCmd(ctx,
cmdArgs, run.Cfg.Mode, settings.NoConfirm)) cmdArgs, cfg.Mode, settings.NoConfirm))
if err == nil { if err == nil {
localCache.RemovePackages(cmdArgs.Targets) localCache.RemovePackages(cmdArgs.Targets)
} }
@ -375,7 +383,7 @@ func handleRemove(ctx context.Context, run *runtime.Runtime, cmdArgs *parser.Arg
} }
// NumberMenu presents a CLI for selecting packages to install. // NumberMenu presents a CLI for selecting packages to install.
func displayNumberMenu(ctx context.Context, run *runtime.Runtime, pkgS []string, dbExecutor db.Executor, func displayNumberMenu(ctx context.Context, cfg *settings.Configuration, pkgS []string, dbExecutor db.Executor,
queryBuilder query.Builder, cmdArgs *parser.Arguments, queryBuilder query.Builder, cmdArgs *parser.Arguments,
) error { ) error {
queryBuilder.Execute(ctx, dbExecutor, pkgS) queryBuilder.Execute(ctx, dbExecutor, pkgS)
@ -389,9 +397,9 @@ func displayNumberMenu(ctx context.Context, run *runtime.Runtime, pkgS []string,
return nil return nil
} }
run.Logger.Infoln(gotext.Get("Packages to install (eg: 1 2 3, 1-3 or ^4)")) cfg.Runtime.Logger.Infoln(gotext.Get("Packages to install (eg: 1 2 3, 1-3 or ^4)"))
numberBuf, err := run.Logger.GetInput("", false) numberBuf, err := cfg.Runtime.Logger.GetInput("", false)
if err != nil { if err != nil {
return err return err
} }
@ -407,27 +415,27 @@ func displayNumberMenu(ctx context.Context, run *runtime.Runtime, pkgS []string,
cmdArgs.Targets = targets cmdArgs.Targets = targets
if len(cmdArgs.Targets) == 0 { if len(cmdArgs.Targets) == 0 {
run.Logger.Println(gotext.Get(" there is nothing to do")) fmt.Println(gotext.Get(" there is nothing to do"))
return nil return nil
} }
return syncInstall(ctx, run, cmdArgs, dbExecutor) return syncInstall(ctx, cfg, cmdArgs, dbExecutor)
} }
func syncList(ctx context.Context, run *runtime.Runtime, func syncList(ctx context.Context, cfg *settings.Configuration,
httpClient *http.Client, cmdArgs *parser.Arguments, dbExecutor db.Executor, httpClient *http.Client, cmdArgs *parser.Arguments, dbExecutor db.Executor,
) error { ) error {
aur := false aur := false
for i := len(cmdArgs.Targets) - 1; i >= 0; i-- { for i := len(cmdArgs.Targets) - 1; i >= 0; i-- {
if cmdArgs.Targets[i] == "aur" && run.Cfg.Mode.AtLeastAUR() { if cmdArgs.Targets[i] == "aur" && cfg.Mode.AtLeastAUR() {
cmdArgs.Targets = append(cmdArgs.Targets[:i], cmdArgs.Targets[i+1:]...) cmdArgs.Targets = append(cmdArgs.Targets[:i], cmdArgs.Targets[i+1:]...)
aur = true aur = true
} }
} }
if run.Cfg.Mode.AtLeastAUR() && (len(cmdArgs.Targets) == 0 || aur) { if cfg.Mode.AtLeastAUR() && (len(cmdArgs.Targets) == 0 || aur) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, run.Cfg.AURURL+"/packages.gz", http.NoBody) req, err := http.NewRequestWithContext(ctx, http.MethodGet, cfg.AURURL+"/packages.gz", http.NoBody)
if err != nil { if err != nil {
return err return err
} }
@ -445,22 +453,22 @@ func syncList(ctx context.Context, run *runtime.Runtime,
for scanner.Scan() { for scanner.Scan() {
name := scanner.Text() name := scanner.Text()
if cmdArgs.ExistsArg("q", "quiet") { if cmdArgs.ExistsArg("q", "quiet") {
run.Logger.Println(name) fmt.Println(name)
} else { } else {
run.Logger.Printf("%s %s %s", text.Magenta("aur"), text.Bold(name), text.Bold(text.Green(gotext.Get("unknown-version")))) fmt.Printf("%s %s %s", text.Magenta("aur"), text.Bold(name), text.Bold(text.Green(gotext.Get("unknown-version"))))
if dbExecutor.LocalPackage(name) != nil { if dbExecutor.LocalPackage(name) != nil {
run.Logger.Print(text.Bold(text.Blue(gotext.Get(" [Installed]")))) fmt.Print(text.Bold(text.Blue(gotext.Get(" [Installed]"))))
} }
run.Logger.Println() fmt.Println()
} }
} }
} }
if run.Cfg.Mode.AtLeastRepo() && (len(cmdArgs.Targets) != 0 || !aur) { if cfg.Mode.AtLeastRepo() && (len(cmdArgs.Targets) != 0 || !aur) {
return run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx, return cfg.Runtime.CmdBuilder.Show(cfg.Runtime.CmdBuilder.BuildPacmanCmd(ctx,
cmdArgs, run.Cfg.Mode, settings.NoConfirm)) cmdArgs, cfg.Mode, settings.NoConfirm))
} }
return nil return nil

View File

@ -19,7 +19,6 @@ import (
"github.com/Jguer/yay/v12/pkg/db/mock" "github.com/Jguer/yay/v12/pkg/db/mock"
mockaur "github.com/Jguer/yay/v12/pkg/dep/mock" mockaur "github.com/Jguer/yay/v12/pkg/dep/mock"
"github.com/Jguer/yay/v12/pkg/query" "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"
"github.com/Jguer/yay/v12/pkg/settings/exe" "github.com/Jguer/yay/v12/pkg/settings/exe"
"github.com/Jguer/yay/v12/pkg/settings/parser" "github.com/Jguer/yay/v12/pkg/settings/parser"
@ -104,19 +103,19 @@ func TestYogurtMenuAURDB(t *testing.T) {
}, },
} }
logger := text.NewLogger(io.Discard, os.Stderr, strings.NewReader("1\n"), true, "test") logger := text.NewLogger(io.Discard, os.Stderr, strings.NewReader("1\n"), true, "test")
cfg := &settings.Configuration{
run := &runtime.Runtime{ RemoveMake: "no",
Cfg: &settings.Configuration{ Runtime: &settings.Runtime{
RemoveMake: "no", Logger: logger,
CmdBuilder: cmdBuilder,
VCSStore: &vcs.Mock{},
QueryBuilder: query.NewSourceQueryBuilder(aurCache, logger, "votes", parser.ModeAny, "name",
true, false, true),
AURClient: aurCache,
}, },
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)
err = handleCmd(context.Background(), cfg, cmdArgs, db)
require.NoError(t, err) require.NoError(t, err)
wantCapture := []string{} wantCapture := []string{}

View File

@ -61,22 +61,22 @@ _yay() {
search unrequired upgrades' 'c e g i k l m n o p s t u') 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') 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 sync=('asdeps asexplicit clean dbonly downloadonly overwrite groups ignore ignoregroup
info list needed nodeps assume-installed print refresh recursive search sysupgrade aur repo' info list needed nodeps assume-installed print refresh recursive search sysupgrade'
'c g i l p s u w y a N') 'c g i l p s u w y')
upgrade=('asdeps asexplicit overwrite needed nodeps assume-installed print recursive' 'p') 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') core=('database files help query remove sync upgrade version' 'D F Q R S U V h')
##yay stuff ##yay stuff
common=('arch cachedir color config confirm dbpath debug gpgdir help hookdir logfile common=('arch cachedir color config confirm dbpath debug gpgdir help hookdir logfile
noconfirm noprogressbar noscriptlet quiet root verbose noconfirm noprogressbar noscriptlet quiet root verbose
makepkg pacman git gpg gpgflags config requestsplitn sudoloop makepkg pacman git gpg gpgflags config requestsplitn sudoloop nosudoloop
redownload noredownload redownloadall rebuild rebuildall rebuildtree norebuild sortby redownload noredownload redownloadall rebuild rebuildall rebuildtree norebuild sortby
singlelineresults doublelineresults answerclean answerdiff answeredit answerupgrade noanswerclean noanswerdiff singlelineresults doublelineresults answerclean answerdiff answeredit answerupgrade noanswerclean noanswerdiff
noansweredit noanswerupgrade cleanmenu diffmenu editmenu cleanafter keepsrc noansweredit noanswerupgrade cleanmenu diffmenu editmenu cleanafter nocleanafter
provides pgpfetch nocleanmenu nodiffmenu provides noprovides pgpfetch nopgpfetch
useask combinedupgrade aur repo makepkgconf useask nouseask combinedupgrade nocombinedupgrade aur repo makepkgconf
nomakepkgconf askremovemake askyesremovemake removemake noremovemake completioninterval aururl aurrpcurl nomakepkgconf askremovemake askyesremovemake removemake noremovemake completioninterval aururl aurrpcurl
searchby batchinstall' searchby batchinstall nobatchinstall'
'b d h q r v') 'b d h q r v')
yays=('clean gendb' 'c') yays=('clean gendb' 'c')
show=('complete defaultconfig currentconfig stats news' 'c d g s w') show=('complete defaultconfig currentconfig stats news' 'c d g s w')

View File

@ -165,8 +165,8 @@ complete -c $progname -n "$webspecific" -s u -l unvote -d 'Unvote for AUR packag
complete -c $progname -n "$webspecific" -xa "$listall" complete -c $progname -n "$webspecific" -xa "$listall"
# New options # 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" -l repo -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 complete -c $progname -n "not $noopt" -s a -l aur -d 'Assume targets are from the repositories' -f
# Yay options # Yay options
complete -c $progname -n "$yayspecific" -s c -l clean -d 'Remove unneeded dependencies' -f complete -c $progname -n "$yayspecific" -s c -l clean -d 'Remove unneeded dependencies' -f
@ -216,6 +216,9 @@ complete -c $progname -n "not $noopt" -l noanswerupgrade -d 'Unset the answer fo
complete -c $progname -n "not $noopt" -l cleanmenu -d 'Give the option to clean build PKGBUILDS' -f complete -c $progname -n "not $noopt" -l 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 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 editmenu -d 'Give the option to edit/view PKGBUILDS' -f
complete -c $progname -n "not $noopt" -l nocleanmenu -d 'Do not clean build PKGBUILDS' -f
complete -c $progname -n "not $noopt" -l nodiffmenu -d 'Do not show diffs for build files' -f
complete -c $progname -n "not $noopt" -l noeditmenu -d 'Do not edit/view PKGBUILDS' -f
complete -c $progname -n "not $noopt" -l askremovemake -d 'Ask to remove make deps after install' -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 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 removemake -d 'Remove make deps after install' -f
@ -225,17 +228,24 @@ complete -c $progname -n "not $noopt" -l bottomup -d 'Shows aur packages first a
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 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 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 devel -d 'Check -git/-svn/-hg development version' -f
complete -c $progname -n "not $noopt" -l nodevel -d 'Disable development version checking' -f
complete -c $progname -n "not $noopt" -l cleanafter -d 'Clean package sources after successful build' -f complete -c $progname -n "not $noopt" -l 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 nocleanafter -d 'Disable package sources cleaning' -f
complete -c $progname -n "not $noopt" -l timeupdate -d 'Check package modification date and version' -f complete -c $progname -n "not $noopt" -l timeupdate -d 'Check package modification date and version' -f
complete -c $progname -n "not $noopt" -l notimeupdate -d 'Check only package version change' -f
complete -c $progname -n "not $noopt" -l redownload -d 'Redownload PKGBUILD of package even if up-to-date' -f complete -c $progname -n "not $noopt" -l 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 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 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 provides -d 'Look for matching providers when searching for packages' -f
complete -c $progname -n "not $noopt" -l noprovides -d 'Just look for packages by pkgname' -f
complete -c $progname -n "not $noopt" -l pgpfetch -d 'Prompt to import PGP keys from PKGBUILDs' -f complete -c $progname -n "not $noopt" -l pgpfetch -d 'Prompt to import PGP keys from PKGBUILDs' -f
complete -c $progname -n "not $noopt" -l nopgpfetch -d 'Do not prompt to import PGP keys' -f
complete -c $progname -n "not $noopt" -l useask -d 'Automatically resolve conflicts using pacmans ask flag' -f complete -c $progname -n "not $noopt" -l useask -d 'Automatically resolve conflicts using pacmans ask flag' -f
complete -c $progname -n "not $noopt" -l nouseask -d 'Confirm conflicts manually during the install' -f
complete -c $progname -n "not $noopt" -l combinedupgrade -d 'Refresh then perform the repo and AUR upgrade together' -f complete -c $progname -n "not $noopt" -l combinedupgrade -d 'Refresh then perform the repo and AUR upgrade together' -f
complete -c $progname -n "not $noopt" -l nocombinedupgrade -d 'Perform the repo upgrade and AUR upgrade separately' -f
complete -c $progname -n "not $noopt" -l batchinstall -d 'Build multiple AUR packages then install them together' -f complete -c $progname -n "not $noopt" -l batchinstall -d 'Build multiple AUR packages then install them together' -f
complete -c $progname -n "not $noopt" -l nobatchinstall -d 'Build and install each AUR package one by one' -f
complete -c $progname -n "not $noopt" -l rebuild -d 'Always build target packages' -f complete -c $progname -n "not $noopt" -l 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 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 rebuildtree -d 'Always build all AUR packages even if installed' -f
@ -243,3 +253,4 @@ complete -c $progname -n "not $noopt" -l norebuild -d 'Skip package build if in
complete -c $progname -n "not $noopt" -l mflags -d 'Pass the following options to makepkg' -f complete -c $progname -n "not $noopt" -l 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 gpgflags -d 'Pass the following options to gpg' -f
complete -c $progname -n "not $noopt" -l sudoloop -d 'Loop sudo calls in the background to avoid timeout' -f complete -c $progname -n "not $noopt" -l sudoloop -d 'Loop sudo calls in the background to avoid timeout' -f
complete -c $progname -n "not $noopt" -l nosudoloop -d 'Do not loop sudo calls in the background' -f

View File

@ -23,7 +23,7 @@ _pacman_opts_commands=(
# options for passing to _arguments: options common to all commands # options for passing to _arguments: options common to all commands
_pacman_opts_common=( _pacman_opts_common=(
{-N,--repo}'[Assume targets are from the repositories]' '--repo[Assume targets are from the repositories]'
{-a,--aur}'[Assume targets are from the AUR]' {-a,--aur}'[Assume targets are from the AUR]'
'--aururl[Set an alternative AUR URL]:url' '--aururl[Set an alternative AUR URL]:url'
'--aurrpcurl[Set an alternative URL for the AUR /rpc endpoint]:url' '--aurrpcurl[Set an alternative URL for the AUR /rpc endpoint]:url'
@ -70,6 +70,9 @@ _pacman_opts_common=(
'--cleanmenu[Give the option to clean build PKGBUILDS]' '--cleanmenu[Give the option to clean build PKGBUILDS]'
'--diffmenu[Give the option to show diffs for build files]' '--diffmenu[Give the option to show diffs for build files]'
'--editmenu[Give the option to edit/view PKGBUILDS]' '--editmenu[Give the option to edit/view PKGBUILDS]'
"--nocleanmenu[Don't clean build PKGBUILDS]"
"--nodiffmenu[Don't show diffs for build files]"
"--noeditmenu[Don't edit/view PKGBUILDS]"
"--askremovemake[Ask to remove makedepends after install]" "--askremovemake[Ask to remove makedepends after install]"
"--askyesremovemake[Ask to remove makedepends after install(with "Y" as default)]" "--askyesremovemake[Ask to remove makedepends after install(with "Y" as default)]"
"--removemake[Remove makedepends after install]" "--removemake[Remove makedepends after install]"
@ -80,26 +83,34 @@ _pacman_opts_common=(
'--singlelineresults[List each search result on its own line]' '--singlelineresults[List each search result on its own line]'
'--doublelineresults[List each search result on two lines, like pacman]' '--doublelineresults[List each search result on two lines, like pacman]'
'--devel[Check -git/-svn/-hg development version]' '--devel[Check -git/-svn/-hg development version]'
'--nodevel[Disable development version checking]'
'--cleanafter[Clean package sources after successful build]' '--cleanafter[Clean package sources after successful build]'
'--keepsrc[Keep pkg/ and src/ after building packages]' '--nocleanafter[Disable package sources cleaning after successful build]'
'--timeupdate[Check packages modification date and version]' '--timeupdate[Check packages modification date and version]'
'--notimeupdate[Check only package version change]'
'--redownload[Always download pkgbuilds of targets]' '--redownload[Always download pkgbuilds of targets]'
'--redownloadall[Always download pkgbuilds of all AUR packages]' '--redownloadall[Always download pkgbuilds of all AUR packages]'
'--noredownload[Skip pkgbuild download if in cache and up to date]' '--noredownload[Skip pkgbuild download if in cache and up to date]'
'--rebuild[Always build target packages]' '--rebuild[Always build target packages]'
'--rebuildall[Always build all AUR packages]' '--rebuildall[Always build all AUR packages]'
'--provides[Look for matching providers when searching for packages]' '--provides[Look for matching providers when searching for packages]'
'--noprovides[Just look for packages by pkgname]'
'--pgpfetch[Prompt to import PGP keys from PKGBUILDs]' '--pgpfetch[Prompt to import PGP keys from PKGBUILDs]'
"--nopgpfetch[Don't prompt to import PGP keys]"
"--useask[Automatically resolve conflicts using pacman's ask flag]" "--useask[Automatically resolve conflicts using pacman's ask flag]"
'--nouseask[Confirm conflicts manually during the install]'
'--combinedupgrade[Refresh then perform the repo and AUR upgrade together]' '--combinedupgrade[Refresh then perform the repo and AUR upgrade together]'
'--nocombinedupgrade[Perform the repo upgrade and AUR upgrade separately]'
'--rebuildtree[Always build all AUR packages even if installed]' '--rebuildtree[Always build all AUR packages even if installed]'
'--norebuild[Skip package build if in cache and up to date]' '--norebuild[Skip package build if in cache and up to date]'
'--mflags[Pass arguments to makepkg]:mflags' '--mflags[Pass arguments to makepkg]:mflags'
'--gpgflags[Pass arguments to gpg]:gpgflags' '--gpgflags[Pass arguments to gpg]:gpgflags'
'--sudoloop[Loop sudo calls in the background to avoid timeout]' '--sudoloop[Loop sudo calls in the background to avoid timeout]'
'--nosudoloop[Do not loop sudo calls in the background]'
'--searchby[Search for packages using a specified field]' '--searchby[Search for packages using a specified field]'
'--sortby[Sort AUR results by a specific field during search]' '--sortby[Sort AUR results by a specific field during search]'
'--batchinstall[Build multiple AUR packages then install them together]' '--batchinstall[Build multiple AUR packages then install them together]'
'--nobatchinstall[Build and install each AUR package one by one]'
) )
# options for passing to _arguments: options for --upgrade commands # options for passing to _arguments: options for --upgrade commands

View File

@ -63,7 +63,7 @@ Yay will also remove cached data about devel packages.
.SH NEW OPTIONS .SH NEW OPTIONS
.TP .TP
.B \-N, \-\-repo .B \-\-repo
Assume all targets are from the repositories. Additionally Actions such as Assume all targets are from the repositories. Additionally Actions such as
sysupgrade will only act on repository packages. sysupgrade will only act on repository packages.
@ -97,10 +97,6 @@ used when migrating to Yay from another AUR helper.
.B \-c, \-\-clean .B \-c, \-\-clean
Remove unneeded dependencies. 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) .SH SHOW OPTIONS (APPLY TO \-P AND \-\-show)
.TP .TP
.B \-c, \-\-complete .B \-c, \-\-complete
@ -297,9 +293,6 @@ Unset the answer for the upgrade menu.
Show the clean menu. This menu gives you the chance to fully delete the 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. 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 .TP
.B \-\-diffmenu .B \-\-diffmenu
Show the diff menu. This menu gives you the option to view diffs from Show the diff menu. This menu gives you the option to view diffs from
@ -317,6 +310,18 @@ before building.
\fBWarning\fR: Yay resolves dependencies ahead of time via the RPC. It is not \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. recommended to edit pkgbuild variables unless you know what you are doing.
.TP
.B \-\-nocleanmenu
Do not show the clean menu.
.TP
.B \-\-nodiffmenu
Do not show the diff menu.
.TP
.B \-\-noeditmenu
Do not show the edit menu.
.TP .TP
.B \-\-askremovemake .B \-\-askremovemake
Ask to remove makedepends after installing packages. Ask to remove makedepends after installing packages.
@ -363,8 +368,9 @@ checked almost instantly and not require the original pkgbuild to be downloaded.
The slower pacaur-like devel checks can be implemented manually by piping The slower pacaur-like devel checks can be implemented manually by piping
a list of packages into yay (see \fBexamples\fR). a list of packages into yay (see \fBexamples\fR).
If 'devel' is enabled in the configuration file, you can temporarily disable it by .TP
using '--devel=false' on the command line .B \-\-nodevel
Do not check for development packages updates during sysupgrade.
.TP .TP
.B \-\-cleanafter .B \-\-cleanafter
@ -375,18 +381,26 @@ This allows VCS packages to easily pull an update
instead of having to reclone the entire repo. instead of having to reclone the entire repo.
.TP .TP
.B \-\-keepsrc .B \-\-nocleanafter
Keep pkg/ and src/ after building packages Do not remove package sources after successful Install.
.TP .TP
.B \-\-timeupdate .B \-\-timeupdate
During sysupgrade also compare the build time of installed packages against During sysupgrade also compare the build time of installed packages against
the last modification time of each package's AUR page. the last modification time of each package's AUR page.
.TP
.B \-\-notimeupdate
Do not consider build times during sysupgrade.
.TP .TP
.B \-\-separatesources .B \-\-separatesources
Separate query results by source, AUR and sync Separate query results by source, AUR and sync
.TP
.B \-\-noseparatesources
Do not separate query results by source for searching
.TP .TP
.B \-\-redownload .B \-\-redownload
Always download pkgbuilds of targets even when a copy is available in cache. Always download pkgbuilds of targets even when a copy is available in cache.
@ -407,11 +421,23 @@ Look for matching providers when searching for AUR packages. When multiple
providers are found a menu will appear prompting you to pick one. This providers are found a menu will appear prompting you to pick one. This
increases dependency resolve time although this should not be noticeable. increases dependency resolve time although this should not be noticeable.
.TP
.B \-\-noprovides
Do not look for matching providers when searching for AUR packages.
Yay will never show its provider menu but Pacman will still show its
provider menu for repo packages.
.TP .TP
.B \-\-pgpfetch .B \-\-pgpfetch
Prompt to import unknown PGP keys from the \fBvalidpgpkeys\fR field of each Prompt to import unknown PGP keys from the \fBvalidpgpkeys\fR field of each
PKGBUILD. PKGBUILD.
.TP
.B \-\-nopgpfetch
Do not prompt to import unknown PGP keys. This is likely to cause a build
failure unless using options such as \fB\-\-skippgpcheck\fR or a customized
gpg config\%.
.TP .TP
.B \-\-useask .B \-\-useask
Use pacman's --ask flag to automatically confirm package conflicts. Yay lists Use pacman's --ask flag to automatically confirm package conflicts. Yay lists
@ -419,6 +445,11 @@ 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. a conflict, causing a package to be removed without the user's confirmation.
However, this is very unlikely. However, this is very unlikely.
.TP
.B \-\-nouseask
Manually resolve package conflicts during the install. Packages which do not
conflict will not need to be confined manually.
.TP .TP
.B \-\-combinedupgrade .B \-\-combinedupgrade
During sysupgrade, Yay will first perform a refresh, then show During sysupgrade, Yay will first perform a refresh, then show
@ -430,6 +461,12 @@ 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 the user's responsibility to either resolve the reason Yay exited or run
a sysupgrade through pacman directly. a sysupgrade through pacman directly.
.TP
.B \-\-nocombinedupgrade
During sysupgrade, Pacman \-Syu will be called, then the AUR upgrade will
start. This means the upgrade menu and pkgbuild review will be performed
after the sysupgrade has finished.
.TP .TP
.B \-\-batchinstall .B \-\-batchinstall
When building and installing AUR packages instead of installing each package When building and installing AUR packages instead of installing each package
@ -437,6 +474,10 @@ 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 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. another package, install all the packages in the install queue.
.TP
.B \-\-nobatchinstall
Always install AUR packages immediately after building them.
.TP .TP
.B \-\-rebuild .B \-\-rebuild
Always build target packages even when a copy is available in cache. Always build target packages even when a copy is available in cache.
@ -490,6 +531,10 @@ separated list that is quoted by the shell.
Loop sudo calls in the background to prevent sudo from timing out during long Loop sudo calls in the background to prevent sudo from timing out during long
builds. builds.
.TP
.B \-\-nosudoloop
Do not loop sudo calls in the background.
.SH EXAMPLES .SH EXAMPLES
.TP .TP
yay \fIfoo\fR yay \fIfoo\fR

View File

@ -7,3 +7,56 @@ import (
) )
var ErrPackagesNotFound = errors.New(gotext.Get("could not find all required packages")) var ErrPackagesNotFound = errors.New(gotext.Get("could not find all required packages"))
type NoPkgDestsFoundError struct {
dir string
}
func (e *NoPkgDestsFoundError) Error() string {
return gotext.Get("could not find any package archives listed in %s", e.dir)
}
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 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 PkgDestNotInListError struct {
name string
}
func (e *PkgDestNotInListError) Error() string {
return gotext.Get("could not find PKGDEST for: %s", e.name)
}
type FailedIgnoredPkgError struct {
pkgErrors map[string]error
}
func (e *FailedIgnoredPkgError) Error() string {
msg := gotext.Get("Failed to install the following packages. Manual intervention is required:")
for pkg, err := range e.pkgErrors {
msg += "\n" + pkg + " - " + err.Error()
}
return msg
}

26
get.go
View File

@ -11,23 +11,25 @@ import (
"github.com/leonelquinteros/gotext" "github.com/leonelquinteros/gotext"
"github.com/Jguer/yay/v12/pkg/download" "github.com/Jguer/yay/v12/pkg/download"
"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/settings/parser"
"github.com/Jguer/yay/v12/pkg/text" "github.com/Jguer/yay/v12/pkg/text"
) )
// yay -Gp. // yay -Gp.
func printPkgbuilds(dbExecutor download.DBSearcher, aurClient aur.QueryClient, func printPkgbuilds(dbExecutor download.DBSearcher, aurClient aur.QueryClient, httpClient *http.Client, targets []string,
httpClient *http.Client, logger *text.Logger, targets []string,
mode parser.TargetMode, aurURL string, mode parser.TargetMode, aurURL string,
) error { ) error {
pkgbuilds, err := download.PKGBUILDs(dbExecutor, aurClient, httpClient, logger, targets, aurURL, mode) pkgbuilds, err := download.PKGBUILDs(dbExecutor, aurClient, httpClient, targets, aurURL, mode)
if err != nil { if err != nil {
logger.Errorln(err) text.Errorln(err)
} }
for target, pkgbuild := range pkgbuilds { if len(pkgbuilds) != 0 {
logger.Printf("\n\n# %s\n\n%s", target, string(pkgbuild)) for target, pkgbuild := range pkgbuilds {
fmt.Printf("\n\n# %s\n\n", target)
fmt.Print(string(pkgbuild))
}
} }
if len(pkgbuilds) != len(targets) { if len(pkgbuilds) != len(targets) {
@ -39,7 +41,7 @@ func printPkgbuilds(dbExecutor download.DBSearcher, aurClient aur.QueryClient,
} }
} }
logger.Warnln(gotext.Get("Unable to find the following packages:"), " ", strings.Join(missing, ", ")) text.Warnln(gotext.Get("Unable to find the following packages:"), " ", strings.Join(missing, ", "))
return fmt.Errorf("") return fmt.Errorf("")
} }
@ -49,7 +51,7 @@ func printPkgbuilds(dbExecutor download.DBSearcher, aurClient aur.QueryClient,
// yay -G. // yay -G.
func getPkgbuilds(ctx context.Context, dbExecutor download.DBSearcher, aurClient aur.QueryClient, func getPkgbuilds(ctx context.Context, dbExecutor download.DBSearcher, aurClient aur.QueryClient,
run *runtime.Runtime, targets []string, force bool, config *settings.Configuration, targets []string, force bool,
) error { ) error {
wd, err := os.Getwd() wd, err := os.Getwd()
if err != nil { if err != nil {
@ -57,9 +59,9 @@ func getPkgbuilds(ctx context.Context, dbExecutor download.DBSearcher, aurClient
} }
cloned, errD := download.PKGBUILDRepos(ctx, dbExecutor, aurClient, cloned, errD := download.PKGBUILDRepos(ctx, dbExecutor, aurClient,
run.CmdBuilder, run.Logger, targets, run.Cfg.Mode, run.Cfg.AURURL, wd, force) config.Runtime.CmdBuilder, targets, config.Mode, config.AURURL, wd, force)
if errD != nil { if errD != nil {
run.Logger.Errorln(errD) text.Errorln(errD)
} }
if len(targets) != len(cloned) { if len(targets) != len(cloned) {
@ -71,7 +73,7 @@ func getPkgbuilds(ctx context.Context, dbExecutor download.DBSearcher, aurClient
} }
} }
run.Logger.Warnln(gotext.Get("Unable to find the following packages:"), " ", strings.Join(missing, ", ")) text.Warnln(gotext.Get("Unable to find the following packages:"), " ", strings.Join(missing, ", "))
err = fmt.Errorf("") err = fmt.Errorf("")
} }

38
go.mod
View File

@ -2,34 +2,40 @@ module github.com/Jguer/yay/v12
require ( require (
github.com/Jguer/aur v1.2.3 github.com/Jguer/aur v1.2.3
github.com/Jguer/go-alpm/v2 v2.2.2 github.com/Jguer/go-alpm/v2 v2.2.0
github.com/Jguer/votar v1.0.0 github.com/Jguer/votar v1.0.0
github.com/Morganamilo/go-pacmanconf v0.0.0-20210502114700-cff030e927a5 github.com/Morganamilo/go-pacmanconf v0.0.0-20210502114700-cff030e927a5
github.com/Morganamilo/go-srcinfo v1.0.0 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/bradleyjkemp/cupaloy v2.3.0+incompatible
github.com/deckarep/golang-set/v2 v2.8.0 github.com/leonelquinteros/gotext v1.5.2
github.com/hashicorp/go-multierror v1.1.1 github.com/stretchr/testify v1.8.3
github.com/leonelquinteros/gotext v1.7.2 golang.org/x/sys v0.9.0
github.com/stretchr/testify v1.10.0 golang.org/x/term v0.9.0
golang.org/x/net v0.41.0 golang.org/x/text v0.10.0 // indirect
golang.org/x/sys v0.33.0
golang.org/x/term v0.32.0
gopkg.in/h2non/gock.v1 v1.1.2 gopkg.in/h2non/gock.v1 v1.1.2
) )
require ( require (
github.com/adrg/strutil v0.3.0
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect github.com/pkg/errors v0.9.1
github.com/itchyny/gojq v0.12.17 // indirect
github.com/itchyny/timefmt-go v0.1.6 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/ohler55/ojg v1.26.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
) )
go 1.23.5 require (
github.com/deckarep/golang-set/v2 v2.3.0
github.com/itchyny/gojq v0.12.13 // indirect
github.com/itchyny/timefmt-go v0.1.5 // indirect
github.com/ohler55/ojg v1.18.7 // indirect
)
toolchain go1.24.0 require github.com/hashicorp/go-multierror v1.1.1
require (
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
)
go 1.19

77
go.sum
View File

@ -1,15 +1,15 @@
github.com/Jguer/aur v1.2.3 h1:D+OGgLxnAnZnw88DsRvnRQsn0Poxsy9ng7pBcsA0krM= github.com/Jguer/aur v1.2.3 h1:D+OGgLxnAnZnw88DsRvnRQsn0Poxsy9ng7pBcsA0krM=
github.com/Jguer/aur v1.2.3/go.mod h1:Dahvb6L1yr0rR7svyYSDwaRJoQMeyvJblwJ3QH/7CUs= github.com/Jguer/aur v1.2.3/go.mod h1:Dahvb6L1yr0rR7svyYSDwaRJoQMeyvJblwJ3QH/7CUs=
github.com/Jguer/go-alpm/v2 v2.2.2 h1:sPwUoZp1X5Tw6K6Ba1lWvVJfcgVNEGVcxARLBttZnC0= github.com/Jguer/go-alpm/v2 v2.2.0 h1:+sh4UEZwTpcAO+vHdySsnLZSnLZIBun8j85BbPExSlg=
github.com/Jguer/go-alpm/v2 v2.2.2/go.mod h1:lfe8gSe83F/KERaQvEfrSqQ4n+8bES+ZIyKWR/gm3MI= github.com/Jguer/go-alpm/v2 v2.2.0/go.mod h1:uLQcTMNM904dRiGU+/JDtDdd7Nd8mVbEVaHjhmziT7w=
github.com/Jguer/votar v1.0.0 h1:drPYpV5Py5BeAQS8xezmT6uCEfLzotNjLf5yfmlHKTg= github.com/Jguer/votar v1.0.0 h1:drPYpV5Py5BeAQS8xezmT6uCEfLzotNjLf5yfmlHKTg=
github.com/Jguer/votar v1.0.0/go.mod h1:rc6vgVlTqNjI4nAnPbDTbdxw/N7kXkbB8BcUDjeFbYQ= 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 h1:TMscPjkb1ThXN32LuFY5bEYIcXZx3YlwzhS1GxNpn/c=
github.com/Morganamilo/go-pacmanconf v0.0.0-20210502114700-cff030e927a5/go.mod h1:Hk55m330jNiwxRodIlMCvw5iEyoRUCIY64W1p9D+tHc= 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 h1:Wh4nEF+HJWo+29hnxM18Q2hi+DUf0GejS13+Wg+dzmI=
github.com/Morganamilo/go-srcinfo v1.0.0/go.mod h1:MP6VGY1NNpVUmYIEgoM9acix95KQqIRyqQ0hCLsyYUY= 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.0 h1:bi/HB2zQbDihC8lxvATDTDzkT4bG7PATtVnDYp5rvq4=
github.com/adrg/strutil v0.3.1/go.mod h1:8h90y18QLrs11IBffcGX3NW/GFBXCMcNg4M7H6MspPA= github.com/adrg/strutil v0.3.0/go.mod h1:Jz0wzBVE6Uiy9wxo62YEqEY1Nwto3QlLl1Il5gkLKWU=
github.com/alexflint/go-arg v1.4.3/go.mod h1:3PZ/wp/8HuqRZMUUgu7I+e1qcpUbvmS258mRXkFH4IA= github.com/alexflint/go-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/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 h1:UafIjBvWQmS9i/xRg+CamMrnLTKNzo+bdmT/oH34c2Y=
@ -17,8 +17,8 @@ github.com/bradleyjkemp/cupaloy v2.3.0+incompatible/go.mod h1:Au1Xw1sgaJ5iSFktEh
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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.3.0 h1:qs18EKUfHm2X9fA50Mr/M5hccg2tNnVqsiBImnyDs0g=
github.com/deckarep/golang-set/v2 v2.8.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= github.com/deckarep/golang-set/v2 v2.3.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= 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.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@ -26,39 +26,60 @@ github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 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 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/itchyny/gojq v0.12.17 h1:8av8eGduDb5+rvEdaOO+zQUjA04MS0m3Ps8HiD+fceg= github.com/itchyny/gojq v0.12.13 h1:IxyYlHYIlspQHHTE0f3cJF0NKDMfajxViuhBLnHd/QU=
github.com/itchyny/gojq v0.12.17/go.mod h1:WBrEMkgAfAGO1LUcGOckBl5O726KPp+OlkKug0I/FEY= github.com/itchyny/gojq v0.12.13/go.mod h1:JzwzAqenfhrPUuwbmEz3nu3JQmFLlQTQMUcOdnu/Sf4=
github.com/itchyny/timefmt-go v0.1.6 h1:ia3s54iciXDdzWzwaVKXZPbiXzxxnv1SPGFfM/myJ5Q= github.com/itchyny/timefmt-go v0.1.5 h1:G0INE2la8S6ru/ZI5JecgyzbbJNs5lG1RcBqa7Jm6GE=
github.com/itchyny/timefmt-go v0.1.6/go.mod h1:RRDZYC5s9ErkjQvTvvU7keJjxUYzIISJGxm9/mAERQg= github.com/itchyny/timefmt-go v0.1.5/go.mod h1:nEP7L+2YmAbT2kZ2HfSs1d8Xtw9LY8D2stDBckWakZ8=
github.com/leonelquinteros/gotext v1.7.2 h1:bDPndU8nt+/kRo1m4l/1OXiiy2v7Z7dfPQ9+YP7G1Mc= github.com/leonelquinteros/gotext v1.5.2 h1:T2y6ebHli+rMBCjcJlHTXyUrgXqsKBhl/ormgvt7lPo=
github.com/leonelquinteros/gotext v1.7.2/go.mod h1:9/haCkm5P7Jay1sxKDGJ5WIg4zkz8oZKw4ekNpALob8= github.com/leonelquinteros/gotext v1.5.2/go.mod h1:AT4NpQrOmyj1L/+hLja6aR0lk81yYYL4ePnj2kp7d6M=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= 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/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 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4=
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
github.com/ohler55/ojg v1.26.1 h1:J5TaLmVEuvnpVH7JMdT1QdbpJU545Yp6cKiCO4aQILc= github.com/ohler55/ojg v1.18.7 h1:sC7zy0usEiWa6bvx3NU1yZH4kCA2F3Qzs6iiDX4+xdk=
github.com/ohler55/ojg v1.26.1/go.mod h1:gQhDVpQLqrmnd2eqGAvJtn+NfKoYJbe/A4Sj3/Vro4o= github.com/ohler55/ojg v1.18.7/go.mod h1:uHcD1ErbErC27Zhb5Df2jUjbseLLcmOCo6oxSr3jZxo=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 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/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.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 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.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.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.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.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.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s=
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.9.0 h1:GRRCnKYhdQrD8kfRAdQ6Zcw1P0OcELxGLKJvtjVMZ28=
golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58=
golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 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/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 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY=

View File

@ -1,4 +1,4 @@
package build package main
import ( import (
"context" "context"
@ -16,6 +16,164 @@ import (
"github.com/Jguer/yay/v12/pkg/vcs" "github.com/Jguer/yay/v12/pkg/vcs"
) )
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 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 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))
}
func parsePackageList(ctx context.Context, cmdBuilder exe.ICmdBuilder,
dir string,
) (pkgdests map[string]string, pkgVersion string, err error) {
stdout, stderr, err := cmdBuilder.Capture(
cmdBuilder.BuildMakepkgCmd(ctx, dir, "--packagelist"))
if err != nil {
return nil, "", fmt.Errorf("%s %w", stderr, err)
}
lines := strings.Split(stdout, "\n")
pkgdests = make(map[string]string)
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
}
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
}
func installPkgArchive(ctx context.Context, func installPkgArchive(ctx context.Context,
cmdBuilder exe.ICmdBuilder, cmdBuilder exe.ICmdBuilder,
mode parser.TargetMode, mode parser.TargetMode,
@ -70,95 +228,3 @@ func setInstallReason(ctx context.Context,
return asexp(ctx, cmdBuilder, mode, cmdArgs, exps) 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) {
stdout, stderr, err := cmdBuilder.Capture(
cmdBuilder.BuildMakepkgCmd(ctx, dir, "--packagelist"))
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
}

View File

@ -4,7 +4,6 @@ package main
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"os" "os"
"path/filepath" "path/filepath"
@ -13,17 +12,20 @@ import (
"github.com/Jguer/yay/v12/pkg/db" "github.com/Jguer/yay/v12/pkg/db"
"github.com/Jguer/yay/v12/pkg/dep" "github.com/Jguer/yay/v12/pkg/dep"
"github.com/Jguer/yay/v12/pkg/multierror" "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"
"github.com/Jguer/yay/v12/pkg/settings/exe" "github.com/Jguer/yay/v12/pkg/settings/exe"
"github.com/Jguer/yay/v12/pkg/settings/parser" "github.com/Jguer/yay/v12/pkg/settings/parser"
"github.com/Jguer/yay/v12/pkg/sync" "github.com/Jguer/yay/v12/pkg/topo"
gosrc "github.com/Morganamilo/go-srcinfo" gosrc "github.com/Morganamilo/go-srcinfo"
"github.com/leonelquinteros/gotext" "github.com/leonelquinteros/gotext"
"github.com/pkg/errors"
) )
var ErrNoBuildFiles = errors.New(gotext.Get("cannot find PKGBUILD and .SRCINFO in directory")) var (
ErrInstallRepoPkgs = errors.New(gotext.Get("error installing repo packages"))
ErrNoBuildFiles = errors.New(gotext.Get("cannot find PKGBUILD and .SRCINFO in directory"))
)
func srcinfoExists(ctx context.Context, func srcinfoExists(ctx context.Context,
cmdBuilder exe.ICmdBuilder, targetDir string, cmdBuilder exe.ICmdBuilder, targetDir string,
@ -43,10 +45,6 @@ func srcinfoExists(ctx context.Context,
return fmt.Errorf("unable to generate .SRCINFO: %w - %s", err, stderr) 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 { if err := os.WriteFile(srcInfoDir, []byte(srcinfo), 0o600); err != nil {
return fmt.Errorf("unable to write .SRCINFO: %w", err) return fmt.Errorf("unable to write .SRCINFO: %w", err)
} }
@ -59,12 +57,12 @@ func srcinfoExists(ctx context.Context,
func installLocalPKGBUILD( func installLocalPKGBUILD(
ctx context.Context, ctx context.Context,
run *runtime.Runtime, config *settings.Configuration,
cmdArgs *parser.Arguments, cmdArgs *parser.Arguments,
dbExecutor db.Executor, dbExecutor db.Executor,
) error { ) error {
aurCache := run.AURClient aurCache := config.Runtime.AURClient
noCheck := strings.Contains(run.Cfg.MFlags, "--nocheck") noCheck := strings.Contains(config.MFlags, "--nocheck")
if len(cmdArgs.Targets) < 1 { if len(cmdArgs.Targets) < 1 {
return errors.New(gotext.Get("no target directories specified")) return errors.New(gotext.Get("no target directories specified"))
@ -72,13 +70,13 @@ func installLocalPKGBUILD(
srcInfos := map[string]*gosrc.Srcinfo{} srcInfos := map[string]*gosrc.Srcinfo{}
for _, targetDir := range cmdArgs.Targets { for _, targetDir := range cmdArgs.Targets {
if err := srcinfoExists(ctx, run.CmdBuilder, targetDir); err != nil { if err := srcinfoExists(ctx, config.Runtime.CmdBuilder, targetDir); err != nil {
return err return err
} }
pkgbuild, err := gosrc.ParseFile(filepath.Join(targetDir, ".SRCINFO")) pkgbuild, err := gosrc.ParseFile(filepath.Join(targetDir, ".SRCINFO"))
if err != nil { if err != nil {
return fmt.Errorf("%s: %w", gotext.Get("failed to parse .SRCINFO"), err) return errors.Wrap(err, gotext.Get("failed to parse .SRCINFO"))
} }
srcInfos[targetDir] = pkgbuild srcInfos[targetDir] = pkgbuild
@ -86,13 +84,14 @@ func installLocalPKGBUILD(
grapher := dep.NewGrapher(dbExecutor, aurCache, false, settings.NoConfirm, grapher := dep.NewGrapher(dbExecutor, aurCache, false, settings.NoConfirm,
cmdArgs.ExistsDouble("d", "nodeps"), noCheck, cmdArgs.ExistsArg("needed"), cmdArgs.ExistsDouble("d", "nodeps"), noCheck, cmdArgs.ExistsArg("needed"),
run.Logger.Child("grapher")) config.Runtime.Logger.Child("grapher"))
graph, err := grapher.GraphFromSrcInfos(ctx, nil, srcInfos) graph := topo.New[string, *dep.InstallInfo]()
graph, err := grapher.GraphFromSrcInfos(ctx, graph, srcInfos)
if err != nil { if err != nil {
return err return err
} }
opService := sync.NewOperationService(ctx, dbExecutor, run) opService := NewOperationService(ctx, config, dbExecutor)
multiErr := &multierror.MultiError{} multiErr := &multierror.MultiError{}
targets := graph.TopoSortedLayerMap(func(name string, ii *dep.InstallInfo) error { targets := graph.TopoSortedLayerMap(func(name string, ii *dep.InstallInfo) error {
if ii.Source == dep.Missing { if ii.Source == dep.Missing {
@ -104,5 +103,5 @@ func installLocalPKGBUILD(
if err := multiErr.Return(); err != nil { if err := multiErr.Return(); err != nil {
return err return err
} }
return opService.Run(ctx, run, cmdArgs, targets, []string{}) return opService.Run(ctx, cmdArgs, targets, []string{})
} }

View File

@ -6,7 +6,6 @@ package main
import ( import (
"context" "context"
"fmt" "fmt"
"io"
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
@ -20,18 +19,12 @@ import (
"github.com/Jguer/yay/v12/pkg/db/mock" "github.com/Jguer/yay/v12/pkg/db/mock"
mockaur "github.com/Jguer/yay/v12/pkg/dep/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"
"github.com/Jguer/yay/v12/pkg/settings/exe" "github.com/Jguer/yay/v12/pkg/settings/exe"
"github.com/Jguer/yay/v12/pkg/settings/parser" "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/yay/v12/pkg/vcs"
) )
func newTestLogger() *text.Logger {
return text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), true, "test")
}
func TestIntegrationLocalInstall(t *testing.T) { func TestIntegrationLocalInstall(t *testing.T) {
makepkgBin := t.TempDir() + "/makepkg" makepkgBin := t.TempDir() + "/makepkg"
pacmanBin := t.TempDir() + "/pacman" pacmanBin := t.TempDir() + "/pacman"
@ -56,14 +49,16 @@ func TestIntegrationLocalInstall(t *testing.T) {
} }
wantShow := []string{ wantShow := []string{
"makepkg --verifysource --skippgpcheck -f -Cc", "makepkg --verifysource -Ccf",
"pacman -S --config /etc/pacman.conf -- community/dotnet-sdk-6.0 community/dotnet-runtime-6.0", "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", "pacman -D -q --asdeps --config /etc/pacman.conf -- dotnet-runtime-6.0 dotnet-sdk-6.0",
"makepkg --nobuild -f -C --ignorearch", "makepkg --nobuild -fC --ignorearch",
"makepkg -c --nobuild --noextract --ignorearch",
"makepkg --nobuild -fC --ignorearch",
"makepkg -c --nobuild --noextract --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 -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", "pacman -D -q --asexplicit --config /etc/pacman.conf -- jellyfin-server jellyfin-web",
"makepkg --nobuild -f -C --ignorearch", "makepkg --nobuild -fC --ignorearch",
"makepkg -c --nobuild --noextract --ignorearch", "makepkg -c --nobuild --noextract --ignorearch",
"pacman -U --config /etc/pacman.conf -- /testdir/jellyfin-10.8.4-1-x86_64.pkg.tar.zst", "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", "pacman -D -q --asexplicit --config /etc/pacman.conf -- jellyfin",
@ -74,6 +69,7 @@ func TestIntegrationLocalInstall(t *testing.T) {
"git -C testdata/jfin git reset --hard HEAD", "git -C testdata/jfin git reset --hard HEAD",
"git -C testdata/jfin git merge --no-edit --ff", "git -C testdata/jfin git merge --no-edit --ff",
"makepkg --packagelist", "makepkg --packagelist",
"makepkg --packagelist",
} }
captureOverride := func(cmd *exec.Cmd) (stdout string, stderr string, err error) { captureOverride := func(cmd *exec.Cmd) (stdout string, stderr string, err error) {
@ -142,25 +138,24 @@ func TestIntegrationLocalInstall(t *testing.T) {
return nil return nil
}, },
LocalPackageFn: func(s string) mock.IPackage { return nil }, LocalPackageFn: func(s string) mock.IPackage { return nil },
InstalledRemotePackageNamesFn: func() []string { return []string{} },
} }
run := &runtime.Runtime{ config := &settings.Configuration{
Cfg: &settings.Configuration{ RemoveMake: "no",
RemoveMake: "no", Runtime: &settings.Runtime{
}, Logger: NewTestLogger(),
Logger: newTestLogger(), CmdBuilder: cmdBuilder,
CmdBuilder: cmdBuilder, VCSStore: &vcs.Mock{},
VCSStore: &vcs.Mock{}, AURClient: &mockaur.MockAUR{
AURClient: &mockaur.MockAUR{ GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) { return []aur.Pkg{}, nil
return []aur.Pkg{}, nil },
}, },
}, },
} }
err = handleCmd(context.Background(), run, cmdArgs, db) err = handleCmd(context.Background(), config, cmdArgs, db)
require.NoError(t, err) require.NoError(t, err)
require.Len(t, mockRunner.ShowCalls, len(wantShow)) require.Len(t, mockRunner.ShowCalls, len(wantShow))
@ -267,19 +262,20 @@ func TestIntegrationLocalInstallMissingDep(t *testing.T) {
LocalPackageFn: func(string) mock.IPackage { return nil }, LocalPackageFn: func(string) mock.IPackage { return nil },
} }
run := &runtime.Runtime{ config := &settings.Configuration{
Cfg: &settings.Configuration{}, Runtime: &settings.Runtime{
Logger: newTestLogger(), Logger: NewTestLogger(),
CmdBuilder: cmdBuilder, CmdBuilder: cmdBuilder,
VCSStore: &vcs.Mock{}, VCSStore: &vcs.Mock{},
AURClient: &mockaur.MockAUR{ AURClient: &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) { GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return []aur.Pkg{}, nil return []aur.Pkg{}, nil
},
}, },
}, },
} }
err = handleCmd(context.Background(), run, cmdArgs, db) err = handleCmd(context.Background(), config, cmdArgs, db)
require.ErrorContains(t, err, wantErr.Error()) require.ErrorContains(t, err, wantErr.Error())
require.Len(t, mockRunner.ShowCalls, len(wantShow)) require.Len(t, mockRunner.ShowCalls, len(wantShow))
@ -321,12 +317,14 @@ func TestIntegrationLocalInstallNeeded(t *testing.T) {
} }
wantShow := []string{ wantShow := []string{
"makepkg --verifysource --skippgpcheck -f -Cc", "makepkg --verifysource -Ccf",
"pacman -S --config /etc/pacman.conf -- community/dotnet-sdk-6.0 community/dotnet-runtime-6.0", "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", "pacman -D -q --asdeps --config /etc/pacman.conf -- dotnet-runtime-6.0 dotnet-sdk-6.0",
"makepkg --nobuild -f -C --ignorearch", "makepkg --nobuild -fC --ignorearch",
"makepkg -c --nobuild --noextract --ignorearch", "makepkg -c --nobuild --noextract --ignorearch",
"makepkg --nobuild -f -C --ignorearch", "makepkg --nobuild -fC --ignorearch",
"makepkg -c --nobuild --noextract --ignorearch",
"makepkg --nobuild -fC --ignorearch",
"makepkg -c --nobuild --noextract --ignorearch", "makepkg -c --nobuild --noextract --ignorearch",
} }
@ -335,6 +333,7 @@ func TestIntegrationLocalInstallNeeded(t *testing.T) {
"git -C testdata/jfin git reset --hard HEAD", "git -C testdata/jfin git reset --hard HEAD",
"git -C testdata/jfin git merge --no-edit --ff", "git -C testdata/jfin git merge --no-edit --ff",
"makepkg --packagelist", "makepkg --packagelist",
"makepkg --packagelist",
} }
captureOverride := func(cmd *exec.Cmd) (stdout string, stderr string, err error) { captureOverride := func(cmd *exec.Cmd) (stdout string, stderr string, err error) {
@ -418,24 +417,23 @@ func TestIntegrationLocalInstallNeeded(t *testing.T) {
return nil return nil
}, },
InstalledRemotePackageNamesFn: func() []string { return []string{} },
} }
run := &runtime.Runtime{ config := &settings.Configuration{
Cfg: &settings.Configuration{ RemoveMake: "no",
RemoveMake: "no", Runtime: &settings.Runtime{
}, Logger: NewTestLogger(),
Logger: newTestLogger(), CmdBuilder: cmdBuilder,
CmdBuilder: cmdBuilder, VCSStore: &vcs.Mock{},
VCSStore: &vcs.Mock{}, AURClient: &mockaur.MockAUR{
AURClient: &mockaur.MockAUR{ GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) { return []aur.Pkg{}, nil
return []aur.Pkg{}, nil },
}, },
}, },
} }
err = handleCmd(context.Background(), run, cmdArgs, db) err = handleCmd(context.Background(), config, cmdArgs, db)
require.NoError(t, err) require.NoError(t, err)
require.Len(t, mockRunner.ShowCalls, len(wantShow), "show calls: %v", mockRunner.ShowCalls) require.Len(t, mockRunner.ShowCalls, len(wantShow), "show calls: %v", mockRunner.ShowCalls)
@ -486,14 +484,16 @@ func TestIntegrationLocalInstallGenerateSRCINFO(t *testing.T) {
} }
wantShow := []string{ wantShow := []string{
"makepkg --verifysource --skippgpcheck -f -Cc", "makepkg --verifysource -Ccf",
"pacman -S --config /etc/pacman.conf -- community/dotnet-sdk-6.0 community/dotnet-runtime-6.0", "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", "pacman -D -q --asdeps --config /etc/pacman.conf -- dotnet-runtime-6.0 dotnet-sdk-6.0",
"makepkg --nobuild -f -C --ignorearch", "makepkg --nobuild -fC --ignorearch",
"makepkg -c --nobuild --noextract --ignorearch",
"makepkg --nobuild -fC --ignorearch",
"makepkg -c --nobuild --noextract --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 -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", "pacman -D -q --asexplicit --config /etc/pacman.conf -- jellyfin-server jellyfin-web",
"makepkg --nobuild -f -C --ignorearch", "makepkg --nobuild -fC --ignorearch",
"makepkg -c --nobuild --noextract --ignorearch", "makepkg -c --nobuild --noextract --ignorearch",
"pacman -U --config /etc/pacman.conf -- /testdir/jellyfin-10.8.4-1-x86_64.pkg.tar.zst", "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", "pacman -D -q --asexplicit --config /etc/pacman.conf -- jellyfin",
@ -505,6 +505,7 @@ func TestIntegrationLocalInstallGenerateSRCINFO(t *testing.T) {
"git -C testdata/jfin git reset --hard HEAD", "git -C testdata/jfin git reset --hard HEAD",
"git -C testdata/jfin git merge --no-edit --ff", "git -C testdata/jfin git merge --no-edit --ff",
"makepkg --packagelist", "makepkg --packagelist",
"makepkg --packagelist",
} }
captureOverride := func(cmd *exec.Cmd) (stdout string, stderr string, err error) { captureOverride := func(cmd *exec.Cmd) (stdout string, stderr string, err error) {
@ -579,25 +580,24 @@ func TestIntegrationLocalInstallGenerateSRCINFO(t *testing.T) {
return nil return nil
}, },
InstalledRemotePackageNamesFn: func() []string { return []string{} },
} }
run := &runtime.Runtime{ config := &settings.Configuration{
Cfg: &settings.Configuration{ RemoveMake: "no",
RemoveMake: "no", Debug: false,
Debug: false, Runtime: &settings.Runtime{
}, Logger: NewTestLogger(),
Logger: newTestLogger(), CmdBuilder: cmdBuilder,
CmdBuilder: cmdBuilder, VCSStore: &vcs.Mock{},
VCSStore: &vcs.Mock{}, AURClient: &mockaur.MockAUR{
AURClient: &mockaur.MockAUR{ GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) { return []aur.Pkg{}, nil
return []aur.Pkg{}, nil },
}, },
}, },
} }
err = handleCmd(context.Background(), run, cmdArgs, db) err = handleCmd(context.Background(), config, cmdArgs, db)
require.NoError(t, err) require.NoError(t, err)
require.Len(t, mockRunner.ShowCalls, len(wantShow)) require.Len(t, mockRunner.ShowCalls, len(wantShow))
@ -648,6 +648,7 @@ func TestIntegrationLocalInstallMissingFiles(t *testing.T) {
wantCapture := []string{} wantCapture := []string{}
captureOverride := func(cmd *exec.Cmd) (stdout string, stderr string, err error) { captureOverride := func(cmd *exec.Cmd) (stdout string, stderr string, err error) {
fmt.Println(cmd.Args)
if cmd.Args[1] == "--printsrcinfo" { if cmd.Args[1] == "--printsrcinfo" {
return string(srcinfo), "", nil return string(srcinfo), "", nil
} }
@ -718,17 +719,16 @@ func TestIntegrationLocalInstallMissingFiles(t *testing.T) {
}, },
} }
config := &runtime.Runtime{ config := &settings.Configuration{
Cfg: &settings.Configuration{ RemoveMake: "no",
RemoveMake: "no", Runtime: &settings.Runtime{
Debug: false, Logger: NewTestLogger(),
}, CmdBuilder: cmdBuilder,
Logger: newTestLogger(), VCSStore: &vcs.Mock{},
CmdBuilder: cmdBuilder, AURClient: &mockaur.MockAUR{
VCSStore: &vcs.Mock{}, GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
AURClient: &mockaur.MockAUR{ return []aur.Pkg{}, nil
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) { },
return []aur.Pkg{}, nil
}, },
}, },
} }
@ -774,12 +774,12 @@ func TestIntegrationLocalInstallWithDepsProvides(t *testing.T) {
} }
wantShow := []string{ wantShow := []string{
"makepkg --verifysource --skippgpcheck -f -Cc", "makepkg --verifysource -Ccf",
"makepkg --nobuild -f -C --ignorearch", "makepkg --nobuild -fC --ignorearch",
"makepkg -c --nobuild --noextract --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 -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", "pacman -D -q --asexplicit --config /etc/pacman.conf -- ceph-libs-bin",
"makepkg --nobuild -f -C --ignorearch", "makepkg --nobuild -fC --ignorearch",
"makepkg -c --nobuild --noextract --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 -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", "pacman -D -q --asexplicit --config /etc/pacman.conf -- ceph-bin",
@ -841,20 +841,19 @@ func TestIntegrationLocalInstallWithDepsProvides(t *testing.T) {
SyncSatisfierFn: func(s string) mock.IPackage { SyncSatisfierFn: func(s string) mock.IPackage {
return nil return nil
}, },
LocalPackageFn: func(s string) mock.IPackage { return nil }, LocalPackageFn: func(s string) mock.IPackage { return nil },
InstalledRemotePackageNamesFn: func() []string { return []string{} },
} }
config := &runtime.Runtime{ config := &settings.Configuration{
Cfg: &settings.Configuration{ RemoveMake: "no",
RemoveMake: "no", Runtime: &settings.Runtime{
}, Logger: NewTestLogger(),
Logger: newTestLogger(), CmdBuilder: cmdBuilder,
CmdBuilder: cmdBuilder, VCSStore: &vcs.Mock{},
VCSStore: &vcs.Mock{}, AURClient: &mockaur.MockAUR{
AURClient: &mockaur.MockAUR{ GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) { return []aur.Pkg{}, nil
return []aur.Pkg{}, nil },
}, },
}, },
} }
@ -901,13 +900,13 @@ func TestIntegrationLocalInstallTwoSrcInfosWithDeps(t *testing.T) {
} }
wantShow := []string{ wantShow := []string{
"makepkg --verifysource --skippgpcheck -f -Cc", "makepkg --verifysource -Ccf",
"makepkg --verifysource --skippgpcheck -f -Cc", "makepkg --verifysource -Ccf",
"makepkg --nobuild -f -C --ignorearch", "makepkg --nobuild -fC --ignorearch",
"makepkg -c --nobuild --noextract --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 -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", "pacman -D -q --asexplicit --config /etc/pacman.conf -- libzip-git",
"makepkg --nobuild -f -C --ignorearch", "makepkg --nobuild -fC --ignorearch",
"makepkg -c --nobuild --noextract --ignorearch", "makepkg -c --nobuild --noextract --ignorearch",
"pacman -U --config /etc/pacman.conf -- /testdir2/gourou-0.8.1-4-x86_64.pkg.tar.zst", "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", "pacman -D -q --asexplicit --config /etc/pacman.conf -- gourou",
@ -981,25 +980,24 @@ func TestIntegrationLocalInstallTwoSrcInfosWithDeps(t *testing.T) {
SyncSatisfierFn: func(s string) mock.IPackage { SyncSatisfierFn: func(s string) mock.IPackage {
return nil return nil
}, },
LocalPackageFn: func(s string) mock.IPackage { return nil }, LocalPackageFn: func(s string) mock.IPackage { return nil },
InstalledRemotePackageNamesFn: func() []string { return []string{} },
} }
run := &runtime.Runtime{ config := &settings.Configuration{
Cfg: &settings.Configuration{ RemoveMake: "no",
RemoveMake: "no", Runtime: &settings.Runtime{
}, Logger: NewTestLogger(),
Logger: newTestLogger(), CmdBuilder: cmdBuilder,
CmdBuilder: cmdBuilder, VCSStore: &vcs.Mock{},
VCSStore: &vcs.Mock{}, AURClient: &mockaur.MockAUR{
AURClient: &mockaur.MockAUR{ GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) { return []aur.Pkg{}, nil
return []aur.Pkg{}, nil },
}, },
}, },
} }
err = handleCmd(context.Background(), run, cmdArgs, db) err = handleCmd(context.Background(), config, cmdArgs, db)
require.NoError(t, err) require.NoError(t, err)
require.Len(t, mockRunner.ShowCalls, len(wantShow)) require.Len(t, mockRunner.ShowCalls, len(wantShow))

82
main.go
View File

@ -6,12 +6,12 @@ import (
"os" "os"
"os/exec" "os/exec"
"runtime/debug" "runtime/debug"
"strings"
"github.com/leonelquinteros/gotext" "github.com/leonelquinteros/gotext"
"github.com/Jguer/yay/v12/pkg/db"
"github.com/Jguer/yay/v12/pkg/db/ialpm" "github.com/Jguer/yay/v12/pkg/db/ialpm"
"github.com/Jguer/yay/v12/pkg/runtime" "github.com/Jguer/yay/v12/pkg/query"
"github.com/Jguer/yay/v12/pkg/settings" "github.com/Jguer/yay/v12/pkg/settings"
"github.com/Jguer/yay/v12/pkg/settings/parser" "github.com/Jguer/yay/v12/pkg/settings/parser"
"github.com/Jguer/yay/v12/pkg/text" "github.com/Jguer/yay/v12/pkg/text"
@ -28,12 +28,7 @@ func initGotext() {
} }
if lc := os.Getenv("LANGUAGE"); lc != "" { if lc := os.Getenv("LANGUAGE"); lc != "" {
// Split LANGUAGE by ':' and prioritize the first locale gotext.Configure(localePath, lc, "yay")
// 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 != "" { } else if lc := os.Getenv("LC_ALL"); lc != "" {
gotext.Configure(localePath, lc, "yay") gotext.Configure(localePath, lc, "yay")
} else if lc := os.Getenv("LC_MESSAGES"); lc != "" { } else if lc := os.Getenv("LC_MESSAGES"); lc != "" {
@ -44,7 +39,6 @@ func initGotext() {
} }
func main() { func main() {
fallbackLog := text.NewLogger(os.Stdout, os.Stderr, os.Stdin, false, "fallback")
var ( var (
err error err error
ctx = context.Background() ctx = context.Background()
@ -53,9 +47,8 @@ func main() {
defer func() { defer func() {
if rec := recover(); rec != nil { if rec := recover(); rec != nil {
fallbackLog.Errorln("Panic occurred:", rec) text.Errorln(rec)
fallbackLog.Errorln("Stack trace:", string(debug.Stack())) debug.PrintStack()
ret = 1
} }
os.Exit(ret) os.Exit(ret)
@ -64,15 +57,15 @@ func main() {
initGotext() initGotext()
if os.Geteuid() == 0 { if os.Geteuid() == 0 {
fallbackLog.Warnln(gotext.Get("Avoid running yay as root/sudo.")) text.Warnln(gotext.Get("Avoid running yay as root/sudo."))
} }
configPath := settings.GetConfigPath() configPath := settings.GetConfigPath()
// Parse config // Parse config
cfg, err := settings.NewConfig(fallbackLog, configPath, yayVersion) cfg, err := settings.NewConfig(configPath, yayVersion)
if err != nil { if err != nil {
if str := err.Error(); str != "" { if str := err.Error(); str != "" {
fallbackLog.Errorln(str) text.Errorln(str)
} }
ret = 1 ret = 1
@ -80,9 +73,13 @@ func main() {
return return
} }
if errS := cfg.RunMigrations(fallbackLog, if cfg.Debug {
text.GlobalLogger.Debug = true
}
if errS := cfg.RunMigrations(
settings.DefaultMigrations(), configPath, yayVersion); errS != nil { settings.DefaultMigrations(), configPath, yayVersion); errS != nil {
fallbackLog.Errorln(errS) text.Errorln(errS)
} }
cmdArgs := parser.MakeArguments() cmdArgs := parser.MakeArguments()
@ -90,7 +87,7 @@ func main() {
// Parse command line // Parse command line
if err = cfg.ParseCommandLine(cmdArgs); err != nil { if err = cfg.ParseCommandLine(cmdArgs); err != nil {
if str := err.Error(); str != "" { if str := err.Error(); str != "" {
fallbackLog.Errorln(str) text.Errorln(str)
} }
ret = 1 ret = 1
@ -100,15 +97,15 @@ func main() {
if cfg.SaveConfig { if cfg.SaveConfig {
if errS := cfg.Save(configPath, yayVersion); errS != nil { if errS := cfg.Save(configPath, yayVersion); errS != nil {
fallbackLog.Errorln(errS) text.Errorln(errS)
} }
} }
// Build run // Build runtime
run, err := runtime.NewRuntime(cfg, cmdArgs, yayVersion) runtime, err := settings.BuildRuntime(cfg, cmdArgs, yayVersion)
if err != nil { if err != nil {
if str := err.Error(); str != "" { if str := err.Error(); str != "" {
fallbackLog.Errorln(str) text.Errorln(str)
} }
ret = 1 ret = 1
@ -116,10 +113,35 @@ func main() {
return return
} }
dbExecutor, err := ialpm.NewExecutor(run.PacmanConf, run.Logger.Child("db")) cfg.Runtime = runtime
cfg.Runtime.QueryBuilder = query.NewSourceQueryBuilder(
cfg.Runtime.AURClient,
cfg.Runtime.Logger.Child("mixed.querybuilder"), cfg.SortBy,
cfg.Mode, cfg.SearchBy,
cfg.BottomUp, cfg.SingleLineResults, cfg.SeparateSources)
var useColor bool
cfg.Runtime.PacmanConf, useColor, err = settings.RetrievePacmanConfig(cmdArgs, cfg.PacmanConf)
if err != nil { if err != nil {
if str := err.Error(); str != "" { if str := err.Error(); str != "" {
fallbackLog.Errorln(str) text.Errorln(str)
}
ret = 1
return
}
cfg.Runtime.CmdBuilder.SetPacmanDBPath(cfg.Runtime.PacmanConf.DBPath)
text.UseColor = useColor
dbExecutor, err := ialpm.NewExecutor(cfg.Runtime.PacmanConf, runtime.Logger.Child("db"))
if err != nil {
if str := err.Error(); str != "" {
text.Errorln(str)
} }
ret = 1 ret = 1
@ -129,16 +151,20 @@ func main() {
defer func() { defer func() {
if rec := recover(); rec != nil { if rec := recover(); rec != nil {
fallbackLog.Errorln("Panic occurred in DB operation:", rec) text.Errorln(rec)
fallbackLog.Errorln("Stack trace:", string(debug.Stack())) debug.PrintStack()
} }
dbExecutor.Cleanup() dbExecutor.Cleanup()
}() }()
if err = handleCmd(ctx, run, cmdArgs, dbExecutor); err != nil { if err = handleCmd(ctx, cfg, cmdArgs, db.Executor(dbExecutor)); err != nil {
if str := err.Error(); str != "" { if str := err.Error(); str != "" {
fallbackLog.Errorln(str) text.Errorln(str)
if cmdArgs.ExistsArg("c") && cmdArgs.ExistsArg("y") && cmdArgs.Op == "S" {
// Remove after 2023-10-01
text.Errorln("Did you mean 'yay -Yc'?")
}
} }
exitError := &exec.ExitError{} exitError := &exec.ExitError{}

View File

@ -2,61 +2,59 @@ package main
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"os" "os"
"path/filepath" "path/filepath"
"github.com/Jguer/yay/v12/pkg/db/ialpm" "github.com/Jguer/yay/v12/pkg/db/ialpm"
"github.com/Jguer/yay/v12/pkg/dep" "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"
"github.com/Jguer/yay/v12/pkg/settings/parser" "github.com/Jguer/yay/v12/pkg/settings/parser"
"github.com/Jguer/yay/v12/pkg/text" "github.com/Jguer/yay/v12/pkg/text"
"github.com/Jguer/aur/metadata" "github.com/Jguer/aur/metadata"
"github.com/leonelquinteros/gotext" "github.com/leonelquinteros/gotext"
"github.com/pkg/errors"
) )
func handleCmd(logger *text.Logger) error { func handleCmd() error {
cfg, err := settings.NewConfig(logger, settings.GetConfigPath(), "") config, err := settings.NewConfig(settings.GetConfigPath(), "")
if err != nil { if err != nil {
return err return err
} }
cmdArgs := parser.MakeArguments() cmdArgs := parser.MakeArguments()
if errP := cfg.ParseCommandLine(cmdArgs); errP != nil { if errP := config.ParseCommandLine(cmdArgs); errP != nil {
return errP return errP
} }
run, err := runtime.NewRuntime(cfg, cmdArgs, "1.0.0") pacmanConf, _, err := settings.RetrievePacmanConfig(cmdArgs, config.PacmanConf)
if err != nil { if err != nil {
return err return err
} }
dbExecutor, err := ialpm.NewExecutor(run.PacmanConf, logger) dbExecutor, err := ialpm.NewExecutor(pacmanConf, text.GlobalLogger)
if err != nil { if err != nil {
return err return err
} }
aurCache, err := metadata.New( aurCache, err := metadata.New(
metadata.WithCacheFilePath( metadata.WithCacheFilePath(
filepath.Join(cfg.BuildDir, "aur.json"))) filepath.Join(config.BuildDir, "aur.json")))
if err != nil { if err != nil {
return fmt.Errorf("%s: %w", gotext.Get("failed to retrieve aur Cache"), err) return errors.Wrap(err, gotext.Get("failed to retrieve aur Cache"))
} }
grapher := dep.NewGrapher(dbExecutor, aurCache, true, settings.NoConfirm, grapher := dep.NewGrapher(dbExecutor, aurCache, true, settings.NoConfirm,
cmdArgs.ExistsDouble("d", "nodeps"), false, false, cmdArgs.ExistsDouble("d", "nodeps"), false, false,
run.Logger.Child("grapher")) config.Runtime.Logger.Child("grapher"))
return graphPackage(context.Background(), grapher, cmdArgs.Targets) return graphPackage(context.Background(), grapher, cmdArgs.Targets)
} }
func main() { func main() {
fallbackLog := text.NewLogger(os.Stdout, os.Stderr, os.Stdin, false, "fallback") if err := handleCmd(); err != nil {
if err := handleCmd(fallbackLog); err != nil { text.Errorln(err)
fallbackLog.Errorln(err)
os.Exit(1) os.Exit(1)
} }
} }

View File

@ -117,7 +117,7 @@ func createAURList(ctx context.Context, client httpRequestDoer, aurURL string, o
return nil return nil
} }
// createRepoList appends Repo packages to completion cache. // CreatePackageList appends Repo packages to completion cache.
func createRepoList(dbExecutor PkgSynchronizer, out io.Writer) error { func createRepoList(dbExecutor PkgSynchronizer, out io.Writer) error {
for _, pkg := range dbExecutor.SyncPackages() { for _, pkg := range dbExecutor.SyncPackages() {
_, err := io.WriteString(out, pkg.Name()+"\t"+pkg.DB().Name()+"\n") _, err := io.WriteString(out, pkg.Name()+"\t"+pkg.DB().Name()+"\n")

View File

@ -52,14 +52,12 @@ type Executor interface {
PackageOptionalDepends(IPackage) []Depend PackageOptionalDepends(IPackage) []Depend
PackageProvides(IPackage) []Depend PackageProvides(IPackage) []Depend
PackagesFromGroup(string) []IPackage PackagesFromGroup(string) []IPackage
PackagesFromGroupAndDB(string, string) ([]IPackage, error)
RefreshHandle() error RefreshHandle() error
SyncUpgrades(enableDowngrade bool) ( SyncUpgrades(enableDowngrade bool) (
map[string]SyncUpgrade, error) map[string]SyncUpgrade, error)
Repos() []string Repos() []string
SatisfierFromDB(string, string) (IPackage, error) SatisfierFromDB(string, string) IPackage
SyncPackage(string) IPackage SyncPackage(string) IPackage
SyncPackageFromDB(string, string) IPackage
SyncPackages(...string) []IPackage SyncPackages(...string) []IPackage
SyncSatisfier(string) IPackage SyncSatisfier(string) IPackage
SyncSatisfierExists(string) bool SyncSatisfierExists(string) bool

View File

@ -176,7 +176,7 @@ func (ae *AlpmExecutor) questionCallback() func(question alpm.QuestionAny) {
return nil return nil
}) })
str := text.Bold(gotext.Get("There are %[1]d providers available for %[2]s:", size, qp.Dep())) str := text.Bold(gotext.Get("There are %d providers available for %s:", size, qp.Dep()))
size = 1 size = 1
@ -311,22 +311,6 @@ func (ae *AlpmExecutor) PackagesFromGroup(groupName string) []alpm.IPackage {
return groupPackages return groupPackages
} }
func (ae *AlpmExecutor) PackagesFromGroupAndDB(groupName, dbName string) ([]alpm.IPackage, error) {
singleDBList, err := ae.handle.SyncDBListByDBName(dbName)
if err != nil {
return nil, err
}
groupPackages := []alpm.IPackage{}
_ = singleDBList.FindGroupPkgs(groupName).ForEach(func(pkg alpm.IPackage) error {
groupPackages = append(groupPackages, pkg)
return nil
})
return groupPackages, nil
}
func (ae *AlpmExecutor) LocalPackages() []alpm.IPackage { func (ae *AlpmExecutor) LocalPackages() []alpm.IPackage {
localPackages := []alpm.IPackage{} localPackages := []alpm.IPackage{}
_ = ae.localDB.PkgCache().ForEach(func(pkg alpm.IPackage) error { _ = ae.localDB.PkgCache().ForEach(func(pkg alpm.IPackage) error {
@ -385,27 +369,18 @@ func (ae *AlpmExecutor) SyncPackage(pkgName string) alpm.IPackage {
return nil return nil
} }
func (ae *AlpmExecutor) SyncPackageFromDB(pkgName, dbName string) alpm.IPackage { func (ae *AlpmExecutor) SatisfierFromDB(pkgName, dbName string) alpm.IPackage {
singleDB, err := ae.handle.SyncDBByName(dbName) singleDB, err := ae.handle.SyncDBByName(dbName)
if err != nil { if err != nil {
return nil return nil
} }
return singleDB.Pkg(pkgName) foundPkg, err := singleDB.PkgCache().FindSatisfier(pkgName)
}
func (ae *AlpmExecutor) SatisfierFromDB(pkgName, dbName string) (alpm.IPackage, error) {
singleDBList, err := ae.handle.SyncDBListByDBName(dbName)
if err != nil { if err != nil {
return nil, err return nil
} }
foundPkg, err := singleDBList.FindSatisfier(pkgName) return foundPkg
if err != nil {
return nil, nil
}
return foundPkg, nil
} }
func (ae *AlpmExecutor) PackageDepends(pkg alpm.IPackage) []alpm.Depend { func (ae *AlpmExecutor) PackageDepends(pkg alpm.IPackage) []alpm.Depend {

View File

@ -28,13 +28,11 @@ type DBExecutor struct {
PackageOptionalDependsFn func(alpm.IPackage) []alpm.Depend PackageOptionalDependsFn func(alpm.IPackage) []alpm.Depend
PackageProvidesFn func(IPackage) []Depend PackageProvidesFn func(IPackage) []Depend
PackagesFromGroupFn func(string) []IPackage PackagesFromGroupFn func(string) []IPackage
PackagesFromGroupAndDBFn func(string, string) ([]IPackage, error)
RefreshHandleFn func() error RefreshHandleFn func() error
ReposFn func() []string ReposFn func() []string
SyncPackageFn func(string) IPackage SyncPackageFn func(string) IPackage
SyncPackagesFn func(...string) []IPackage SyncPackagesFn func(...string) []IPackage
SyncSatisfierFn func(string) IPackage SyncSatisfierFn func(string) IPackage
SatisfierFromDBFn func(string, string) (IPackage, error)
SyncUpgradesFn func(bool) (map[string]db.SyncUpgrade, error) SyncUpgradesFn func(bool) (map[string]db.SyncUpgrade, error)
SetLoggerFn func(*text.Logger) SetLoggerFn func(*text.Logger)
} }
@ -142,13 +140,6 @@ func (t *DBExecutor) PackagesFromGroup(s string) []IPackage {
panic("implement me") 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 { func (t *DBExecutor) RefreshHandle() error {
if t.RefreshHandleFn != nil { if t.RefreshHandleFn != nil {
return t.RefreshHandleFn() return t.RefreshHandleFn()
@ -170,10 +161,7 @@ func (t *DBExecutor) Repos() []string {
panic("implement me") panic("implement me")
} }
func (t *DBExecutor) SatisfierFromDB(s, s2 string) (IPackage, error) { func (t *DBExecutor) SatisfierFromDB(s, s2 string) IPackage {
if t.SatisfierFromDBFn != nil {
return t.SatisfierFromDBFn(s, s2)
}
panic("implement me") panic("implement me")
} }

View File

@ -12,10 +12,10 @@ import (
"github.com/leonelquinteros/gotext" "github.com/leonelquinteros/gotext"
"github.com/Jguer/yay/v12/pkg/db" "github.com/Jguer/yay/v12/pkg/db"
"github.com/Jguer/yay/v12/pkg/dep/topo"
"github.com/Jguer/yay/v12/pkg/intrange" "github.com/Jguer/yay/v12/pkg/intrange"
aur "github.com/Jguer/yay/v12/pkg/query" aur "github.com/Jguer/yay/v12/pkg/query"
"github.com/Jguer/yay/v12/pkg/text" "github.com/Jguer/yay/v12/pkg/text"
"github.com/Jguer/yay/v12/pkg/topo"
) )
type InstallInfo struct { type InstallInfo struct {
@ -37,7 +37,7 @@ func (i *InstallInfo) String() string {
} }
type ( type (
Reason uint Reason int
Source int Source int
) )
@ -123,15 +123,11 @@ func NewGrapher(dbExecutor db.Executor, aurCache aurc.QueryClient,
} }
} }
func NewGraph() *topo.Graph[string, *InstallInfo] {
return topo.New[string, *InstallInfo]()
}
func (g *Grapher) GraphFromTargets(ctx context.Context, func (g *Grapher) GraphFromTargets(ctx context.Context,
graph *topo.Graph[string, *InstallInfo], targets []string, graph *topo.Graph[string, *InstallInfo], targets []string,
) (*topo.Graph[string, *InstallInfo], error) { ) (*topo.Graph[string, *InstallInfo], error) {
if graph == nil { if graph == nil {
graph = NewGraph() graph = topo.New[string, *InstallInfo]()
} }
aurTargets := make([]string, 0, len(targets)) aurTargets := make([]string, 0, len(targets))
@ -142,7 +138,19 @@ func (g *Grapher) GraphFromTargets(ctx context.Context,
switch target.DB { switch target.DB {
case "": // unspecified db case "": // unspecified db
if pkg := g.dbExecutor.SyncSatisfier(target.Name); pkg != nil { if pkg := g.dbExecutor.SyncSatisfier(target.Name); pkg != nil {
g.GraphSyncPkg(ctx, graph, pkg, nil) dbName := pkg.DB().Name()
reason := Explicit
if localPkg := g.dbExecutor.LocalPackage(pkg.Name()); localPkg != nil {
reason = Reason(localPkg.Reason())
}
g.GraphSyncPkg(ctx, graph, pkg, &InstallInfo{
Source: Sync,
Reason: reason,
Version: pkg.Version(),
SyncDBName: &dbName,
})
continue continue
} }
@ -150,7 +158,18 @@ func (g *Grapher) GraphFromTargets(ctx context.Context,
groupPackages := g.dbExecutor.PackagesFromGroup(target.Name) groupPackages := g.dbExecutor.PackagesFromGroup(target.Name)
if len(groupPackages) > 0 { if len(groupPackages) > 0 {
dbName := groupPackages[0].DB().Name() dbName := groupPackages[0].DB().Name()
g.GraphSyncGroup(ctx, graph, target.Name, dbName) graph.AddNode(target.Name)
g.ValidateAndSetNodeInfo(graph, target.Name, &topo.NodeInfo[*InstallInfo]{
Color: colorMap[Explicit],
Background: bgColorMap[Sync],
Value: &InstallInfo{
Source: Sync,
Reason: Explicit,
Version: "",
SyncDBName: &dbName,
IsGroup: true,
},
})
continue continue
} }
@ -159,26 +178,22 @@ func (g *Grapher) GraphFromTargets(ctx context.Context,
case "aur": case "aur":
aurTargets = append(aurTargets, target.Name) aurTargets = append(aurTargets, target.Name)
default: default:
pkg, err := g.dbExecutor.SatisfierFromDB(target.Name, target.DB) reason := Explicit
if err != nil { if pkg := g.dbExecutor.LocalPackage(target.Name); pkg != nil {
return nil, err reason = Reason(pkg.Reason())
}
if pkg != nil {
g.GraphSyncPkg(ctx, graph, pkg, nil)
continue
} }
groupPackages, err := g.dbExecutor.PackagesFromGroupAndDB(target.Name, target.DB) graph.AddNode(target.Name)
if err != nil { g.ValidateAndSetNodeInfo(graph, target.Name, &topo.NodeInfo[*InstallInfo]{
return nil, err Color: colorMap[reason],
} Background: bgColorMap[Sync],
if len(groupPackages) > 0 { Value: &InstallInfo{
g.GraphSyncGroup(ctx, graph, target.Name, target.DB) Source: Sync,
Reason: reason,
continue Version: target.Version,
} SyncDBName: &target.DB,
},
g.logger.Errorln(gotext.Get("No package found for"), " ", target) })
} }
} }
@ -206,7 +221,7 @@ func (g *Grapher) pickSrcInfoPkgs(pkgs []*aurc.Pkg) ([]*aurc.Pkg, error) {
} }
include, exclude, _, otherExclude := intrange.ParseNumberMenu(numberBuf) include, exclude, _, otherExclude := intrange.ParseNumberMenu(numberBuf)
isInclude := len(exclude) == 0 && otherExclude.Cardinality() == 0 isInclude := len(exclude) == 0 && len(otherExclude) == 0
for i := 1; i <= len(pkgs); i++ { for i := 1; i <= len(pkgs); i++ {
target := i - 1 target := i - 1
@ -239,7 +254,7 @@ func (g *Grapher) GraphFromSrcInfos(ctx context.Context, graph *topo.Graph[strin
srcInfos map[string]*gosrc.Srcinfo, srcInfos map[string]*gosrc.Srcinfo,
) (*topo.Graph[string, *InstallInfo], error) { ) (*topo.Graph[string, *InstallInfo], error) {
if graph == nil { if graph == nil {
graph = NewGraph() graph = topo.New[string, *InstallInfo]()
} }
aurPkgsAdded := []*aurc.Pkg{} aurPkgsAdded := []*aurc.Pkg{}
@ -314,10 +329,10 @@ func (g *Grapher) addDepNodes(ctx context.Context, pkg *aur.Pkg, graph *topo.Gra
func (g *Grapher) GraphSyncPkg(ctx context.Context, func (g *Grapher) GraphSyncPkg(ctx context.Context,
graph *topo.Graph[string, *InstallInfo], graph *topo.Graph[string, *InstallInfo],
pkg alpm.IPackage, upgradeInfo *db.SyncUpgrade, pkg alpm.IPackage, instalInfo *InstallInfo,
) *topo.Graph[string, *InstallInfo] { ) *topo.Graph[string, *InstallInfo] {
if graph == nil { if graph == nil {
graph = NewGraph() graph = topo.New[string, *InstallInfo]()
} }
graph.AddNode(pkg.Name()) graph.AddNode(pkg.Name())
@ -327,53 +342,10 @@ func (g *Grapher) GraphSyncPkg(ctx context.Context,
return nil return nil
}) })
dbName := pkg.DB().Name()
info := &InstallInfo{
Source: Sync,
Reason: Explicit,
Version: pkg.Version(),
SyncDBName: &dbName,
}
if upgradeInfo == nil {
if localPkg := g.dbExecutor.LocalPackage(pkg.Name()); localPkg != nil {
info.Reason = Reason(localPkg.Reason())
}
} else {
info.Upgrade = true
info.Reason = Reason(upgradeInfo.Reason)
info.LocalVersion = upgradeInfo.LocalVersion
}
g.ValidateAndSetNodeInfo(graph, pkg.Name(), &topo.NodeInfo[*InstallInfo]{ g.ValidateAndSetNodeInfo(graph, pkg.Name(), &topo.NodeInfo[*InstallInfo]{
Color: colorMap[info.Reason], Color: colorMap[Reason(pkg.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], Background: bgColorMap[Sync],
Value: &InstallInfo{ Value: instalInfo,
Source: Sync,
Reason: Explicit,
Version: "",
SyncDBName: &dbName,
IsGroup: true,
},
}) })
return graph return graph
@ -384,7 +356,7 @@ func (g *Grapher) GraphAURTarget(ctx context.Context,
pkg *aurc.Pkg, instalInfo *InstallInfo, pkg *aurc.Pkg, instalInfo *InstallInfo,
) *topo.Graph[string, *InstallInfo] { ) *topo.Graph[string, *InstallInfo] {
if graph == nil { if graph == nil {
graph = NewGraph() graph = topo.New[string, *InstallInfo]()
} }
graph.AddNode(pkg.Name) graph.AddNode(pkg.Name)
@ -405,7 +377,7 @@ func (g *Grapher) GraphFromAUR(ctx context.Context,
targets []string, targets []string,
) (*topo.Graph[string, *InstallInfo], error) { ) (*topo.Graph[string, *InstallInfo], error) {
if graph == nil { if graph == nil {
graph = NewGraph() graph = topo.New[string, *InstallInfo]()
} }
if len(targets) == 0 { if len(targets) == 0 {
@ -536,13 +508,12 @@ func (g *Grapher) findDepsFromAUR(ctx context.Context,
} }
// remove packages that don't satisfy the dependency // remove packages that don't satisfy the dependency
satisfyingPkgs := make([]aurc.Pkg, 0, len(aurPkgs)) for i := 0; i < len(aurPkgs); i++ {
for i := range aurPkgs { if !satisfiesAur(depString, &aurPkgs[i]) {
if satisfiesAur(depString, &aurPkgs[i]) { aurPkgs = append(aurPkgs[:i], aurPkgs[i+1:]...)
satisfyingPkgs = append(satisfyingPkgs, aurPkgs[i]) i--
} }
} }
aurPkgs = satisfyingPkgs
if len(aurPkgs) == 0 { if len(aurPkgs) == 0 {
g.logger.Errorln(gotext.Get("No AUR package found for"), " ", depString) g.logger.Errorln(gotext.Get("No AUR package found for"), " ", depString)
@ -723,7 +694,7 @@ func (g *Grapher) provideMenu(dep string, options []aur.Pkg) *aur.Pkg {
return &options[0] return &options[0]
} }
str := text.Bold(gotext.Get("There are %[1]d providers available for %[2]s:", size, dep)) str := text.Bold(gotext.Get("There are %d providers available for %s:", size, dep))
str += "\n" str += "\n"
size = 1 size = 1
@ -749,7 +720,7 @@ func (g *Grapher) provideMenu(dep string, options []aur.Pkg) *aur.Pkg {
if err != nil { if err != nil {
g.logger.Errorln(err) g.logger.Errorln(err)
return &options[0] break
} }
if numberBuf == "" { if numberBuf == "" {
@ -772,6 +743,8 @@ func (g *Grapher) provideMenu(dep string, options []aur.Pkg) *aur.Pkg {
return &options[num-1] return &options[num-1]
} }
return nil
} }
func makeAURPKGFromSrcinfo(dbExecutor db.Executor, srcInfo *gosrc.Srcinfo) ([]*aur.Pkg, error) { func makeAURPKGFromSrcinfo(dbExecutor db.Executor, srcInfo *gosrc.Srcinfo) ([]*aur.Pkg, error) {
@ -804,20 +777,19 @@ func makeAURPKGFromSrcinfo(dbExecutor db.Executor, srcInfo *gosrc.Srcinfo) ([]*a
Description: getDesc(pkg), Description: getDesc(pkg),
URL: pkg.URL, URL: pkg.URL,
Depends: append(archStringToString(alpmArch, pkg.Depends), Depends: append(archStringToString(alpmArch, pkg.Depends),
archStringToString(alpmArch, srcInfo.Depends)...), archStringToString(alpmArch, srcInfo.Package.Depends)...),
MakeDepends: archStringToString(alpmArch, srcInfo.MakeDepends), MakeDepends: archStringToString(alpmArch, srcInfo.PackageBase.MakeDepends),
CheckDepends: archStringToString(alpmArch, srcInfo.CheckDepends), CheckDepends: archStringToString(alpmArch, srcInfo.PackageBase.CheckDepends),
Conflicts: append(archStringToString(alpmArch, pkg.Conflicts), Conflicts: append(archStringToString(alpmArch, pkg.Conflicts),
archStringToString(alpmArch, srcInfo.Conflicts)...), archStringToString(alpmArch, srcInfo.Package.Conflicts)...),
Provides: append(archStringToString(alpmArch, pkg.Provides), Provides: append(archStringToString(alpmArch, pkg.Provides),
archStringToString(alpmArch, srcInfo.Provides)...), archStringToString(alpmArch, srcInfo.Package.Provides)...),
Replaces: append(archStringToString(alpmArch, pkg.Replaces), Replaces: append(archStringToString(alpmArch, pkg.Replaces),
archStringToString(alpmArch, srcInfo.Replaces)...), archStringToString(alpmArch, srcInfo.Package.Replaces)...),
OptDepends: append(archStringToString(alpmArch, pkg.OptDepends), OptDepends: []string{},
archStringToString(alpmArch, srcInfo.OptDepends)...), Groups: pkg.Groups,
Groups: pkg.Groups, License: pkg.License,
License: pkg.License, Keywords: []string{},
Keywords: []string{},
}) })
} }

View File

@ -690,20 +690,6 @@ func TestGrapher_GraphFromTargets_ReinstalledDeps(t *testing.T) {
panic("implement me " + s) 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 { LocalSatisfierExistsFn: func(s string) bool {
switch s { switch s {
@ -769,6 +755,15 @@ func TestGrapher_GraphFromTargets_ReinstalledDeps(t *testing.T) {
Version: "1.9.2-1", Version: "1.9.2-1",
SyncDBName: ptrString("extra"), SyncDBName: ptrString("extra"),
}, },
// In an ideal world this should be the same as "libzip dep",
// but due to the difference in handling cases with specified and
// unspecified dbs, this is how it is.
"extra/libzip dep": {
Source: Sync,
Reason: Dep,
Version: "",
SyncDBName: ptrString("extra"),
},
} }
tests := []struct { tests := []struct {
@ -791,7 +786,7 @@ func TestGrapher_GraphFromTargets_ReinstalledDeps(t *testing.T) {
targets: []string{"aur/gourou", "extra/libzip"}, targets: []string{"aur/gourou", "extra/libzip"},
wantLayers: []map[string]*InstallInfo{ wantLayers: []map[string]*InstallInfo{
{"gourou": installInfos["gourou dep"]}, {"gourou": installInfos["gourou dep"]},
{"libzip": installInfos["libzip dep"]}, {"libzip": installInfos["extra/libzip dep"]},
}, },
wantErr: false, wantErr: false,
}, },

View File

@ -1,9 +0,0 @@
package topo
import "errors"
var (
ErrSelfReferential = errors.New(" self-referential dependencies not allowed")
ErrConflictingAlias = errors.New(" alias already defined")
ErrCircular = errors.New(" circular dependencies not allowed")
)

View File

@ -48,7 +48,7 @@ func AURPKGBUILDRepo(ctx context.Context, cmdBuilder exe.GitCmdBuilder, aurURL,
func AURPKGBUILDRepos( func AURPKGBUILDRepos(
ctx context.Context, ctx context.Context,
cmdBuilder exe.GitCmdBuilder, logger *text.Logger, cmdBuilder exe.GitCmdBuilder,
targets []string, aurURL, dest string, force bool, targets []string, aurURL, dest string, force bool,
) (map[string]bool, error) { ) (map[string]bool, error) {
cloned := make(map[string]bool, len(targets)) cloned := make(map[string]bool, len(targets))
@ -63,34 +63,30 @@ func AURPKGBUILDRepos(
for _, target := range targets { for _, target := range targets {
sem <- 1 sem <- 1
wg.Add(1) wg.Add(1)
go func(target string) { go func(target string) {
defer func() {
<-sem
wg.Done()
}()
newClone, err := AURPKGBUILDRepo(ctx, cmdBuilder, aurURL, target, dest, force) newClone, err := AURPKGBUILDRepo(ctx, cmdBuilder, aurURL, target, dest, force)
mux.Lock() progress := 0
progress := len(cloned)
if err != nil { if err != nil {
errs.Add(err) errs.Add(err)
} else {
mux.Lock()
cloned[target] = newClone
progress = len(cloned)
mux.Unlock() mux.Unlock()
logger.OperationInfoln(
gotext.Get("(%d/%d) Failed to download PKGBUILD: %s",
progress, len(targets), text.Cyan(target)))
return
} }
cloned[target] = newClone text.OperationInfoln(
progress = len(cloned)
mux.Unlock()
logger.OperationInfoln(
gotext.Get("(%d/%d) Downloaded PKGBUILD: %s", gotext.Get("(%d/%d) Downloaded PKGBUILD: %s",
progress, len(targets), text.Cyan(target))) progress, len(targets), text.Cyan(target)))
<-sem
wg.Done()
}(target) }(target)
} }

View File

@ -158,7 +158,7 @@ func TestAURPKGBUILDRepos(t *testing.T) {
GitFlags: []string{}, GitFlags: []string{},
}, },
} }
cloned, err := AURPKGBUILDRepos(context.Background(), cmdBuilder, newTestLogger(), targets, "https://aur.archlinux.org", dir, false) cloned, err := AURPKGBUILDRepos(context.Background(), cmdBuilder, targets, "https://aur.archlinux.org", dir, false)
assert.NoError(t, err) assert.NoError(t, err)
assert.EqualValues(t, map[string]bool{"yay": true, "yay-bin": false, "yay-git": true}, cloned) assert.EqualValues(t, map[string]bool{"yay": true, "yay-bin": false, "yay-git": true}, cloned)

View File

@ -24,7 +24,7 @@ type httpRequestDoer interface {
type DBSearcher interface { type DBSearcher interface {
SyncPackage(string) db.IPackage SyncPackage(string) db.IPackage
SyncPackageFromDB(string, string) db.IPackage SatisfierFromDB(string, string) db.IPackage
} }
func downloadGitRepo(ctx context.Context, cmdBuilder exe.GitCmdBuilder, func downloadGitRepo(ctx context.Context, cmdBuilder exe.GitCmdBuilder,
@ -81,8 +81,8 @@ func getURLName(pkg db.IPackage) string {
return name return name
} }
func PKGBUILDs(dbExecutor DBSearcher, aurClient aur.QueryClient, httpClient *http.Client, func PKGBUILDs(dbExecutor DBSearcher, aurClient aur.QueryClient, httpClient *http.Client, targets []string,
logger *text.Logger, targets []string, aurURL string, mode parser.TargetMode, aurURL string, mode parser.TargetMode,
) (map[string][]byte, error) { ) (map[string][]byte, error) {
pkgbuilds := make(map[string][]byte, len(targets)) pkgbuilds := make(map[string][]byte, len(targets))
@ -96,7 +96,7 @@ func PKGBUILDs(dbExecutor DBSearcher, aurClient aur.QueryClient, httpClient *htt
for _, target := range targets { for _, target := range targets {
// Probably replaceable by something in query. // Probably replaceable by something in query.
dbName, name, isAUR, toSkip := getPackageUsableName(dbExecutor, aurClient, logger, target, mode) dbName, name, isAUR, toSkip := getPackageUsableName(dbExecutor, aurClient, target, mode)
if toSkip { if toSkip {
continue continue
} }
@ -136,7 +136,7 @@ func PKGBUILDs(dbExecutor DBSearcher, aurClient aur.QueryClient, httpClient *htt
} }
func PKGBUILDRepos(ctx context.Context, dbExecutor DBSearcher, aurClient aur.QueryClient, func PKGBUILDRepos(ctx context.Context, dbExecutor DBSearcher, aurClient aur.QueryClient,
cmdBuilder exe.GitCmdBuilder, logger *text.Logger, cmdBuilder exe.GitCmdBuilder,
targets []string, mode parser.TargetMode, aurURL, dest string, force bool, targets []string, mode parser.TargetMode, aurURL, dest string, force bool,
) (map[string]bool, error) { ) (map[string]bool, error) {
cloned := make(map[string]bool, len(targets)) cloned := make(map[string]bool, len(targets))
@ -151,7 +151,7 @@ func PKGBUILDRepos(ctx context.Context, dbExecutor DBSearcher, aurClient aur.Que
for _, target := range targets { for _, target := range targets {
// Probably replaceable by something in query. // Probably replaceable by something in query.
dbName, name, isAUR, toSkip := getPackageUsableName(dbExecutor, aurClient, logger, target, mode) dbName, name, isAUR, toSkip := getPackageUsableName(dbExecutor, aurClient, target, mode)
if toSkip { if toSkip {
continue continue
} }
@ -184,11 +184,11 @@ func PKGBUILDRepos(ctx context.Context, dbExecutor DBSearcher, aurClient aur.Que
} }
if aur { if aur {
logger.OperationInfoln( text.OperationInfoln(
gotext.Get("(%d/%d) Downloaded PKGBUILD: %s", gotext.Get("(%d/%d) Downloaded PKGBUILD: %s",
progress, len(targets), text.Cyan(pkgName))) progress, len(targets), text.Cyan(pkgName)))
} else { } else {
logger.OperationInfoln( text.OperationInfoln(
gotext.Get("(%d/%d) Downloaded PKGBUILD from ABS: %s", gotext.Get("(%d/%d) Downloaded PKGBUILD from ABS: %s",
progress, len(targets), text.Cyan(pkgName))) progress, len(targets), text.Cyan(pkgName)))
} }
@ -206,13 +206,13 @@ func PKGBUILDRepos(ctx context.Context, dbExecutor DBSearcher, aurClient aur.Que
// TODO: replace with dep.ResolveTargets. // TODO: replace with dep.ResolveTargets.
func getPackageUsableName(dbExecutor DBSearcher, aurClient aur.QueryClient, func getPackageUsableName(dbExecutor DBSearcher, aurClient aur.QueryClient,
logger *text.Logger, target string, mode parser.TargetMode, target string, mode parser.TargetMode,
) (dbname, pkgname string, isAUR, toSkip bool) { ) (dbname, pkgname string, isAUR, toSkip bool) {
dbName, name := text.SplitDBFromName(target) dbName, name := text.SplitDBFromName(target)
if dbName != "aur" && mode.AtLeastRepo() { if dbName != "aur" && mode.AtLeastRepo() {
var pkg db.IPackage var pkg db.IPackage
if dbName != "" { if dbName != "" {
pkg = dbExecutor.SyncPackageFromDB(name, dbName) pkg = dbExecutor.SatisfierFromDB(name, dbName)
} else { } else {
pkg = dbExecutor.SyncPackage(name) pkg = dbExecutor.SyncPackage(name)
} }
@ -239,7 +239,7 @@ func getPackageUsableName(dbExecutor DBSearcher, aurClient aur.QueryClient,
Needles: []string{name}, Needles: []string{name},
}) })
if err != nil { if err != nil {
logger.Warnln(err) text.Warnln(err)
return dbName, name, true, true return dbName, name, true, true
} }

View File

@ -42,7 +42,7 @@ func TestIntegrationPKGBUILDReposDefinedDBClone(t *testing.T) {
absPackagesDB: map[string]string{"linux": "core"}, absPackagesDB: map[string]string{"linux": "core"},
} }
cloned, err := PKGBUILDRepos(context.Background(), searcher, mockClient, cloned, err := PKGBUILDRepos(context.Background(), searcher, mockClient,
cmdBuilder, testLogger.Child("test"), cmdBuilder,
targets, parser.ModeAny, "https://aur.archlinux.org", dir, false) targets, parser.ModeAny, "https://aur.archlinux.org", dir, false)
assert.NoError(t, err) assert.NoError(t, err)
@ -71,7 +71,7 @@ func TestIntegrationPKGBUILDReposNotExist(t *testing.T) {
absPackagesDB: map[string]string{"yay": "core"}, absPackagesDB: map[string]string{"yay": "core"},
} }
cloned, err := PKGBUILDRepos(context.Background(), searcher, mockClient, cloned, err := PKGBUILDRepos(context.Background(), searcher, mockClient,
cmdBuilder, testLogger.Child("test"), cmdBuilder,
targets, parser.ModeAny, "https://aur.archlinux.org", dir, false) targets, parser.ModeAny, "https://aur.archlinux.org", dir, false)
assert.Error(t, err) assert.Error(t, err)
@ -88,13 +88,12 @@ func TestIntegrationPKGBUILDFull(t *testing.T) {
}, },
} }
testLogger := text.NewLogger(os.Stdout, os.Stderr, strings.NewReader(""), true, "test")
targets := []string{"core/linux", "aur/yay-bin", "yay-git"} targets := []string{"core/linux", "aur/yay-bin", "yay-git"}
searcher := &testDBSearcher{ searcher := &testDBSearcher{
absPackagesDB: map[string]string{"linux": "core"}, absPackagesDB: map[string]string{"linux": "core"},
} }
fetched, err := PKGBUILDs(searcher, mockClient, &http.Client{}, testLogger.Child("test"), fetched, err := PKGBUILDs(searcher, mockClient, &http.Client{},
targets, "https://aur.archlinux.org", parser.ModeAny) targets, "https://aur.archlinux.org", parser.ModeAny)
assert.NoError(t, err) assert.NoError(t, err)

View File

@ -5,7 +5,6 @@ package download
import ( import (
"context" "context"
"io"
"net/http" "net/http"
"os" "os"
"path/filepath" "path/filepath"
@ -23,10 +22,6 @@ import (
"github.com/Jguer/yay/v12/pkg/text" "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 2 aur packages and 1 in repo
// GIVEN package in repo is already present // GIVEN package in repo is already present
// WHEN defining package db as a target // WHEN defining package db as a target
@ -61,7 +56,7 @@ func TestPKGBUILDReposDefinedDBPull(t *testing.T) {
absPackagesDB: map[string]string{"yay": "core"}, absPackagesDB: map[string]string{"yay": "core"},
} }
cloned, err := PKGBUILDRepos(context.Background(), searcher, mockClient, cloned, err := PKGBUILDRepos(context.Background(), searcher, mockClient,
cmdBuilder, newTestLogger(), cmdBuilder,
targets, parser.ModeAny, "https://aur.archlinux.org", dir, false) targets, parser.ModeAny, "https://aur.archlinux.org", dir, false)
assert.NoError(t, err) assert.NoError(t, err)
@ -95,7 +90,7 @@ func TestPKGBUILDReposDefinedDBClone(t *testing.T) {
absPackagesDB: map[string]string{"yay": "core"}, absPackagesDB: map[string]string{"yay": "core"},
} }
cloned, err := PKGBUILDRepos(context.Background(), searcher, mockClient, cloned, err := PKGBUILDRepos(context.Background(), searcher, mockClient,
cmdBuilder, newTestLogger(), cmdBuilder,
targets, parser.ModeAny, "https://aur.archlinux.org", dir, false) targets, parser.ModeAny, "https://aur.archlinux.org", dir, false)
assert.NoError(t, err) assert.NoError(t, err)
@ -129,7 +124,7 @@ func TestPKGBUILDReposClone(t *testing.T) {
absPackagesDB: map[string]string{"yay": "core"}, absPackagesDB: map[string]string{"yay": "core"},
} }
cloned, err := PKGBUILDRepos(context.Background(), searcher, mockClient, cloned, err := PKGBUILDRepos(context.Background(), searcher, mockClient,
cmdBuilder, newTestLogger(), cmdBuilder,
targets, parser.ModeAny, "https://aur.archlinux.org", dir, false) targets, parser.ModeAny, "https://aur.archlinux.org", dir, false)
assert.NoError(t, err) assert.NoError(t, err)
@ -163,7 +158,7 @@ func TestPKGBUILDReposNotFound(t *testing.T) {
absPackagesDB: map[string]string{"yay": "core"}, absPackagesDB: map[string]string{"yay": "core"},
} }
cloned, err := PKGBUILDRepos(context.Background(), searcher, mockClient, cloned, err := PKGBUILDRepos(context.Background(), searcher, mockClient,
cmdBuilder, newTestLogger(), cmdBuilder,
targets, parser.ModeAny, "https://aur.archlinux.org", dir, false) targets, parser.ModeAny, "https://aur.archlinux.org", dir, false)
assert.NoError(t, err) assert.NoError(t, err)
@ -197,7 +192,7 @@ func TestPKGBUILDReposRepoMode(t *testing.T) {
absPackagesDB: map[string]string{"yay": "core"}, absPackagesDB: map[string]string{"yay": "core"},
} }
cloned, err := PKGBUILDRepos(context.Background(), searcher, mockClient, cloned, err := PKGBUILDRepos(context.Background(), searcher, mockClient,
cmdBuilder, newTestLogger(), cmdBuilder,
targets, parser.ModeRepo, "https://aur.archlinux.org", dir, false) targets, parser.ModeRepo, "https://aur.archlinux.org", dir, false)
assert.NoError(t, err) assert.NoError(t, err)
@ -235,7 +230,7 @@ func TestPKGBUILDFull(t *testing.T) {
absPackagesDB: map[string]string{"yay": "core"}, absPackagesDB: map[string]string{"yay": "core"},
} }
fetched, err := PKGBUILDs(searcher, mockClient, &http.Client{}, newTestLogger(), fetched, err := PKGBUILDs(searcher, mockClient, &http.Client{},
targets, "https://aur.archlinux.org", parser.ModeAny) targets, "https://aur.archlinux.org", parser.ModeAny)
assert.NoError(t, err) assert.NoError(t, err)
@ -273,7 +268,7 @@ func TestPKGBUILDReposMissingAUR(t *testing.T) {
absPackagesDB: map[string]string{"yay": "core"}, absPackagesDB: map[string]string{"yay": "core"},
} }
cloned, err := PKGBUILDRepos(context.Background(), searcher, mockClient, cloned, err := PKGBUILDRepos(context.Background(), searcher, mockClient,
cmdBuilder, newTestLogger(), cmdBuilder,
targets, parser.ModeAny, "https://aur.archlinux.org", dir, false) targets, parser.ModeAny, "https://aur.archlinux.org", dir, false)
assert.NoError(t, err) assert.NoError(t, err)

View File

@ -102,7 +102,7 @@ func (d *testDBSearcher) SyncPackage(name string) db.IPackage {
return nil return nil
} }
func (d *testDBSearcher) SyncPackageFromDB(name string, db string) db.IPackage { func (d *testDBSearcher) SatisfierFromDB(name string, db string) db.IPackage {
if v, ok := d.absPackagesDB[name]; ok && v == db { if v, ok := d.absPackagesDB[name]; ok && v == db {
return &testPackage{ return &testPackage{
name: name, name: name,

View File

@ -5,7 +5,7 @@ import (
"strings" "strings"
"unicode" "unicode"
mapset "github.com/deckarep/golang-set/v2" "github.com/Jguer/yay/v12/pkg/stringset"
) )
// IntRange stores a max and min amount for range. // IntRange stores a max and min amount for range.
@ -17,10 +17,10 @@ type IntRange struct {
// IntRanges is a slice of IntRange. // IntRanges is a slice of IntRange.
type IntRanges []IntRange type IntRanges []IntRange
func makeIntRange(minVal, maxVal int) IntRange { func makeIntRange(min, max int) IntRange {
return IntRange{ return IntRange{
min: minVal, min,
max: maxVal, max,
} }
} }
@ -42,6 +42,24 @@ func (rs IntRanges) Get(n int) bool {
return false return false
} }
// Min returns min value between a and b.
func Min(a, b int) int {
if a < b {
return a
}
return b
}
// Max returns max value between a and b.
func Max(a, b int) int {
if a < b {
return b
}
return a
}
// ParseNumberMenu parses input for number menus split by spaces or commas // ParseNumberMenu parses input for number menus split by spaces or commas
// supports individual selection: 1 2 3 4 // supports individual selection: 1 2 3 4
// supports range selections: 1-4 10-20 // supports range selections: 1-4 10-20
@ -53,12 +71,12 @@ func (rs IntRanges) Get(n int) bool {
// of course the implementation is up to the caller, this function mearley parses // of course the implementation is up to the caller, this function mearley parses
// the input and organizes it. // the input and organizes it.
func ParseNumberMenu(input string) (include, exclude IntRanges, func ParseNumberMenu(input string) (include, exclude IntRanges,
otherInclude, otherExclude mapset.Set[string], otherInclude, otherExclude stringset.StringSet,
) { ) {
include = make(IntRanges, 0) include = make(IntRanges, 0)
exclude = make(IntRanges, 0) exclude = make(IntRanges, 0)
otherInclude = mapset.NewThreadUnsafeSet[string]() otherInclude = make(stringset.StringSet)
otherExclude = mapset.NewThreadUnsafeSet[string]() otherExclude = make(stringset.StringSet)
words := strings.FieldsFunc(input, func(c rune) bool { words := strings.FieldsFunc(input, func(c rune) bool {
return unicode.IsSpace(c) || c == ',' return unicode.IsSpace(c) || c == ','
@ -84,22 +102,22 @@ func ParseNumberMenu(input string) (include, exclude IntRanges,
num1, err = strconv.Atoi(ranges[0]) num1, err = strconv.Atoi(ranges[0])
if err != nil { if err != nil {
other.Add(strings.ToLower(word)) other.Set(strings.ToLower(word))
continue continue
} }
if len(ranges) == 2 { if len(ranges) == 2 {
num2, err = strconv.Atoi(ranges[1]) num2, err = strconv.Atoi(ranges[1])
if err != nil { if err != nil {
other.Add(strings.ToLower(word)) other.Set(strings.ToLower(word))
continue continue
} }
} else { } else {
num2 = num1 num2 = num1
} }
mi := min(num1, num2) mi := Min(num1, num2)
ma := max(num1, num2) ma := Max(num1, num2)
if !invert { if !invert {
include = append(include, makeIntRange(mi, ma)) include = append(include, makeIntRange(mi, ma))

View File

@ -6,8 +6,7 @@ package intrange
import ( import (
"testing" "testing"
mapset "github.com/deckarep/golang-set/v2" "github.com/Jguer/yay/v12/pkg/stringset"
"github.com/stretchr/testify/assert"
) )
func TestParseNumberMenu(t *testing.T) { func TestParseNumberMenu(t *testing.T) {
@ -15,8 +14,8 @@ func TestParseNumberMenu(t *testing.T) {
type result struct { type result struct {
Include IntRanges Include IntRanges
Exclude IntRanges Exclude IntRanges
OtherInclude mapset.Set[string] OtherInclude stringset.StringSet
OtherExclude mapset.Set[string] OtherExclude stringset.StringSet
} }
inputs := []string{ inputs := []string{
@ -41,15 +40,15 @@ func TestParseNumberMenu(t *testing.T) {
makeIntRange(3, 3), makeIntRange(3, 3),
makeIntRange(4, 4), makeIntRange(4, 4),
makeIntRange(5, 5), makeIntRange(5, 5),
}, IntRanges{}, mapset.NewThreadUnsafeSet[string](), mapset.NewThreadUnsafeSet[string]()}, }, IntRanges{}, make(stringset.StringSet), make(stringset.StringSet)},
{IntRanges{ {IntRanges{
makeIntRange(1, 10), makeIntRange(1, 10),
makeIntRange(5, 15), makeIntRange(5, 15),
}, IntRanges{}, mapset.NewThreadUnsafeSet[string](), mapset.NewThreadUnsafeSet[string]()}, }, IntRanges{}, make(stringset.StringSet), make(stringset.StringSet)},
{IntRanges{ {IntRanges{
makeIntRange(5, 10), makeIntRange(5, 10),
makeIntRange(85, 90), makeIntRange(85, 90),
}, IntRanges{}, mapset.NewThreadUnsafeSet[string](), mapset.NewThreadUnsafeSet[string]()}, }, IntRanges{}, make(stringset.StringSet), make(stringset.StringSet)},
{ {
IntRanges{ IntRanges{
makeIntRange(1, 1), makeIntRange(1, 1),
@ -62,18 +61,18 @@ func TestParseNumberMenu(t *testing.T) {
makeIntRange(38, 40), makeIntRange(38, 40),
makeIntRange(123, 123), makeIntRange(123, 123),
}, },
mapset.NewThreadUnsafeSet[string](), mapset.NewThreadUnsafeSet[string](), make(stringset.StringSet), make(stringset.StringSet),
}, },
{IntRanges{}, IntRanges{}, mapset.NewThreadUnsafeSet("abort", "all", "none"), mapset.NewThreadUnsafeSet[string]()}, {IntRanges{}, IntRanges{}, stringset.Make("abort", "all", "none"), make(stringset.StringSet)},
{IntRanges{}, IntRanges{}, mapset.NewThreadUnsafeSet("a-b"), mapset.NewThreadUnsafeSet("abort", "a-b")}, {IntRanges{}, IntRanges{}, stringset.Make("a-b"), stringset.Make("abort", "a-b")},
{IntRanges{}, IntRanges{}, mapset.NewThreadUnsafeSet("-9223372036854775809-9223372036854775809"), mapset.NewThreadUnsafeSet[string]()}, {IntRanges{}, IntRanges{}, stringset.Make("-9223372036854775809-9223372036854775809"), make(stringset.StringSet)},
{IntRanges{ {IntRanges{
makeIntRange(1, 1), makeIntRange(1, 1),
makeIntRange(2, 2), makeIntRange(2, 2),
makeIntRange(3, 3), makeIntRange(3, 3),
makeIntRange(4, 4), makeIntRange(4, 4),
makeIntRange(5, 5), makeIntRange(5, 5),
}, IntRanges{}, mapset.NewThreadUnsafeSet[string](), mapset.NewThreadUnsafeSet[string]()}, }, IntRanges{}, make(stringset.StringSet), make(stringset.StringSet)},
{IntRanges{ {IntRanges{
makeIntRange(1, 1), makeIntRange(1, 1),
makeIntRange(2, 2), makeIntRange(2, 2),
@ -83,20 +82,23 @@ func TestParseNumberMenu(t *testing.T) {
makeIntRange(6, 6), makeIntRange(6, 6),
makeIntRange(7, 7), makeIntRange(7, 7),
makeIntRange(8, 8), makeIntRange(8, 8),
}, IntRanges{}, mapset.NewThreadUnsafeSet[string](), mapset.NewThreadUnsafeSet[string]()}, }, IntRanges{}, make(stringset.StringSet), make(stringset.StringSet)},
{IntRanges{}, IntRanges{}, mapset.NewThreadUnsafeSet[string](), mapset.NewThreadUnsafeSet[string]()}, {IntRanges{}, IntRanges{}, make(stringset.StringSet), make(stringset.StringSet)},
{IntRanges{}, IntRanges{}, mapset.NewThreadUnsafeSet[string](), mapset.NewThreadUnsafeSet[string]()}, {IntRanges{}, IntRanges{}, make(stringset.StringSet), make(stringset.StringSet)},
{IntRanges{}, IntRanges{}, mapset.NewThreadUnsafeSet("a", "b", "c", "d", "e"), mapset.NewThreadUnsafeSet[string]()}, {IntRanges{}, IntRanges{}, stringset.Make("a", "b", "c", "d", "e"), make(stringset.StringSet)},
} }
for n, in := range inputs { for n, in := range inputs {
res := expected[n] res := expected[n]
include, exclude, otherInclude, otherExclude := ParseNumberMenu(in) 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) if !intRangesEqual(include, res.Include) ||
assert.True(t, intRangesEqual(exclude, res.Exclude), "Test %d Failed: Expected: exclude=%+v got exclude=%+v", n+1, res.Exclude, exclude) !intRangesEqual(exclude, res.Exclude) ||
assert.True(t, otherInclude.Equal(res.OtherInclude), "Test %d Failed: Expected: otherInclude=%+v got otherInclude=%+v", n+1, res.OtherInclude, otherInclude) !stringset.Equal(otherInclude, res.OtherInclude) ||
assert.True(t, otherExclude.Equal(res.OtherExclude), "Test %d Failed: Expected: otherExclude=%+v got otherExclude=%+v", n+1, res.OtherExclude, otherExclude) !stringset.Equal(otherExclude, res.OtherExclude) {
t.Fatalf("Test %d Failed: Expected: include=%+v exclude=%+v otherInclude=%+v otherExclude=%+v got include=%+v excluive=%+v otherInclude=%+v otherExclude=%+v",
n+1, res.Include, res.Exclude, res.OtherInclude, res.OtherExclude, include, exclude, otherInclude, otherExclude)
}
} }
} }

View File

@ -9,7 +9,6 @@ import (
mapset "github.com/deckarep/golang-set/v2" mapset "github.com/deckarep/golang-set/v2"
"github.com/leonelquinteros/gotext" "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"
"github.com/Jguer/yay/v12/pkg/text" "github.com/Jguer/yay/v12/pkg/text"
) )
@ -24,9 +23,7 @@ func anyExistInCache(pkgbuildDirs map[string]string) bool {
return false return false
} }
func CleanFn(ctx context.Context, run *runtime.Runtime, w io.Writer, func CleanFn(ctx context.Context, config *settings.Configuration, w io.Writer, pkgbuildDirsByBase map[string]string) error {
pkgbuildDirsByBase map[string]string, installed mapset.Set[string],
) error {
if len(pkgbuildDirsByBase) == 0 { if len(pkgbuildDirsByBase) == 0 {
return nil // no work to do return nil // no work to do
} }
@ -50,25 +47,26 @@ func CleanFn(ctx context.Context, run *runtime.Runtime, w io.Writer,
bases = append(bases, pkg) bases = append(bases, pkg)
} }
toClean, errClean := selectionMenu(run.Logger, pkgbuildDirsByBase, bases, installed, // TOFIX: empty installed slice means installed filter is disabled
toClean, errClean := selectionMenu(w, pkgbuildDirsByBase, bases, mapset.NewSet[string](),
gotext.Get("Packages to cleanBuild?"), gotext.Get("Packages to cleanBuild?"),
settings.NoConfirm, run.Cfg.AnswerClean, skipFunc) settings.NoConfirm, config.AnswerClean, skipFunc)
if errClean != nil { if errClean != nil {
return errClean return errClean
} }
for i, base := range toClean { for i, base := range toClean {
dir := pkgbuildDirsByBase[base] dir := pkgbuildDirsByBase[base]
run.Logger.OperationInfoln(gotext.Get("Deleting (%d/%d): %s", i+1, len(toClean), text.Cyan(dir))) text.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 { if err := config.Runtime.CmdBuilder.Show(config.Runtime.CmdBuilder.BuildGitCmd(ctx, dir, "reset", "--hard", "origin/HEAD")); err != nil {
run.Logger.Warnln(gotext.Get("Unable to clean:"), dir) text.Warnln(gotext.Get("Unable to clean:"), dir)
return err return err
} }
if err := run.CmdBuilder.Show(run.CmdBuilder.BuildGitCmd(ctx, dir, "clean", "-fdx")); err != nil { if err := config.Runtime.CmdBuilder.Show(config.Runtime.CmdBuilder.BuildGitCmd(ctx, dir, "clean", "-fdx")); err != nil {
run.Logger.Warnln(gotext.Get("Unable to clean:"), dir) text.Warnln(gotext.Get("Unable to clean:"), dir)
return err return err
} }

View File

@ -5,13 +5,13 @@ import (
"context" "context"
"fmt" "fmt"
"io" "io"
"os"
"strings" "strings"
mapset "github.com/deckarep/golang-set/v2" mapset "github.com/deckarep/golang-set/v2"
"github.com/leonelquinteros/gotext" "github.com/leonelquinteros/gotext"
"github.com/Jguer/yay/v12/pkg/multierror" "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"
"github.com/Jguer/yay/v12/pkg/settings/exe" "github.com/Jguer/yay/v12/pkg/settings/exe"
"github.com/Jguer/yay/v12/pkg/text" "github.com/Jguer/yay/v12/pkg/text"
@ -22,7 +22,7 @@ const (
gitDiffRefName = "AUR_SEEN" gitDiffRefName = "AUR_SEEN"
) )
func showPkgbuildDiffs(ctx context.Context, cmdBuilder exe.ICmdBuilder, logger *text.Logger, func showPkgbuildDiffs(ctx context.Context, cmdBuilder exe.ICmdBuilder,
pkgbuildDirs map[string]string, bases []string, pkgbuildDirs map[string]string, bases []string,
) error { ) error {
var errMulti multierror.MultiError var errMulti multierror.MultiError
@ -46,7 +46,7 @@ func showPkgbuildDiffs(ctx context.Context, cmdBuilder exe.ICmdBuilder, logger *
} }
if !hasDiff { if !hasDiff {
logger.Warnln(gotext.Get("%s: No changes -- skipping", text.Cyan(pkg))) text.Warnln(gotext.Get("%s: No changes -- skipping", text.Cyan(pkg)))
continue continue
} }
@ -85,13 +85,13 @@ func gitHasDiff(ctx context.Context, cmdBuilder exe.ICmdBuilder, dir string) (bo
return lastseen != upstream, nil return lastseen != upstream, nil
} }
// If AUR_SEEN does not exists, we have never reviewed a diff for this package // If YAY_DIFF_REVIEW does not exists, we have never reviewed a diff for this package
// and should display it. // and should display it.
return true, nil return true, nil
} }
// Return whether or not we have reviewed a diff yet. It checks for the existence of // Return wether or not we have reviewed a diff yet. It checks for the existence of
// AUR_SEEN in the git ref-list. // YAY_DIFF_REVIEW in the git ref-list.
func gitHasLastSeenRef(ctx context.Context, cmdBuilder exe.ICmdBuilder, dir string) bool { func gitHasLastSeenRef(ctx context.Context, cmdBuilder exe.ICmdBuilder, dir string) bool {
_, _, err := cmdBuilder.Capture( _, _, err := cmdBuilder.Capture(
cmdBuilder.BuildGitCmd(ctx, cmdBuilder.BuildGitCmd(ctx,
@ -100,7 +100,7 @@ func gitHasLastSeenRef(ctx context.Context, cmdBuilder exe.ICmdBuilder, dir stri
return err == nil return err == nil
} }
// Returns the last reviewed hash. If AUR_SEEN exists it will return this hash. // Returns the last reviewed hash. If YAY_DIFF_REVIEW exists it will return this hash.
// If it does not it will return empty tree as no diff have been reviewed yet. // 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) { func getLastSeenHash(ctx context.Context, cmdBuilder exe.ICmdBuilder, dir string) (string, error) {
if gitHasLastSeenRef(ctx, cmdBuilder, dir) { if gitHasLastSeenRef(ctx, cmdBuilder, dir) {
@ -119,7 +119,7 @@ func getLastSeenHash(ctx context.Context, cmdBuilder exe.ICmdBuilder, dir string
return gitEmptyTree, nil return gitEmptyTree, nil
} }
// Update the AUR_SEEN ref to HEAD. We use this ref to determine which diff were // Update the YAY_DIFF_REVIEW ref to HEAD. We use this ref to determine which diff were
// reviewed by the user. // reviewed by the user.
func gitUpdateSeenRef(ctx context.Context, cmdBuilder exe.ICmdBuilder, dir string) error { func gitUpdateSeenRef(ctx context.Context, cmdBuilder exe.ICmdBuilder, dir string) error {
_, stderr, err := cmdBuilder.Capture( _, stderr, err := cmdBuilder.Capture(
@ -145,9 +145,7 @@ func updatePkgbuildSeenRef(ctx context.Context, cmdBuilder exe.ICmdBuilder, pkgb
return errMulti.Return() return errMulti.Return()
} }
func DiffFn(ctx context.Context, run *runtime.Runtime, w io.Writer, func DiffFn(ctx context.Context, config *settings.Configuration, w io.Writer, pkgbuildDirsByBase map[string]string) error {
pkgbuildDirsByBase map[string]string, installed mapset.Set[string],
) error {
if len(pkgbuildDirsByBase) == 0 { if len(pkgbuildDirsByBase) == 0 {
return nil // no work to do return nil // no work to do
} }
@ -157,23 +155,23 @@ func DiffFn(ctx context.Context, run *runtime.Runtime, w io.Writer,
bases = append(bases, base) bases = append(bases, base)
} }
toDiff, errMenu := selectionMenu(run.Logger, pkgbuildDirsByBase, bases, installed, gotext.Get("Diffs to show?"), toDiff, errMenu := selectionMenu(w, pkgbuildDirsByBase, bases, mapset.NewThreadUnsafeSet[string](), gotext.Get("Diffs to show?"),
settings.NoConfirm, run.Cfg.AnswerDiff, nil) settings.NoConfirm, config.AnswerDiff, nil)
if errMenu != nil || len(toDiff) == 0 { if errMenu != nil || len(toDiff) == 0 {
return errMenu return errMenu
} }
if errD := showPkgbuildDiffs(ctx, run.CmdBuilder, run.Logger, pkgbuildDirsByBase, toDiff); errD != nil { if errD := showPkgbuildDiffs(ctx, config.Runtime.CmdBuilder, pkgbuildDirsByBase, toDiff); errD != nil {
return errD return errD
} }
run.Logger.Println() fmt.Println()
if !run.Logger.ContinueTask(gotext.Get("Proceed with install?"), true, false) { if !text.ContinueTask(os.Stdin, gotext.Get("Proceed with install?"), true, false) {
return settings.ErrUserAbort{} return settings.ErrUserAbort{}
} }
if errUpd := updatePkgbuildSeenRef(ctx, run.CmdBuilder, pkgbuildDirsByBase, toDiff); errUpd != nil { if errUpd := updatePkgbuildSeenRef(ctx, config.Runtime.CmdBuilder, pkgbuildDirsByBase, toDiff); errUpd != nil {
return errUpd return errUpd
} }

View File

@ -14,7 +14,6 @@ import (
mapset "github.com/deckarep/golang-set/v2" mapset "github.com/deckarep/golang-set/v2"
"github.com/leonelquinteros/gotext" "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"
"github.com/Jguer/yay/v12/pkg/text" "github.com/Jguer/yay/v12/pkg/text"
) )
@ -60,7 +59,7 @@ func editor(log *text.Logger, editorConfig, editorFlags string, noConfirm bool)
for { for {
log.Infoln(gotext.Get("Edit PKGBUILD with?")) log.Infoln(gotext.Get("Edit PKGBUILD with?"))
editorInput, err := log.GetInput("", noConfirm) editorInput, err := text.GetInput(os.Stdin, "", noConfirm)
if err != nil { if err != nil {
log.Errorln(err) log.Errorln(err)
continue continue
@ -114,8 +113,8 @@ func editPkgbuilds(log *text.Logger, pkgbuildDirs map[string]string, bases []str
return nil return nil
} }
func EditFn(ctx context.Context, run *runtime.Runtime, w io.Writer, func EditFn(ctx context.Context, cfg *settings.Configuration, w io.Writer,
pkgbuildDirsByBase map[string]string, installed mapset.Set[string], pkgbuildDirsByBase map[string]string,
) error { ) error {
if len(pkgbuildDirsByBase) == 0 { if len(pkgbuildDirsByBase) == 0 {
return nil // no work to do return nil // no work to do
@ -126,21 +125,22 @@ func EditFn(ctx context.Context, run *runtime.Runtime, w io.Writer,
bases = append(bases, pkg) bases = append(bases, pkg)
} }
toEdit, errMenu := selectionMenu(run.Logger, pkgbuildDirsByBase, bases, installed, toEdit, errMenu := selectionMenu(w, pkgbuildDirsByBase, bases,
gotext.Get("PKGBUILDs to edit?"), settings.NoConfirm, run.Cfg.AnswerEdit, nil) mapset.NewThreadUnsafeSet[string](),
gotext.Get("PKGBUILDs to edit?"), settings.NoConfirm, cfg.AnswerEdit, nil)
if errMenu != nil || len(toEdit) == 0 { if errMenu != nil || len(toEdit) == 0 {
return errMenu return errMenu
} }
// TOFIX: remove or use srcinfo data // TOFIX: remove or use srcinfo data
if errEdit := editPkgbuilds(run.Logger, pkgbuildDirsByBase, if errEdit := editPkgbuilds(cfg.Runtime.Logger, pkgbuildDirsByBase,
toEdit, run.Cfg.Editor, run.Cfg.EditorFlags, nil, settings.NoConfirm); errEdit != nil { toEdit, cfg.Editor, cfg.EditorFlags, nil, settings.NoConfirm); errEdit != nil {
return errEdit return errEdit
} }
run.Logger.Println() cfg.Runtime.Logger.Println()
if !run.Logger.ContinueTask(gotext.Get("Proceed with install?"), true, false) { if !text.ContinueTask(os.Stdin, gotext.Get("Proceed with install?"), true, false) {
return settings.ErrUserAbort{} return settings.ErrUserAbort{}
} }

View File

@ -2,6 +2,7 @@ package menus
import ( import (
"fmt" "fmt"
"io"
"os" "os"
"github.com/leonelquinteros/gotext" "github.com/leonelquinteros/gotext"
@ -13,9 +14,7 @@ import (
mapset "github.com/deckarep/golang-set/v2" mapset "github.com/deckarep/golang-set/v2"
) )
func pkgbuildNumberMenu(logger *text.Logger, pkgbuildDirs map[string]string, func pkgbuildNumberMenu(w io.Writer, pkgbuildDirs map[string]string, bases []string, installed mapset.Set[string]) {
bases []string, installed mapset.Set[string],
) {
toPrint := "" toPrint := ""
for n, pkgBase := range bases { for n, pkgBase := range bases {
@ -35,32 +34,32 @@ func pkgbuildNumberMenu(logger *text.Logger, pkgbuildDirs map[string]string,
toPrint += "\n" toPrint += "\n"
} }
logger.Print(toPrint) fmt.Fprint(w, toPrint)
} }
func selectionMenu(logger *text.Logger, pkgbuildDirs map[string]string, bases []string, installed mapset.Set[string], func selectionMenu(w io.Writer, pkgbuildDirs map[string]string, bases []string, installed mapset.Set[string],
message string, noConfirm bool, defaultAnswer string, skipFunc func(string) bool, message string, noConfirm bool, defaultAnswer string, skipFunc func(string) bool,
) ([]string, error) { ) ([]string, error) {
selected := make([]string, 0) selected := make([]string, 0)
pkgbuildNumberMenu(logger, pkgbuildDirs, bases, installed) pkgbuildNumberMenu(w, pkgbuildDirs, bases, installed)
logger.Infoln(message) text.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")))) text.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) selectInput, err := text.GetInput(os.Stdin, defaultAnswer, noConfirm)
if err != nil { if err != nil {
return nil, err return nil, err
} }
eInclude, eExclude, eOtherInclude, eOtherExclude := intrange.ParseNumberMenu(selectInput) eInclude, eExclude, eOtherInclude, eOtherExclude := intrange.ParseNumberMenu(selectInput)
eIsInclude := len(eExclude) == 0 && eOtherExclude.Cardinality() == 0 eIsInclude := len(eExclude) == 0 && len(eOtherExclude) == 0
if eOtherInclude.Contains("abort") || eOtherInclude.Contains("ab") { if eOtherInclude.Get("abort") || eOtherInclude.Get("ab") {
return nil, settings.ErrUserAbort{} return nil, settings.ErrUserAbort{}
} }
if eOtherInclude.Contains("n") || eOtherInclude.Contains("none") { if eOtherInclude.Get("n") || eOtherInclude.Get("none") {
return selected, nil return selected, nil
} }
@ -75,26 +74,26 @@ func selectionMenu(logger *text.Logger, pkgbuildDirs map[string]string, bases []
continue continue
} }
if anyInstalled && (eOtherInclude.Contains("i") || eOtherInclude.Contains("installed")) { if anyInstalled && (eOtherInclude.Get("i") || eOtherInclude.Get("installed")) {
selected = append(selected, pkgBase) selected = append(selected, pkgBase)
continue continue
} }
if !anyInstalled && (eOtherInclude.Contains("no") || eOtherInclude.Contains("notinstalled")) { if !anyInstalled && (eOtherInclude.Get("no") || eOtherInclude.Get("notinstalled")) {
selected = append(selected, pkgBase) selected = append(selected, pkgBase)
continue continue
} }
if eOtherInclude.Contains("a") || eOtherInclude.Contains("all") { if eOtherInclude.Get("a") || eOtherInclude.Get("all") {
selected = append(selected, pkgBase) selected = append(selected, pkgBase)
continue continue
} }
if eIsInclude && (eInclude.Get(len(bases)-i) || eOtherInclude.Contains(pkgBase)) { if eIsInclude && (eInclude.Get(len(bases)-i) || eOtherInclude.Get(pkgBase)) {
selected = append(selected, pkgBase) selected = append(selected, pkgBase)
} }
if !eIsInclude && (!eExclude.Get(len(bases)-i) && !eOtherExclude.Contains(pkgBase)) { if !eIsInclude && (!eExclude.Get(len(bases)-i) && !eOtherExclude.Get(pkgBase)) {
selected = append(selected, pkgBase) selected = append(selected, pkgBase)
} }
} }

View File

@ -4,9 +4,11 @@ import (
"bytes" "bytes"
"context" "context"
"encoding/xml" "encoding/xml"
"fmt"
"html" "html"
"io" "io"
"net/http" "net/http"
"os"
"strings" "strings"
"time" "time"
@ -21,13 +23,13 @@ type item struct {
Creator string `xml:"dc:creator"` Creator string `xml:"dc:creator"`
} }
func (item *item) printNews(logger *text.Logger, buildTime time.Time, all, quiet bool) { func (item *item) print(buildTime time.Time, all, quiet bool) {
var fd string var fd string
date, err := time.Parse(time.RFC1123Z, item.PubDate) date, err := time.Parse(time.RFC1123Z, item.PubDate)
if err != nil { if err != nil {
logger.Errorln(err) fmt.Fprintln(os.Stderr, err)
} else { } else {
fd = text.FormatTime(int(date.Unix())) fd = text.FormatTime(int(date.Unix()))
if !all && !buildTime.IsZero() { if !all && !buildTime.IsZero() {
@ -37,11 +39,11 @@ func (item *item) printNews(logger *text.Logger, buildTime time.Time, all, quiet
} }
} }
logger.Println(text.Bold(text.Magenta(fd)), text.Bold(strings.TrimSpace(item.Title))) fmt.Println(text.Bold(text.Magenta(fd)), text.Bold(strings.TrimSpace(item.Title)))
if !quiet { if !quiet {
desc := strings.TrimSpace(parseNews(item.Description)) desc := strings.TrimSpace(parseNews(item.Description))
logger.Println(desc) fmt.Println(desc)
} }
} }
@ -58,9 +60,7 @@ type rss struct {
Channel channel `xml:"channel"` Channel channel `xml:"channel"`
} }
func PrintNewsFeed(ctx context.Context, client *http.Client, logger *text.Logger, func PrintNewsFeed(ctx context.Context, client *http.Client, cutOffDate time.Time, bottomUp, all, quiet bool) error {
cutOffDate time.Time, bottomUp, all, quiet bool,
) error {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://archlinux.org/feeds/news", http.NoBody) req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://archlinux.org/feeds/news", http.NoBody)
if err != nil { if err != nil {
return err return err
@ -87,11 +87,11 @@ func PrintNewsFeed(ctx context.Context, client *http.Client, logger *text.Logger
if bottomUp { if bottomUp {
for i := len(rssGot.Channel.Items) - 1; i >= 0; i-- { for i := len(rssGot.Channel.Items) - 1; i >= 0; i-- {
rssGot.Channel.Items[i].printNews(logger, cutOffDate, all, quiet) rssGot.Channel.Items[i].print(cutOffDate, all, quiet)
} }
} else { } else {
for i := 0; i < len(rssGot.Channel.Items); i++ { for i := 0; i < len(rssGot.Channel.Items); i++ {
rssGot.Channel.Items[i].printNews(logger, cutOffDate, all, quiet) rssGot.Channel.Items[i].print(cutOffDate, all, quiet)
} }
} }

View File

@ -8,15 +8,12 @@ import (
"io" "io"
"net/http" "net/http"
"os" "os"
"strings"
"testing" "testing"
"time" "time"
"github.com/bradleyjkemp/cupaloy" "github.com/bradleyjkemp/cupaloy"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"gopkg.in/h2non/gock.v1" "gopkg.in/h2non/gock.v1"
"github.com/Jguer/yay/v12/pkg/text"
) )
const lastNews = ` const lastNews = `
@ -138,16 +135,17 @@ func TestPrintNewsFeed(t *testing.T) {
defer gock.Off() defer gock.Off()
rescueStdout := os.Stdout
r, w, _ := os.Pipe() r, w, _ := os.Pipe()
logger := text.NewLogger(w, w, strings.NewReader(""), false, "logger") os.Stdout = w
err := PrintNewsFeed(context.Background(), &http.Client{}, logger, err := PrintNewsFeed(context.Background(), &http.Client{}, tt.args.cutOffDate, tt.args.bottomUp, tt.args.all, tt.args.quiet)
tt.args.cutOffDate, tt.args.bottomUp, tt.args.all, tt.args.quiet)
assert.NoError(t, err) assert.NoError(t, err)
w.Close() w.Close()
out, _ := io.ReadAll(r) out, _ := io.ReadAll(r)
cupaloy.SnapshotT(t, out) cupaloy.SnapshotT(t, out)
os.Stdout = rescueStdout
}) })
} }
} }
@ -166,14 +164,15 @@ func TestPrintNewsFeedSameDay(t *testing.T) {
defer gock.Off() defer gock.Off()
rescueStdout := os.Stdout
r, w, _ := os.Pipe() r, w, _ := os.Pipe()
logger := text.NewLogger(w, w, strings.NewReader(""), false, "logger") os.Stdout = w
err := PrintNewsFeed(context.Background(), &http.Client{}, logger, err := PrintNewsFeed(context.Background(), &http.Client{}, lastNewsTime, true, false, false)
lastNewsTime, true, false, false)
assert.NoError(t, err) assert.NoError(t, err)
w.Close() w.Close()
out, _ := io.ReadAll(r) out, _ := io.ReadAll(r)
cupaloy.SnapshotT(t, out) cupaloy.SnapshotT(t, out)
os.Stdout = rescueStdout
} }

View File

@ -4,6 +4,8 @@ import (
"bytes" "bytes"
"context" "context"
"errors" "errors"
"fmt"
"os"
"os/exec" "os/exec"
"strings" "strings"
@ -48,7 +50,7 @@ type GPGCmdBuilder interface {
// CheckPgpKeys iterates through the keys listed in the PKGBUILDs and if needed, // CheckPgpKeys iterates through the keys listed in the PKGBUILDs and if needed,
// asks the user whether yay should try to import them. // 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, func CheckPgpKeys(ctx context.Context, pkgbuildDirsByBase map[string]string, srcinfos map[string]*gosrc.Srcinfo,
cmdBuilder GPGCmdBuilder, noConfirm bool, cmdBuilder GPGCmdBuilder, noConfirm bool,
) ([]string, error) { ) ([]string, error) {
// Let's check the keys individually, and then we can offer to import // Let's check the keys individually, and then we can offer to import
@ -78,23 +80,24 @@ func CheckPgpKeys(ctx context.Context, logger *text.Logger, pkgbuildDirsByBase m
return []string{}, nil return []string{}, nil
} }
str, err := formatKeysToImport(logger, problematic) str, err := formatKeysToImport(problematic)
if err != nil { if err != nil {
return nil, err return nil, err
} }
logger.Println("\n", str) fmt.Println()
fmt.Println(str)
if logger.ContinueTask(gotext.Get("Import?"), true, noConfirm) { if text.ContinueTask(os.Stdin, gotext.Get("Import?"), true, noConfirm) {
return problematic.toSlice(), importKeys(ctx, logger, cmdBuilder, problematic.toSlice()) return problematic.toSlice(), importKeys(ctx, cmdBuilder, problematic.toSlice())
} }
return problematic.toSlice(), nil return problematic.toSlice(), nil
} }
// importKeys tries to import the list of keys specified in its argument. // 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 { func importKeys(ctx context.Context, cmdBuilder GPGCmdBuilder, keys []string) error {
logger.OperationInfoln(gotext.Get("Importing keys with gpg...")) text.OperationInfoln(gotext.Get("Importing keys with gpg..."))
if err := cmdBuilder.Show(cmdBuilder.BuildGPGCmd(ctx, append([]string{"--recv-keys"}, keys...)...)); err != nil { if err := cmdBuilder.Show(cmdBuilder.BuildGPGCmd(ctx, append([]string{"--recv-keys"}, keys...)...)); err != nil {
return errors.New(gotext.Get("problem importing keys")) return errors.New(gotext.Get("problem importing keys"))
@ -105,14 +108,14 @@ func importKeys(ctx context.Context, logger *text.Logger, cmdBuilder GPGCmdBuild
// formatKeysToImport receives a set of keys and returns a string containing the // formatKeysToImport receives a set of keys and returns a string containing the
// question asking the user wants to import the problematic keys. // question asking the user wants to import the problematic keys.
func formatKeysToImport(logger *text.Logger, keys pgpKeySet) (string, error) { func formatKeysToImport(keys pgpKeySet) (string, error) {
if len(keys) == 0 { if len(keys) == 0 {
return "", errors.New(gotext.Get("no keys to import")) return "", errors.New(gotext.Get("no keys to import"))
} }
var buffer bytes.Buffer var buffer bytes.Buffer
buffer.WriteString(logger.SprintOperationInfo(gotext.Get("PGP keys need importing:"))) buffer.WriteString(text.SprintOperationInfo(gotext.Get("PGP keys need importing:")))
for key, bases := range keys { for key, bases := range keys {
pkglist := "" pkglist := ""
@ -121,7 +124,7 @@ func formatKeysToImport(logger *text.Logger, keys pgpKeySet) (string, error) {
} }
pkglist = strings.TrimRight(pkglist, " ") pkglist = strings.TrimRight(pkglist, " ")
buffer.WriteString("\n" + logger.SprintWarn(gotext.Get("%s, required by: %s", text.Cyan(key), text.Cyan(pkglist)))) buffer.WriteString("\n" + text.SprintWarn(gotext.Get("%s, required by: %s", text.Cyan(key), text.Cyan(pkglist))))
} }
return buffer.String(), nil return buffer.String(), nil

View File

@ -6,7 +6,6 @@ package pgp
import ( import (
"context" "context"
"fmt" "fmt"
"io"
"os" "os"
"os/exec" "os/exec"
"sort" "sort"
@ -18,13 +17,8 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/Jguer/yay/v12/pkg/settings/exe" "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 { func makeSrcinfo(pkgbase string, pgpkeys ...string) *gosrc.Srcinfo {
srcinfo := gosrc.Srcinfo{} srcinfo := gosrc.Srcinfo{}
srcinfo.Pkgbase = pkgbase srcinfo.Pkgbase = pkgbase
@ -234,7 +228,7 @@ func TestCheckPgpKeys(t *testing.T) {
GPGFlags: []string{"--homedir /tmp"}, GPGFlags: []string{"--homedir /tmp"},
Runner: mockRunner, Runner: mockRunner,
} }
problematic, err := CheckPgpKeys(context.Background(), newTestLogger(), tt.pkgs, tt.srcinfos, &cmdBuilder, true) problematic, err := CheckPgpKeys(context.Background(), tt.pkgs, tt.srcinfos, &cmdBuilder, true)
require.Len(t, mockRunner.ShowCalls, len(tt.wantShow)) require.Len(t, mockRunner.ShowCalls, len(tt.wantShow))
require.Len(t, mockRunner.CaptureCalls, len(tt.wantCapture)) require.Len(t, mockRunner.CaptureCalls, len(tt.wantCapture))

View File

@ -9,6 +9,7 @@ import (
"github.com/Jguer/go-alpm/v2" "github.com/Jguer/go-alpm/v2"
"github.com/Jguer/yay/v12/pkg/db" "github.com/Jguer/yay/v12/pkg/db"
"github.com/Jguer/yay/v12/pkg/stringset"
"github.com/Jguer/yay/v12/pkg/text" "github.com/Jguer/yay/v12/pkg/text"
) )
@ -17,12 +18,16 @@ type AURWarnings struct {
OutOfDate []string OutOfDate []string
Missing []string Missing []string
LocalNewer []string LocalNewer []string
Ignore stringset.StringSet
log *text.Logger log *text.Logger
} }
func NewWarnings(logger *text.Logger) *AURWarnings { func NewWarnings(logger *text.Logger) *AURWarnings {
return &AURWarnings{log: logger} if logger == nil {
logger = text.GlobalLogger
}
return &AURWarnings{Ignore: make(stringset.StringSet), log: logger}
} }
func (warnings *AURWarnings) AddToWarnings(remote map[string]alpm.IPackage, aurPkg *aur.Pkg) { func (warnings *AURWarnings) AddToWarnings(remote map[string]alpm.IPackage, aurPkg *aur.Pkg) {
@ -52,14 +57,10 @@ func (warnings *AURWarnings) AddToWarnings(remote map[string]alpm.IPackage, aurP
} }
} }
func (warnings *AURWarnings) CalculateMissing(remoteNames []string, func (warnings *AURWarnings) CalculateMissing(remoteNames []string, remote map[string]alpm.IPackage, aurData map[string]*aur.Pkg) {
remote map[string]alpm.IPackage, aurData map[string]*aur.Pkg,
) {
for _, name := range remoteNames { for _, name := range remoteNames {
if _, ok := aurData[name]; !ok && !remote[name].ShouldIgnore() { if _, ok := aurData[name]; !ok && !remote[name].ShouldIgnore() {
if _, ok := aurData[strings.TrimSuffix(name, "-debug")]; !ok { warnings.Missing = append(warnings.Missing, name)
warnings.Missing = append(warnings.Missing, name)
}
} }
} }
} }

View File

@ -7,19 +7,19 @@ import (
"github.com/Jguer/yay/v12/pkg/text" "github.com/Jguer/yay/v12/pkg/text"
) )
func RemoveInvalidTargets(logger *text.Logger, targets []string, mode parser.TargetMode) []string { func RemoveInvalidTargets(targets []string, mode parser.TargetMode) []string {
filteredTargets := make([]string, 0) filteredTargets := make([]string, 0)
for _, target := range targets { for _, target := range targets {
dbName, _ := text.SplitDBFromName(target) dbName, _ := text.SplitDBFromName(target)
if dbName == "aur" && !mode.AtLeastAUR() { if dbName == "aur" && !mode.AtLeastAUR() {
logger.Warnln(gotext.Get("%s: can't use target with option --repo -- skipping", text.Cyan(target))) text.Warnln(gotext.Get("%s: can't use target with option --repo -- skipping", text.Cyan(target)))
continue continue
} }
if dbName != "aur" && dbName != "" && !mode.AtLeastRepo() { if dbName != "aur" && dbName != "" && !mode.AtLeastRepo() {
logger.Warnln(gotext.Get("%s: can't use target with option --aur -- skipping", text.Cyan(target))) text.Warnln(gotext.Get("%s: can't use target with option --aur -- skipping", text.Cyan(target)))
continue continue
} }

View File

@ -11,12 +11,12 @@ import (
"github.com/Jguer/go-alpm/v2" "github.com/Jguer/go-alpm/v2"
"github.com/adrg/strutil" "github.com/adrg/strutil"
"github.com/adrg/strutil/metrics" "github.com/adrg/strutil/metrics"
mapset "github.com/deckarep/golang-set/v2"
"github.com/leonelquinteros/gotext" "github.com/leonelquinteros/gotext"
"github.com/Jguer/yay/v12/pkg/db" "github.com/Jguer/yay/v12/pkg/db"
"github.com/Jguer/yay/v12/pkg/intrange" "github.com/Jguer/yay/v12/pkg/intrange"
"github.com/Jguer/yay/v12/pkg/settings/parser" "github.com/Jguer/yay/v12/pkg/settings/parser"
"github.com/Jguer/yay/v12/pkg/stringset"
"github.com/Jguer/yay/v12/pkg/text" "github.com/Jguer/yay/v12/pkg/text"
) )
@ -35,7 +35,7 @@ type Builder interface {
Len() int Len() int
Execute(ctx context.Context, dbExecutor db.Executor, pkgS []string) Execute(ctx context.Context, dbExecutor db.Executor, pkgS []string)
Results(dbExecutor db.Executor, verboseSearch SearchVerbosity) error Results(dbExecutor db.Executor, verboseSearch SearchVerbosity) error
GetTargets(include, exclude intrange.IntRanges, otherExclude mapset.Set[string]) ([]string, error) GetTargets(include, exclude intrange.IntRanges, otherExclude stringset.StringSet) ([]string, error)
} }
type SourceQueryBuilder struct { type SourceQueryBuilder struct {
@ -130,7 +130,7 @@ func (a *abstractResults) Less(i, j int) bool {
func (s *SourceQueryBuilder) Execute(ctx context.Context, dbExecutor db.Executor, pkgS []string) { func (s *SourceQueryBuilder) Execute(ctx context.Context, dbExecutor db.Executor, pkgS []string) {
var aurErr error var aurErr error
pkgS = RemoveInvalidTargets(s.logger, pkgS, s.targetMode) pkgS = RemoveInvalidTargets(pkgS, s.targetMode)
metric := &metrics.Hamming{ metric := &metrics.Hamming{
CaseSensitive: false, CaseSensitive: false,
@ -253,10 +253,10 @@ func (s *SourceQueryBuilder) Len() int {
} }
func (s *SourceQueryBuilder) GetTargets(include, exclude intrange.IntRanges, func (s *SourceQueryBuilder) GetTargets(include, exclude intrange.IntRanges,
otherExclude mapset.Set[string], otherExclude stringset.StringSet,
) ([]string, error) { ) ([]string, error) {
var ( var (
isInclude = len(exclude) == 0 && otherExclude.Cardinality() == 0 isInclude = len(exclude) == 0 && len(otherExclude) == 0
targets []string targets []string
lenRes = len(s.results) lenRes = len(s.results)
) )
@ -289,7 +289,7 @@ func matchesSearch(pkg *aur.Pkg, terms []string) bool {
desc := strings.ToLower(pkg.Description) desc := strings.ToLower(pkg.Description)
targ := strings.ToLower(pkgN) targ := strings.ToLower(pkgN)
if !strings.Contains(name, targ) && !strings.Contains(desc, targ) { if !(strings.Contains(name, targ) || strings.Contains(desc, targ)) {
return false return false
} }
} }

View File

@ -37,7 +37,7 @@ func GetVersionDiff(oldVersion, newVersion string) (left, right string) {
} }
for index, char := range oldVersion { for index, char := range oldVersion {
charIsSpecial := !unicode.IsLetter(char) && !unicode.IsNumber(char) charIsSpecial := !(unicode.IsLetter(char) || unicode.IsNumber(char))
if (index >= len(newVersion)) || (char != rune(newVersion[index])) { if (index >= len(newVersion)) || (char != rune(newVersion[index])) {
if charIsSpecial { if charIsSpecial {

View File

@ -1,66 +0,0 @@
//go:build !integration
// +build !integration
package runtime
import (
"path/filepath"
"runtime"
"testing"
"github.com/Morganamilo/go-pacmanconf"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/Jguer/yay/v12/pkg/settings/parser"
)
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)
assert.EqualValues(t, expectedPacmanConf, pacmanConf)
}

View File

@ -5,6 +5,7 @@ import (
"strings" "strings"
"github.com/Jguer/yay/v12/pkg/settings/parser" "github.com/Jguer/yay/v12/pkg/settings/parser"
"github.com/Jguer/yay/v12/pkg/text"
) )
func (c *Configuration) ParseCommandLine(a *parser.Arguments) error { func (c *Configuration) ParseCommandLine(a *parser.Arguments) error {
@ -14,6 +15,9 @@ func (c *Configuration) ParseCommandLine(a *parser.Arguments) error {
c.extractYayOptions(a) c.extractYayOptions(a)
// Reload CmdBuilder
c.Runtime.CmdBuilder = c.CmdBuilder(nil)
return nil return nil
} }
@ -42,29 +46,29 @@ func (c *Configuration) extractYayOptions(a *parser.Arguments) {
} }
func (c *Configuration) handleOption(option, value string) bool { func (c *Configuration) handleOption(option, value string) bool {
boolValue, err := strconv.ParseBool(value)
if err != nil {
boolValue = true
}
switch option { switch option {
case "aururl": case "aururl":
c.AURURL = value c.AURURL = value
case "aurrpcurl": case "aurrpcurl":
c.AURRPCURL = value c.AURRPCURL = value
case "save": case "save":
c.SaveConfig = boolValue c.SaveConfig = true
case "afterclean", "cleanafter": case "afterclean", "cleanafter":
c.CleanAfter = boolValue c.CleanAfter = true
case "keepsrc": case "noafterclean", "nocleanafter":
c.KeepSrc = boolValue c.CleanAfter = false
case "debug": case "debug":
c.Debug = boolValue c.Debug = true
return !boolValue text.GlobalLogger.Debug = true
return false
case "devel": case "devel":
c.Devel = boolValue c.Devel = true
case "nodevel":
c.Devel = false
case "timeupdate": case "timeupdate":
c.TimeUpdate = boolValue c.TimeUpdate = true
case "notimeupdate":
c.TimeUpdate = false
case "topdown": case "topdown":
c.BottomUp = false c.BottomUp = false
case "bottomup": case "bottomup":
@ -83,7 +87,7 @@ func (c *Configuration) handleOption(option, value string) bool {
case "searchby": case "searchby":
c.SearchBy = value c.SearchBy = value
case "noconfirm": case "noconfirm":
NoConfirm = boolValue NoConfirm = true
case "config": case "config":
c.PacmanConf = value c.PacmanConf = value
case "redownload": case "redownload":
@ -101,7 +105,9 @@ func (c *Configuration) handleOption(option, value string) bool {
case "norebuild": case "norebuild":
c.ReBuild = parser.RebuildModeNo c.ReBuild = parser.RebuildModeNo
case "batchinstall": case "batchinstall":
c.BatchInstall = boolValue c.BatchInstall = true
case "nobatchinstall":
c.BatchInstall = false
case "answerclean": case "answerclean":
c.AnswerClean = value c.AnswerClean = value
case "noanswerclean": case "noanswerclean":
@ -152,24 +158,40 @@ func (c *Configuration) handleOption(option, value string) bool {
c.RequestSplitN = n c.RequestSplitN = n
} }
case "sudoloop": case "sudoloop":
c.SudoLoop = boolValue c.SudoLoop = true
case "nosudoloop":
c.SudoLoop = false
case "provides": case "provides":
c.Provides = boolValue c.Provides = true
case "noprovides":
c.Provides = false
case "pgpfetch": case "pgpfetch":
c.PGPFetch = boolValue c.PGPFetch = true
case "nopgpfetch":
c.PGPFetch = false
case "cleanmenu": case "cleanmenu":
c.CleanMenu = boolValue c.CleanMenu = true
case "nocleanmenu":
c.CleanMenu = false
case "diffmenu": case "diffmenu":
c.DiffMenu = boolValue c.DiffMenu = true
case "nodiffmenu":
c.DiffMenu = false
case "editmenu": case "editmenu":
c.EditMenu = boolValue c.EditMenu = true
case "noeditmenu":
c.EditMenu = false
case "useask": case "useask":
c.UseAsk = boolValue c.UseAsk = true
case "nouseask":
c.UseAsk = false
case "combinedupgrade": case "combinedupgrade":
c.CombinedUpgrade = boolValue c.CombinedUpgrade = true
case "nocombinedupgrade":
c.CombinedUpgrade = false
case "a", "aur": case "a", "aur":
c.Mode = parser.ModeAUR c.Mode = parser.ModeAUR
case "N", "repo": case "repo":
c.Mode = parser.ModeRepo c.Mode = parser.ModeRepo
case "removemake": case "removemake":
c.RemoveMake = "yes" c.RemoveMake = "yes"
@ -180,7 +202,9 @@ func (c *Configuration) handleOption(option, value string) bool {
case "askyesremovemake": case "askyesremovemake":
c.RemoveMake = "askyes" c.RemoveMake = "askyes"
case "separatesources": case "separatesources":
c.SeparateSources = boolValue c.SeparateSources = true
case "noseparatesources":
c.SeparateSources = false
default: default:
return false return false
} }

View File

@ -9,6 +9,7 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/Jguer/yay/v12/pkg/settings/exe"
"github.com/Jguer/yay/v12/pkg/settings/parser" "github.com/Jguer/yay/v12/pkg/settings/parser"
"github.com/Jguer/yay/v12/pkg/text" "github.com/Jguer/yay/v12/pkg/text"
@ -23,53 +24,53 @@ var NoConfirm = false
// Configuration stores yay's config. // Configuration stores yay's config.
type Configuration struct { type Configuration struct {
AURURL string `json:"aururl"` Runtime *Runtime `json:"-"`
AURRPCURL string `json:"aurrpcurl"` AURURL string `json:"aururl"`
BuildDir string `json:"buildDir"` AURRPCURL string `json:"aurrpcurl"`
Editor string `json:"editor"` BuildDir string `json:"buildDir"`
EditorFlags string `json:"editorflags"` Editor string `json:"editor"`
MakepkgBin string `json:"makepkgbin"` EditorFlags string `json:"editorflags"`
MakepkgConf string `json:"makepkgconf"` MakepkgBin string `json:"makepkgbin"`
PacmanBin string `json:"pacmanbin"` MakepkgConf string `json:"makepkgconf"`
PacmanConf string `json:"pacmanconf"` PacmanBin string `json:"pacmanbin"`
ReDownload string `json:"redownload"` PacmanConf string `json:"pacmanconf"`
AnswerClean string `json:"answerclean"` ReDownload string `json:"redownload"`
AnswerDiff string `json:"answerdiff"` AnswerClean string `json:"answerclean"`
AnswerEdit string `json:"answeredit"` AnswerDiff string `json:"answerdiff"`
AnswerUpgrade string `json:"answerupgrade"` AnswerEdit string `json:"answeredit"`
GitBin string `json:"gitbin"` AnswerUpgrade string `json:"answerupgrade"`
GpgBin string `json:"gpgbin"` GitBin string `json:"gitbin"`
GpgFlags string `json:"gpgflags"` GpgBin string `json:"gpgbin"`
MFlags string `json:"mflags"` GpgFlags string `json:"gpgflags"`
SortBy string `json:"sortby"` MFlags string `json:"mflags"`
SearchBy string `json:"searchby"` SortBy string `json:"sortby"`
GitFlags string `json:"gitflags"` SearchBy string `json:"searchby"`
RemoveMake string `json:"removemake"` GitFlags string `json:"gitflags"`
SudoBin string `json:"sudobin"` RemoveMake string `json:"removemake"`
SudoFlags string `json:"sudoflags"` SudoBin string `json:"sudobin"`
Version string `json:"version"` SudoFlags string `json:"sudoflags"`
RequestSplitN int `json:"requestsplitn"` Version string `json:"version"`
CompletionInterval int `json:"completionrefreshtime"` RequestSplitN int `json:"requestsplitn"`
MaxConcurrentDownloads int `json:"maxconcurrentdownloads"` CompletionInterval int `json:"completionrefreshtime"`
BottomUp bool `json:"bottomup"` MaxConcurrentDownloads int `json:"maxconcurrentdownloads"`
SudoLoop bool `json:"sudoloop"` BottomUp bool `json:"bottomup"`
TimeUpdate bool `json:"timeupdate"` SudoLoop bool `json:"sudoloop"`
Devel bool `json:"devel"` TimeUpdate bool `json:"timeupdate"`
CleanAfter bool `json:"cleanAfter"` Devel bool `json:"devel"`
KeepSrc bool `json:"keepSrc"` CleanAfter bool `json:"cleanAfter"`
Provides bool `json:"provides"` Provides bool `json:"provides"`
PGPFetch bool `json:"pgpfetch"` PGPFetch bool `json:"pgpfetch"`
CleanMenu bool `json:"cleanmenu"` CleanMenu bool `json:"cleanmenu"`
DiffMenu bool `json:"diffmenu"` DiffMenu bool `json:"diffmenu"`
EditMenu bool `json:"editmenu"` EditMenu bool `json:"editmenu"`
CombinedUpgrade bool `json:"combinedupgrade"` CombinedUpgrade bool `json:"combinedupgrade"`
UseAsk bool `json:"useask"` UseAsk bool `json:"useask"`
BatchInstall bool `json:"batchinstall"` BatchInstall bool `json:"batchinstall"`
SingleLineResults bool `json:"singlelineresults"` SingleLineResults bool `json:"singlelineresults"`
SeparateSources bool `json:"separatesources"` SeparateSources bool `json:"separatesources"`
Debug bool `json:"debug"` Debug bool `json:"debug"`
UseRPC bool `json:"rpc"` UseRPC bool `json:"rpc"`
DoubleConfirm bool `json:"doubleconfirm"` // confirm install before and after build DoubleConfirm bool `json:"doubleconfirm"` // confirm install before and after build
CompletionPath string `json:"-"` CompletionPath string `json:"-"`
VCSFilePath string `json:"-"` VCSFilePath string `json:"-"`
@ -194,7 +195,6 @@ func DefaultConfig(version string) *Configuration {
AURURL: "https://aur.archlinux.org", AURURL: "https://aur.archlinux.org",
BuildDir: os.ExpandEnv("$HOME/.cache/yay"), BuildDir: os.ExpandEnv("$HOME/.cache/yay"),
CleanAfter: false, CleanAfter: false,
KeepSrc: false,
Editor: "", Editor: "",
EditorFlags: "", EditorFlags: "",
Devel: false, Devel: false,
@ -208,7 +208,7 @@ func DefaultConfig(version string) *Configuration {
GitFlags: "", GitFlags: "",
BottomUp: true, BottomUp: true,
CompletionInterval: 7, CompletionInterval: 7,
MaxConcurrentDownloads: 1, MaxConcurrentDownloads: 0,
SortBy: "votes", SortBy: "votes",
SearchBy: "name-desc", SearchBy: "name-desc",
SudoLoop: false, SudoLoop: false,
@ -237,16 +237,19 @@ func DefaultConfig(version string) *Configuration {
Debug: false, Debug: false,
UseRPC: true, UseRPC: true,
DoubleConfirm: true, DoubleConfirm: true,
Mode: parser.ModeAny, Runtime: &Runtime{
Logger: text.GlobalLogger,
},
Mode: parser.ModeAny,
} }
} }
func NewConfig(logger *text.Logger, configPath, version string) (*Configuration, error) { func NewConfig(configPath, version string) (*Configuration, error) {
newConfig := DefaultConfig(version) newConfig := DefaultConfig(version)
cacheHome, errCache := getCacheHome() cacheHome, errCache := getCacheHome()
if errCache != nil && logger != nil { if errCache != nil {
logger.Errorln(errCache) text.Errorln(errCache)
} }
newConfig.BuildDir = cacheHome newConfig.BuildDir = cacheHome
@ -292,3 +295,27 @@ func (c *Configuration) load(configPath string) {
} }
} }
} }
func (c *Configuration) CmdBuilder(runner exe.Runner) exe.ICmdBuilder {
if runner == nil {
runner = &exe.OSRunner{Log: c.Runtime.Logger.Child("runner")}
}
return &exe.CmdBuilder{
GitBin: c.GitBin,
GitFlags: strings.Fields(c.GitFlags),
GPGBin: c.GpgBin,
GPGFlags: strings.Fields(c.GpgFlags),
MakepkgFlags: strings.Fields(c.MFlags),
MakepkgConfPath: c.MakepkgConf,
MakepkgBin: c.MakepkgBin,
SudoBin: c.SudoBin,
SudoFlags: strings.Fields(c.SudoFlags),
SudoLoopEnabled: c.SudoLoop,
PacmanBin: c.PacmanBin,
PacmanConfigPath: c.PacmanConf,
PacmanDBPath: "",
Runner: runner,
Log: c.Runtime.Logger.Child("cmd_builder"),
}
}

View File

@ -36,7 +36,7 @@ func TestNewConfig(t *testing.T) {
_, err = f.WriteString(string(configJSON)) _, err = f.WriteString(string(configJSON))
assert.NoError(t, err) assert.NoError(t, err)
newConfig, err := NewConfig(nil, GetConfigPath(), "v1.0.0") newConfig, err := NewConfig(GetConfigPath(), "v1.0.0")
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, filepath.Join(cacheDir, "test-build-dir"), newConfig.BuildDir) assert.Equal(t, filepath.Join(cacheDir, "test-build-dir"), newConfig.BuildDir)
@ -69,7 +69,7 @@ func TestNewConfigAURDEST(t *testing.T) {
_, err = f.WriteString(string(configJSON)) _, err = f.WriteString(string(configJSON))
assert.NoError(t, err) assert.NoError(t, err)
newConfig, err := NewConfig(nil, GetConfigPath(), "v1.0.0") newConfig, err := NewConfig(GetConfigPath(), "v1.0.0")
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, filepath.Join(cacheDir, "test-build-dir"), newConfig.BuildDir) assert.Equal(t, filepath.Join(cacheDir, "test-build-dir"), newConfig.BuildDir)
@ -102,7 +102,7 @@ func TestNewConfigAURDESTTildeExpansion(t *testing.T) {
_, err = f.WriteString(string(configJSON)) _, err = f.WriteString(string(configJSON))
assert.NoError(t, err) assert.NoError(t, err)
newConfig, err := NewConfig(nil, GetConfigPath(), "v1.0.0") newConfig, err := NewConfig(GetConfigPath(), "v1.0.0")
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, filepath.Join(homeDir, "test-build-dir"), newConfig.BuildDir) assert.Equal(t, filepath.Join(homeDir, "test-build-dir"), newConfig.BuildDir)

View File

@ -15,7 +15,6 @@ import (
mapset "github.com/deckarep/golang-set/v2" mapset "github.com/deckarep/golang-set/v2"
"github.com/leonelquinteros/gotext" "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/settings/parser"
"github.com/Jguer/yay/v12/pkg/text" "github.com/Jguer/yay/v12/pkg/text"
) )
@ -39,7 +38,7 @@ type ICmdBuilder interface {
BuildMakepkgCmd(ctx context.Context, dir string, 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 BuildPacmanCmd(ctx context.Context, args *parser.Arguments, mode parser.TargetMode, noConfirm bool) *exec.Cmd
AddMakepkgFlag(string) AddMakepkgFlag(string)
GetKeepSrc() bool SetPacmanDBPath(string)
SudoLoop() SudoLoop()
} }
@ -57,32 +56,10 @@ type CmdBuilder struct {
PacmanBin string PacmanBin string
PacmanConfigPath string PacmanConfigPath string
PacmanDBPath string PacmanDBPath string
KeepSrc bool
Runner Runner Runner Runner
Log *text.Logger 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 { func (c *CmdBuilder) BuildGPGCmd(ctx context.Context, extraArgs ...string) *exec.Cmd {
args := make([]string, len(c.GPGFlags), len(c.GPGFlags)+len(extraArgs)) args := make([]string, len(c.GPGFlags), len(c.GPGFlags)+len(extraArgs))
copy(args, c.GPGFlags) copy(args, c.GPGFlags)
@ -158,6 +135,10 @@ func (c *CmdBuilder) BuildMakepkgCmd(ctx context.Context, dir string, extraArgs
return cmd return cmd
} }
func (c *CmdBuilder) SetPacmanDBPath(dbPath string) {
c.PacmanDBPath = dbPath
}
// deElevateCommand, `systemd-run` code based on pikaur. // deElevateCommand, `systemd-run` code based on pikaur.
func (c *CmdBuilder) deElevateCommand(ctx context.Context, cmd *exec.Cmd) *exec.Cmd { func (c *CmdBuilder) deElevateCommand(ctx context.Context, cmd *exec.Cmd) *exec.Cmd {
if os.Geteuid() != 0 { if os.Geteuid() != 0 {
@ -173,12 +154,11 @@ func (c *CmdBuilder) deElevateCommand(ctx context.Context, cmd *exec.Cmd) *exec.
if userFound, err := user.Lookup(ogCaller); err == nil { if userFound, err := user.Lookup(ogCaller); err == nil {
cmd.SysProcAttr = &syscall.SysProcAttr{} cmd.SysProcAttr = &syscall.SysProcAttr{}
uid64, errUid := strconv.ParseUint(userFound.Uid, 10, 32) uid, _ := strconv.Atoi(userFound.Uid)
gid64, errGid := strconv.ParseUint(userFound.Gid, 10, 32) gid, _ := strconv.Atoi(userFound.Gid)
if errUid == nil && errGid == nil { cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)}
cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid64), Gid: uint32(gid64)}
return cmd return cmd
}
} }
cmdArgs := []string{ cmdArgs := []string{
@ -262,7 +242,7 @@ func (c *CmdBuilder) waitLock(dbPath string) {
time.Sleep(3 * time.Second) time.Sleep(3 * time.Second)
if _, err := os.Stat(lockDBPath); err != nil { if _, err := os.Stat(lockDBPath); err != nil {
c.Log.Println() fmt.Println()
return return
} }
@ -300,7 +280,3 @@ func (c *CmdBuilder) Show(cmd *exec.Cmd) error {
func (c *CmdBuilder) Capture(cmd *exec.Cmd) (stdout, stderr string, err error) { func (c *CmdBuilder) Capture(cmd *exec.Cmd) (stdout, stderr string, err error) {
return c.Runner.Capture(cmd) return c.Runner.Capture(cmd)
} }
func (c *CmdBuilder) GetKeepSrc() bool {
return c.KeepSrc
}

View File

@ -19,10 +19,6 @@ type OSRunner struct {
Log *text.Logger Log *text.Logger
} }
func NewOSRunner(log *text.Logger) *OSRunner {
return &OSRunner{log}
}
func (r *OSRunner) Show(cmd *exec.Cmd) error { func (r *OSRunner) Show(cmd *exec.Cmd) error {
cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
cmd.SysProcAttr = &syscall.SysProcAttr{ cmd.SysProcAttr = &syscall.SysProcAttr{

View File

@ -25,7 +25,6 @@ type MockBuilder struct {
BuildMakepkgCmdCalls []Call BuildMakepkgCmdCalls []Call
BuildMakepkgCmdFn func(ctx context.Context, dir string, extraArgs ...string) *exec.Cmd 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 BuildPacmanCmdFn func(ctx context.Context, args *parser.Arguments, mode parser.TargetMode, noConfirm bool) *exec.Cmd
GetKeepSrcFn func() bool
} }
type MockRunner struct { type MockRunner struct {
@ -37,10 +36,6 @@ type MockRunner struct {
CaptureFn func(cmd *exec.Cmd) (stdout string, stderr string, err 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 { func (m *MockBuilder) BuildMakepkgCmd(ctx context.Context, dir string, extraArgs ...string) *exec.Cmd {
var res *exec.Cmd var res *exec.Cmd
if m.BuildMakepkgCmdFn != nil { if m.BuildMakepkgCmdFn != nil {
@ -96,10 +91,6 @@ func (m *MockBuilder) Show(cmd *exec.Cmd) error {
return m.Runner.Show(cmd) return m.Runner.Show(cmd)
} }
func (m *MockBuilder) GetKeepSrc() bool {
return false
}
func (m *MockRunner) Capture(cmd *exec.Cmd) (stdout, stderr string, err error) { func (m *MockRunner) Capture(cmd *exec.Cmd) (stdout, stderr string, err error) {
m.CaptureCallsMu.Lock() m.CaptureCallsMu.Lock()
m.CaptureCalls = append(m.CaptureCalls, Call{ m.CaptureCalls = append(m.CaptureCalls, Call{

View File

@ -45,7 +45,7 @@ func DefaultMigrations() []configMigration {
} }
} }
func (c *Configuration) RunMigrations(logger *text.Logger, migrations []configMigration, func (c *Configuration) RunMigrations(migrations []configMigration,
configPath, newVersion string, configPath, newVersion string,
) error { ) error {
saveConfig := false saveConfig := false
@ -53,7 +53,7 @@ func (c *Configuration) RunMigrations(logger *text.Logger, migrations []configMi
for _, migration := range migrations { for _, migration := range migrations {
if db.VerCmp(migration.TargetVersion(), c.Version) > 0 { if db.VerCmp(migration.TargetVersion(), c.Version) > 0 {
if migration.Do(c) { if migration.Do(c) {
logger.Infoln("Config migration executed (", text.Infoln("Config migration executed (",
migration.TargetVersion(), "):", migration) migration.TargetVersion(), "):", migration)
saveConfig = true saveConfig = true

View File

@ -16,10 +16,6 @@ import (
"github.com/Jguer/yay/v12/pkg/text" "github.com/Jguer/yay/v12/pkg/text"
) )
func newTestLogger() *text.Logger {
return text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), true, "test")
}
func TestMigrationNothingToDo(t *testing.T) { func TestMigrationNothingToDo(t *testing.T) {
t.Parallel() t.Parallel()
// Create temporary file for config // Create temporary file for config
@ -32,10 +28,13 @@ func TestMigrationNothingToDo(t *testing.T) {
config := Configuration{ config := Configuration{
Version: "99.0.0", Version: "99.0.0",
// Create runtime with runtimeVersion // Create runtime with runtimeVersion
Runtime: &Runtime{
Logger: text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), false, "test"),
},
} }
// Run Migration // Run Migration
err = config.RunMigrations(newTestLogger(), DefaultMigrations(), testFilePath, "20.0.0") err = config.RunMigrations(DefaultMigrations(), testFilePath, "20.0.0")
require.NoError(t, err) require.NoError(t, err)
// Check file contents if wantSave otherwise check file empty // Check file contents if wantSave otherwise check file empty
@ -54,6 +53,9 @@ func TestProvidesMigrationDo(t *testing.T) {
migration := &configProviderMigration{} migration := &configProviderMigration{}
config := Configuration{ config := Configuration{
Provides: true, Provides: true,
Runtime: &Runtime{
Logger: text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), false, "test"),
},
} }
assert.True(t, migration.Do(&config)) assert.True(t, migration.Do(&config))
@ -133,10 +135,13 @@ func TestProvidesMigration(t *testing.T) {
Version: tc.testConfig.Version, Version: tc.testConfig.Version,
Provides: tc.testConfig.Provides, Provides: tc.testConfig.Provides,
// Create runtime with runtimeVersion // Create runtime with runtimeVersion
Runtime: &Runtime{
Logger: text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), false, "test"),
},
} }
// Run Migration // Run Migration
err = tcConfig.RunMigrations(newTestLogger(), err = tcConfig.RunMigrations(
[]configMigration{&configProviderMigration{}}, []configMigration{&configProviderMigration{}},
testFilePath, tc.newVersion) testFilePath, tc.newVersion)

View File

@ -1,4 +1,4 @@
package runtime package settings
import ( import (
"fmt" "fmt"
@ -10,7 +10,7 @@ import (
"golang.org/x/term" "golang.org/x/term"
) )
func retrievePacmanConfig(cmdArgs *parser.Arguments, pacmanConfigPath string) (*pacmanconf.Config, bool, error) { func RetrievePacmanConfig(cmdArgs *parser.Arguments, pacmanConfigPath string) (*pacmanconf.Config, bool, error) {
root := "/" root := "/"
if value, _, exists := cmdArgs.GetArg("root", "r"); exists { if value, _, exists := cmdArgs.GetArg("root", "r"); exists {
root = value root = value

View File

@ -0,0 +1,54 @@
//go:build !integration
// +build !integration
package settings
import (
"testing"
"github.com/Morganamilo/go-pacmanconf"
"github.com/stretchr/testify/assert"
"github.com/Jguer/yay/v12/pkg/settings/parser"
)
func TestPacmanConf(t *testing.T) {
t.Parallel()
expectedPacmanConf := &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: false,
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"}},
},
}
pacmanConf, color, err := RetrievePacmanConfig(parser.MakeArguments(), "../../testdata/pacman.conf")
assert.Nil(t, err)
assert.NotNil(t, pacmanConf)
assert.Equal(t, color, false)
assert.EqualValues(t, expectedPacmanConf, pacmanConf)
}

View File

@ -25,7 +25,7 @@ func (o *Option) Add(args ...string) {
} }
func (o *Option) First() string { func (o *Option) First() string {
if len(o.Args) == 0 { if o.Args == nil || len(o.Args) == 0 {
return "" return ""
} }
@ -372,15 +372,16 @@ func isArg(arg string) bool {
case "y", "refresh": case "y", "refresh":
case "x", "regex": case "x", "regex":
case "machinereadable": case "machinereadable":
case "disable-sandbox":
// yay options // yay options
case "aururl": case "aururl":
case "aurrpcurl": case "aurrpcurl":
case "save": case "save":
case "afterclean", "cleanafter": case "afterclean", "cleanafter":
case "keepsrc": case "noafterclean", "nocleanafter":
case "devel": case "devel":
case "nodevel":
case "timeupdate": case "timeupdate":
case "notimeupdate":
case "topdown": case "topdown":
case "bottomup": case "bottomup":
case "completioninterval": case "completioninterval":
@ -394,6 +395,7 @@ func isArg(arg string) bool {
case "rebuildtree": case "rebuildtree":
case "norebuild": case "norebuild":
case "batchinstall": case "batchinstall":
case "nobatchinstall":
case "answerclean": case "answerclean":
case "noanswerclean": case "noanswerclean":
case "answerdiff": case "answerdiff":
@ -418,15 +420,23 @@ func isArg(arg string) bool {
case "sudoflags": case "sudoflags":
case "requestsplitn": case "requestsplitn":
case "sudoloop": case "sudoloop":
case "nosudoloop":
case "provides": case "provides":
case "noprovides":
case "pgpfetch": case "pgpfetch":
case "nopgpfetch":
case "cleanmenu": case "cleanmenu":
case "nocleanmenu":
case "diffmenu": case "diffmenu":
case "nodiffmenu":
case "editmenu": case "editmenu":
case "noeditmenu":
case "useask": case "useask":
case "nouseask":
case "combinedupgrade": case "combinedupgrade":
case "nocombinedupgrade":
case "a", "aur": case "a", "aur":
case "N", "repo": case "repo":
case "removemake": case "removemake":
case "noremovemake": case "noremovemake":
case "askremovemake": case "askremovemake":
@ -439,7 +449,7 @@ func isArg(arg string) bool {
case "defaultconfig": case "defaultconfig":
case "singlelineresults": case "singlelineresults":
case "doublelineresults": case "doublelineresults":
case "separatesources": case "separatesources", "noseparatesources":
default: default:
return false return false
} }
@ -565,6 +575,7 @@ func (a *Arguments) parseShortOption(arg, param string) (usedNext bool, err erro
break break
} else { } else {
err = a.AddArg(char) err = a.AddArg(char)
if err != nil { if err != nil {
return return
} }

View File

@ -1,4 +1,4 @@
package runtime package settings
import ( import (
"context" "context"
@ -9,8 +9,8 @@ import (
"github.com/leonelquinteros/gotext" "github.com/leonelquinteros/gotext"
"github.com/Jguer/yay/v12/pkg/db"
"github.com/Jguer/yay/v12/pkg/query" "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/exe"
"github.com/Jguer/yay/v12/pkg/settings/parser" "github.com/Jguer/yay/v12/pkg/settings/parser"
"github.com/Jguer/yay/v12/pkg/text" "github.com/Jguer/yay/v12/pkg/text"
@ -21,12 +21,9 @@ import (
"github.com/Jguer/aur/rpc" "github.com/Jguer/aur/rpc"
"github.com/Jguer/votar/pkg/vote" "github.com/Jguer/votar/pkg/vote"
"github.com/Morganamilo/go-pacmanconf" "github.com/Morganamilo/go-pacmanconf"
"golang.org/x/net/proxy"
) )
type Runtime struct { type Runtime struct {
Cfg *settings.Configuration
QueryBuilder query.Builder QueryBuilder query.Builder
PacmanConf *pacmanconf.Config PacmanConf *pacmanconf.Config
VCSStore vcs.Store VCSStore vcs.Store
@ -34,27 +31,18 @@ type Runtime struct {
HTTPClient *http.Client HTTPClient *http.Client
VoteClient *vote.Client VoteClient *vote.Client
AURClient aur.QueryClient AURClient aur.QueryClient
DBExecutor db.Executor
Logger *text.Logger Logger *text.Logger
} }
func NewRuntime(cfg *settings.Configuration, cmdArgs *parser.Arguments, version string) (*Runtime, error) { func BuildRuntime(cfg *Configuration, cmdArgs *parser.Arguments, version string) (*Runtime, error) {
logger := text.NewLogger(os.Stdout, os.Stderr, os.Stdin, cfg.Debug, "runtime") logger := text.NewLogger(os.Stdout, os.Stderr, os.Stdin, cfg.Debug, "runtime")
runner := exe.NewOSRunner(logger.Child("runner")) cmdBuilder := cfg.CmdBuilder(nil)
transport := http.DefaultTransport.(*http.Transport).Clone()
if socks5_proxy := os.Getenv("SOCKS5_PROXY"); socks5_proxy != "" {
dialer, err := proxy.SOCKS5("tcp", socks5_proxy, nil, proxy.Direct)
if err != nil {
return nil, err
}
transport = &http.Transport{Dial: dialer.Dial}
}
httpClient := &http.Client{ httpClient := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error { CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse return http.ErrUseLastResponse
}, },
Transport: transport,
} }
userAgent := fmt.Sprintf("Yay/%s", version) userAgent := fmt.Sprintf("Yay/%s", version)
@ -98,16 +86,6 @@ func NewRuntime(cfg *settings.Configuration, cmdArgs *parser.Arguments, version
aurCache = aurClient 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( vcsStore := vcs.NewInfoStore(
cfg.VCSFilePath, cmdBuilder, cfg.VCSFilePath, cmdBuilder,
logger.Child("vcs")) logger.Child("vcs"))
@ -116,23 +94,17 @@ func NewRuntime(cfg *settings.Configuration, cmdArgs *parser.Arguments, version
return nil, err return nil, err
} }
queryBuilder := query.NewSourceQueryBuilder( runtime := &Runtime{
aurClient, QueryBuilder: nil,
logger.Child("mixed.querybuilder"), cfg.SortBy, PacmanConf: nil,
cfg.Mode, cfg.SearchBy,
cfg.BottomUp, cfg.SingleLineResults, cfg.SeparateSources)
run := &Runtime{
Cfg: cfg,
QueryBuilder: queryBuilder,
PacmanConf: pacmanConf,
VCSStore: vcsStore, VCSStore: vcsStore,
CmdBuilder: cmdBuilder, CmdBuilder: cmdBuilder,
HTTPClient: &http.Client{}, HTTPClient: &http.Client{},
VoteClient: voteClient, VoteClient: voteClient,
AURClient: aurCache, AURClient: aurCache,
Logger: logger, DBExecutor: nil,
Logger: text.NewLogger(os.Stdout, os.Stderr, os.Stdin, cfg.Debug, "runtime"),
} }
return run, nil return runtime, nil
} }

View File

@ -1,27 +1,20 @@
//go:build !integration //go:build !integration
// +build !integration // +build !integration
package runtime_test package settings_test
import ( import (
"path/filepath"
"testing" "testing"
"github.com/stretchr/testify/assert" "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"
"github.com/Jguer/yay/v12/pkg/settings/parser" "github.com/Jguer/yay/v12/pkg/settings/parser"
"github.com/Jguer/yay/v12/pkg/text"
) )
func TestBuildRuntime(t *testing.T) { func TestBuildRuntime(t *testing.T) {
t.Parallel() t.Parallel()
path := "../../testdata/pacman.conf"
absPath, err := filepath.Abs(path)
require.NoError(t, err)
// Prepare test inputs // Prepare test inputs
cfg := &settings.Configuration{ cfg := &settings.Configuration{
Debug: true, Debug: true,
@ -30,23 +23,24 @@ func TestBuildRuntime(t *testing.T) {
AURRPCURL: "https://aur.archlinux.org/rpc", AURRPCURL: "https://aur.archlinux.org/rpc",
BuildDir: "/tmp", BuildDir: "/tmp",
VCSFilePath: "", VCSFilePath: "",
PacmanConf: absPath, Runtime: &settings.Runtime{Logger: text.NewLogger(nil, nil, nil, false, "")},
} }
cmdArgs := parser.MakeArguments() cmdArgs := parser.MakeArguments()
version := "1.0.0" version := "1.0.0"
// Call the function being tested // Call the function being tested
run, err := runtime.NewRuntime(cfg, cmdArgs, version) runtime, err := settings.BuildRuntime(cfg, cmdArgs, version)
require.NoError(t, err)
// Assert the function's output // Assert the function's output
assert.NotNil(t, run) assert.NotNil(t, runtime)
assert.NotNil(t, run.QueryBuilder) assert.Nil(t, err)
assert.NotNil(t, run.PacmanConf) assert.Nil(t, runtime.QueryBuilder)
assert.NotNil(t, run.VCSStore) assert.Nil(t, runtime.PacmanConf)
assert.NotNil(t, run.CmdBuilder) assert.NotNil(t, runtime.VCSStore)
assert.NotNil(t, run.HTTPClient) assert.NotNil(t, runtime.CmdBuilder)
assert.NotNil(t, run.VoteClient) assert.NotNil(t, runtime.HTTPClient)
assert.NotNil(t, run.AURClient) assert.NotNil(t, runtime.VoteClient)
assert.NotNil(t, run.Logger) assert.NotNil(t, runtime.AURClient)
assert.Nil(t, runtime.DBExecutor)
assert.NotNil(t, runtime.Logger)
} }

View File

@ -11,28 +11,28 @@ import (
"github.com/Jguer/yay/v12/pkg/db" "github.com/Jguer/yay/v12/pkg/db"
"github.com/Jguer/yay/v12/pkg/dep" "github.com/Jguer/yay/v12/pkg/dep"
"github.com/Jguer/yay/v12/pkg/pgp"
"github.com/Jguer/yay/v12/pkg/settings" "github.com/Jguer/yay/v12/pkg/settings"
"github.com/Jguer/yay/v12/pkg/settings/exe" "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/text"
"github.com/Jguer/yay/v12/pkg/vcs" "github.com/Jguer/yay/v12/pkg/vcs"
) )
// TODO: add tests
type Service struct { type Service struct {
dbExecutor db.Executor dbExecutor db.Executor
cfg *settings.Configuration cfg *settings.Configuration
cmdBuilder pgp.GPGCmdBuilder cmdBuilder exe.ICmdBuilder
vcsStore vcs.Store vcsStore vcs.Store
log *text.Logger
pkgBuildDirs map[string]string pkgBuildDirs map[string]string
srcInfos map[string]*gosrc.Srcinfo srcInfos map[string]*gosrc.Srcinfo
} }
func NewService(dbExecutor db.Executor, cfg *settings.Configuration, logger *text.Logger, func NewService(dbExecutor db.Executor, cfg *settings.Configuration,
cmdBuilder exe.ICmdBuilder, vcsStore vcs.Store, pkgBuildDirs map[string]string, cmdBuilder exe.ICmdBuilder, vcsStore vcs.Store, pkgBuildDirs map[string]string,
) (*Service, error) { ) (*Service, error) {
srcinfos, err := ParseSrcinfoFilesByBase(logger, pkgBuildDirs, true) srcinfos, err := ParseSrcinfoFilesByBase(pkgBuildDirs, true)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -43,7 +43,6 @@ func NewService(dbExecutor db.Executor, cfg *settings.Configuration, logger *tex
vcsStore: vcsStore, vcsStore: vcsStore,
pkgBuildDirs: pkgBuildDirs, pkgBuildDirs: pkgBuildDirs,
srcInfos: srcinfos, srcInfos: srcinfos,
log: logger,
}, nil }, nil
} }
@ -69,7 +68,7 @@ nextpkg:
} }
func (s *Service) CheckPGPKeys(ctx context.Context) error { func (s *Service) CheckPGPKeys(ctx context.Context) error {
_, errCPK := pgp.CheckPgpKeys(ctx, s.log.Child("pgp"), s.pkgBuildDirs, s.srcInfos, s.cmdBuilder, settings.NoConfirm) _, errCPK := pgp.CheckPgpKeys(ctx, s.pkgBuildDirs, s.srcInfos, s.cmdBuilder, settings.NoConfirm)
return errCPK return errCPK
} }
@ -84,15 +83,15 @@ func (s *Service) UpdateVCSStore(ctx context.Context, targets []map[string]*dep.
for i := range srcinfo.Packages { for i := range srcinfo.Packages {
for j := range targets { for j := range targets {
if _, ok := targets[j][srcinfo.Packages[i].Pkgname]; !ok { if _, ok := targets[j][srcinfo.Packages[i].Pkgname]; !ok {
s.log.Debugln("skipping VCS update for", srcinfo.Packages[i].Pkgname, "not in targets") text.Debugln("skipping VCS update for", srcinfo.Packages[i].Pkgname, "not in targets")
continue continue
} }
if _, ok := ignore[srcinfo.Packages[i].Pkgname]; ok { if _, ok := ignore[srcinfo.Packages[i].Pkgname]; ok {
s.log.Debugln("skipping VCS update for", srcinfo.Packages[i].Pkgname, "due to install error") text.Debugln("skipping VCS update for", srcinfo.Packages[i].Pkgname, "due to install error")
continue continue
} }
s.log.Debugln("checking VCS entry for", srcinfo.Packages[i].Pkgname, fmt.Sprintf("source: %v", srcinfo.Source)) text.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) s.vcsStore.Update(ctx, srcinfo.Packages[i].Pkgname, srcinfo.Source)
} }
} }
@ -101,17 +100,17 @@ func (s *Service) UpdateVCSStore(ctx context.Context, targets []map[string]*dep.
return nil return nil
} }
func ParseSrcinfoFilesByBase(logger *text.Logger, pkgBuildDirs map[string]string, errIsFatal bool) (map[string]*gosrc.Srcinfo, error) { func ParseSrcinfoFilesByBase(pkgBuildDirs map[string]string, errIsFatal bool) (map[string]*gosrc.Srcinfo, error) {
srcinfos := make(map[string]*gosrc.Srcinfo) srcinfos := make(map[string]*gosrc.Srcinfo)
k := 0 k := 0
for base, dir := range pkgBuildDirs { for base, dir := range pkgBuildDirs {
logger.OperationInfoln(gotext.Get("(%d/%d) Parsing SRCINFO: %s", k+1, len(pkgBuildDirs), text.Cyan(base))) text.OperationInfoln(gotext.Get("(%d/%d) Parsing SRCINFO: %s", k+1, len(pkgBuildDirs), text.Cyan(base)))
pkgbuild, err := gosrc.ParseFile(filepath.Join(dir, ".SRCINFO")) pkgbuild, err := gosrc.ParseFile(filepath.Join(dir, ".SRCINFO"))
if err != nil { if err != nil {
if !errIsFatal { if !errIsFatal {
logger.Warnln(gotext.Get("failed to parse %s -- skipping: %s", base, err)) text.Warnln(gotext.Get("failed to parse %s -- skipping: %s", base, err))
continue continue
} }

107
pkg/stringset/stringset.go Normal file
View File

@ -0,0 +1,107 @@
package stringset
// StringSet is a basic set implementation for strings.
// This is used a lot so it deserves its own type.
// Other types of sets are used throughout the code but do not have
// their own typedef.
// String sets and <type>sets should be used throughout the code when applicable,
// they are a lot more flexible than slices and provide easy lookup.
type StringSet map[string]struct{}
// MapStringSet is a Map of StringSets.
type MapStringSet map[string]StringSet
// 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 StringSet is creayed containing v.
func (mss MapStringSet) Add(n, v string) {
if _, ok := mss[n]; !ok {
mss[n] = make(StringSet)
}
mss[n].Set(v)
}
// Set sets key in StringSet.
func (set StringSet) Set(v string) {
set[v] = struct{}{}
}
// Extend sets multiple keys in StringSet.
func (set StringSet) Extend(s ...string) {
for _, v := range s {
set[v] = struct{}{}
}
}
// Get returns true if the key exists in the set.
func (set StringSet) Get(v string) bool {
_, exists := set[v]
return exists
}
// Remove deletes a key from the set.
func (set StringSet) Remove(v string) {
delete(set, v)
}
// ToSlice turns all keys into a string slice.
func (set StringSet) ToSlice() []string {
slice := make([]string, 0, len(set))
for v := range set {
slice = append(slice, v)
}
return slice
}
// Copy copies a StringSet into a new structure of the same type.
func (set StringSet) Copy() StringSet {
newSet := make(StringSet)
for str := range set {
newSet.Set(str)
}
return newSet
}
// FromSlice creates a new StringSet from an input slice.
func FromSlice(in []string) StringSet {
set := make(StringSet)
for _, v := range in {
set.Set(v)
}
return set
}
// Make creates a new StringSet from a set of arguments.
func Make(in ...string) StringSet {
return FromSlice(in)
}
// Equal compares if two StringSets have the same values.
func Equal(a, b StringSet) 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 {
if !b.Get(n) {
return false
}
}
return true
}

View File

@ -1,67 +0,0 @@
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)
}

View File

@ -1,167 +0,0 @@
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 {
tc := tc // 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)
}
})
}
}

View File

@ -1,132 +0,0 @@
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)
}

View File

@ -1,138 +0,0 @@
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"))
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)
}
go func() {
errComp := completion.Update(ctx, run.HTTPClient, o.dbExecutor,
o.cfg.AURURL, o.cfg.CompletionPath, o.cfg.CompletionInterval, false)
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
}

View File

@ -1,62 +0,0 @@
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++
}
}

View File

@ -1,39 +0,0 @@
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
}

View File

@ -3,18 +3,14 @@ package text
import ( import (
"bufio" "bufio"
"fmt" "fmt"
"strings" "io"
"unicode"
"unicode/utf8"
"github.com/leonelquinteros/gotext"
) )
func (l *Logger) GetInput(defaultValue string, noConfirm bool) (string, error) { func (l *Logger) GetInput(defaultValue string, noConfirm bool) (string, error) {
l.Info() Info()
if defaultValue != "" || noConfirm { if defaultValue != "" || noConfirm {
l.Println(defaultValue) fmt.Println(defaultValue)
return defaultValue, nil return defaultValue, nil
} }
@ -32,48 +28,6 @@ func (l *Logger) GetInput(defaultValue string, noConfirm bool) (string, error) {
return string(buf), nil return string(buf), nil
} }
// ContinueTask prompts if user wants to continue task. func GetInput(r io.Reader, defaultValue string, noConfirm bool) (string, error) {
// If NoConfirm is set the action will continue without user input. return GlobalLogger.GetInput(defaultValue, noConfirm)
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))
} }

139
pkg/text/print.go Normal file
View File

@ -0,0 +1,139 @@
package text
import (
"fmt"
"os"
"strconv"
"strings"
"syscall"
"unicode"
"github.com/leonelquinteros/gotext"
"golang.org/x/sys/unix"
)
const (
arrow = "==>"
smallArrow = " ->"
opSymbol = "::"
)
var (
cachedColumnCount = -1
GlobalLogger = NewLogger(os.Stdout, os.Stderr, os.Stdin, false, "global")
)
func Debugln(a ...interface{}) {
GlobalLogger.Debugln(a...)
}
func OperationInfoln(a ...interface{}) {
GlobalLogger.OperationInfoln(a...)
}
func OperationInfo(a ...interface{}) {
GlobalLogger.OperationInfo(a...)
}
func SprintOperationInfo(a ...interface{}) string {
return GlobalLogger.SprintOperationInfo(a...)
}
func Info(a ...interface{}) {
GlobalLogger.Info(a...)
}
func Infoln(a ...interface{}) {
GlobalLogger.Infoln(a...)
}
func SprintWarn(a ...interface{}) string {
return GlobalLogger.SprintWarn(a...)
}
func Warn(a ...interface{}) {
GlobalLogger.Warn(a...)
}
func Warnln(a ...interface{}) {
GlobalLogger.Warnln(a...)
}
func SprintError(a ...interface{}) string {
return GlobalLogger.SprintError(a...)
}
func Error(a ...interface{}) {
GlobalLogger.Error(a...)
}
func Errorln(a ...interface{}) {
GlobalLogger.Errorln(a...)
}
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
}
func PrintInfoValue(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(Bold("%-*s: "), keyTextCount, key)
if len(values) == 0 || (len(values) == 1 && values[0] == "") {
fmt.Fprintf(os.Stdout, "%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)
}
fmt.Println(str)
}

View File

@ -5,12 +5,6 @@ import (
"io" "io"
) )
const (
arrow = "==>"
smallArrow = " ->"
opSymbol = "::"
)
type Logger struct { type Logger struct {
name string name string
Debug bool Debug bool

View File

@ -1,8 +1,13 @@
package text package text
import ( import (
"fmt"
"io"
"strings" "strings"
"unicode" "unicode"
"unicode/utf8"
"github.com/leonelquinteros/gotext"
) )
const ( const (
@ -23,12 +28,12 @@ func SplitDBFromName(pkg string) (db, name string) {
// LessRunes compares two rune values, and returns true if the first argument is lexicographicaly smaller. // LessRunes compares two rune values, and returns true if the first argument is lexicographicaly smaller.
func LessRunes(iRunes, jRunes []rune) bool { func LessRunes(iRunes, jRunes []rune) bool {
maxLen := len(iRunes) max := len(iRunes)
if maxLen > len(jRunes) { if max > len(jRunes) {
maxLen = len(jRunes) max = len(jRunes)
} }
for idx := 0; idx < maxLen; idx++ { for idx := 0; idx < max; idx++ {
ir := iRunes[idx] ir := iRunes[idx]
jr := jRunes[idx] jr := jRunes[idx]
@ -47,3 +52,49 @@ func LessRunes(iRunes, jRunes []rune) bool {
return len(iRunes) < len(jRunes) return len(iRunes) < len(jRunes)
} }
// ContinueTask prompts if user wants to continue task.
// If NoConfirm is set the action will continue without user input.
func ContinueTask(input io.Reader, 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))
}
OperationInfo(Bold(s), Bold(postFix))
if _, err := fmt.Fscanln(input, &response); err != nil {
return preset
}
return strings.EqualFold(response, yes) ||
strings.EqualFold(response, y) ||
(!strings.EqualFold(yDefault, n) && strings.EqualFold(response, yDefault))
}

View File

@ -4,7 +4,6 @@
package text package text
import ( import (
"io"
"os" "os"
"path" "path"
"strings" "strings"
@ -75,8 +74,7 @@ func TestContinueTask(t *testing.T) {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
// create io.Reader with value of input // create io.Reader with value of input
in := strings.NewReader(tt.args.input) in := strings.NewReader(tt.args.input)
logger := NewLogger(io.Discard, io.Discard, in, false, "test") got := ContinueTask(in, tt.args.s, tt.args.preset, tt.args.noConfirm)
got := logger.ContinueTask(tt.args.s, tt.args.preset, tt.args.noConfirm)
require.Equal(t, tt.want, got) require.Equal(t, tt.want, got)
}) })
} }
@ -122,8 +120,7 @@ msgstr "да"
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
in := strings.NewReader(tt.args.input) in := strings.NewReader(tt.args.input)
logger := NewLogger(io.Discard, io.Discard, in, false, "test") got := ContinueTask(in, tt.args.s, tt.args.preset, tt.args.noConfirm)
got := logger.ContinueTask(tt.args.s, tt.args.preset, tt.args.noConfirm)
require.Equal(t, tt.want, got) require.Equal(t, tt.want, got)
}) })
} }
@ -171,8 +168,7 @@ msgstr "ja"
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
in := strings.NewReader(tt.args.input) in := strings.NewReader(tt.args.input)
logger := NewLogger(io.Discard, io.Discard, in, false, "test") got := ContinueTask(in, tt.args.s, tt.args.preset, tt.args.noConfirm)
got := logger.ContinueTask(tt.args.s, tt.args.preset, tt.args.noConfirm)
require.Equal(t, tt.want, got) require.Equal(t, tt.want, got)
}) })
} }

View File

@ -5,6 +5,8 @@ import (
"strings" "strings"
"github.com/Jguer/go-alpm/v2" "github.com/Jguer/go-alpm/v2"
"github.com/Jguer/yay/v12/pkg/text"
) )
type ( type (
@ -242,6 +244,7 @@ func (g *Graph[T, V]) Prune(node T) []T {
// Remove edges from things that depend on `node`. // Remove edges from things that depend on `node`.
for dependent := range g.dependents[node] { for dependent := range g.dependents[node] {
last := g.dependencies.removeFromDepmap(dependent, node) last := g.dependencies.removeFromDepmap(dependent, node)
text.Debugln("pruning dependent", dependent, last)
if last { if last {
pruned = append(pruned, g.Prune(dependent)...) pruned = append(pruned, g.Prune(dependent)...)
} }
@ -252,6 +255,7 @@ func (g *Graph[T, V]) Prune(node T) []T {
// Remove all edges from node to the things it depends on. // Remove all edges from node to the things it depends on.
for dependency := range g.dependencies[node] { for dependency := range g.dependencies[node] {
last := g.dependents.removeFromDepmap(dependency, node) last := g.dependents.removeFromDepmap(dependency, node)
text.Debugln("pruning dependency", dependency, last)
if last { if last {
pruned = append(pruned, g.Prune(dependency)...) pruned = append(pruned, g.Prune(dependency)...)
} }

9
pkg/topo/errors.go Normal file
View File

@ -0,0 +1,9 @@
package topo
import "errors"
var (
ErrSelfReferential = errors.New("self-referential dependencies not allowed")
ErrConflictingAlias = errors.New("alias already defined")
ErrCircular = errors.New("circular dependencies not allowed")
)

Some files were not shown because too many files have changed in this diff Show More