mirror of
https://github.com/Jguer/yay.git
synced 2025-06-25 00:03:32 -04:00
Compare commits
No commits in common. "next" and "v11.3.1" have entirely different histories.
@ -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"]
|
@ -1,14 +0,0 @@
|
||||
{
|
||||
"name": "Existing Dockerfile",
|
||||
"build": {
|
||||
"context": "..",
|
||||
"dockerfile": "../.devcontainer/Dockerfile"
|
||||
},
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"extensions": [
|
||||
"golang.go"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
5
.github/ISSUE_TEMPLATE/bug_report.md
vendored
5
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -32,11 +32,6 @@ Example: `yay v8.1139.r0.g9ac4ab6 - libalpm v11.0.1` -->
|
||||
Include the FULL output of any relevant commands/configs
|
||||
The current yay config can be printed with `yay -Pg`
|
||||
Paste services are only needed for excessive output (>500 lines)
|
||||
Use --debug to add pacman and yay debug logs
|
||||
or add the following key to your ~/.config/yay/config.json to only get yay debug logs
|
||||
{
|
||||
"debug": true
|
||||
}
|
||||
-->
|
||||
|
||||
```sh
|
||||
|
15
.github/dependabot.yml
vendored
15
.github/dependabot.yml
vendored
@ -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:
|
||||
- '*'
|
155
.github/workflows/builder-image.yml
vendored
155
.github/workflows/builder-image.yml
vendored
@ -1,143 +1,40 @@
|
||||
name: Builder Image
|
||||
name: Builder image
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 3 * * 1" # Every Monday at 3 AM
|
||||
- cron: "0 3 * * 1"
|
||||
push:
|
||||
paths:
|
||||
- "ci.Dockerfile"
|
||||
- ".github/workflows/builder-image.yml"
|
||||
|
||||
env:
|
||||
REGISTRY_IMAGE: jguer/yay-builder
|
||||
|
||||
- "**/builder-image.yml"
|
||||
jobs:
|
||||
build:
|
||||
name: Push builder image to Docker Hub
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform:
|
||||
- linux/amd64
|
||||
- linux/arm/v7
|
||||
- linux/arm64
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- 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
|
||||
uses: docker/setup-buildx-action@v1
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Extract metadata for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: |
|
||||
${{ env.REGISTRY_IMAGE }}
|
||||
ghcr.io/${{ env.REGISTRY_IMAGE }}
|
||||
tags: |
|
||||
type=raw,value=latest
|
||||
type=sha,format=long
|
||||
|
||||
- name: Build and push by digest
|
||||
id: build
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: ci.Dockerfile
|
||||
platforms: ${{ matrix.platform }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
outputs: type=image,name=${{ env.REGISTRY_IMAGE }},push-by-digest=true,name-canonical=true,push=true
|
||||
|
||||
- name: Export digest
|
||||
run: |
|
||||
mkdir -p /tmp/digests
|
||||
digest="${{ steps.build.outputs.digest }}"
|
||||
echo -n "$digest" > "/tmp/digests/$(echo "${{ matrix.platform }}" | tr '/' '_')"
|
||||
|
||||
- name: Upload digest
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: digest-${{ matrix.platform == 'linux/amd64' && 'amd64' || matrix.platform == 'linux/arm/v7' && 'armv7' || 'arm64' }}
|
||||
path: /tmp/digests/*
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
merge:
|
||||
needs: [build]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download digests
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
pattern: digest-*
|
||||
merge-multiple: true
|
||||
path: /tmp/digests
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Extract metadata for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: |
|
||||
${{ env.REGISTRY_IMAGE }}
|
||||
ghcr.io/${{ env.REGISTRY_IMAGE }}
|
||||
tags: |
|
||||
type=raw,value=latest
|
||||
type=sha,format=short
|
||||
|
||||
- name: Create and push manifest list
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
- name: Push to Docker Hub
|
||||
uses: docker/build-push-action@v2
|
||||
env:
|
||||
DOCKER_CLI_EXPERIMENTAL: enabled
|
||||
run: |
|
||||
# Extract Docker Hub tags
|
||||
DH_TAGS=$(echo '${{ steps.meta.outputs.tags }}' | grep -v "^ghcr.io" | xargs -I {} echo "-t {}")
|
||||
|
||||
# Extract GitHub Container Registry tags
|
||||
GHCR_TAGS=$(echo '${{ steps.meta.outputs.tags }}' | grep "^ghcr.io" | xargs -I {} echo "-t {}")
|
||||
|
||||
# Create a manifest list using the image digests from /tmp/digests/*
|
||||
DIGESTS=$(for file in /tmp/digests/*; do
|
||||
echo -n "${{ env.REGISTRY_IMAGE }}@$(cat $file) "
|
||||
done)
|
||||
|
||||
# Create the manifest list for Docker Hub
|
||||
docker buildx imagetools create $DH_TAGS $DIGESTS
|
||||
|
||||
# Create the manifest list for GitHub Container Registry
|
||||
docker buildx imagetools create $GHCR_TAGS $DIGESTS
|
||||
|
||||
- name: Inspect image
|
||||
run: |
|
||||
docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:latest
|
||||
DOCKER_BUILDKIT: 0
|
||||
COMPOSE_DOCKER_CLI_BUILD: 0
|
||||
with:
|
||||
platforms: linux/amd64,linux/arm/v7,linux/arm64
|
||||
file: ci.Dockerfile
|
||||
push: true
|
||||
tags: jguer/yay-builder:latest
|
||||
secrets: |
|
||||
DOCKER_BUILDKIT=0
|
||||
COMPOSE_DOCKER_CLI_BUILD=0
|
||||
cache-from: type=registry,ref=jguer/yay-builder:latest
|
||||
cache-to: type=inline
|
||||
|
116
.github/workflows/multiarch-build.yml
vendored
116
.github/workflows/multiarch-build.yml
vendored
@ -1,5 +1,4 @@
|
||||
name: Build Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
@ -9,36 +8,35 @@ jobs:
|
||||
build-releases:
|
||||
strategy:
|
||||
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 }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/setup-qemu-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
platforms: all
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
with:
|
||||
version: latest
|
||||
- name: Read info
|
||||
id: tags
|
||||
shell: bash
|
||||
run: |
|
||||
echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
|
||||
echo "TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
|
||||
echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\/v/}
|
||||
echo ::set-output name=TAG::${GITHUB_REF/refs\/tags\//}
|
||||
arch="${{ matrix.arch }}"
|
||||
echo "PLATFORM=${arch%% *}" >> $GITHUB_OUTPUT
|
||||
echo "ARCH=${arch##* }" >> $GITHUB_OUTPUT
|
||||
|
||||
echo ::set-output name=PLATFORM::${arch%% *}
|
||||
echo ::set-output name=ARCH::${arch##* }
|
||||
- name: Build ${{ matrix.arch }} release
|
||||
run: |
|
||||
mkdir artifacts
|
||||
@ -49,45 +47,75 @@ jobs:
|
||||
-t yay:${{ steps.tags.outputs.arch }} . --load
|
||||
make docker-release ARCH=${{ steps.tags.outputs.arch }} VERSION=${{ steps.tags.outputs.version }} PREFIX="/usr"
|
||||
mv *.tar.gz artifacts
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: yay_${{ steps.tags.outputs.arch }}
|
||||
path: artifacts
|
||||
|
||||
create_release:
|
||||
name: Create release from this build
|
||||
needs: [build-releases]
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Read info
|
||||
id: tags
|
||||
shell: bash
|
||||
run: |
|
||||
echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
|
||||
echo "TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\/v/}
|
||||
echo ::set-output name=TAG::${GITHUB_REF/refs\/tags\//}
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
pattern: yay_*
|
||||
merge-multiple: true
|
||||
|
||||
name: yay_x86_64
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: yay_armv7h
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: yay_aarch64
|
||||
- name: Create Release
|
||||
id: create_release
|
||||
uses: actions/create-release@master
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
gh release create ${{ steps.tags.outputs.tag }} \
|
||||
--title "${{ steps.tags.outputs.tag }}" \
|
||||
--generate-notes \
|
||||
./yay_${{ steps.tags.outputs.version }}_*.tar.gz
|
||||
|
||||
- name: Release Notary Action
|
||||
uses: docker://aevea/release-notary:latest
|
||||
with:
|
||||
tag_name: ${{ steps.tags.outputs.tag }}
|
||||
release_name: ${{ steps.tags.outputs.tag }}
|
||||
draft: false
|
||||
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 }}
|
||||
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
|
||||
uses: docker://aevea/release-notary:0.9.3
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
26
.github/workflows/testing-git.yml
vendored
26
.github/workflows/testing-git.yml
vendored
@ -9,31 +9,27 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Lint and test yay (-git)
|
||||
name: Lint and test yay
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ghcr.io/jguer/yay-builder:latest
|
||||
image: jguer/yay-builder:latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v3
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: /home/runner/work/yay/yay/pacman-git
|
||||
key: ${{ runner.os }}-pacman-${{ hashFiles('/home/runner/work/yay/yay/pacman-git/PKGBUILD') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pacman-
|
||||
- name: checkout pacman-git
|
||||
run: |
|
||||
git -C ./pacman-git pull || git clone https://aur.archlinux.org/pacman-git
|
||||
pacman -Sy --overwrite=* --noconfirm archlinux-keyring
|
||||
pacman -Su --overwrite=* --noconfirm sudo base-devel
|
||||
git clone https://aur.archlinux.org/pacman-git
|
||||
useradd github
|
||||
echo 'github ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers
|
||||
chmod -R 777 pacman-git
|
||||
su github -c 'cd pacman-git; yes | makepkg -i --nocheck'
|
||||
echo 'github ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers
|
||||
su github -c 'cd pacman-git; yes | makepkg -si --nocheck'
|
||||
- name: Run Build and Tests with pacman-git
|
||||
run: |
|
||||
make test
|
||||
run: make test
|
||||
|
38
.github/workflows/testing.yml
vendored
38
.github/workflows/testing.yml
vendored
@ -1,44 +1,28 @@
|
||||
name: Test against pacman
|
||||
on:
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- "doc/**"
|
||||
- "**/*.po"
|
||||
- "README.md"
|
||||
- ".gitignore"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Lint and test yay
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ghcr.io/jguer/yay-builder:latest
|
||||
image: jguer/yay-builder:latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v3
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-
|
||||
- name: Lint
|
||||
env:
|
||||
GOFLAGS: -buildvcs=false -tags=next
|
||||
run: /app/bin/golangci-lint run -v ./...
|
||||
run: /app/bin/golangci-lint run ./...
|
||||
- name: Run Build and Tests
|
||||
run: make test
|
||||
|
||||
- name: Run Integration Tests
|
||||
continue-on-error: true
|
||||
run: |
|
||||
useradd -m yay &&
|
||||
chown -R yay:yay . &&
|
||||
cp -r ~/go/ /home/yay/go/ &&
|
||||
chown -R yay:yay /home/yay/go/ &&
|
||||
su yay -c "make test-integration"
|
||||
- name: Build yay Artifact
|
||||
env:
|
||||
GOFLAGS: -buildvcs=false -tags=next
|
||||
run: make
|
||||
- name: Upload yay Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: yay
|
||||
path: ./yay
|
||||
if-no-files-found: error
|
||||
overwrite: true
|
||||
run: make test
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -28,7 +28,3 @@ qemu-*
|
||||
*.pot
|
||||
*.po~
|
||||
*.pprof
|
||||
|
||||
node_modules/
|
||||
xgotext
|
||||
.devcontainer/
|
156
.golangci.yml
156
.golangci.yml
@ -1,94 +1,100 @@
|
||||
version: "2"
|
||||
run:
|
||||
go: "1.20"
|
||||
linters-settings:
|
||||
dupl:
|
||||
threshold: 100
|
||||
funlen:
|
||||
lines: 100
|
||||
statements: 50
|
||||
goconst:
|
||||
min-len: 3
|
||||
min-occurrences: 4
|
||||
gocritic:
|
||||
enabled-tags:
|
||||
- diagnostic
|
||||
- experimental
|
||||
- opinionated
|
||||
- performance
|
||||
- style
|
||||
gocyclo:
|
||||
min-complexity: 15
|
||||
goimports:
|
||||
local-prefixes: github.com/Jguer/yay/v11
|
||||
gomnd:
|
||||
settings:
|
||||
mnd:
|
||||
# don't include the "operation" and "assign"
|
||||
checks: argument,case,condition,return
|
||||
govet:
|
||||
check-shadowing: true
|
||||
lll:
|
||||
line-length: 140
|
||||
maligned:
|
||||
suggest-new: true
|
||||
misspell:
|
||||
locale: US
|
||||
|
||||
linters:
|
||||
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:
|
||||
- bodyclose
|
||||
- dogsled
|
||||
- usestdlibvars
|
||||
- deadcode
|
||||
- depguard
|
||||
- dupl
|
||||
- errcheck
|
||||
- errorlint
|
||||
- gochecknoinits
|
||||
- gocritic
|
||||
- gofmt
|
||||
- goimports
|
||||
- goprintffuncname
|
||||
- gosec
|
||||
- gosimple
|
||||
- govet
|
||||
- ineffassign
|
||||
- lll
|
||||
- misspell
|
||||
- nakedret
|
||||
- noctx
|
||||
- nolintlint
|
||||
- prealloc
|
||||
- revive
|
||||
- rowserrcheck
|
||||
- staticcheck
|
||||
- structcheck
|
||||
- stylecheck
|
||||
- typecheck
|
||||
- unconvert
|
||||
- unparam
|
||||
- unused
|
||||
- tenv
|
||||
- varcheck
|
||||
- whitespace
|
||||
settings:
|
||||
dupl:
|
||||
threshold: 100
|
||||
funlen:
|
||||
lines: 100
|
||||
statements: 50
|
||||
goconst:
|
||||
min-len: 3
|
||||
min-occurrences: 4
|
||||
gocritic:
|
||||
enabled-tags:
|
||||
- diagnostic
|
||||
- experimental
|
||||
- opinionated
|
||||
- performance
|
||||
- style
|
||||
gocyclo:
|
||||
min-complexity: 15
|
||||
lll:
|
||||
line-length: 140
|
||||
misspell:
|
||||
locale: US
|
||||
nolintlint:
|
||||
require-explanation: false
|
||||
require-specific: false
|
||||
allow-unused: false
|
||||
exclusions:
|
||||
generated: lax
|
||||
presets:
|
||||
- comments
|
||||
- common-false-positives
|
||||
- legacy
|
||||
- std-error-handling
|
||||
rules:
|
||||
- linters:
|
||||
- dupl
|
||||
- errcheck
|
||||
- errorlint
|
||||
- gochecknoinits
|
||||
- gocritic
|
||||
- godot
|
||||
- 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$
|
||||
- wsl
|
||||
- godot
|
||||
# - maligned
|
||||
# - interfacer
|
||||
# - nilerr
|
||||
# - nlreturn
|
||||
# - exhaustivestruct
|
||||
# - errname
|
||||
# - forbidigo
|
||||
|
||||
run:
|
||||
go: '1.18'
|
||||
|
||||
issues:
|
||||
exclude-rules:
|
||||
- path: _test\.go
|
||||
linters:
|
||||
- lll
|
||||
- revive
|
||||
- wsl
|
||||
- govet
|
||||
- godot
|
||||
- errcheck
|
||||
- stylecheck
|
||||
- dupl
|
||||
- gocritic
|
||||
- gochecknoinits
|
||||
|
||||
exclude:
|
||||
- G204
|
||||
|
@ -1,29 +1,31 @@
|
||||
default_stages: [commit]
|
||||
|
||||
repos:
|
||||
- repo: https://github.com/dnephin/pre-commit-golang
|
||||
rev: v0.5.1
|
||||
- repo: git://github.com/dnephin/pre-commit-golang
|
||||
rev: v0.3.5
|
||||
hooks:
|
||||
- id: go-fmt
|
||||
- id: go-imports
|
||||
args: [-local=github.com/Jguer/yay/v11/]
|
||||
- id: golangci-lint
|
||||
- id: go-unit-tests
|
||||
- id: go-build
|
||||
|
||||
- repo: https://github.com/pre-commit/mirrors-prettier
|
||||
rev: v4.0.0-alpha.8 # Use the sha or tag you want to point at
|
||||
rev: v2.2.1 # Use the sha or tag you want to point at
|
||||
hooks:
|
||||
- id: prettier
|
||||
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.5.0 # Use the ref you want to point at
|
||||
rev: v3.4.0 # Use the ref you want to point at
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
- id: check-json
|
||||
- id: check-yaml
|
||||
- id: check-added-large-files
|
||||
|
||||
- repo: https://github.com/commitizen-tools/commitizen
|
||||
rev: v3.15.0
|
||||
- repo: https://github.com/Woile/commitizen
|
||||
rev: v2.17.6
|
||||
hooks:
|
||||
- id: commitizen
|
||||
stages: [commit-msg]
|
||||
|
7
.vscode/settings.json
vendored
7
.vscode/settings.json
vendored
@ -1,7 +0,0 @@
|
||||
{
|
||||
"go.lintTool": "golangci-lint",
|
||||
"gopls": {
|
||||
"formatting.gofumpt": true,
|
||||
"formatting.local": "github.com/Jguer/yay/v12"
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
FROM ghcr.io/jguer/yay-builder:latest
|
||||
LABEL maintainer="Jguer,docker@jguer.space"
|
||||
FROM jguer/yay-builder:latest
|
||||
LABEL maintainer="Jguer,joaogg3 at google mail"
|
||||
|
||||
ARG VERSION
|
||||
ARG PREFIX
|
||||
@ -9,4 +9,4 @@ WORKDIR /app
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN make release VERSION=${VERSION} PREFIX=${PREFIX} ARCH=${ARCH}
|
||||
RUN make release VERSION=${VERSION} PREFIX=${PREFIX} ARCH=${ARCH}
|
||||
|
22
Makefile
22
Makefile
@ -10,8 +10,8 @@ GO ?= go
|
||||
PKGNAME := yay
|
||||
PREFIX := /usr/local
|
||||
|
||||
MAJORVERSION := 12
|
||||
MINORVERSION := 0
|
||||
MAJORVERSION := 11
|
||||
MINORVERSION := 3
|
||||
PATCHVERSION := 0
|
||||
VERSION ?= ${MAJORVERSION}.${MINORVERSION}.${PATCHVERSION}
|
||||
|
||||
@ -19,14 +19,15 @@ LOCALEDIR := po
|
||||
SYSTEMLOCALEPATH := $(PREFIX)/share/locale/
|
||||
|
||||
# ls -1 po | sed -e 's/\.po$//' | paste -sd " "
|
||||
LANGS := ca cs de en es eu fr_FR he id it_IT ja ko pl_PL pt_BR pt ru_RU ru sv tr uk zh_CN zh_TW
|
||||
LANGS := de en es eu fr_FR id it_IT ja ko pl_PL pt pt_BR ru_RU sv tr uk zh_CN zh_TW
|
||||
POTFILE := default.pot
|
||||
POFILES := $(addprefix $(LOCALEDIR)/,$(addsuffix .po,$(LANGS)))
|
||||
MOFILES := $(POFILES:.po=.mo)
|
||||
|
||||
FLAGS ?= -trimpath -mod=readonly -modcacherw
|
||||
EXTRA_FLAGS ?= -buildmode=pie
|
||||
LDFLAGS := -X "main.yayVersion=${VERSION}" -X "main.localePath=${SYSTEMLOCALEPATH}" -linkmode=external -compressdwarf=false
|
||||
LDFLAGS := -X "main.yayVersion=${VERSION}" -X "main.localePath=${SYSTEMLOCALEPATH}" -linkmode=external
|
||||
FLAGS += $(shell pacman -T 'pacman-git' && echo "-tags next")
|
||||
|
||||
RELEASE_DIR := ${PKGNAME}_${VERSION}_${ARCH}
|
||||
PACKAGE := $(RELEASE_DIR).tar.gz
|
||||
@ -52,10 +53,6 @@ test_lint: test lint
|
||||
test:
|
||||
$(GO) test -race -covermode=atomic $(FLAGS) ./...
|
||||
|
||||
.PHONY: test-integration
|
||||
test-integration:
|
||||
$(GO) test -tags=integration $(FLAGS) ./...
|
||||
|
||||
.PHONY: build
|
||||
build: $(BIN)
|
||||
|
||||
@ -69,7 +66,7 @@ docker-release-all:
|
||||
make docker-release-aarch64 ARCH=aarch64
|
||||
|
||||
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 container rm yay-$(ARCH)
|
||||
|
||||
@ -82,7 +79,9 @@ docker-build:
|
||||
|
||||
.PHONY: 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
|
||||
fmt:
|
||||
@ -123,9 +122,8 @@ $(PACKAGE): $(BIN) $(RELEASE_DIR) ${MOFILES}
|
||||
|
||||
locale:
|
||||
xgotext -in . -out po
|
||||
mv po/default.pot po/en.po
|
||||
for lang in ${LANGS}; do \
|
||||
test -f po/$$lang.po || msginit --no-translator -l po/$$lang.po -i po/${POTFILE} -o po/$$lang.po; \
|
||||
test -f po/$$lang.po || msginit -l po/$$lang.po -i po/${POTFILE} -o po/$$lang.po \
|
||||
msgmerge -U po/$$lang.po po/${POTFILE}; \
|
||||
touch po/$$lang.po; \
|
||||
done
|
||||
|
128
README.md
128
README.md
@ -19,7 +19,6 @@ Yet Another Yogurt - An AUR Helper Written in Go
|
||||
- Narrow search (`yay linux header` will first search `linux` and then narrow on `header`)
|
||||
- Find matching package providers during search and allow selection
|
||||
- Remove make dependencies at the end of the build process
|
||||
- Build local PKGBUILDs with AUR dependencies
|
||||
- Un/Vote for packages
|
||||
|
||||
[](https://asciinema.org/a/399431)
|
||||
@ -30,18 +29,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.
|
||||
|
||||
> [!WARNING]
|
||||
> We are using `sudo` in these examples, you can switch that out for a different privilege escalation tool.
|
||||
|
||||
### Source
|
||||
|
||||
The initial installation of Yay can be done by cloning the PKGBUILD and
|
||||
building with makepkg:
|
||||
|
||||
We make sure we have the `base-devel` package group installed.
|
||||
Before you begin, make sure you have the `base-devel` package group installed.
|
||||
|
||||
```sh
|
||||
sudo pacman -S --needed git base-devel
|
||||
pacman -S --needed git base-devel
|
||||
git clone https://aur.archlinux.org/yay.git
|
||||
cd yay
|
||||
makepkg -si
|
||||
@ -50,27 +46,22 @@ makepkg -si
|
||||
If you want to do all of this at once, we can chain the commands like so:
|
||||
|
||||
```sh
|
||||
sudo pacman -S --needed git base-devel && git clone https://aur.archlinux.org/yay.git && cd yay && makepkg -si
|
||||
pacman -S --needed git base-devel && git clone https://aur.archlinux.org/yay.git && cd yay && makepkg -si
|
||||
```
|
||||
|
||||
|
||||
### Binary
|
||||
|
||||
If you do not want to compile yay yourself you can use the builds generated by
|
||||
GitHub Actions.
|
||||
|
||||
```sh
|
||||
sudo pacman -S --needed git base-devel
|
||||
pacman -S --needed git base-devel
|
||||
git clone https://aur.archlinux.org/yay-bin.git
|
||||
cd yay-bin
|
||||
makepkg -si
|
||||
```
|
||||
|
||||
If you want to do all of this at once, we can chain the commands like so:
|
||||
|
||||
```sh
|
||||
sudo pacman -S --needed git base-devel && git clone https://aur.archlinux.org/yay-bin.git && cd yay-bin && makepkg -si
|
||||
```
|
||||
|
||||
### Other distributions
|
||||
|
||||
If you're using Manjaro or [another distribution that packages `yay`](https://repology.org/project/yay/versions)
|
||||
@ -79,8 +70,8 @@ you can simply install yay using pacman (as root):
|
||||
```sh
|
||||
pacman -S --needed git base-devel yay
|
||||
```
|
||||
> [!WARNING]
|
||||
> distributions sometimes lag updating yay on their repositories.
|
||||
|
||||
⚠️ distributions sometimes lag updating yay on their repositories.
|
||||
|
||||
## First Use
|
||||
|
||||
@ -97,65 +88,90 @@ pacman -S --needed git base-devel yay
|
||||
|
||||
## Examples of Custom Operations
|
||||
|
||||
| Command | Description |
|
||||
| --------------------------------- | ---------------------------------------------------------------------------------------------------------- |
|
||||
| `yay` | Alias to `yay -Syu`. |
|
||||
| `yay <Search Term>` | Present package-installation selection menu. |
|
||||
| `yay -Bi <dir>` | Install dependencies and build a local PKGBUILD. |
|
||||
| `yay -G <AUR Package>` | Download PKGBUILD from ABS or AUR. (yay v12.0+) |
|
||||
| `yay -Gp <AUR Package>` | Print to stdout PKGBUILD from ABS or AUR. |
|
||||
| `yay -Ps` | Print system statistics. |
|
||||
| `yay -Syu --devel` | Perform system upgrade, but also check for development package updates. |
|
||||
| `yay -Syu --timeupdate` | Perform system upgrade and use PKGBUILD modification time (not version number) to determine update. |
|
||||
| `yay -Wu <AUR Package>` | Unvote for package (Requires setting `AUR_USERNAME` and `AUR_PASSWORD` environment variables) (yay v11.3+) |
|
||||
| `yay -Wv <AUR Package>` | Vote for package (Requires setting `AUR_USERNAME` and `AUR_PASSWORD` environment variables). (yay v11.3+) |
|
||||
| `yay -Y --combinedupgrade --save` | Make combined upgrade the default mode. |
|
||||
| `yay -Y --gendb` | Generate development package database used for devel update. |
|
||||
| `yay -Yc` | Clean unneeded dependencies. |
|
||||
| Command | Description |
|
||||
| --------------------------------- | --------------------------------------------------------------------------------------------------- |
|
||||
| `yay` | Alias to `yay -Syu`. |
|
||||
| `yay <Search Term>` | Present package-installation selection menu. |
|
||||
| `yay -Y --combinedupgrade --save` | Make combined upgrade the default mode. |
|
||||
| `yay -Ps` | Print system statistics. |
|
||||
| `yay -Yc` | Clean unneeded dependencies. |
|
||||
| `yay -G <AUR Package>` | Download PKGBUILD from ABS or AUR. |
|
||||
| `yay -Gp <AUR Package>` | Print to stdout PKGBUILD from ABS or AUR. |
|
||||
| `yay -Y --gendb` | Generate development package database used for devel update. |
|
||||
| `yay -Syu --devel` | Perform system upgrade, but also check for development package updates. |
|
||||
| `yay -Syu --timeupdate` | Perform system upgrade and use PKGBUILD modification time (not version number) to determine update. |
|
||||
| `yay -Wv <AUR Package>` | Vote for package (Requires setting `AUR_USERNAME` and `AUR_PASSWORD` environment variables). (yay v11.3+) |
|
||||
| `yay -Wu <AUR Package>` | Unvote for package (Requires setting `AUR_USERNAME` and `AUR_PASSWORD` environment variables) (yay v11.3+)|
|
||||
|
||||
## Frequently Asked Questions
|
||||
|
||||
- **Yay does not display colored output. How do I fix it?**
|
||||
|
||||
Make sure you have the `Color` option in your `/etc/pacman.conf`
|
||||
(see issue [#123](https://github.com/Jguer/yay/issues/123)).
|
||||
Make sure you have the `Color` option in your `/etc/pacman.conf`
|
||||
(see issue [#123](https://github.com/Jguer/yay/issues/123)).
|
||||
|
||||
- **Yay is not prompting to skip packages during system upgrade.**
|
||||
|
||||
The default behavior was changed after
|
||||
[v8.918](https://github.com/Jguer/yay/releases/tag/v8.918)
|
||||
(see [3bdb534](https://github.com/Jguer/yay/commit/3bdb5343218d99d40f8a449b887348611f6bdbfc)
|
||||
and issue [#554](https://github.com/Jguer/yay/issues/554)).
|
||||
To restore the package-skip behavior use `--combinedupgrade` (make
|
||||
it permanent by appending `--save`). Note: skipping packages will leave your
|
||||
system in a
|
||||
[partially-upgraded state](https://wiki.archlinux.org/index.php/System_maintenance#Partial_upgrades_are_unsupported).
|
||||
|
||||
- **Sometimes diffs are printed to the terminal, and other times they are paged via less. How do I fix this?**
|
||||
|
||||
Yay uses `git diff` to display diffs, which by default tells less not to
|
||||
page if the output can fit into one terminal length. This behavior can be
|
||||
overridden by exporting your own flags (`export LESS=SRX`).
|
||||
Yay uses `git diff` to display diffs, which by default tells less not to
|
||||
page if the output can fit into one terminal length. This behavior can be
|
||||
overridden by exporting your own flags (`export LESS=SRX`).
|
||||
|
||||
- **Yay is not asking me to edit PKGBUILDS, and I don't like the diff menu! What can I do?**
|
||||
|
||||
`yay --editmenu --diffmenu=false --save`
|
||||
`yay --editmenu --nodiffmenu --save`
|
||||
|
||||
- **How can I tell Yay to act only on AUR packages, or only on repo packages?**
|
||||
|
||||
`yay -{OPERATION} --aur`
|
||||
`yay -{OPERATION} --repo`
|
||||
`yay -{OPERATION} --aur`
|
||||
`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
|
||||
the packages have been flagged out of date on the AUR, but
|
||||
their maintainers have not yet updated the `PKGBUILD`s
|
||||
(see [outdated AUR packages](https://wiki.archlinux.org/index.php/Arch_User_Repository#Foo_in_the_AUR_is_outdated.3B_what_should_I_do.3F)).
|
||||
This message does not mean that updated AUR packages are available. It means
|
||||
the packages have been flagged out of date on the AUR, but
|
||||
their maintainers have not yet updated the `PKGBUILD`s
|
||||
(see [outdated AUR packages](https://wiki.archlinux.org/index.php/Arch_User_Repository#Foo_in_the_AUR_is_outdated.3B_what_should_I_do.3F)).
|
||||
|
||||
- **Yay doesn't install dependencies added to a PKGBUILD during installation.**
|
||||
|
||||
Yay resolves all dependencies ahead of time. You are free to edit the
|
||||
PKGBUILD in any way, but any problems you cause are your own and should not be
|
||||
reported unless they can be reproduced with the original PKGBUILD.
|
||||
Yay resolves all dependencies ahead of time. You are free to edit the
|
||||
PKGBUILD in any way, but any problems you cause are your own and should not be
|
||||
reported unless they can be reproduced with the original PKGBUILD.
|
||||
|
||||
- **I know my `-git` package has updates but yay doesn't offer to update it**
|
||||
|
||||
Yay uses a hash cache for development packages. Normally it is updated at the end of the package install with the message `Found git repo`.
|
||||
If you transition between aur helpers and did not install the devel package using yay at some point, it is possible it never got added to the cache. `yay -Y --gendb` will fix the current version of every devel package and start checking from there.
|
||||
Yay uses an hash cache for development packages. Normally it is updated at the end of the package install with the message `Found git repo`.
|
||||
If you transition between aur helpers and did not install the devel package using yay at some point, it is possible it never got added to the cache. `yay -Y --gendb` will fix the current version of every devel package and start checking from there.
|
||||
|
||||
- **I want to help out!**
|
||||
|
||||
Check [CONTRIBUTING.md](./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
|
||||
|
||||
@ -172,14 +188,14 @@ tools.
|
||||
|
||||
## Images
|
||||
|
||||
<p align="center">
|
||||
<img src="https://raw.githubusercontent.com/Jguer/jguer.github.io/refs/heads/master/yay/yay.png" width="42%">
|
||||
<img src="https://raw.githubusercontent.com/Jguer/jguer.github.io/refs/heads/master/yay/yay-s.png" width="42%">
|
||||
<p float="left">
|
||||
<img src="https://rawcdn.githack.com/Jguer/jguer.github.io/77647f396cb7156fd32e30970dbeaf6d6dc7f983/yay/yay.png" width="42%"/>
|
||||
<img src="https://rawcdn.githack.com/Jguer/jguer.github.io/77647f396cb7156fd32e30970dbeaf6d6dc7f983/yay/yay-s.png" width="42%"/>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="https://raw.githubusercontent.com/Jguer/jguer.github.io/refs/heads/master/yay/yay-y.png" width="42%">
|
||||
<img src="https://raw.githubusercontent.com/Jguer/jguer.github.io/refs/heads/master/yay/yay-ps.png" width="42%">
|
||||
<p float="left">
|
||||
<img src="https://rawcdn.githack.com/Jguer/jguer.github.io/77647f396cb7156fd32e30970dbeaf6d6dc7f983/yay/yay-y.png" width="42%"/>
|
||||
<img src="https://rawcdn.githack.com/Jguer/jguer.github.io/77647f396cb7156fd32e30970dbeaf6d6dc7f983/yay/yay-ps.png" width="42%"/>
|
||||
</p>
|
||||
|
||||
### Other AUR helpers/tools
|
||||
|
13
SECURITY.md
13
SECURITY.md
@ -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.
|
125
aur_source.go
Normal file
125
aur_source.go
Normal file
@ -0,0 +1,125 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sync"
|
||||
|
||||
"github.com/leonelquinteros/gotext"
|
||||
|
||||
"github.com/Jguer/yay/v11/pkg/dep"
|
||||
"github.com/Jguer/yay/v11/pkg/multierror"
|
||||
"github.com/Jguer/yay/v11/pkg/settings/exe"
|
||||
"github.com/Jguer/yay/v11/pkg/stringset"
|
||||
"github.com/Jguer/yay/v11/pkg/text"
|
||||
)
|
||||
|
||||
type ErrDownloadSource struct {
|
||||
inner error
|
||||
pkgName string
|
||||
errOut string
|
||||
}
|
||||
|
||||
func (e ErrDownloadSource) Error() string {
|
||||
return fmt.Sprintln(gotext.Get("error downloading sources: %s", text.Cyan(e.pkgName)),
|
||||
"\n\t context:", e.inner.Error(), "\n\t", e.errOut)
|
||||
}
|
||||
|
||||
func (e *ErrDownloadSource) Unwrap() error {
|
||||
return e.inner
|
||||
}
|
||||
|
||||
func downloadPKGBUILDSource(ctx context.Context, cmdBuilder exe.ICmdBuilder, dest,
|
||||
base string, incompatible stringset.StringSet) (err error) {
|
||||
dir := filepath.Join(dest, base)
|
||||
args := []string{"--verifysource", "-Ccf"}
|
||||
|
||||
if incompatible.Get(base) {
|
||||
args = append(args, "--ignorearch")
|
||||
}
|
||||
|
||||
err = cmdBuilder.Show(
|
||||
cmdBuilder.BuildMakepkgCmd(ctx, dir, args...))
|
||||
if err != nil {
|
||||
return ErrDownloadSource{inner: err, pkgName: base, errOut: ""}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func downloadPKGBUILDSourceWorker(ctx context.Context, wg *sync.WaitGroup, dest string,
|
||||
cBase <-chan string, valOut chan<- string, errOut chan<- error,
|
||||
cmdBuilder exe.ICmdBuilder, incompatible stringset.StringSet) {
|
||||
for base := range cBase {
|
||||
err := downloadPKGBUILDSource(ctx, cmdBuilder, dest, base, incompatible)
|
||||
if err != nil {
|
||||
errOut <- ErrDownloadSource{inner: err, pkgName: base, errOut: ""}
|
||||
} else {
|
||||
valOut <- base
|
||||
}
|
||||
}
|
||||
|
||||
wg.Done()
|
||||
}
|
||||
|
||||
func downloadPKGBUILDSourceFanout(ctx context.Context, cmdBuilder exe.ICmdBuilder, dest string,
|
||||
bases []dep.Base, incompatible stringset.StringSet, maxConcurrentDownloads int) error {
|
||||
if len(bases) == 1 {
|
||||
return downloadPKGBUILDSource(ctx, cmdBuilder, dest, bases[0].Pkgbase(), incompatible)
|
||||
}
|
||||
|
||||
var (
|
||||
numOfWorkers = runtime.NumCPU()
|
||||
wg = &sync.WaitGroup{}
|
||||
c = make(chan string)
|
||||
fanInChanValues = make(chan string)
|
||||
fanInChanErrors = make(chan error)
|
||||
)
|
||||
|
||||
if maxConcurrentDownloads != 0 {
|
||||
numOfWorkers = maxConcurrentDownloads
|
||||
}
|
||||
|
||||
go func() {
|
||||
for _, base := range bases {
|
||||
c <- base.Pkgbase()
|
||||
}
|
||||
|
||||
close(c)
|
||||
}()
|
||||
|
||||
// Launch Workers
|
||||
wg.Add(numOfWorkers)
|
||||
|
||||
for s := 0; s < numOfWorkers; s++ {
|
||||
go downloadPKGBUILDSourceWorker(ctx, wg, dest, c,
|
||||
fanInChanValues, fanInChanErrors, cmdBuilder, incompatible)
|
||||
}
|
||||
|
||||
go func() {
|
||||
wg.Wait()
|
||||
close(fanInChanValues)
|
||||
close(fanInChanErrors)
|
||||
}()
|
||||
|
||||
returnErr := multierror.MultiError{}
|
||||
|
||||
receiver:
|
||||
for {
|
||||
select {
|
||||
case _, ok := <-fanInChanValues:
|
||||
if !ok {
|
||||
break receiver
|
||||
}
|
||||
case err, ok := <-fanInChanErrors:
|
||||
if !ok {
|
||||
break receiver
|
||||
}
|
||||
returnErr.Add(err)
|
||||
}
|
||||
}
|
||||
|
||||
return returnErr.Return()
|
||||
}
|
@ -1,20 +1,20 @@
|
||||
//go:build !integration
|
||||
// +build !integration
|
||||
|
||||
package workdir
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/Jguer/yay/v12/pkg/multierror"
|
||||
"github.com/Jguer/yay/v12/pkg/settings/exe"
|
||||
"github.com/Jguer/aur"
|
||||
|
||||
"github.com/Jguer/yay/v11/pkg/dep"
|
||||
"github.com/Jguer/yay/v11/pkg/multierror"
|
||||
"github.com/Jguer/yay/v11/pkg/settings/exe"
|
||||
"github.com/Jguer/yay/v11/pkg/stringset"
|
||||
)
|
||||
|
||||
type TestMakepkgBuilder struct {
|
||||
@ -32,11 +32,6 @@ func (z *TestMakepkgBuilder) BuildMakepkgCmd(ctx context.Context, dir string, ex
|
||||
if z.want != "" {
|
||||
assert.Contains(z.test, cmd.String(), z.want)
|
||||
}
|
||||
|
||||
if z.GetKeepSrc() {
|
||||
assert.NotContains(z.test, cmd.String(), "-Cc")
|
||||
}
|
||||
|
||||
if z.wantDir != "" {
|
||||
assert.Equal(z.test, z.wantDir, cmd.Dir)
|
||||
}
|
||||
@ -50,54 +45,20 @@ func (z *TestMakepkgBuilder) Show(cmd *exec.Cmd) error {
|
||||
return z.showError
|
||||
}
|
||||
|
||||
func (z *TestMakepkgBuilder) GetKeepSrc() bool {
|
||||
return z.parentBuilder.KeepSrc
|
||||
}
|
||||
|
||||
// GIVEN 1 package
|
||||
// WHEN downloadPKGBUILDSource is called
|
||||
// THEN 1 call should be made to makepkg with the specified parameters and dir
|
||||
func Test_downloadPKGBUILDSource(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
type testCase struct {
|
||||
desc string
|
||||
keepSrc bool
|
||||
want string
|
||||
}
|
||||
|
||||
testCases := []testCase{
|
||||
{
|
||||
desc: "keepsrc",
|
||||
keepSrc: true,
|
||||
want: "makepkg --nocheck --config /etc/not.conf --verifysource --skippgpcheck -f",
|
||||
},
|
||||
{
|
||||
desc: "nokeepsrc",
|
||||
keepSrc: false,
|
||||
want: "makepkg --nocheck --config /etc/not.conf --verifysource --skippgpcheck -f -Cc",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
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))
|
||||
})
|
||||
cmdBuilder := &TestMakepkgBuilder{
|
||||
parentBuilder: &exe.CmdBuilder{MakepkgConfPath: "/etc/not.conf", MakepkgFlags: []string{"--nocheck"}, MakepkgBin: "makepkg"},
|
||||
test: t,
|
||||
want: "makepkg --nocheck --config /etc/not.conf --verifysource -Ccf",
|
||||
wantDir: "/tmp/yay-bin",
|
||||
}
|
||||
err := downloadPKGBUILDSource(context.TODO(), cmdBuilder, "/tmp", "yay-bin", stringset.Make())
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, int(cmdBuilder.passes))
|
||||
}
|
||||
|
||||
// GIVEN 1 package
|
||||
@ -108,13 +69,13 @@ func Test_downloadPKGBUILDSourceError(t *testing.T) {
|
||||
cmdBuilder := &TestMakepkgBuilder{
|
||||
parentBuilder: &exe.CmdBuilder{MakepkgConfPath: "/etc/not.conf", MakepkgFlags: []string{"--nocheck"}, MakepkgBin: "makepkg"},
|
||||
test: t,
|
||||
want: "makepkg --nocheck --config /etc/not.conf --verifysource --skippgpcheck -f -Cc",
|
||||
want: "makepkg --nocheck --config /etc/not.conf --verifysource -Ccf",
|
||||
wantDir: "/tmp/yay-bin",
|
||||
showError: &exec.ExitError{},
|
||||
}
|
||||
err := downloadPKGBUILDSource(context.Background(), cmdBuilder, filepath.Join("/tmp", "yay-bin"), false)
|
||||
err := downloadPKGBUILDSource(context.TODO(), cmdBuilder, "/tmp", "yay-bin", stringset.Make())
|
||||
assert.Error(t, err)
|
||||
assert.EqualError(t, err, "error downloading sources: \x1b[36m/tmp/yay-bin\x1b[0m \n\t context: <nil> \n\t \n")
|
||||
assert.EqualError(t, err, "error downloading sources: \x1b[36myay-bin\x1b[0m \n\t context: <nil> \n\t \n")
|
||||
}
|
||||
|
||||
// GIVEN 5 packages
|
||||
@ -123,13 +84,14 @@ func Test_downloadPKGBUILDSourceError(t *testing.T) {
|
||||
func Test_downloadPKGBUILDSourceFanout(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
pkgBuildDirs := map[string]string{
|
||||
"yay": "/tmp/yay",
|
||||
"yay-bin": "/tmp/yay-bin",
|
||||
"yay-git": "/tmp/yay-git",
|
||||
"yay-v11": "/tmp/yay-v11",
|
||||
"yay-v12": "/tmp/yay-v12",
|
||||
bases := []dep.Base{
|
||||
{&aur.Pkg{PackageBase: "yay"}},
|
||||
{&aur.Pkg{PackageBase: "yay-bin"}},
|
||||
{&aur.Pkg{PackageBase: "yay-git"}},
|
||||
{&aur.Pkg{PackageBase: "yay-v11"}},
|
||||
{&aur.Pkg{PackageBase: "yay-v12"}},
|
||||
}
|
||||
|
||||
for _, maxConcurrentDownloads := range []int{0, 3} {
|
||||
t.Run(fmt.Sprintf("maxconcurrentdownloads set to %d", maxConcurrentDownloads), func(t *testing.T) {
|
||||
cmdBuilder := &TestMakepkgBuilder{
|
||||
@ -140,7 +102,7 @@ func Test_downloadPKGBUILDSourceFanout(t *testing.T) {
|
||||
test: t,
|
||||
}
|
||||
|
||||
err := downloadPKGBUILDSourceFanout(context.Background(), cmdBuilder, pkgBuildDirs, true, maxConcurrentDownloads)
|
||||
err := downloadPKGBUILDSourceFanout(context.TODO(), cmdBuilder, "/tmp", bases, stringset.Make(), maxConcurrentDownloads)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 5, int(cmdBuilder.passes))
|
||||
})
|
||||
@ -160,9 +122,11 @@ func Test_downloadPKGBUILDSourceFanoutNoCC(t *testing.T) {
|
||||
test: t,
|
||||
}
|
||||
|
||||
pkgBuildDirs := map[string]string{"yay": "/tmp/yay"}
|
||||
bases := []dep.Base{
|
||||
{&aur.Pkg{PackageBase: "yay"}},
|
||||
}
|
||||
|
||||
err := downloadPKGBUILDSourceFanout(context.Background(), cmdBuilder, pkgBuildDirs, false, 0)
|
||||
err := downloadPKGBUILDSourceFanout(context.TODO(), cmdBuilder, "/tmp", bases, stringset.Make(), 0)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, int(cmdBuilder.passes))
|
||||
}
|
||||
@ -181,15 +145,15 @@ func Test_downloadPKGBUILDSourceFanoutError(t *testing.T) {
|
||||
showError: &exec.ExitError{},
|
||||
}
|
||||
|
||||
pkgBuildDirs := map[string]string{
|
||||
"yay": "/tmp/yay",
|
||||
"yay-bin": "/tmp/yay-bin",
|
||||
"yay-git": "/tmp/yay-git",
|
||||
"yay-v11": "/tmp/yay-v11",
|
||||
"yay-v12": "/tmp/yay-v12",
|
||||
bases := []dep.Base{
|
||||
{&aur.Pkg{PackageBase: "yay"}},
|
||||
{&aur.Pkg{PackageBase: "yay-bin"}},
|
||||
{&aur.Pkg{PackageBase: "yay-git"}},
|
||||
{&aur.Pkg{PackageBase: "yay-v11"}},
|
||||
{&aur.Pkg{PackageBase: "yay-v12"}},
|
||||
}
|
||||
|
||||
err := downloadPKGBUILDSourceFanout(context.Background(), cmdBuilder, pkgBuildDirs, false, 0)
|
||||
err := downloadPKGBUILDSourceFanout(context.TODO(), cmdBuilder, "/tmp", bases, stringset.Make(), 0)
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, 5, int(cmdBuilder.passes))
|
||||
assert.Len(t, err.(*multierror.MultiError).Errors, 5)
|
@ -1,15 +1,11 @@
|
||||
FROM docker.io/ljmf00/archlinux:devel
|
||||
LABEL maintainer="Jguer,docker@jguer.space"
|
||||
FROM docker.io/heywoodlh/archlinux:latest
|
||||
|
||||
ENV GO111MODULE=on
|
||||
WORKDIR /app
|
||||
|
||||
RUN sed -i '/^\[community\]/,/^\[/ s/^/#/' /etc/pacman.conf
|
||||
|
||||
COPY go.mod .
|
||||
|
||||
RUN pacman-key --init && pacman -Sy && pacman -S --overwrite=* --noconfirm archlinux-keyring && \
|
||||
pacman -Su --overwrite=* --needed --noconfirm pacman doxygen meson asciidoc go git gcc make sudo base-devel && \
|
||||
RUN pacman -Sy && pacman -S --overwrite=* --noconfirm archlinux-keyring && pacman -Su --overwrite=* --needed --noconfirm go git && \
|
||||
rm -rfv /var/cache/pacman/* /var/lib/pacman/sync/* && \
|
||||
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s v2.1.5 && \
|
||||
go mod download
|
||||
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s v1.50.1 && \
|
||||
go mod download
|
||||
|
140
clean.go
140
clean.go
@ -2,75 +2,68 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/Jguer/aur"
|
||||
mapset "github.com/deckarep/golang-set/v2"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
|
||||
"github.com/Jguer/yay/v12/pkg/db"
|
||||
"github.com/Jguer/yay/v12/pkg/runtime"
|
||||
"github.com/Jguer/yay/v12/pkg/settings"
|
||||
"github.com/Jguer/yay/v12/pkg/settings/exe"
|
||||
"github.com/Jguer/yay/v12/pkg/settings/parser"
|
||||
"github.com/Jguer/yay/v11/pkg/db"
|
||||
"github.com/Jguer/yay/v11/pkg/dep"
|
||||
"github.com/Jguer/yay/v11/pkg/query"
|
||||
"github.com/Jguer/yay/v11/pkg/settings"
|
||||
"github.com/Jguer/yay/v11/pkg/settings/parser"
|
||||
"github.com/Jguer/yay/v11/pkg/stringset"
|
||||
"github.com/Jguer/yay/v11/pkg/text"
|
||||
)
|
||||
|
||||
// CleanDependencies removes all dangling dependencies in system.
|
||||
func cleanDependencies(ctx context.Context, cfg *settings.Configuration,
|
||||
cmdBuilder exe.ICmdBuilder, cmdArgs *parser.Arguments, dbExecutor db.Executor,
|
||||
removeOptional bool,
|
||||
) error {
|
||||
func cleanDependencies(ctx context.Context, cmdArgs *parser.Arguments, dbExecutor db.Executor, removeOptional bool) error {
|
||||
hanging := hangingPackages(removeOptional, dbExecutor)
|
||||
if len(hanging) != 0 {
|
||||
return cleanRemove(ctx, cfg, cmdBuilder, cmdArgs, hanging)
|
||||
return cleanRemove(ctx, cmdArgs, hanging)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CleanRemove sends a full removal command to pacman with the pkgName slice.
|
||||
func cleanRemove(ctx context.Context, cfg *settings.Configuration,
|
||||
cmdBuilder exe.ICmdBuilder, cmdArgs *parser.Arguments, pkgNames []string,
|
||||
) error {
|
||||
func cleanRemove(ctx context.Context, cmdArgs *parser.Arguments, pkgNames []string) error {
|
||||
if len(pkgNames) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
arguments := cmdArgs.CopyGlobal()
|
||||
if err := arguments.AddArg("R", "s", "u"); err != nil {
|
||||
return err
|
||||
}
|
||||
_ = arguments.AddArg("R")
|
||||
arguments.AddTarget(pkgNames...)
|
||||
|
||||
return cmdBuilder.Show(
|
||||
cmdBuilder.BuildPacmanCmd(ctx,
|
||||
arguments, cfg.Mode, settings.NoConfirm))
|
||||
return config.Runtime.CmdBuilder.Show(
|
||||
config.Runtime.CmdBuilder.BuildPacmanCmd(ctx,
|
||||
arguments, config.Runtime.Mode, settings.NoConfirm))
|
||||
}
|
||||
|
||||
func syncClean(ctx context.Context, run *runtime.Runtime, cmdArgs *parser.Arguments, dbExecutor db.Executor) error {
|
||||
func syncClean(ctx context.Context, cmdArgs *parser.Arguments, dbExecutor db.Executor) error {
|
||||
keepInstalled := false
|
||||
keepCurrent := false
|
||||
|
||||
_, removeAll, _ := cmdArgs.GetArg("c", "clean")
|
||||
|
||||
for _, v := range run.PacmanConf.CleanMethod {
|
||||
switch v {
|
||||
case "KeepInstalled":
|
||||
for _, v := range config.Runtime.PacmanConf.CleanMethod {
|
||||
if v == "KeepInstalled" {
|
||||
keepInstalled = true
|
||||
case "KeepCurrent":
|
||||
} else if v == "KeepCurrent" {
|
||||
keepCurrent = true
|
||||
}
|
||||
}
|
||||
|
||||
if run.Cfg.Mode.AtLeastRepo() {
|
||||
if err := run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx,
|
||||
cmdArgs, run.Cfg.Mode, settings.NoConfirm)); err != nil {
|
||||
if config.Runtime.Mode.AtLeastRepo() {
|
||||
if err := config.Runtime.CmdBuilder.Show(config.Runtime.CmdBuilder.BuildPacmanCmd(ctx,
|
||||
cmdArgs, config.Runtime.Mode, settings.NoConfirm)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if !run.Cfg.Mode.AtLeastAUR() {
|
||||
if !config.Runtime.Mode.AtLeastAUR() {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -81,10 +74,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?")
|
||||
}
|
||||
|
||||
run.Logger.Println(gotext.Get("\nBuild directory:"), run.Cfg.BuildDir)
|
||||
fmt.Println(gotext.Get("\nBuild directory:"), config.BuildDir)
|
||||
|
||||
if run.Logger.ContinueTask(question, true, settings.NoConfirm) {
|
||||
if err := cleanAUR(ctx, run, keepInstalled, keepCurrent, removeAll, dbExecutor); err != nil {
|
||||
if text.ContinueTask(os.Stdin, question, true, settings.NoConfirm) {
|
||||
if err := cleanAUR(ctx, keepInstalled, keepCurrent, removeAll, dbExecutor); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -93,24 +86,22 @@ func syncClean(ctx context.Context, run *runtime.Runtime, cmdArgs *parser.Argume
|
||||
return nil
|
||||
}
|
||||
|
||||
if run.Logger.ContinueTask(gotext.Get("Do you want to remove ALL untracked AUR files?"), true, settings.NoConfirm) {
|
||||
return cleanUntracked(ctx, run)
|
||||
if text.ContinueTask(os.Stdin, gotext.Get("Do you want to remove ALL untracked AUR files?"), true, settings.NoConfirm) {
|
||||
return cleanUntracked(ctx)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func cleanAUR(ctx context.Context, run *runtime.Runtime,
|
||||
keepInstalled, keepCurrent, removeAll bool, dbExecutor db.Executor,
|
||||
) error {
|
||||
run.Logger.Println(gotext.Get("removing AUR packages from cache..."))
|
||||
func cleanAUR(ctx context.Context, keepInstalled, keepCurrent, removeAll bool, dbExecutor db.Executor) error {
|
||||
fmt.Println(gotext.Get("removing AUR packages from cache..."))
|
||||
|
||||
installedBases := mapset.NewThreadUnsafeSet[string]()
|
||||
inAURBases := mapset.NewThreadUnsafeSet[string]()
|
||||
installedBases := make(stringset.StringSet)
|
||||
inAURBases := make(stringset.StringSet)
|
||||
|
||||
remotePackages := dbExecutor.InstalledRemotePackages()
|
||||
remotePackages, _ := query.GetRemotePackages(dbExecutor)
|
||||
|
||||
files, err := os.ReadDir(run.Cfg.BuildDir)
|
||||
files, err := os.ReadDir(config.BuildDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -130,23 +121,21 @@ func cleanAUR(ctx context.Context, run *runtime.Runtime,
|
||||
// Querying the AUR is slow and needs internet so don't do it if we
|
||||
// don't need to.
|
||||
if keepCurrent {
|
||||
info, errInfo := run.AURClient.Get(ctx, &aur.Query{
|
||||
Needles: cachedPackages,
|
||||
})
|
||||
info, errInfo := query.AURInfo(ctx, config.Runtime.AURClient, cachedPackages, &query.AURWarnings{}, config.RequestSplitN)
|
||||
if errInfo != nil {
|
||||
return errInfo
|
||||
}
|
||||
|
||||
for i := range info {
|
||||
inAURBases.Add(info[i].PackageBase)
|
||||
for _, pkg := range info {
|
||||
inAURBases.Set(pkg.PackageBase)
|
||||
}
|
||||
}
|
||||
|
||||
for _, pkg := range remotePackages {
|
||||
if pkg.Base() != "" {
|
||||
installedBases.Add(pkg.Base())
|
||||
installedBases.Set(pkg.Base())
|
||||
} else {
|
||||
installedBases.Add(pkg.Name())
|
||||
installedBases.Set(pkg.Name())
|
||||
}
|
||||
}
|
||||
|
||||
@ -156,29 +145,28 @@ func cleanAUR(ctx context.Context, run *runtime.Runtime,
|
||||
}
|
||||
|
||||
if !removeAll {
|
||||
if keepInstalled && installedBases.Contains(file.Name()) {
|
||||
if keepInstalled && installedBases.Get(file.Name()) {
|
||||
continue
|
||||
}
|
||||
|
||||
if keepCurrent && inAURBases.Contains(file.Name()) {
|
||||
if keepCurrent && inAURBases.Get(file.Name()) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
dir := filepath.Join(run.Cfg.BuildDir, file.Name())
|
||||
run.Logger.Debugln("removing", dir)
|
||||
if err = os.RemoveAll(dir); err != nil {
|
||||
run.Logger.Warnln(gotext.Get("Unable to remove %s: %s", dir, err))
|
||||
err = os.RemoveAll(filepath.Join(config.BuildDir, file.Name()))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func cleanUntracked(ctx context.Context, run *runtime.Runtime) error {
|
||||
run.Logger.Println(gotext.Get("removing untracked AUR files from cache..."))
|
||||
func cleanUntracked(ctx context.Context) error {
|
||||
fmt.Println(gotext.Get("removing untracked AUR files from cache..."))
|
||||
|
||||
files, err := os.ReadDir(run.Cfg.BuildDir)
|
||||
files, err := os.ReadDir(config.BuildDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -188,11 +176,11 @@ func cleanUntracked(ctx context.Context, run *runtime.Runtime) error {
|
||||
continue
|
||||
}
|
||||
|
||||
dir := filepath.Join(run.Cfg.BuildDir, file.Name())
|
||||
run.Logger.Debugln("cleaning", dir)
|
||||
dir := filepath.Join(config.BuildDir, file.Name())
|
||||
if isGitRepository(dir) {
|
||||
if err := run.CmdBuilder.Show(run.CmdBuilder.BuildGitCmd(ctx, dir, "clean", "-fx")); err != nil {
|
||||
run.Logger.Warnln(gotext.Get("Unable to clean:"), dir)
|
||||
if err := config.Runtime.CmdBuilder.Show(config.Runtime.CmdBuilder.BuildGitCmd(ctx, dir, "clean", "-fx")); err != nil {
|
||||
text.Warnln(gotext.Get("Unable to clean:"), dir)
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -205,3 +193,29 @@ func isGitRepository(dir string) bool {
|
||||
_, err := os.Stat(filepath.Join(dir, ".git"))
|
||||
return !os.IsNotExist(err)
|
||||
}
|
||||
|
||||
func cleanAfter(ctx context.Context, bases []dep.Base) {
|
||||
fmt.Println(gotext.Get("removing untracked AUR files from cache..."))
|
||||
|
||||
for i, base := range bases {
|
||||
dir := filepath.Join(config.BuildDir, base.Pkgbase())
|
||||
if !isGitRepository(dir) {
|
||||
continue
|
||||
}
|
||||
|
||||
text.OperationInfoln(gotext.Get("Cleaning (%d/%d): %s", i+1, len(bases), text.Cyan(dir)))
|
||||
|
||||
_, stderr, err := config.Runtime.CmdBuilder.Capture(
|
||||
config.Runtime.CmdBuilder.BuildGitCmd(
|
||||
ctx, dir, "reset", "--hard", "HEAD"))
|
||||
if err != nil {
|
||||
text.Errorln(gotext.Get("error resetting %s: %s", base.String(), stderr))
|
||||
}
|
||||
|
||||
if err := config.Runtime.CmdBuilder.Show(
|
||||
config.Runtime.CmdBuilder.BuildGitCmd(
|
||||
ctx, dir, "clean", "-fx", "--exclude='*.pkg.*'")); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
116
clean_test.go
116
clean_test.go
@ -1,116 +0,0 @@
|
||||
//go:build !integration
|
||||
// +build !integration
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/Jguer/go-alpm/v2"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/Jguer/yay/v12/pkg/db/mock"
|
||||
"github.com/Jguer/yay/v12/pkg/runtime"
|
||||
"github.com/Jguer/yay/v12/pkg/settings"
|
||||
"github.com/Jguer/yay/v12/pkg/settings/exe"
|
||||
"github.com/Jguer/yay/v12/pkg/settings/parser"
|
||||
)
|
||||
|
||||
func TestCleanHanging(t *testing.T) {
|
||||
pacmanBin := t.TempDir() + "/pacman"
|
||||
|
||||
t.Parallel()
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
args []string
|
||||
wantShow []string
|
||||
}{
|
||||
{
|
||||
name: "clean",
|
||||
args: []string{"Y", "c"},
|
||||
wantShow: []string{"pacman", "-R", "-s", "-u", "--config", "/etc/pacman.conf", "--", "lsp-plugins"},
|
||||
},
|
||||
{
|
||||
name: "clean double",
|
||||
args: []string{"Y", "c", "c"},
|
||||
wantShow: []string{"pacman", "-R", "-s", "-u", "--config", "/etc/pacman.conf", "--", "lsp-plugins", "linux-headers"},
|
||||
},
|
||||
}
|
||||
|
||||
dbExc := &mock.DBExecutor{
|
||||
PackageOptionalDependsFn: func(i alpm.IPackage) []alpm.Depend {
|
||||
if i.Name() == "linux" {
|
||||
return []alpm.Depend{
|
||||
{
|
||||
Name: "linux-headers",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return []alpm.Depend{}
|
||||
},
|
||||
PackageProvidesFn: func(p alpm.IPackage) []alpm.Depend { return []alpm.Depend{} },
|
||||
PackageDependsFn: func(p alpm.IPackage) []alpm.Depend { return []alpm.Depend{} },
|
||||
LocalPackagesFn: func() []mock.IPackage {
|
||||
return []mock.IPackage{
|
||||
&mock.Package{
|
||||
PReason: alpm.PkgReasonExplicit,
|
||||
PName: "linux",
|
||||
},
|
||||
&mock.Package{
|
||||
PReason: alpm.PkgReasonDepend,
|
||||
PName: "lsp-plugins",
|
||||
},
|
||||
&mock.Package{
|
||||
PReason: alpm.PkgReasonDepend,
|
||||
PName: "linux-headers",
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
mockRunner := &exe.MockRunner{
|
||||
CaptureFn: func(cmd *exec.Cmd) (stdout string, stderr string, err error) {
|
||||
return "", "", nil
|
||||
},
|
||||
ShowFn: func(cmd *exec.Cmd) error { return nil },
|
||||
}
|
||||
cmdBuilder := &exe.CmdBuilder{
|
||||
SudoBin: "su",
|
||||
PacmanBin: pacmanBin,
|
||||
PacmanConfigPath: "/etc/pacman.conf",
|
||||
GitBin: "git",
|
||||
Runner: mockRunner,
|
||||
SudoLoopEnabled: false,
|
||||
}
|
||||
|
||||
run := &runtime.Runtime{CmdBuilder: cmdBuilder, Cfg: &settings.Configuration{}}
|
||||
cmdArgs := parser.MakeArguments()
|
||||
cmdArgs.AddArg(tc.args...)
|
||||
|
||||
err := handleCmd(context.Background(),
|
||||
run, cmdArgs, dbExc,
|
||||
)
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
for i, call := range mockRunner.ShowCalls {
|
||||
show := call.Args[0].(*exec.Cmd).String()
|
||||
show = strings.ReplaceAll(show, pacmanBin, "pacman")
|
||||
|
||||
// options are in a different order on different systems and on CI root user is used
|
||||
assert.Subset(t, strings.Split(show, " "),
|
||||
strings.Split(tc.wantShow[i], " "),
|
||||
fmt.Sprintf("%d - %s", i, show))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
286
cmd.go
286
cmd.go
@ -6,28 +6,27 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
alpm "github.com/Jguer/go-alpm/v2"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
|
||||
"github.com/Jguer/yay/v12/pkg/completion"
|
||||
"github.com/Jguer/yay/v12/pkg/db"
|
||||
"github.com/Jguer/yay/v12/pkg/download"
|
||||
"github.com/Jguer/yay/v12/pkg/intrange"
|
||||
"github.com/Jguer/yay/v12/pkg/news"
|
||||
"github.com/Jguer/yay/v12/pkg/query"
|
||||
"github.com/Jguer/yay/v12/pkg/runtime"
|
||||
"github.com/Jguer/yay/v12/pkg/settings"
|
||||
"github.com/Jguer/yay/v12/pkg/settings/exe"
|
||||
"github.com/Jguer/yay/v12/pkg/settings/parser"
|
||||
"github.com/Jguer/yay/v12/pkg/text"
|
||||
"github.com/Jguer/yay/v12/pkg/upgrade"
|
||||
"github.com/Jguer/yay/v12/pkg/vcs"
|
||||
"github.com/Jguer/yay/v11/pkg/completion"
|
||||
"github.com/Jguer/yay/v11/pkg/db"
|
||||
"github.com/Jguer/yay/v11/pkg/download"
|
||||
"github.com/Jguer/yay/v11/pkg/intrange"
|
||||
"github.com/Jguer/yay/v11/pkg/news"
|
||||
"github.com/Jguer/yay/v11/pkg/query"
|
||||
"github.com/Jguer/yay/v11/pkg/settings"
|
||||
"github.com/Jguer/yay/v11/pkg/settings/parser"
|
||||
"github.com/Jguer/yay/v11/pkg/text"
|
||||
"github.com/Jguer/yay/v11/pkg/upgrade"
|
||||
"github.com/Jguer/yay/v11/pkg/vcs"
|
||||
)
|
||||
|
||||
func usage(logger *text.Logger) {
|
||||
logger.Println(`Usage:
|
||||
func usage() {
|
||||
fmt.Println(`Usage:
|
||||
yay
|
||||
yay <operation> [...]
|
||||
yay <package(s)>
|
||||
@ -44,17 +43,15 @@ operations:
|
||||
yay {-U --upgrade} [options] <file(s)>
|
||||
|
||||
New operations:
|
||||
yay {-B --build} [options] [dir]
|
||||
yay {-G --getpkgbuild} [options] [package(s)]
|
||||
yay {-P --show} [options]
|
||||
yay {-W --web} [options] [package(s)]
|
||||
yay {-Y --yay} [options] [package(s)]
|
||||
yay {-P --show} [options]
|
||||
yay {-G --getpkgbuild} [options] [package(s)]
|
||||
|
||||
If no operation is specified 'yay -Syu' will be performed
|
||||
If no operation is specified and targets are provided -Y will be assumed
|
||||
If no arguments are provided 'yay -Syu' will be performed
|
||||
If no operation is provided -Y will be assumed
|
||||
|
||||
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
|
||||
|
||||
Permanent configuration options:
|
||||
@ -92,19 +89,24 @@ Permanent configuration options:
|
||||
--cleanmenu Give the option to clean build PKGBUILDS
|
||||
--diffmenu Give the option to show diffs for build files
|
||||
--editmenu Give the option to edit/view PKGBUILDS
|
||||
--upgrademenu Show a detailed list of updates with the option to skip any
|
||||
--nocleanmenu Don't clean build PKGBUILDS
|
||||
--nodiffmenu Don't show diffs for build files
|
||||
--noeditmenu Don't edit/view PKGBUILDS
|
||||
--noupgrademenu Don't show the upgrade menu
|
||||
--askremovemake Ask to remove makedepends after install
|
||||
--askyesremovemake Ask to remove makedepends after install("Y" as default)
|
||||
--removemake Remove makedepends after install
|
||||
--noremovemake Don't remove makedepends after install
|
||||
|
||||
--cleanafter Remove package sources after successful install
|
||||
--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
|
||||
--topdown Shows repository's packages first and then AUR's
|
||||
--singlelineresults List each search result on its own line
|
||||
--doublelineresults List each search result on two lines, like pacman
|
||||
|
||||
--devel Check development packages during sysupgrade
|
||||
--nodevel Do not check development packages
|
||||
--rebuild Always build target packages
|
||||
--rebuildall Always build all AUR packages
|
||||
--norebuild Skip package build if in cache and up to date
|
||||
@ -113,14 +115,23 @@ Permanent configuration options:
|
||||
--noredownload Skip pkgbuild download if in cache and up to date
|
||||
--redownloadall Always download pkgbuilds of all AUR packages
|
||||
--provides Look for matching providers when searching for packages
|
||||
--noprovides Just look for packages by pkgname
|
||||
--pgpfetch Prompt to import PGP keys from PKGBUILDs
|
||||
--nopgpfetch Don't prompt to import PGP keys
|
||||
--useask Automatically resolve conflicts using pacman's ask flag
|
||||
--nouseask Confirm conflicts manually during the install
|
||||
--combinedupgrade Refresh then perform the repo and AUR upgrade together
|
||||
--nocombinedupgrade Perform the repo upgrade and AUR upgrade separately
|
||||
--batchinstall Build multiple AUR packages then install them together
|
||||
--nobatchinstall Build and install each AUR package one by one
|
||||
|
||||
--sudo <file> sudo command to use
|
||||
--sudoflags <flags> Pass arguments to sudo
|
||||
--sudoloop Loop sudo calls in the background to avoid timeout
|
||||
--nosudoloop Do not loop sudo calls in the background
|
||||
|
||||
--timeupdate Check packages' AUR page for changes during sysupgrade
|
||||
--notimeupdate Do not check packages' AUR page for changes
|
||||
|
||||
show specific options:
|
||||
-c --complete Used for completions
|
||||
@ -130,7 +141,7 @@ show specific options:
|
||||
-w --news Print arch news
|
||||
|
||||
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
|
||||
|
||||
getpkgbuild specific options:
|
||||
@ -138,49 +149,46 @@ getpkgbuild specific options:
|
||||
-p --print Print pkgbuild of packages`)
|
||||
}
|
||||
|
||||
func handleCmd(ctx context.Context, run *runtime.Runtime,
|
||||
cmdArgs *parser.Arguments, dbExecutor db.Executor,
|
||||
) error {
|
||||
func handleCmd(ctx context.Context, cmdArgs *parser.Arguments, dbExecutor db.Executor) error {
|
||||
if cmdArgs.ExistsArg("h", "help") {
|
||||
return handleHelp(ctx, run, cmdArgs)
|
||||
return handleHelp(ctx, cmdArgs)
|
||||
}
|
||||
|
||||
if run.Cfg.SudoLoop && cmdArgs.NeedRoot(run.Cfg.Mode) {
|
||||
run.CmdBuilder.SudoLoop()
|
||||
if config.SudoLoop && cmdArgs.NeedRoot(config.Runtime.Mode) {
|
||||
config.Runtime.CmdBuilder.SudoLoop()
|
||||
}
|
||||
|
||||
switch cmdArgs.Op {
|
||||
case "V", "version":
|
||||
handleVersion(run.Logger)
|
||||
handleVersion()
|
||||
|
||||
return nil
|
||||
case "D", "database":
|
||||
return run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx,
|
||||
cmdArgs, run.Cfg.Mode, settings.NoConfirm))
|
||||
return config.Runtime.CmdBuilder.Show(config.Runtime.CmdBuilder.BuildPacmanCmd(ctx,
|
||||
cmdArgs, config.Runtime.Mode, settings.NoConfirm))
|
||||
case "F", "files":
|
||||
return run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx,
|
||||
cmdArgs, run.Cfg.Mode, settings.NoConfirm))
|
||||
return config.Runtime.CmdBuilder.Show(config.Runtime.CmdBuilder.BuildPacmanCmd(ctx,
|
||||
cmdArgs, config.Runtime.Mode, settings.NoConfirm))
|
||||
case "Q", "query":
|
||||
return handleQuery(ctx, run, cmdArgs, dbExecutor)
|
||||
return handleQuery(ctx, cmdArgs, dbExecutor)
|
||||
case "R", "remove":
|
||||
return handleRemove(ctx, run, cmdArgs, run.VCSStore)
|
||||
return handleRemove(ctx, cmdArgs, config.Runtime.VCSStore)
|
||||
case "S", "sync":
|
||||
return handleSync(ctx, run, cmdArgs, dbExecutor)
|
||||
return handleSync(ctx, cmdArgs, dbExecutor)
|
||||
case "T", "deptest":
|
||||
return run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx,
|
||||
cmdArgs, run.Cfg.Mode, settings.NoConfirm))
|
||||
return config.Runtime.CmdBuilder.Show(config.Runtime.CmdBuilder.BuildPacmanCmd(ctx,
|
||||
cmdArgs, config.Runtime.Mode, settings.NoConfirm))
|
||||
case "U", "upgrade":
|
||||
return handleUpgrade(ctx, run, cmdArgs)
|
||||
case "B", "build":
|
||||
return handleBuild(ctx, run, dbExecutor, cmdArgs)
|
||||
return config.Runtime.CmdBuilder.Show(config.Runtime.CmdBuilder.BuildPacmanCmd(ctx,
|
||||
cmdArgs, config.Runtime.Mode, settings.NoConfirm))
|
||||
case "G", "getpkgbuild":
|
||||
return handleGetpkgbuild(ctx, run, cmdArgs, dbExecutor)
|
||||
return handleGetpkgbuild(ctx, cmdArgs, dbExecutor)
|
||||
case "P", "show":
|
||||
return handlePrint(ctx, run, cmdArgs, dbExecutor)
|
||||
return handlePrint(ctx, cmdArgs, dbExecutor)
|
||||
case "Y", "yay":
|
||||
return handleYay(ctx, run, cmdArgs, run.CmdBuilder,
|
||||
dbExecutor, run.QueryBuilder)
|
||||
return handleYay(ctx, cmdArgs, dbExecutor, config.Runtime.QueryBuilder)
|
||||
case "W", "web":
|
||||
return handleWeb(ctx, run, cmdArgs)
|
||||
return handleWeb(ctx, cmdArgs)
|
||||
}
|
||||
|
||||
return errors.New(gotext.Get("unhandled operation"))
|
||||
@ -196,33 +204,32 @@ func getFilter(cmdArgs *parser.Arguments) (upgrade.Filter, error) {
|
||||
case deps && explicit:
|
||||
return nil, errors.New(gotext.Get("invalid option: '--deps' and '--explicit' may not be used together"))
|
||||
case deps:
|
||||
return func(pkg *upgrade.Upgrade) bool {
|
||||
return func(pkg upgrade.Upgrade) bool {
|
||||
return pkg.Reason == alpm.PkgReasonDepend
|
||||
}, nil
|
||||
case explicit:
|
||||
return func(pkg *upgrade.Upgrade) bool {
|
||||
return func(pkg upgrade.Upgrade) bool {
|
||||
return pkg.Reason == alpm.PkgReasonExplicit
|
||||
}, nil
|
||||
}
|
||||
|
||||
return func(pkg *upgrade.Upgrade) bool {
|
||||
return func(pkg upgrade.Upgrade) bool {
|
||||
return true
|
||||
}, nil
|
||||
}
|
||||
|
||||
func handleQuery(ctx context.Context, run *runtime.Runtime, cmdArgs *parser.Arguments, dbExecutor db.Executor) error {
|
||||
func handleQuery(ctx context.Context, cmdArgs *parser.Arguments, dbExecutor db.Executor) error {
|
||||
if cmdArgs.ExistsArg("u", "upgrades") {
|
||||
filter, err := getFilter(cmdArgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return printUpdateList(ctx, run, cmdArgs, dbExecutor,
|
||||
cmdArgs.ExistsDouble("u", "sysupgrade"), filter)
|
||||
return printUpdateList(ctx, cmdArgs, dbExecutor, cmdArgs.ExistsDouble("u", "sysupgrade"), filter)
|
||||
}
|
||||
|
||||
if err := run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx,
|
||||
cmdArgs, run.Cfg.Mode, settings.NoConfirm)); err != nil {
|
||||
if err := config.Runtime.CmdBuilder.Show(config.Runtime.CmdBuilder.BuildPacmanCmd(ctx,
|
||||
cmdArgs, config.Runtime.Mode, settings.NoConfirm)); err != nil {
|
||||
if str := err.Error(); strings.Contains(str, "exit status") {
|
||||
// yay -Qdt should not output anything in case of error
|
||||
return fmt.Errorf("")
|
||||
@ -234,153 +241,138 @@ func handleQuery(ctx context.Context, run *runtime.Runtime, cmdArgs *parser.Argu
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleHelp(ctx context.Context, run *runtime.Runtime, cmdArgs *parser.Arguments) error {
|
||||
usage(run.Logger)
|
||||
func handleHelp(ctx context.Context, cmdArgs *parser.Arguments) error {
|
||||
switch cmdArgs.Op {
|
||||
case "Y", "yay", "G", "getpkgbuild", "P", "show", "W", "web", "B", "build":
|
||||
case "Y", "yay", "G", "getpkgbuild", "P", "show":
|
||||
usage()
|
||||
return nil
|
||||
}
|
||||
|
||||
run.Logger.Println("\npacman operation specific options:")
|
||||
return run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx,
|
||||
cmdArgs, run.Cfg.Mode, settings.NoConfirm))
|
||||
return config.Runtime.CmdBuilder.Show(config.Runtime.CmdBuilder.BuildPacmanCmd(ctx,
|
||||
cmdArgs, config.Runtime.Mode, settings.NoConfirm))
|
||||
}
|
||||
|
||||
func handleVersion(logger *text.Logger) {
|
||||
logger.Printf("yay v%s - libalpm v%s\n", yayVersion, alpm.Version())
|
||||
func handleVersion() {
|
||||
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, cmdArgs *parser.Arguments, dbExecutor db.Executor) error {
|
||||
switch {
|
||||
case cmdArgs.ExistsArg("d", "defaultconfig"):
|
||||
tmpConfig := settings.DefaultConfig(yayVersion)
|
||||
run.Logger.Printf("%v", tmpConfig)
|
||||
fmt.Printf("%v", tmpConfig)
|
||||
|
||||
return nil
|
||||
case cmdArgs.ExistsArg("g", "currentconfig"):
|
||||
run.Logger.Printf("%v", run.Cfg)
|
||||
fmt.Printf("%v", config)
|
||||
|
||||
return nil
|
||||
case cmdArgs.ExistsArg("n", "numberupgrades"):
|
||||
filter, err := getFilter(cmdArgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return printNumberOfUpdates(ctx, dbExecutor, cmdArgs.ExistsDouble("u", "sysupgrade"), filter)
|
||||
case cmdArgs.ExistsArg("w", "news"):
|
||||
double := cmdArgs.ExistsDouble("w", "news")
|
||||
quiet := cmdArgs.ExistsArg("q", "quiet")
|
||||
|
||||
return news.PrintNewsFeed(ctx, run.HTTPClient, run.Logger,
|
||||
dbExecutor.LastBuildTime(), run.Cfg.BottomUp, double, quiet)
|
||||
return news.PrintNewsFeed(ctx, config.Runtime.HTTPClient, dbExecutor.LastBuildTime(), config.BottomUp, double, quiet)
|
||||
case cmdArgs.ExistsDouble("c", "complete"):
|
||||
return completion.Show(ctx, config.Runtime.HTTPClient, dbExecutor,
|
||||
config.AURURL, config.Runtime.CompletionPath, config.CompletionInterval, true)
|
||||
case cmdArgs.ExistsArg("c", "complete"):
|
||||
return completion.Show(ctx, run.HTTPClient, dbExecutor,
|
||||
run.Cfg.AURURL, run.Cfg.CompletionPath, run.Cfg.CompletionInterval, cmdArgs.ExistsDouble("c", "complete"))
|
||||
return completion.Show(ctx, config.Runtime.HTTPClient, dbExecutor,
|
||||
config.AURURL, config.Runtime.CompletionPath, config.CompletionInterval, false)
|
||||
case cmdArgs.ExistsArg("s", "stats"):
|
||||
return localStatistics(ctx, run, dbExecutor)
|
||||
return localStatistics(ctx, dbExecutor)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleYay(ctx context.Context, run *runtime.Runtime,
|
||||
cmdArgs *parser.Arguments, cmdBuilder exe.ICmdBuilder,
|
||||
dbExecutor db.Executor, queryBuilder query.Builder,
|
||||
) error {
|
||||
func handleYay(ctx context.Context, cmdArgs *parser.Arguments, dbExecutor db.Executor, queryBuilder query.Builder) error {
|
||||
switch {
|
||||
case cmdArgs.ExistsArg("gendb"):
|
||||
return createDevelDB(ctx, run, dbExecutor)
|
||||
return createDevelDB(ctx, config, dbExecutor)
|
||||
case cmdArgs.ExistsDouble("c"):
|
||||
return cleanDependencies(ctx, run.Cfg, cmdBuilder, cmdArgs, dbExecutor, true)
|
||||
return cleanDependencies(ctx, cmdArgs, dbExecutor, true)
|
||||
case cmdArgs.ExistsArg("c", "clean"):
|
||||
return cleanDependencies(ctx, run.Cfg, cmdBuilder, cmdArgs, dbExecutor, false)
|
||||
return cleanDependencies(ctx, cmdArgs, dbExecutor, false)
|
||||
case len(cmdArgs.Targets) > 0:
|
||||
return displayNumberMenu(ctx, run, cmdArgs.Targets, dbExecutor, queryBuilder, cmdArgs)
|
||||
return displayNumberMenu(ctx, cmdArgs.Targets, dbExecutor, queryBuilder, cmdArgs)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleWeb(ctx context.Context, run *runtime.Runtime, cmdArgs *parser.Arguments) error {
|
||||
func handleWeb(ctx context.Context, cmdArgs *parser.Arguments) error {
|
||||
switch {
|
||||
case cmdArgs.ExistsArg("v", "vote"):
|
||||
return handlePackageVote(ctx, cmdArgs.Targets, run.AURClient, run.Logger,
|
||||
run.VoteClient, true)
|
||||
return handlePackageVote(ctx, cmdArgs.Targets, config.Runtime.AURClient,
|
||||
config.Runtime.VoteClient, config.RequestSplitN, true)
|
||||
case cmdArgs.ExistsArg("u", "unvote"):
|
||||
return handlePackageVote(ctx, cmdArgs.Targets, run.AURClient, run.Logger,
|
||||
run.VoteClient, false)
|
||||
return handlePackageVote(ctx, cmdArgs.Targets, config.Runtime.AURClient,
|
||||
config.Runtime.VoteClient, config.RequestSplitN, false)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleGetpkgbuild(ctx context.Context, run *runtime.Runtime, cmdArgs *parser.Arguments, dbExecutor download.DBSearcher) error {
|
||||
func handleGetpkgbuild(ctx context.Context, cmdArgs *parser.Arguments, dbExecutor download.DBSearcher) error {
|
||||
if cmdArgs.ExistsArg("p", "print") {
|
||||
return printPkgbuilds(dbExecutor, run.AURClient,
|
||||
run.HTTPClient, run.Logger, cmdArgs.Targets, run.Cfg.Mode, run.Cfg.AURURL)
|
||||
return printPkgbuilds(dbExecutor, config.Runtime.HTTPClient, cmdArgs.Targets, config.Runtime.Mode, config.AURURL)
|
||||
}
|
||||
|
||||
return getPkgbuilds(ctx, dbExecutor, run.AURClient, run,
|
||||
cmdArgs.Targets, cmdArgs.ExistsArg("f", "force"))
|
||||
return getPkgbuilds(ctx, dbExecutor, config, cmdArgs.Targets, cmdArgs.ExistsArg("f", "force"))
|
||||
}
|
||||
|
||||
func handleUpgrade(ctx context.Context,
|
||||
run *runtime.Runtime, cmdArgs *parser.Arguments,
|
||||
) error {
|
||||
return run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx,
|
||||
cmdArgs, run.Cfg.Mode, settings.NoConfirm))
|
||||
}
|
||||
|
||||
// -B* options
|
||||
func handleBuild(ctx context.Context,
|
||||
run *runtime.Runtime, dbExecutor db.Executor, cmdArgs *parser.Arguments,
|
||||
) error {
|
||||
if cmdArgs.ExistsArg("i", "install") {
|
||||
return installLocalPKGBUILD(ctx, run, cmdArgs, dbExecutor)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleSync(ctx context.Context, run *runtime.Runtime, cmdArgs *parser.Arguments, dbExecutor db.Executor) error {
|
||||
func handleSync(ctx context.Context, cmdArgs *parser.Arguments, dbExecutor db.Executor) error {
|
||||
targets := cmdArgs.Targets
|
||||
|
||||
switch {
|
||||
case cmdArgs.ExistsArg("s", "search"):
|
||||
return syncSearch(ctx, targets, dbExecutor, run.QueryBuilder, !cmdArgs.ExistsArg("q", "quiet"))
|
||||
return syncSearch(ctx, targets, config.Runtime.AURClient, dbExecutor, config.Runtime.QueryBuilder, !cmdArgs.ExistsArg("q", "quiet"))
|
||||
case cmdArgs.ExistsArg("p", "print", "print-format"):
|
||||
return run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx,
|
||||
cmdArgs, run.Cfg.Mode, settings.NoConfirm))
|
||||
return config.Runtime.CmdBuilder.Show(config.Runtime.CmdBuilder.BuildPacmanCmd(ctx,
|
||||
cmdArgs, config.Runtime.Mode, settings.NoConfirm))
|
||||
case cmdArgs.ExistsArg("c", "clean"):
|
||||
return syncClean(ctx, run, cmdArgs, dbExecutor)
|
||||
return syncClean(ctx, cmdArgs, dbExecutor)
|
||||
case cmdArgs.ExistsArg("l", "list"):
|
||||
return syncList(ctx, run, run.HTTPClient, cmdArgs, dbExecutor)
|
||||
return syncList(ctx, config.Runtime.HTTPClient, cmdArgs, dbExecutor)
|
||||
case cmdArgs.ExistsArg("g", "groups"):
|
||||
return run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx,
|
||||
cmdArgs, run.Cfg.Mode, settings.NoConfirm))
|
||||
return config.Runtime.CmdBuilder.Show(config.Runtime.CmdBuilder.BuildPacmanCmd(ctx,
|
||||
cmdArgs, config.Runtime.Mode, settings.NoConfirm))
|
||||
case cmdArgs.ExistsArg("i", "info"):
|
||||
return syncInfo(ctx, run, cmdArgs, targets, dbExecutor)
|
||||
return syncInfo(ctx, cmdArgs, targets, dbExecutor)
|
||||
case cmdArgs.ExistsArg("u", "sysupgrade") || len(cmdArgs.Targets) > 0:
|
||||
return syncInstall(ctx, run, cmdArgs, dbExecutor)
|
||||
return install(ctx, cmdArgs, dbExecutor, false)
|
||||
case cmdArgs.ExistsArg("y", "refresh"):
|
||||
return run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx,
|
||||
cmdArgs, run.Cfg.Mode, settings.NoConfirm))
|
||||
return config.Runtime.CmdBuilder.Show(config.Runtime.CmdBuilder.BuildPacmanCmd(ctx,
|
||||
cmdArgs, config.Runtime.Mode, settings.NoConfirm))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleRemove(ctx context.Context, run *runtime.Runtime, cmdArgs *parser.Arguments, localCache vcs.Store) error {
|
||||
err := run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx,
|
||||
cmdArgs, run.Cfg.Mode, settings.NoConfirm))
|
||||
func handleRemove(ctx context.Context, cmdArgs *parser.Arguments, localCache *vcs.InfoStore) error {
|
||||
err := config.Runtime.CmdBuilder.Show(config.Runtime.CmdBuilder.BuildPacmanCmd(ctx,
|
||||
cmdArgs, config.Runtime.Mode, settings.NoConfirm))
|
||||
if err == nil {
|
||||
localCache.RemovePackages(cmdArgs.Targets)
|
||||
localCache.RemovePackage(cmdArgs.Targets)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// NumberMenu presents a CLI for selecting packages to install.
|
||||
func displayNumberMenu(ctx context.Context, run *runtime.Runtime, pkgS []string, dbExecutor db.Executor,
|
||||
func displayNumberMenu(ctx context.Context, pkgS []string, dbExecutor db.Executor,
|
||||
queryBuilder query.Builder, cmdArgs *parser.Arguments,
|
||||
) error {
|
||||
queryBuilder.Execute(ctx, dbExecutor, pkgS)
|
||||
queryBuilder.Execute(ctx, dbExecutor, config.Runtime.AURClient, pkgS)
|
||||
|
||||
if err := queryBuilder.Results(dbExecutor, query.NumberMenu); err != nil {
|
||||
if err := queryBuilder.Results(os.Stdout, dbExecutor, query.NumberMenu); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -389,9 +381,9 @@ func displayNumberMenu(ctx context.Context, run *runtime.Runtime, pkgS []string,
|
||||
return nil
|
||||
}
|
||||
|
||||
run.Logger.Infoln(gotext.Get("Packages to install (eg: 1 2 3, 1-3 or ^4)"))
|
||||
text.Infoln(gotext.Get("Packages to install (eg: 1 2 3, 1-3 or ^4)"))
|
||||
|
||||
numberBuf, err := run.Logger.GetInput("", false)
|
||||
numberBuf, err := text.GetInput("", false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -403,31 +395,29 @@ func displayNumberMenu(ctx context.Context, run *runtime.Runtime, pkgS []string,
|
||||
return err
|
||||
}
|
||||
|
||||
// modify the arguments to pass for the install
|
||||
cmdArgs.Targets = targets
|
||||
arguments := cmdArgs.CopyGlobal()
|
||||
arguments.AddTarget(targets...)
|
||||
|
||||
if len(cmdArgs.Targets) == 0 {
|
||||
run.Logger.Println(gotext.Get(" there is nothing to do"))
|
||||
if len(arguments.Targets) == 0 {
|
||||
fmt.Println(gotext.Get(" there is nothing to do"))
|
||||
return nil
|
||||
}
|
||||
|
||||
return syncInstall(ctx, run, cmdArgs, dbExecutor)
|
||||
return install(ctx, arguments, dbExecutor, true)
|
||||
}
|
||||
|
||||
func syncList(ctx context.Context, run *runtime.Runtime,
|
||||
httpClient *http.Client, cmdArgs *parser.Arguments, dbExecutor db.Executor,
|
||||
) error {
|
||||
func syncList(ctx context.Context, httpClient *http.Client, cmdArgs *parser.Arguments, dbExecutor db.Executor) error {
|
||||
aur := false
|
||||
|
||||
for i := len(cmdArgs.Targets) - 1; i >= 0; i-- {
|
||||
if cmdArgs.Targets[i] == "aur" && run.Cfg.Mode.AtLeastAUR() {
|
||||
if cmdArgs.Targets[i] == "aur" && config.Runtime.Mode.AtLeastAUR() {
|
||||
cmdArgs.Targets = append(cmdArgs.Targets[:i], cmdArgs.Targets[i+1:]...)
|
||||
aur = true
|
||||
}
|
||||
}
|
||||
|
||||
if run.Cfg.Mode.AtLeastAUR() && (len(cmdArgs.Targets) == 0 || aur) {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, run.Cfg.AURURL+"/packages.gz", http.NoBody)
|
||||
if config.Runtime.Mode.AtLeastAUR() && (len(cmdArgs.Targets) == 0 || aur) {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, config.AURURL+"/packages.gz", http.NoBody)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -445,22 +435,22 @@ func syncList(ctx context.Context, run *runtime.Runtime,
|
||||
for scanner.Scan() {
|
||||
name := scanner.Text()
|
||||
if cmdArgs.ExistsArg("q", "quiet") {
|
||||
run.Logger.Println(name)
|
||||
fmt.Println(name)
|
||||
} 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 {
|
||||
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) {
|
||||
return run.CmdBuilder.Show(run.CmdBuilder.BuildPacmanCmd(ctx,
|
||||
cmdArgs, run.Cfg.Mode, settings.NoConfirm))
|
||||
if config.Runtime.Mode.AtLeastRepo() && (len(cmdArgs.Targets) != 0 || !aur) {
|
||||
return config.Runtime.CmdBuilder.Show(config.Runtime.CmdBuilder.BuildPacmanCmd(ctx,
|
||||
cmdArgs, config.Runtime.Mode, settings.NoConfirm))
|
||||
}
|
||||
|
||||
return nil
|
||||
|
140
cmd_test.go
140
cmd_test.go
@ -1,140 +0,0 @@
|
||||
//go:build !integration
|
||||
// +build !integration
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/Jguer/aur"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/Jguer/yay/v12/pkg/db/mock"
|
||||
mockaur "github.com/Jguer/yay/v12/pkg/dep/mock"
|
||||
"github.com/Jguer/yay/v12/pkg/query"
|
||||
"github.com/Jguer/yay/v12/pkg/runtime"
|
||||
"github.com/Jguer/yay/v12/pkg/settings"
|
||||
"github.com/Jguer/yay/v12/pkg/settings/exe"
|
||||
"github.com/Jguer/yay/v12/pkg/settings/parser"
|
||||
"github.com/Jguer/yay/v12/pkg/text"
|
||||
"github.com/Jguer/yay/v12/pkg/vcs"
|
||||
)
|
||||
|
||||
func TestYogurtMenuAURDB(t *testing.T) {
|
||||
t.Skip("skip until Operation service is an interface")
|
||||
t.Parallel()
|
||||
makepkgBin := t.TempDir() + "/makepkg"
|
||||
pacmanBin := t.TempDir() + "/pacman"
|
||||
gitBin := t.TempDir() + "/git"
|
||||
f, err := os.OpenFile(makepkgBin, os.O_RDONLY|os.O_CREATE, 0o755)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, f.Close())
|
||||
|
||||
f, err = os.OpenFile(pacmanBin, os.O_RDONLY|os.O_CREATE, 0o755)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, f.Close())
|
||||
|
||||
f, err = os.OpenFile(gitBin, os.O_RDONLY|os.O_CREATE, 0o755)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, f.Close())
|
||||
|
||||
captureOverride := func(cmd *exec.Cmd) (stdout string, stderr string, err error) {
|
||||
return "", "", nil
|
||||
}
|
||||
|
||||
showOverride := func(cmd *exec.Cmd) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
mockRunner := &exe.MockRunner{CaptureFn: captureOverride, ShowFn: showOverride}
|
||||
cmdBuilder := &exe.CmdBuilder{
|
||||
MakepkgBin: makepkgBin,
|
||||
SudoBin: "su",
|
||||
PacmanBin: pacmanBin,
|
||||
PacmanConfigPath: "/etc/pacman.conf",
|
||||
GitBin: "git",
|
||||
Runner: mockRunner,
|
||||
SudoLoopEnabled: false,
|
||||
}
|
||||
|
||||
cmdArgs := parser.MakeArguments()
|
||||
cmdArgs.AddArg("Y")
|
||||
cmdArgs.AddTarget("yay")
|
||||
|
||||
db := &mock.DBExecutor{
|
||||
AlpmArchitecturesFn: func() ([]string, error) {
|
||||
return []string{"x86_64"}, nil
|
||||
},
|
||||
RefreshHandleFn: func() error {
|
||||
return nil
|
||||
},
|
||||
ReposFn: func() []string {
|
||||
return []string{"aur"}
|
||||
},
|
||||
SyncPackagesFn: func(s ...string) []mock.IPackage {
|
||||
return []mock.IPackage{
|
||||
&mock.Package{
|
||||
PName: "yay",
|
||||
PBase: "yay",
|
||||
PVersion: "10.0.0",
|
||||
PDB: mock.NewDB("aur"),
|
||||
},
|
||||
}
|
||||
},
|
||||
LocalPackageFn: func(s string) mock.IPackage {
|
||||
return nil
|
||||
},
|
||||
}
|
||||
aurCache := &mockaur.MockAUR{
|
||||
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
|
||||
return []aur.Pkg{
|
||||
{
|
||||
Name: "yay",
|
||||
PackageBase: "yay",
|
||||
Version: "10.0.0",
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
}
|
||||
logger := text.NewLogger(io.Discard, os.Stderr, strings.NewReader("1\n"), true, "test")
|
||||
|
||||
run := &runtime.Runtime{
|
||||
Cfg: &settings.Configuration{
|
||||
RemoveMake: "no",
|
||||
},
|
||||
Logger: logger,
|
||||
CmdBuilder: cmdBuilder,
|
||||
VCSStore: &vcs.Mock{},
|
||||
QueryBuilder: query.NewSourceQueryBuilder(aurCache, logger, "votes", parser.ModeAny, "name",
|
||||
true, false, true),
|
||||
AURClient: aurCache,
|
||||
}
|
||||
err = handleCmd(context.Background(), run, cmdArgs, db)
|
||||
require.NoError(t, err)
|
||||
|
||||
wantCapture := []string{}
|
||||
wantShow := []string{
|
||||
"pacman -S -y --config /etc/pacman.conf --",
|
||||
"pacman -S -y -u --config /etc/pacman.conf --",
|
||||
}
|
||||
|
||||
require.Len(t, mockRunner.ShowCalls, len(wantShow))
|
||||
require.Len(t, mockRunner.CaptureCalls, len(wantCapture))
|
||||
|
||||
for i, call := range mockRunner.ShowCalls {
|
||||
show := call.Args[0].(*exec.Cmd).String()
|
||||
show = strings.ReplaceAll(show, makepkgBin, "makepkg")
|
||||
show = strings.ReplaceAll(show, pacmanBin, "pacman")
|
||||
show = strings.ReplaceAll(show, gitBin, "pacman")
|
||||
|
||||
// options are in a different order on different systems and on CI root user is used
|
||||
assert.Subset(t, strings.Split(show, " "), strings.Split(wantShow[i], " "), fmt.Sprintf("%d - %s", i, show))
|
||||
}
|
||||
}
|
@ -61,22 +61,22 @@ _yay() {
|
||||
search unrequired upgrades' 'c e g i k l m n o p s t u')
|
||||
remove=('cascade dbonly nodeps assume-installed nosave print recursive unneeded' 'c n p s u')
|
||||
sync=('asdeps asexplicit clean dbonly downloadonly overwrite groups ignore ignoregroup
|
||||
info list needed nodeps assume-installed print refresh recursive search sysupgrade aur repo'
|
||||
'c g i l p s u w y a N')
|
||||
info list needed nodeps assume-installed print refresh recursive search sysupgrade'
|
||||
'c g i l p s u w y')
|
||||
upgrade=('asdeps asexplicit overwrite needed nodeps assume-installed print recursive' 'p')
|
||||
core=('database files help query remove sync upgrade version' 'D F Q R S U V h')
|
||||
|
||||
##yay stuff
|
||||
common=('arch cachedir color config confirm dbpath debug gpgdir help hookdir logfile
|
||||
noconfirm noprogressbar noscriptlet quiet root verbose
|
||||
makepkg pacman git gpg gpgflags config requestsplitn sudoloop
|
||||
makepkg pacman git gpg gpgflags config requestsplitn sudoloop nosudoloop
|
||||
redownload noredownload redownloadall rebuild rebuildall rebuildtree norebuild sortby
|
||||
singlelineresults doublelineresults answerclean answerdiff answeredit answerupgrade noanswerclean noanswerdiff
|
||||
noansweredit noanswerupgrade cleanmenu diffmenu editmenu cleanafter keepsrc
|
||||
provides pgpfetch
|
||||
useask combinedupgrade aur repo makepkgconf
|
||||
nomakepkgconf askremovemake askyesremovemake removemake noremovemake completioninterval aururl aurrpcurl
|
||||
searchby batchinstall'
|
||||
noansweredit noanswerupgrade cleanmenu diffmenu editmenu upgrademenu cleanafter nocleanafter
|
||||
nocleanmenu nodiffmenu noupgrademenu provides noprovides pgpfetch nopgpfetch
|
||||
useask nouseask combinedupgrade nocombinedupgrade aur repo makepkgconf
|
||||
nomakepkgconf askremovemake removemake noremovemake completioninterval aururl aurrpcurl
|
||||
searchby batchinstall nobatchinstall'
|
||||
'b d h q r v')
|
||||
yays=('clean gendb' 'c')
|
||||
show=('complete defaultconfig currentconfig stats news' 'c d g s w')
|
||||
|
@ -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"
|
||||
|
||||
# New options
|
||||
complete -c $progname -n "not $noopt" -s a -l aur -d 'Assume targets are from the AUR' -f
|
||||
complete -c $progname -n "not $noopt" -s N -l repo -d 'Assume targets are from the repositories' -f
|
||||
complete -c $progname -n "not $noopt" -l repo -d 'Assume targets are from the AUR' -f
|
||||
complete -c $progname -n "not $noopt" -s a -l aur -d 'Assume targets are from the repositories' -f
|
||||
|
||||
# Yay options
|
||||
complete -c $progname -n "$yayspecific" -s c -l clean -d 'Remove unneeded dependencies' -f
|
||||
@ -216,8 +216,12 @@ complete -c $progname -n "not $noopt" -l noanswerupgrade -d 'Unset the answer fo
|
||||
complete -c $progname -n "not $noopt" -l cleanmenu -d 'Give the option to clean build PKGBUILDS' -f
|
||||
complete -c $progname -n "not $noopt" -l diffmenu -d 'Give the option to show diffs for build files' -f
|
||||
complete -c $progname -n "not $noopt" -l editmenu -d 'Give the option to edit/view PKGBUILDS' -f
|
||||
complete -c $progname -n "not $noopt" -l upgrademenu -d 'Show a detailed list of updates with the option to skip any' -f
|
||||
complete -c $progname -n "not $noopt" -l nocleanmenu -d 'Do not clean build PKGBUILDS' -f
|
||||
complete -c $progname -n "not $noopt" -l nodiffmenu -d 'Do not show diffs for build files' -f
|
||||
complete -c $progname -n "not $noopt" -l noeditmenu -d 'Do not edit/view PKGBUILDS' -f
|
||||
complete -c $progname -n "not $noopt" -l noupgrademenu -d 'Do not show the upgrade menu' -f
|
||||
complete -c $progname -n "not $noopt" -l askremovemake -d 'Ask to remove make deps after install' -f
|
||||
complete -c $progname -n "not $noopt" -l askyesremovemake -d 'Ask to remove make deps after install(with "Y" as default)' -f
|
||||
complete -c $progname -n "not $noopt" -l removemake -d 'Remove make deps after install' -f
|
||||
complete -c $progname -n "not $noopt" -l noremovemake -d 'Do not remove make deps after install' -f
|
||||
complete -c $progname -n "not $noopt" -l topdown -d 'Shows repository packages first and then aur' -f
|
||||
@ -225,17 +229,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 doublelineresults -d 'List each search result on two lines, like pacman' -f
|
||||
complete -c $progname -n "not $noopt" -l devel -d 'Check -git/-svn/-hg development version' -f
|
||||
complete -c $progname -n "not $noopt" -l nodevel -d 'Disable development version checking' -f
|
||||
complete -c $progname -n "not $noopt" -l cleanafter -d 'Clean package sources after successful build' -f
|
||||
complete -c $progname -n "not $noopt" -l 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 notimeupdate -d 'Check only package version change' -f
|
||||
complete -c $progname -n "not $noopt" -l redownload -d 'Redownload PKGBUILD of package even if up-to-date' -f
|
||||
complete -c $progname -n "not $noopt" -l redownloadall -d 'Redownload PKGBUILD of package and deps even if up-to-date' -f
|
||||
complete -c $progname -n "not $noopt" -l noredownload -d 'Do not redownload up-to-date PKGBUILDs' -f
|
||||
complete -c $progname -n "not $noopt" -l provides -d 'Look for matching providers when searching for packages' -f
|
||||
complete -c $progname -n "not $noopt" -l noprovides -d 'Just look for packages by pkgname' -f
|
||||
complete -c $progname -n "not $noopt" -l pgpfetch -d 'Prompt to import PGP keys from PKGBUILDs' -f
|
||||
complete -c $progname -n "not $noopt" -l nopgpfetch -d 'Do not prompt to import PGP keys' -f
|
||||
complete -c $progname -n "not $noopt" -l useask -d 'Automatically resolve conflicts using pacmans ask flag' -f
|
||||
complete -c $progname -n "not $noopt" -l nouseask -d 'Confirm conflicts manually during the install' -f
|
||||
complete -c $progname -n "not $noopt" -l combinedupgrade -d 'Refresh then perform the repo and AUR upgrade together' -f
|
||||
complete -c $progname -n "not $noopt" -l nocombinedupgrade -d 'Perform the repo upgrade and AUR upgrade separately' -f
|
||||
complete -c $progname -n "not $noopt" -l batchinstall -d 'Build multiple AUR packages then install them together' -f
|
||||
complete -c $progname -n "not $noopt" -l nobatchinstall -d 'Build and install each AUR package one by one' -f
|
||||
complete -c $progname -n "not $noopt" -l rebuild -d 'Always build target packages' -f
|
||||
complete -c $progname -n "not $noopt" -l rebuildall -d 'Always build all AUR packages' -f
|
||||
complete -c $progname -n "not $noopt" -l rebuildtree -d 'Always build all AUR packages even if installed' -f
|
||||
@ -243,3 +254,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 gpgflags -d 'Pass the following options to gpg' -f
|
||||
complete -c $progname -n "not $noopt" -l sudoloop -d 'Loop sudo calls in the background to avoid timeout' -f
|
||||
complete -c $progname -n "not $noopt" -l nosudoloop -d 'Do not loop sudo calls in the background' -f
|
||||
|
@ -1,5 +1,5 @@
|
||||
#compdef yay
|
||||
# vim:tabstop=2 shiftwidth=2 filetype=zsh
|
||||
# vim:fdm=marker foldlevel=0 tabstop=2 shiftwidth=2 filetype=zsh
|
||||
|
||||
typeset -A opt_args
|
||||
setopt extendedglob
|
||||
@ -23,7 +23,7 @@ _pacman_opts_commands=(
|
||||
|
||||
# options for passing to _arguments: options common to all commands
|
||||
_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]'
|
||||
'--aururl[Set an alternative AUR URL]:url'
|
||||
'--aurrpcurl[Set an alternative URL for the AUR /rpc endpoint]:url'
|
||||
@ -70,8 +70,12 @@ _pacman_opts_common=(
|
||||
'--cleanmenu[Give the option to clean build PKGBUILDS]'
|
||||
'--diffmenu[Give the option to show diffs for build files]'
|
||||
'--editmenu[Give the option to edit/view PKGBUILDS]'
|
||||
'--upgrademenu[Show a detailed list of updates with the option to skip any]'
|
||||
"--nocleanmenu[Don't clean build PKGBUILDS]"
|
||||
"--nodiffmenu[Don't show diffs for build files]"
|
||||
"--noeditmenu[Don't edit/view PKGBUILDS]"
|
||||
"--noupgrademenu[Don't show the upgrade menu]"
|
||||
"--askremovemake[Ask to remove makedepends after install]"
|
||||
"--askyesremovemake[Ask to remove makedepends after install(with "Y" as default)]"
|
||||
"--removemake[Remove makedepends after install]"
|
||||
"--noremovemake[Don't remove makedepends after install]"
|
||||
|
||||
@ -80,26 +84,34 @@ _pacman_opts_common=(
|
||||
'--singlelineresults[List each search result on its own line]'
|
||||
'--doublelineresults[List each search result on two lines, like pacman]'
|
||||
'--devel[Check -git/-svn/-hg development version]'
|
||||
'--nodevel[Disable development version checking]'
|
||||
'--cleanafter[Clean package sources after successful build]'
|
||||
'--keepsrc[Keep pkg/ and src/ after building packages]'
|
||||
'--nocleanafter[Disable package sources cleaning after successful build]'
|
||||
'--timeupdate[Check packages modification date and version]'
|
||||
'--notimeupdate[Check only package version change]'
|
||||
'--redownload[Always download pkgbuilds of targets]'
|
||||
'--redownloadall[Always download pkgbuilds of all AUR packages]'
|
||||
'--noredownload[Skip pkgbuild download if in cache and up to date]'
|
||||
'--rebuild[Always build target packages]'
|
||||
'--rebuildall[Always build all AUR packages]'
|
||||
'--provides[Look for matching providers when searching for packages]'
|
||||
'--noprovides[Just look for packages by pkgname]'
|
||||
'--pgpfetch[Prompt to import PGP keys from PKGBUILDs]'
|
||||
"--nopgpfetch[Don't prompt to import PGP keys]"
|
||||
"--useask[Automatically resolve conflicts using pacman's ask flag]"
|
||||
'--nouseask[Confirm conflicts manually during the install]'
|
||||
'--combinedupgrade[Refresh then perform the repo and AUR upgrade together]'
|
||||
'--nocombinedupgrade[Perform the repo upgrade and AUR upgrade separately]'
|
||||
'--rebuildtree[Always build all AUR packages even if installed]'
|
||||
'--norebuild[Skip package build if in cache and up to date]'
|
||||
'--mflags[Pass arguments to makepkg]:mflags'
|
||||
'--gpgflags[Pass arguments to gpg]:gpgflags'
|
||||
'--sudoloop[Loop sudo calls in the background to avoid timeout]'
|
||||
'--nosudoloop[Do not loop sudo calls in the background]'
|
||||
'--searchby[Search for packages using a specified field]'
|
||||
'--sortby[Sort AUR results by a specific field during search]'
|
||||
'--batchinstall[Build multiple AUR packages then install them together]'
|
||||
'--nobatchinstall[Build and install each AUR package one by one]'
|
||||
)
|
||||
|
||||
# options for passing to _arguments: options for --upgrade commands
|
||||
@ -500,17 +512,18 @@ _pacman_zsh_comp() {
|
||||
"$_pacman_opts_query_modifiers[@]" \
|
||||
'*:package file:_files -g "*.pkg.tar*~*.sig(.,@)"'
|
||||
;;
|
||||
T*)
|
||||
_pacman_action_deptest
|
||||
;;
|
||||
Q*)
|
||||
_pacman_action_query
|
||||
;;
|
||||
P*)
|
||||
_arguments -s : \
|
||||
'-P' \
|
||||
"$_pacman_opts_print_modifiers[@]"
|
||||
;;
|
||||
W*)
|
||||
_arguments -s : \
|
||||
'-W' \
|
||||
"$_pacman_opts_web_modifiers[@]"
|
||||
;;
|
||||
R*)
|
||||
@ -543,7 +556,10 @@ _pacman_zsh_comp() {
|
||||
_pacman_action_sync
|
||||
;;
|
||||
T*)
|
||||
_pacman_action_deptest
|
||||
_arguments -s : \
|
||||
'-T' \
|
||||
"$_pacman_opts_common[@]" \
|
||||
":packages:_pacman_all_packages"
|
||||
;;
|
||||
U*)
|
||||
_pacman_action_upgrade
|
||||
@ -553,12 +569,10 @@ _pacman_zsh_comp() {
|
||||
;;
|
||||
Y*)
|
||||
_arguments -s : \
|
||||
'-Y' \
|
||||
"$_pacman_opts_yay_modifiers[@]"
|
||||
;;
|
||||
G*)
|
||||
_arguments -s : \
|
||||
'-G' \
|
||||
"$_pacman_opts_getpkgbuild_modifiers[@]"
|
||||
;;
|
||||
|
||||
|
140
doc/yay.8
140
doc/yay.8
@ -1,4 +1,4 @@
|
||||
.TH "YAY" "8" "2019\-10\-21" "Yay v12.0+" "Yay Manual"
|
||||
.TH "YAY" "8" "2019\-10\-21" "Yay v9.4+" "Yay Manual"
|
||||
.nh
|
||||
.ad l
|
||||
.SH NAME
|
||||
@ -19,15 +19,10 @@ This manpage only covers options unique to Yay. For other options see
|
||||
\fBpacman(8)\fR.
|
||||
|
||||
.SH YAY OPERATIONS
|
||||
|
||||
.TP
|
||||
.B \-Y, \-\-yay
|
||||
Perform yay specific operations. This is the default if no other operation is
|
||||
selected and targets are defined.
|
||||
|
||||
.TP
|
||||
.B \-B, \-\-build
|
||||
Build a PKGBUILD in a given directory.
|
||||
selected.
|
||||
|
||||
.TP
|
||||
.B \-P, \-\-show
|
||||
@ -37,14 +32,10 @@ Perform yay specific print operations.
|
||||
.B \-G, \-\-getpkgbuild
|
||||
Downloads PKGBUILD from ABS or AUR. The ABS can only be used for Arch Linux repositories.
|
||||
|
||||
.TP
|
||||
.B \-W, \-\-web
|
||||
Web related operations such as voting for AUR packages.
|
||||
|
||||
.RE
|
||||
If no operation is specified 'yay \-Syu' will be performed
|
||||
If no arguments are provided 'yay \-Syu' will be performed.
|
||||
|
||||
If no operation is specified and targets are provided \-Y will be assumed
|
||||
If no operation is selected \-Y will be assumed.
|
||||
|
||||
.SH EXTENDED PACMAN OPERATIONS
|
||||
.TP
|
||||
@ -63,7 +54,7 @@ Yay will also remove cached data about devel packages.
|
||||
|
||||
.SH NEW OPTIONS
|
||||
.TP
|
||||
.B \-N, \-\-repo
|
||||
.B \-\-repo
|
||||
Assume all targets are from the repositories. Additionally Actions such as
|
||||
sysupgrade will only act on repository packages.
|
||||
|
||||
@ -82,10 +73,6 @@ packages.
|
||||
Displays a list of packages matching the search terms and prompts the user on
|
||||
which packages to install (yogurt mode).
|
||||
|
||||
The first search term is used to query the different sources and
|
||||
the following search terms are used to narrow the search results
|
||||
through exact matching.
|
||||
|
||||
.TP
|
||||
.B \-\-gendb
|
||||
Generate development package database. Tracks the latest commit for each
|
||||
@ -97,16 +84,16 @@ used when migrating to Yay from another AUR helper.
|
||||
.B \-c, \-\-clean
|
||||
Remove unneeded dependencies.
|
||||
|
||||
.TP
|
||||
.B \-cc
|
||||
Remove unneeded dependencies, including packages optionally required by any other package.
|
||||
|
||||
.SH SHOW OPTIONS (APPLY TO \-P AND \-\-show)
|
||||
.SH SHOW OPTIONS (APPLY TO \-P AND \-\-SHOW)
|
||||
.TP
|
||||
.B \-c, \-\-complete
|
||||
Print a list of all AUR and repo packages. This allows shell completion
|
||||
and is not intended to be used directly by the user.
|
||||
|
||||
.TP
|
||||
.B \-f, \-\-fish
|
||||
During complete adjust the output for the fish shell.
|
||||
|
||||
.TP
|
||||
.B \-d, \-\-defaultconfig
|
||||
Print default yay configuration.
|
||||
@ -115,12 +102,20 @@ Print default yay configuration.
|
||||
.B \-g, \-\-currentconfig
|
||||
Print current yay configuration.
|
||||
|
||||
.TP
|
||||
.B \-n, \-\-numberupgrades
|
||||
Deprecated, use \fByay -Qu\fR and \fBwc -l\fR instead\%.
|
||||
|
||||
.TP
|
||||
.B \-s, \-\-stats
|
||||
Displays information about installed packages and system health. If there are
|
||||
orphaned, or out\-of\-date packages, or packages that no longer exist on the
|
||||
AUR; warnings will be displayed.
|
||||
|
||||
.TP
|
||||
.B \-u, \-\-upgrades
|
||||
Deprecated, use \fByay -Qu\fR instead\%.
|
||||
|
||||
.TP
|
||||
.B \-w, \-\-news
|
||||
Print new news from the Archlinux homepage. News is considered new if it is
|
||||
@ -131,12 +126,7 @@ available news.
|
||||
.B \-q, \-\-quiet
|
||||
Only show titles when printing news.
|
||||
|
||||
.SH BUILD OPTIONS (APPLY TO \-B AND \-\-build)
|
||||
.TP
|
||||
.B \-i, \-\-install
|
||||
Build and install a PKGBUILD in a given directory
|
||||
|
||||
.SH GETPKGBUILD OPTIONS (APPLY TO \-G AND \-\-getpkgbuild)
|
||||
.SH GETPKGBUILD OPTIONS (APPLY TO \-G AND \-\-GETPKGBUILD)
|
||||
.TP
|
||||
.B \-f, \-\-force
|
||||
Force download for ABS packages that already exist in the current directory. This
|
||||
@ -146,7 +136,7 @@ ensures directories are not accidentally overwritten.
|
||||
.B \-p, \-\-print
|
||||
Prints the PKGBUILD of the given packages to stdout.
|
||||
|
||||
.SH WEB OPTIONS (APPLY TO \-W AND \-\-web)
|
||||
.SH WEB OPTIONS (APPLY TO \-W AND \-\-WEB)
|
||||
|
||||
.TP
|
||||
Web related operations such as voting for AUR packages.
|
||||
@ -182,8 +172,8 @@ the AUR cache when deciding if Yay should skip builds.
|
||||
|
||||
.TP
|
||||
.B \-\-editor <command>
|
||||
Editor to use when editing PKGBUILDs. If this is not set the \fBVISUAL\fR
|
||||
environment variable will be checked, followed by \fBEDITOR\fR. If none of
|
||||
Editor to use when editing PKGBUILDs. If this is not set the \fBEDITOR\fR
|
||||
environment variable will be checked, followed by \fBVISUAL\fR. If none of
|
||||
these are set Yay will prompt the user for an editor.
|
||||
|
||||
.TP
|
||||
@ -249,7 +239,7 @@ cache to never be refreshed.
|
||||
Sort AUR results by a specific field during search.
|
||||
|
||||
.TP
|
||||
.B \-\-searchby <name|name-desc|maintainer|depends|checkdepends|makedepends|optdepends|provides|conflicts|replaces|groups|keywords|comaintainers>
|
||||
.B \-\-searchby <name|name-desc|maintainer|depends|checkdepends|makedepends|optdepends>
|
||||
Search for AUR packages by querying the specified field.
|
||||
|
||||
.TP
|
||||
@ -297,9 +287,6 @@ Unset the answer for the upgrade menu.
|
||||
Show the clean menu. This menu gives you the chance to fully delete the
|
||||
downloaded build files from Yay's cache before redownloading a fresh copy.
|
||||
|
||||
If 'cleanmenu' is enabled in the configuration file, you can temporarily disable it by
|
||||
using '--cleanmenu=false' on the command line
|
||||
|
||||
.TP
|
||||
.B \-\-diffmenu
|
||||
Show the diff menu. This menu gives you the option to view diffs from
|
||||
@ -318,12 +305,35 @@ before building.
|
||||
recommended to edit pkgbuild variables unless you know what you are doing.
|
||||
|
||||
.TP
|
||||
.B \-\-askremovemake
|
||||
Ask to remove makedepends after installing packages.
|
||||
.B \-\-upgrademenu
|
||||
Show a detailed list of updates in a similar format to VerbosePkgLists.
|
||||
Upgrades can also be skipped using numbers, number ranges or repo names.
|
||||
Additionally ^ can be used to invert the selection.
|
||||
|
||||
\fBWarning\fR: It is not recommended to skip updates from the repositories as
|
||||
this can lead to partial upgrades. This feature is intended to easily skip AUR
|
||||
updates on the fly that may be broken or have a long compile time. Ultimately
|
||||
it is up to the user what upgrades they skip.
|
||||
|
||||
.TP
|
||||
.B \-\-askyesremovemake
|
||||
Ask to remove makedepends after installing packages(with "Y" as default).
|
||||
.B \-\-nocleanmenu
|
||||
Do not show the clean menu.
|
||||
|
||||
.TP
|
||||
.B \-\-nodiffmenu
|
||||
Do not show the diff menu.
|
||||
|
||||
.TP
|
||||
.B \-\-noeditmenu
|
||||
Do not show the edit menu.
|
||||
|
||||
.TP
|
||||
.B \-\-noupgrademenu
|
||||
Do not show the upgrade menu.
|
||||
|
||||
.TP
|
||||
.B \-\-askremovemake
|
||||
Ask to remove makedepends after installing packages.
|
||||
|
||||
.TP
|
||||
.B \-\-removemake
|
||||
@ -363,8 +373,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
|
||||
a list of packages into yay (see \fBexamples\fR).
|
||||
|
||||
If 'devel' is enabled in the configuration file, you can temporarily disable it by
|
||||
using '--devel=false' on the command line
|
||||
.TP
|
||||
.B \-\-nodevel
|
||||
Do not check for development packages updates during sysupgrade.
|
||||
|
||||
.TP
|
||||
.B \-\-cleanafter
|
||||
@ -375,18 +386,26 @@ This allows VCS packages to easily pull an update
|
||||
instead of having to reclone the entire repo.
|
||||
|
||||
.TP
|
||||
.B \-\-keepsrc
|
||||
Keep pkg/ and src/ after building packages
|
||||
.B \-\-nocleanafter
|
||||
Do not remove package sources after successful Install.
|
||||
|
||||
.TP
|
||||
.B \-\-timeupdate
|
||||
During sysupgrade also compare the build time of installed packages against
|
||||
the last modification time of each package's AUR page.
|
||||
|
||||
.TP
|
||||
.B \-\-notimeupdate
|
||||
Do not consider build times during sysupgrade.
|
||||
|
||||
.TP
|
||||
.B \-\-separatesources
|
||||
Separate query results by source, AUR and sync
|
||||
|
||||
.TP
|
||||
.B \-\-noseparatesources
|
||||
Do not separate query results by source for searching
|
||||
|
||||
.TP
|
||||
.B \-\-redownload
|
||||
Always download pkgbuilds of targets even when a copy is available in cache.
|
||||
@ -407,11 +426,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
|
||||
increases dependency resolve time although this should not be noticeable.
|
||||
|
||||
.TP
|
||||
.B \-\-noprovides
|
||||
Do not look for matching providers when searching for AUR packages.
|
||||
Yay will never show its provider menu but Pacman will still show its
|
||||
provider menu for repo packages.
|
||||
|
||||
.TP
|
||||
.B \-\-pgpfetch
|
||||
Prompt to import unknown PGP keys from the \fBvalidpgpkeys\fR field of each
|
||||
PKGBUILD.
|
||||
|
||||
.TP
|
||||
.B \-\-nopgpfetch
|
||||
Do not prompt to import unknown PGP keys. This is likely to cause a build
|
||||
failure unless using options such as \fB\-\-skippgpcheck\fR or a customized
|
||||
gpg config\%.
|
||||
|
||||
.TP
|
||||
.B \-\-useask
|
||||
Use pacman's --ask flag to automatically confirm package conflicts. Yay lists
|
||||
@ -419,6 +450,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.
|
||||
However, this is very unlikely.
|
||||
|
||||
.TP
|
||||
.B \-\-nouseask
|
||||
Manually resolve package conflicts during the install. Packages which do not
|
||||
conflict will not need to be confined manually.
|
||||
|
||||
.TP
|
||||
.B \-\-combinedupgrade
|
||||
During sysupgrade, Yay will first perform a refresh, then show
|
||||
@ -430,6 +466,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
|
||||
a sysupgrade through pacman directly.
|
||||
|
||||
.TP
|
||||
.B \-\-nocombinedupgrade
|
||||
During sysupgrade, Pacman \-Syu will be called, then the AUR upgrade will
|
||||
start. This means the upgrade menu and pkgbuild review will be performed
|
||||
after the sysupgrade has finished.
|
||||
|
||||
.TP
|
||||
.B \-\-batchinstall
|
||||
When building and installing AUR packages instead of installing each package
|
||||
@ -437,6 +479,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
|
||||
another package, install all the packages in the install queue.
|
||||
|
||||
.TP
|
||||
.B \-\-nobatchinstall
|
||||
Always install AUR packages immediately after building them.
|
||||
|
||||
.TP
|
||||
.B \-\-rebuild
|
||||
Always build target packages even when a copy is available in cache.
|
||||
@ -490,6 +536,10 @@ separated list that is quoted by the shell.
|
||||
Loop sudo calls in the background to prevent sudo from timing out during long
|
||||
builds.
|
||||
|
||||
.TP
|
||||
.B \-\-nosudoloop
|
||||
Do not loop sudo calls in the background.
|
||||
|
||||
.SH EXAMPLES
|
||||
.TP
|
||||
yay \fIfoo\fR
|
||||
@ -594,6 +644,6 @@ See the arch wiki at https://wiki.archlinux.org/index.php/Arch_User_Repository f
|
||||
Please report bugs to our GitHub page https://github.com/Jguer/yay
|
||||
|
||||
.SH AUTHORS
|
||||
Jguer <joguer@proton.me>
|
||||
Jguer <joaogg3@gmail.com>
|
||||
.br
|
||||
Morgan <morganamilo@archlinux.org>
|
||||
|
@ -1,9 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/leonelquinteros/gotext"
|
||||
)
|
||||
|
||||
var ErrPackagesNotFound = errors.New(gotext.Get("could not find all required packages"))
|
41
get.go
41
get.go
@ -7,27 +7,27 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/Jguer/aur"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
|
||||
"github.com/Jguer/yay/v12/pkg/download"
|
||||
"github.com/Jguer/yay/v12/pkg/runtime"
|
||||
"github.com/Jguer/yay/v12/pkg/settings/parser"
|
||||
"github.com/Jguer/yay/v12/pkg/text"
|
||||
"github.com/Jguer/yay/v11/pkg/download"
|
||||
"github.com/Jguer/yay/v11/pkg/settings"
|
||||
"github.com/Jguer/yay/v11/pkg/settings/parser"
|
||||
"github.com/Jguer/yay/v11/pkg/text"
|
||||
)
|
||||
|
||||
// yay -Gp.
|
||||
func printPkgbuilds(dbExecutor download.DBSearcher, aurClient aur.QueryClient,
|
||||
httpClient *http.Client, logger *text.Logger, targets []string,
|
||||
mode parser.TargetMode, aurURL string,
|
||||
) error {
|
||||
pkgbuilds, err := download.PKGBUILDs(dbExecutor, aurClient, httpClient, logger, targets, aurURL, mode)
|
||||
func printPkgbuilds(dbExecutor download.DBSearcher, httpClient *http.Client, targets []string,
|
||||
mode parser.TargetMode, aurURL string) error {
|
||||
pkgbuilds, err := download.PKGBUILDs(dbExecutor, httpClient, targets, aurURL, mode)
|
||||
if err != nil {
|
||||
logger.Errorln(err)
|
||||
text.Errorln(err)
|
||||
}
|
||||
|
||||
for target, pkgbuild := range pkgbuilds {
|
||||
logger.Printf("\n\n# %s\n\n%s", target, string(pkgbuild))
|
||||
if len(pkgbuilds) != 0 {
|
||||
for target, pkgbuild := range pkgbuilds {
|
||||
fmt.Printf("\n\n# %s\n\n", target)
|
||||
fmt.Print(string(pkgbuild))
|
||||
}
|
||||
}
|
||||
|
||||
if len(pkgbuilds) != len(targets) {
|
||||
@ -39,7 +39,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("")
|
||||
}
|
||||
@ -48,18 +48,17 @@ func printPkgbuilds(dbExecutor download.DBSearcher, aurClient aur.QueryClient,
|
||||
}
|
||||
|
||||
// yay -G.
|
||||
func getPkgbuilds(ctx context.Context, dbExecutor download.DBSearcher, aurClient aur.QueryClient,
|
||||
run *runtime.Runtime, targets []string, force bool,
|
||||
) error {
|
||||
func getPkgbuilds(ctx context.Context, dbExecutor download.DBSearcher,
|
||||
config *settings.Configuration, targets []string, force bool) error {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cloned, errD := download.PKGBUILDRepos(ctx, dbExecutor, aurClient,
|
||||
run.CmdBuilder, run.Logger, targets, run.Cfg.Mode, run.Cfg.AURURL, wd, force)
|
||||
cloned, errD := download.PKGBUILDRepos(ctx, dbExecutor,
|
||||
config.Runtime.CmdBuilder, targets, config.Runtime.Mode, config.AURURL, wd, force)
|
||||
if errD != nil {
|
||||
run.Logger.Errorln(errD)
|
||||
text.Errorln(errD)
|
||||
}
|
||||
|
||||
if len(targets) != len(cloned) {
|
||||
@ -71,7 +70,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("")
|
||||
}
|
||||
|
10
globals.go
Normal file
10
globals.go
Normal file
@ -0,0 +1,10 @@
|
||||
package main
|
||||
|
||||
import "github.com/Jguer/yay/v11/pkg/settings"
|
||||
|
||||
var (
|
||||
yayVersion = "11.3.0" // To be set by compiler.
|
||||
localePath = "/usr/share/locale" // To be set by compiler.
|
||||
)
|
||||
|
||||
var config *settings.Configuration // YayConf holds the current config values for yay.
|
29
go.mod
29
go.mod
@ -1,35 +1,26 @@
|
||||
module github.com/Jguer/yay/v12
|
||||
module github.com/Jguer/yay/v11
|
||||
|
||||
require (
|
||||
github.com/Jguer/aur v1.2.3
|
||||
github.com/Jguer/go-alpm/v2 v2.2.2
|
||||
github.com/Jguer/aur v1.0.1
|
||||
github.com/Jguer/go-alpm/v2 v2.1.2
|
||||
github.com/Jguer/votar v1.0.0
|
||||
github.com/Morganamilo/go-pacmanconf v0.0.0-20210502114700-cff030e927a5
|
||||
github.com/Morganamilo/go-srcinfo v1.0.0
|
||||
github.com/adrg/strutil v0.3.1
|
||||
github.com/bradleyjkemp/cupaloy v2.3.0+incompatible
|
||||
github.com/deckarep/golang-set/v2 v2.8.0
|
||||
github.com/hashicorp/go-multierror v1.1.1
|
||||
github.com/leonelquinteros/gotext v1.7.2
|
||||
github.com/stretchr/testify v1.10.0
|
||||
golang.org/x/net v0.41.0
|
||||
golang.org/x/sys v0.33.0
|
||||
golang.org/x/term v0.32.0
|
||||
github.com/leonelquinteros/gotext v1.5.0
|
||||
github.com/stretchr/testify v1.8.0
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab
|
||||
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
gopkg.in/h2non/gock.v1 v1.1.2
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/adrg/strutil v0.3.0
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/itchyny/gojq v0.12.17 // indirect
|
||||
github.com/itchyny/timefmt-go v0.1.6 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/ohler55/ojg v1.26.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
go 1.23.5
|
||||
|
||||
toolchain go1.24.0
|
||||
go 1.17
|
||||
|
63
go.sum
63
go.sum
@ -1,15 +1,15 @@
|
||||
github.com/Jguer/aur v1.2.3 h1:D+OGgLxnAnZnw88DsRvnRQsn0Poxsy9ng7pBcsA0krM=
|
||||
github.com/Jguer/aur v1.2.3/go.mod h1:Dahvb6L1yr0rR7svyYSDwaRJoQMeyvJblwJ3QH/7CUs=
|
||||
github.com/Jguer/go-alpm/v2 v2.2.2 h1:sPwUoZp1X5Tw6K6Ba1lWvVJfcgVNEGVcxARLBttZnC0=
|
||||
github.com/Jguer/go-alpm/v2 v2.2.2/go.mod h1:lfe8gSe83F/KERaQvEfrSqQ4n+8bES+ZIyKWR/gm3MI=
|
||||
github.com/Jguer/aur v1.0.1 h1:+GDOq0RuVn7CXpXzd8W85/+hPNDYonRZ3ONPm87e1jo=
|
||||
github.com/Jguer/aur v1.0.1/go.mod h1:1/SQjhWahmk2xKcmAm6XO1zGqK8HgYw3xlJM6a7845E=
|
||||
github.com/Jguer/go-alpm/v2 v2.1.2 h1:CGTIxzuEpT9Q3a7IBrx0E6acoYoaHX2Z93UOApPDhgU=
|
||||
github.com/Jguer/go-alpm/v2 v2.1.2/go.mod h1:uLQcTMNM904dRiGU+/JDtDdd7Nd8mVbEVaHjhmziT7w=
|
||||
github.com/Jguer/votar v1.0.0 h1:drPYpV5Py5BeAQS8xezmT6uCEfLzotNjLf5yfmlHKTg=
|
||||
github.com/Jguer/votar v1.0.0/go.mod h1:rc6vgVlTqNjI4nAnPbDTbdxw/N7kXkbB8BcUDjeFbYQ=
|
||||
github.com/Morganamilo/go-pacmanconf v0.0.0-20210502114700-cff030e927a5 h1:TMscPjkb1ThXN32LuFY5bEYIcXZx3YlwzhS1GxNpn/c=
|
||||
github.com/Morganamilo/go-pacmanconf v0.0.0-20210502114700-cff030e927a5/go.mod h1:Hk55m330jNiwxRodIlMCvw5iEyoRUCIY64W1p9D+tHc=
|
||||
github.com/Morganamilo/go-srcinfo v1.0.0 h1:Wh4nEF+HJWo+29hnxM18Q2hi+DUf0GejS13+Wg+dzmI=
|
||||
github.com/Morganamilo/go-srcinfo v1.0.0/go.mod h1:MP6VGY1NNpVUmYIEgoM9acix95KQqIRyqQ0hCLsyYUY=
|
||||
github.com/adrg/strutil v0.3.1 h1:OLvSS7CSJO8lBii4YmBt8jiK9QOtB9CzCzwl4Ic/Fz4=
|
||||
github.com/adrg/strutil v0.3.1/go.mod h1:8h90y18QLrs11IBffcGX3NW/GFBXCMcNg4M7H6MspPA=
|
||||
github.com/adrg/strutil v0.3.0 h1:bi/HB2zQbDihC8lxvATDTDzkT4bG7PATtVnDYp5rvq4=
|
||||
github.com/adrg/strutil v0.3.0/go.mod h1:Jz0wzBVE6Uiy9wxo62YEqEY1Nwto3QlLl1Il5gkLKWU=
|
||||
github.com/alexflint/go-arg v1.4.3/go.mod h1:3PZ/wp/8HuqRZMUUgu7I+e1qcpUbvmS258mRXkFH4IA=
|
||||
github.com/alexflint/go-scalar v1.1.0/go.mod h1:LoFvNMqS1CPrMVltza4LvnGKhaSpc3oyLEBUZVhhS2o=
|
||||
github.com/bradleyjkemp/cupaloy v2.3.0+incompatible h1:UafIjBvWQmS9i/xRg+CamMrnLTKNzo+bdmT/oH34c2Y=
|
||||
@ -17,52 +17,45 @@ 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.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/deckarep/golang-set/v2 v2.8.0 h1:swm0rlPCmdWn9mESxKOjWk8hXSqoxOp+ZlfuyaAdFlQ=
|
||||
github.com/deckarep/golang-set/v2 v2.8.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/itchyny/gojq v0.12.17 h1:8av8eGduDb5+rvEdaOO+zQUjA04MS0m3Ps8HiD+fceg=
|
||||
github.com/itchyny/gojq v0.12.17/go.mod h1:WBrEMkgAfAGO1LUcGOckBl5O726KPp+OlkKug0I/FEY=
|
||||
github.com/itchyny/timefmt-go v0.1.6 h1:ia3s54iciXDdzWzwaVKXZPbiXzxxnv1SPGFfM/myJ5Q=
|
||||
github.com/itchyny/timefmt-go v0.1.6/go.mod h1:RRDZYC5s9ErkjQvTvvU7keJjxUYzIISJGxm9/mAERQg=
|
||||
github.com/leonelquinteros/gotext v1.7.2 h1:bDPndU8nt+/kRo1m4l/1OXiiy2v7Z7dfPQ9+YP7G1Mc=
|
||||
github.com/leonelquinteros/gotext v1.7.2/go.mod h1:9/haCkm5P7Jay1sxKDGJ5WIg4zkz8oZKw4ekNpALob8=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/leonelquinteros/gotext v1.5.0 h1:ODY7LzLpZWWSJdAHnzhreOr6cwLXTAmc914FOauSkBM=
|
||||
github.com/leonelquinteros/gotext v1.5.0/go.mod h1:OCiUVHuhP9LGFBQ1oAmdtNCHJCiHiQA8lf4nAifHkr0=
|
||||
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4=
|
||||
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
|
||||
github.com/ohler55/ojg v1.26.1 h1:J5TaLmVEuvnpVH7JMdT1QdbpJU545Yp6cKiCO4aQILc=
|
||||
github.com/ohler55/ojg v1.26.1/go.mod h1:gQhDVpQLqrmnd2eqGAvJtn+NfKoYJbe/A4Sj3/Vro4o=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
|
||||
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
|
||||
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 h1:Q5284mrmYTpACcm+eAKjKJH48BBwSyfJqmmGDTtT8Vc=
|
||||
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/tools v0.0.0-20200221224223-e1da425f72fd/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/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/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY=
|
||||
gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
855
install.go
Normal file
855
install.go
Normal file
@ -0,0 +1,855 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
alpm "github.com/Jguer/go-alpm/v2"
|
||||
gosrc "github.com/Morganamilo/go-srcinfo"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
|
||||
"github.com/Jguer/yay/v11/pkg/completion"
|
||||
"github.com/Jguer/yay/v11/pkg/db"
|
||||
"github.com/Jguer/yay/v11/pkg/dep"
|
||||
"github.com/Jguer/yay/v11/pkg/download"
|
||||
"github.com/Jguer/yay/v11/pkg/menus"
|
||||
"github.com/Jguer/yay/v11/pkg/pgp"
|
||||
"github.com/Jguer/yay/v11/pkg/query"
|
||||
"github.com/Jguer/yay/v11/pkg/settings"
|
||||
"github.com/Jguer/yay/v11/pkg/settings/parser"
|
||||
"github.com/Jguer/yay/v11/pkg/stringset"
|
||||
"github.com/Jguer/yay/v11/pkg/text"
|
||||
)
|
||||
|
||||
func asdeps(ctx context.Context, cmdArgs *parser.Arguments, pkgs []string) (err error) {
|
||||
if len(pkgs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
cmdArgs = cmdArgs.CopyGlobal()
|
||||
_ = cmdArgs.AddArg("q", "D", "asdeps")
|
||||
cmdArgs.AddTarget(pkgs...)
|
||||
|
||||
err = config.Runtime.CmdBuilder.Show(config.Runtime.CmdBuilder.BuildPacmanCmd(ctx,
|
||||
cmdArgs, config.Runtime.Mode, settings.NoConfirm))
|
||||
if err != nil {
|
||||
return errors.New(gotext.Get("error updating package install reason to dependency"))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func asexp(ctx context.Context, cmdArgs *parser.Arguments, pkgs []string) (err error) {
|
||||
if len(pkgs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
cmdArgs = cmdArgs.CopyGlobal()
|
||||
_ = cmdArgs.AddArg("q", "D", "asexplicit")
|
||||
cmdArgs.AddTarget(pkgs...)
|
||||
|
||||
err = config.Runtime.CmdBuilder.Show(config.Runtime.CmdBuilder.BuildPacmanCmd(ctx,
|
||||
cmdArgs, config.Runtime.Mode, settings.NoConfirm))
|
||||
if err != nil {
|
||||
return errors.New(gotext.Get("error updating package install reason to explicit"))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Install handles package installs.
|
||||
func install(ctx context.Context, cmdArgs *parser.Arguments, dbExecutor db.Executor, ignoreProviders bool) error {
|
||||
var (
|
||||
incompatible stringset.StringSet
|
||||
do *dep.Order
|
||||
srcinfos map[string]*gosrc.Srcinfo
|
||||
noDeps = cmdArgs.ExistsDouble("d", "nodeps")
|
||||
noCheck = strings.Contains(config.MFlags, "--nocheck")
|
||||
assumeInstalled = cmdArgs.GetArgs("assume-installed")
|
||||
sysupgradeArg = cmdArgs.ExistsArg("u", "sysupgrade")
|
||||
refreshArg = cmdArgs.ExistsArg("y", "refresh")
|
||||
warnings = query.NewWarnings()
|
||||
)
|
||||
|
||||
if noDeps {
|
||||
config.Runtime.CmdBuilder.AddMakepkgFlag("-d")
|
||||
}
|
||||
|
||||
if config.Runtime.Mode.AtLeastRepo() {
|
||||
if config.CombinedUpgrade {
|
||||
if refreshArg {
|
||||
if errR := earlyRefresh(ctx, cmdArgs); errR != nil {
|
||||
return errors.New(gotext.Get("error refreshing databases"))
|
||||
}
|
||||
}
|
||||
} else if refreshArg || sysupgradeArg || len(cmdArgs.Targets) > 0 {
|
||||
if errP := earlyPacmanCall(ctx, cmdArgs, dbExecutor); errP != nil {
|
||||
return errP
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we may have done -Sy, our handle now has an old
|
||||
// database.
|
||||
if errRefresh := dbExecutor.RefreshHandle(); errRefresh != nil {
|
||||
return errRefresh
|
||||
}
|
||||
|
||||
localNames, remoteNames, err := query.GetPackageNamesBySource(dbExecutor)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
remoteNamesCache := stringset.FromSlice(remoteNames)
|
||||
localNamesCache := stringset.FromSlice(localNames)
|
||||
|
||||
requestTargets := cmdArgs.Copy().Targets
|
||||
|
||||
// create the arguments to pass for the repo install
|
||||
arguments := cmdArgs.Copy()
|
||||
arguments.DelArg("asdeps", "asdep")
|
||||
arguments.DelArg("asexplicit", "asexp")
|
||||
arguments.Op = "S"
|
||||
arguments.ClearTargets()
|
||||
|
||||
if config.Runtime.Mode == parser.ModeAUR {
|
||||
arguments.DelArg("u", "sysupgrade")
|
||||
}
|
||||
|
||||
// if we are doing -u also request all packages needing update
|
||||
if sysupgradeArg {
|
||||
ignore, targets, errUp := sysupgradeTargets(ctx, dbExecutor, cmdArgs.ExistsDouble("u", "sysupgrade"))
|
||||
if errUp != nil {
|
||||
return errUp
|
||||
}
|
||||
|
||||
for _, up := range targets {
|
||||
cmdArgs.AddTarget(up)
|
||||
requestTargets = append(requestTargets, up)
|
||||
}
|
||||
|
||||
if len(ignore) > 0 {
|
||||
arguments.CreateOrAppendOption("ignore", ignore.ToSlice()...)
|
||||
}
|
||||
}
|
||||
|
||||
targets := stringset.FromSlice(cmdArgs.Targets)
|
||||
|
||||
dp, err := dep.GetPool(ctx, requestTargets,
|
||||
warnings, dbExecutor, config.Runtime.AURClient, config.Runtime.Mode,
|
||||
ignoreProviders, settings.NoConfirm, config.Provides, config.ReBuild, config.RequestSplitN, noDeps, noCheck, assumeInstalled)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if errC := dp.CheckMissing(noDeps, noCheck); errC != nil {
|
||||
return errC
|
||||
}
|
||||
|
||||
if len(dp.Aur) == 0 {
|
||||
if !config.CombinedUpgrade {
|
||||
if sysupgradeArg {
|
||||
fmt.Println(gotext.Get(" there is nothing to do"))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
cmdArgs.Op = "S"
|
||||
cmdArgs.DelArg("y", "refresh")
|
||||
|
||||
if arguments.ExistsArg("ignore") {
|
||||
cmdArgs.CreateOrAppendOption("ignore", arguments.GetArgs("ignore")...)
|
||||
}
|
||||
|
||||
return config.Runtime.CmdBuilder.Show(config.Runtime.CmdBuilder.BuildPacmanCmd(ctx,
|
||||
cmdArgs, config.Runtime.Mode, settings.NoConfirm))
|
||||
}
|
||||
|
||||
conflicts, errCC := dp.CheckConflicts(config.UseAsk, settings.NoConfirm, noDeps)
|
||||
if errCC != nil {
|
||||
return errCC
|
||||
}
|
||||
|
||||
do = dep.GetOrder(dp, noDeps, noCheck)
|
||||
|
||||
for _, pkg := range do.Repo {
|
||||
arguments.AddTarget(pkg.DB().Name() + "/" + pkg.Name())
|
||||
}
|
||||
|
||||
for _, pkg := range dp.Groups {
|
||||
arguments.AddTarget(pkg)
|
||||
}
|
||||
|
||||
if len(do.Aur) == 0 && len(arguments.Targets) == 0 &&
|
||||
(!cmdArgs.ExistsArg("u", "sysupgrade") || config.Runtime.Mode == parser.ModeAUR) {
|
||||
fmt.Println(gotext.Get(" there is nothing to do"))
|
||||
return nil
|
||||
}
|
||||
|
||||
do.Print()
|
||||
fmt.Println()
|
||||
|
||||
if config.CleanAfter {
|
||||
defer cleanAfter(ctx, do.Aur)
|
||||
}
|
||||
|
||||
if do.HasMake() {
|
||||
switch config.RemoveMake {
|
||||
case "yes":
|
||||
defer func() {
|
||||
err = removeMake(ctx, do)
|
||||
}()
|
||||
|
||||
case "no":
|
||||
break
|
||||
default:
|
||||
if text.ContinueTask(os.Stdin, gotext.Get("Remove make dependencies after install?"), false, settings.NoConfirm) {
|
||||
defer func() {
|
||||
err = removeMake(ctx, do)
|
||||
}()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if errCleanMenu := menus.Clean(config.CleanMenu,
|
||||
config.BuildDir, do.Aur,
|
||||
remoteNamesCache, settings.NoConfirm, config.AnswerClean); errCleanMenu != nil {
|
||||
if errors.As(errCleanMenu, &settings.ErrUserAbort{}) {
|
||||
return errCleanMenu
|
||||
}
|
||||
|
||||
text.Errorln(errCleanMenu)
|
||||
}
|
||||
|
||||
toSkip := pkgbuildsToSkip(do.Aur, targets)
|
||||
toClone := make([]string, 0, len(do.Aur))
|
||||
|
||||
for _, base := range do.Aur {
|
||||
if !toSkip.Get(base.Pkgbase()) {
|
||||
toClone = append(toClone, base.Pkgbase())
|
||||
}
|
||||
}
|
||||
|
||||
if toSkipSlice := toSkip.ToSlice(); len(toSkipSlice) != 0 {
|
||||
text.OperationInfoln(
|
||||
gotext.Get("PKGBUILD up to date, Skipping (%d/%d): %s",
|
||||
len(toSkipSlice), len(toClone), text.Cyan(strings.Join(toSkipSlice, ", "))))
|
||||
}
|
||||
|
||||
cloned, errA := download.AURPKGBUILDRepos(ctx,
|
||||
config.Runtime.CmdBuilder, toClone, config.AURURL, config.BuildDir, false)
|
||||
if errA != nil {
|
||||
return errA
|
||||
}
|
||||
|
||||
if errDiffMenu := menus.Diff(ctx, config.Runtime.CmdBuilder, config.BuildDir,
|
||||
config.DiffMenu, do.Aur, remoteNamesCache,
|
||||
cloned, settings.NoConfirm, config.AnswerDiff); errDiffMenu != nil {
|
||||
if errors.As(errDiffMenu, &settings.ErrUserAbort{}) {
|
||||
return errDiffMenu
|
||||
}
|
||||
|
||||
text.Errorln(errDiffMenu)
|
||||
}
|
||||
|
||||
if errM := mergePkgbuilds(ctx, do.Aur); errM != nil {
|
||||
return errM
|
||||
}
|
||||
|
||||
srcinfos, err = parseSrcinfoFiles(do.Aur, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if errEditMenu := menus.Edit(config.EditMenu, config.BuildDir, do.Aur,
|
||||
config.Editor, config.EditorFlags, remoteNamesCache, srcinfos,
|
||||
settings.NoConfirm, config.AnswerEdit); errEditMenu != nil {
|
||||
if errors.As(errEditMenu, &settings.ErrUserAbort{}) {
|
||||
return errEditMenu
|
||||
}
|
||||
|
||||
text.Errorln(errEditMenu)
|
||||
}
|
||||
|
||||
incompatible, err = getIncompatible(do.Aur, srcinfos, dbExecutor)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if config.PGPFetch {
|
||||
if errCPK := pgp.CheckPgpKeys(do.Aur, srcinfos, config.GpgBin, config.GpgFlags, settings.NoConfirm); errCPK != nil {
|
||||
return errCPK
|
||||
}
|
||||
}
|
||||
|
||||
if !config.CombinedUpgrade {
|
||||
arguments.DelArg("u", "sysupgrade")
|
||||
}
|
||||
|
||||
if len(arguments.Targets) > 0 || arguments.ExistsArg("u") {
|
||||
if errShow := config.Runtime.CmdBuilder.Show(config.Runtime.CmdBuilder.BuildPacmanCmd(ctx,
|
||||
arguments, config.Runtime.Mode, settings.NoConfirm)); errShow != nil {
|
||||
return errors.New(gotext.Get("error installing repo packages"))
|
||||
}
|
||||
|
||||
deps := make([]string, 0)
|
||||
exp := make([]string, 0)
|
||||
|
||||
for _, pkg := range do.Repo {
|
||||
if !dp.Explicit.Get(pkg.Name()) && !localNamesCache.Get(pkg.Name()) && !remoteNamesCache.Get(pkg.Name()) {
|
||||
deps = append(deps, pkg.Name())
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if cmdArgs.ExistsArg("asdeps", "asdep") && dp.Explicit.Get(pkg.Name()) {
|
||||
deps = append(deps, pkg.Name())
|
||||
} else if cmdArgs.ExistsArg("asexp", "asexplicit") && dp.Explicit.Get(pkg.Name()) {
|
||||
exp = append(exp, pkg.Name())
|
||||
}
|
||||
}
|
||||
|
||||
if errDeps := asdeps(ctx, cmdArgs, deps); errDeps != nil {
|
||||
return errDeps
|
||||
}
|
||||
|
||||
if errExp := asexp(ctx, cmdArgs, exp); errExp != nil {
|
||||
return errExp
|
||||
}
|
||||
}
|
||||
|
||||
go func() {
|
||||
_ = completion.Update(ctx, config.Runtime.HTTPClient, dbExecutor,
|
||||
config.AURURL, config.Runtime.CompletionPath, config.CompletionInterval, false)
|
||||
}()
|
||||
|
||||
if errP := downloadPKGBUILDSourceFanout(ctx, config.Runtime.CmdBuilder, config.BuildDir,
|
||||
do.Aur, incompatible, config.MaxConcurrentDownloads); errP != nil {
|
||||
text.Errorln(errP)
|
||||
}
|
||||
|
||||
if errB := buildInstallPkgbuilds(ctx, cmdArgs, dbExecutor, dp, do, srcinfos, incompatible, conflicts, noDeps, noCheck); errB != nil {
|
||||
return errB
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func removeMake(ctx context.Context, do *dep.Order) error {
|
||||
removeArguments := parser.MakeArguments()
|
||||
|
||||
err := removeArguments.AddArg("R", "u")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, pkg := range do.GetMake() {
|
||||
removeArguments.AddTarget(pkg)
|
||||
}
|
||||
|
||||
oldValue := settings.NoConfirm
|
||||
settings.NoConfirm = true
|
||||
err = config.Runtime.CmdBuilder.Show(config.Runtime.CmdBuilder.BuildPacmanCmd(ctx,
|
||||
removeArguments, config.Runtime.Mode, settings.NoConfirm))
|
||||
settings.NoConfirm = oldValue
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func inRepos(dbExecutor db.Executor, pkg string) bool {
|
||||
target := dep.ToTarget(pkg)
|
||||
|
||||
if target.DB == "aur" {
|
||||
return false
|
||||
} else if target.DB != "" {
|
||||
return true
|
||||
}
|
||||
|
||||
previousHideMenus := settings.HideMenus
|
||||
settings.HideMenus = true
|
||||
exists := dbExecutor.SyncSatisfierExists(target.DepString())
|
||||
settings.HideMenus = previousHideMenus
|
||||
|
||||
return exists || len(dbExecutor.PackagesFromGroup(target.Name)) > 0
|
||||
}
|
||||
|
||||
func earlyPacmanCall(ctx context.Context, cmdArgs *parser.Arguments, dbExecutor db.Executor) error {
|
||||
arguments := cmdArgs.Copy()
|
||||
arguments.Op = "S"
|
||||
targets := cmdArgs.Targets
|
||||
cmdArgs.ClearTargets()
|
||||
arguments.ClearTargets()
|
||||
|
||||
if config.Runtime.Mode == parser.ModeRepo {
|
||||
arguments.Targets = targets
|
||||
} else {
|
||||
// separate aur and repo targets
|
||||
for _, target := range targets {
|
||||
if inRepos(dbExecutor, target) {
|
||||
arguments.AddTarget(target)
|
||||
} else {
|
||||
cmdArgs.AddTarget(target)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if cmdArgs.ExistsArg("y", "refresh") || cmdArgs.ExistsArg("u", "sysupgrade") || len(arguments.Targets) > 0 {
|
||||
if err := config.Runtime.CmdBuilder.Show(config.Runtime.CmdBuilder.BuildPacmanCmd(ctx,
|
||||
arguments, config.Runtime.Mode, settings.NoConfirm)); err != nil {
|
||||
return errors.New(gotext.Get("error installing repo packages"))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func earlyRefresh(ctx context.Context, cmdArgs *parser.Arguments) error {
|
||||
arguments := cmdArgs.Copy()
|
||||
cmdArgs.DelArg("y", "refresh")
|
||||
arguments.DelArg("u", "sysupgrade")
|
||||
arguments.DelArg("s", "search")
|
||||
arguments.DelArg("i", "info")
|
||||
arguments.DelArg("l", "list")
|
||||
arguments.ClearTargets()
|
||||
|
||||
return config.Runtime.CmdBuilder.Show(config.Runtime.CmdBuilder.BuildPacmanCmd(ctx,
|
||||
arguments, config.Runtime.Mode, settings.NoConfirm))
|
||||
}
|
||||
|
||||
func alpmArchIsSupported(alpmArch []string, arch string) bool {
|
||||
if arch == "any" {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, a := range alpmArch {
|
||||
if a == arch {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func getIncompatible(bases []dep.Base, srcinfos map[string]*gosrc.Srcinfo, dbExecutor db.Executor) (stringset.StringSet, error) {
|
||||
incompatible := make(stringset.StringSet)
|
||||
basesMap := make(map[string]dep.Base)
|
||||
|
||||
alpmArch, err := dbExecutor.AlpmArchitectures()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nextpkg:
|
||||
for _, base := range bases {
|
||||
for _, arch := range srcinfos[base.Pkgbase()].Arch {
|
||||
if alpmArchIsSupported(alpmArch, arch) {
|
||||
continue nextpkg
|
||||
}
|
||||
}
|
||||
|
||||
incompatible.Set(base.Pkgbase())
|
||||
basesMap[base.Pkgbase()] = base
|
||||
}
|
||||
|
||||
if len(incompatible) > 0 {
|
||||
text.Warnln(gotext.Get("The following packages are not compatible with your architecture:"))
|
||||
|
||||
for pkg := range incompatible {
|
||||
fmt.Print(" " + text.Cyan(basesMap[pkg].String()))
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
|
||||
if !text.ContinueTask(os.Stdin, gotext.Get("Try to build them anyway?"), true, settings.NoConfirm) {
|
||||
return nil, &settings.ErrUserAbort{}
|
||||
}
|
||||
}
|
||||
|
||||
return incompatible, nil
|
||||
}
|
||||
|
||||
func parsePackageList(ctx context.Context, dir string) (pkgdests map[string]string, pkgVersion string, err error) {
|
||||
stdout, stderr, err := config.Runtime.CmdBuilder.Capture(
|
||||
config.Runtime.CmdBuilder.BuildMakepkgCmd(ctx, dir, "--packagelist"))
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("%s %s", 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
|
||||
}
|
||||
|
||||
return pkgdests, pkgVersion, nil
|
||||
}
|
||||
|
||||
func parseSrcinfoFiles(bases []dep.Base, errIsFatal bool) (map[string]*gosrc.Srcinfo, error) {
|
||||
srcinfos := make(map[string]*gosrc.Srcinfo)
|
||||
|
||||
for k, base := range bases {
|
||||
pkg := base.Pkgbase()
|
||||
dir := filepath.Join(config.BuildDir, pkg)
|
||||
|
||||
text.OperationInfoln(gotext.Get("(%d/%d) Parsing SRCINFO: %s", k+1, len(bases), text.Cyan(base.String())))
|
||||
|
||||
pkgbuild, err := gosrc.ParseFile(filepath.Join(dir, ".SRCINFO"))
|
||||
if err != nil {
|
||||
if !errIsFatal {
|
||||
text.Warnln(gotext.Get("failed to parse %s -- skipping: %s", base.String(), err))
|
||||
continue
|
||||
}
|
||||
|
||||
return nil, errors.New(gotext.Get("failed to parse %s: %s", base.String(), err))
|
||||
}
|
||||
|
||||
srcinfos[pkg] = pkgbuild
|
||||
}
|
||||
|
||||
return srcinfos, nil
|
||||
}
|
||||
|
||||
func pkgbuildsToSkip(bases []dep.Base, targets stringset.StringSet) stringset.StringSet {
|
||||
toSkip := make(stringset.StringSet)
|
||||
|
||||
for _, base := range bases {
|
||||
isTarget := false
|
||||
for _, pkg := range base {
|
||||
isTarget = isTarget || targets.Get(pkg.Name)
|
||||
}
|
||||
|
||||
if (config.ReDownload == "yes" && isTarget) || config.ReDownload == "all" {
|
||||
continue
|
||||
}
|
||||
|
||||
dir := filepath.Join(config.BuildDir, base.Pkgbase(), ".SRCINFO")
|
||||
pkgbuild, err := gosrc.ParseFile(dir)
|
||||
|
||||
if err == nil {
|
||||
if db.VerCmp(pkgbuild.Version(), base.Version()) >= 0 {
|
||||
toSkip.Set(base.Pkgbase())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return toSkip
|
||||
}
|
||||
|
||||
func gitMerge(ctx context.Context, path, name string) error {
|
||||
_, stderr, err := config.Runtime.CmdBuilder.Capture(
|
||||
config.Runtime.CmdBuilder.BuildGitCmd(ctx,
|
||||
filepath.Join(path, name), "reset", "--hard", "HEAD"))
|
||||
if err != nil {
|
||||
return errors.New(gotext.Get("error resetting %s: %s", name, stderr))
|
||||
}
|
||||
|
||||
_, stderr, err = config.Runtime.CmdBuilder.Capture(
|
||||
config.Runtime.CmdBuilder.BuildGitCmd(ctx,
|
||||
filepath.Join(path, name), "merge", "--no-edit", "--ff"))
|
||||
if err != nil {
|
||||
return errors.New(gotext.Get("error merging %s: %s", name, stderr))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func mergePkgbuilds(ctx context.Context, bases []dep.Base) error {
|
||||
for _, base := range bases {
|
||||
err := gitMerge(ctx, config.BuildDir, base.Pkgbase())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func buildInstallPkgbuilds(
|
||||
ctx context.Context,
|
||||
cmdArgs *parser.Arguments,
|
||||
dbExecutor db.Executor,
|
||||
dp *dep.Pool,
|
||||
do *dep.Order,
|
||||
srcinfos map[string]*gosrc.Srcinfo,
|
||||
incompatible stringset.StringSet,
|
||||
conflicts stringset.MapStringSet, noDeps, noCheck bool,
|
||||
) error {
|
||||
arguments := cmdArgs.Copy()
|
||||
arguments.ClearTargets()
|
||||
arguments.Op = "U"
|
||||
arguments.DelArg("confirm")
|
||||
arguments.DelArg("noconfirm")
|
||||
arguments.DelArg("c", "clean")
|
||||
arguments.DelArg("q", "quiet")
|
||||
arguments.DelArg("q", "quiet")
|
||||
arguments.DelArg("y", "refresh")
|
||||
arguments.DelArg("u", "sysupgrade")
|
||||
arguments.DelArg("w", "downloadonly")
|
||||
|
||||
deps := make([]string, 0)
|
||||
exp := make([]string, 0)
|
||||
oldConfirm := settings.NoConfirm
|
||||
settings.NoConfirm = true
|
||||
|
||||
// remotenames: names of all non repo packages on the system
|
||||
localNames, remoteNames, err := query.GetPackageNamesBySource(dbExecutor)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// cache as a stringset. maybe make it return a string set in the first
|
||||
// place
|
||||
remoteNamesCache := stringset.FromSlice(remoteNames)
|
||||
localNamesCache := stringset.FromSlice(localNames)
|
||||
|
||||
for i, base := range do.Aur {
|
||||
pkg := base.Pkgbase()
|
||||
dir := filepath.Join(config.BuildDir, pkg)
|
||||
built := true
|
||||
|
||||
satisfied := true
|
||||
all:
|
||||
for _, pkg := range base {
|
||||
for _, dep := range dep.ComputeCombinedDepList(pkg, noDeps, noCheck) {
|
||||
if !dp.AlpmExecutor.LocalSatisfierExists(dep) {
|
||||
satisfied = false
|
||||
text.Warnln(gotext.Get("%s not satisfied, flushing install queue", dep))
|
||||
|
||||
break all
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !satisfied || !config.BatchInstall {
|
||||
err = doInstall(ctx, arguments, cmdArgs, deps, exp)
|
||||
arguments.ClearTargets()
|
||||
|
||||
deps = make([]string, 0)
|
||||
exp = make([]string, 0)
|
||||
|
||||
if err != nil {
|
||||
if i != 0 {
|
||||
go config.Runtime.VCSStore.RemovePackage([]string{do.Aur[i-1].String()})
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
srcinfo := srcinfos[pkg]
|
||||
|
||||
args := []string{"--nobuild", "-fC"}
|
||||
|
||||
if incompatible.Get(pkg) {
|
||||
args = append(args, "--ignorearch")
|
||||
}
|
||||
|
||||
// pkgver bump
|
||||
if err = config.Runtime.CmdBuilder.Show(
|
||||
config.Runtime.CmdBuilder.BuildMakepkgCmd(ctx, dir, args...)); err != nil {
|
||||
return errors.New(gotext.Get("error making: %s", base.String()))
|
||||
}
|
||||
|
||||
pkgdests, pkgVersion, errList := parsePackageList(ctx, dir)
|
||||
if errList != nil {
|
||||
return errList
|
||||
}
|
||||
|
||||
isExplicit := false
|
||||
for _, b := range base {
|
||||
isExplicit = isExplicit || dp.Explicit.Get(b.Name)
|
||||
}
|
||||
|
||||
if config.ReBuild == "no" || (config.ReBuild == "yes" && !isExplicit) {
|
||||
for _, split := range base {
|
||||
pkgdest, ok := pkgdests[split.Name]
|
||||
if !ok {
|
||||
return errors.New(gotext.Get("could not find PKGDEST for: %s", split.Name))
|
||||
}
|
||||
|
||||
if _, errStat := os.Stat(pkgdest); os.IsNotExist(errStat) {
|
||||
built = false
|
||||
} else if errStat != nil {
|
||||
return errStat
|
||||
}
|
||||
}
|
||||
} else {
|
||||
built = false
|
||||
}
|
||||
|
||||
if cmdArgs.ExistsArg("needed") {
|
||||
installed := true
|
||||
for _, split := range base {
|
||||
installed = dp.AlpmExecutor.IsCorrectVersionInstalled(split.Name, pkgVersion)
|
||||
}
|
||||
|
||||
if installed {
|
||||
err = config.Runtime.CmdBuilder.Show(
|
||||
config.Runtime.CmdBuilder.BuildMakepkgCmd(ctx,
|
||||
dir, "-c", "--nobuild", "--noextract", "--ignorearch"))
|
||||
if err != nil {
|
||||
return errors.New(gotext.Get("error making: %s", err))
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stdout, gotext.Get("%s is up to date -- skipping", text.Cyan(pkg+"-"+pkgVersion)))
|
||||
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if built {
|
||||
err = config.Runtime.CmdBuilder.Show(
|
||||
config.Runtime.CmdBuilder.BuildMakepkgCmd(ctx,
|
||||
dir, "-c", "--nobuild", "--noextract", "--ignorearch"))
|
||||
if err != nil {
|
||||
return errors.New(gotext.Get("error making: %s", err))
|
||||
}
|
||||
|
||||
text.Warnln(gotext.Get("%s already made -- skipping build", text.Cyan(pkg+"-"+pkgVersion)))
|
||||
} else {
|
||||
args := []string{"-cf", "--noconfirm", "--noextract", "--noprepare", "--holdver"}
|
||||
|
||||
if incompatible.Get(pkg) {
|
||||
args = append(args, "--ignorearch")
|
||||
}
|
||||
|
||||
if errMake := config.Runtime.CmdBuilder.Show(
|
||||
config.Runtime.CmdBuilder.BuildMakepkgCmd(ctx,
|
||||
dir, args...)); errMake != nil {
|
||||
return errors.New(gotext.Get("error making: %s", base.String()))
|
||||
}
|
||||
}
|
||||
|
||||
// conflicts have been checked so answer y for them
|
||||
if config.UseAsk && cmdArgs.ExistsArg("ask") {
|
||||
ask, _ := strconv.Atoi(cmdArgs.Options["ask"].First())
|
||||
uask := alpm.QuestionType(ask) | alpm.QuestionTypeConflictPkg
|
||||
cmdArgs.Options["ask"].Set(fmt.Sprint(uask))
|
||||
} else {
|
||||
for _, split := range base {
|
||||
if _, ok := conflicts[split.Name]; ok {
|
||||
settings.NoConfirm = false
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var errAdd error
|
||||
|
||||
for _, split := range base {
|
||||
for suffix, optional := range map[string]bool{"": false, "-debug": true} {
|
||||
deps, exp, errAdd = doAddTarget(dp, localNamesCache, remoteNamesCache,
|
||||
arguments, cmdArgs, pkgdests, deps, exp, split.Name+suffix, optional)
|
||||
if errAdd != nil {
|
||||
return errAdd
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
mux sync.Mutex
|
||||
wg sync.WaitGroup
|
||||
)
|
||||
|
||||
for _, pkg := range base {
|
||||
wg.Add(1)
|
||||
|
||||
go config.Runtime.VCSStore.Update(ctx, pkg.Name, srcinfo.Source, &mux, &wg)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
err = doInstall(ctx, arguments, cmdArgs, deps, exp)
|
||||
if err != nil {
|
||||
go config.Runtime.VCSStore.RemovePackage([]string{do.Aur[len(do.Aur)-1].String()})
|
||||
}
|
||||
|
||||
settings.NoConfirm = oldConfirm
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func doInstall(ctx context.Context, arguments, cmdArgs *parser.Arguments, pkgDeps, pkgExp []string) error {
|
||||
if len(arguments.Targets) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if errShow := config.Runtime.CmdBuilder.Show(config.Runtime.CmdBuilder.BuildPacmanCmd(ctx,
|
||||
arguments, config.Runtime.Mode, settings.NoConfirm)); errShow != nil {
|
||||
return errShow
|
||||
}
|
||||
|
||||
if errStore := config.Runtime.VCSStore.Save(); errStore != nil {
|
||||
fmt.Fprintln(os.Stderr, errStore)
|
||||
}
|
||||
|
||||
if errDeps := asdeps(ctx, cmdArgs, pkgDeps); errDeps != nil {
|
||||
return errDeps
|
||||
}
|
||||
|
||||
return asexp(ctx, cmdArgs, pkgExp)
|
||||
}
|
||||
|
||||
func doAddTarget(dp *dep.Pool, localNamesCache, remoteNamesCache stringset.StringSet,
|
||||
arguments, cmdArgs *parser.Arguments, pkgdests map[string]string,
|
||||
deps, exp []string, name string, optional bool,
|
||||
) (newDeps, newExp []string, err error) {
|
||||
pkgdest, ok := pkgdests[name]
|
||||
if !ok {
|
||||
if optional {
|
||||
return deps, exp, nil
|
||||
}
|
||||
|
||||
return deps, exp, errors.New(gotext.Get("could not find PKGDEST for: %s", name))
|
||||
}
|
||||
|
||||
if _, errStat := os.Stat(pkgdest); os.IsNotExist(errStat) {
|
||||
if optional {
|
||||
return deps, exp, nil
|
||||
}
|
||||
|
||||
return deps, exp, errors.New(
|
||||
gotext.Get(
|
||||
"the PKGDEST for %s is listed by makepkg but does not exist: %s",
|
||||
name, pkgdest))
|
||||
}
|
||||
|
||||
arguments.AddTarget(pkgdest)
|
||||
|
||||
switch {
|
||||
case cmdArgs.ExistsArg("asdeps", "asdep"):
|
||||
deps = append(deps, name)
|
||||
case cmdArgs.ExistsArg("asexplicit", "asexp"):
|
||||
exp = append(exp, name)
|
||||
case !dp.Explicit.Get(name) && !localNamesCache.Get(name) && !remoteNamesCache.Get(name):
|
||||
deps = append(deps, name)
|
||||
}
|
||||
|
||||
return deps, exp, nil
|
||||
}
|
108
local_install.go
108
local_install.go
@ -1,108 +0,0 @@
|
||||
// Experimental code for install local with dependency refactoring
|
||||
// Not at feature parity with install.go
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/Jguer/yay/v12/pkg/db"
|
||||
"github.com/Jguer/yay/v12/pkg/dep"
|
||||
"github.com/Jguer/yay/v12/pkg/multierror"
|
||||
"github.com/Jguer/yay/v12/pkg/runtime"
|
||||
"github.com/Jguer/yay/v12/pkg/settings"
|
||||
"github.com/Jguer/yay/v12/pkg/settings/exe"
|
||||
"github.com/Jguer/yay/v12/pkg/settings/parser"
|
||||
"github.com/Jguer/yay/v12/pkg/sync"
|
||||
|
||||
gosrc "github.com/Morganamilo/go-srcinfo"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
)
|
||||
|
||||
var ErrNoBuildFiles = errors.New(gotext.Get("cannot find PKGBUILD and .SRCINFO in directory"))
|
||||
|
||||
func srcinfoExists(ctx context.Context,
|
||||
cmdBuilder exe.ICmdBuilder, targetDir string,
|
||||
) error {
|
||||
srcInfoDir := filepath.Join(targetDir, ".SRCINFO")
|
||||
pkgbuildDir := filepath.Join(targetDir, "PKGBUILD")
|
||||
if _, err := os.Stat(srcInfoDir); err == nil {
|
||||
if _, err := os.Stat(pkgbuildDir); err == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := os.Stat(pkgbuildDir); err == nil {
|
||||
// run makepkg to generate .SRCINFO
|
||||
srcinfo, stderr, err := cmdBuilder.Capture(cmdBuilder.BuildMakepkgCmd(ctx, targetDir, "--printsrcinfo"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to generate .SRCINFO: %w - %s", err, stderr)
|
||||
}
|
||||
|
||||
if srcinfo == "" {
|
||||
return fmt.Errorf("generated .SRCINFO is empty, check your PKGBUILD for errors")
|
||||
}
|
||||
|
||||
if err := os.WriteFile(srcInfoDir, []byte(srcinfo), 0o600); err != nil {
|
||||
return fmt.Errorf("unable to write .SRCINFO: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("%w: %s", ErrNoBuildFiles, targetDir)
|
||||
}
|
||||
|
||||
func installLocalPKGBUILD(
|
||||
ctx context.Context,
|
||||
run *runtime.Runtime,
|
||||
cmdArgs *parser.Arguments,
|
||||
dbExecutor db.Executor,
|
||||
) error {
|
||||
aurCache := run.AURClient
|
||||
noCheck := strings.Contains(run.Cfg.MFlags, "--nocheck")
|
||||
|
||||
if len(cmdArgs.Targets) < 1 {
|
||||
return errors.New(gotext.Get("no target directories specified"))
|
||||
}
|
||||
|
||||
srcInfos := map[string]*gosrc.Srcinfo{}
|
||||
for _, targetDir := range cmdArgs.Targets {
|
||||
if err := srcinfoExists(ctx, run.CmdBuilder, targetDir); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pkgbuild, err := gosrc.ParseFile(filepath.Join(targetDir, ".SRCINFO"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", gotext.Get("failed to parse .SRCINFO"), err)
|
||||
}
|
||||
|
||||
srcInfos[targetDir] = pkgbuild
|
||||
}
|
||||
|
||||
grapher := dep.NewGrapher(dbExecutor, aurCache, false, settings.NoConfirm,
|
||||
cmdArgs.ExistsDouble("d", "nodeps"), noCheck, cmdArgs.ExistsArg("needed"),
|
||||
run.Logger.Child("grapher"))
|
||||
graph, err := grapher.GraphFromSrcInfos(ctx, nil, srcInfos)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
opService := sync.NewOperationService(ctx, dbExecutor, run)
|
||||
multiErr := &multierror.MultiError{}
|
||||
targets := graph.TopoSortedLayerMap(func(name string, ii *dep.InstallInfo) error {
|
||||
if ii.Source == dep.Missing {
|
||||
multiErr.Add(fmt.Errorf("%w: %s %s", ErrPackagesNotFound, name, ii.Version))
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if err := multiErr.Return(); err != nil {
|
||||
return err
|
||||
}
|
||||
return opService.Run(ctx, run, cmdArgs, targets, []string{})
|
||||
}
|
File diff suppressed because it is too large
Load Diff
147
main.go
147
main.go
@ -2,24 +2,21 @@ package main // import "github.com/Jguer/yay"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
|
||||
pacmanconf "github.com/Morganamilo/go-pacmanconf"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
"golang.org/x/term"
|
||||
|
||||
"github.com/Jguer/yay/v12/pkg/db/ialpm"
|
||||
"github.com/Jguer/yay/v12/pkg/runtime"
|
||||
"github.com/Jguer/yay/v12/pkg/settings"
|
||||
"github.com/Jguer/yay/v12/pkg/settings/parser"
|
||||
"github.com/Jguer/yay/v12/pkg/text"
|
||||
)
|
||||
|
||||
var (
|
||||
yayVersion = "12.0.4" // To be set by compiler.
|
||||
localePath = "/usr/share/locale" // To be set by compiler.
|
||||
"github.com/Jguer/yay/v11/pkg/db"
|
||||
"github.com/Jguer/yay/v11/pkg/db/ialpm"
|
||||
"github.com/Jguer/yay/v11/pkg/query"
|
||||
"github.com/Jguer/yay/v11/pkg/settings"
|
||||
"github.com/Jguer/yay/v11/pkg/settings/parser"
|
||||
"github.com/Jguer/yay/v11/pkg/text"
|
||||
)
|
||||
|
||||
func initGotext() {
|
||||
@ -28,12 +25,7 @@ func initGotext() {
|
||||
}
|
||||
|
||||
if lc := os.Getenv("LANGUAGE"); lc != "" {
|
||||
// Split LANGUAGE by ':' and prioritize the first locale
|
||||
// Should fix in gotext to support this
|
||||
locales := strings.Split(lc, ":")
|
||||
if len(locales) > 0 && locales[0] != "" {
|
||||
gotext.Configure(localePath, locales[0], "yay")
|
||||
}
|
||||
gotext.Configure(localePath, lc, "yay")
|
||||
} else if lc := os.Getenv("LC_ALL"); lc != "" {
|
||||
gotext.Configure(localePath, lc, "yay")
|
||||
} else if lc := os.Getenv("LC_MESSAGES"); lc != "" {
|
||||
@ -43,8 +35,61 @@ func initGotext() {
|
||||
}
|
||||
}
|
||||
|
||||
func initAlpm(cmdArgs *parser.Arguments, pacmanConfigPath string) (*pacmanconf.Config, bool, error) {
|
||||
root := "/"
|
||||
if value, _, exists := cmdArgs.GetArg("root", "r"); exists {
|
||||
root = value
|
||||
}
|
||||
|
||||
pacmanConf, stderr, err := pacmanconf.PacmanConf("--config", pacmanConfigPath, "--root", root)
|
||||
if err != nil {
|
||||
cmdErr := err
|
||||
if stderr != "" {
|
||||
cmdErr = fmt.Errorf("%s\n%s", err, stderr)
|
||||
}
|
||||
|
||||
return nil, false, cmdErr
|
||||
}
|
||||
|
||||
if dbPath, _, exists := cmdArgs.GetArg("dbpath", "b"); exists {
|
||||
pacmanConf.DBPath = dbPath
|
||||
}
|
||||
|
||||
if arch := cmdArgs.GetArgs("arch"); arch != nil {
|
||||
pacmanConf.Architecture = append(pacmanConf.Architecture, arch...)
|
||||
}
|
||||
|
||||
if ignoreArray := cmdArgs.GetArgs("ignore"); ignoreArray != nil {
|
||||
pacmanConf.IgnorePkg = append(pacmanConf.IgnorePkg, ignoreArray...)
|
||||
}
|
||||
|
||||
if ignoreGroupsArray := cmdArgs.GetArgs("ignoregroup"); ignoreGroupsArray != nil {
|
||||
pacmanConf.IgnoreGroup = append(pacmanConf.IgnoreGroup, ignoreGroupsArray...)
|
||||
}
|
||||
|
||||
if cacheArray := cmdArgs.GetArgs("cachedir"); cacheArray != nil {
|
||||
pacmanConf.CacheDir = cacheArray
|
||||
}
|
||||
|
||||
if gpgDir, _, exists := cmdArgs.GetArg("gpgdir"); exists {
|
||||
pacmanConf.GPGDir = gpgDir
|
||||
}
|
||||
|
||||
useColor := pacmanConf.Color && term.IsTerminal(int(os.Stdout.Fd()))
|
||||
|
||||
switch value, _, _ := cmdArgs.GetArg("color"); value {
|
||||
case "always":
|
||||
useColor = true
|
||||
case "auto":
|
||||
useColor = term.IsTerminal(int(os.Stdout.Fd()))
|
||||
case "never":
|
||||
useColor = false
|
||||
}
|
||||
|
||||
return pacmanConf, useColor, nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
fallbackLog := text.NewLogger(os.Stdout, os.Stderr, os.Stdin, false, "fallback")
|
||||
var (
|
||||
err error
|
||||
ctx = context.Background()
|
||||
@ -53,9 +98,8 @@ func main() {
|
||||
|
||||
defer func() {
|
||||
if rec := recover(); rec != nil {
|
||||
fallbackLog.Errorln("Panic occurred:", rec)
|
||||
fallbackLog.Errorln("Stack trace:", string(debug.Stack()))
|
||||
ret = 1
|
||||
text.Errorln(rec)
|
||||
debug.PrintStack()
|
||||
}
|
||||
|
||||
os.Exit(ret)
|
||||
@ -64,15 +108,13 @@ func main() {
|
||||
initGotext()
|
||||
|
||||
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()
|
||||
// Parse config
|
||||
cfg, err := settings.NewConfig(fallbackLog, configPath, yayVersion)
|
||||
config, err = settings.NewConfig(yayVersion)
|
||||
if err != nil {
|
||||
if str := err.Error(); str != "" {
|
||||
fallbackLog.Errorln(str)
|
||||
text.Errorln(str)
|
||||
}
|
||||
|
||||
ret = 1
|
||||
@ -80,17 +122,16 @@ func main() {
|
||||
return
|
||||
}
|
||||
|
||||
if errS := cfg.RunMigrations(fallbackLog,
|
||||
settings.DefaultMigrations(), configPath, yayVersion); errS != nil {
|
||||
fallbackLog.Errorln(errS)
|
||||
if errS := config.RunMigrations(
|
||||
settings.DefaultMigrations(), config.Runtime.ConfigPath); errS != nil {
|
||||
text.Errorln(errS)
|
||||
}
|
||||
|
||||
cmdArgs := parser.MakeArguments()
|
||||
|
||||
// Parse command line
|
||||
if err = cfg.ParseCommandLine(cmdArgs); err != nil {
|
||||
if err = config.ParseCommandLine(cmdArgs); err != nil {
|
||||
if str := err.Error(); str != "" {
|
||||
fallbackLog.Errorln(str)
|
||||
text.Errorln(str)
|
||||
}
|
||||
|
||||
ret = 1
|
||||
@ -98,17 +139,26 @@ func main() {
|
||||
return
|
||||
}
|
||||
|
||||
if cfg.SaveConfig {
|
||||
if errS := cfg.Save(configPath, yayVersion); errS != nil {
|
||||
fallbackLog.Errorln(errS)
|
||||
if config.Runtime.SaveConfig {
|
||||
if errS := config.Save(config.Runtime.ConfigPath); errS != nil {
|
||||
text.Errorln(errS)
|
||||
}
|
||||
}
|
||||
|
||||
// Build run
|
||||
run, err := runtime.NewRuntime(cfg, cmdArgs, yayVersion)
|
||||
if config.SeparateSources {
|
||||
config.Runtime.QueryBuilder = query.NewSourceQueryBuilder(config.SortBy,
|
||||
config.Runtime.Mode, config.SearchBy, config.BottomUp, config.SingleLineResults)
|
||||
} else {
|
||||
config.Runtime.QueryBuilder = query.NewMixedSourceQueryBuilder(config.SortBy,
|
||||
config.Runtime.Mode, config.SearchBy, config.BottomUp, config.SingleLineResults)
|
||||
}
|
||||
|
||||
var useColor bool
|
||||
|
||||
config.Runtime.PacmanConf, useColor, err = initAlpm(cmdArgs, config.PacmanConf)
|
||||
if err != nil {
|
||||
if str := err.Error(); str != "" {
|
||||
fallbackLog.Errorln(str)
|
||||
text.Errorln(str)
|
||||
}
|
||||
|
||||
ret = 1
|
||||
@ -116,10 +166,14 @@ func main() {
|
||||
return
|
||||
}
|
||||
|
||||
dbExecutor, err := ialpm.NewExecutor(run.PacmanConf, run.Logger.Child("db"))
|
||||
config.Runtime.CmdBuilder.SetPacmanDBPath(config.Runtime.PacmanConf.DBPath)
|
||||
|
||||
text.UseColor = useColor
|
||||
|
||||
dbExecutor, err := ialpm.NewExecutor(config.Runtime.PacmanConf)
|
||||
if err != nil {
|
||||
if str := err.Error(); str != "" {
|
||||
fallbackLog.Errorln(str)
|
||||
text.Errorln(str)
|
||||
}
|
||||
|
||||
ret = 1
|
||||
@ -129,20 +183,19 @@ func main() {
|
||||
|
||||
defer func() {
|
||||
if rec := recover(); rec != nil {
|
||||
fallbackLog.Errorln("Panic occurred in DB operation:", rec)
|
||||
fallbackLog.Errorln("Stack trace:", string(debug.Stack()))
|
||||
text.Errorln(rec)
|
||||
debug.PrintStack()
|
||||
}
|
||||
|
||||
dbExecutor.Cleanup()
|
||||
}()
|
||||
|
||||
if err = handleCmd(ctx, run, cmdArgs, dbExecutor); err != nil {
|
||||
if err = handleCmd(ctx, cmdArgs, db.Executor(dbExecutor)); err != nil {
|
||||
if str := err.Error(); str != "" {
|
||||
fallbackLog.Errorln(str)
|
||||
text.Errorln(str)
|
||||
}
|
||||
|
||||
exitError := &exec.ExitError{}
|
||||
if errors.As(err, &exitError) {
|
||||
if exitError, ok := err.(*exec.ExitError); ok {
|
||||
// mirror pacman exit code when applicable
|
||||
ret = exitError.ExitCode()
|
||||
return
|
||||
|
51
main_test.go
Normal file
51
main_test.go
Normal file
@ -0,0 +1,51 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/Morganamilo/go-pacmanconf"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/Jguer/yay/v11/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 := initAlpm(parser.MakeArguments(), "testdata/pacman.conf")
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, pacmanConf)
|
||||
assert.Equal(t, color, false)
|
||||
assert.EqualValues(t, expectedPacmanConf, pacmanConf)
|
||||
}
|
@ -1,82 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/Jguer/yay/v12/pkg/db/ialpm"
|
||||
"github.com/Jguer/yay/v12/pkg/dep"
|
||||
"github.com/Jguer/yay/v12/pkg/runtime"
|
||||
"github.com/Jguer/yay/v12/pkg/settings"
|
||||
"github.com/Jguer/yay/v12/pkg/settings/parser"
|
||||
"github.com/Jguer/yay/v12/pkg/text"
|
||||
|
||||
"github.com/Jguer/aur/metadata"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
)
|
||||
|
||||
func handleCmd(logger *text.Logger) error {
|
||||
cfg, err := settings.NewConfig(logger, settings.GetConfigPath(), "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmdArgs := parser.MakeArguments()
|
||||
if errP := cfg.ParseCommandLine(cmdArgs); errP != nil {
|
||||
return errP
|
||||
}
|
||||
|
||||
run, err := runtime.NewRuntime(cfg, cmdArgs, "1.0.0")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dbExecutor, err := ialpm.NewExecutor(run.PacmanConf, logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
aurCache, err := metadata.New(
|
||||
metadata.WithCacheFilePath(
|
||||
filepath.Join(cfg.BuildDir, "aur.json")))
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", gotext.Get("failed to retrieve aur Cache"), err)
|
||||
}
|
||||
|
||||
grapher := dep.NewGrapher(dbExecutor, aurCache, true, settings.NoConfirm,
|
||||
cmdArgs.ExistsDouble("d", "nodeps"), false, false,
|
||||
run.Logger.Child("grapher"))
|
||||
|
||||
return graphPackage(context.Background(), grapher, cmdArgs.Targets)
|
||||
}
|
||||
|
||||
func main() {
|
||||
fallbackLog := text.NewLogger(os.Stdout, os.Stderr, os.Stdin, false, "fallback")
|
||||
if err := handleCmd(fallbackLog); err != nil {
|
||||
fallbackLog.Errorln(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func graphPackage(
|
||||
ctx context.Context,
|
||||
grapher *dep.Grapher,
|
||||
targets []string,
|
||||
) error {
|
||||
if len(targets) != 1 {
|
||||
return errors.New(gotext.Get("only one target is allowed"))
|
||||
}
|
||||
|
||||
graph, err := grapher.GraphFromAUR(ctx, nil, []string{targets[0]})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stdout, graph.String())
|
||||
fmt.Fprintln(os.Stdout, "\nlayers map\n", graph.TopoSortedLayerMap(nil))
|
||||
|
||||
return nil
|
||||
}
|
@ -13,7 +13,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Jguer/yay/v12/pkg/db"
|
||||
"github.com/Jguer/yay/v11/pkg/db"
|
||||
)
|
||||
|
||||
type PkgSynchronizer interface {
|
||||
@ -117,7 +117,7 @@ func createAURList(ctx context.Context, client httpRequestDoer, aurURL string, o
|
||||
return nil
|
||||
}
|
||||
|
||||
// createRepoList appends Repo packages to completion cache.
|
||||
// CreatePackageList appends Repo packages to completion cache.
|
||||
func createRepoList(dbExecutor PkgSynchronizer, out io.Writer) error {
|
||||
for _, pkg := range dbExecutor.SyncPackages() {
|
||||
_, err := io.WriteString(out, pkg.Name()+"\t"+pkg.DB().Name()+"\n")
|
||||
|
@ -1,6 +1,3 @@
|
||||
//go:build !integration
|
||||
// +build !integration
|
||||
|
||||
package completion
|
||||
|
||||
import (
|
||||
@ -62,7 +59,7 @@ func Test_createAURList(t *testing.T) {
|
||||
returnErr: nil,
|
||||
}
|
||||
out := &bytes.Buffer{}
|
||||
err := createAURList(context.Background(), doer, "https://aur.archlinux.org", out)
|
||||
err := createAURList(context.TODO(), doer, "https://aur.archlinux.org", out)
|
||||
assert.NoError(t, err)
|
||||
gotOut := out.String()
|
||||
assert.Equal(t, expectPackageCompletion, gotOut)
|
||||
@ -79,7 +76,7 @@ func Test_createAURListHTTPError(t *testing.T) {
|
||||
}
|
||||
|
||||
out := &bytes.Buffer{}
|
||||
err := createAURList(context.Background(), doer, "https://aur.archlinux.org", out)
|
||||
err := createAURList(context.TODO(), doer, "https://aur.archlinux.org", out)
|
||||
assert.EqualError(t, err, "Not available")
|
||||
}
|
||||
|
||||
@ -94,6 +91,6 @@ func Test_createAURListStatusError(t *testing.T) {
|
||||
}
|
||||
|
||||
out := &bytes.Buffer{}
|
||||
err := createAURList(context.Background(), doer, "https://aur.archlinux.org", out)
|
||||
err := createAURList(context.TODO(), doer, "https://aur.archlinux.org", out)
|
||||
assert.EqualError(t, err, "invalid status code: 503")
|
||||
}
|
||||
|
@ -4,8 +4,6 @@ import (
|
||||
"time"
|
||||
|
||||
alpm "github.com/Jguer/go-alpm/v2"
|
||||
|
||||
"github.com/Jguer/yay/v12/pkg/text"
|
||||
)
|
||||
|
||||
type (
|
||||
@ -21,48 +19,33 @@ func VerCmp(v1, v2 string) int {
|
||||
|
||||
type Upgrade struct {
|
||||
Name string
|
||||
Base string
|
||||
Repository string
|
||||
LocalVersion string
|
||||
RemoteVersion string
|
||||
Reason alpm.PkgReason
|
||||
Extra string // Extra information to be displayed
|
||||
}
|
||||
|
||||
type SyncUpgrade struct {
|
||||
Package alpm.IPackage
|
||||
LocalVersion string
|
||||
Reason alpm.PkgReason
|
||||
}
|
||||
|
||||
type Executor interface {
|
||||
AlpmArchitectures() ([]string, error)
|
||||
BiggestPackages() []IPackage
|
||||
Cleanup()
|
||||
InstalledRemotePackageNames() []string
|
||||
InstalledRemotePackages() map[string]IPackage
|
||||
InstalledSyncPackageNames() []string
|
||||
IsCorrectVersionInstalled(string, string) bool
|
||||
LastBuildTime() time.Time
|
||||
LocalPackage(string) IPackage
|
||||
LocalPackages() []IPackage
|
||||
LocalSatisfierExists(string) bool
|
||||
PackageConflicts(IPackage) []Depend
|
||||
PackageDepends(IPackage) []Depend
|
||||
PackageGroups(IPackage) []string
|
||||
PackageOptionalDepends(IPackage) []Depend
|
||||
PackageProvides(IPackage) []Depend
|
||||
PackagesFromGroup(string) []IPackage
|
||||
PackagesFromGroupAndDB(string, string) ([]IPackage, error)
|
||||
RefreshHandle() error
|
||||
SyncUpgrades(enableDowngrade bool) (
|
||||
map[string]SyncUpgrade, error)
|
||||
RepoUpgrades(bool) ([]Upgrade, error)
|
||||
Repos() []string
|
||||
SatisfierFromDB(string, string) (IPackage, error)
|
||||
SatisfierFromDB(string, string) IPackage
|
||||
SyncPackage(string) IPackage
|
||||
SyncPackageFromDB(string, string) IPackage
|
||||
SyncPackages(...string) []IPackage
|
||||
SyncSatisfier(string) IPackage
|
||||
SyncSatisfierExists(string) bool
|
||||
|
||||
SetLogger(logger *text.Logger)
|
||||
}
|
||||
|
@ -11,9 +11,10 @@ import (
|
||||
pacmanconf "github.com/Morganamilo/go-pacmanconf"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
|
||||
"github.com/Jguer/yay/v12/pkg/db"
|
||||
"github.com/Jguer/yay/v12/pkg/settings"
|
||||
"github.com/Jguer/yay/v12/pkg/text"
|
||||
"github.com/Jguer/yay/v11/pkg/db"
|
||||
"github.com/Jguer/yay/v11/pkg/settings"
|
||||
"github.com/Jguer/yay/v11/pkg/text"
|
||||
"github.com/Jguer/yay/v11/pkg/upgrade"
|
||||
)
|
||||
|
||||
type AlpmExecutor struct {
|
||||
@ -22,31 +23,16 @@ type AlpmExecutor struct {
|
||||
syncDB alpm.IDBList
|
||||
syncDBsCache []alpm.IDB
|
||||
conf *pacmanconf.Config
|
||||
log *text.Logger
|
||||
|
||||
installedRemotePkgNames []string
|
||||
installedRemotePkgMap map[string]alpm.IPackage
|
||||
installedSyncPkgNames []string
|
||||
}
|
||||
|
||||
func NewExecutor(pacmanConf *pacmanconf.Config, logger *text.Logger) (*AlpmExecutor, error) {
|
||||
ae := &AlpmExecutor{
|
||||
handle: nil,
|
||||
localDB: nil,
|
||||
syncDB: nil,
|
||||
syncDBsCache: []alpm.IDB{},
|
||||
conf: pacmanConf,
|
||||
log: logger,
|
||||
installedRemotePkgNames: nil,
|
||||
installedRemotePkgMap: nil,
|
||||
installedSyncPkgNames: nil,
|
||||
}
|
||||
func NewExecutor(pacmanConf *pacmanconf.Config) (*AlpmExecutor, error) {
|
||||
ae := &AlpmExecutor{conf: pacmanConf}
|
||||
|
||||
if err := ae.RefreshHandle(); err != nil {
|
||||
err := ae.RefreshHandle()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var err error
|
||||
ae.localDB, err = ae.handle.LocalDB()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -143,14 +129,12 @@ func configureAlpm(pacmanConf *pacmanconf.Config, alpmHandle *alpm.Handle) error
|
||||
return alpmHandle.SetCheckSpace(pacmanConf.CheckSpace)
|
||||
}
|
||||
|
||||
func (ae *AlpmExecutor) logCallback() func(level alpm.LogLevel, str string) {
|
||||
return func(level alpm.LogLevel, str string) {
|
||||
switch level {
|
||||
case alpm.LogWarning:
|
||||
ae.log.Warn(str)
|
||||
case alpm.LogError:
|
||||
ae.log.Error(str)
|
||||
}
|
||||
func logCallback(level alpm.LogLevel, str string) {
|
||||
switch level {
|
||||
case alpm.LogWarning:
|
||||
text.Warn(str)
|
||||
case alpm.LogError:
|
||||
text.Error(str)
|
||||
}
|
||||
}
|
||||
|
||||
@ -176,7 +160,7 @@ func (ae *AlpmExecutor) questionCallback() func(question alpm.QuestionAny) {
|
||||
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
|
||||
|
||||
@ -188,28 +172,28 @@ func (ae *AlpmExecutor) questionCallback() func(question alpm.QuestionAny) {
|
||||
if dbName != thisDB {
|
||||
dbName = thisDB
|
||||
str += "\n"
|
||||
str += ae.log.SprintOperationInfo(gotext.Get("Repository"), " ", dbName, "\n ")
|
||||
str += text.SprintOperationInfo(gotext.Get("Repository"), " ", dbName, "\n ")
|
||||
}
|
||||
str += fmt.Sprintf("%d) %s ", size, pkg.Name())
|
||||
size++
|
||||
return nil
|
||||
})
|
||||
|
||||
ae.log.OperationInfoln(str)
|
||||
text.OperationInfoln(str)
|
||||
|
||||
for {
|
||||
ae.log.Println(gotext.Get("\nEnter a number (default=1): "))
|
||||
fmt.Println(gotext.Get("\nEnter a number (default=1): "))
|
||||
|
||||
// TODO: reenable noconfirm
|
||||
if settings.NoConfirm {
|
||||
ae.log.Println()
|
||||
fmt.Println()
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
numberBuf, err := ae.log.GetInput("", false)
|
||||
numberBuf, err := text.GetInput("", false)
|
||||
if err != nil {
|
||||
ae.log.Errorln(err)
|
||||
text.Errorln(err)
|
||||
break
|
||||
}
|
||||
|
||||
@ -219,12 +203,12 @@ func (ae *AlpmExecutor) questionCallback() func(question alpm.QuestionAny) {
|
||||
|
||||
num, err := strconv.Atoi(numberBuf)
|
||||
if err != nil {
|
||||
ae.log.Errorln(gotext.Get("invalid number: %s", numberBuf))
|
||||
text.Errorln(gotext.Get("invalid number: %s", numberBuf))
|
||||
continue
|
||||
}
|
||||
|
||||
if num < 1 || num > size {
|
||||
ae.log.Errorln(gotext.Get("invalid value: %d is not between %d and %d", num, 1, size))
|
||||
text.Errorln(gotext.Get("invalid value: %d is not between %d and %d", num, 1, size))
|
||||
continue
|
||||
}
|
||||
|
||||
@ -252,7 +236,7 @@ func (ae *AlpmExecutor) RefreshHandle() error {
|
||||
}
|
||||
|
||||
alpmSetQuestionCallback(alpmHandle, ae.questionCallback())
|
||||
alpmSetLogCallback(alpmHandle, ae.logCallback())
|
||||
alpmSetLogCallback(alpmHandle, logCallback)
|
||||
ae.handle = alpmHandle
|
||||
ae.syncDBsCache = nil
|
||||
|
||||
@ -311,22 +295,6 @@ func (ae *AlpmExecutor) PackagesFromGroup(groupName string) []alpm.IPackage {
|
||||
return groupPackages
|
||||
}
|
||||
|
||||
func (ae *AlpmExecutor) PackagesFromGroupAndDB(groupName, dbName string) ([]alpm.IPackage, error) {
|
||||
singleDBList, err := ae.handle.SyncDBListByDBName(dbName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
groupPackages := []alpm.IPackage{}
|
||||
_ = singleDBList.FindGroupPkgs(groupName).ForEach(func(pkg alpm.IPackage) error {
|
||||
groupPackages = append(groupPackages, pkg)
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return groupPackages, nil
|
||||
}
|
||||
|
||||
func (ae *AlpmExecutor) LocalPackages() []alpm.IPackage {
|
||||
localPackages := []alpm.IPackage{}
|
||||
_ = ae.localDB.PkgCache().ForEach(func(pkg alpm.IPackage) error {
|
||||
@ -385,27 +353,18 @@ func (ae *AlpmExecutor) SyncPackage(pkgName string) alpm.IPackage {
|
||||
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)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return singleDB.Pkg(pkgName)
|
||||
}
|
||||
|
||||
func (ae *AlpmExecutor) SatisfierFromDB(pkgName, dbName string) (alpm.IPackage, error) {
|
||||
singleDBList, err := ae.handle.SyncDBListByDBName(dbName)
|
||||
foundPkg, err := singleDB.PkgCache().FindSatisfier(pkgName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil
|
||||
}
|
||||
|
||||
foundPkg, err := singleDBList.FindSatisfier(pkgName)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return foundPkg, nil
|
||||
return foundPkg
|
||||
}
|
||||
|
||||
func (ae *AlpmExecutor) PackageDepends(pkg alpm.IPackage) []alpm.Depend {
|
||||
@ -423,6 +382,11 @@ func (ae *AlpmExecutor) PackageProvides(pkg alpm.IPackage) []alpm.Depend {
|
||||
return alpmPackage.Provides().Slice()
|
||||
}
|
||||
|
||||
func (ae *AlpmExecutor) PackageConflicts(pkg alpm.IPackage) []alpm.Depend {
|
||||
alpmPackage := pkg.(*alpm.Package)
|
||||
return alpmPackage.Conflicts().Slice()
|
||||
}
|
||||
|
||||
func (ae *AlpmExecutor) PackageGroups(pkg alpm.IPackage) []string {
|
||||
alpmPackage := pkg.(*alpm.Package)
|
||||
return alpmPackage.Groups().Slice()
|
||||
@ -430,19 +394,18 @@ func (ae *AlpmExecutor) PackageGroups(pkg alpm.IPackage) []string {
|
||||
|
||||
// upRepo gathers local packages and checks if they have new versions.
|
||||
// Output: Upgrade type package list.
|
||||
func (ae *AlpmExecutor) SyncUpgrades(enableDowngrade bool) (
|
||||
map[string]db.SyncUpgrade, error,
|
||||
) {
|
||||
ups := map[string]db.SyncUpgrade{}
|
||||
func (ae *AlpmExecutor) RepoUpgrades(enableDowngrade bool) ([]db.Upgrade, error) {
|
||||
var errReturn error
|
||||
|
||||
slice := []db.Upgrade{}
|
||||
|
||||
localDB, errDB := ae.handle.LocalDB()
|
||||
if errDB != nil {
|
||||
return ups, errDB
|
||||
return slice, errDB
|
||||
}
|
||||
|
||||
if err := ae.handle.TransInit(alpm.TransFlagNoLock); err != nil {
|
||||
return ups, err
|
||||
return slice, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
@ -450,7 +413,7 @@ func (ae *AlpmExecutor) SyncUpgrades(enableDowngrade bool) (
|
||||
}()
|
||||
|
||||
if err := ae.handle.SyncSysupgrade(enableDowngrade); err != nil {
|
||||
return ups, err
|
||||
return slice, err
|
||||
}
|
||||
|
||||
_ = ae.handle.TransGetAdd().ForEach(func(pkg alpm.IPackage) error {
|
||||
@ -462,16 +425,17 @@ func (ae *AlpmExecutor) SyncUpgrades(enableDowngrade bool) (
|
||||
reason = localPkg.Reason()
|
||||
}
|
||||
|
||||
ups[pkg.Name()] = db.SyncUpgrade{
|
||||
Package: pkg,
|
||||
Reason: reason,
|
||||
LocalVersion: localVer,
|
||||
}
|
||||
|
||||
slice = append(slice, upgrade.Upgrade{
|
||||
Name: pkg.Name(),
|
||||
Repository: pkg.DB().Name(),
|
||||
LocalVersion: localVer,
|
||||
RemoteVersion: pkg.Version(),
|
||||
Reason: reason,
|
||||
})
|
||||
return nil
|
||||
})
|
||||
|
||||
return ups, errReturn
|
||||
return slice, errReturn
|
||||
}
|
||||
|
||||
func (ae *AlpmExecutor) BiggestPackages() []alpm.IPackage {
|
||||
|
@ -1,18 +1,11 @@
|
||||
//go:build !integration
|
||||
// +build !integration
|
||||
|
||||
package ialpm
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
alpm "github.com/Jguer/go-alpm/v2"
|
||||
"github.com/Morganamilo/go-pacmanconf"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/Jguer/yay/v12/pkg/text"
|
||||
)
|
||||
|
||||
func TestAlpmExecutor(t *testing.T) {
|
||||
@ -48,7 +41,7 @@ func TestAlpmExecutor(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
aExec, err := NewExecutor(pacmanConf, text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), false, "test"))
|
||||
aExec, err := NewExecutor(pacmanConf)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.NotNil(t, aExec.conf)
|
||||
|
@ -1,54 +0,0 @@
|
||||
package ialpm
|
||||
|
||||
import (
|
||||
alpm "github.com/Jguer/go-alpm/v2"
|
||||
|
||||
"github.com/Jguer/yay/v12/pkg/text"
|
||||
)
|
||||
|
||||
// GetPackageNamesBySource returns package names with and without correspondence in SyncDBS respectively.
|
||||
func (ae *AlpmExecutor) getPackageNamesBySource() {
|
||||
if ae.installedRemotePkgMap == nil {
|
||||
ae.installedRemotePkgMap = map[string]alpm.IPackage{}
|
||||
}
|
||||
for _, localpkg := range ae.LocalPackages() {
|
||||
pkgName := localpkg.Name()
|
||||
if ae.SyncPackage(pkgName) != nil {
|
||||
ae.installedSyncPkgNames = append(ae.installedSyncPkgNames, pkgName)
|
||||
} else {
|
||||
ae.installedRemotePkgNames = append(ae.installedRemotePkgNames, pkgName)
|
||||
ae.installedRemotePkgMap[pkgName] = localpkg
|
||||
}
|
||||
}
|
||||
|
||||
ae.log.Debugln("populating db executor package caches.",
|
||||
"sync_len", len(ae.installedSyncPkgNames), "remote_len", len(ae.installedRemotePkgNames))
|
||||
}
|
||||
|
||||
func (ae *AlpmExecutor) InstalledRemotePackages() map[string]alpm.IPackage {
|
||||
if ae.installedRemotePkgMap == nil {
|
||||
ae.getPackageNamesBySource()
|
||||
}
|
||||
|
||||
return ae.installedRemotePkgMap
|
||||
}
|
||||
|
||||
func (ae *AlpmExecutor) InstalledRemotePackageNames() []string {
|
||||
if ae.installedRemotePkgNames == nil {
|
||||
ae.getPackageNamesBySource()
|
||||
}
|
||||
|
||||
return ae.installedRemotePkgNames
|
||||
}
|
||||
|
||||
func (ae *AlpmExecutor) InstalledSyncPackageNames() []string {
|
||||
if ae.installedSyncPkgNames == nil {
|
||||
ae.getPackageNamesBySource()
|
||||
}
|
||||
|
||||
return ae.installedSyncPkgNames
|
||||
}
|
||||
|
||||
func (ae *AlpmExecutor) SetLogger(logger *text.Logger) {
|
||||
ae.log = logger
|
||||
}
|
@ -3,8 +3,7 @@ package mock
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/Jguer/yay/v12/pkg/db"
|
||||
"github.com/Jguer/yay/v12/pkg/text"
|
||||
"github.com/Jguer/yay/v11/pkg/db"
|
||||
|
||||
"github.com/Jguer/go-alpm/v2"
|
||||
)
|
||||
@ -15,200 +14,92 @@ type (
|
||||
Upgrade = db.Upgrade
|
||||
)
|
||||
|
||||
type DBExecutor struct {
|
||||
db.Executor
|
||||
AlpmArchitecturesFn func() ([]string, error)
|
||||
InstalledRemotePackageNamesFn func() []string
|
||||
InstalledRemotePackagesFn func() map[string]IPackage
|
||||
IsCorrectVersionInstalledFn func(string, string) bool
|
||||
LocalPackageFn func(string) IPackage
|
||||
LocalPackagesFn func() []IPackage
|
||||
LocalSatisfierExistsFn func(string) bool
|
||||
PackageDependsFn func(IPackage) []Depend
|
||||
PackageOptionalDependsFn func(alpm.IPackage) []alpm.Depend
|
||||
PackageProvidesFn func(IPackage) []Depend
|
||||
PackagesFromGroupFn func(string) []IPackage
|
||||
PackagesFromGroupAndDBFn func(string, string) ([]IPackage, error)
|
||||
RefreshHandleFn func() error
|
||||
ReposFn func() []string
|
||||
SyncPackageFn func(string) IPackage
|
||||
SyncPackagesFn func(...string) []IPackage
|
||||
SyncSatisfierFn func(string) IPackage
|
||||
SatisfierFromDBFn func(string, string) (IPackage, error)
|
||||
SyncUpgradesFn func(bool) (map[string]db.SyncUpgrade, error)
|
||||
SetLoggerFn func(*text.Logger)
|
||||
}
|
||||
type DBExecutor struct{}
|
||||
|
||||
func (t *DBExecutor) InstalledRemotePackageNames() []string {
|
||||
if t.InstalledRemotePackageNamesFn != nil {
|
||||
return t.InstalledRemotePackageNamesFn()
|
||||
}
|
||||
func (t DBExecutor) AlpmArchitectures() ([]string, error) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (t *DBExecutor) InstalledRemotePackages() map[string]IPackage {
|
||||
if t.InstalledRemotePackagesFn != nil {
|
||||
return t.InstalledRemotePackagesFn()
|
||||
}
|
||||
func (t DBExecutor) BiggestPackages() []IPackage {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (t *DBExecutor) AlpmArchitectures() ([]string, error) {
|
||||
if t.AlpmArchitecturesFn != nil {
|
||||
return t.AlpmArchitecturesFn()
|
||||
}
|
||||
func (t DBExecutor) Cleanup() {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (t *DBExecutor) BiggestPackages() []IPackage {
|
||||
func (t DBExecutor) IsCorrectVersionInstalled(s, s2 string) bool {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (t *DBExecutor) Cleanup() {
|
||||
func (t DBExecutor) LastBuildTime() time.Time {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (t *DBExecutor) IsCorrectVersionInstalled(s, s2 string) bool {
|
||||
if t.IsCorrectVersionInstalledFn != nil {
|
||||
return t.IsCorrectVersionInstalledFn(s, s2)
|
||||
}
|
||||
func (t DBExecutor) LocalPackage(s string) IPackage {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t DBExecutor) LocalPackages() []IPackage {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (t *DBExecutor) LastBuildTime() time.Time {
|
||||
func (t DBExecutor) LocalSatisfierExists(s string) bool {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (t *DBExecutor) LocalPackage(s string) IPackage {
|
||||
if t.LocalPackageFn != nil {
|
||||
return t.LocalPackageFn(s)
|
||||
}
|
||||
|
||||
func (t DBExecutor) PackageConflicts(iPackage IPackage) []Depend {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (t *DBExecutor) LocalPackages() []IPackage {
|
||||
if t.LocalPackagesFn != nil {
|
||||
return t.LocalPackagesFn()
|
||||
}
|
||||
|
||||
func (t DBExecutor) PackageDepends(iPackage IPackage) []Depend {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (t *DBExecutor) LocalSatisfierExists(s string) bool {
|
||||
if t.LocalSatisfierExistsFn != nil {
|
||||
return t.LocalSatisfierExistsFn(s)
|
||||
}
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (t *DBExecutor) PackageConflicts(iPackage IPackage) []Depend {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (t *DBExecutor) PackageDepends(iPackage IPackage) []Depend {
|
||||
if t.PackageDependsFn != nil {
|
||||
return t.PackageDependsFn(iPackage)
|
||||
}
|
||||
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (t *DBExecutor) PackageGroups(iPackage IPackage) []string {
|
||||
func (t DBExecutor) PackageGroups(iPackage IPackage) []string {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
func (t *DBExecutor) PackageOptionalDepends(iPackage IPackage) []Depend {
|
||||
if t.PackageOptionalDependsFn != nil {
|
||||
return t.PackageOptionalDependsFn(iPackage)
|
||||
}
|
||||
|
||||
func (t DBExecutor) PackageOptionalDepends(iPackage IPackage) []Depend {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (t *DBExecutor) PackageProvides(iPackage IPackage) []Depend {
|
||||
if t.PackageProvidesFn != nil {
|
||||
return t.PackageProvidesFn(iPackage)
|
||||
}
|
||||
|
||||
func (t DBExecutor) PackageProvides(iPackage IPackage) []Depend {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (t *DBExecutor) PackagesFromGroup(s string) []IPackage {
|
||||
if t.PackagesFromGroupFn != nil {
|
||||
return t.PackagesFromGroupFn(s)
|
||||
}
|
||||
|
||||
func (t DBExecutor) PackagesFromGroup(s string) []IPackage {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (t *DBExecutor) PackagesFromGroupAndDB(s, s2 string) ([]IPackage, error) {
|
||||
if t.PackagesFromGroupAndDBFn != nil {
|
||||
return t.PackagesFromGroupAndDBFn(s, s2)
|
||||
}
|
||||
func (t DBExecutor) RefreshHandle() error {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (t *DBExecutor) RefreshHandle() error {
|
||||
if t.RefreshHandleFn != nil {
|
||||
return t.RefreshHandleFn()
|
||||
}
|
||||
func (t DBExecutor) RepoUpgrades(b bool) ([]Upgrade, error) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (t *DBExecutor) SyncUpgrades(b bool) (map[string]db.SyncUpgrade, error) {
|
||||
if t.SyncUpgradesFn != nil {
|
||||
return t.SyncUpgradesFn(b)
|
||||
}
|
||||
func (t DBExecutor) Repos() []string {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (t *DBExecutor) Repos() []string {
|
||||
if t.ReposFn != nil {
|
||||
return t.ReposFn()
|
||||
}
|
||||
func (t DBExecutor) SatisfierFromDB(s, s2 string) IPackage {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (t *DBExecutor) SatisfierFromDB(s, s2 string) (IPackage, error) {
|
||||
if t.SatisfierFromDBFn != nil {
|
||||
return t.SatisfierFromDBFn(s, s2)
|
||||
}
|
||||
func (t DBExecutor) SyncPackage(s string) IPackage {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (t *DBExecutor) SyncPackage(s string) IPackage {
|
||||
if t.SyncPackageFn != nil {
|
||||
return t.SyncPackageFn(s)
|
||||
}
|
||||
func (t DBExecutor) SyncPackages(s ...string) []IPackage {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (t *DBExecutor) SyncPackages(s ...string) []IPackage {
|
||||
if t.SyncPackagesFn != nil {
|
||||
return t.SyncPackagesFn(s...)
|
||||
}
|
||||
func (t DBExecutor) SyncSatisfier(s string) IPackage {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (t *DBExecutor) SyncSatisfier(s string) IPackage {
|
||||
if t.SyncSatisfierFn != nil {
|
||||
return t.SyncSatisfierFn(s)
|
||||
}
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (t *DBExecutor) SyncSatisfierExists(s string) bool {
|
||||
if t.SyncSatisfierFn != nil {
|
||||
return t.SyncSatisfierFn(s) != nil
|
||||
}
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (t *DBExecutor) SetLogger(logger *text.Logger) {
|
||||
if t.SetLoggerFn != nil {
|
||||
t.SetLoggerFn(logger)
|
||||
return
|
||||
}
|
||||
func (t DBExecutor) SyncSatisfierExists(s string) bool {
|
||||
panic("implement me")
|
||||
}
|
||||
|
@ -6,26 +6,6 @@ import (
|
||||
alpm "github.com/Jguer/go-alpm/v2"
|
||||
)
|
||||
|
||||
type DependList struct {
|
||||
Depends []Depend
|
||||
}
|
||||
|
||||
func (d DependList) Slice() []alpm.Depend {
|
||||
return d.Depends
|
||||
}
|
||||
|
||||
func (d DependList) ForEach(f func(*alpm.Depend) error) error {
|
||||
for i := range d.Depends {
|
||||
dep := &d.Depends[i]
|
||||
err := f(dep)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type Package struct {
|
||||
PBase string
|
||||
PBuildDate time.Time
|
||||
@ -37,8 +17,6 @@ type Package struct {
|
||||
PSize int64
|
||||
PVersion string
|
||||
PReason alpm.PkgReason
|
||||
PDepends alpm.IDependList
|
||||
PProvides alpm.IDependList
|
||||
}
|
||||
|
||||
func (p *Package) Base() string {
|
||||
@ -82,137 +60,131 @@ func (p *Package) Reason() alpm.PkgReason {
|
||||
}
|
||||
|
||||
func (p *Package) FileName() string {
|
||||
panic("not implemented")
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
func (p *Package) Base64Signature() string {
|
||||
panic("not implemented")
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
func (p *Package) Validation() alpm.Validation {
|
||||
panic("not implemented")
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
// Architecture returns the package target Architecture.
|
||||
func (p *Package) Architecture() string {
|
||||
panic("not implemented")
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
// Backup returns a list of package backups.
|
||||
func (p *Package) Backup() alpm.BackupList {
|
||||
panic("not implemented")
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
// Conflicts returns the conflicts of the package as a DependList.
|
||||
func (p *Package) Conflicts() alpm.IDependList {
|
||||
panic("not implemented")
|
||||
func (p *Package) Conflicts() alpm.DependList {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
// Depends returns the package's dependency list.
|
||||
func (p *Package) Depends() alpm.IDependList {
|
||||
if p.PDepends != nil {
|
||||
return p.PDepends
|
||||
}
|
||||
return alpm.DependList{}
|
||||
func (p *Package) Depends() alpm.DependList {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
// Depends returns the package's optional dependency list.
|
||||
func (p *Package) OptionalDepends() alpm.IDependList {
|
||||
panic("not implemented")
|
||||
func (p *Package) OptionalDepends() alpm.DependList {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
// Depends returns the package's check dependency list.
|
||||
func (p *Package) CheckDepends() alpm.IDependList {
|
||||
panic("not implemented")
|
||||
func (p *Package) CheckDepends() alpm.DependList {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
// Depends returns the package's make dependency list.
|
||||
func (p *Package) MakeDepends() alpm.IDependList {
|
||||
panic("not implemented")
|
||||
func (p *Package) MakeDepends() alpm.DependList {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
// Files returns the file list of the package.
|
||||
func (p *Package) Files() []alpm.File {
|
||||
panic("not implemented")
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
// ContainsFile checks if the path is in the package filelist.
|
||||
func (p *Package) ContainsFile(path string) (alpm.File, error) {
|
||||
panic("not implemented")
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
// Groups returns the groups the package belongs to.
|
||||
func (p *Package) Groups() alpm.StringList {
|
||||
panic("not implemented")
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
// InstallDate returns the package install date.
|
||||
func (p *Package) InstallDate() time.Time {
|
||||
panic("not implemented")
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
// Licenses returns the package license list.
|
||||
func (p *Package) Licenses() alpm.StringList {
|
||||
panic("not implemented")
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
// SHA256Sum returns package SHA256Sum.
|
||||
func (p *Package) SHA256Sum() string {
|
||||
panic("not implemented")
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
// MD5Sum returns package MD5Sum.
|
||||
func (p *Package) MD5Sum() string {
|
||||
panic("not implemented")
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
// Packager returns package packager name.
|
||||
func (p *Package) Packager() string {
|
||||
panic("not implemented")
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
// Provides returns DependList of packages provides by package.
|
||||
func (p *Package) Provides() alpm.IDependList {
|
||||
if p.PProvides == nil {
|
||||
return alpm.DependList{}
|
||||
}
|
||||
return p.PProvides
|
||||
func (p *Package) Provides() alpm.DependList {
|
||||
return alpm.DependList{}
|
||||
}
|
||||
|
||||
// Origin returns package origin.
|
||||
func (p *Package) Origin() alpm.PkgFrom {
|
||||
panic("not implemented")
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
// Replaces returns a DependList with the packages this package replaces.
|
||||
func (p *Package) Replaces() alpm.IDependList {
|
||||
panic("not implemented")
|
||||
func (p *Package) Replaces() alpm.DependList {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
// URL returns the upstream URL of the package.
|
||||
func (p *Package) URL() string {
|
||||
panic("not implemented")
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
// ComputeRequiredBy returns the names of reverse dependencies of a package.
|
||||
func (p *Package) ComputeRequiredBy() []string {
|
||||
panic("not implemented")
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
// ComputeOptionalFor returns the names of packages that optionally
|
||||
// require the given package.
|
||||
func (p *Package) ComputeOptionalFor() []string {
|
||||
panic("not implemented")
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
// SyncNewVersion checks if there is a new version of the
|
||||
// package in a given DBlist.
|
||||
func (p *Package) SyncNewVersion(l alpm.IDBList) alpm.IPackage {
|
||||
panic("not implemented")
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
func (p *Package) Type() string {
|
||||
panic("not implemented")
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
type DB struct {
|
||||
|
@ -1,15 +0,0 @@
|
||||
package db
|
||||
|
||||
func ArchIsSupported(alpmArch []string, arch string) bool {
|
||||
if arch == "any" {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, a := range alpmArch {
|
||||
if a == arch {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
68
pkg/dep/base.go
Normal file
68
pkg/dep/base.go
Normal file
@ -0,0 +1,68 @@
|
||||
package dep
|
||||
|
||||
import (
|
||||
aur "github.com/Jguer/yay/v11/pkg/query"
|
||||
"github.com/Jguer/yay/v11/pkg/stringset"
|
||||
)
|
||||
|
||||
// Base is an AUR base package.
|
||||
type Base []*aur.Pkg
|
||||
|
||||
// Pkgbase returns the first base package.
|
||||
func (b Base) Pkgbase() string {
|
||||
return b[0].PackageBase
|
||||
}
|
||||
|
||||
// Version returns the first base package version.
|
||||
func (b Base) Version() string {
|
||||
return b[0].Version
|
||||
}
|
||||
|
||||
// URLPath returns the first base package URL.
|
||||
func (b Base) URLPath() string {
|
||||
return b[0].URLPath
|
||||
}
|
||||
|
||||
func (b Base) AnyIsInSet(set stringset.StringSet) bool {
|
||||
for _, pkg := range b {
|
||||
if set.Get(pkg.Name) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Packages foo and bar from a pkgbase named base would print like so:
|
||||
// base (foo bar).
|
||||
func (b Base) String() string {
|
||||
pkg := b[0]
|
||||
str := pkg.PackageBase
|
||||
|
||||
if len(b) > 1 || pkg.PackageBase != pkg.Name {
|
||||
str2 := " ("
|
||||
for _, split := range b {
|
||||
str2 += split.Name + " "
|
||||
}
|
||||
|
||||
str2 = str2[:len(str2)-1] + ")"
|
||||
|
||||
str += str2
|
||||
}
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
func GetBases(pkgs []*aur.Pkg) []Base {
|
||||
basesMap := make(map[string]Base)
|
||||
for _, pkg := range pkgs {
|
||||
basesMap[pkg.PackageBase] = append(basesMap[pkg.PackageBase], pkg)
|
||||
}
|
||||
|
||||
bases := make([]Base, 0, len(basesMap))
|
||||
for _, base := range basesMap {
|
||||
bases = append(bases, base)
|
||||
}
|
||||
|
||||
return bases
|
||||
}
|
@ -3,10 +3,43 @@ package dep
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/Jguer/yay/v12/pkg/db"
|
||||
aur "github.com/Jguer/yay/v12/pkg/query"
|
||||
"github.com/Jguer/yay/v11/pkg/db"
|
||||
aur "github.com/Jguer/yay/v11/pkg/query"
|
||||
"github.com/Jguer/yay/v11/pkg/text"
|
||||
)
|
||||
|
||||
type providers struct {
|
||||
lookfor string
|
||||
Pkgs []*aur.Pkg
|
||||
}
|
||||
|
||||
func makeProviders(name string) providers {
|
||||
return providers{
|
||||
name,
|
||||
make([]*aur.Pkg, 0),
|
||||
}
|
||||
}
|
||||
|
||||
func (q providers) Len() int {
|
||||
return len(q.Pkgs)
|
||||
}
|
||||
|
||||
func (q providers) Less(i, j int) bool {
|
||||
if q.lookfor == q.Pkgs[i].Name {
|
||||
return true
|
||||
}
|
||||
|
||||
if q.lookfor == q.Pkgs[j].Name {
|
||||
return false
|
||||
}
|
||||
|
||||
return text.LessRunes([]rune(q.Pkgs[i].Name), []rune(q.Pkgs[j].Name))
|
||||
}
|
||||
|
||||
func (q providers) Swap(i, j int) {
|
||||
q.Pkgs[i], q.Pkgs[j] = q.Pkgs[j], q.Pkgs[i]
|
||||
}
|
||||
|
||||
func splitDep(dep string) (pkg, mod, ver string) {
|
||||
split := strings.FieldsFunc(dep, func(c rune) bool {
|
||||
match := c == '>' || c == '<' || c == '='
|
||||
@ -47,7 +80,7 @@ func provideSatisfies(provide, dep, pkgVersion string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Unversioned provides can not satisfy a versioned dep
|
||||
// Unversioned provieds can not satisfy a versioned dep
|
||||
if provideMod == "" && depMod != "" {
|
||||
provideVersion = pkgVersion // Example package: pagure
|
||||
}
|
||||
@ -85,3 +118,17 @@ func satisfiesAur(dep string, pkg *aur.Pkg) bool {
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func satisfiesRepo(dep string, pkg db.IPackage, dbExecutor db.Executor) bool {
|
||||
if pkgSatisfies(pkg.Name(), pkg.Version(), dep) {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, provided := range dbExecutor.PackageProvides(pkg) {
|
||||
if provideSatisfies(provided.String(), dep, pkg.Version()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
324
pkg/dep/depCheck.go
Normal file
324
pkg/dep/depCheck.go
Normal file
@ -0,0 +1,324 @@
|
||||
package dep
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/leonelquinteros/gotext"
|
||||
|
||||
"github.com/Jguer/yay/v11/pkg/stringset"
|
||||
"github.com/Jguer/yay/v11/pkg/text"
|
||||
)
|
||||
|
||||
func (dp *Pool) checkInnerConflict(name, conflict string, conflicts stringset.MapStringSet) {
|
||||
for _, pkg := range dp.Aur {
|
||||
if pkg.Name == name {
|
||||
continue
|
||||
}
|
||||
|
||||
if satisfiesAur(conflict, pkg) {
|
||||
conflicts.Add(name, pkg.Name)
|
||||
}
|
||||
}
|
||||
|
||||
for _, pkg := range dp.Repo {
|
||||
if pkg.Name() == name {
|
||||
continue
|
||||
}
|
||||
|
||||
if satisfiesRepo(conflict, pkg, dp.AlpmExecutor) {
|
||||
conflicts.Add(name, pkg.Name())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (dp *Pool) checkForwardConflict(name, conflict string, conflicts stringset.MapStringSet) {
|
||||
for _, pkg := range dp.AlpmExecutor.LocalPackages() {
|
||||
if pkg.Name() == name || dp.hasPackage(pkg.Name()) {
|
||||
continue
|
||||
}
|
||||
|
||||
if satisfiesRepo(conflict, pkg, dp.AlpmExecutor) {
|
||||
n := pkg.Name()
|
||||
if n != conflict {
|
||||
n += " (" + conflict + ")"
|
||||
}
|
||||
|
||||
conflicts.Add(name, n)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (dp *Pool) checkReverseConflict(name, conflict string, conflicts stringset.MapStringSet) {
|
||||
for _, pkg := range dp.Aur {
|
||||
if pkg.Name == name {
|
||||
continue
|
||||
}
|
||||
|
||||
if satisfiesAur(conflict, pkg) {
|
||||
if name != conflict {
|
||||
name += " (" + conflict + ")"
|
||||
}
|
||||
|
||||
conflicts.Add(pkg.Name, name)
|
||||
}
|
||||
}
|
||||
|
||||
for _, pkg := range dp.Repo {
|
||||
if pkg.Name() == name {
|
||||
continue
|
||||
}
|
||||
|
||||
if satisfiesRepo(conflict, pkg, dp.AlpmExecutor) {
|
||||
if name != conflict {
|
||||
name += " (" + conflict + ")"
|
||||
}
|
||||
|
||||
conflicts.Add(pkg.Name(), name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (dp *Pool) checkInnerConflicts(conflicts stringset.MapStringSet) {
|
||||
for _, pkg := range dp.Aur {
|
||||
for _, conflict := range pkg.Conflicts {
|
||||
dp.checkInnerConflict(pkg.Name, conflict, conflicts)
|
||||
}
|
||||
}
|
||||
|
||||
for _, pkg := range dp.Repo {
|
||||
for _, conflict := range dp.AlpmExecutor.PackageConflicts(pkg) {
|
||||
dp.checkInnerConflict(pkg.Name(), conflict.String(), conflicts)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (dp *Pool) checkForwardConflicts(conflicts stringset.MapStringSet) {
|
||||
for _, pkg := range dp.Aur {
|
||||
for _, conflict := range pkg.Conflicts {
|
||||
dp.checkForwardConflict(pkg.Name, conflict, conflicts)
|
||||
}
|
||||
}
|
||||
|
||||
for _, pkg := range dp.Repo {
|
||||
for _, conflict := range dp.AlpmExecutor.PackageConflicts(pkg) {
|
||||
dp.checkForwardConflict(pkg.Name(), conflict.String(), conflicts)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (dp *Pool) checkReverseConflicts(conflicts stringset.MapStringSet) {
|
||||
for _, pkg := range dp.AlpmExecutor.LocalPackages() {
|
||||
if dp.hasPackage(pkg.Name()) {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, conflict := range dp.AlpmExecutor.PackageConflicts(pkg) {
|
||||
dp.checkReverseConflict(pkg.Name(), conflict.String(), conflicts)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (dp *Pool) CheckConflicts(useAsk, noConfirm, noDeps bool) (stringset.MapStringSet, error) {
|
||||
conflicts := make(stringset.MapStringSet)
|
||||
if noDeps {
|
||||
return conflicts, nil
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
|
||||
innerConflicts := make(stringset.MapStringSet)
|
||||
|
||||
wg.Add(2)
|
||||
|
||||
text.OperationInfoln(gotext.Get("Checking for conflicts..."))
|
||||
|
||||
go func() {
|
||||
dp.checkForwardConflicts(conflicts)
|
||||
dp.checkReverseConflicts(conflicts)
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
text.OperationInfoln(gotext.Get("Checking for inner conflicts..."))
|
||||
|
||||
go func() {
|
||||
dp.checkInnerConflicts(innerConflicts)
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
wg.Wait()
|
||||
|
||||
if len(innerConflicts) != 0 {
|
||||
text.Errorln(gotext.Get("Inner conflicts found:"))
|
||||
|
||||
for name, pkgs := range innerConflicts {
|
||||
str := text.SprintError(name + ":")
|
||||
for pkg := range pkgs {
|
||||
str += " " + text.Cyan(pkg) + ","
|
||||
}
|
||||
|
||||
str = strings.TrimSuffix(str, ",")
|
||||
|
||||
fmt.Println(str)
|
||||
}
|
||||
}
|
||||
|
||||
if len(conflicts) != 0 {
|
||||
text.Errorln(gotext.Get("Package conflicts found:"))
|
||||
|
||||
for name, pkgs := range conflicts {
|
||||
str := text.SprintError(gotext.Get("Installing %s will remove:", text.Cyan(name)))
|
||||
for pkg := range pkgs {
|
||||
str += " " + text.Cyan(pkg) + ","
|
||||
}
|
||||
|
||||
str = strings.TrimSuffix(str, ",")
|
||||
|
||||
fmt.Println(str)
|
||||
}
|
||||
}
|
||||
|
||||
// Add the inner conflicts to the conflicts
|
||||
// These are used to decide what to pass --ask to (if set) or don't pass --noconfirm to
|
||||
// As we have no idea what the order is yet we add every inner conflict to the slice
|
||||
for name, pkgs := range innerConflicts {
|
||||
conflicts[name] = make(stringset.StringSet)
|
||||
for pkg := range pkgs {
|
||||
conflicts[pkg] = make(stringset.StringSet)
|
||||
}
|
||||
}
|
||||
|
||||
if len(conflicts) > 0 {
|
||||
if !useAsk {
|
||||
if noConfirm {
|
||||
return nil, errors.New(gotext.Get("package conflicts can not be resolved with noconfirm, aborting"))
|
||||
}
|
||||
|
||||
text.Errorln(gotext.Get("Conflicting packages will have to be confirmed manually"))
|
||||
}
|
||||
}
|
||||
|
||||
return conflicts, nil
|
||||
}
|
||||
|
||||
type missing struct {
|
||||
Good stringset.StringSet
|
||||
Missing map[string][][]string
|
||||
}
|
||||
|
||||
func (dp *Pool) _checkMissing(dep string, stack []string, missing *missing, noDeps, noCheckDeps bool) {
|
||||
if missing.Good.Get(dep) {
|
||||
return
|
||||
}
|
||||
|
||||
if trees, ok := missing.Missing[dep]; ok {
|
||||
for _, tree := range trees {
|
||||
if stringSliceEqual(tree, stack) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
missing.Missing[dep] = append(missing.Missing[dep], stack)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if aurPkg := dp.findSatisfierAur(dep); aurPkg != nil {
|
||||
missing.Good.Set(dep)
|
||||
|
||||
combinedDepList := ComputeCombinedDepList(aurPkg, noDeps, noCheckDeps)
|
||||
for _, aurDep := range combinedDepList {
|
||||
if dp.AlpmExecutor.LocalSatisfierExists(aurDep) {
|
||||
missing.Good.Set(aurDep)
|
||||
continue
|
||||
}
|
||||
|
||||
dp._checkMissing(aurDep, append(stack, aurPkg.Name), missing, noDeps, noCheckDeps)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if repoPkg := dp.findSatisfierRepo(dep); repoPkg != nil {
|
||||
missing.Good.Set(dep)
|
||||
|
||||
if noDeps {
|
||||
return
|
||||
}
|
||||
|
||||
for _, dep := range dp.AlpmExecutor.PackageDepends(repoPkg) {
|
||||
if dp.AlpmExecutor.LocalSatisfierExists(dep.String()) {
|
||||
missing.Good.Set(dep.String())
|
||||
continue
|
||||
}
|
||||
|
||||
dp._checkMissing(dep.String(), append(stack, repoPkg.Name()), missing, noDeps, noCheckDeps)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
missing.Missing[dep] = [][]string{stack}
|
||||
}
|
||||
|
||||
func stringSliceEqual(a, b []string) bool {
|
||||
if a == nil && b == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
if a == nil || b == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := 0; i < len(a); i++ {
|
||||
if a[i] != b[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (dp *Pool) CheckMissing(noDeps, noCheckDeps bool) error {
|
||||
missing := &missing{
|
||||
make(stringset.StringSet),
|
||||
make(map[string][][]string),
|
||||
}
|
||||
|
||||
for _, target := range dp.Targets {
|
||||
dp._checkMissing(target.DepString(), make([]string, 0), missing, noDeps, noCheckDeps)
|
||||
}
|
||||
|
||||
if len(missing.Missing) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
text.Errorln(gotext.Get("Could not find all required packages:"))
|
||||
|
||||
for dep, trees := range missing.Missing {
|
||||
for _, tree := range trees {
|
||||
fmt.Fprintf(os.Stderr, "\t%s", text.Cyan(dep))
|
||||
|
||||
if len(tree) == 0 {
|
||||
fmt.Fprint(os.Stderr, gotext.Get(" (Target"))
|
||||
} else {
|
||||
fmt.Fprint(os.Stderr, gotext.Get(" (Wanted by: "))
|
||||
for n := 0; n < len(tree)-1; n++ {
|
||||
fmt.Fprint(os.Stderr, text.Cyan(tree[n]), " -> ")
|
||||
}
|
||||
fmt.Fprint(os.Stderr, text.Cyan(tree[len(tree)-1]))
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stderr, ")")
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("")
|
||||
}
|
199
pkg/dep/depOrder.go
Normal file
199
pkg/dep/depOrder.go
Normal file
@ -0,0 +1,199 @@
|
||||
package dep
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/Jguer/yay/v11/pkg/db"
|
||||
aur "github.com/Jguer/yay/v11/pkg/query"
|
||||
"github.com/Jguer/yay/v11/pkg/stringset"
|
||||
"github.com/Jguer/yay/v11/pkg/text"
|
||||
)
|
||||
|
||||
type Order struct {
|
||||
Aur []Base
|
||||
Repo []db.IPackage
|
||||
Runtime stringset.StringSet
|
||||
}
|
||||
|
||||
func newOrder() *Order {
|
||||
return &Order{
|
||||
make([]Base, 0),
|
||||
make([]db.IPackage, 0),
|
||||
make(stringset.StringSet),
|
||||
}
|
||||
}
|
||||
|
||||
func GetOrder(dp *Pool, noDeps, noCheckDeps bool) *Order {
|
||||
do := newOrder()
|
||||
|
||||
for _, target := range dp.Targets {
|
||||
dep := target.DepString()
|
||||
|
||||
if aurPkg := dp.Aur[dep]; aurPkg != nil && pkgSatisfies(aurPkg.Name, aurPkg.Version, dep) {
|
||||
do.orderPkgAur(aurPkg, dp, true, noDeps, noCheckDeps)
|
||||
} else if aurPkg := dp.findSatisfierAur(dep); aurPkg != nil {
|
||||
do.orderPkgAur(aurPkg, dp, true, noDeps, noCheckDeps)
|
||||
} else if repoPkg := dp.findSatisfierRepo(dep); repoPkg != nil {
|
||||
do.orderPkgRepo(repoPkg, dp, true)
|
||||
}
|
||||
}
|
||||
|
||||
return do
|
||||
}
|
||||
|
||||
func (do *Order) orderPkgAur(pkg *aur.Pkg, dp *Pool, runtime, noDeps, noCheckDeps bool) {
|
||||
if runtime {
|
||||
do.Runtime.Set(pkg.Name)
|
||||
}
|
||||
|
||||
delete(dp.Aur, pkg.Name)
|
||||
|
||||
for i, dep := range ComputeCombinedDepList(pkg, noDeps, noCheckDeps) {
|
||||
if aurPkg := dp.findSatisfierAur(dep); aurPkg != nil {
|
||||
do.orderPkgAur(aurPkg, dp, runtime && i == 0, noDeps, noCheckDeps)
|
||||
}
|
||||
|
||||
if repoPkg := dp.findSatisfierRepo(dep); repoPkg != nil {
|
||||
do.orderPkgRepo(repoPkg, dp, runtime && i == 0)
|
||||
}
|
||||
}
|
||||
|
||||
for i, base := range do.Aur {
|
||||
if base.Pkgbase() == pkg.PackageBase {
|
||||
do.Aur[i] = append(base, pkg)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
do.Aur = append(do.Aur, Base{pkg})
|
||||
}
|
||||
|
||||
func (do *Order) orderPkgRepo(pkg db.IPackage, dp *Pool, runtime bool) {
|
||||
if runtime {
|
||||
do.Runtime.Set(pkg.Name())
|
||||
}
|
||||
|
||||
delete(dp.Repo, pkg.Name())
|
||||
|
||||
for _, dep := range dp.AlpmExecutor.PackageDepends(pkg) {
|
||||
if repoPkg := dp.findSatisfierRepo(dep.String()); repoPkg != nil {
|
||||
do.orderPkgRepo(repoPkg, dp, runtime)
|
||||
}
|
||||
}
|
||||
|
||||
do.Repo = append(do.Repo, pkg)
|
||||
}
|
||||
|
||||
func (do *Order) HasMake() bool {
|
||||
lenAur := 0
|
||||
for _, base := range do.Aur {
|
||||
lenAur += len(base)
|
||||
}
|
||||
|
||||
return len(do.Runtime) != lenAur+len(do.Repo)
|
||||
}
|
||||
|
||||
func (do *Order) GetMake() []string {
|
||||
makeOnly := []string{}
|
||||
|
||||
for _, base := range do.Aur {
|
||||
for _, pkg := range base {
|
||||
if !do.Runtime.Get(pkg.Name) {
|
||||
makeOnly = append(makeOnly, pkg.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, pkg := range do.Repo {
|
||||
if !do.Runtime.Get(pkg.Name()) {
|
||||
makeOnly = append(makeOnly, pkg.Name())
|
||||
}
|
||||
}
|
||||
|
||||
return makeOnly
|
||||
}
|
||||
|
||||
// Print prints repository packages to be downloaded.
|
||||
func (do *Order) Print() {
|
||||
repo := ""
|
||||
repoMake := ""
|
||||
aurString := ""
|
||||
aurMake := ""
|
||||
|
||||
repoLen := 0
|
||||
repoMakeLen := 0
|
||||
aurLen := 0
|
||||
aurMakeLen := 0
|
||||
|
||||
for _, pkg := range do.Repo {
|
||||
pkgStr := fmt.Sprintf(" %s-%s", pkg.Name(), pkg.Version())
|
||||
if do.Runtime.Get(pkg.Name()) {
|
||||
repo += pkgStr
|
||||
repoLen++
|
||||
} else {
|
||||
repoMake += pkgStr
|
||||
repoMakeLen++
|
||||
}
|
||||
}
|
||||
|
||||
for _, base := range do.Aur {
|
||||
pkg := base.Pkgbase()
|
||||
pkgStr := " " + pkg + "-" + base[0].Version
|
||||
pkgStrMake := pkgStr
|
||||
|
||||
push := false
|
||||
pushMake := false
|
||||
|
||||
switch {
|
||||
case len(base) > 1, pkg != base[0].Name:
|
||||
pkgStr += " ("
|
||||
pkgStrMake += " ("
|
||||
|
||||
for _, split := range base {
|
||||
if do.Runtime.Get(split.Name) {
|
||||
pkgStr += split.Name + " "
|
||||
aurLen++
|
||||
|
||||
push = true
|
||||
} else {
|
||||
pkgStrMake += split.Name + " "
|
||||
aurMakeLen++
|
||||
pushMake = true
|
||||
}
|
||||
}
|
||||
|
||||
pkgStr = pkgStr[:len(pkgStr)-1] + ")"
|
||||
pkgStrMake = pkgStrMake[:len(pkgStrMake)-1] + ")"
|
||||
case do.Runtime.Get(base[0].Name):
|
||||
aurLen++
|
||||
|
||||
push = true
|
||||
default:
|
||||
aurMakeLen++
|
||||
|
||||
pushMake = true
|
||||
}
|
||||
|
||||
if push {
|
||||
aurString += pkgStr
|
||||
}
|
||||
|
||||
if pushMake {
|
||||
aurMake += pkgStrMake
|
||||
}
|
||||
}
|
||||
|
||||
printDownloads("Repo", repoLen, repo)
|
||||
printDownloads("Repo Make", repoMakeLen, repoMake)
|
||||
printDownloads("Aur", aurLen, aurString)
|
||||
printDownloads("Aur Make", aurMakeLen, aurMake)
|
||||
}
|
||||
|
||||
func printDownloads(repoName string, length int, packages string) {
|
||||
if length < 1 {
|
||||
return
|
||||
}
|
||||
|
||||
repoInfo := fmt.Sprintf(text.Bold(text.Blue("[%s:%d]")), repoName, length)
|
||||
fmt.Println(repoInfo + text.Cyan(packages))
|
||||
}
|
586
pkg/dep/depPool.go
Normal file
586
pkg/dep/depPool.go
Normal file
@ -0,0 +1,586 @@
|
||||
package dep
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/Jguer/aur"
|
||||
alpm "github.com/Jguer/go-alpm/v2"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
|
||||
"github.com/Jguer/yay/v11/pkg/db"
|
||||
"github.com/Jguer/yay/v11/pkg/query"
|
||||
"github.com/Jguer/yay/v11/pkg/settings"
|
||||
"github.com/Jguer/yay/v11/pkg/settings/parser"
|
||||
"github.com/Jguer/yay/v11/pkg/stringset"
|
||||
"github.com/Jguer/yay/v11/pkg/text"
|
||||
)
|
||||
|
||||
type Target struct {
|
||||
DB string
|
||||
Name string
|
||||
Mod string
|
||||
Version string
|
||||
}
|
||||
|
||||
func ToTarget(pkg string) Target {
|
||||
dbName, depString := text.SplitDBFromName(pkg)
|
||||
name, mod, depVersion := splitDep(depString)
|
||||
|
||||
return Target{
|
||||
DB: dbName,
|
||||
Name: name,
|
||||
Mod: mod,
|
||||
Version: depVersion,
|
||||
}
|
||||
}
|
||||
|
||||
func (t Target) DepString() string {
|
||||
return t.Name + t.Mod + t.Version
|
||||
}
|
||||
|
||||
func (t Target) String() string {
|
||||
if t.DB != "" {
|
||||
return t.DB + "/" + t.DepString()
|
||||
}
|
||||
|
||||
return t.DepString()
|
||||
}
|
||||
|
||||
type Pool struct {
|
||||
Targets []Target
|
||||
Explicit stringset.StringSet
|
||||
Repo map[string]db.IPackage
|
||||
Aur map[string]*query.Pkg
|
||||
AurCache map[string]*query.Pkg
|
||||
Groups []string
|
||||
AlpmExecutor db.Executor
|
||||
Warnings *query.AURWarnings
|
||||
aurClient aur.ClientInterface
|
||||
}
|
||||
|
||||
func newPool(dbExecutor db.Executor, aurClient aur.ClientInterface) *Pool {
|
||||
dp := &Pool{
|
||||
Targets: []Target{},
|
||||
Explicit: map[string]struct{}{},
|
||||
Repo: map[string]alpm.IPackage{},
|
||||
Aur: map[string]*aur.Pkg{},
|
||||
AurCache: map[string]*aur.Pkg{},
|
||||
Groups: []string{},
|
||||
AlpmExecutor: dbExecutor,
|
||||
Warnings: nil,
|
||||
aurClient: aurClient,
|
||||
}
|
||||
|
||||
return dp
|
||||
}
|
||||
|
||||
// Includes db/ prefixes and group installs.
|
||||
func (dp *Pool) ResolveTargets(ctx context.Context, pkgs []string,
|
||||
mode parser.TargetMode,
|
||||
ignoreProviders, noConfirm, provides bool, rebuild string, splitN int, noDeps, noCheckDeps bool, assumeInstalled []string,
|
||||
) error {
|
||||
// RPC requests are slow
|
||||
// Combine as many AUR package requests as possible into a single RPC call
|
||||
aurTargets := make(stringset.StringSet)
|
||||
|
||||
pkgs = query.RemoveInvalidTargets(pkgs, mode)
|
||||
|
||||
for _, pkg := range pkgs {
|
||||
target := ToTarget(pkg)
|
||||
|
||||
// skip targets already satisfied
|
||||
// even if the user enters db/pkg and aur/pkg the latter will
|
||||
// still get skipped even if it's from a different database to
|
||||
// the one specified
|
||||
// this is how pacman behaves
|
||||
if dp.hasPackage(target.DepString()) || isInAssumeInstalled(target.DepString(), assumeInstalled) {
|
||||
continue
|
||||
}
|
||||
|
||||
var foundPkg db.IPackage
|
||||
|
||||
// aur/ prefix means we only check the aur
|
||||
if target.DB == "aur" || mode == parser.ModeAUR {
|
||||
dp.Targets = append(dp.Targets, target)
|
||||
aurTargets.Set(target.DepString())
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
// If there's a different prefix only look in that repo
|
||||
if target.DB != "" {
|
||||
foundPkg = dp.AlpmExecutor.SatisfierFromDB(target.DepString(), target.DB)
|
||||
} else {
|
||||
// otherwise find it in any repo
|
||||
foundPkg = dp.AlpmExecutor.SyncSatisfier(target.DepString())
|
||||
}
|
||||
|
||||
if foundPkg != nil {
|
||||
dp.Targets = append(dp.Targets, target)
|
||||
dp.Explicit.Set(foundPkg.Name())
|
||||
dp.ResolveRepoDependency(foundPkg, noDeps)
|
||||
|
||||
continue
|
||||
} else {
|
||||
// check for groups
|
||||
// currently we don't resolve the packages in a group
|
||||
// only check if the group exists
|
||||
// would be better to check the groups from singleDB if
|
||||
// the user specified a db but there's no easy way to do
|
||||
// it without making alpm_lists so don't bother for now
|
||||
// db/group is probably a rare use case
|
||||
groupPackages := dp.AlpmExecutor.PackagesFromGroup(target.Name)
|
||||
if len(groupPackages) > 0 {
|
||||
dp.Groups = append(dp.Groups, target.String())
|
||||
for _, pkg := range groupPackages {
|
||||
dp.Explicit.Set(pkg.Name())
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// if there was no db prefix check the aur
|
||||
if target.DB == "" {
|
||||
aurTargets.Set(target.DepString())
|
||||
}
|
||||
|
||||
dp.Targets = append(dp.Targets, target)
|
||||
}
|
||||
|
||||
if len(aurTargets) > 0 && mode.AtLeastAUR() {
|
||||
return dp.resolveAURPackages(ctx, aurTargets, true, ignoreProviders,
|
||||
noConfirm, provides, rebuild, splitN, noDeps, noCheckDeps)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Pseudo provides finder.
|
||||
// Try to find provides by performing a search of the package name
|
||||
// This effectively performs -Ss on each package
|
||||
// then runs -Si on each result to cache the information.
|
||||
//
|
||||
// For example if you were to -S yay then yay -Ss would give:
|
||||
// yay-git yay-bin yay realyog pacui pacui-git ruby-yard
|
||||
// These packages will all be added to the cache in case they are needed later
|
||||
// Ofcouse only the first three packages provide yay, the rest are just false
|
||||
// positives.
|
||||
//
|
||||
// This method increases dependency resolve time.
|
||||
func (dp *Pool) findProvides(ctx context.Context, pkgs stringset.StringSet) error {
|
||||
var (
|
||||
mux sync.Mutex
|
||||
wg sync.WaitGroup
|
||||
)
|
||||
|
||||
doSearch := func(pkg string) {
|
||||
defer wg.Done()
|
||||
|
||||
var (
|
||||
err error
|
||||
results []query.Pkg
|
||||
)
|
||||
|
||||
// Hack for a bigger search result, if the user wants
|
||||
// java-envronment we can search for just java instead and get
|
||||
// more hits.
|
||||
pkg, _, _ = splitDep(pkg) // openimagedenoise-git > ispc-git #1234
|
||||
words := strings.Split(pkg, "-")
|
||||
|
||||
for i := range words {
|
||||
results, err = dp.aurClient.Search(ctx, strings.Join(words[:i+1], "-"), aur.None)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for iR := range results {
|
||||
mux.Lock()
|
||||
if _, ok := dp.AurCache[results[iR].Name]; !ok {
|
||||
pkgs.Set(results[iR].Name)
|
||||
}
|
||||
mux.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
for pkg := range pkgs {
|
||||
if dp.AlpmExecutor.LocalPackage(pkg) != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
wg.Add(1)
|
||||
|
||||
go doSearch(pkg)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dp *Pool) cacheAURPackages(ctx context.Context, _pkgs stringset.StringSet, provides bool, splitN int) error {
|
||||
pkgs := _pkgs.Copy()
|
||||
toQuery := make([]string, 0)
|
||||
|
||||
for pkg := range pkgs {
|
||||
if _, ok := dp.AurCache[pkg]; ok {
|
||||
pkgs.Remove(pkg)
|
||||
}
|
||||
}
|
||||
|
||||
if len(pkgs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if provides {
|
||||
err := dp.findProvides(ctx, pkgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for pkg := range pkgs {
|
||||
if _, ok := dp.AurCache[pkg]; !ok {
|
||||
name, _, ver := splitDep(pkg)
|
||||
if ver != "" {
|
||||
toQuery = append(toQuery, name, name+"-"+ver)
|
||||
} else {
|
||||
toQuery = append(toQuery, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
info, err := query.AURInfo(ctx, dp.aurClient, toQuery, dp.Warnings, splitN)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, pkg := range info {
|
||||
// Dump everything in cache just in case we need it later
|
||||
dp.AurCache[pkg.Name] = pkg
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Compute dependency lists used in Package dep searching and ordering.
|
||||
// Order sensitive TOFIX.
|
||||
func ComputeCombinedDepList(pkg *aur.Pkg, noDeps, noCheckDeps bool) []string {
|
||||
combinedDepList := make([]string, 0, len(pkg.Depends)+len(pkg.MakeDepends)+len(pkg.CheckDepends))
|
||||
|
||||
if !noDeps {
|
||||
combinedDepList = append(combinedDepList, pkg.Depends...)
|
||||
}
|
||||
|
||||
combinedDepList = append(combinedDepList, pkg.MakeDepends...)
|
||||
|
||||
if !noCheckDeps {
|
||||
combinedDepList = append(combinedDepList, pkg.CheckDepends...)
|
||||
}
|
||||
|
||||
return combinedDepList
|
||||
}
|
||||
|
||||
func (dp *Pool) resolveAURPackages(ctx context.Context,
|
||||
pkgs stringset.StringSet,
|
||||
explicit, ignoreProviders, noConfirm, provides bool,
|
||||
rebuild string, splitN int, noDeps, noCheckDeps bool,
|
||||
) error {
|
||||
newPackages := make(stringset.StringSet)
|
||||
newAURPackages := make(stringset.StringSet)
|
||||
|
||||
err := dp.cacheAURPackages(ctx, pkgs, provides, splitN)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(pkgs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
for name := range pkgs {
|
||||
_, ok := dp.Aur[name]
|
||||
if ok {
|
||||
continue
|
||||
}
|
||||
|
||||
pkg := dp.findSatisfierAurCache(name, ignoreProviders, noConfirm, provides)
|
||||
if pkg == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if explicit {
|
||||
dp.Explicit.Set(pkg.Name)
|
||||
}
|
||||
|
||||
dp.Aur[pkg.Name] = pkg
|
||||
|
||||
combinedDepList := ComputeCombinedDepList(pkg, noDeps, noCheckDeps)
|
||||
for _, dep := range combinedDepList {
|
||||
newPackages.Set(dep)
|
||||
}
|
||||
}
|
||||
|
||||
for dep := range newPackages {
|
||||
if dp.hasSatisfier(dep) {
|
||||
continue
|
||||
}
|
||||
|
||||
isInstalled := dp.AlpmExecutor.LocalSatisfierExists(dep)
|
||||
hm := settings.HideMenus
|
||||
settings.HideMenus = isInstalled
|
||||
repoPkg := dp.AlpmExecutor.SyncSatisfier(dep) // has satisfier in repo: fetch it
|
||||
settings.HideMenus = hm
|
||||
|
||||
if isInstalled && (rebuild != "tree" || repoPkg != nil) {
|
||||
continue
|
||||
}
|
||||
|
||||
if repoPkg != nil {
|
||||
dp.ResolveRepoDependency(repoPkg, false)
|
||||
continue
|
||||
}
|
||||
|
||||
// assume it's in the aur
|
||||
// ditch the versioning because the RPC can't handle it
|
||||
newAURPackages.Set(dep)
|
||||
}
|
||||
|
||||
err = dp.resolveAURPackages(ctx, newAURPackages, false, ignoreProviders,
|
||||
noConfirm, provides, rebuild, splitN, noDeps, noCheckDeps)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (dp *Pool) ResolveRepoDependency(pkg db.IPackage, noDeps bool) {
|
||||
dp.Repo[pkg.Name()] = pkg
|
||||
|
||||
if noDeps {
|
||||
return
|
||||
}
|
||||
|
||||
for _, dep := range dp.AlpmExecutor.PackageDepends(pkg) {
|
||||
if dp.hasSatisfier(dep.String()) {
|
||||
continue
|
||||
}
|
||||
|
||||
// has satisfier installed: skip
|
||||
if dp.AlpmExecutor.LocalSatisfierExists(dep.String()) {
|
||||
continue
|
||||
}
|
||||
|
||||
// has satisfier in repo: fetch it
|
||||
if repoPkg := dp.AlpmExecutor.SyncSatisfier(dep.String()); repoPkg != nil {
|
||||
dp.ResolveRepoDependency(repoPkg, noDeps)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func GetPool(ctx context.Context, pkgs []string,
|
||||
warnings *query.AURWarnings,
|
||||
dbExecutor db.Executor,
|
||||
aurClient aur.ClientInterface,
|
||||
mode parser.TargetMode,
|
||||
ignoreProviders, noConfirm, provides bool,
|
||||
rebuild string, splitN int, noDeps bool, noCheckDeps bool, assumeInstalled []string,
|
||||
) (*Pool, error) {
|
||||
dp := newPool(dbExecutor, aurClient)
|
||||
|
||||
dp.Warnings = warnings
|
||||
err := dp.ResolveTargets(ctx, pkgs, mode, ignoreProviders, noConfirm, provides,
|
||||
rebuild, splitN, noDeps, noCheckDeps, assumeInstalled)
|
||||
|
||||
return dp, err
|
||||
}
|
||||
|
||||
func (dp *Pool) findSatisfierAur(dep string) *query.Pkg {
|
||||
for _, pkg := range dp.Aur {
|
||||
if satisfiesAur(dep, pkg) {
|
||||
return pkg
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// This is mostly used to promote packages from the cache
|
||||
// to the Install list
|
||||
// Provide a pacman style provider menu if there's more than one candidate
|
||||
// This acts slightly differently from Pacman, It will give
|
||||
// a menu even if a package with a matching name exists. I believe this
|
||||
// method is better because most of the time you are choosing between
|
||||
// foo and foo-git.
|
||||
// Using Pacman's ways trying to install foo would never give you
|
||||
// a menu.
|
||||
// TODO: maybe intermix repo providers in the menu.
|
||||
func (dp *Pool) findSatisfierAurCache(dep string, ignoreProviders, noConfirm, provides bool) *query.Pkg {
|
||||
depName, _, _ := splitDep(dep)
|
||||
seen := make(stringset.StringSet)
|
||||
providerSlice := makeProviders(depName)
|
||||
|
||||
if dp.AlpmExecutor.LocalPackage(depName) != nil {
|
||||
if pkg, ok := dp.AurCache[dep]; ok && pkgSatisfies(pkg.Name, pkg.Version, dep) {
|
||||
return pkg
|
||||
}
|
||||
}
|
||||
|
||||
if ignoreProviders {
|
||||
for _, pkg := range dp.AurCache {
|
||||
if pkgSatisfies(pkg.Name, pkg.Version, dep) {
|
||||
for _, target := range dp.Targets {
|
||||
if target.Name == pkg.Name {
|
||||
return pkg
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, pkg := range dp.AurCache {
|
||||
if seen.Get(pkg.Name) {
|
||||
continue
|
||||
}
|
||||
|
||||
if pkgSatisfies(pkg.Name, pkg.Version, dep) {
|
||||
providerSlice.Pkgs = append(providerSlice.Pkgs, pkg)
|
||||
seen.Set(pkg.Name)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
for _, provide := range pkg.Provides {
|
||||
if provideSatisfies(provide, dep, pkg.Version) {
|
||||
providerSlice.Pkgs = append(providerSlice.Pkgs, pkg)
|
||||
seen.Set(pkg.Name)
|
||||
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !provides && providerSlice.Len() >= 1 {
|
||||
return providerSlice.Pkgs[0]
|
||||
}
|
||||
|
||||
if providerSlice.Len() == 1 {
|
||||
return providerSlice.Pkgs[0]
|
||||
}
|
||||
|
||||
if providerSlice.Len() > 1 {
|
||||
sort.Sort(providerSlice)
|
||||
return providerMenu(dep, providerSlice, noConfirm)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dp *Pool) findSatisfierRepo(dep string) db.IPackage {
|
||||
for _, pkg := range dp.Repo {
|
||||
if satisfiesRepo(dep, pkg, dp.AlpmExecutor) {
|
||||
return pkg
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dp *Pool) hasSatisfier(dep string) bool {
|
||||
return dp.findSatisfierRepo(dep) != nil || dp.findSatisfierAur(dep) != nil
|
||||
}
|
||||
|
||||
func (dp *Pool) hasPackage(name string) bool {
|
||||
for _, pkg := range dp.Repo {
|
||||
if pkg.Name() == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
for _, pkg := range dp.Aur {
|
||||
if pkg.Name == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
for _, pkg := range dp.Groups {
|
||||
if pkg == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func isInAssumeInstalled(name string, assumeInstalled []string) bool {
|
||||
for _, pkgAndVersion := range assumeInstalled {
|
||||
assumeName, _, _ := splitDep(pkgAndVersion)
|
||||
|
||||
depName, _, _ := splitDep(name)
|
||||
if assumeName == depName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func providerMenu(dep string, providers providers, noConfirm bool) *query.Pkg {
|
||||
size := providers.Len()
|
||||
|
||||
str := text.Bold(gotext.Get("There are %d providers available for %s:", size, dep))
|
||||
str += "\n"
|
||||
|
||||
size = 1
|
||||
str += text.SprintOperationInfo(gotext.Get("Repository AUR"), "\n ")
|
||||
|
||||
for _, pkg := range providers.Pkgs {
|
||||
str += fmt.Sprintf("%d) %s ", size, pkg.Name)
|
||||
size++
|
||||
}
|
||||
|
||||
text.OperationInfoln(str)
|
||||
|
||||
for {
|
||||
fmt.Println(gotext.Get("\nEnter a number (default=1): "))
|
||||
|
||||
if noConfirm {
|
||||
fmt.Println("1")
|
||||
return providers.Pkgs[0]
|
||||
}
|
||||
|
||||
numberBuf, err := text.GetInput("", false)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
if numberBuf == "" {
|
||||
return providers.Pkgs[0]
|
||||
}
|
||||
|
||||
num, err := strconv.Atoi(numberBuf)
|
||||
if err != nil {
|
||||
text.Errorln(gotext.Get("invalid number: %s", numberBuf))
|
||||
continue
|
||||
}
|
||||
|
||||
if num < 1 || num >= size {
|
||||
text.Errorln(gotext.Get("invalid value: %d is not between %d and %d", num, 1, size-1))
|
||||
continue
|
||||
}
|
||||
|
||||
return providers.Pkgs[num-1]
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -1,853 +0,0 @@
|
||||
package dep
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
aurc "github.com/Jguer/aur"
|
||||
alpm "github.com/Jguer/go-alpm/v2"
|
||||
gosrc "github.com/Morganamilo/go-srcinfo"
|
||||
mapset "github.com/deckarep/golang-set/v2"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
|
||||
"github.com/Jguer/yay/v12/pkg/db"
|
||||
"github.com/Jguer/yay/v12/pkg/dep/topo"
|
||||
"github.com/Jguer/yay/v12/pkg/intrange"
|
||||
aur "github.com/Jguer/yay/v12/pkg/query"
|
||||
"github.com/Jguer/yay/v12/pkg/text"
|
||||
)
|
||||
|
||||
type InstallInfo struct {
|
||||
Source Source
|
||||
Reason Reason
|
||||
Version string
|
||||
LocalVersion string
|
||||
SrcinfoPath *string
|
||||
AURBase *string
|
||||
SyncDBName *string
|
||||
|
||||
IsGroup bool
|
||||
Upgrade bool
|
||||
Devel bool
|
||||
}
|
||||
|
||||
func (i *InstallInfo) String() string {
|
||||
return fmt.Sprintf("InstallInfo{Source: %v, Reason: %v}", i.Source, i.Reason)
|
||||
}
|
||||
|
||||
type (
|
||||
Reason uint
|
||||
Source int
|
||||
)
|
||||
|
||||
func (r Reason) String() string {
|
||||
return ReasonNames[r]
|
||||
}
|
||||
|
||||
func (s Source) String() string {
|
||||
return SourceNames[s]
|
||||
}
|
||||
|
||||
const (
|
||||
Explicit Reason = iota // 0
|
||||
Dep // 1
|
||||
MakeDep // 2
|
||||
CheckDep // 3
|
||||
)
|
||||
|
||||
var ReasonNames = map[Reason]string{
|
||||
Explicit: gotext.Get("Explicit"),
|
||||
Dep: gotext.Get("Dependency"),
|
||||
MakeDep: gotext.Get("Make Dependency"),
|
||||
CheckDep: gotext.Get("Check Dependency"),
|
||||
}
|
||||
|
||||
const (
|
||||
AUR Source = iota
|
||||
Sync
|
||||
Local
|
||||
SrcInfo
|
||||
Missing
|
||||
)
|
||||
|
||||
var SourceNames = map[Source]string{
|
||||
AUR: gotext.Get("AUR"),
|
||||
Sync: gotext.Get("Sync"),
|
||||
Local: gotext.Get("Local"),
|
||||
SrcInfo: gotext.Get("SRCINFO"),
|
||||
Missing: gotext.Get("Missing"),
|
||||
}
|
||||
|
||||
var bgColorMap = map[Source]string{
|
||||
AUR: "lightblue",
|
||||
Sync: "lemonchiffon",
|
||||
Local: "darkolivegreen1",
|
||||
Missing: "tomato",
|
||||
}
|
||||
|
||||
var colorMap = map[Reason]string{
|
||||
Explicit: "black",
|
||||
Dep: "deeppink",
|
||||
MakeDep: "navyblue",
|
||||
CheckDep: "forestgreen",
|
||||
}
|
||||
|
||||
type Grapher struct {
|
||||
logger *text.Logger
|
||||
providerCache map[string][]aur.Pkg
|
||||
|
||||
dbExecutor db.Executor
|
||||
aurClient aurc.QueryClient
|
||||
fullGraph bool // If true, the graph will include all dependencies including already installed ones or repo
|
||||
noConfirm bool // If true, the graph will not prompt for confirmation
|
||||
noDeps bool // If true, the graph will not include dependencies
|
||||
noCheckDeps bool // If true, the graph will not include check dependencies
|
||||
needed bool // If true, the graph will only include packages that are not installed
|
||||
}
|
||||
|
||||
func NewGrapher(dbExecutor db.Executor, aurCache aurc.QueryClient,
|
||||
fullGraph, noConfirm, noDeps, noCheckDeps, needed bool,
|
||||
logger *text.Logger,
|
||||
) *Grapher {
|
||||
return &Grapher{
|
||||
dbExecutor: dbExecutor,
|
||||
aurClient: aurCache,
|
||||
fullGraph: fullGraph,
|
||||
noConfirm: noConfirm,
|
||||
noDeps: noDeps,
|
||||
noCheckDeps: noCheckDeps,
|
||||
needed: needed,
|
||||
providerCache: make(map[string][]aurc.Pkg, 5),
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
func NewGraph() *topo.Graph[string, *InstallInfo] {
|
||||
return topo.New[string, *InstallInfo]()
|
||||
}
|
||||
|
||||
func (g *Grapher) GraphFromTargets(ctx context.Context,
|
||||
graph *topo.Graph[string, *InstallInfo], targets []string,
|
||||
) (*topo.Graph[string, *InstallInfo], error) {
|
||||
if graph == nil {
|
||||
graph = NewGraph()
|
||||
}
|
||||
|
||||
aurTargets := make([]string, 0, len(targets))
|
||||
|
||||
for _, targetString := range targets {
|
||||
target := ToTarget(targetString)
|
||||
|
||||
switch target.DB {
|
||||
case "": // unspecified db
|
||||
if pkg := g.dbExecutor.SyncSatisfier(target.Name); pkg != nil {
|
||||
g.GraphSyncPkg(ctx, graph, pkg, nil)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
groupPackages := g.dbExecutor.PackagesFromGroup(target.Name)
|
||||
if len(groupPackages) > 0 {
|
||||
dbName := groupPackages[0].DB().Name()
|
||||
g.GraphSyncGroup(ctx, graph, target.Name, dbName)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
fallthrough
|
||||
case "aur":
|
||||
aurTargets = append(aurTargets, target.Name)
|
||||
default:
|
||||
pkg, err := g.dbExecutor.SatisfierFromDB(target.Name, target.DB)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if pkg != nil {
|
||||
g.GraphSyncPkg(ctx, graph, pkg, nil)
|
||||
continue
|
||||
}
|
||||
|
||||
groupPackages, err := g.dbExecutor.PackagesFromGroupAndDB(target.Name, target.DB)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(groupPackages) > 0 {
|
||||
g.GraphSyncGroup(ctx, graph, target.Name, target.DB)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
g.logger.Errorln(gotext.Get("No package found for"), " ", target)
|
||||
}
|
||||
}
|
||||
|
||||
var errA error
|
||||
graph, errA = g.GraphFromAUR(ctx, graph, aurTargets)
|
||||
if errA != nil {
|
||||
return nil, errA
|
||||
}
|
||||
|
||||
return graph, nil
|
||||
}
|
||||
|
||||
func (g *Grapher) pickSrcInfoPkgs(pkgs []*aurc.Pkg) ([]*aurc.Pkg, error) {
|
||||
final := make([]*aurc.Pkg, 0, len(pkgs))
|
||||
for i := range pkgs {
|
||||
g.logger.Println(text.Magenta(strconv.Itoa(i+1)+" ") + text.Bold(pkgs[i].Name) +
|
||||
" " + text.Cyan(pkgs[i].Version))
|
||||
g.logger.Println(" " + pkgs[i].Description)
|
||||
}
|
||||
g.logger.Infoln(gotext.Get("Packages to exclude") + " (eg: \"1 2 3\", \"1-3\", \"^4\"):")
|
||||
|
||||
numberBuf, err := g.logger.GetInput("", g.noConfirm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
include, exclude, _, otherExclude := intrange.ParseNumberMenu(numberBuf)
|
||||
isInclude := len(exclude) == 0 && otherExclude.Cardinality() == 0
|
||||
|
||||
for i := 1; i <= len(pkgs); i++ {
|
||||
target := i - 1
|
||||
|
||||
if isInclude && !include.Get(i) {
|
||||
final = append(final, pkgs[target])
|
||||
}
|
||||
|
||||
if !isInclude && (exclude.Get(i)) {
|
||||
final = append(final, pkgs[target])
|
||||
}
|
||||
}
|
||||
|
||||
return final, nil
|
||||
}
|
||||
|
||||
func (g *Grapher) addAurPkgProvides(pkg *aurc.Pkg, graph *topo.Graph[string, *InstallInfo]) {
|
||||
for i := range pkg.Provides {
|
||||
depName, mod, version := splitDep(pkg.Provides[i])
|
||||
g.logger.Debugln(pkg.String() + " provides: " + depName)
|
||||
graph.Provides(depName, &alpm.Depend{
|
||||
Name: depName,
|
||||
Version: version,
|
||||
Mod: aurDepModToAlpmDep(mod),
|
||||
}, pkg.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func (g *Grapher) GraphFromSrcInfos(ctx context.Context, graph *topo.Graph[string, *InstallInfo],
|
||||
srcInfos map[string]*gosrc.Srcinfo,
|
||||
) (*topo.Graph[string, *InstallInfo], error) {
|
||||
if graph == nil {
|
||||
graph = NewGraph()
|
||||
}
|
||||
|
||||
aurPkgsAdded := []*aurc.Pkg{}
|
||||
for pkgBuildDir, pkgbuild := range srcInfos {
|
||||
pkgBuildDir := pkgBuildDir
|
||||
|
||||
aurPkgs, err := makeAURPKGFromSrcinfo(g.dbExecutor, pkgbuild)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(aurPkgs) > 1 {
|
||||
var errPick error
|
||||
aurPkgs, errPick = g.pickSrcInfoPkgs(aurPkgs)
|
||||
if errPick != nil {
|
||||
return nil, errPick
|
||||
}
|
||||
}
|
||||
|
||||
for _, pkg := range aurPkgs {
|
||||
pkg := pkg
|
||||
|
||||
reason := Explicit
|
||||
if pkg := g.dbExecutor.LocalPackage(pkg.Name); pkg != nil {
|
||||
reason = Reason(pkg.Reason())
|
||||
}
|
||||
|
||||
graph.AddNode(pkg.Name)
|
||||
|
||||
g.addAurPkgProvides(pkg, graph)
|
||||
|
||||
g.ValidateAndSetNodeInfo(graph, pkg.Name, &topo.NodeInfo[*InstallInfo]{
|
||||
Color: colorMap[reason],
|
||||
Background: bgColorMap[AUR],
|
||||
Value: &InstallInfo{
|
||||
Source: SrcInfo,
|
||||
Reason: reason,
|
||||
SrcinfoPath: &pkgBuildDir,
|
||||
AURBase: &pkg.PackageBase,
|
||||
Version: pkg.Version,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
aurPkgsAdded = append(aurPkgsAdded, aurPkgs...)
|
||||
}
|
||||
|
||||
g.AddDepsForPkgs(ctx, aurPkgsAdded, graph)
|
||||
|
||||
return graph, nil
|
||||
}
|
||||
|
||||
func (g *Grapher) AddDepsForPkgs(ctx context.Context, pkgs []*aur.Pkg, graph *topo.Graph[string, *InstallInfo]) {
|
||||
for _, pkg := range pkgs {
|
||||
g.addDepNodes(ctx, pkg, graph)
|
||||
}
|
||||
}
|
||||
|
||||
func (g *Grapher) addDepNodes(ctx context.Context, pkg *aur.Pkg, graph *topo.Graph[string, *InstallInfo]) {
|
||||
if len(pkg.MakeDepends) > 0 {
|
||||
g.addNodes(ctx, graph, pkg.Name, pkg.MakeDepends, MakeDep)
|
||||
}
|
||||
|
||||
if !g.noDeps && len(pkg.Depends) > 0 {
|
||||
g.addNodes(ctx, graph, pkg.Name, pkg.Depends, Dep)
|
||||
}
|
||||
|
||||
if !g.noCheckDeps && !g.noDeps && len(pkg.CheckDepends) > 0 {
|
||||
g.addNodes(ctx, graph, pkg.Name, pkg.CheckDepends, CheckDep)
|
||||
}
|
||||
}
|
||||
|
||||
func (g *Grapher) GraphSyncPkg(ctx context.Context,
|
||||
graph *topo.Graph[string, *InstallInfo],
|
||||
pkg alpm.IPackage, upgradeInfo *db.SyncUpgrade,
|
||||
) *topo.Graph[string, *InstallInfo] {
|
||||
if graph == nil {
|
||||
graph = NewGraph()
|
||||
}
|
||||
|
||||
graph.AddNode(pkg.Name())
|
||||
_ = pkg.Provides().ForEach(func(p *alpm.Depend) error {
|
||||
g.logger.Debugln(pkg.Name() + " provides: " + p.String())
|
||||
graph.Provides(p.Name, p, pkg.Name())
|
||||
return nil
|
||||
})
|
||||
|
||||
dbName := pkg.DB().Name()
|
||||
info := &InstallInfo{
|
||||
Source: Sync,
|
||||
Reason: Explicit,
|
||||
Version: pkg.Version(),
|
||||
SyncDBName: &dbName,
|
||||
}
|
||||
|
||||
if upgradeInfo == nil {
|
||||
if localPkg := g.dbExecutor.LocalPackage(pkg.Name()); localPkg != nil {
|
||||
info.Reason = Reason(localPkg.Reason())
|
||||
}
|
||||
} else {
|
||||
info.Upgrade = true
|
||||
info.Reason = Reason(upgradeInfo.Reason)
|
||||
info.LocalVersion = upgradeInfo.LocalVersion
|
||||
}
|
||||
|
||||
g.ValidateAndSetNodeInfo(graph, pkg.Name(), &topo.NodeInfo[*InstallInfo]{
|
||||
Color: colorMap[info.Reason],
|
||||
Background: bgColorMap[info.Source],
|
||||
Value: info,
|
||||
})
|
||||
|
||||
return graph
|
||||
}
|
||||
|
||||
func (g *Grapher) GraphSyncGroup(ctx context.Context,
|
||||
graph *topo.Graph[string, *InstallInfo],
|
||||
groupName, dbName string,
|
||||
) *topo.Graph[string, *InstallInfo] {
|
||||
if graph == nil {
|
||||
graph = NewGraph()
|
||||
}
|
||||
|
||||
graph.AddNode(groupName)
|
||||
|
||||
g.ValidateAndSetNodeInfo(graph, groupName, &topo.NodeInfo[*InstallInfo]{
|
||||
Color: colorMap[Explicit],
|
||||
Background: bgColorMap[Sync],
|
||||
Value: &InstallInfo{
|
||||
Source: Sync,
|
||||
Reason: Explicit,
|
||||
Version: "",
|
||||
SyncDBName: &dbName,
|
||||
IsGroup: true,
|
||||
},
|
||||
})
|
||||
|
||||
return graph
|
||||
}
|
||||
|
||||
func (g *Grapher) GraphAURTarget(ctx context.Context,
|
||||
graph *topo.Graph[string, *InstallInfo],
|
||||
pkg *aurc.Pkg, instalInfo *InstallInfo,
|
||||
) *topo.Graph[string, *InstallInfo] {
|
||||
if graph == nil {
|
||||
graph = NewGraph()
|
||||
}
|
||||
|
||||
graph.AddNode(pkg.Name)
|
||||
|
||||
g.addAurPkgProvides(pkg, graph)
|
||||
|
||||
g.ValidateAndSetNodeInfo(graph, pkg.Name, &topo.NodeInfo[*InstallInfo]{
|
||||
Color: colorMap[instalInfo.Reason],
|
||||
Background: bgColorMap[AUR],
|
||||
Value: instalInfo,
|
||||
})
|
||||
|
||||
return graph
|
||||
}
|
||||
|
||||
func (g *Grapher) GraphFromAUR(ctx context.Context,
|
||||
graph *topo.Graph[string, *InstallInfo],
|
||||
targets []string,
|
||||
) (*topo.Graph[string, *InstallInfo], error) {
|
||||
if graph == nil {
|
||||
graph = NewGraph()
|
||||
}
|
||||
|
||||
if len(targets) == 0 {
|
||||
return graph, nil
|
||||
}
|
||||
|
||||
aurPkgs, errCache := g.aurClient.Get(ctx, &aurc.Query{By: aurc.Name, Needles: targets})
|
||||
if errCache != nil {
|
||||
g.logger.Errorln(errCache)
|
||||
}
|
||||
|
||||
for i := range aurPkgs {
|
||||
pkg := &aurPkgs[i]
|
||||
if _, ok := g.providerCache[pkg.Name]; !ok {
|
||||
g.providerCache[pkg.Name] = []aurc.Pkg{*pkg}
|
||||
}
|
||||
}
|
||||
|
||||
aurPkgsAdded := []*aurc.Pkg{}
|
||||
|
||||
for _, target := range targets {
|
||||
if cachedProvidePkg, ok := g.providerCache[target]; ok {
|
||||
aurPkgs = cachedProvidePkg
|
||||
} else {
|
||||
var errA error
|
||||
aurPkgs, errA = g.aurClient.Get(ctx, &aurc.Query{By: aurc.Provides, Needles: []string{target}, Contains: true})
|
||||
if errA != nil {
|
||||
g.logger.Errorln(gotext.Get("Failed to find AUR package for"), " ", target, ":", errA)
|
||||
}
|
||||
}
|
||||
|
||||
if len(aurPkgs) == 0 {
|
||||
g.logger.Errorln(gotext.Get("No AUR package found for"), " ", target)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
aurPkg := &aurPkgs[0]
|
||||
if len(aurPkgs) > 1 {
|
||||
chosen := g.provideMenu(target, aurPkgs)
|
||||
aurPkg = chosen
|
||||
g.providerCache[target] = []aurc.Pkg{*aurPkg}
|
||||
}
|
||||
|
||||
reason := Explicit
|
||||
if pkg := g.dbExecutor.LocalPackage(aurPkg.Name); pkg != nil {
|
||||
reason = Reason(pkg.Reason())
|
||||
|
||||
if g.needed {
|
||||
if db.VerCmp(pkg.Version(), aurPkg.Version) >= 0 {
|
||||
g.logger.Warnln(gotext.Get("%s is up to date -- skipping", text.Cyan(pkg.Name()+"-"+pkg.Version())))
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
graph = g.GraphAURTarget(ctx, graph, aurPkg, &InstallInfo{
|
||||
AURBase: &aurPkg.PackageBase,
|
||||
Reason: reason,
|
||||
Source: AUR,
|
||||
Version: aurPkg.Version,
|
||||
})
|
||||
aurPkgsAdded = append(aurPkgsAdded, aurPkg)
|
||||
}
|
||||
|
||||
g.AddDepsForPkgs(ctx, aurPkgsAdded, graph)
|
||||
|
||||
return graph, nil
|
||||
}
|
||||
|
||||
// Removes found deps from the deps mapset and returns the found deps.
|
||||
func (g *Grapher) findDepsFromAUR(ctx context.Context,
|
||||
deps mapset.Set[string],
|
||||
) []aurc.Pkg {
|
||||
pkgsToAdd := make([]aurc.Pkg, 0, deps.Cardinality())
|
||||
if deps.Cardinality() == 0 {
|
||||
return []aurc.Pkg{}
|
||||
}
|
||||
|
||||
missingNeedles := make([]string, 0, deps.Cardinality())
|
||||
for _, depString := range deps.ToSlice() {
|
||||
if _, ok := g.providerCache[depString]; !ok {
|
||||
depName, _, _ := splitDep(depString)
|
||||
missingNeedles = append(missingNeedles, depName)
|
||||
}
|
||||
}
|
||||
|
||||
if len(missingNeedles) != 0 {
|
||||
g.logger.Debugln("deps to find", missingNeedles)
|
||||
// provider search is more demanding than a simple search
|
||||
// try to find name match if possible and then try to find provides.
|
||||
aurPkgs, errCache := g.aurClient.Get(ctx, &aurc.Query{
|
||||
By: aurc.Name, Needles: missingNeedles, Contains: false,
|
||||
})
|
||||
if errCache != nil {
|
||||
g.logger.Errorln(errCache)
|
||||
}
|
||||
|
||||
for i := range aurPkgs {
|
||||
pkg := &aurPkgs[i]
|
||||
if deps.Contains(pkg.Name) {
|
||||
g.providerCache[pkg.Name] = append(g.providerCache[pkg.Name], *pkg)
|
||||
}
|
||||
|
||||
for _, val := range pkg.Provides {
|
||||
if val == pkg.Name {
|
||||
continue
|
||||
}
|
||||
if deps.Contains(val) {
|
||||
g.providerCache[val] = append(g.providerCache[val], *pkg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, depString := range deps.ToSlice() {
|
||||
var aurPkgs []aurc.Pkg
|
||||
depName, _, _ := splitDep(depString)
|
||||
|
||||
if cachedProvidePkg, ok := g.providerCache[depString]; ok {
|
||||
aurPkgs = cachedProvidePkg
|
||||
} else {
|
||||
var errA error
|
||||
aurPkgs, errA = g.aurClient.Get(ctx, &aurc.Query{By: aurc.Provides, Needles: []string{depName}, Contains: true})
|
||||
if errA != nil {
|
||||
g.logger.Errorln(gotext.Get("Failed to find AUR package for"), depString, ":", errA)
|
||||
}
|
||||
}
|
||||
|
||||
// remove packages that don't satisfy the dependency
|
||||
satisfyingPkgs := make([]aurc.Pkg, 0, len(aurPkgs))
|
||||
for i := range aurPkgs {
|
||||
if satisfiesAur(depString, &aurPkgs[i]) {
|
||||
satisfyingPkgs = append(satisfyingPkgs, aurPkgs[i])
|
||||
}
|
||||
}
|
||||
aurPkgs = satisfyingPkgs
|
||||
|
||||
if len(aurPkgs) == 0 {
|
||||
g.logger.Errorln(gotext.Get("No AUR package found for"), " ", depString)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
pkg := aurPkgs[0]
|
||||
if len(aurPkgs) > 1 {
|
||||
chosen := g.provideMenu(depString, aurPkgs)
|
||||
pkg = *chosen
|
||||
}
|
||||
|
||||
g.providerCache[depString] = []aurc.Pkg{pkg}
|
||||
deps.Remove(depString)
|
||||
pkgsToAdd = append(pkgsToAdd, pkg)
|
||||
}
|
||||
|
||||
return pkgsToAdd
|
||||
}
|
||||
|
||||
func (g *Grapher) ValidateAndSetNodeInfo(graph *topo.Graph[string, *InstallInfo],
|
||||
node string, nodeInfo *topo.NodeInfo[*InstallInfo],
|
||||
) {
|
||||
info := graph.GetNodeInfo(node)
|
||||
if info != nil && info.Value != nil {
|
||||
if info.Value.Reason < nodeInfo.Value.Reason {
|
||||
return // refuse to downgrade reason
|
||||
}
|
||||
|
||||
if info.Value.Upgrade {
|
||||
return // refuse to overwrite an upgrade
|
||||
}
|
||||
}
|
||||
|
||||
graph.SetNodeInfo(node, nodeInfo)
|
||||
}
|
||||
|
||||
func (g *Grapher) addNodes(
|
||||
ctx context.Context,
|
||||
graph *topo.Graph[string, *InstallInfo],
|
||||
parentPkgName string,
|
||||
deps []string,
|
||||
depType Reason,
|
||||
) {
|
||||
targetsToFind := mapset.NewThreadUnsafeSet(deps...)
|
||||
// Check if in graph already
|
||||
for _, depString := range targetsToFind.ToSlice() {
|
||||
depName, _, _ := splitDep(depString)
|
||||
if !graph.Exists(depName) && !graph.ProvidesExists(depName) {
|
||||
continue
|
||||
}
|
||||
|
||||
if graph.Exists(depName) {
|
||||
if err := graph.DependOn(depName, parentPkgName); err != nil {
|
||||
g.logger.Warnln(depString, parentPkgName, err)
|
||||
}
|
||||
|
||||
targetsToFind.Remove(depString)
|
||||
}
|
||||
|
||||
if p := graph.GetProviderNode(depName); p != nil {
|
||||
if provideSatisfies(p.String(), depString, p.Version) {
|
||||
if err := graph.DependOn(p.Provider, parentPkgName); err != nil {
|
||||
g.logger.Warnln(p.Provider, parentPkgName, err)
|
||||
}
|
||||
|
||||
targetsToFind.Remove(depString)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check installed
|
||||
for _, depString := range targetsToFind.ToSlice() {
|
||||
depName, _, _ := splitDep(depString)
|
||||
if !g.dbExecutor.LocalSatisfierExists(depString) {
|
||||
continue
|
||||
}
|
||||
|
||||
if g.fullGraph {
|
||||
g.ValidateAndSetNodeInfo(
|
||||
graph,
|
||||
depName,
|
||||
&topo.NodeInfo[*InstallInfo]{Color: colorMap[depType], Background: bgColorMap[Local]})
|
||||
|
||||
if err := graph.DependOn(depName, parentPkgName); err != nil {
|
||||
g.logger.Warnln(depName, parentPkgName, err)
|
||||
}
|
||||
}
|
||||
|
||||
targetsToFind.Remove(depString)
|
||||
}
|
||||
|
||||
// Check Sync
|
||||
for _, depString := range targetsToFind.ToSlice() {
|
||||
alpmPkg := g.dbExecutor.SyncSatisfier(depString)
|
||||
if alpmPkg == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := graph.DependOn(alpmPkg.Name(), parentPkgName); err != nil {
|
||||
g.logger.Warnln("repo dep warn:", depString, parentPkgName, err)
|
||||
}
|
||||
|
||||
dbName := alpmPkg.DB().Name()
|
||||
g.ValidateAndSetNodeInfo(
|
||||
graph,
|
||||
alpmPkg.Name(),
|
||||
&topo.NodeInfo[*InstallInfo]{
|
||||
Color: colorMap[depType],
|
||||
Background: bgColorMap[Sync],
|
||||
Value: &InstallInfo{
|
||||
Source: Sync,
|
||||
Reason: depType,
|
||||
Version: alpmPkg.Version(),
|
||||
SyncDBName: &dbName,
|
||||
},
|
||||
})
|
||||
|
||||
if newDeps := alpmPkg.Depends().Slice(); len(newDeps) != 0 && g.fullGraph {
|
||||
newDepsSlice := make([]string, 0, len(newDeps))
|
||||
for _, newDep := range newDeps {
|
||||
newDepsSlice = append(newDepsSlice, newDep.Name)
|
||||
}
|
||||
|
||||
g.addNodes(ctx, graph, alpmPkg.Name(), newDepsSlice, Dep)
|
||||
}
|
||||
|
||||
targetsToFind.Remove(depString)
|
||||
}
|
||||
|
||||
// Check AUR
|
||||
pkgsToAdd := g.findDepsFromAUR(ctx, targetsToFind)
|
||||
for i := range pkgsToAdd {
|
||||
aurPkg := &pkgsToAdd[i]
|
||||
if err := graph.DependOn(aurPkg.Name, parentPkgName); err != nil {
|
||||
g.logger.Warnln("aur dep warn:", aurPkg.Name, parentPkgName, err)
|
||||
}
|
||||
|
||||
graph.SetNodeInfo(
|
||||
aurPkg.Name,
|
||||
&topo.NodeInfo[*InstallInfo]{
|
||||
Color: colorMap[depType],
|
||||
Background: bgColorMap[AUR],
|
||||
Value: &InstallInfo{
|
||||
Source: AUR,
|
||||
Reason: depType,
|
||||
AURBase: &aurPkg.PackageBase,
|
||||
Version: aurPkg.Version,
|
||||
},
|
||||
})
|
||||
|
||||
g.addDepNodes(ctx, aurPkg, graph)
|
||||
}
|
||||
|
||||
// Add missing to graph
|
||||
for _, depString := range targetsToFind.ToSlice() {
|
||||
depName, mod, ver := splitDep(depString)
|
||||
// no dep found. add as missing
|
||||
if err := graph.DependOn(depName, parentPkgName); err != nil {
|
||||
g.logger.Warnln("missing dep warn:", depString, parentPkgName, err)
|
||||
}
|
||||
graph.SetNodeInfo(depName, &topo.NodeInfo[*InstallInfo]{
|
||||
Color: colorMap[depType],
|
||||
Background: bgColorMap[Missing],
|
||||
Value: &InstallInfo{
|
||||
Source: Missing,
|
||||
Reason: depType,
|
||||
Version: fmt.Sprintf("%s%s", mod, ver),
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (g *Grapher) provideMenu(dep string, options []aur.Pkg) *aur.Pkg {
|
||||
size := len(options)
|
||||
if size == 1 {
|
||||
return &options[0]
|
||||
}
|
||||
|
||||
str := text.Bold(gotext.Get("There are %[1]d providers available for %[2]s:", size, dep))
|
||||
str += "\n"
|
||||
|
||||
size = 1
|
||||
str += g.logger.SprintOperationInfo(gotext.Get("Repository AUR"), "\n ")
|
||||
|
||||
for i := range options {
|
||||
str += fmt.Sprintf("%d) %s ", size, options[i].Name)
|
||||
size++
|
||||
}
|
||||
|
||||
g.logger.OperationInfoln(str)
|
||||
|
||||
for {
|
||||
g.logger.Println(gotext.Get("\nEnter a number (default=1): "))
|
||||
|
||||
if g.noConfirm {
|
||||
g.logger.Println("1")
|
||||
|
||||
return &options[0]
|
||||
}
|
||||
|
||||
numberBuf, err := g.logger.GetInput("", false)
|
||||
if err != nil {
|
||||
g.logger.Errorln(err)
|
||||
|
||||
return &options[0]
|
||||
}
|
||||
|
||||
if numberBuf == "" {
|
||||
return &options[0]
|
||||
}
|
||||
|
||||
num, err := strconv.Atoi(numberBuf)
|
||||
if err != nil {
|
||||
g.logger.Errorln(gotext.Get("invalid number: %s", numberBuf))
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if num < 1 || num >= size {
|
||||
g.logger.Errorln(gotext.Get("invalid value: %d is not between %d and %d",
|
||||
num, 1, size-1))
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
return &options[num-1]
|
||||
}
|
||||
}
|
||||
|
||||
func makeAURPKGFromSrcinfo(dbExecutor db.Executor, srcInfo *gosrc.Srcinfo) ([]*aur.Pkg, error) {
|
||||
pkgs := make([]*aur.Pkg, 0, 1)
|
||||
|
||||
alpmArch, err := dbExecutor.AlpmArchitectures()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
alpmArch = append(alpmArch, "") // srcinfo assumes no value as ""
|
||||
|
||||
getDesc := func(pkg *gosrc.Package) string {
|
||||
if pkg.Pkgdesc != "" {
|
||||
return pkg.Pkgdesc
|
||||
}
|
||||
|
||||
return srcInfo.Pkgdesc
|
||||
}
|
||||
|
||||
for i := range srcInfo.Packages {
|
||||
pkg := &srcInfo.Packages[i]
|
||||
|
||||
pkgs = append(pkgs, &aur.Pkg{
|
||||
ID: 0,
|
||||
Name: pkg.Pkgname,
|
||||
PackageBaseID: 0,
|
||||
PackageBase: srcInfo.Pkgbase,
|
||||
Version: srcInfo.Version(),
|
||||
Description: getDesc(pkg),
|
||||
URL: pkg.URL,
|
||||
Depends: append(archStringToString(alpmArch, pkg.Depends),
|
||||
archStringToString(alpmArch, srcInfo.Depends)...),
|
||||
MakeDepends: archStringToString(alpmArch, srcInfo.MakeDepends),
|
||||
CheckDepends: archStringToString(alpmArch, srcInfo.CheckDepends),
|
||||
Conflicts: append(archStringToString(alpmArch, pkg.Conflicts),
|
||||
archStringToString(alpmArch, srcInfo.Conflicts)...),
|
||||
Provides: append(archStringToString(alpmArch, pkg.Provides),
|
||||
archStringToString(alpmArch, srcInfo.Provides)...),
|
||||
Replaces: append(archStringToString(alpmArch, pkg.Replaces),
|
||||
archStringToString(alpmArch, srcInfo.Replaces)...),
|
||||
OptDepends: append(archStringToString(alpmArch, pkg.OptDepends),
|
||||
archStringToString(alpmArch, srcInfo.OptDepends)...),
|
||||
Groups: pkg.Groups,
|
||||
License: pkg.License,
|
||||
Keywords: []string{},
|
||||
})
|
||||
}
|
||||
|
||||
return pkgs, nil
|
||||
}
|
||||
|
||||
func archStringToString(alpmArches []string, archString []gosrc.ArchString) []string {
|
||||
pkgs := make([]string, 0, len(archString))
|
||||
|
||||
for _, arch := range archString {
|
||||
if db.ArchIsSupported(alpmArches, arch.Arch) {
|
||||
pkgs = append(pkgs, arch.Value)
|
||||
}
|
||||
}
|
||||
|
||||
return pkgs
|
||||
}
|
||||
|
||||
func aurDepModToAlpmDep(mod string) alpm.DepMod {
|
||||
switch mod {
|
||||
case "=":
|
||||
return alpm.DepModEq
|
||||
case ">=":
|
||||
return alpm.DepModGE
|
||||
case "<=":
|
||||
return alpm.DepModLE
|
||||
case ">":
|
||||
return alpm.DepModGT
|
||||
case "<":
|
||||
return alpm.DepModLT
|
||||
}
|
||||
return alpm.DepModAny
|
||||
}
|
@ -1,811 +0,0 @@
|
||||
//go:build !integration
|
||||
// +build !integration
|
||||
|
||||
package dep
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
aurc "github.com/Jguer/aur"
|
||||
alpm "github.com/Jguer/go-alpm/v2"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/Jguer/yay/v12/pkg/db"
|
||||
"github.com/Jguer/yay/v12/pkg/db/mock"
|
||||
mockaur "github.com/Jguer/yay/v12/pkg/dep/mock"
|
||||
aur "github.com/Jguer/yay/v12/pkg/query"
|
||||
"github.com/Jguer/yay/v12/pkg/text"
|
||||
)
|
||||
|
||||
func ptrString(s string) *string {
|
||||
return &s
|
||||
}
|
||||
|
||||
func getFromFile(t *testing.T, filePath string) mockaur.GetFunc {
|
||||
f, err := os.Open(filePath)
|
||||
require.NoError(t, err)
|
||||
|
||||
fBytes, err := io.ReadAll(f)
|
||||
require.NoError(t, err)
|
||||
|
||||
pkgs := []aur.Pkg{}
|
||||
err = json.Unmarshal(fBytes, &pkgs)
|
||||
require.NoError(t, err)
|
||||
|
||||
return func(ctx context.Context, query *aurc.Query) ([]aur.Pkg, error) {
|
||||
return pkgs, nil
|
||||
}
|
||||
}
|
||||
|
||||
func TestGrapher_GraphFromTargets_jellyfin(t *testing.T) {
|
||||
mockDB := &mock.DBExecutor{
|
||||
SyncPackageFn: func(string) mock.IPackage { return nil },
|
||||
SyncSatisfierFn: func(s string) mock.IPackage {
|
||||
switch s {
|
||||
case "jellyfin":
|
||||
return nil
|
||||
case "dotnet-runtime-6.0":
|
||||
return &mock.Package{
|
||||
PName: "dotnet-runtime-6.0",
|
||||
PBase: "dotnet-runtime-6.0",
|
||||
PVersion: "6.0.100-1",
|
||||
PDB: mock.NewDB("community"),
|
||||
}
|
||||
case "dotnet-sdk-6.0":
|
||||
return &mock.Package{
|
||||
PName: "dotnet-sdk-6.0",
|
||||
PBase: "dotnet-sdk-6.0",
|
||||
PVersion: "6.0.100-1",
|
||||
PDB: mock.NewDB("community"),
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
PackagesFromGroupFn: func(string) []mock.IPackage { return nil },
|
||||
LocalSatisfierExistsFn: func(s string) bool {
|
||||
switch s {
|
||||
case "dotnet-sdk-6.0", "dotnet-runtime-6.0", "jellyfin-server=10.8.8", "jellyfin-web=10.8.8":
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
},
|
||||
LocalPackageFn: func(string) mock.IPackage { return nil },
|
||||
}
|
||||
|
||||
mockAUR := &mockaur.MockAUR{GetFn: func(ctx context.Context, query *aurc.Query) ([]aur.Pkg, error) {
|
||||
if query.Needles[0] == "jellyfin" {
|
||||
jfinFn := getFromFile(t, "testdata/jellyfin.json")
|
||||
return jfinFn(ctx, query)
|
||||
}
|
||||
|
||||
if query.Needles[0] == "jellyfin-web" {
|
||||
jfinWebFn := getFromFile(t, "testdata/jellyfin-web.json")
|
||||
return jfinWebFn(ctx, query)
|
||||
}
|
||||
|
||||
if query.Needles[0] == "jellyfin-server" {
|
||||
jfinServerFn := getFromFile(t, "testdata/jellyfin-server.json")
|
||||
return jfinServerFn(ctx, query)
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("implement me %v", query.Needles))
|
||||
}}
|
||||
|
||||
type fields struct {
|
||||
dbExecutor db.Executor
|
||||
aurCache aurc.QueryClient
|
||||
noDeps bool
|
||||
noCheckDeps bool
|
||||
}
|
||||
type args struct {
|
||||
targets []string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
want []map[string]*InstallInfo
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "noDeps",
|
||||
fields: fields{
|
||||
dbExecutor: mockDB,
|
||||
aurCache: mockAUR,
|
||||
noDeps: true,
|
||||
noCheckDeps: false,
|
||||
},
|
||||
args: args{
|
||||
targets: []string{"jellyfin"},
|
||||
},
|
||||
want: []map[string]*InstallInfo{
|
||||
{
|
||||
"jellyfin": {
|
||||
Source: AUR,
|
||||
Reason: Explicit,
|
||||
Version: "10.8.8-1",
|
||||
AURBase: ptrString("jellyfin"),
|
||||
},
|
||||
},
|
||||
{
|
||||
"dotnet-sdk-6.0": {
|
||||
Source: Sync,
|
||||
Reason: MakeDep,
|
||||
Version: "6.0.100-1",
|
||||
SyncDBName: ptrString("community"),
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "deps",
|
||||
fields: fields{
|
||||
dbExecutor: mockDB,
|
||||
aurCache: mockAUR,
|
||||
noDeps: false,
|
||||
noCheckDeps: false,
|
||||
},
|
||||
args: args{
|
||||
targets: []string{"jellyfin"},
|
||||
},
|
||||
want: []map[string]*InstallInfo{
|
||||
{
|
||||
"jellyfin": {
|
||||
Source: AUR,
|
||||
Reason: Explicit,
|
||||
Version: "10.8.8-1",
|
||||
AURBase: ptrString("jellyfin"),
|
||||
},
|
||||
},
|
||||
{
|
||||
"jellyfin-web": {
|
||||
Source: AUR,
|
||||
Reason: Dep,
|
||||
Version: "10.8.8-1",
|
||||
AURBase: ptrString("jellyfin"),
|
||||
},
|
||||
"jellyfin-server": {
|
||||
Source: AUR,
|
||||
Reason: Dep,
|
||||
Version: "10.8.8-1",
|
||||
AURBase: ptrString("jellyfin"),
|
||||
},
|
||||
},
|
||||
{
|
||||
"dotnet-sdk-6.0": {
|
||||
Source: Sync,
|
||||
Reason: MakeDep,
|
||||
Version: "6.0.100-1",
|
||||
SyncDBName: ptrString("community"),
|
||||
},
|
||||
"dotnet-runtime-6.0": {
|
||||
Source: Sync,
|
||||
Reason: Dep,
|
||||
Version: "6.0.100-1",
|
||||
SyncDBName: ptrString("community"),
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
g := NewGrapher(tt.fields.dbExecutor,
|
||||
tt.fields.aurCache, false, true,
|
||||
tt.fields.noDeps, tt.fields.noCheckDeps, false,
|
||||
text.NewLogger(io.Discard, io.Discard, &os.File{}, true, "test"))
|
||||
got, err := g.GraphFromTargets(context.Background(), nil, tt.args.targets)
|
||||
require.NoError(t, err)
|
||||
layers := got.TopoSortedLayerMap(nil)
|
||||
require.EqualValues(t, tt.want, layers, layers)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGrapher_GraphProvides_androidsdk(t *testing.T) {
|
||||
mockDB := &mock.DBExecutor{
|
||||
SyncPackageFn: func(string) mock.IPackage { return nil },
|
||||
SyncSatisfierFn: func(s string) mock.IPackage {
|
||||
switch s {
|
||||
case "android-sdk":
|
||||
return nil
|
||||
case "jdk11-openjdk":
|
||||
return &mock.Package{
|
||||
PName: "jdk11-openjdk",
|
||||
PVersion: "11.0.12.u7-1",
|
||||
PDB: mock.NewDB("community"),
|
||||
PProvides: mock.DependList{
|
||||
Depends: []alpm.Depend{
|
||||
{Name: "java-environment", Version: "11", Mod: alpm.DepModEq},
|
||||
{Name: "java-environment-openjdk", Version: "11", Mod: alpm.DepModEq},
|
||||
{Name: "jdk11-openjdk", Version: "11.0.19.u7-1", Mod: alpm.DepModEq},
|
||||
},
|
||||
},
|
||||
}
|
||||
case "java-environment":
|
||||
panic("not supposed to be called")
|
||||
}
|
||||
panic("implement me " + s)
|
||||
},
|
||||
PackagesFromGroupFn: func(string) []mock.IPackage { return nil },
|
||||
LocalSatisfierExistsFn: func(s string) bool {
|
||||
switch s {
|
||||
case "java-environment":
|
||||
return false
|
||||
}
|
||||
|
||||
switch s {
|
||||
case "libxtst", "fontconfig", "freetype2", "lib32-gcc-libs", "lib32-glibc", "libx11", "libxext", "libxrender", "zlib", "gcc-libs":
|
||||
return true
|
||||
}
|
||||
|
||||
panic("implement me " + s)
|
||||
},
|
||||
LocalPackageFn: func(string) mock.IPackage { return nil },
|
||||
}
|
||||
|
||||
mockAUR := &mockaur.MockAUR{GetFn: func(ctx context.Context, query *aurc.Query) ([]aur.Pkg, error) {
|
||||
if query.Needles[0] == "android-sdk" {
|
||||
jfinFn := getFromFile(t, "testdata/android-sdk.json")
|
||||
return jfinFn(ctx, query)
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("implement me %v", query.Needles))
|
||||
}}
|
||||
|
||||
type fields struct {
|
||||
dbExecutor db.Executor
|
||||
aurCache aurc.QueryClient
|
||||
noDeps bool
|
||||
noCheckDeps bool
|
||||
}
|
||||
type args struct {
|
||||
targets []string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
want []map[string]*InstallInfo
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "explicit dep",
|
||||
fields: fields{
|
||||
dbExecutor: mockDB,
|
||||
aurCache: mockAUR,
|
||||
noDeps: false,
|
||||
noCheckDeps: false,
|
||||
},
|
||||
args: args{
|
||||
targets: []string{"android-sdk", "jdk11-openjdk"},
|
||||
},
|
||||
want: []map[string]*InstallInfo{
|
||||
{
|
||||
"android-sdk": {
|
||||
Source: AUR,
|
||||
Reason: Explicit,
|
||||
Version: "26.1.1-2",
|
||||
AURBase: ptrString("android-sdk"),
|
||||
},
|
||||
},
|
||||
{
|
||||
"jdk11-openjdk": {
|
||||
Source: Sync,
|
||||
Reason: Explicit,
|
||||
Version: "11.0.12.u7-1",
|
||||
SyncDBName: ptrString("community"),
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
g := NewGrapher(tt.fields.dbExecutor,
|
||||
tt.fields.aurCache, false, true,
|
||||
tt.fields.noDeps, tt.fields.noCheckDeps, false,
|
||||
text.NewLogger(io.Discard, io.Discard, &os.File{}, true, "test"))
|
||||
got, err := g.GraphFromTargets(context.Background(), nil, tt.args.targets)
|
||||
require.NoError(t, err)
|
||||
layers := got.TopoSortedLayerMap(nil)
|
||||
require.EqualValues(t, tt.want, layers, layers)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGrapher_GraphFromAUR_Deps_ceph_bin(t *testing.T) {
|
||||
mockDB := &mock.DBExecutor{
|
||||
SyncPackageFn: func(string) mock.IPackage { return nil },
|
||||
PackagesFromGroupFn: func(string) []mock.IPackage { return []mock.IPackage{} },
|
||||
SyncSatisfierFn: func(s string) mock.IPackage {
|
||||
switch s {
|
||||
case "ceph-bin", "ceph-libs-bin":
|
||||
return nil
|
||||
case "ceph", "ceph-libs", "ceph-libs=17.2.6-2":
|
||||
return nil
|
||||
}
|
||||
|
||||
panic("implement me " + s)
|
||||
},
|
||||
|
||||
LocalSatisfierExistsFn: func(s string) bool {
|
||||
switch s {
|
||||
case "ceph-libs", "ceph-libs=17.2.6-2":
|
||||
return false
|
||||
case "dep1", "dep2", "dep3", "makedep1", "makedep2", "checkdep1":
|
||||
return true
|
||||
}
|
||||
|
||||
panic("implement me " + s)
|
||||
},
|
||||
LocalPackageFn: func(string) mock.IPackage { return nil },
|
||||
}
|
||||
|
||||
mockAUR := &mockaur.MockAUR{GetFn: func(ctx context.Context, query *aurc.Query) ([]aur.Pkg, error) {
|
||||
mockPkgs := map[string]aur.Pkg{
|
||||
"ceph-bin": {
|
||||
Name: "ceph-bin",
|
||||
PackageBase: "ceph-bin",
|
||||
Version: "17.2.6-2",
|
||||
Depends: []string{"ceph-libs=17.2.6-2", "dep1"},
|
||||
Provides: []string{"ceph=17.2.6-2"},
|
||||
},
|
||||
"ceph-libs-bin": {
|
||||
Name: "ceph-libs-bin",
|
||||
PackageBase: "ceph-bin",
|
||||
Version: "17.2.6-2",
|
||||
Depends: []string{"dep1", "dep2"},
|
||||
Provides: []string{"ceph-libs=17.2.6-2"},
|
||||
},
|
||||
"ceph": {
|
||||
Name: "ceph",
|
||||
PackageBase: "ceph",
|
||||
Version: "17.2.6-2",
|
||||
Depends: []string{"ceph-libs=17.2.6-2", "dep1"},
|
||||
MakeDepends: []string{"makedep1"},
|
||||
CheckDepends: []string{"checkdep1"},
|
||||
Provides: []string{"ceph=17.2.6-2"},
|
||||
},
|
||||
"ceph-libs": {
|
||||
Name: "ceph-libs",
|
||||
PackageBase: "ceph",
|
||||
Version: "17.2.6-2",
|
||||
Depends: []string{"dep1", "dep2", "dep3"},
|
||||
MakeDepends: []string{"makedep1", "makedep2"},
|
||||
CheckDepends: []string{"checkdep1"},
|
||||
Provides: []string{"ceph-libs=17.2.6-2"},
|
||||
},
|
||||
}
|
||||
|
||||
pkgs := []aur.Pkg{}
|
||||
for _, needle := range query.Needles {
|
||||
if pkg, ok := mockPkgs[needle]; ok {
|
||||
pkgs = append(pkgs, pkg)
|
||||
} else {
|
||||
panic(fmt.Sprintf("implement me %v", needle))
|
||||
}
|
||||
}
|
||||
|
||||
return pkgs, nil
|
||||
}}
|
||||
|
||||
installInfos := map[string]*InstallInfo{
|
||||
"ceph-bin exp": {
|
||||
Source: AUR,
|
||||
Reason: Explicit,
|
||||
Version: "17.2.6-2",
|
||||
AURBase: ptrString("ceph-bin"),
|
||||
},
|
||||
"ceph-libs-bin exp": {
|
||||
Source: AUR,
|
||||
Reason: Explicit,
|
||||
Version: "17.2.6-2",
|
||||
AURBase: ptrString("ceph-bin"),
|
||||
},
|
||||
"ceph exp": {
|
||||
Source: AUR,
|
||||
Reason: Explicit,
|
||||
Version: "17.2.6-2",
|
||||
AURBase: ptrString("ceph"),
|
||||
},
|
||||
"ceph-libs exp": {
|
||||
Source: AUR,
|
||||
Reason: Explicit,
|
||||
Version: "17.2.6-2",
|
||||
AURBase: ptrString("ceph"),
|
||||
},
|
||||
"ceph-libs dep": {
|
||||
Source: AUR,
|
||||
Reason: Dep,
|
||||
Version: "17.2.6-2",
|
||||
AURBase: ptrString("ceph"),
|
||||
},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
targets []string
|
||||
wantLayers []map[string]*InstallInfo
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "ceph-bin ceph-libs-bin",
|
||||
targets: []string{"ceph-bin", "ceph-libs-bin"},
|
||||
wantLayers: []map[string]*InstallInfo{
|
||||
{"ceph-bin": installInfos["ceph-bin exp"]},
|
||||
{"ceph-libs-bin": installInfos["ceph-libs-bin exp"]},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "ceph-libs-bin ceph-bin (reversed order)",
|
||||
targets: []string{"ceph-libs-bin", "ceph-bin"},
|
||||
wantLayers: []map[string]*InstallInfo{
|
||||
{"ceph-bin": installInfos["ceph-bin exp"]},
|
||||
{"ceph-libs-bin": installInfos["ceph-libs-bin exp"]},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "ceph",
|
||||
targets: []string{"ceph"},
|
||||
wantLayers: []map[string]*InstallInfo{
|
||||
{"ceph": installInfos["ceph exp"]},
|
||||
{"ceph-libs": installInfos["ceph-libs dep"]},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "ceph-bin",
|
||||
targets: []string{"ceph-bin"},
|
||||
wantLayers: []map[string]*InstallInfo{
|
||||
{"ceph-bin": installInfos["ceph-bin exp"]},
|
||||
{"ceph-libs": installInfos["ceph-libs dep"]},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "ceph-bin ceph-libs",
|
||||
targets: []string{"ceph-bin", "ceph-libs"},
|
||||
wantLayers: []map[string]*InstallInfo{
|
||||
{"ceph-bin": installInfos["ceph-bin exp"]},
|
||||
{"ceph-libs": installInfos["ceph-libs exp"]},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "ceph-libs ceph-bin (reversed order)",
|
||||
targets: []string{"ceph-libs", "ceph-bin"},
|
||||
wantLayers: []map[string]*InstallInfo{
|
||||
{"ceph-bin": installInfos["ceph-bin exp"]},
|
||||
{"ceph-libs": installInfos["ceph-libs exp"]},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "ceph ceph-libs-bin",
|
||||
targets: []string{"ceph", "ceph-libs-bin"},
|
||||
wantLayers: []map[string]*InstallInfo{
|
||||
{"ceph": installInfos["ceph exp"]},
|
||||
{"ceph-libs-bin": installInfos["ceph-libs-bin exp"]},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "ceph-libs-bin ceph (reversed order)",
|
||||
targets: []string{"ceph-libs-bin", "ceph"},
|
||||
wantLayers: []map[string]*InstallInfo{
|
||||
{"ceph": installInfos["ceph exp"]},
|
||||
{"ceph-libs-bin": installInfos["ceph-libs-bin exp"]},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
g := NewGrapher(mockDB, mockAUR,
|
||||
false, true, false, false, false,
|
||||
text.NewLogger(io.Discard, io.Discard, &os.File{}, true, "test"))
|
||||
got, err := g.GraphFromTargets(context.Background(), nil, tt.targets)
|
||||
require.NoError(t, err)
|
||||
layers := got.TopoSortedLayerMap(nil)
|
||||
require.EqualValues(t, tt.wantLayers, layers, layers)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGrapher_GraphFromAUR_Deps_gourou(t *testing.T) {
|
||||
mockDB := &mock.DBExecutor{
|
||||
SyncPackageFn: func(string) mock.IPackage { return nil },
|
||||
PackagesFromGroupFn: func(string) []mock.IPackage { return []mock.IPackage{} },
|
||||
SyncSatisfierFn: func(s string) mock.IPackage {
|
||||
switch s {
|
||||
case "gourou", "libzip-git":
|
||||
return nil
|
||||
case "libzip":
|
||||
return &mock.Package{
|
||||
PName: "libzip",
|
||||
PVersion: "1.9.2-1",
|
||||
PDB: mock.NewDB("extra"),
|
||||
}
|
||||
}
|
||||
|
||||
panic("implement me " + s)
|
||||
},
|
||||
|
||||
LocalSatisfierExistsFn: func(s string) bool {
|
||||
switch s {
|
||||
case "gourou", "libzip", "libzip-git":
|
||||
return false
|
||||
case "dep1", "dep2":
|
||||
return true
|
||||
}
|
||||
|
||||
panic("implement me " + s)
|
||||
},
|
||||
LocalPackageFn: func(string) mock.IPackage { return nil },
|
||||
}
|
||||
|
||||
mockAUR := &mockaur.MockAUR{GetFn: func(ctx context.Context, query *aurc.Query) ([]aur.Pkg, error) {
|
||||
mockPkgs := map[string]aur.Pkg{
|
||||
"gourou": {
|
||||
Name: "gourou",
|
||||
PackageBase: "gourou",
|
||||
Version: "0.8.1",
|
||||
Depends: []string{"libzip"},
|
||||
},
|
||||
"libzip-git": {
|
||||
Name: "libzip-git",
|
||||
PackageBase: "libzip-git",
|
||||
Version: "1.9.2.r159.gb3ac716c-1",
|
||||
Depends: []string{"dep1", "dep2"},
|
||||
Provides: []string{"libzip=1.9.2.r159.gb3ac716c"},
|
||||
},
|
||||
}
|
||||
|
||||
pkgs := []aur.Pkg{}
|
||||
for _, needle := range query.Needles {
|
||||
if pkg, ok := mockPkgs[needle]; ok {
|
||||
pkgs = append(pkgs, pkg)
|
||||
} else {
|
||||
panic(fmt.Sprintf("implement me %v", needle))
|
||||
}
|
||||
}
|
||||
|
||||
return pkgs, nil
|
||||
}}
|
||||
|
||||
installInfos := map[string]*InstallInfo{
|
||||
"gourou exp": {
|
||||
Source: AUR,
|
||||
Reason: Explicit,
|
||||
Version: "0.8.1",
|
||||
AURBase: ptrString("gourou"),
|
||||
},
|
||||
"libzip dep": {
|
||||
Source: Sync,
|
||||
Reason: Dep,
|
||||
Version: "1.9.2-1",
|
||||
SyncDBName: ptrString("extra"),
|
||||
},
|
||||
"libzip exp": {
|
||||
Source: Sync,
|
||||
Reason: Explicit,
|
||||
Version: "1.9.2-1",
|
||||
SyncDBName: ptrString("extra"),
|
||||
},
|
||||
"libzip-git exp": {
|
||||
Source: AUR,
|
||||
Reason: Explicit,
|
||||
Version: "1.9.2.r159.gb3ac716c-1",
|
||||
AURBase: ptrString("libzip-git"),
|
||||
},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
targets []string
|
||||
wantLayers []map[string]*InstallInfo
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "gourou",
|
||||
targets: []string{"gourou"},
|
||||
wantLayers: []map[string]*InstallInfo{
|
||||
{"gourou": installInfos["gourou exp"]},
|
||||
{"libzip": installInfos["libzip dep"]},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "gourou libzip",
|
||||
targets: []string{"gourou", "libzip"},
|
||||
wantLayers: []map[string]*InstallInfo{
|
||||
{"gourou": installInfos["gourou exp"]},
|
||||
{"libzip": installInfos["libzip exp"]},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "gourou libzip-git",
|
||||
targets: []string{"gourou", "libzip-git"},
|
||||
wantLayers: []map[string]*InstallInfo{
|
||||
{"gourou": installInfos["gourou exp"]},
|
||||
{"libzip-git": installInfos["libzip-git exp"]},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "libzip-git gourou (reversed order)",
|
||||
targets: []string{"libzip-git", "gourou"},
|
||||
wantLayers: []map[string]*InstallInfo{
|
||||
{"gourou": installInfos["gourou exp"]},
|
||||
{"libzip-git": installInfos["libzip-git exp"]},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
g := NewGrapher(mockDB, mockAUR,
|
||||
false, true, false, false, false,
|
||||
text.NewLogger(io.Discard, io.Discard, &os.File{}, true, "test"))
|
||||
got, err := g.GraphFromTargets(context.Background(), nil, tt.targets)
|
||||
require.NoError(t, err)
|
||||
layers := got.TopoSortedLayerMap(nil)
|
||||
require.EqualValues(t, tt.wantLayers, layers, layers)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGrapher_GraphFromTargets_ReinstalledDeps(t *testing.T) {
|
||||
mockDB := &mock.DBExecutor{
|
||||
SyncPackageFn: func(string) mock.IPackage { return nil },
|
||||
PackagesFromGroupFn: func(string) []mock.IPackage { return []mock.IPackage{} },
|
||||
SyncSatisfierFn: func(s string) mock.IPackage {
|
||||
switch s {
|
||||
case "gourou":
|
||||
return nil
|
||||
case "libzip":
|
||||
return &mock.Package{
|
||||
PName: "libzip",
|
||||
PVersion: "1.9.2-1",
|
||||
PDB: mock.NewDB("extra"),
|
||||
}
|
||||
}
|
||||
|
||||
panic("implement me " + s)
|
||||
},
|
||||
SatisfierFromDBFn: func(s, s2 string) (mock.IPackage, error) {
|
||||
if s2 == "extra" {
|
||||
switch s {
|
||||
case "libzip":
|
||||
return &mock.Package{
|
||||
PName: "libzip",
|
||||
PVersion: "1.9.2-1",
|
||||
PDB: mock.NewDB("extra"),
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
panic("implement me " + s2 + "/" + s)
|
||||
},
|
||||
|
||||
LocalSatisfierExistsFn: func(s string) bool {
|
||||
switch s {
|
||||
case "gourou", "libzip":
|
||||
return true
|
||||
}
|
||||
|
||||
panic("implement me " + s)
|
||||
},
|
||||
LocalPackageFn: func(s string) mock.IPackage {
|
||||
switch s {
|
||||
case "libzip":
|
||||
return &mock.Package{
|
||||
PName: "libzip",
|
||||
PVersion: "1.9.2-1",
|
||||
PDB: mock.NewDB("extra"),
|
||||
PReason: alpm.PkgReasonDepend,
|
||||
}
|
||||
case "gourou":
|
||||
return &mock.Package{
|
||||
PName: "gourou",
|
||||
PVersion: "0.8.1",
|
||||
PDB: mock.NewDB("aur"),
|
||||
PReason: alpm.PkgReasonDepend,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
mockAUR := &mockaur.MockAUR{GetFn: func(ctx context.Context, query *aurc.Query) ([]aur.Pkg, error) {
|
||||
mockPkgs := map[string]aur.Pkg{
|
||||
"gourou": {
|
||||
Name: "gourou",
|
||||
PackageBase: "gourou",
|
||||
Version: "0.8.1",
|
||||
Depends: []string{"libzip"},
|
||||
},
|
||||
}
|
||||
|
||||
pkgs := []aur.Pkg{}
|
||||
for _, needle := range query.Needles {
|
||||
if pkg, ok := mockPkgs[needle]; ok {
|
||||
pkgs = append(pkgs, pkg)
|
||||
} else {
|
||||
panic(fmt.Sprintf("implement me %v", needle))
|
||||
}
|
||||
}
|
||||
|
||||
return pkgs, nil
|
||||
}}
|
||||
|
||||
installInfos := map[string]*InstallInfo{
|
||||
"gourou dep": {
|
||||
Source: AUR,
|
||||
Reason: Dep,
|
||||
Version: "0.8.1",
|
||||
AURBase: ptrString("gourou"),
|
||||
},
|
||||
"libzip dep": {
|
||||
Source: Sync,
|
||||
Reason: Dep,
|
||||
Version: "1.9.2-1",
|
||||
SyncDBName: ptrString("extra"),
|
||||
},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
targets []string
|
||||
wantLayers []map[string]*InstallInfo
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "gourou libzip",
|
||||
targets: []string{"gourou", "libzip"},
|
||||
wantLayers: []map[string]*InstallInfo{
|
||||
{"gourou": installInfos["gourou dep"]},
|
||||
{"libzip": installInfos["libzip dep"]},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "aur/gourou extra/libzip",
|
||||
targets: []string{"aur/gourou", "extra/libzip"},
|
||||
wantLayers: []map[string]*InstallInfo{
|
||||
{"gourou": installInfos["gourou dep"]},
|
||||
{"libzip": installInfos["libzip dep"]},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
g := NewGrapher(mockDB, mockAUR,
|
||||
false, true, false, false, false,
|
||||
text.NewLogger(io.Discard, io.Discard, &os.File{}, true, "test"))
|
||||
got, err := g.GraphFromTargets(context.Background(), nil, tt.targets)
|
||||
require.NoError(t, err)
|
||||
layers := got.TopoSortedLayerMap(nil)
|
||||
require.EqualValues(t, tt.wantLayers, layers, layers)
|
||||
})
|
||||
}
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
package mock
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/Jguer/aur"
|
||||
)
|
||||
|
||||
type GetFunc func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error)
|
||||
|
||||
type MockAUR struct {
|
||||
GetFn GetFunc
|
||||
}
|
||||
|
||||
func (m *MockAUR) Get(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
|
||||
if m.GetFn != nil {
|
||||
return m.GetFn(ctx, query)
|
||||
}
|
||||
|
||||
panic("implement me")
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
package dep
|
||||
|
||||
import "github.com/Jguer/yay/v12/pkg/text"
|
||||
|
||||
type Target struct {
|
||||
DB string
|
||||
Name string
|
||||
Mod string
|
||||
Version string
|
||||
}
|
||||
|
||||
func ToTarget(pkg string) Target {
|
||||
dbName, depString := text.SplitDBFromName(pkg)
|
||||
name, mod, depVersion := splitDep(depString)
|
||||
|
||||
return Target{
|
||||
DB: dbName,
|
||||
Name: name,
|
||||
Mod: mod,
|
||||
Version: depVersion,
|
||||
}
|
||||
}
|
||||
|
||||
func (t Target) DepString() string {
|
||||
return t.Name + t.Mod + t.Version
|
||||
}
|
||||
|
||||
func (t Target) String() string {
|
||||
if t.DB != "" {
|
||||
return t.DB + "/" + t.DepString()
|
||||
}
|
||||
|
||||
return t.DepString()
|
||||
}
|
3
pkg/dep/testdata/android-sdk.json
vendored
3
pkg/dep/testdata/android-sdk.json
vendored
@ -1,3 +0,0 @@
|
||||
[
|
||||
{"ID":1055234,"Name":"android-sdk","PackageBaseID":13751,"PackageBase":"android-sdk","Version":"26.1.1-2","Description":"Google Android SDK","URL":"https://developer.android.com/studio/releases/sdk-tools.html","NumVotes":1487,"Popularity":0.802316,"OutOfDate":null,"Maintainer":"dreamingincode","Submitter":null,"FirstSubmitted":1194895596,"LastModified":1647982720,"URLPath":"/cgit/aur.git/snapshot/android-sdk.tar.gz","Depends":["java-environment","libxtst","fontconfig","freetype2","lib32-gcc-libs","lib32-glibc","libx11","libxext","libxrender","zlib","gcc-libs"],"OptDepends":["android-emulator","android-sdk-platform-tools","android-udev"],"License":["custom"],"Keywords":["android","development"]}
|
||||
]
|
3
pkg/dep/testdata/jellyfin-server.json
vendored
3
pkg/dep/testdata/jellyfin-server.json
vendored
@ -1,3 +0,0 @@
|
||||
[
|
||||
{"ID":1176791,"Name":"jellyfin-server","PackageBaseID":138631,"PackageBase":"jellyfin","Version":"10.8.8-1","Description":"Jellyfin server component","URL":"https://github.com/jellyfin/jellyfin","NumVotes":84,"Popularity":1.272964,"OutOfDate":null,"Maintainer":"z3ntu","Submitter":"z3ntu","FirstSubmitted":1547053171,"LastModified":1669830147,"URLPath":"/cgit/aur.git/snapshot/jellyfin-server.tar.gz","Depends":["dotnet-runtime-6.0","aspnet-runtime-6.0","ffmpeg","sqlite"],"MakeDepends":["dotnet-sdk-6.0","nodejs","npm","git"],"License":["GPL2"]}
|
||||
]
|
3
pkg/dep/testdata/jellyfin-web.json
vendored
3
pkg/dep/testdata/jellyfin-web.json
vendored
@ -1,3 +0,0 @@
|
||||
[
|
||||
{"ID":1176790,"Name":"jellyfin-web","PackageBaseID":138631,"PackageBase":"jellyfin","Version":"10.8.8-1","Description":"Jellyfin web client","URL":"https://github.com/jellyfin/jellyfin","NumVotes":84,"Popularity":1.272964,"OutOfDate":null,"Maintainer":"z3ntu","Submitter":"z3ntu","FirstSubmitted":1547053171,"LastModified":1669830147,"URLPath":"/cgit/aur.git/snapshot/jellyfin-web.tar.gz","MakeDepends":["dotnet-sdk-6.0","nodejs","npm","git"],"License":["GPL2"]}
|
||||
]
|
3
pkg/dep/testdata/jellyfin.json
vendored
3
pkg/dep/testdata/jellyfin.json
vendored
@ -1,3 +0,0 @@
|
||||
[
|
||||
{"ID":1176789,"Name":"jellyfin","PackageBaseID":138631,"PackageBase":"jellyfin","Version":"10.8.8-1","Description":"The Free Software Media System","URL":"https://github.com/jellyfin/jellyfin","NumVotes":84,"Popularity":1.272964,"OutOfDate":null,"Maintainer":"z3ntu","Submitter":"z3ntu","FirstSubmitted":1547053171,"LastModified":1669830147,"URLPath":"/cgit/aur.git/snapshot/jellyfin.tar.gz","Depends":["jellyfin-web=10.8.8","jellyfin-server=10.8.8"],"MakeDepends":["dotnet-sdk-6.0","nodejs","npm","git"],"License":["GPL2"]}
|
||||
]
|
@ -1,371 +0,0 @@
|
||||
package topo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/Jguer/go-alpm/v2"
|
||||
)
|
||||
|
||||
type (
|
||||
NodeSet[T comparable] map[T]bool
|
||||
ProvidesMap[T comparable] map[T]*DependencyInfo[T]
|
||||
DepMap[T comparable] map[T]NodeSet[T]
|
||||
)
|
||||
|
||||
func (n NodeSet[T]) Slice() []T {
|
||||
var slice []T
|
||||
|
||||
for node := range n {
|
||||
slice = append(slice, node)
|
||||
}
|
||||
|
||||
return slice
|
||||
}
|
||||
|
||||
type NodeInfo[V any] struct {
|
||||
Color string
|
||||
Background string
|
||||
Value V
|
||||
}
|
||||
|
||||
type DependencyInfo[T comparable] struct {
|
||||
Provider T
|
||||
alpm.Depend
|
||||
}
|
||||
|
||||
type CheckFn[T comparable, V any] func(T, V) error
|
||||
|
||||
type Graph[T comparable, V any] struct {
|
||||
nodes NodeSet[T]
|
||||
|
||||
// node info map
|
||||
nodeInfo map[T]*NodeInfo[V]
|
||||
|
||||
// `provides` tracks provides -> node.
|
||||
provides ProvidesMap[T]
|
||||
|
||||
// `dependencies` tracks child -> parents.
|
||||
dependencies DepMap[T]
|
||||
// `dependents` tracks parent -> children.
|
||||
dependents DepMap[T]
|
||||
}
|
||||
|
||||
func New[T comparable, V any]() *Graph[T, V] {
|
||||
return &Graph[T, V]{
|
||||
nodes: make(NodeSet[T]),
|
||||
dependencies: make(DepMap[T]),
|
||||
dependents: make(DepMap[T]),
|
||||
nodeInfo: make(map[T]*NodeInfo[V]),
|
||||
provides: make(ProvidesMap[T]),
|
||||
}
|
||||
}
|
||||
|
||||
func (g *Graph[T, V]) Len() int {
|
||||
return len(g.nodes)
|
||||
}
|
||||
|
||||
func (g *Graph[T, V]) Exists(node T) bool {
|
||||
_, ok := g.nodes[node]
|
||||
|
||||
return ok
|
||||
}
|
||||
|
||||
func (g *Graph[T, V]) AddNode(node T) {
|
||||
g.nodes[node] = true
|
||||
}
|
||||
|
||||
func (g *Graph[T, V]) ProvidesExists(provides T) bool {
|
||||
_, ok := g.provides[provides]
|
||||
|
||||
return ok
|
||||
}
|
||||
|
||||
func (g *Graph[T, V]) GetProviderNode(provides T) *DependencyInfo[T] {
|
||||
return g.provides[provides]
|
||||
}
|
||||
|
||||
func (g *Graph[T, V]) Provides(provides T, depInfo *alpm.Depend, node T) {
|
||||
g.provides[provides] = &DependencyInfo[T]{
|
||||
Provider: node,
|
||||
Depend: *depInfo,
|
||||
}
|
||||
}
|
||||
|
||||
func (g *Graph[T, V]) ForEach(f CheckFn[T, V]) error {
|
||||
for node := range g.nodes {
|
||||
if err := f(node, g.nodeInfo[node].Value); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Graph[T, V]) SetNodeInfo(node T, nodeInfo *NodeInfo[V]) {
|
||||
g.nodeInfo[node] = nodeInfo
|
||||
}
|
||||
|
||||
func (g *Graph[T, V]) GetNodeInfo(node T) *NodeInfo[V] {
|
||||
return g.nodeInfo[node]
|
||||
}
|
||||
|
||||
func (g *Graph[T, V]) DependOn(child, parent T) error {
|
||||
if child == parent {
|
||||
return ErrSelfReferential
|
||||
}
|
||||
|
||||
if g.DependsOn(parent, child) {
|
||||
return ErrCircular
|
||||
}
|
||||
|
||||
g.AddNode(parent)
|
||||
g.AddNode(child)
|
||||
|
||||
// Add edges.
|
||||
g.dependents.addNodeToNodeset(parent, child)
|
||||
g.dependencies.addNodeToNodeset(child, parent)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Graph[T, V]) String() string {
|
||||
var sb strings.Builder
|
||||
|
||||
sb.WriteString("digraph {\n")
|
||||
sb.WriteString("compound=true;\n")
|
||||
sb.WriteString("concentrate=true;\n")
|
||||
sb.WriteString("node [shape = record, ordering=out];\n")
|
||||
|
||||
for node := range g.nodes {
|
||||
extra := ""
|
||||
|
||||
if info, ok := g.nodeInfo[node]; ok {
|
||||
if info.Background != "" || info.Color != "" {
|
||||
extra = fmt.Sprintf("[color = %s, style = filled, fillcolor = %s]", info.Color, info.Background)
|
||||
}
|
||||
}
|
||||
|
||||
sb.WriteString(fmt.Sprintf("\t\"%v\"%s;\n", node, extra))
|
||||
}
|
||||
|
||||
for parent, children := range g.dependencies {
|
||||
for child := range children {
|
||||
sb.WriteString(fmt.Sprintf("\t\"%v\" -> \"%v\";\n", parent, child))
|
||||
}
|
||||
}
|
||||
|
||||
sb.WriteString("}")
|
||||
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func (g *Graph[T, V]) DependsOn(child, parent T) bool {
|
||||
deps := g.Dependencies(child)
|
||||
_, ok := deps[parent]
|
||||
|
||||
return ok
|
||||
}
|
||||
|
||||
func (g *Graph[T, V]) HasDependent(parent, child T) bool {
|
||||
deps := g.Dependents(parent)
|
||||
_, ok := deps[child]
|
||||
|
||||
return ok
|
||||
}
|
||||
|
||||
// leavesMap returns a map of leaves with the node as key and the node info value as value.
|
||||
func (g *Graph[T, V]) leavesMap() map[T]V {
|
||||
leaves := make(map[T]V, 0)
|
||||
|
||||
for node := range g.nodes {
|
||||
if _, ok := g.dependencies[node]; !ok {
|
||||
nodeInfo := g.GetNodeInfo(node)
|
||||
if nodeInfo == nil {
|
||||
nodeInfo = &NodeInfo[V]{}
|
||||
}
|
||||
|
||||
leaves[node] = nodeInfo.Value
|
||||
}
|
||||
}
|
||||
|
||||
return leaves
|
||||
}
|
||||
|
||||
// TopoSortedLayerMap returns a slice of all of the graph nodes in topological sort order with their node info.
|
||||
func (g *Graph[T, V]) TopoSortedLayerMap(checkFn CheckFn[T, V]) []map[T]V {
|
||||
layers := []map[T]V{}
|
||||
|
||||
// Copy the graph
|
||||
shrinkingGraph := g.clone()
|
||||
|
||||
for {
|
||||
leaves := shrinkingGraph.leavesMap()
|
||||
if len(leaves) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
layers = append(layers, leaves)
|
||||
|
||||
for leafNode := range leaves {
|
||||
if checkFn != nil {
|
||||
if err := checkFn(leafNode, leaves[leafNode]); err != nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
shrinkingGraph.remove(leafNode)
|
||||
}
|
||||
}
|
||||
|
||||
return layers
|
||||
}
|
||||
|
||||
// returns if it was the last
|
||||
func (dm DepMap[T]) removeFromDepmap(key, node T) bool {
|
||||
if nodes := dm[key]; len(nodes) == 1 {
|
||||
// The only element in the nodeset must be `node`, so we
|
||||
// can delete the entry entirely.
|
||||
delete(dm, key)
|
||||
return true
|
||||
} else {
|
||||
// Otherwise, remove the single node from the nodeset.
|
||||
delete(nodes, node)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Prune removes the node,
|
||||
// its dependencies if there are no other dependents
|
||||
// and its dependents
|
||||
func (g *Graph[T, V]) Prune(node T) []T {
|
||||
pruned := []T{node}
|
||||
// Remove edges from things that depend on `node`.
|
||||
for dependent := range g.dependents[node] {
|
||||
last := g.dependencies.removeFromDepmap(dependent, node)
|
||||
if last {
|
||||
pruned = append(pruned, g.Prune(dependent)...)
|
||||
}
|
||||
}
|
||||
|
||||
delete(g.dependents, node)
|
||||
|
||||
// Remove all edges from node to the things it depends on.
|
||||
for dependency := range g.dependencies[node] {
|
||||
last := g.dependents.removeFromDepmap(dependency, node)
|
||||
if last {
|
||||
pruned = append(pruned, g.Prune(dependency)...)
|
||||
}
|
||||
}
|
||||
|
||||
delete(g.dependencies, node)
|
||||
|
||||
// Finally, remove the node itself.
|
||||
delete(g.nodes, node)
|
||||
return pruned
|
||||
}
|
||||
|
||||
func (g *Graph[T, V]) remove(node T) {
|
||||
// Remove edges from things that depend on `node`.
|
||||
for dependent := range g.dependents[node] {
|
||||
g.dependencies.removeFromDepmap(dependent, node)
|
||||
}
|
||||
|
||||
delete(g.dependents, node)
|
||||
|
||||
// Remove all edges from node to the things it depends on.
|
||||
for dependency := range g.dependencies[node] {
|
||||
g.dependents.removeFromDepmap(dependency, node)
|
||||
}
|
||||
|
||||
delete(g.dependencies, node)
|
||||
|
||||
// Finally, remove the node itself.
|
||||
delete(g.nodes, node)
|
||||
}
|
||||
|
||||
func (g *Graph[T, V]) Dependencies(child T) NodeSet[T] {
|
||||
return g.buildTransitive(child, g.ImmediateDependencies)
|
||||
}
|
||||
|
||||
func (g *Graph[T, V]) ImmediateDependencies(node T) NodeSet[T] {
|
||||
return g.dependencies[node]
|
||||
}
|
||||
|
||||
func (g *Graph[T, V]) Dependents(parent T) NodeSet[T] {
|
||||
return g.buildTransitive(parent, g.immediateDependents)
|
||||
}
|
||||
|
||||
func (g *Graph[T, V]) immediateDependents(node T) NodeSet[T] {
|
||||
return g.dependents[node]
|
||||
}
|
||||
|
||||
func (g *Graph[T, V]) clone() *Graph[T, V] {
|
||||
return &Graph[T, V]{
|
||||
dependencies: g.dependencies.copy(),
|
||||
dependents: g.dependents.copy(),
|
||||
nodes: g.nodes.copy(),
|
||||
nodeInfo: g.nodeInfo, // not copied, as it is not modified
|
||||
}
|
||||
}
|
||||
|
||||
// buildTransitive starts at `root` and continues calling `nextFn` to keep discovering more nodes until
|
||||
// the graph cannot produce any more. It returns the set of all discovered nodes.
|
||||
func (g *Graph[T, V]) buildTransitive(root T, nextFn func(T) NodeSet[T]) NodeSet[T] {
|
||||
if _, ok := g.nodes[root]; !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
out := make(NodeSet[T])
|
||||
searchNext := []T{root}
|
||||
|
||||
for len(searchNext) > 0 {
|
||||
// List of new nodes from this layer of the dependency graph. This is
|
||||
// assigned to `searchNext` at the end of the outer "discovery" loop.
|
||||
discovered := []T{}
|
||||
|
||||
for _, node := range searchNext {
|
||||
// For each node to discover, find the next nodes.
|
||||
for nextNode := range nextFn(node) {
|
||||
// If we have not seen the node before, add it to the output as well
|
||||
// as the list of nodes to traverse in the next iteration.
|
||||
if _, ok := out[nextNode]; !ok {
|
||||
out[nextNode] = true
|
||||
|
||||
discovered = append(discovered, nextNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
searchNext = discovered
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func (s NodeSet[T]) copy() NodeSet[T] {
|
||||
out := make(NodeSet[T], len(s))
|
||||
for k, v := range s {
|
||||
out[k] = v
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func (dm DepMap[T]) copy() DepMap[T] {
|
||||
out := make(DepMap[T], len(dm))
|
||||
for k := range dm {
|
||||
out[k] = dm[k].copy()
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func (dm DepMap[T]) addNodeToNodeset(key, node T) {
|
||||
nodes, ok := dm[key]
|
||||
if !ok {
|
||||
nodes = make(NodeSet[T])
|
||||
dm[key] = nodes
|
||||
}
|
||||
|
||||
nodes[node] = true
|
||||
}
|
@ -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")
|
||||
)
|
@ -6,61 +6,63 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"regexp"
|
||||
|
||||
"github.com/leonelquinteros/gotext"
|
||||
|
||||
"github.com/Jguer/yay/v12/pkg/settings/exe"
|
||||
"github.com/Jguer/yay/v11/pkg/settings/exe"
|
||||
)
|
||||
|
||||
const (
|
||||
MaxConcurrentFetch = 20
|
||||
absPackageURL = "https://gitlab.archlinux.org/archlinux/packaging/packages"
|
||||
_urlPackagePath = "%s/raw/packages/%s/trunk/PKGBUILD"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalidRepository = errors.New(gotext.Get("invalid repository"))
|
||||
ErrABSPackageNotFound = errors.New(gotext.Get("package not found in repos"))
|
||||
ABSPackageURL = "https://github.com/archlinux/svntogit-packages"
|
||||
ABSCommunityURL = "https://github.com/archlinux/svntogit-community"
|
||||
)
|
||||
|
||||
type regexReplace struct {
|
||||
repl string
|
||||
match *regexp.Regexp
|
||||
}
|
||||
func getRepoURL(db string) (string, error) {
|
||||
switch db {
|
||||
case "core", "extra", "testing":
|
||||
return ABSPackageURL, nil
|
||||
case "community", "multilib", "community-testing", "multilib-testing":
|
||||
return ABSCommunityURL, nil
|
||||
}
|
||||
|
||||
// regex replacements for Gitlab URLs
|
||||
// info: https://gitlab.archlinux.org/archlinux/devtools/-/blob/6ce666a1669235749c17d5c44d8a24dea4a135da/src/lib/api/gitlab.sh#L84
|
||||
var gitlabRepl = []regexReplace{
|
||||
{repl: `$1-$2`, match: regexp.MustCompile(`([a-zA-Z0-9]+)\+([a-zA-Z]+)`)},
|
||||
{repl: `plus`, match: regexp.MustCompile(`\+`)},
|
||||
{repl: `-`, match: regexp.MustCompile(`[^a-zA-Z0-9_\-.]`)},
|
||||
{repl: `-`, match: regexp.MustCompile(`[_\-]{2,}`)},
|
||||
{repl: `unix-tree`, match: regexp.MustCompile(`^tree$`)},
|
||||
return "", ErrInvalidRepository
|
||||
}
|
||||
|
||||
// Return format for pkgbuild
|
||||
// https://gitlab.archlinux.org/archlinux/packaging/packages/0ad/-/raw/main/PKGBUILD
|
||||
func getPackagePKGBUILDURL(pkgName string) string {
|
||||
return fmt.Sprintf("%s/%s/-/raw/main/PKGBUILD", absPackageURL, convertPkgNameForURL(pkgName))
|
||||
// https://github.com/archlinux/svntogit-community/raw/packages/neovim/trunk/PKGBUILD
|
||||
func getPackageURL(db, pkgName string) (string, error) {
|
||||
repoURL, err := getRepoURL(db)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return fmt.Sprintf(_urlPackagePath, repoURL, pkgName), err
|
||||
}
|
||||
|
||||
// Return format for pkgbuild repo
|
||||
// https://gitlab.archlinux.org/archlinux/packaging/packages/0ad.git
|
||||
func getPackageRepoURL(pkgName string) string {
|
||||
return fmt.Sprintf("%s/%s.git", absPackageURL, convertPkgNameForURL(pkgName))
|
||||
}
|
||||
|
||||
// convert pkgName for Gitlab URL path (repo name)
|
||||
func convertPkgNameForURL(pkgName string) string {
|
||||
for _, regex := range gitlabRepl {
|
||||
pkgName = regex.match.ReplaceAllString(pkgName, regex.repl)
|
||||
// https://github.com/archlinux/svntogit-community.git
|
||||
func getPackageRepoURL(db string) (string, error) {
|
||||
repoURL, err := getRepoURL(db)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return pkgName
|
||||
|
||||
return repoURL + ".git", err
|
||||
}
|
||||
|
||||
// ABSPKGBUILD retrieves the PKGBUILD file to a dest directory.
|
||||
func ABSPKGBUILD(httpClient httpRequestDoer, dbName, pkgName string) ([]byte, error) {
|
||||
packageURL := getPackagePKGBUILDURL(pkgName)
|
||||
packageURL, err := getPackageURL(dbName, pkgName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := httpClient.Get(packageURL)
|
||||
if err != nil {
|
||||
@ -83,10 +85,12 @@ func ABSPKGBUILD(httpClient httpRequestDoer, dbName, pkgName string) ([]byte, er
|
||||
|
||||
// ABSPKGBUILDRepo retrieves the PKGBUILD repository to a dest directory.
|
||||
func ABSPKGBUILDRepo(ctx context.Context, cmdBuilder exe.GitCmdBuilder,
|
||||
dbName, pkgName, dest string, force bool,
|
||||
) (bool, error) {
|
||||
pkgURL := getPackageRepoURL(pkgName)
|
||||
dbName, pkgName, dest string, force bool) (bool, error) {
|
||||
pkgURL, err := getPackageRepoURL(dbName)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return downloadGitRepo(ctx, cmdBuilder, pkgURL,
|
||||
pkgName, dest, force, "--single-branch")
|
||||
pkgName, dest, force, "--single-branch", "-b", "packages/"+pkgName)
|
||||
}
|
||||
|
@ -1,6 +1,3 @@
|
||||
//go:build !integration
|
||||
// +build !integration
|
||||
|
||||
package download
|
||||
|
||||
import (
|
||||
@ -13,7 +10,7 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/Jguer/yay/v12/pkg/settings/exe"
|
||||
"github.com/Jguer/yay/v11/pkg/settings/exe"
|
||||
)
|
||||
|
||||
const gitExtrasPKGBUILD = `pkgname=git-extras
|
||||
@ -50,12 +47,12 @@ func Test_getPackageURL(t *testing.T) {
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "extra package",
|
||||
name: "community package",
|
||||
args: args{
|
||||
db: "extra",
|
||||
db: "community",
|
||||
pkgName: "kitty",
|
||||
},
|
||||
want: "https://gitlab.archlinux.org/archlinux/packaging/packages/kitty/-/raw/main/PKGBUILD",
|
||||
want: "https://github.com/archlinux/svntogit-community/raw/packages/kitty/trunk/PKGBUILD",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
@ -64,69 +61,27 @@ func Test_getPackageURL(t *testing.T) {
|
||||
db: "core",
|
||||
pkgName: "linux",
|
||||
},
|
||||
want: "https://gitlab.archlinux.org/archlinux/packaging/packages/linux/-/raw/main/PKGBUILD",
|
||||
want: "https://github.com/archlinux/svntogit-packages/raw/packages/linux/trunk/PKGBUILD",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "personal repo package",
|
||||
args: args{
|
||||
db: "sweswe",
|
||||
pkgName: "zabix",
|
||||
pkgName: "linux",
|
||||
},
|
||||
want: "https://gitlab.archlinux.org/archlinux/packaging/packages/zabix/-/raw/main/PKGBUILD",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "special name +",
|
||||
args: args{
|
||||
db: "core",
|
||||
pkgName: "my+package",
|
||||
},
|
||||
want: "https://gitlab.archlinux.org/archlinux/packaging/packages/my-package/-/raw/main/PKGBUILD",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "special name %",
|
||||
args: args{
|
||||
db: "core",
|
||||
pkgName: "my%package",
|
||||
},
|
||||
want: "https://gitlab.archlinux.org/archlinux/packaging/packages/my-package/-/raw/main/PKGBUILD",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "special name _-",
|
||||
args: args{
|
||||
db: "core",
|
||||
pkgName: "my_-package",
|
||||
},
|
||||
want: "https://gitlab.archlinux.org/archlinux/packaging/packages/my-package/-/raw/main/PKGBUILD",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "special name ++",
|
||||
args: args{
|
||||
db: "core",
|
||||
pkgName: "my++package",
|
||||
},
|
||||
want: "https://gitlab.archlinux.org/archlinux/packaging/packages/mypluspluspackage/-/raw/main/PKGBUILD",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "special name tree",
|
||||
args: args{
|
||||
db: "sweswe",
|
||||
pkgName: "tree",
|
||||
},
|
||||
want: "https://gitlab.archlinux.org/archlinux/packaging/packages/unix-tree/-/raw/main/PKGBUILD",
|
||||
wantErr: false,
|
||||
want: "",
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
got := getPackagePKGBUILDURL(tt.args.pkgName)
|
||||
got, err := getPackageURL(tt.args.db, tt.args.pkgName)
|
||||
if tt.wantErr {
|
||||
assert.ErrorIs(t, err, ErrInvalidRepository)
|
||||
}
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
@ -155,7 +110,7 @@ func TestGetABSPkgbuild(t *testing.T) {
|
||||
body: gitExtrasPKGBUILD,
|
||||
status: 200,
|
||||
pkgName: "git-extras",
|
||||
wantURL: "https://gitlab.archlinux.org/archlinux/packaging/packages/git-extras/-/raw/main/PKGBUILD",
|
||||
wantURL: "https://github.com/archlinux/svntogit-packages/raw/packages/git-extras/trunk/PKGBUILD",
|
||||
},
|
||||
want: gitExtrasPKGBUILD,
|
||||
wantErr: false,
|
||||
@ -167,7 +122,7 @@ func TestGetABSPkgbuild(t *testing.T) {
|
||||
body: "",
|
||||
status: 404,
|
||||
pkgName: "git-git",
|
||||
wantURL: "https://gitlab.archlinux.org/archlinux/packaging/packages/git-git/-/raw/main/PKGBUILD",
|
||||
wantURL: "https://github.com/archlinux/svntogit-packages/raw/packages/git-git/trunk/PKGBUILD",
|
||||
},
|
||||
want: "",
|
||||
wantErr: true,
|
||||
@ -199,7 +154,7 @@ func Test_getPackageRepoURL(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
type args struct {
|
||||
pkgName string
|
||||
db string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
@ -208,59 +163,32 @@ func Test_getPackageRepoURL(t *testing.T) {
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "extra package",
|
||||
args: args{pkgName: "zoxide"},
|
||||
want: "https://gitlab.archlinux.org/archlinux/packaging/packages/zoxide.git",
|
||||
name: "community package",
|
||||
args: args{db: "community"},
|
||||
want: "https://github.com/archlinux/svntogit-community.git",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "core package",
|
||||
args: args{pkgName: "linux"},
|
||||
want: "https://gitlab.archlinux.org/archlinux/packaging/packages/linux.git",
|
||||
args: args{db: "core"},
|
||||
want: "https://github.com/archlinux/svntogit-packages.git",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "personal repo package",
|
||||
args: args{pkgName: "sweswe"},
|
||||
want: "https://gitlab.archlinux.org/archlinux/packaging/packages/sweswe.git",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "special name +",
|
||||
args: args{pkgName: "my+package"},
|
||||
want: "https://gitlab.archlinux.org/archlinux/packaging/packages/my-package.git",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "special name %",
|
||||
args: args{pkgName: "my%package"},
|
||||
want: "https://gitlab.archlinux.org/archlinux/packaging/packages/my-package.git",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "special name _-",
|
||||
args: args{pkgName: "my_-package"},
|
||||
want: "https://gitlab.archlinux.org/archlinux/packaging/packages/my-package.git",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "special name ++",
|
||||
args: args{pkgName: "my++package"},
|
||||
want: "https://gitlab.archlinux.org/archlinux/packaging/packages/mypluspluspackage.git",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "special name tree",
|
||||
args: args{pkgName: "tree"},
|
||||
want: "https://gitlab.archlinux.org/archlinux/packaging/packages/unix-tree.git",
|
||||
wantErr: false,
|
||||
args: args{db: "sweswe"},
|
||||
want: "",
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
got := getPackageRepoURL(tt.args.pkgName)
|
||||
got, err := getPackageRepoURL(tt.args.db)
|
||||
if tt.wantErr {
|
||||
assert.ErrorIs(t, err, ErrInvalidRepository)
|
||||
}
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
@ -272,13 +200,13 @@ func Test_getPackageRepoURL(t *testing.T) {
|
||||
func TestABSPKGBUILDRepo(t *testing.T) {
|
||||
t.Parallel()
|
||||
cmdRunner := &testRunner{}
|
||||
want := "/usr/local/bin/git --no-replace-objects -C /tmp/doesnt-exist clone --no-progress --single-branch https://gitlab.archlinux.org/archlinux/packaging/packages/linux.git linux"
|
||||
want := "/usr/local/bin/git --no-replace-objects -C /tmp/doesnt-exist clone --no-progress --single-branch -b packages/linux https://github.com/archlinux/svntogit-packages.git linux"
|
||||
if os.Getuid() == 0 {
|
||||
ld := "systemd-run"
|
||||
if path, _ := exec.LookPath(ld); path != "" {
|
||||
ld = path
|
||||
}
|
||||
want = fmt.Sprintf("%s --service-type=oneshot --pipe --wait --pty --quiet -p DynamicUser=yes -p CacheDirectory=yay -E HOME=/tmp --no-replace-objects -C /tmp/doesnt-exist clone --no-progress --single-branch https://gitlab.archlinux.org/archlinux/packaging/packages/linux.git linux", ld)
|
||||
want = fmt.Sprintf("%s --service-type=oneshot --pipe --wait --pty --quiet -p DynamicUser=yes -p CacheDirectory=yay -E HOME=/tmp --no-replace-objects -C /tmp/doesnt-exist clone --no-progress --single-branch -b packages/linux https://github.com/archlinux/svntogit-packages.git linux", ld)
|
||||
}
|
||||
|
||||
cmdBuilder := &testGitBuilder{
|
||||
@ -291,7 +219,7 @@ func TestABSPKGBUILDRepo(t *testing.T) {
|
||||
GitFlags: []string{"--no-replace-objects"},
|
||||
},
|
||||
}
|
||||
newClone, err := ABSPKGBUILDRepo(context.Background(), cmdBuilder, "core", "linux", "/tmp/doesnt-exist", false)
|
||||
newClone, err := ABSPKGBUILDRepo(context.TODO(), cmdBuilder, "core", "linux", "/tmp/doesnt-exist", false)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, true, newClone)
|
||||
}
|
||||
@ -325,7 +253,7 @@ func TestABSPKGBUILDRepoExistsPerms(t *testing.T) {
|
||||
GitFlags: []string{"--no-replace-objects"},
|
||||
},
|
||||
}
|
||||
newClone, err := ABSPKGBUILDRepo(context.Background(), cmdBuilder, "core", "linux", dir, false)
|
||||
newClone, err := ABSPKGBUILDRepo(context.TODO(), cmdBuilder, "core", "linux", dir, false)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, false, newClone)
|
||||
}
|
||||
|
@ -10,9 +10,9 @@ import (
|
||||
|
||||
"github.com/leonelquinteros/gotext"
|
||||
|
||||
"github.com/Jguer/yay/v12/pkg/multierror"
|
||||
"github.com/Jguer/yay/v12/pkg/settings/exe"
|
||||
"github.com/Jguer/yay/v12/pkg/text"
|
||||
"github.com/Jguer/yay/v11/pkg/multierror"
|
||||
"github.com/Jguer/yay/v11/pkg/settings/exe"
|
||||
"github.com/Jguer/yay/v11/pkg/text"
|
||||
)
|
||||
|
||||
func AURPKGBUILD(httpClient httpRequestDoer, pkgName, aurURL string) ([]byte, error) {
|
||||
@ -48,9 +48,8 @@ func AURPKGBUILDRepo(ctx context.Context, cmdBuilder exe.GitCmdBuilder, aurURL,
|
||||
|
||||
func AURPKGBUILDRepos(
|
||||
ctx context.Context,
|
||||
cmdBuilder exe.GitCmdBuilder, logger *text.Logger,
|
||||
targets []string, aurURL, dest string, force bool,
|
||||
) (map[string]bool, error) {
|
||||
cmdBuilder exe.GitCmdBuilder,
|
||||
targets []string, aurURL, dest string, force bool) (map[string]bool, error) {
|
||||
cloned := make(map[string]bool, len(targets))
|
||||
|
||||
var (
|
||||
@ -63,34 +62,30 @@ func AURPKGBUILDRepos(
|
||||
|
||||
for _, target := range targets {
|
||||
sem <- 1
|
||||
|
||||
wg.Add(1)
|
||||
|
||||
go func(target string) {
|
||||
defer func() {
|
||||
<-sem
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
newClone, err := AURPKGBUILDRepo(ctx, cmdBuilder, aurURL, target, dest, force)
|
||||
|
||||
mux.Lock()
|
||||
progress := len(cloned)
|
||||
progress := 0
|
||||
|
||||
if err != nil {
|
||||
errs.Add(err)
|
||||
} else {
|
||||
mux.Lock()
|
||||
cloned[target] = newClone
|
||||
progress = len(cloned)
|
||||
mux.Unlock()
|
||||
logger.OperationInfoln(
|
||||
gotext.Get("(%d/%d) Failed to download PKGBUILD: %s",
|
||||
progress, len(targets), text.Cyan(target)))
|
||||
return
|
||||
}
|
||||
|
||||
cloned[target] = newClone
|
||||
progress = len(cloned)
|
||||
mux.Unlock()
|
||||
|
||||
logger.OperationInfoln(
|
||||
text.OperationInfoln(
|
||||
gotext.Get("(%d/%d) Downloaded PKGBUILD: %s",
|
||||
progress, len(targets), text.Cyan(target)))
|
||||
|
||||
<-sem
|
||||
|
||||
wg.Done()
|
||||
}(target)
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,3 @@
|
||||
//go:build !integration
|
||||
// +build !integration
|
||||
|
||||
package download
|
||||
|
||||
import (
|
||||
@ -13,7 +10,7 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/Jguer/yay/v12/pkg/settings/exe"
|
||||
"github.com/Jguer/yay/v11/pkg/settings/exe"
|
||||
)
|
||||
|
||||
func TestGetAURPkgbuild(t *testing.T) {
|
||||
@ -101,7 +98,7 @@ func TestAURPKGBUILDRepo(t *testing.T) {
|
||||
GitFlags: []string{"--no-replace-objects"},
|
||||
},
|
||||
}
|
||||
newCloned, err := AURPKGBUILDRepo(context.Background(), cmdBuilder, "https://aur.archlinux.org", "yay-bin", "/tmp/doesnt-exist", false)
|
||||
newCloned, err := AURPKGBUILDRepo(context.TODO(), cmdBuilder, "https://aur.archlinux.org", "yay-bin", "/tmp/doesnt-exist", false)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, true, newCloned)
|
||||
}
|
||||
@ -135,7 +132,7 @@ func TestAURPKGBUILDRepoExistsPerms(t *testing.T) {
|
||||
GitFlags: []string{"--no-replace-objects"},
|
||||
},
|
||||
}
|
||||
cloned, err := AURPKGBUILDRepo(context.Background(), cmdBuilder, "https://aur.archlinux.org", "yay-bin", dir, false)
|
||||
cloned, err := AURPKGBUILDRepo(context.TODO(), cmdBuilder, "https://aur.archlinux.org", "yay-bin", dir, false)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, false, cloned)
|
||||
}
|
||||
@ -158,7 +155,7 @@ func TestAURPKGBUILDRepos(t *testing.T) {
|
||||
GitFlags: []string{},
|
||||
},
|
||||
}
|
||||
cloned, err := AURPKGBUILDRepos(context.Background(), cmdBuilder, newTestLogger(), targets, "https://aur.archlinux.org", dir, false)
|
||||
cloned, err := AURPKGBUILDRepos(context.TODO(), cmdBuilder, targets, "https://aur.archlinux.org", dir, false)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, map[string]bool{"yay": true, "yay-bin": false, "yay-git": true}, cloned)
|
||||
|
@ -9,13 +9,11 @@ import (
|
||||
|
||||
"github.com/leonelquinteros/gotext"
|
||||
|
||||
"github.com/Jguer/aur"
|
||||
|
||||
"github.com/Jguer/yay/v12/pkg/db"
|
||||
"github.com/Jguer/yay/v12/pkg/multierror"
|
||||
"github.com/Jguer/yay/v12/pkg/settings/exe"
|
||||
"github.com/Jguer/yay/v12/pkg/settings/parser"
|
||||
"github.com/Jguer/yay/v12/pkg/text"
|
||||
"github.com/Jguer/yay/v11/pkg/db"
|
||||
"github.com/Jguer/yay/v11/pkg/multierror"
|
||||
"github.com/Jguer/yay/v11/pkg/settings/exe"
|
||||
"github.com/Jguer/yay/v11/pkg/settings/parser"
|
||||
"github.com/Jguer/yay/v11/pkg/text"
|
||||
)
|
||||
|
||||
type httpRequestDoer interface {
|
||||
@ -24,12 +22,11 @@ type httpRequestDoer interface {
|
||||
|
||||
type DBSearcher interface {
|
||||
SyncPackage(string) db.IPackage
|
||||
SyncPackageFromDB(string, string) db.IPackage
|
||||
SatisfierFromDB(string, string) db.IPackage
|
||||
}
|
||||
|
||||
func downloadGitRepo(ctx context.Context, cmdBuilder exe.GitCmdBuilder,
|
||||
pkgURL, pkgName, dest string, force bool, gitArgs ...string,
|
||||
) (bool, error) {
|
||||
pkgURL, pkgName, dest string, force bool, gitArgs ...string) (bool, error) {
|
||||
finalDir := filepath.Join(dest, pkgName)
|
||||
newClone := true
|
||||
|
||||
@ -81,9 +78,8 @@ func getURLName(pkg db.IPackage) string {
|
||||
return name
|
||||
}
|
||||
|
||||
func PKGBUILDs(dbExecutor DBSearcher, aurClient aur.QueryClient, httpClient *http.Client,
|
||||
logger *text.Logger, targets []string, aurURL string, mode parser.TargetMode,
|
||||
) (map[string][]byte, error) {
|
||||
func PKGBUILDs(dbExecutor DBSearcher, httpClient *http.Client, targets []string,
|
||||
aurURL string, mode parser.TargetMode) (map[string][]byte, error) {
|
||||
pkgbuilds := make(map[string][]byte, len(targets))
|
||||
|
||||
var (
|
||||
@ -96,7 +92,7 @@ func PKGBUILDs(dbExecutor DBSearcher, aurClient aur.QueryClient, httpClient *htt
|
||||
|
||||
for _, target := range targets {
|
||||
// Probably replaceable by something in query.
|
||||
dbName, name, isAUR, toSkip := getPackageUsableName(dbExecutor, aurClient, logger, target, mode)
|
||||
dbName, name, aur, toSkip := getPackageUsableName(dbExecutor, target, mode)
|
||||
if toSkip {
|
||||
continue
|
||||
}
|
||||
@ -127,7 +123,7 @@ func PKGBUILDs(dbExecutor DBSearcher, aurClient aur.QueryClient, httpClient *htt
|
||||
|
||||
<-sem
|
||||
wg.Done()
|
||||
}(target, dbName, name, isAUR)
|
||||
}(target, dbName, name, aur)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
@ -135,10 +131,9 @@ func PKGBUILDs(dbExecutor DBSearcher, aurClient aur.QueryClient, httpClient *htt
|
||||
return pkgbuilds, errs.Return()
|
||||
}
|
||||
|
||||
func PKGBUILDRepos(ctx context.Context, dbExecutor DBSearcher, aurClient aur.QueryClient,
|
||||
cmdBuilder exe.GitCmdBuilder, logger *text.Logger,
|
||||
targets []string, mode parser.TargetMode, aurURL, dest string, force bool,
|
||||
) (map[string]bool, error) {
|
||||
func PKGBUILDRepos(ctx context.Context, dbExecutor DBSearcher,
|
||||
cmdBuilder exe.GitCmdBuilder,
|
||||
targets []string, mode parser.TargetMode, aurURL, dest string, force bool) (map[string]bool, error) {
|
||||
cloned := make(map[string]bool, len(targets))
|
||||
|
||||
var (
|
||||
@ -151,7 +146,7 @@ func PKGBUILDRepos(ctx context.Context, dbExecutor DBSearcher, aurClient aur.Que
|
||||
|
||||
for _, target := range targets {
|
||||
// Probably replaceable by something in query.
|
||||
dbName, name, isAUR, toSkip := getPackageUsableName(dbExecutor, aurClient, logger, target, mode)
|
||||
dbName, name, aur, toSkip := getPackageUsableName(dbExecutor, target, mode)
|
||||
if toSkip {
|
||||
continue
|
||||
}
|
||||
@ -184,11 +179,11 @@ func PKGBUILDRepos(ctx context.Context, dbExecutor DBSearcher, aurClient aur.Que
|
||||
}
|
||||
|
||||
if aur {
|
||||
logger.OperationInfoln(
|
||||
text.OperationInfoln(
|
||||
gotext.Get("(%d/%d) Downloaded PKGBUILD: %s",
|
||||
progress, len(targets), text.Cyan(pkgName)))
|
||||
} else {
|
||||
logger.OperationInfoln(
|
||||
text.OperationInfoln(
|
||||
gotext.Get("(%d/%d) Downloaded PKGBUILD from ABS: %s",
|
||||
progress, len(targets), text.Cyan(pkgName)))
|
||||
}
|
||||
@ -196,7 +191,7 @@ func PKGBUILDRepos(ctx context.Context, dbExecutor DBSearcher, aurClient aur.Que
|
||||
<-sem
|
||||
|
||||
wg.Done()
|
||||
}(target, dbName, name, isAUR)
|
||||
}(target, dbName, name, aur)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
@ -205,47 +200,34 @@ func PKGBUILDRepos(ctx context.Context, dbExecutor DBSearcher, aurClient aur.Que
|
||||
}
|
||||
|
||||
// TODO: replace with dep.ResolveTargets.
|
||||
func getPackageUsableName(dbExecutor DBSearcher, aurClient aur.QueryClient,
|
||||
logger *text.Logger, target string, mode parser.TargetMode,
|
||||
) (dbname, pkgname string, isAUR, toSkip bool) {
|
||||
func getPackageUsableName(dbExecutor DBSearcher, target string, mode parser.TargetMode) (dbname, pkgname string, aur, toSkip bool) {
|
||||
aur = true
|
||||
|
||||
dbName, name := text.SplitDBFromName(target)
|
||||
if dbName != "aur" && mode.AtLeastRepo() {
|
||||
var pkg db.IPackage
|
||||
if dbName != "" {
|
||||
pkg = dbExecutor.SyncPackageFromDB(name, dbName)
|
||||
pkg = dbExecutor.SatisfierFromDB(name, dbName)
|
||||
if pkg == nil {
|
||||
// if the user precised a db but the package is not in the db
|
||||
// then it is missing
|
||||
// Mode does not allow AUR packages
|
||||
return dbName, name, aur, true
|
||||
}
|
||||
} else {
|
||||
pkg = dbExecutor.SyncPackage(name)
|
||||
}
|
||||
|
||||
if pkg != nil {
|
||||
aur = false
|
||||
name = getURLName(pkg)
|
||||
dbName = pkg.DB().Name()
|
||||
return dbName, name, false, false
|
||||
}
|
||||
|
||||
// If the package is not found in the database and it was expected to be
|
||||
if pkg == nil && dbName != "" {
|
||||
return dbName, name, true, true
|
||||
}
|
||||
}
|
||||
|
||||
if mode == parser.ModeRepo {
|
||||
return dbName, name, true, true
|
||||
if aur && mode == parser.ModeRepo {
|
||||
return dbName, name, aur, true
|
||||
}
|
||||
|
||||
pkgs, err := aurClient.Get(context.Background(), &aur.Query{
|
||||
By: aur.Name,
|
||||
Contains: false,
|
||||
Needles: []string{name},
|
||||
})
|
||||
if err != nil {
|
||||
logger.Warnln(err)
|
||||
return dbName, name, true, true
|
||||
}
|
||||
|
||||
if len(pkgs) == 0 {
|
||||
return dbName, name, true, true
|
||||
}
|
||||
|
||||
return "aur", name, true, false
|
||||
return dbName, name, aur, false
|
||||
}
|
||||
|
@ -1,106 +0,0 @@
|
||||
//go:build integration
|
||||
// +build integration
|
||||
|
||||
package download
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/Jguer/aur"
|
||||
|
||||
mockaur "github.com/Jguer/yay/v12/pkg/dep/mock"
|
||||
"github.com/Jguer/yay/v12/pkg/settings/exe"
|
||||
"github.com/Jguer/yay/v12/pkg/settings/parser"
|
||||
"github.com/Jguer/yay/v12/pkg/text"
|
||||
)
|
||||
|
||||
func TestIntegrationPKGBUILDReposDefinedDBClone(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
|
||||
mockClient := &mockaur.MockAUR{
|
||||
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
|
||||
return []aur.Pkg{{}}, nil // fakes a package found for all
|
||||
},
|
||||
}
|
||||
targets := []string{"core/linux", "yay-bin", "yay-git"}
|
||||
|
||||
testLogger := text.NewLogger(os.Stdout, os.Stderr, strings.NewReader(""), true, "test")
|
||||
cmdRunner := &exe.OSRunner{Log: testLogger}
|
||||
cmdBuilder := &exe.CmdBuilder{
|
||||
Runner: cmdRunner,
|
||||
GitBin: "git",
|
||||
GitFlags: []string{},
|
||||
Log: testLogger,
|
||||
}
|
||||
searcher := &testDBSearcher{
|
||||
absPackagesDB: map[string]string{"linux": "core"},
|
||||
}
|
||||
cloned, err := PKGBUILDRepos(context.Background(), searcher, mockClient,
|
||||
cmdBuilder, testLogger.Child("test"),
|
||||
targets, parser.ModeAny, "https://aur.archlinux.org", dir, false)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, map[string]bool{"core/linux": true, "yay-bin": true, "yay-git": true}, cloned)
|
||||
}
|
||||
|
||||
func TestIntegrationPKGBUILDReposNotExist(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
|
||||
mockClient := &mockaur.MockAUR{
|
||||
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
|
||||
return []aur.Pkg{{}}, nil // fakes a package found for all
|
||||
},
|
||||
}
|
||||
targets := []string{"core/yay", "yay-bin", "yay-git"}
|
||||
testLogger := text.NewLogger(os.Stdout, os.Stderr, strings.NewReader(""), true, "test")
|
||||
cmdRunner := &exe.OSRunner{Log: testLogger}
|
||||
cmdBuilder := &exe.CmdBuilder{
|
||||
Runner: cmdRunner,
|
||||
GitBin: "git",
|
||||
GitFlags: []string{},
|
||||
Log: testLogger,
|
||||
}
|
||||
|
||||
searcher := &testDBSearcher{
|
||||
absPackagesDB: map[string]string{"yay": "core"},
|
||||
}
|
||||
cloned, err := PKGBUILDRepos(context.Background(), searcher, mockClient,
|
||||
cmdBuilder, testLogger.Child("test"),
|
||||
targets, parser.ModeAny, "https://aur.archlinux.org", dir, false)
|
||||
|
||||
assert.Error(t, err)
|
||||
assert.EqualValues(t, map[string]bool{"yay-bin": true, "yay-git": true}, cloned)
|
||||
}
|
||||
|
||||
// GIVEN 2 aur packages and 1 in repo
|
||||
// WHEN defining as specified targets
|
||||
// THEN all aur be found and cloned
|
||||
func TestIntegrationPKGBUILDFull(t *testing.T) {
|
||||
mockClient := &mockaur.MockAUR{
|
||||
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
|
||||
return []aur.Pkg{{}}, nil
|
||||
},
|
||||
}
|
||||
|
||||
testLogger := text.NewLogger(os.Stdout, os.Stderr, strings.NewReader(""), true, "test")
|
||||
targets := []string{"core/linux", "aur/yay-bin", "yay-git"}
|
||||
searcher := &testDBSearcher{
|
||||
absPackagesDB: map[string]string{"linux": "core"},
|
||||
}
|
||||
|
||||
fetched, err := PKGBUILDs(searcher, mockClient, &http.Client{}, testLogger.Child("test"),
|
||||
targets, "https://aur.archlinux.org", parser.ModeAny)
|
||||
|
||||
assert.NoError(t, err)
|
||||
|
||||
for _, target := range targets {
|
||||
assert.Contains(t, fetched, target)
|
||||
assert.NotEmpty(t, fetched[target])
|
||||
}
|
||||
}
|
@ -1,32 +1,19 @@
|
||||
//go:build !integration
|
||||
// +build !integration
|
||||
|
||||
package download
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/h2non/gock.v1"
|
||||
|
||||
"github.com/Jguer/aur"
|
||||
|
||||
mockaur "github.com/Jguer/yay/v12/pkg/dep/mock"
|
||||
"github.com/Jguer/yay/v12/pkg/settings/exe"
|
||||
"github.com/Jguer/yay/v12/pkg/settings/parser"
|
||||
"github.com/Jguer/yay/v12/pkg/text"
|
||||
"github.com/Jguer/yay/v11/pkg/settings/exe"
|
||||
"github.com/Jguer/yay/v11/pkg/settings/parser"
|
||||
)
|
||||
|
||||
func newTestLogger() *text.Logger {
|
||||
return text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), true, "test")
|
||||
}
|
||||
|
||||
// GIVEN 2 aur packages and 1 in repo
|
||||
// GIVEN package in repo is already present
|
||||
// WHEN defining package db as a target
|
||||
@ -35,14 +22,6 @@ func TestPKGBUILDReposDefinedDBPull(t *testing.T) {
|
||||
t.Parallel()
|
||||
dir := t.TempDir()
|
||||
|
||||
mockClient := &mockaur.MockAUR{
|
||||
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
|
||||
return []aur.Pkg{{}}, nil // fakes a package found for all
|
||||
},
|
||||
}
|
||||
|
||||
testLogger := text.NewLogger(os.Stdout, os.Stderr, strings.NewReader(""), true, "test")
|
||||
|
||||
os.MkdirAll(filepath.Join(dir, "yay", ".git"), 0o777)
|
||||
|
||||
targets := []string{"core/yay", "yay-bin", "yay-git"}
|
||||
@ -54,14 +33,13 @@ func TestPKGBUILDReposDefinedDBPull(t *testing.T) {
|
||||
Runner: cmdRunner,
|
||||
GitBin: "/usr/local/bin/git",
|
||||
GitFlags: []string{},
|
||||
Log: testLogger,
|
||||
},
|
||||
}
|
||||
searcher := &testDBSearcher{
|
||||
absPackagesDB: map[string]string{"yay": "core"},
|
||||
}
|
||||
cloned, err := PKGBUILDRepos(context.Background(), searcher, mockClient,
|
||||
cmdBuilder, newTestLogger(),
|
||||
cloned, err := PKGBUILDRepos(context.TODO(), searcher,
|
||||
cmdBuilder,
|
||||
targets, parser.ModeAny, "https://aur.archlinux.org", dir, false)
|
||||
|
||||
assert.NoError(t, err)
|
||||
@ -75,11 +53,6 @@ func TestPKGBUILDReposDefinedDBClone(t *testing.T) {
|
||||
t.Parallel()
|
||||
dir := t.TempDir()
|
||||
|
||||
mockClient := &mockaur.MockAUR{
|
||||
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
|
||||
return []aur.Pkg{{}}, nil // fakes a package found for all
|
||||
},
|
||||
}
|
||||
targets := []string{"core/yay", "yay-bin", "yay-git"}
|
||||
cmdRunner := &testRunner{}
|
||||
cmdBuilder := &testGitBuilder{
|
||||
@ -94,8 +67,8 @@ func TestPKGBUILDReposDefinedDBClone(t *testing.T) {
|
||||
searcher := &testDBSearcher{
|
||||
absPackagesDB: map[string]string{"yay": "core"},
|
||||
}
|
||||
cloned, err := PKGBUILDRepos(context.Background(), searcher, mockClient,
|
||||
cmdBuilder, newTestLogger(),
|
||||
cloned, err := PKGBUILDRepos(context.TODO(), searcher,
|
||||
cmdBuilder,
|
||||
targets, parser.ModeAny, "https://aur.archlinux.org", dir, false)
|
||||
|
||||
assert.NoError(t, err)
|
||||
@ -109,11 +82,6 @@ func TestPKGBUILDReposClone(t *testing.T) {
|
||||
t.Parallel()
|
||||
dir := t.TempDir()
|
||||
|
||||
mockClient := &mockaur.MockAUR{
|
||||
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
|
||||
return []aur.Pkg{{}}, nil // fakes a package found for all
|
||||
},
|
||||
}
|
||||
targets := []string{"yay", "yay-bin", "yay-git"}
|
||||
cmdRunner := &testRunner{}
|
||||
cmdBuilder := &testGitBuilder{
|
||||
@ -128,8 +96,8 @@ func TestPKGBUILDReposClone(t *testing.T) {
|
||||
searcher := &testDBSearcher{
|
||||
absPackagesDB: map[string]string{"yay": "core"},
|
||||
}
|
||||
cloned, err := PKGBUILDRepos(context.Background(), searcher, mockClient,
|
||||
cmdBuilder, newTestLogger(),
|
||||
cloned, err := PKGBUILDRepos(context.TODO(), searcher,
|
||||
cmdBuilder,
|
||||
targets, parser.ModeAny, "https://aur.archlinux.org", dir, false)
|
||||
|
||||
assert.NoError(t, err)
|
||||
@ -143,11 +111,6 @@ func TestPKGBUILDReposNotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
dir := t.TempDir()
|
||||
|
||||
mockClient := &mockaur.MockAUR{
|
||||
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
|
||||
return []aur.Pkg{{}}, nil // fakes a package found for all
|
||||
},
|
||||
}
|
||||
targets := []string{"extra/yay", "yay-bin", "yay-git"}
|
||||
cmdRunner := &testRunner{}
|
||||
cmdBuilder := &testGitBuilder{
|
||||
@ -162,8 +125,8 @@ func TestPKGBUILDReposNotFound(t *testing.T) {
|
||||
searcher := &testDBSearcher{
|
||||
absPackagesDB: map[string]string{"yay": "core"},
|
||||
}
|
||||
cloned, err := PKGBUILDRepos(context.Background(), searcher, mockClient,
|
||||
cmdBuilder, newTestLogger(),
|
||||
cloned, err := PKGBUILDRepos(context.TODO(), searcher,
|
||||
cmdBuilder,
|
||||
targets, parser.ModeAny, "https://aur.archlinux.org", dir, false)
|
||||
|
||||
assert.NoError(t, err)
|
||||
@ -177,11 +140,6 @@ func TestPKGBUILDReposRepoMode(t *testing.T) {
|
||||
t.Parallel()
|
||||
dir := t.TempDir()
|
||||
|
||||
mockClient := &mockaur.MockAUR{
|
||||
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
|
||||
return []aur.Pkg{}, nil // fakes a package found for all
|
||||
},
|
||||
}
|
||||
targets := []string{"yay", "yay-bin", "yay-git"}
|
||||
cmdRunner := &testRunner{}
|
||||
cmdBuilder := &testGitBuilder{
|
||||
@ -196,8 +154,8 @@ func TestPKGBUILDReposRepoMode(t *testing.T) {
|
||||
searcher := &testDBSearcher{
|
||||
absPackagesDB: map[string]string{"yay": "core"},
|
||||
}
|
||||
cloned, err := PKGBUILDRepos(context.Background(), searcher, mockClient,
|
||||
cmdBuilder, newTestLogger(),
|
||||
cloned, err := PKGBUILDRepos(context.TODO(), searcher,
|
||||
cmdBuilder,
|
||||
targets, parser.ModeRepo, "https://aur.archlinux.org", dir, false)
|
||||
|
||||
assert.NoError(t, err)
|
||||
@ -210,11 +168,6 @@ func TestPKGBUILDReposRepoMode(t *testing.T) {
|
||||
func TestPKGBUILDFull(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
mockClient := &mockaur.MockAUR{
|
||||
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
|
||||
return []aur.Pkg{{}}, nil
|
||||
},
|
||||
}
|
||||
gock.New("https://aur.archlinux.org").
|
||||
Get("/cgit/aur.git/plain/PKGBUILD").MatchParam("h", "yay-git").
|
||||
Reply(200).
|
||||
@ -224,8 +177,8 @@ func TestPKGBUILDFull(t *testing.T) {
|
||||
Reply(200).
|
||||
BodyString("example_yay-bin")
|
||||
|
||||
gock.New("https://gitlab.archlinux.org/").
|
||||
Get("archlinux/packaging/packages/yay/-/raw/main/PKGBUILD").
|
||||
gock.New("https://github.com/").
|
||||
Get("/archlinux/svntogit-packages/raw/packages/yay/trunk/PKGBUILD").
|
||||
Reply(200).
|
||||
BodyString("example_yay")
|
||||
|
||||
@ -235,7 +188,7 @@ func TestPKGBUILDFull(t *testing.T) {
|
||||
absPackagesDB: map[string]string{"yay": "core"},
|
||||
}
|
||||
|
||||
fetched, err := PKGBUILDs(searcher, mockClient, &http.Client{}, newTestLogger(),
|
||||
fetched, err := PKGBUILDs(searcher, &http.Client{},
|
||||
targets, "https://aur.archlinux.org", parser.ModeAny)
|
||||
|
||||
assert.NoError(t, err)
|
||||
@ -245,37 +198,3 @@ func TestPKGBUILDFull(t *testing.T) {
|
||||
"yay-git": []byte("example_yay-git"),
|
||||
}, fetched)
|
||||
}
|
||||
|
||||
// GIVEN 2 aur packages and 1 in repo
|
||||
// WHEN aur packages are not found
|
||||
// only repo should be cloned
|
||||
func TestPKGBUILDReposMissingAUR(t *testing.T) {
|
||||
t.Parallel()
|
||||
dir := t.TempDir()
|
||||
|
||||
mockClient := &mockaur.MockAUR{
|
||||
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
|
||||
return []aur.Pkg{}, nil // fakes a package found for all
|
||||
},
|
||||
}
|
||||
targets := []string{"core/yay", "aur/yay-bin", "aur/yay-git"}
|
||||
cmdRunner := &testRunner{}
|
||||
cmdBuilder := &testGitBuilder{
|
||||
index: 0,
|
||||
test: t,
|
||||
parentBuilder: &exe.CmdBuilder{
|
||||
Runner: cmdRunner,
|
||||
GitBin: "/usr/local/bin/git",
|
||||
GitFlags: []string{},
|
||||
},
|
||||
}
|
||||
searcher := &testDBSearcher{
|
||||
absPackagesDB: map[string]string{"yay": "core"},
|
||||
}
|
||||
cloned, err := PKGBUILDRepos(context.Background(), searcher, mockClient,
|
||||
cmdBuilder, newTestLogger(),
|
||||
targets, parser.ModeAny, "https://aur.archlinux.org", dir, false)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, map[string]bool{"core/yay": true}, cloned)
|
||||
}
|
||||
|
@ -12,8 +12,8 @@ import (
|
||||
|
||||
"github.com/Jguer/go-alpm/v2"
|
||||
|
||||
"github.com/Jguer/yay/v12/pkg/db"
|
||||
"github.com/Jguer/yay/v12/pkg/settings/exe"
|
||||
"github.com/Jguer/yay/v11/pkg/db"
|
||||
"github.com/Jguer/yay/v11/pkg/settings/exe"
|
||||
)
|
||||
|
||||
type testRunner struct{}
|
||||
@ -102,7 +102,7 @@ func (d *testDBSearcher) SyncPackage(name string) db.IPackage {
|
||||
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 {
|
||||
return &testPackage{
|
||||
name: name,
|
||||
|
@ -5,7 +5,7 @@ import (
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
mapset "github.com/deckarep/golang-set/v2"
|
||||
"github.com/Jguer/yay/v11/pkg/stringset"
|
||||
)
|
||||
|
||||
// IntRange stores a max and min amount for range.
|
||||
@ -17,10 +17,10 @@ type IntRange struct {
|
||||
// IntRanges is a slice of IntRange.
|
||||
type IntRanges []IntRange
|
||||
|
||||
func makeIntRange(minVal, maxVal int) IntRange {
|
||||
func makeIntRange(min, max int) IntRange {
|
||||
return IntRange{
|
||||
min: minVal,
|
||||
max: maxVal,
|
||||
min,
|
||||
max,
|
||||
}
|
||||
}
|
||||
|
||||
@ -42,6 +42,24 @@ func (rs IntRanges) Get(n int) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Min returns min value between a and b.
|
||||
func Min(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// Max returns max value between a and b.
|
||||
func Max(a, b int) int {
|
||||
if a < b {
|
||||
return b
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// ParseNumberMenu parses input for number menus split by spaces or commas
|
||||
// supports individual selection: 1 2 3 4
|
||||
// supports range selections: 1-4 10-20
|
||||
@ -53,12 +71,11 @@ func (rs IntRanges) Get(n int) bool {
|
||||
// of course the implementation is up to the caller, this function mearley parses
|
||||
// the input and organizes it.
|
||||
func ParseNumberMenu(input string) (include, exclude IntRanges,
|
||||
otherInclude, otherExclude mapset.Set[string],
|
||||
) {
|
||||
otherInclude, otherExclude stringset.StringSet) {
|
||||
include = make(IntRanges, 0)
|
||||
exclude = make(IntRanges, 0)
|
||||
otherInclude = mapset.NewThreadUnsafeSet[string]()
|
||||
otherExclude = mapset.NewThreadUnsafeSet[string]()
|
||||
otherInclude = make(stringset.StringSet)
|
||||
otherExclude = make(stringset.StringSet)
|
||||
|
||||
words := strings.FieldsFunc(input, func(c rune) bool {
|
||||
return unicode.IsSpace(c) || c == ','
|
||||
@ -84,22 +101,22 @@ func ParseNumberMenu(input string) (include, exclude IntRanges,
|
||||
|
||||
num1, err = strconv.Atoi(ranges[0])
|
||||
if err != nil {
|
||||
other.Add(strings.ToLower(word))
|
||||
other.Set(strings.ToLower(word))
|
||||
continue
|
||||
}
|
||||
|
||||
if len(ranges) == 2 {
|
||||
num2, err = strconv.Atoi(ranges[1])
|
||||
if err != nil {
|
||||
other.Add(strings.ToLower(word))
|
||||
other.Set(strings.ToLower(word))
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
num2 = num1
|
||||
}
|
||||
|
||||
mi := min(num1, num2)
|
||||
ma := max(num1, num2)
|
||||
mi := Min(num1, num2)
|
||||
ma := Max(num1, num2)
|
||||
|
||||
if !invert {
|
||||
include = append(include, makeIntRange(mi, ma))
|
||||
|
@ -1,13 +1,9 @@
|
||||
//go:build !integration
|
||||
// +build !integration
|
||||
|
||||
package intrange
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
mapset "github.com/deckarep/golang-set/v2"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/Jguer/yay/v11/pkg/stringset"
|
||||
)
|
||||
|
||||
func TestParseNumberMenu(t *testing.T) {
|
||||
@ -15,8 +11,8 @@ func TestParseNumberMenu(t *testing.T) {
|
||||
type result struct {
|
||||
Include IntRanges
|
||||
Exclude IntRanges
|
||||
OtherInclude mapset.Set[string]
|
||||
OtherExclude mapset.Set[string]
|
||||
OtherInclude stringset.StringSet
|
||||
OtherExclude stringset.StringSet
|
||||
}
|
||||
|
||||
inputs := []string{
|
||||
@ -41,15 +37,15 @@ func TestParseNumberMenu(t *testing.T) {
|
||||
makeIntRange(3, 3),
|
||||
makeIntRange(4, 4),
|
||||
makeIntRange(5, 5),
|
||||
}, IntRanges{}, mapset.NewThreadUnsafeSet[string](), mapset.NewThreadUnsafeSet[string]()},
|
||||
}, IntRanges{}, make(stringset.StringSet), make(stringset.StringSet)},
|
||||
{IntRanges{
|
||||
makeIntRange(1, 10),
|
||||
makeIntRange(5, 15),
|
||||
}, IntRanges{}, mapset.NewThreadUnsafeSet[string](), mapset.NewThreadUnsafeSet[string]()},
|
||||
}, IntRanges{}, make(stringset.StringSet), make(stringset.StringSet)},
|
||||
{IntRanges{
|
||||
makeIntRange(5, 10),
|
||||
makeIntRange(85, 90),
|
||||
}, IntRanges{}, mapset.NewThreadUnsafeSet[string](), mapset.NewThreadUnsafeSet[string]()},
|
||||
}, IntRanges{}, make(stringset.StringSet), make(stringset.StringSet)},
|
||||
{
|
||||
IntRanges{
|
||||
makeIntRange(1, 1),
|
||||
@ -62,18 +58,18 @@ func TestParseNumberMenu(t *testing.T) {
|
||||
makeIntRange(38, 40),
|
||||
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{}, mapset.NewThreadUnsafeSet("a-b"), mapset.NewThreadUnsafeSet("abort", "a-b")},
|
||||
{IntRanges{}, IntRanges{}, mapset.NewThreadUnsafeSet("-9223372036854775809-9223372036854775809"), mapset.NewThreadUnsafeSet[string]()},
|
||||
{IntRanges{}, IntRanges{}, stringset.Make("abort", "all", "none"), make(stringset.StringSet)},
|
||||
{IntRanges{}, IntRanges{}, stringset.Make("a-b"), stringset.Make("abort", "a-b")},
|
||||
{IntRanges{}, IntRanges{}, stringset.Make("-9223372036854775809-9223372036854775809"), make(stringset.StringSet)},
|
||||
{IntRanges{
|
||||
makeIntRange(1, 1),
|
||||
makeIntRange(2, 2),
|
||||
makeIntRange(3, 3),
|
||||
makeIntRange(4, 4),
|
||||
makeIntRange(5, 5),
|
||||
}, IntRanges{}, mapset.NewThreadUnsafeSet[string](), mapset.NewThreadUnsafeSet[string]()},
|
||||
}, IntRanges{}, make(stringset.StringSet), make(stringset.StringSet)},
|
||||
{IntRanges{
|
||||
makeIntRange(1, 1),
|
||||
makeIntRange(2, 2),
|
||||
@ -83,20 +79,23 @@ func TestParseNumberMenu(t *testing.T) {
|
||||
makeIntRange(6, 6),
|
||||
makeIntRange(7, 7),
|
||||
makeIntRange(8, 8),
|
||||
}, IntRanges{}, mapset.NewThreadUnsafeSet[string](), mapset.NewThreadUnsafeSet[string]()},
|
||||
{IntRanges{}, IntRanges{}, mapset.NewThreadUnsafeSet[string](), mapset.NewThreadUnsafeSet[string]()},
|
||||
{IntRanges{}, IntRanges{}, mapset.NewThreadUnsafeSet[string](), mapset.NewThreadUnsafeSet[string]()},
|
||||
{IntRanges{}, IntRanges{}, mapset.NewThreadUnsafeSet("a", "b", "c", "d", "e"), mapset.NewThreadUnsafeSet[string]()},
|
||||
}, IntRanges{}, make(stringset.StringSet), make(stringset.StringSet)},
|
||||
{IntRanges{}, IntRanges{}, make(stringset.StringSet), make(stringset.StringSet)},
|
||||
{IntRanges{}, IntRanges{}, make(stringset.StringSet), make(stringset.StringSet)},
|
||||
{IntRanges{}, IntRanges{}, stringset.Make("a", "b", "c", "d", "e"), make(stringset.StringSet)},
|
||||
}
|
||||
|
||||
for n, in := range inputs {
|
||||
res := expected[n]
|
||||
include, exclude, otherInclude, otherExclude := ParseNumberMenu(in)
|
||||
|
||||
assert.True(t, intRangesEqual(include, res.Include), "Test %d Failed: Expected: include=%+v got include=%+v", n+1, res.Include, include)
|
||||
assert.True(t, intRangesEqual(exclude, res.Exclude), "Test %d Failed: Expected: exclude=%+v got exclude=%+v", n+1, res.Exclude, exclude)
|
||||
assert.True(t, otherInclude.Equal(res.OtherInclude), "Test %d Failed: Expected: otherInclude=%+v got otherInclude=%+v", n+1, res.OtherInclude, otherInclude)
|
||||
assert.True(t, otherExclude.Equal(res.OtherExclude), "Test %d Failed: Expected: otherExclude=%+v got otherExclude=%+v", n+1, res.OtherExclude, otherExclude)
|
||||
if !intRangesEqual(include, res.Include) ||
|
||||
!intRangesEqual(exclude, res.Exclude) ||
|
||||
!stringset.Equal(otherInclude, res.OtherInclude) ||
|
||||
!stringset.Equal(otherExclude, res.OtherExclude) {
|
||||
t.Fatalf("Test %d Failed: Expected: include=%+v exclude=%+v otherInclude=%+v otherExclude=%+v got include=%+v excluive=%+v otherInclude=%+v otherExclude=%+v",
|
||||
n+1, res.Include, res.Exclude, res.OtherInclude, res.OtherExclude, include, exclude, otherInclude, otherExclude)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,20 +2,22 @@
|
||||
package menus
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
mapset "github.com/deckarep/golang-set/v2"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
|
||||
"github.com/Jguer/yay/v12/pkg/runtime"
|
||||
"github.com/Jguer/yay/v12/pkg/settings"
|
||||
"github.com/Jguer/yay/v12/pkg/text"
|
||||
"github.com/Jguer/yay/v11/pkg/dep"
|
||||
"github.com/Jguer/yay/v11/pkg/stringset"
|
||||
"github.com/Jguer/yay/v11/pkg/text"
|
||||
)
|
||||
|
||||
func anyExistInCache(pkgbuildDirs map[string]string) bool {
|
||||
for _, dir := range pkgbuildDirs {
|
||||
func anyExistInCache(buildDir string, bases []dep.Base) bool {
|
||||
for _, base := range bases {
|
||||
pkg := base.Pkgbase()
|
||||
dir := filepath.Join(buildDir, pkg)
|
||||
|
||||
if _, err := os.Stat(dir); !os.IsNotExist(err) {
|
||||
return true
|
||||
}
|
||||
@ -24,20 +26,14 @@ func anyExistInCache(pkgbuildDirs map[string]string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func CleanFn(ctx context.Context, run *runtime.Runtime, w io.Writer,
|
||||
pkgbuildDirsByBase map[string]string, installed mapset.Set[string],
|
||||
) error {
|
||||
if len(pkgbuildDirsByBase) == 0 {
|
||||
return nil // no work to do
|
||||
}
|
||||
|
||||
if !anyExistInCache(pkgbuildDirsByBase) {
|
||||
func Clean(cleanMenuOption bool, buildDir string, bases []dep.Base,
|
||||
installed stringset.StringSet, noConfirm bool, answerClean string) error {
|
||||
if !(cleanMenuOption && anyExistInCache(buildDir, bases)) {
|
||||
return nil
|
||||
}
|
||||
|
||||
skipFunc := func(pkg string) bool {
|
||||
dir := pkgbuildDirsByBase[pkg]
|
||||
// TOFIX: new install engine dir will always exist, check if unclean instead
|
||||
dir := filepath.Join(buildDir, pkg)
|
||||
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
||||
return true
|
||||
}
|
||||
@ -45,32 +41,18 @@ func CleanFn(ctx context.Context, run *runtime.Runtime, w io.Writer,
|
||||
return false
|
||||
}
|
||||
|
||||
bases := make([]string, 0, len(pkgbuildDirsByBase))
|
||||
for pkg := range pkgbuildDirsByBase {
|
||||
bases = append(bases, pkg)
|
||||
}
|
||||
|
||||
toClean, errClean := selectionMenu(run.Logger, pkgbuildDirsByBase, bases, installed,
|
||||
gotext.Get("Packages to cleanBuild?"),
|
||||
settings.NoConfirm, run.Cfg.AnswerClean, skipFunc)
|
||||
toClean, errClean := selectionMenu(buildDir, bases, installed, gotext.Get("Packages to cleanBuild?"),
|
||||
noConfirm, answerClean, skipFunc)
|
||||
if errClean != nil {
|
||||
return errClean
|
||||
}
|
||||
|
||||
for i, base := range toClean {
|
||||
dir := pkgbuildDirsByBase[base]
|
||||
run.Logger.OperationInfoln(gotext.Get("Deleting (%d/%d): %s", i+1, len(toClean), text.Cyan(dir)))
|
||||
dir := filepath.Join(buildDir, base.Pkgbase())
|
||||
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 {
|
||||
run.Logger.Warnln(gotext.Get("Unable to clean:"), dir)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
if err := run.CmdBuilder.Show(run.CmdBuilder.BuildGitCmd(ctx, dir, "clean", "-fdx")); err != nil {
|
||||
run.Logger.Warnln(gotext.Get("Unable to clean:"), dir)
|
||||
|
||||
return err
|
||||
if err := os.RemoveAll(dir); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,17 +4,18 @@ package menus
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
mapset "github.com/deckarep/golang-set/v2"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
|
||||
"github.com/Jguer/yay/v12/pkg/multierror"
|
||||
"github.com/Jguer/yay/v12/pkg/runtime"
|
||||
"github.com/Jguer/yay/v12/pkg/settings"
|
||||
"github.com/Jguer/yay/v12/pkg/settings/exe"
|
||||
"github.com/Jguer/yay/v12/pkg/text"
|
||||
"github.com/Jguer/yay/v11/pkg/dep"
|
||||
"github.com/Jguer/yay/v11/pkg/multierror"
|
||||
"github.com/Jguer/yay/v11/pkg/settings"
|
||||
"github.com/Jguer/yay/v11/pkg/settings/exe"
|
||||
"github.com/Jguer/yay/v11/pkg/stringset"
|
||||
"github.com/Jguer/yay/v11/pkg/text"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -22,23 +23,24 @@ const (
|
||||
gitDiffRefName = "AUR_SEEN"
|
||||
)
|
||||
|
||||
func showPkgbuildDiffs(ctx context.Context, cmdBuilder exe.ICmdBuilder, logger *text.Logger,
|
||||
pkgbuildDirs map[string]string, bases []string,
|
||||
) error {
|
||||
func showPkgbuildDiffs(ctx context.Context, cmdBuilder exe.ICmdBuilder, buildDir string, bases []dep.Base, cloned map[string]bool) error {
|
||||
var errMulti multierror.MultiError
|
||||
|
||||
for _, pkg := range bases {
|
||||
dir := pkgbuildDirs[pkg]
|
||||
for _, base := range bases {
|
||||
pkg := base.Pkgbase()
|
||||
dir := filepath.Join(buildDir, pkg)
|
||||
|
||||
start, err := getLastSeenHash(ctx, cmdBuilder, dir)
|
||||
start, err := getLastSeenHash(ctx, cmdBuilder, buildDir, pkg)
|
||||
if err != nil {
|
||||
errMulti.Add(err)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if start != gitEmptyTree {
|
||||
hasDiff, err := gitHasDiff(ctx, cmdBuilder, dir)
|
||||
if cloned[pkg] {
|
||||
start = gitEmptyTree
|
||||
} else {
|
||||
hasDiff, err := gitHasDiff(ctx, cmdBuilder, buildDir, pkg)
|
||||
if err != nil {
|
||||
errMulti.Add(err)
|
||||
|
||||
@ -46,7 +48,7 @@ func showPkgbuildDiffs(ctx context.Context, cmdBuilder exe.ICmdBuilder, logger *
|
||||
}
|
||||
|
||||
if !hasDiff {
|
||||
logger.Warnln(gotext.Get("%s: No changes -- skipping", text.Cyan(pkg)))
|
||||
text.Warnln(gotext.Get("%s: No changes -- skipping", text.Cyan(base.String())))
|
||||
|
||||
continue
|
||||
}
|
||||
@ -71,12 +73,12 @@ func showPkgbuildDiffs(ctx context.Context, cmdBuilder exe.ICmdBuilder, logger *
|
||||
|
||||
// Check whether or not a diff exists between the last reviewed diff and
|
||||
// HEAD@{upstream}.
|
||||
func gitHasDiff(ctx context.Context, cmdBuilder exe.ICmdBuilder, dir string) (bool, error) {
|
||||
if gitHasLastSeenRef(ctx, cmdBuilder, dir) {
|
||||
func gitHasDiff(ctx context.Context, cmdBuilder exe.ICmdBuilder, path, name string) (bool, error) {
|
||||
if gitHasLastSeenRef(ctx, cmdBuilder, path, name) {
|
||||
stdout, stderr, err := cmdBuilder.Capture(
|
||||
cmdBuilder.BuildGitCmd(ctx, dir, "rev-parse", gitDiffRefName, "HEAD@{upstream}"))
|
||||
cmdBuilder.BuildGitCmd(ctx, filepath.Join(path, name), "rev-parse", gitDiffRefName, "HEAD@{upstream}"))
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("%s%w", stderr, err)
|
||||
return false, fmt.Errorf("%s%s", stderr, err)
|
||||
}
|
||||
|
||||
lines := strings.Split(stdout, "\n")
|
||||
@ -85,30 +87,30 @@ func gitHasDiff(ctx context.Context, cmdBuilder exe.ICmdBuilder, dir string) (bo
|
||||
|
||||
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.
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Return whether or not we have reviewed a diff yet. It checks for the existence of
|
||||
// AUR_SEEN in the git ref-list.
|
||||
func gitHasLastSeenRef(ctx context.Context, cmdBuilder exe.ICmdBuilder, dir string) bool {
|
||||
// Return wether or not we have reviewed a diff yet. It checks for the existence of
|
||||
// YAY_DIFF_REVIEW in the git ref-list.
|
||||
func gitHasLastSeenRef(ctx context.Context, cmdBuilder exe.ICmdBuilder, path, name string) bool {
|
||||
_, _, err := cmdBuilder.Capture(
|
||||
cmdBuilder.BuildGitCmd(ctx,
|
||||
dir, "rev-parse", "--quiet", "--verify", gitDiffRefName))
|
||||
filepath.Join(path, name), "rev-parse", "--quiet", "--verify", gitDiffRefName))
|
||||
|
||||
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.
|
||||
func getLastSeenHash(ctx context.Context, cmdBuilder exe.ICmdBuilder, dir string) (string, error) {
|
||||
if gitHasLastSeenRef(ctx, cmdBuilder, dir) {
|
||||
func getLastSeenHash(ctx context.Context, cmdBuilder exe.ICmdBuilder, path, name string) (string, error) {
|
||||
if gitHasLastSeenRef(ctx, cmdBuilder, path, name) {
|
||||
stdout, stderr, err := cmdBuilder.Capture(
|
||||
cmdBuilder.BuildGitCmd(ctx,
|
||||
dir, "rev-parse", gitDiffRefName))
|
||||
filepath.Join(path, name), "rev-parse", gitDiffRefName))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("%s %w", stderr, err)
|
||||
return "", fmt.Errorf("%s %s", stderr, err)
|
||||
}
|
||||
|
||||
lines := strings.Split(stdout, "\n")
|
||||
@ -119,25 +121,26 @@ func getLastSeenHash(ctx context.Context, cmdBuilder exe.ICmdBuilder, dir string
|
||||
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.
|
||||
func gitUpdateSeenRef(ctx context.Context, cmdBuilder exe.ICmdBuilder, dir string) error {
|
||||
func gitUpdateSeenRef(ctx context.Context, cmdBuilder exe.ICmdBuilder, path, name string) error {
|
||||
_, stderr, err := cmdBuilder.Capture(
|
||||
cmdBuilder.BuildGitCmd(ctx,
|
||||
dir, "update-ref", gitDiffRefName, "HEAD"))
|
||||
filepath.Join(path, name), "update-ref", gitDiffRefName, "HEAD"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s %w", stderr, err)
|
||||
return fmt.Errorf("%s %s", stderr, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func updatePkgbuildSeenRef(ctx context.Context, cmdBuilder exe.ICmdBuilder, pkgbuildDirs map[string]string, bases []string) error {
|
||||
func updatePkgbuildSeenRef(ctx context.Context, cmdBuilder exe.ICmdBuilder, buildDir string, bases []dep.Base) error {
|
||||
var errMulti multierror.MultiError
|
||||
|
||||
for _, pkg := range bases {
|
||||
dir := pkgbuildDirs[pkg]
|
||||
if err := gitUpdateSeenRef(ctx, cmdBuilder, dir); err != nil {
|
||||
for _, base := range bases {
|
||||
pkg := base.Pkgbase()
|
||||
|
||||
if err := gitUpdateSeenRef(ctx, cmdBuilder, buildDir, pkg); err != nil {
|
||||
errMulti.Add(err)
|
||||
}
|
||||
}
|
||||
@ -145,35 +148,31 @@ func updatePkgbuildSeenRef(ctx context.Context, cmdBuilder exe.ICmdBuilder, pkgb
|
||||
return errMulti.Return()
|
||||
}
|
||||
|
||||
func DiffFn(ctx context.Context, run *runtime.Runtime, w io.Writer,
|
||||
pkgbuildDirsByBase map[string]string, installed mapset.Set[string],
|
||||
func Diff(ctx context.Context, cmdBuilder exe.ICmdBuilder,
|
||||
buildDir string, diffMenuOption bool, bases []dep.Base,
|
||||
installed stringset.StringSet, cloned map[string]bool, noConfirm bool, diffDefaultAnswer string,
|
||||
) error {
|
||||
if len(pkgbuildDirsByBase) == 0 {
|
||||
return nil // no work to do
|
||||
if !diffMenuOption {
|
||||
return nil
|
||||
}
|
||||
|
||||
bases := make([]string, 0, len(pkgbuildDirsByBase))
|
||||
for base := range pkgbuildDirsByBase {
|
||||
bases = append(bases, base)
|
||||
}
|
||||
|
||||
toDiff, errMenu := selectionMenu(run.Logger, pkgbuildDirsByBase, bases, installed, gotext.Get("Diffs to show?"),
|
||||
settings.NoConfirm, run.Cfg.AnswerDiff, nil)
|
||||
toDiff, errMenu := selectionMenu(buildDir, bases, installed, gotext.Get("Diffs to show?"),
|
||||
noConfirm, diffDefaultAnswer, nil)
|
||||
if errMenu != nil || len(toDiff) == 0 {
|
||||
return errMenu
|
||||
}
|
||||
|
||||
if errD := showPkgbuildDiffs(ctx, run.CmdBuilder, run.Logger, pkgbuildDirsByBase, toDiff); errD != nil {
|
||||
if errD := showPkgbuildDiffs(ctx, cmdBuilder, buildDir, toDiff, cloned); errD != nil {
|
||||
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{}
|
||||
}
|
||||
|
||||
if errUpd := updatePkgbuildSeenRef(ctx, run.CmdBuilder, pkgbuildDirsByBase, toDiff); errUpd != nil {
|
||||
if errUpd := updatePkgbuildSeenRef(ctx, cmdBuilder, buildDir, toDiff); errUpd != nil {
|
||||
return errUpd
|
||||
}
|
||||
|
||||
|
@ -2,30 +2,29 @@
|
||||
package menus
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
gosrc "github.com/Morganamilo/go-srcinfo"
|
||||
mapset "github.com/deckarep/golang-set/v2"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
|
||||
"github.com/Jguer/yay/v12/pkg/runtime"
|
||||
"github.com/Jguer/yay/v12/pkg/settings"
|
||||
"github.com/Jguer/yay/v12/pkg/text"
|
||||
"github.com/Jguer/yay/v11/pkg/dep"
|
||||
"github.com/Jguer/yay/v11/pkg/settings"
|
||||
"github.com/Jguer/yay/v11/pkg/stringset"
|
||||
"github.com/Jguer/yay/v11/pkg/text"
|
||||
)
|
||||
|
||||
// Editor returns the preferred system editor.
|
||||
func editor(log *text.Logger, editorConfig, editorFlags string, noConfirm bool) (editor string, args []string) {
|
||||
func editor(editorConfig, editorFlags string, noConfirm bool) (editor string, args []string) {
|
||||
switch {
|
||||
case editorConfig != "":
|
||||
editor, err := exec.LookPath(editorConfig)
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
} else {
|
||||
return editor, strings.Fields(editorFlags)
|
||||
}
|
||||
@ -35,7 +34,7 @@ func editor(log *text.Logger, editorConfig, editorFlags string, noConfirm bool)
|
||||
if editorArgs := strings.Fields(os.Getenv("VISUAL")); len(editorArgs) != 0 {
|
||||
editor, err := exec.LookPath(editorArgs[0])
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
} else {
|
||||
return editor, editorArgs[1:]
|
||||
}
|
||||
@ -46,7 +45,7 @@ func editor(log *text.Logger, editorConfig, editorFlags string, noConfirm bool)
|
||||
if editorArgs := strings.Fields(os.Getenv("EDITOR")); len(editorArgs) != 0 {
|
||||
editor, err := exec.LookPath(editorArgs[0])
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
} else {
|
||||
return editor, editorArgs[1:]
|
||||
}
|
||||
@ -54,15 +53,16 @@ func editor(log *text.Logger, editorConfig, editorFlags string, noConfirm bool)
|
||||
|
||||
fallthrough
|
||||
default:
|
||||
log.Errorln("\n", gotext.Get("%s is not set", text.Bold(text.Cyan("$EDITOR"))))
|
||||
log.Warnln(gotext.Get("Add %s or %s to your environment variables", text.Bold(text.Cyan("$EDITOR")), text.Bold(text.Cyan("$VISUAL"))))
|
||||
fmt.Fprintln(os.Stderr)
|
||||
text.Errorln(gotext.Get("%s is not set", text.Bold(text.Cyan("$EDITOR"))))
|
||||
text.Warnln(gotext.Get("Add %s or %s to your environment variables", text.Bold(text.Cyan("$EDITOR")), text.Bold(text.Cyan("$VISUAL"))))
|
||||
|
||||
for {
|
||||
log.Infoln(gotext.Get("Edit PKGBUILD with?"))
|
||||
text.Infoln(gotext.Get("Edit PKGBUILD with?"))
|
||||
|
||||
editorInput, err := log.GetInput("", noConfirm)
|
||||
editorInput, err := text.GetInput("", noConfirm)
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
continue
|
||||
}
|
||||
|
||||
@ -73,7 +73,7 @@ func editor(log *text.Logger, editorConfig, editorFlags string, noConfirm bool)
|
||||
|
||||
editor, err := exec.LookPath(editorArgs[0])
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
continue
|
||||
}
|
||||
|
||||
@ -82,26 +82,25 @@ func editor(log *text.Logger, editorConfig, editorFlags string, noConfirm bool)
|
||||
}
|
||||
}
|
||||
|
||||
func editPkgbuilds(log *text.Logger, pkgbuildDirs map[string]string, bases []string, editorConfig,
|
||||
func editPkgbuilds(buildDir string, bases []dep.Base, editorConfig,
|
||||
editorFlags string, srcinfos map[string]*gosrc.Srcinfo, noConfirm bool,
|
||||
) error {
|
||||
pkgbuilds := make([]string, 0, len(bases))
|
||||
|
||||
for _, pkg := range bases {
|
||||
dir := pkgbuildDirs[pkg]
|
||||
for _, base := range bases {
|
||||
pkg := base.Pkgbase()
|
||||
dir := filepath.Join(buildDir, pkg)
|
||||
pkgbuilds = append(pkgbuilds, filepath.Join(dir, "PKGBUILD"))
|
||||
|
||||
if srcinfos != nil {
|
||||
for _, splitPkg := range srcinfos[pkg].SplitPackages() {
|
||||
if splitPkg.Install != "" {
|
||||
pkgbuilds = append(pkgbuilds, filepath.Join(dir, splitPkg.Install))
|
||||
}
|
||||
for _, splitPkg := range srcinfos[pkg].SplitPackages() {
|
||||
if splitPkg.Install != "" {
|
||||
pkgbuilds = append(pkgbuilds, filepath.Join(dir, splitPkg.Install))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(pkgbuilds) > 0 {
|
||||
editor, editorArgs := editor(log, editorConfig, editorFlags, noConfirm)
|
||||
editor, editorArgs := editor(editorConfig, editorFlags, noConfirm)
|
||||
editorArgs = append(editorArgs, pkgbuilds...)
|
||||
editcmd := exec.Command(editor, editorArgs...)
|
||||
editcmd.Stdin, editcmd.Stdout, editcmd.Stderr = os.Stdin, os.Stdout, os.Stderr
|
||||
@ -114,33 +113,27 @@ func editPkgbuilds(log *text.Logger, pkgbuildDirs map[string]string, bases []str
|
||||
return nil
|
||||
}
|
||||
|
||||
func EditFn(ctx context.Context, run *runtime.Runtime, w io.Writer,
|
||||
pkgbuildDirsByBase map[string]string, installed mapset.Set[string],
|
||||
func Edit(editMenuOption bool, buildDir string, bases []dep.Base, editorConfig,
|
||||
editorFlags string, installed stringset.StringSet, srcinfos map[string]*gosrc.Srcinfo,
|
||||
noConfirm bool, editDefaultAnswer string,
|
||||
) error {
|
||||
if len(pkgbuildDirsByBase) == 0 {
|
||||
return nil // no work to do
|
||||
if !editMenuOption {
|
||||
return nil
|
||||
}
|
||||
|
||||
bases := make([]string, 0, len(pkgbuildDirsByBase))
|
||||
for pkg := range pkgbuildDirsByBase {
|
||||
bases = append(bases, pkg)
|
||||
}
|
||||
|
||||
toEdit, errMenu := selectionMenu(run.Logger, pkgbuildDirsByBase, bases, installed,
|
||||
gotext.Get("PKGBUILDs to edit?"), settings.NoConfirm, run.Cfg.AnswerEdit, nil)
|
||||
toEdit, errMenu := selectionMenu(buildDir, bases,
|
||||
installed, gotext.Get("PKGBUILDs to edit?"), noConfirm, editDefaultAnswer, nil)
|
||||
if errMenu != nil || len(toEdit) == 0 {
|
||||
return errMenu
|
||||
}
|
||||
|
||||
// TOFIX: remove or use srcinfo data
|
||||
if errEdit := editPkgbuilds(run.Logger, pkgbuildDirsByBase,
|
||||
toEdit, run.Cfg.Editor, run.Cfg.EditorFlags, nil, settings.NoConfirm); errEdit != nil {
|
||||
if errEdit := editPkgbuilds(buildDir, toEdit, editorConfig, editorFlags, srcinfos, noConfirm); errEdit != nil {
|
||||
return errEdit
|
||||
}
|
||||
|
||||
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{}
|
||||
}
|
||||
|
||||
|
@ -3,31 +3,31 @@ package menus
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/leonelquinteros/gotext"
|
||||
|
||||
"github.com/Jguer/yay/v12/pkg/intrange"
|
||||
"github.com/Jguer/yay/v12/pkg/settings"
|
||||
"github.com/Jguer/yay/v12/pkg/text"
|
||||
|
||||
mapset "github.com/deckarep/golang-set/v2"
|
||||
"github.com/Jguer/yay/v11/pkg/dep"
|
||||
"github.com/Jguer/yay/v11/pkg/intrange"
|
||||
"github.com/Jguer/yay/v11/pkg/settings"
|
||||
"github.com/Jguer/yay/v11/pkg/stringset"
|
||||
"github.com/Jguer/yay/v11/pkg/text"
|
||||
)
|
||||
|
||||
func pkgbuildNumberMenu(logger *text.Logger, pkgbuildDirs map[string]string,
|
||||
bases []string, installed mapset.Set[string],
|
||||
) {
|
||||
func pkgbuildNumberMenu(buildDir string, bases []dep.Base, installed stringset.StringSet) {
|
||||
toPrint := ""
|
||||
|
||||
for n, pkgBase := range bases {
|
||||
dir := pkgbuildDirs[pkgBase]
|
||||
toPrint += fmt.Sprintf(text.Magenta("%3d")+" %-40s", len(pkgbuildDirs)-n,
|
||||
text.Bold(pkgBase))
|
||||
for n, base := range bases {
|
||||
pkg := base.Pkgbase()
|
||||
dir := filepath.Join(buildDir, pkg)
|
||||
|
||||
if installed.Contains(pkgBase) {
|
||||
toPrint += fmt.Sprintf(text.Magenta("%3d")+" %-40s", len(bases)-n,
|
||||
text.Bold(base.String()))
|
||||
|
||||
if base.AnyIsInSet(installed) {
|
||||
toPrint += text.Bold(text.Green(gotext.Get(" (Installed)")))
|
||||
}
|
||||
|
||||
// TODO: remove or refactor to check if git dir is unclean
|
||||
if _, err := os.Stat(dir); !os.IsNotExist(err) {
|
||||
toPrint += text.Bold(text.Green(gotext.Get(" (Build Files Exist)")))
|
||||
}
|
||||
@ -35,67 +35,68 @@ func pkgbuildNumberMenu(logger *text.Logger, pkgbuildDirs map[string]string,
|
||||
toPrint += "\n"
|
||||
}
|
||||
|
||||
logger.Print(toPrint)
|
||||
fmt.Print(toPrint)
|
||||
}
|
||||
|
||||
func selectionMenu(logger *text.Logger, pkgbuildDirs map[string]string, bases []string, installed mapset.Set[string],
|
||||
message string, noConfirm bool, defaultAnswer string, skipFunc func(string) bool,
|
||||
) ([]string, error) {
|
||||
selected := make([]string, 0)
|
||||
func selectionMenu(buildDir string, bases []dep.Base, installed stringset.StringSet,
|
||||
message string, noConfirm bool, defaultAnswer string, skipFunc func(string) bool) ([]dep.Base, error) {
|
||||
selected := make([]dep.Base, 0)
|
||||
|
||||
pkgbuildNumberMenu(logger, pkgbuildDirs, bases, installed)
|
||||
pkgbuildNumberMenu(buildDir, bases, installed)
|
||||
|
||||
logger.Infoln(message)
|
||||
logger.Infoln(gotext.Get("%s [A]ll [Ab]ort [I]nstalled [No]tInstalled or (1 2 3, 1-3, ^4)", text.Cyan(gotext.Get("[N]one"))))
|
||||
text.Infoln(message)
|
||||
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(defaultAnswer, noConfirm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
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{}
|
||||
}
|
||||
|
||||
if eOtherInclude.Contains("n") || eOtherInclude.Contains("none") {
|
||||
if eOtherInclude.Get("n") || eOtherInclude.Get("none") {
|
||||
return selected, nil
|
||||
}
|
||||
|
||||
for i, pkgBase := range bases {
|
||||
if skipFunc != nil && skipFunc(pkgBase) {
|
||||
for i, base := range bases {
|
||||
pkg := base.Pkgbase()
|
||||
|
||||
if skipFunc != nil && skipFunc(pkg) {
|
||||
continue
|
||||
}
|
||||
|
||||
anyInstalled := installed.Contains(pkgBase)
|
||||
anyInstalled := base.AnyIsInSet(installed)
|
||||
|
||||
if !eIsInclude && eExclude.Get(len(bases)-i) {
|
||||
continue
|
||||
}
|
||||
|
||||
if anyInstalled && (eOtherInclude.Contains("i") || eOtherInclude.Contains("installed")) {
|
||||
selected = append(selected, pkgBase)
|
||||
if anyInstalled && (eOtherInclude.Get("i") || eOtherInclude.Get("installed")) {
|
||||
selected = append(selected, base)
|
||||
continue
|
||||
}
|
||||
|
||||
if !anyInstalled && (eOtherInclude.Contains("no") || eOtherInclude.Contains("notinstalled")) {
|
||||
selected = append(selected, pkgBase)
|
||||
if !anyInstalled && (eOtherInclude.Get("no") || eOtherInclude.Get("notinstalled")) {
|
||||
selected = append(selected, base)
|
||||
continue
|
||||
}
|
||||
|
||||
if eOtherInclude.Contains("a") || eOtherInclude.Contains("all") {
|
||||
selected = append(selected, pkgBase)
|
||||
if eOtherInclude.Get("a") || eOtherInclude.Get("all") {
|
||||
selected = append(selected, base)
|
||||
continue
|
||||
}
|
||||
|
||||
if eIsInclude && (eInclude.Get(len(bases)-i) || eOtherInclude.Contains(pkgBase)) {
|
||||
selected = append(selected, pkgBase)
|
||||
if eIsInclude && (eInclude.Get(len(bases)-i) || eOtherInclude.Get(pkg)) {
|
||||
selected = append(selected, base)
|
||||
}
|
||||
|
||||
if !eIsInclude && (!eExclude.Get(len(bases)-i) && !eOtherExclude.Contains(pkgBase)) {
|
||||
selected = append(selected, pkgBase)
|
||||
if !eIsInclude && (!eExclude.Get(len(bases)-i) && !eOtherExclude.Get(pkg)) {
|
||||
selected = append(selected, base)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,13 +4,15 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"html"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Jguer/yay/v12/pkg/text"
|
||||
"github.com/Jguer/yay/v11/pkg/text"
|
||||
)
|
||||
|
||||
type item struct {
|
||||
@ -21,13 +23,13 @@ type item struct {
|
||||
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
|
||||
|
||||
date, err := time.Parse(time.RFC1123Z, item.PubDate)
|
||||
|
||||
if err != nil {
|
||||
logger.Errorln(err)
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
} else {
|
||||
fd = text.FormatTime(int(date.Unix()))
|
||||
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 {
|
||||
desc := strings.TrimSpace(parseNews(item.Description))
|
||||
logger.Println(desc)
|
||||
fmt.Println(desc)
|
||||
}
|
||||
}
|
||||
|
||||
@ -58,9 +60,7 @@ type rss struct {
|
||||
Channel channel `xml:"channel"`
|
||||
}
|
||||
|
||||
func PrintNewsFeed(ctx context.Context, client *http.Client, logger *text.Logger,
|
||||
cutOffDate time.Time, bottomUp, all, quiet bool,
|
||||
) error {
|
||||
func PrintNewsFeed(ctx context.Context, client *http.Client, cutOffDate time.Time, bottomUp, all, quiet bool) error {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://archlinux.org/feeds/news", http.NoBody)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -87,11 +87,11 @@ func PrintNewsFeed(ctx context.Context, client *http.Client, logger *text.Logger
|
||||
|
||||
if bottomUp {
|
||||
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 {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,3 @@
|
||||
//go:build !integration
|
||||
// +build !integration
|
||||
|
||||
package news
|
||||
|
||||
import (
|
||||
@ -8,15 +5,12 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/bradleyjkemp/cupaloy"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/h2non/gock.v1"
|
||||
|
||||
"github.com/Jguer/yay/v12/pkg/text"
|
||||
)
|
||||
|
||||
const lastNews = `
|
||||
@ -127,7 +121,6 @@ func TestPrintNewsFeed(t *testing.T) {
|
||||
{name: "latest-quiet", args: args{bottomUp: true, cutOffDate: lastNewsTime, all: false, quiet: true}, wantErr: false},
|
||||
{name: "latest-quiet-topdown", args: args{bottomUp: false, cutOffDate: lastNewsTime, all: false, quiet: true}, wantErr: false},
|
||||
}
|
||||
t.Setenv("TZ", "UTC")
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
@ -138,16 +131,17 @@ func TestPrintNewsFeed(t *testing.T) {
|
||||
|
||||
defer gock.Off()
|
||||
|
||||
rescueStdout := os.Stdout
|
||||
r, w, _ := os.Pipe()
|
||||
logger := text.NewLogger(w, w, strings.NewReader(""), false, "logger")
|
||||
os.Stdout = w
|
||||
|
||||
err := PrintNewsFeed(context.Background(), &http.Client{}, logger,
|
||||
tt.args.cutOffDate, tt.args.bottomUp, tt.args.all, tt.args.quiet)
|
||||
err := PrintNewsFeed(context.TODO(), &http.Client{}, tt.args.cutOffDate, tt.args.bottomUp, tt.args.all, tt.args.quiet)
|
||||
assert.NoError(t, err)
|
||||
|
||||
w.Close()
|
||||
out, _ := io.ReadAll(r)
|
||||
cupaloy.SnapshotT(t, out)
|
||||
os.Stdout = rescueStdout
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -166,14 +160,15 @@ func TestPrintNewsFeedSameDay(t *testing.T) {
|
||||
|
||||
defer gock.Off()
|
||||
|
||||
rescueStdout := os.Stdout
|
||||
r, w, _ := os.Pipe()
|
||||
logger := text.NewLogger(w, w, strings.NewReader(""), false, "logger")
|
||||
os.Stdout = w
|
||||
|
||||
err := PrintNewsFeed(context.Background(), &http.Client{}, logger,
|
||||
lastNewsTime, true, false, false)
|
||||
err := PrintNewsFeed(context.TODO(), &http.Client{}, lastNewsTime, true, false, false)
|
||||
assert.NoError(t, err)
|
||||
|
||||
w.Close()
|
||||
out, _ := io.ReadAll(r)
|
||||
cupaloy.SnapshotT(t, out)
|
||||
os.Stdout = rescueStdout
|
||||
}
|
||||
|
@ -0,0 +1,5 @@
|
||||
|
||||
|
||||
[1m[33m -> [0m[0m[36mABAF11C65A2970B130ABE3C479BE3E4300411886[0m, required by: [36mdummy-1 (dummy-1 dummy-2)[0m
|
||||
[1m[36m:: [0m[0m[1mImporting keys with gpg...[0m
|
||||
[1m[36m:: [0m[0m[1mPGP keys need importing:[0m
|
@ -0,0 +1,5 @@
|
||||
|
||||
|
||||
[1m[33m -> [0m[0m[36m487EACC08557AD082088DABA1EB2638FF56C0C53[0m, required by: [36mcower[0m
|
||||
[1m[36m:: [0m[0m[1mImporting keys with gpg...[0m
|
||||
[1m[36m:: [0m[0m[1mPGP keys need importing:[0m
|
@ -0,0 +1,5 @@
|
||||
|
||||
|
||||
[1m[33m -> [0m[0m[36mC52048C0C0748FEE227D47A2702353E0F7E48EDB[0m, required by: [36mdummy-3[0m
|
||||
[1m[36m:: [0m[0m[1mImporting keys with gpg...[0m
|
||||
[1m[36m:: [0m[0m[1mPGP keys need importing:[0m
|
1
pkg/pgp/.snapshots/TestCheckPgpKeys-two_existing
Normal file
1
pkg/pgp/.snapshots/TestCheckPgpKeys-two_existing
Normal file
@ -0,0 +1 @@
|
||||
|
@ -0,0 +1,6 @@
|
||||
|
||||
|
||||
[1m[33m -> [0m[0m[36m11E521D646982372EB577A1F8F0871F202119294[0m, required by: [36mlibc++[0m
|
||||
[1m[33m -> [0m[0m[36mB6C8F98282B944E3B0D5C2530FC3042E345AD05D[0m, required by: [36mlibc++[0m
|
||||
[1m[36m:: [0m[0m[1mImporting keys with gpg...[0m
|
||||
[1m[36m:: [0m[0m[1mPGP keys need importing:[0m
|
@ -2,21 +2,22 @@ package pgp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
gosrc "github.com/Morganamilo/go-srcinfo"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
|
||||
"github.com/Jguer/yay/v12/pkg/settings/exe"
|
||||
"github.com/Jguer/yay/v12/pkg/text"
|
||||
"github.com/Jguer/yay/v11/pkg/dep"
|
||||
"github.com/Jguer/yay/v11/pkg/text"
|
||||
)
|
||||
|
||||
// pgpKeySet maps a PGP key with a list of PKGBUILDs that require it.
|
||||
// This is similar to stringSet, used throughout the code.
|
||||
type pgpKeySet map[string][]string
|
||||
type pgpKeySet map[string][]dep.Base
|
||||
|
||||
func (set pgpKeySet) toSlice() []string {
|
||||
slice := make([]string, 0, len(set))
|
||||
@ -27,7 +28,7 @@ func (set pgpKeySet) toSlice() []string {
|
||||
return slice
|
||||
}
|
||||
|
||||
func (set pgpKeySet) set(key, p string) {
|
||||
func (set pgpKeySet) set(key string, p dep.Base) {
|
||||
// Using ToUpper to make sure keys with a different case will be
|
||||
// considered the same.
|
||||
upperKey := strings.ToUpper(key)
|
||||
@ -41,62 +42,66 @@ func (set pgpKeySet) get(key string) bool {
|
||||
return exists
|
||||
}
|
||||
|
||||
type GPGCmdBuilder interface {
|
||||
exe.Runner
|
||||
BuildGPGCmd(ctx context.Context, extraArgs ...string) *exec.Cmd
|
||||
}
|
||||
|
||||
// CheckPgpKeys iterates through the keys listed in the PKGBUILDs and if needed,
|
||||
// asks the user whether yay should try to import them.
|
||||
func CheckPgpKeys(ctx context.Context, logger *text.Logger, pkgbuildDirsByBase map[string]string, srcinfos map[string]*gosrc.Srcinfo,
|
||||
cmdBuilder GPGCmdBuilder, noConfirm bool,
|
||||
) ([]string, error) {
|
||||
func CheckPgpKeys(bases []dep.Base, srcinfos map[string]*gosrc.Srcinfo,
|
||||
gpgBin, gpgFlags string, noConfirm bool,
|
||||
) error {
|
||||
// Let's check the keys individually, and then we can offer to import
|
||||
// the problematic ones.
|
||||
problematic := make(pgpKeySet)
|
||||
|
||||
args := append(strings.Fields(gpgFlags), "--list-keys")
|
||||
|
||||
// Mapping all the keys.
|
||||
for pkg := range pkgbuildDirsByBase {
|
||||
for _, base := range bases {
|
||||
pkg := base.Pkgbase()
|
||||
srcinfo := srcinfos[pkg]
|
||||
|
||||
for _, key := range srcinfo.ValidPGPKeys {
|
||||
// If key already marked as problematic, indicate the current
|
||||
// PKGBUILD requires it.
|
||||
if problematic.get(key) {
|
||||
problematic.set(key, pkg)
|
||||
problematic.set(key, base)
|
||||
continue
|
||||
}
|
||||
|
||||
if err := cmdBuilder.Show(cmdBuilder.BuildGPGCmd(ctx, "--list-keys", key)); err != nil {
|
||||
problematic.set(key, pkg)
|
||||
cmd := exec.Command(gpgBin, append(args, key)...)
|
||||
if err := cmd.Run(); err != nil {
|
||||
problematic.set(key, base)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No key issues!
|
||||
if len(problematic) == 0 {
|
||||
return []string{}, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
str, err := formatKeysToImport(logger, problematic)
|
||||
str, err := formatKeysToImport(problematic)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Println("\n", str)
|
||||
fmt.Println()
|
||||
fmt.Println(str)
|
||||
|
||||
if logger.ContinueTask(gotext.Get("Import?"), true, noConfirm) {
|
||||
return problematic.toSlice(), importKeys(ctx, logger, cmdBuilder, problematic.toSlice())
|
||||
if text.ContinueTask(os.Stdin, gotext.Get("Import?"), true, noConfirm) {
|
||||
return importKeys(problematic.toSlice(), gpgBin, gpgFlags)
|
||||
}
|
||||
|
||||
return problematic.toSlice(), nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// importKeys tries to import the list of keys specified in its argument.
|
||||
func importKeys(ctx context.Context, logger *text.Logger, cmdBuilder GPGCmdBuilder, keys []string) error {
|
||||
logger.OperationInfoln(gotext.Get("Importing keys with gpg..."))
|
||||
func importKeys(keys []string, gpgBin, gpgFlags string) error {
|
||||
args := append(strings.Fields(gpgFlags), "--recv-keys")
|
||||
cmd := exec.Command(gpgBin, append(args, keys...)...)
|
||||
cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
|
||||
|
||||
if err := cmdBuilder.Show(cmdBuilder.BuildGPGCmd(ctx, append([]string{"--recv-keys"}, keys...)...)); err != nil {
|
||||
text.OperationInfoln(gotext.Get("Importing keys with gpg..."))
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
return errors.New(gotext.Get("problem importing keys"))
|
||||
}
|
||||
|
||||
@ -105,23 +110,23 @@ func importKeys(ctx context.Context, logger *text.Logger, cmdBuilder GPGCmdBuild
|
||||
|
||||
// formatKeysToImport receives a set of keys and returns a string containing the
|
||||
// question asking the user wants to import the problematic keys.
|
||||
func formatKeysToImport(logger *text.Logger, keys pgpKeySet) (string, error) {
|
||||
func formatKeysToImport(keys pgpKeySet) (string, error) {
|
||||
if len(keys) == 0 {
|
||||
return "", errors.New(gotext.Get("no keys to import"))
|
||||
}
|
||||
|
||||
var buffer bytes.Buffer
|
||||
|
||||
buffer.WriteString(logger.SprintOperationInfo(gotext.Get("PGP keys need importing:")))
|
||||
buffer.WriteString(text.SprintOperationInfo(gotext.Get("PGP keys need importing:")))
|
||||
|
||||
for key, bases := range keys {
|
||||
pkglist := ""
|
||||
for _, base := range bases {
|
||||
pkglist += base + " "
|
||||
pkglist += base.String() + " "
|
||||
}
|
||||
|
||||
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
|
266
pkg/pgp/keys_test.go
Normal file
266
pkg/pgp/keys_test.go
Normal file
@ -0,0 +1,266 @@
|
||||
package pgp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
aur "github.com/Jguer/aur"
|
||||
gosrc "github.com/Morganamilo/go-srcinfo"
|
||||
"github.com/bradleyjkemp/cupaloy"
|
||||
|
||||
"github.com/Jguer/yay/v11/pkg/dep"
|
||||
)
|
||||
|
||||
const (
|
||||
// The default port used by the PGP key server.
|
||||
gpgServerPort = 11371
|
||||
)
|
||||
|
||||
func init() {
|
||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
regex := regexp.MustCompile(`search=0[xX]([a-fA-F0-9]+)`)
|
||||
matches := regex.FindStringSubmatch(r.RequestURI)
|
||||
data := ""
|
||||
if matches != nil {
|
||||
data = getPgpKey(matches[1])
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/pgp-keys")
|
||||
_, err := w.Write([]byte(data))
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func newPkg(basename string) *aur.Pkg {
|
||||
return &aur.Pkg{Name: basename, PackageBase: basename}
|
||||
}
|
||||
|
||||
func getPgpKey(key string) string {
|
||||
var buffer bytes.Buffer
|
||||
|
||||
if contents, err := os.ReadFile(path.Join("testdata", key)); err == nil {
|
||||
buffer.WriteString("-----BEGIN PGP PUBLIC KEY BLOCK-----\n")
|
||||
buffer.WriteString("Version: SKS 1.1.6\n")
|
||||
buffer.WriteString("Comment: Hostname: yay\n\n")
|
||||
buffer.Write(contents)
|
||||
buffer.WriteString("\n-----END PGP PUBLIC KEY BLOCK-----\n")
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
func startPgpKeyServer() *http.Server {
|
||||
srv := &http.Server{Addr: fmt.Sprintf("127.0.0.1:%d", gpgServerPort), ReadHeaderTimeout: 1 * time.Second}
|
||||
|
||||
go func() {
|
||||
err := srv.ListenAndServe()
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
}
|
||||
}()
|
||||
return srv
|
||||
}
|
||||
|
||||
func TestImportKeys(t *testing.T) {
|
||||
keyringDir := t.TempDir()
|
||||
|
||||
server := startPgpKeyServer()
|
||||
defer func() {
|
||||
err := server.Shutdown(context.TODO())
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
}
|
||||
}()
|
||||
|
||||
casetests := []struct {
|
||||
keys []string
|
||||
wantError bool
|
||||
}{
|
||||
// Single key, should succeed.
|
||||
// C52048C0C0748FEE227D47A2702353E0F7E48EDB: Thomas Dickey.
|
||||
{
|
||||
keys: []string{"C52048C0C0748FEE227D47A2702353E0F7E48EDB"},
|
||||
wantError: false,
|
||||
},
|
||||
// Two keys, should succeed as well.
|
||||
// 11E521D646982372EB577A1F8F0871F202119294: Tom Stellard.
|
||||
// B6C8F98282B944E3B0D5C2530FC3042E345AD05D: Hans Wennborg.
|
||||
{
|
||||
keys: []string{
|
||||
"11E521D646982372EB577A1F8F0871F202119294",
|
||||
"B6C8F98282B944E3B0D5C2530FC3042E345AD05D",
|
||||
},
|
||||
wantError: false,
|
||||
},
|
||||
// Single invalid key, should fail.
|
||||
{
|
||||
keys: []string{"THIS-SHOULD-FAIL"},
|
||||
wantError: true,
|
||||
},
|
||||
// Two invalid keys, should fail.
|
||||
{
|
||||
keys: []string{"THIS-SHOULD-FAIL", "THIS-ONE-SHOULD-FAIL-TOO"},
|
||||
wantError: true,
|
||||
},
|
||||
// Invalid + valid key. Should fail as well.
|
||||
// 647F28654894E3BD457199BE38DBBDC86092693E: Greg Kroah-Hartman.
|
||||
{
|
||||
keys: []string{
|
||||
"THIS-SHOULD-FAIL",
|
||||
"647F28654894E3BD457199BE38DBBDC86092693E",
|
||||
},
|
||||
wantError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range casetests {
|
||||
err := importKeys(tt.keys, "gpg", fmt.Sprintf("--homedir %s --keyserver 127.0.0.1", keyringDir))
|
||||
if !tt.wantError {
|
||||
if err != nil {
|
||||
t.Fatalf("Got error %q, want no error", err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
// Here, we want to see the error.
|
||||
if err == nil {
|
||||
t.Fatalf("Got no error; want error")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func makeSrcinfo(pkgbase string, pgpkeys ...string) *gosrc.Srcinfo {
|
||||
srcinfo := gosrc.Srcinfo{}
|
||||
srcinfo.Pkgbase = pkgbase
|
||||
srcinfo.ValidPGPKeys = pgpkeys
|
||||
|
||||
return &srcinfo
|
||||
}
|
||||
|
||||
func TestCheckPgpKeys(t *testing.T) {
|
||||
keyringDir := t.TempDir()
|
||||
|
||||
server := startPgpKeyServer()
|
||||
defer func() {
|
||||
err := server.Shutdown(context.TODO())
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
}
|
||||
}()
|
||||
|
||||
casetests := []struct {
|
||||
name string
|
||||
pkgs dep.Base
|
||||
srcinfos map[string]*gosrc.Srcinfo
|
||||
wantError bool
|
||||
}{
|
||||
// cower: single package, one valid key not yet in the keyring.
|
||||
// 487EACC08557AD082088DABA1EB2638FF56C0C53: Dave Reisner.
|
||||
{
|
||||
name: " one valid key not yet in the keyring",
|
||||
pkgs: dep.Base{newPkg("cower")},
|
||||
srcinfos: map[string]*gosrc.Srcinfo{"cower": makeSrcinfo("cower", "487EACC08557AD082088DABA1EB2638FF56C0C53")},
|
||||
wantError: false,
|
||||
},
|
||||
// libc++: single package, two valid keys not yet in the keyring.
|
||||
// 11E521D646982372EB577A1F8F0871F202119294: Tom Stellard.
|
||||
// B6C8F98282B944E3B0D5C2530FC3042E345AD05D: Hans Wennborg.
|
||||
{
|
||||
name: "two valid keys not yet in the keyring",
|
||||
pkgs: dep.Base{newPkg("libc++")},
|
||||
srcinfos: map[string]*gosrc.Srcinfo{
|
||||
"libc++": makeSrcinfo("libc++", "11E521D646982372EB577A1F8F0871F202119294", "B6C8F98282B944E3B0D5C2530FC3042E345AD05D"),
|
||||
},
|
||||
wantError: false,
|
||||
},
|
||||
// Two dummy packages requiring the same key.
|
||||
// ABAF11C65A2970B130ABE3C479BE3E4300411886: Linus Torvalds.
|
||||
{
|
||||
name: "Two dummy packages requiring the same key",
|
||||
pkgs: dep.Base{newPkg("dummy-1"), newPkg("dummy-2")},
|
||||
srcinfos: map[string]*gosrc.Srcinfo{
|
||||
"dummy-1": makeSrcinfo("dummy-1",
|
||||
"ABAF11C65A2970B130ABE3C479BE3E4300411886"),
|
||||
"dummy-2": makeSrcinfo("dummy-2", "ABAF11C65A2970B130ABE3C479BE3E4300411886"),
|
||||
},
|
||||
wantError: false,
|
||||
},
|
||||
// dummy package: single package, two valid keys, one of them already
|
||||
// in the keyring.
|
||||
// 11E521D646982372EB577A1F8F0871F202119294: Tom Stellard.
|
||||
// C52048C0C0748FEE227D47A2702353E0F7E48EDB: Thomas Dickey.
|
||||
{
|
||||
name: "one already in keyring",
|
||||
pkgs: dep.Base{newPkg("dummy-3")},
|
||||
srcinfos: map[string]*gosrc.Srcinfo{
|
||||
"dummy-3": makeSrcinfo("dummy-3", "11E521D646982372EB577A1F8F0871F202119294", "C52048C0C0748FEE227D47A2702353E0F7E48EDB"),
|
||||
},
|
||||
wantError: false,
|
||||
},
|
||||
// Two dummy packages with existing keys.
|
||||
{
|
||||
name: "two existing",
|
||||
pkgs: dep.Base{newPkg("dummy-4"), newPkg("dummy-5")},
|
||||
srcinfos: map[string]*gosrc.Srcinfo{
|
||||
"dummy-4": makeSrcinfo("dummy-4", "11E521D646982372EB577A1F8F0871F202119294"),
|
||||
"dummy-5": makeSrcinfo("dummy-5", "C52048C0C0748FEE227D47A2702353E0F7E48EDB"),
|
||||
},
|
||||
wantError: false,
|
||||
},
|
||||
// Dummy package with invalid key, should fail.
|
||||
{
|
||||
name: "one invalid",
|
||||
pkgs: dep.Base{newPkg("dummy-7")},
|
||||
srcinfos: map[string]*gosrc.Srcinfo{"dummy-7": makeSrcinfo("dummy-7", "THIS-SHOULD-FAIL")},
|
||||
wantError: true,
|
||||
},
|
||||
// Dummy package with both an invalid an another valid key, should fail.
|
||||
// A314827C4E4250A204CE6E13284FC34C8E4B1A25: Thomas Bächler.
|
||||
{
|
||||
name: "one invalid, one valid",
|
||||
pkgs: dep.Base{newPkg("dummy-8")},
|
||||
srcinfos: map[string]*gosrc.Srcinfo{"dummy-8": makeSrcinfo("dummy-8", "A314827C4E4250A204CE6E13284FC34C8E4B1A25", "THIS-SHOULD-FAIL")},
|
||||
wantError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range casetests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
rescueStdout := os.Stdout
|
||||
r, w, _ := os.Pipe()
|
||||
os.Stdout = w
|
||||
|
||||
err := CheckPgpKeys([]dep.Base{tt.pkgs}, tt.srcinfos, "gpg",
|
||||
fmt.Sprintf("--homedir %s --keyserver 127.0.0.1", keyringDir), true)
|
||||
if !tt.wantError {
|
||||
if err != nil {
|
||||
t.Fatalf("Got error %q, want no error", err)
|
||||
}
|
||||
|
||||
w.Close()
|
||||
out, _ := io.ReadAll(r)
|
||||
os.Stdout = rescueStdout
|
||||
|
||||
splitLines := strings.Split(string(out), "\n")
|
||||
sort.Strings(splitLines)
|
||||
|
||||
cupaloy.SnapshotT(t, strings.Join(splitLines, "\n"))
|
||||
return
|
||||
}
|
||||
// Here, we want to see the error.
|
||||
if err == nil {
|
||||
t.Fatalf("Got no error; want error")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
100
pkg/query/aur_info.go
Normal file
100
pkg/query/aur_info.go
Normal file
@ -0,0 +1,100 @@
|
||||
package query
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"github.com/Jguer/aur"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
|
||||
"github.com/Jguer/yay/v11/pkg/intrange"
|
||||
"github.com/Jguer/yay/v11/pkg/multierror"
|
||||
"github.com/Jguer/yay/v11/pkg/text"
|
||||
)
|
||||
|
||||
type Pkg = aur.Pkg
|
||||
|
||||
// Queries the aur for information about specified packages.
|
||||
// All packages should be queried in a single aur request except when the number
|
||||
// of packages exceeds the number set in config.RequestSplitN.
|
||||
// If the number does exceed config.RequestSplitN multiple aur requests will be
|
||||
// performed concurrently.
|
||||
func AURInfo(ctx context.Context, aurClient aur.ClientInterface, names []string, warnings *AURWarnings, splitN int) ([]*Pkg, error) {
|
||||
info := make([]*Pkg, 0, len(names))
|
||||
seen := make(map[string]int)
|
||||
|
||||
var (
|
||||
mux sync.Mutex
|
||||
wg sync.WaitGroup
|
||||
errs multierror.MultiError
|
||||
)
|
||||
|
||||
makeRequest := func(n, max int) {
|
||||
defer wg.Done()
|
||||
|
||||
tempInfo, requestErr := aurClient.Info(ctx, names[n:max])
|
||||
if requestErr != nil {
|
||||
errs.Add(requestErr)
|
||||
return
|
||||
}
|
||||
|
||||
mux.Lock()
|
||||
for i := range tempInfo {
|
||||
info = append(info, &tempInfo[i])
|
||||
}
|
||||
mux.Unlock()
|
||||
}
|
||||
|
||||
for n := 0; n < len(names); n += splitN {
|
||||
max := intrange.Min(len(names), n+splitN)
|
||||
|
||||
wg.Add(1)
|
||||
|
||||
go makeRequest(n, max)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
if err := errs.Return(); err != nil {
|
||||
return info, err
|
||||
}
|
||||
|
||||
for k, pkg := range info {
|
||||
seen[pkg.Name] = k
|
||||
}
|
||||
|
||||
for _, name := range names {
|
||||
i, ok := seen[name]
|
||||
if !ok && !warnings.Ignore.Get(name) {
|
||||
warnings.Missing = append(warnings.Missing, name)
|
||||
continue
|
||||
}
|
||||
|
||||
pkg := info[i]
|
||||
|
||||
if pkg.Maintainer == "" && !warnings.Ignore.Get(name) {
|
||||
warnings.Orphans = append(warnings.Orphans, name)
|
||||
}
|
||||
|
||||
if pkg.OutOfDate != 0 && !warnings.Ignore.Get(name) {
|
||||
warnings.OutOfDate = append(warnings.OutOfDate, name)
|
||||
}
|
||||
}
|
||||
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func AURInfoPrint(ctx context.Context, aurClient aur.ClientInterface, names []string, splitN int) ([]*Pkg, error) {
|
||||
text.OperationInfoln(gotext.Get("Querying AUR..."))
|
||||
|
||||
warnings := &AURWarnings{}
|
||||
|
||||
info, err := AURInfo(ctx, aurClient, names, warnings, splitN)
|
||||
if err != nil {
|
||||
return info, err
|
||||
}
|
||||
|
||||
warnings.Print()
|
||||
|
||||
return info, nil
|
||||
}
|
@ -1,92 +1,47 @@
|
||||
package query
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/leonelquinteros/gotext"
|
||||
|
||||
"github.com/Jguer/aur"
|
||||
"github.com/Jguer/go-alpm/v2"
|
||||
|
||||
"github.com/Jguer/yay/v12/pkg/db"
|
||||
"github.com/Jguer/yay/v12/pkg/text"
|
||||
"github.com/Jguer/yay/v11/pkg/stringset"
|
||||
"github.com/Jguer/yay/v11/pkg/text"
|
||||
)
|
||||
|
||||
type AURWarnings struct {
|
||||
Orphans []string
|
||||
OutOfDate []string
|
||||
Missing []string
|
||||
LocalNewer []string
|
||||
|
||||
log *text.Logger
|
||||
Orphans []string
|
||||
OutOfDate []string
|
||||
Missing []string
|
||||
Ignore stringset.StringSet
|
||||
}
|
||||
|
||||
func NewWarnings(logger *text.Logger) *AURWarnings {
|
||||
return &AURWarnings{log: logger}
|
||||
}
|
||||
|
||||
func (warnings *AURWarnings) AddToWarnings(remote map[string]alpm.IPackage, aurPkg *aur.Pkg) {
|
||||
name := aurPkg.Name
|
||||
pkg, ok := remote[name]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
if aurPkg.Maintainer == "" && !pkg.ShouldIgnore() {
|
||||
warnings.Orphans = append(warnings.Orphans, name)
|
||||
}
|
||||
|
||||
if aurPkg.OutOfDate != 0 && !pkg.ShouldIgnore() {
|
||||
warnings.OutOfDate = append(warnings.OutOfDate, name)
|
||||
}
|
||||
|
||||
if !pkg.ShouldIgnore() && !isDevelPackage(pkg) && db.VerCmp(pkg.Version(), aurPkg.Version) > 0 {
|
||||
left, right := GetVersionDiff(pkg.Version(), aurPkg.Version)
|
||||
|
||||
newerMsg := gotext.Get("%s: local (%s) is newer than AUR (%s)",
|
||||
text.Cyan(name),
|
||||
left, right,
|
||||
)
|
||||
|
||||
warnings.LocalNewer = append(warnings.LocalNewer, newerMsg)
|
||||
}
|
||||
}
|
||||
|
||||
func (warnings *AURWarnings) CalculateMissing(remoteNames []string,
|
||||
remote map[string]alpm.IPackage, aurData map[string]*aur.Pkg,
|
||||
) {
|
||||
for _, name := range remoteNames {
|
||||
if _, ok := aurData[name]; !ok && !remote[name].ShouldIgnore() {
|
||||
if _, ok := aurData[strings.TrimSuffix(name, "-debug")]; !ok {
|
||||
warnings.Missing = append(warnings.Missing, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
func NewWarnings() *AURWarnings {
|
||||
return &AURWarnings{Ignore: make(stringset.StringSet)}
|
||||
}
|
||||
|
||||
func (warnings *AURWarnings) Print() {
|
||||
normalMissing, debugMissing := filterDebugPkgs(warnings.Missing)
|
||||
|
||||
if len(normalMissing) > 0 {
|
||||
warnings.log.Warnln(gotext.Get("Packages not in AUR:"), formatNames(normalMissing))
|
||||
text.Warn(gotext.Get("Missing AUR Packages:"))
|
||||
printRange(normalMissing)
|
||||
}
|
||||
|
||||
if len(debugMissing) > 0 {
|
||||
warnings.log.Warnln(gotext.Get("Missing AUR Debug Packages:"), formatNames(debugMissing))
|
||||
text.Warn(gotext.Get("Missing AUR Debug Packages:"))
|
||||
printRange(debugMissing)
|
||||
}
|
||||
|
||||
if len(warnings.Orphans) > 0 {
|
||||
warnings.log.Warnln(gotext.Get("Orphan (unmaintained) AUR Packages:"), formatNames(warnings.Orphans))
|
||||
text.Warn(gotext.Get("Orphaned AUR Packages:"))
|
||||
printRange(warnings.Orphans)
|
||||
}
|
||||
|
||||
if len(warnings.OutOfDate) > 0 {
|
||||
warnings.log.Warnln(gotext.Get("Flagged Out Of Date AUR Packages:"), formatNames(warnings.OutOfDate))
|
||||
}
|
||||
|
||||
if len(warnings.LocalNewer) > 0 {
|
||||
for _, newer := range warnings.LocalNewer {
|
||||
warnings.log.Warnln(newer)
|
||||
}
|
||||
text.Warn(gotext.Get("Flagged Out Of Date AUR Packages:"))
|
||||
printRange(warnings.OutOfDate)
|
||||
}
|
||||
}
|
||||
|
||||
@ -105,6 +60,10 @@ func filterDebugPkgs(names []string) (normal, debug []string) {
|
||||
return
|
||||
}
|
||||
|
||||
func formatNames(names []string) string {
|
||||
return " " + text.Cyan(strings.Join(names, " "))
|
||||
func printRange(names []string) {
|
||||
for _, name := range names {
|
||||
fmt.Print(" " + text.Cyan(name))
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
}
|
||||
|
@ -3,23 +3,53 @@ package query
|
||||
import (
|
||||
"github.com/leonelquinteros/gotext"
|
||||
|
||||
"github.com/Jguer/yay/v12/pkg/settings/parser"
|
||||
"github.com/Jguer/yay/v12/pkg/text"
|
||||
"github.com/Jguer/yay/v11/pkg/db"
|
||||
"github.com/Jguer/yay/v11/pkg/settings/parser"
|
||||
"github.com/Jguer/yay/v11/pkg/text"
|
||||
)
|
||||
|
||||
func RemoveInvalidTargets(logger *text.Logger, targets []string, mode parser.TargetMode) []string {
|
||||
// GetPackageNamesBySource returns package names with and without correspondence in SyncDBS respectively.
|
||||
func GetPackageNamesBySource(dbExecutor db.Executor) (local, remote []string, err error) {
|
||||
for _, localpkg := range dbExecutor.LocalPackages() {
|
||||
pkgName := localpkg.Name()
|
||||
if dbExecutor.SyncPackage(pkgName) != nil {
|
||||
local = append(local, pkgName)
|
||||
} else {
|
||||
remote = append(remote, pkgName)
|
||||
}
|
||||
}
|
||||
|
||||
return local, remote, err
|
||||
}
|
||||
|
||||
// GetRemotePackages returns packages with no correspondence in SyncDBS.
|
||||
func GetRemotePackages(dbExecutor db.Executor) (
|
||||
remote []db.IPackage,
|
||||
remoteNames []string) {
|
||||
for _, localpkg := range dbExecutor.LocalPackages() {
|
||||
pkgName := localpkg.Name()
|
||||
if dbExecutor.SyncPackage(pkgName) == nil {
|
||||
remote = append(remote, localpkg)
|
||||
remoteNames = append(remoteNames, pkgName)
|
||||
}
|
||||
}
|
||||
|
||||
return remote, remoteNames
|
||||
}
|
||||
|
||||
func RemoveInvalidTargets(targets []string, mode parser.TargetMode) []string {
|
||||
filteredTargets := make([]string, 0)
|
||||
|
||||
for _, target := range targets {
|
||||
dbName, _ := text.SplitDBFromName(target)
|
||||
|
||||
if dbName == "aur" && !mode.AtLeastAUR() {
|
||||
logger.Warnln(gotext.Get("%s: can't use target with option --repo -- skipping", text.Cyan(target)))
|
||||
text.Warnln(gotext.Get("%s: can't use target with option --repo -- skipping", text.Cyan(target)))
|
||||
continue
|
||||
}
|
||||
|
||||
if dbName != "aur" && dbName != "" && !mode.AtLeastRepo() {
|
||||
logger.Warnln(gotext.Get("%s: can't use target with option --aur -- skipping", text.Cyan(target)))
|
||||
text.Warnln(gotext.Get("%s: can't use target with option --aur -- skipping", text.Cyan(target)))
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -1,89 +0,0 @@
|
||||
package query
|
||||
|
||||
import (
|
||||
"hash/fnv"
|
||||
"strings"
|
||||
|
||||
"github.com/adrg/strutil"
|
||||
)
|
||||
|
||||
const minVotes = 30
|
||||
|
||||
// TODO: Add support for Popularity and LastModified
|
||||
func (a *abstractResults) aurSortByMetric(pkg *abstractResult) float64 {
|
||||
return 1 - (minVotes / (minVotes + float64(pkg.votes)))
|
||||
}
|
||||
|
||||
func (a *abstractResults) GetMetric(pkg *abstractResult) float64 {
|
||||
if v, ok := a.distanceCache[pkg.name]; ok {
|
||||
return v
|
||||
}
|
||||
|
||||
if strings.EqualFold(pkg.name, a.search) {
|
||||
return 1.0
|
||||
}
|
||||
|
||||
sim := strutil.Similarity(pkg.name, a.search, a.metric)
|
||||
|
||||
for _, prov := range pkg.provides {
|
||||
// If the package provides search, it's a perfect match
|
||||
// AUR packages don't populate provides
|
||||
candidate := strutil.Similarity(prov, a.search, a.metric) * 0.80
|
||||
if candidate > sim {
|
||||
sim = candidate
|
||||
}
|
||||
}
|
||||
|
||||
simDesc := strutil.Similarity(pkg.description, a.search, a.metric)
|
||||
|
||||
// slightly overweight sync sources by always giving them max popularity
|
||||
popularity := 1.0
|
||||
if pkg.source == sourceAUR {
|
||||
popularity = a.aurSortByMetric(pkg)
|
||||
}
|
||||
|
||||
sim = sim*0.5 + simDesc*0.2 + popularity*0.3
|
||||
|
||||
a.distanceCache[pkg.name] = sim
|
||||
|
||||
return sim
|
||||
}
|
||||
|
||||
func (a *abstractResults) separateSourceScore(source string, score float64) float64 {
|
||||
if !a.separateSources {
|
||||
return 0
|
||||
}
|
||||
|
||||
if score == 1.0 {
|
||||
return 50
|
||||
}
|
||||
|
||||
switch source {
|
||||
case sourceAUR:
|
||||
return 0
|
||||
case "core":
|
||||
return 40
|
||||
case "extra":
|
||||
return 30
|
||||
case "community":
|
||||
return 20
|
||||
case "multilib":
|
||||
return 10
|
||||
}
|
||||
|
||||
if v, ok := a.separateSourceCache[source]; ok {
|
||||
return v
|
||||
}
|
||||
|
||||
h := fnv.New32a()
|
||||
h.Write([]byte(source))
|
||||
sourceScore := float64(int(h.Sum32())%9 + 2)
|
||||
a.separateSourceCache[source] = sourceScore
|
||||
|
||||
return sourceScore
|
||||
}
|
||||
|
||||
func (a *abstractResults) calculateMetric(pkg *abstractResult) float64 {
|
||||
score := a.GetMetric(pkg)
|
||||
return a.separateSourceScore(pkg.source, score) + score
|
||||
}
|
@ -2,43 +2,35 @@ package query
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/Jguer/aur"
|
||||
"github.com/Jguer/go-alpm/v2"
|
||||
"github.com/adrg/strutil"
|
||||
"github.com/adrg/strutil/metrics"
|
||||
mapset "github.com/deckarep/golang-set/v2"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
|
||||
"github.com/Jguer/yay/v12/pkg/db"
|
||||
"github.com/Jguer/yay/v12/pkg/intrange"
|
||||
"github.com/Jguer/yay/v12/pkg/settings/parser"
|
||||
"github.com/Jguer/yay/v12/pkg/text"
|
||||
"github.com/Jguer/yay/v11/pkg/db"
|
||||
"github.com/Jguer/yay/v11/pkg/intrange"
|
||||
"github.com/Jguer/yay/v11/pkg/settings/parser"
|
||||
"github.com/Jguer/yay/v11/pkg/stringset"
|
||||
"github.com/Jguer/yay/v11/pkg/text"
|
||||
)
|
||||
|
||||
const sourceAUR = "aur"
|
||||
|
||||
type SearchVerbosity int
|
||||
|
||||
// Verbosity settings for search.
|
||||
const (
|
||||
NumberMenu SearchVerbosity = iota
|
||||
Detailed
|
||||
Minimal
|
||||
)
|
||||
|
||||
type Builder interface {
|
||||
Len() int
|
||||
Execute(ctx context.Context, dbExecutor db.Executor, pkgS []string)
|
||||
Results(dbExecutor db.Executor, verboseSearch SearchVerbosity) error
|
||||
GetTargets(include, exclude intrange.IntRanges, otherExclude mapset.Set[string]) ([]string, error)
|
||||
Execute(ctx context.Context, dbExecutor db.Executor, aurClient aur.ClientInterface, pkgS []string)
|
||||
Results(w io.Writer, dbExecutor db.Executor, verboseSearch SearchVerbosity) error
|
||||
GetTargets(include, exclude intrange.IntRanges, otherExclude stringset.StringSet) ([]string, error)
|
||||
}
|
||||
|
||||
type SourceQueryBuilder struct {
|
||||
type MixedSourceQueryBuilder struct {
|
||||
results []abstractResult
|
||||
sortBy string
|
||||
searchBy string
|
||||
@ -46,31 +38,21 @@ type SourceQueryBuilder struct {
|
||||
queryMap map[string]map[string]interface{}
|
||||
bottomUp bool
|
||||
singleLineResults bool
|
||||
separateSources bool
|
||||
|
||||
aurClient aur.QueryClient
|
||||
logger *text.Logger
|
||||
}
|
||||
|
||||
func NewSourceQueryBuilder(
|
||||
aurClient aur.QueryClient,
|
||||
logger *text.Logger,
|
||||
func NewMixedSourceQueryBuilder(
|
||||
sortBy string,
|
||||
targetMode parser.TargetMode,
|
||||
searchBy string,
|
||||
bottomUp,
|
||||
singleLineResults bool,
|
||||
separateSources bool,
|
||||
) *SourceQueryBuilder {
|
||||
return &SourceQueryBuilder{
|
||||
aurClient: aurClient,
|
||||
logger: logger,
|
||||
) *MixedSourceQueryBuilder {
|
||||
return &MixedSourceQueryBuilder{
|
||||
bottomUp: bottomUp,
|
||||
sortBy: sortBy,
|
||||
targetMode: targetMode,
|
||||
searchBy: searchBy,
|
||||
singleLineResults: singleLineResults,
|
||||
separateSources: separateSources,
|
||||
queryMap: map[string]map[string]interface{}{},
|
||||
results: make([]abstractResult, 0, 100),
|
||||
}
|
||||
@ -85,71 +67,81 @@ type abstractResult struct {
|
||||
}
|
||||
|
||||
type abstractResults struct {
|
||||
results []abstractResult
|
||||
search string
|
||||
bottomUp bool
|
||||
metric strutil.StringMetric
|
||||
separateSources bool
|
||||
sortBy string
|
||||
|
||||
distanceCache map[string]float64
|
||||
separateSourceCache map[string]float64
|
||||
results []abstractResult
|
||||
search string
|
||||
distanceCache map[string]float64
|
||||
bottomUp bool
|
||||
metric strutil.StringMetric
|
||||
}
|
||||
|
||||
func (a *abstractResults) Len() int { return len(a.results) }
|
||||
func (a *abstractResults) Swap(i, j int) { a.results[i], a.results[j] = a.results[j], a.results[i] }
|
||||
|
||||
func (a *abstractResults) GetMetric(pkg *abstractResult) float64 {
|
||||
if v, ok := a.distanceCache[pkg.name]; ok {
|
||||
return v
|
||||
}
|
||||
|
||||
sim := strutil.Similarity(pkg.name, a.search, a.metric)
|
||||
|
||||
for _, prov := range pkg.provides {
|
||||
// If the package provides search, it's a perfect match
|
||||
// AUR packages don't populate provides
|
||||
candidate := strutil.Similarity(prov, a.search, a.metric)
|
||||
if candidate > sim {
|
||||
sim = candidate
|
||||
}
|
||||
}
|
||||
|
||||
simDesc := strutil.Similarity(pkg.description, a.search, a.metric)
|
||||
|
||||
// slightly overweight sync sources by always giving them max popularity
|
||||
popularity := 1.0
|
||||
if pkg.source == sourceAUR {
|
||||
popularity = float64(pkg.votes) / float64(pkg.votes+60)
|
||||
}
|
||||
|
||||
sim = sim*0.6 + simDesc*0.2 + popularity*0.2
|
||||
|
||||
a.distanceCache[pkg.name] = sim
|
||||
|
||||
return sim
|
||||
}
|
||||
|
||||
func (a *abstractResults) Less(i, j int) bool {
|
||||
pkgA := a.results[i]
|
||||
pkgB := a.results[j]
|
||||
|
||||
var cmpResult bool
|
||||
|
||||
switch a.sortBy {
|
||||
case "name":
|
||||
cmpResult = !text.LessRunes([]rune(pkgA.name), []rune(pkgB.name))
|
||||
if a.separateSources {
|
||||
cmpSources := strings.Compare(pkgA.source, pkgB.source)
|
||||
if cmpSources != 0 {
|
||||
cmpResult = cmpSources > 0
|
||||
}
|
||||
}
|
||||
default:
|
||||
simA := a.calculateMetric(&pkgA)
|
||||
simB := a.calculateMetric(&pkgB)
|
||||
cmpResult = simA > simB
|
||||
}
|
||||
simA := a.GetMetric(&pkgA)
|
||||
simB := a.GetMetric(&pkgB)
|
||||
|
||||
if a.bottomUp {
|
||||
cmpResult = !cmpResult
|
||||
return simA < simB
|
||||
}
|
||||
|
||||
return cmpResult
|
||||
return simA > simB
|
||||
}
|
||||
|
||||
func (s *SourceQueryBuilder) Execute(ctx context.Context, dbExecutor db.Executor, pkgS []string) {
|
||||
func (s *MixedSourceQueryBuilder) Execute(ctx context.Context, dbExecutor db.Executor, aurClient aur.ClientInterface, pkgS []string) {
|
||||
var aurErr error
|
||||
|
||||
pkgS = RemoveInvalidTargets(s.logger, pkgS, s.targetMode)
|
||||
pkgS = RemoveInvalidTargets(pkgS, s.targetMode)
|
||||
|
||||
metric := &metrics.Hamming{
|
||||
metric := &metrics.JaroWinkler{
|
||||
CaseSensitive: false,
|
||||
}
|
||||
|
||||
sortableResults := &abstractResults{
|
||||
results: []abstractResult{},
|
||||
search: strings.Join(pkgS, ""),
|
||||
bottomUp: s.bottomUp,
|
||||
metric: metric,
|
||||
separateSources: s.separateSources,
|
||||
sortBy: s.sortBy,
|
||||
distanceCache: map[string]float64{},
|
||||
separateSourceCache: map[string]float64{},
|
||||
results: []abstractResult{},
|
||||
search: strings.Join(pkgS, ""),
|
||||
distanceCache: map[string]float64{},
|
||||
bottomUp: s.bottomUp,
|
||||
metric: metric,
|
||||
}
|
||||
|
||||
if s.targetMode.AtLeastAUR() {
|
||||
var aurResults []aur.Pkg
|
||||
aurResults, aurErr = queryAUR(ctx, s.aurClient, pkgS, s.searchBy)
|
||||
var aurResults aurQuery
|
||||
aurResults, aurErr = queryAUR(ctx, aurClient, pkgS, s.searchBy)
|
||||
dbName := sourceAUR
|
||||
|
||||
for i := range aurResults {
|
||||
@ -157,12 +149,6 @@ func (s *SourceQueryBuilder) Execute(ctx context.Context, dbExecutor db.Executor
|
||||
s.queryMap[dbName] = map[string]interface{}{}
|
||||
}
|
||||
|
||||
by := getSearchBy(s.searchBy)
|
||||
if (by == aur.NameDesc || by == aur.None || by == aur.Name) &&
|
||||
!matchesSearch(&aurResults[i], pkgS) {
|
||||
continue
|
||||
}
|
||||
|
||||
s.queryMap[dbName][aurResults[i].Name] = aurResults[i]
|
||||
|
||||
sortableResults.results = append(sortableResults.results, abstractResult{
|
||||
@ -208,18 +194,18 @@ func (s *SourceQueryBuilder) Execute(ctx context.Context, dbExecutor db.Executor
|
||||
s.results = sortableResults.results
|
||||
|
||||
if aurErr != nil {
|
||||
s.logger.Errorln(ErrAURSearch{inner: aurErr})
|
||||
text.Errorln(ErrAURSearch{inner: aurErr})
|
||||
|
||||
if len(repoResults) != 0 {
|
||||
s.logger.Warnln(gotext.Get("Showing repo packages only"))
|
||||
text.Warnln(gotext.Get("Showing repo packages only"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SourceQueryBuilder) Results(dbExecutor db.Executor, verboseSearch SearchVerbosity) error {
|
||||
func (s *MixedSourceQueryBuilder) Results(w io.Writer, dbExecutor db.Executor, verboseSearch SearchVerbosity) error {
|
||||
for i := range s.results {
|
||||
if verboseSearch == Minimal {
|
||||
s.logger.Println(s.results[i].name)
|
||||
_, _ = fmt.Fprintln(w, s.results[i].name)
|
||||
continue
|
||||
}
|
||||
|
||||
@ -234,34 +220,34 @@ func (s *SourceQueryBuilder) Results(dbExecutor db.Executor, verboseSearch Searc
|
||||
}
|
||||
|
||||
pkg := s.queryMap[s.results[i].source][s.results[i].name]
|
||||
|
||||
switch pPkg := pkg.(type) {
|
||||
case aur.Pkg:
|
||||
toPrint += aurPkgSearchString(&pPkg, dbExecutor, s.singleLineResults)
|
||||
case alpm.IPackage:
|
||||
toPrint += syncPkgSearchString(pPkg, dbExecutor, s.singleLineResults)
|
||||
if s.results[i].source == sourceAUR {
|
||||
aurPkg := pkg.(aur.Pkg)
|
||||
toPrint += aurPkgSearchString(&aurPkg, dbExecutor, s.singleLineResults)
|
||||
} else {
|
||||
syncPkg := pkg.(alpm.IPackage)
|
||||
toPrint += syncPkgSearchString(syncPkg, dbExecutor, s.singleLineResults)
|
||||
}
|
||||
|
||||
s.logger.Println(toPrint)
|
||||
fmt.Fprintln(w, toPrint)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SourceQueryBuilder) Len() int {
|
||||
func (s *MixedSourceQueryBuilder) Len() int {
|
||||
return len(s.results)
|
||||
}
|
||||
|
||||
func (s *SourceQueryBuilder) GetTargets(include, exclude intrange.IntRanges,
|
||||
otherExclude mapset.Set[string],
|
||||
func (s *MixedSourceQueryBuilder) GetTargets(include, exclude intrange.IntRanges,
|
||||
otherExclude stringset.StringSet,
|
||||
) ([]string, error) {
|
||||
var (
|
||||
isInclude = len(exclude) == 0 && otherExclude.Cardinality() == 0
|
||||
isInclude = len(exclude) == 0 && len(otherExclude) == 0
|
||||
targets []string
|
||||
lenRes = len(s.results)
|
||||
)
|
||||
|
||||
for i := 1; i <= s.Len(); i++ {
|
||||
for i := 0; i <= s.Len(); i++ {
|
||||
target := i - 1
|
||||
if s.bottomUp {
|
||||
target = lenRes - i
|
||||
@ -274,25 +260,3 @@ func (s *SourceQueryBuilder) GetTargets(include, exclude intrange.IntRanges,
|
||||
|
||||
return targets, nil
|
||||
}
|
||||
|
||||
func matchesSearch(pkg *aur.Pkg, terms []string) bool {
|
||||
if len(terms) <= 1 {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, pkgN := range terms {
|
||||
if strings.IndexFunc(pkgN, unicode.IsSymbol) != -1 {
|
||||
return true
|
||||
}
|
||||
|
||||
name := strings.ToLower(pkg.Name)
|
||||
desc := strings.ToLower(pkg.Description)
|
||||
targ := strings.ToLower(pkgN)
|
||||
|
||||
if !strings.Contains(name, targ) && !strings.Contains(desc, targ) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
61
pkg/query/mixed_sources_test.go
Normal file
61
pkg/query/mixed_sources_test.go
Normal file
@ -0,0 +1,61 @@
|
||||
package query
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/Jguer/yay/v11/pkg/settings/parser"
|
||||
|
||||
"github.com/Jguer/aur"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMixedSourceQueryBuilder(t *testing.T) {
|
||||
t.Parallel()
|
||||
type testCase struct {
|
||||
desc string
|
||||
bottomUp bool
|
||||
want string
|
||||
}
|
||||
|
||||
testCases := []testCase{
|
||||
{desc: "bottomup", bottomUp: true, want: "\x1b[1m\x1b[34maur\x1b[0m\x1b[0m/\x1b[1mlinux-ck\x1b[0m \x1b[36m5.16.12-1\x1b[0m\x1b[1m (+450\x1b[0m \x1b[1m1.51) \x1b[0m\n The Linux-ck kernel and modules with ck's hrtimer patches\n\x1b[1m\x1b[33mcore\x1b[0m\x1b[0m/\x1b[1mlinux-zen\x1b[0m \x1b[36m5.16.0\x1b[0m\x1b[1m (1.0 B 1.0 B) \x1b[0m\n The Linux ZEN kernel and modules\n\x1b[1m\x1b[33mcore\x1b[0m\x1b[0m/\x1b[1mlinux\x1b[0m \x1b[36m5.16.0\x1b[0m\x1b[1m (1.0 B 1.0 B) \x1b[0m\n The Linux kernel and modules\n"},
|
||||
{
|
||||
desc: "topdown", bottomUp: false,
|
||||
want: "\x1b[1m\x1b[33mcore\x1b[0m\x1b[0m/\x1b[1mlinux\x1b[0m \x1b[36m5.16.0\x1b[0m\x1b[1m (1.0 B 1.0 B) \x1b[0m\n The Linux kernel and modules\n\x1b[1m\x1b[33mcore\x1b[0m\x1b[0m/\x1b[1mlinux-zen\x1b[0m \x1b[36m5.16.0\x1b[0m\x1b[1m (1.0 B 1.0 B) \x1b[0m\n The Linux ZEN kernel and modules\n\x1b[1m\x1b[34maur\x1b[0m\x1b[0m/\x1b[1mlinux-ck\x1b[0m \x1b[36m5.16.12-1\x1b[0m\x1b[1m (+450\x1b[0m \x1b[1m1.51) \x1b[0m\n The Linux-ck kernel and modules with ck's hrtimer patches\n",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
queryBuilder := NewMixedSourceQueryBuilder("votes", parser.ModeAny, "", tc.bottomUp, false)
|
||||
search := []string{"linux"}
|
||||
mockStore := &mockDB{}
|
||||
|
||||
client, err := aur.NewClient(aur.WithHTTPClient(&mockDoer{}))
|
||||
require.NoError(t, err)
|
||||
queryBuilder.Execute(context.Background(), mockStore, client, search)
|
||||
assert.Len(t, queryBuilder.results, 3)
|
||||
assert.Equal(t, 3, queryBuilder.Len())
|
||||
|
||||
if tc.bottomUp {
|
||||
assert.Equal(t, "linux-ck", queryBuilder.results[0].name)
|
||||
assert.Equal(t, "linux-zen", queryBuilder.results[1].name)
|
||||
assert.Equal(t, "linux", queryBuilder.results[2].name)
|
||||
} else {
|
||||
assert.Equal(t, "linux-ck", queryBuilder.results[2].name)
|
||||
assert.Equal(t, "linux-zen", queryBuilder.results[1].name)
|
||||
assert.Equal(t, "linux", queryBuilder.results[0].name)
|
||||
}
|
||||
|
||||
w := &strings.Builder{}
|
||||
queryBuilder.Results(w, mockStore, Detailed)
|
||||
|
||||
wString := w.String()
|
||||
require.GreaterOrEqual(t, len(wString), 1)
|
||||
assert.Equal(t, tc.want, wString)
|
||||
})
|
||||
}
|
||||
}
|
@ -1,362 +0,0 @@
|
||||
//go:build !integration
|
||||
// +build !integration
|
||||
|
||||
package query
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/Jguer/aur"
|
||||
|
||||
"github.com/Jguer/yay/v12/pkg/db/mock"
|
||||
mockaur "github.com/Jguer/yay/v12/pkg/dep/mock"
|
||||
"github.com/Jguer/yay/v12/pkg/settings/parser"
|
||||
"github.com/Jguer/yay/v12/pkg/text"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestSourceQueryBuilder(t *testing.T) {
|
||||
t.Parallel()
|
||||
type testCase struct {
|
||||
desc string
|
||||
search []string
|
||||
bottomUp bool
|
||||
separateSources bool
|
||||
sortBy string
|
||||
verbosity SearchVerbosity
|
||||
targetMode parser.TargetMode
|
||||
singleLineResults bool
|
||||
searchBy string
|
||||
wantResults []string
|
||||
wantOutput []string
|
||||
}
|
||||
|
||||
testCases := []testCase{
|
||||
{
|
||||
desc: "sort-by-votes bottomup separatesources",
|
||||
search: []string{"linux"},
|
||||
bottomUp: true,
|
||||
separateSources: true,
|
||||
sortBy: "votes",
|
||||
verbosity: Detailed,
|
||||
wantResults: []string{"linux-ck", "linux-zen", "linux"},
|
||||
wantOutput: []string{
|
||||
"\x1b[1m\x1b[34maur\x1b[0m\x1b[0m/\x1b[1mlinux-ck\x1b[0m \x1b[36m5.16.12-1\x1b[0m\x1b[1m (+450\x1b[0m \x1b[1m1.51) \x1b[0m\n The Linux-ck kernel and modules with ck's hrtimer patches\n",
|
||||
"\x1b[1m\x1b[33mcore\x1b[0m\x1b[0m/\x1b[1mlinux-zen\x1b[0m \x1b[36m5.16.0\x1b[0m\x1b[1m (1.0 B 1.0 B) \x1b[0m\n The Linux ZEN kernel and modules\n",
|
||||
"\x1b[1m\x1b[33mcore\x1b[0m\x1b[0m/\x1b[1mlinux\x1b[0m \x1b[36m5.16.0\x1b[0m\x1b[1m (1.0 B 1.0 B) \x1b[0m\n The Linux kernel and modules\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "sort-by-votes topdown separatesources",
|
||||
search: []string{"linux"},
|
||||
bottomUp: false,
|
||||
separateSources: true,
|
||||
sortBy: "votes",
|
||||
verbosity: Detailed,
|
||||
wantResults: []string{"linux", "linux-zen", "linux-ck"},
|
||||
wantOutput: []string{
|
||||
"\x1b[1m\x1b[33mcore\x1b[0m\x1b[0m/\x1b[1mlinux\x1b[0m \x1b[36m5.16.0\x1b[0m\x1b[1m (1.0 B 1.0 B) \x1b[0m\n The Linux kernel and modules\n",
|
||||
"\x1b[1m\x1b[33mcore\x1b[0m\x1b[0m/\x1b[1mlinux-zen\x1b[0m \x1b[36m5.16.0\x1b[0m\x1b[1m (1.0 B 1.0 B) \x1b[0m\n The Linux ZEN kernel and modules\n",
|
||||
"\x1b[1m\x1b[34maur\x1b[0m\x1b[0m/\x1b[1mlinux-ck\x1b[0m \x1b[36m5.16.12-1\x1b[0m\x1b[1m (+450\x1b[0m \x1b[1m1.51) \x1b[0m\n The Linux-ck kernel and modules with ck's hrtimer patches\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "sort-by-votes bottomup noseparatesources",
|
||||
search: []string{"linux"},
|
||||
bottomUp: true,
|
||||
separateSources: false,
|
||||
sortBy: "votes",
|
||||
verbosity: Detailed,
|
||||
wantResults: []string{"linux-zen", "linux-ck", "linux"},
|
||||
wantOutput: []string{
|
||||
"\x1b[1m\x1b[33mcore\x1b[0m\x1b[0m/\x1b[1mlinux-zen\x1b[0m \x1b[36m5.16.0\x1b[0m\x1b[1m (1.0 B 1.0 B) \x1b[0m\n The Linux ZEN kernel and modules\n",
|
||||
"\x1b[1m\x1b[34maur\x1b[0m\x1b[0m/\x1b[1mlinux-ck\x1b[0m \x1b[36m5.16.12-1\x1b[0m\x1b[1m (+450\x1b[0m \x1b[1m1.51) \x1b[0m\n The Linux-ck kernel and modules with ck's hrtimer patches\n",
|
||||
"\x1b[1m\x1b[33mcore\x1b[0m\x1b[0m/\x1b[1mlinux\x1b[0m \x1b[36m5.16.0\x1b[0m\x1b[1m (1.0 B 1.0 B) \x1b[0m\n The Linux kernel and modules\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "sort-by-votes topdown noseparatesources",
|
||||
search: []string{"linux"},
|
||||
bottomUp: false,
|
||||
separateSources: false,
|
||||
sortBy: "votes",
|
||||
verbosity: Detailed,
|
||||
wantResults: []string{"linux", "linux-ck", "linux-zen"},
|
||||
wantOutput: []string{
|
||||
"\x1b[1m\x1b[33mcore\x1b[0m\x1b[0m/\x1b[1mlinux\x1b[0m \x1b[36m5.16.0\x1b[0m\x1b[1m (1.0 B 1.0 B) \x1b[0m\n The Linux kernel and modules\n",
|
||||
"\x1b[1m\x1b[34maur\x1b[0m\x1b[0m/\x1b[1mlinux-ck\x1b[0m \x1b[36m5.16.12-1\x1b[0m\x1b[1m (+450\x1b[0m \x1b[1m1.51) \x1b[0m\n The Linux-ck kernel and modules with ck's hrtimer patches\n",
|
||||
"\x1b[1m\x1b[33mcore\x1b[0m\x1b[0m/\x1b[1mlinux-zen\x1b[0m \x1b[36m5.16.0\x1b[0m\x1b[1m (1.0 B 1.0 B) \x1b[0m\n The Linux ZEN kernel and modules\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "sort-by-name bottomup separatesources",
|
||||
search: []string{"linux"},
|
||||
bottomUp: true,
|
||||
separateSources: true,
|
||||
sortBy: "name",
|
||||
verbosity: Detailed,
|
||||
wantResults: []string{"linux-ck", "linux", "linux-zen"},
|
||||
wantOutput: []string{
|
||||
"\x1b[1m\x1b[34maur\x1b[0m\x1b[0m/\x1b[1mlinux-ck\x1b[0m \x1b[36m5.16.12-1\x1b[0m\x1b[1m (+450\x1b[0m \x1b[1m1.51) \x1b[0m\n The Linux-ck kernel and modules with ck's hrtimer patches\n",
|
||||
"\x1b[1m\x1b[33mcore\x1b[0m\x1b[0m/\x1b[1mlinux\x1b[0m \x1b[36m5.16.0\x1b[0m\x1b[1m (1.0 B 1.0 B) \x1b[0m\n The Linux kernel and modules\n",
|
||||
"\x1b[1m\x1b[33mcore\x1b[0m\x1b[0m/\x1b[1mlinux-zen\x1b[0m \x1b[36m5.16.0\x1b[0m\x1b[1m (1.0 B 1.0 B) \x1b[0m\n The Linux ZEN kernel and modules\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "sort-by-name topdown separatesources",
|
||||
search: []string{"linux"},
|
||||
bottomUp: false,
|
||||
separateSources: true,
|
||||
sortBy: "name",
|
||||
verbosity: Detailed,
|
||||
wantResults: []string{"linux-zen", "linux", "linux-ck"},
|
||||
wantOutput: []string{
|
||||
"\x1b[1m\x1b[33mcore\x1b[0m\x1b[0m/\x1b[1mlinux-zen\x1b[0m \x1b[36m5.16.0\x1b[0m\x1b[1m (1.0 B 1.0 B) \x1b[0m\n The Linux ZEN kernel and modules\n",
|
||||
"\x1b[1m\x1b[33mcore\x1b[0m\x1b[0m/\x1b[1mlinux\x1b[0m \x1b[36m5.16.0\x1b[0m\x1b[1m (1.0 B 1.0 B) \x1b[0m\n The Linux kernel and modules\n",
|
||||
"\x1b[1m\x1b[34maur\x1b[0m\x1b[0m/\x1b[1mlinux-ck\x1b[0m \x1b[36m5.16.12-1\x1b[0m\x1b[1m (+450\x1b[0m \x1b[1m1.51) \x1b[0m\n The Linux-ck kernel and modules with ck's hrtimer patches\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "sort-by-name bottomup noseparatesources",
|
||||
search: []string{"linux"},
|
||||
bottomUp: true,
|
||||
separateSources: false,
|
||||
sortBy: "name",
|
||||
verbosity: Detailed,
|
||||
wantResults: []string{"linux", "linux-ck", "linux-zen"},
|
||||
wantOutput: []string{
|
||||
"\x1b[1m\x1b[33mcore\x1b[0m\x1b[0m/\x1b[1mlinux\x1b[0m \x1b[36m5.16.0\x1b[0m\x1b[1m (1.0 B 1.0 B) \x1b[0m\n The Linux kernel and modules\n",
|
||||
"\x1b[1m\x1b[34maur\x1b[0m\x1b[0m/\x1b[1mlinux-ck\x1b[0m \x1b[36m5.16.12-1\x1b[0m\x1b[1m (+450\x1b[0m \x1b[1m1.51) \x1b[0m\n The Linux-ck kernel and modules with ck's hrtimer patches\n",
|
||||
"\x1b[1m\x1b[33mcore\x1b[0m\x1b[0m/\x1b[1mlinux-zen\x1b[0m \x1b[36m5.16.0\x1b[0m\x1b[1m (1.0 B 1.0 B) \x1b[0m\n The Linux ZEN kernel and modules\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "sort-by-name topdown noseparatesources",
|
||||
search: []string{"linux"},
|
||||
bottomUp: false,
|
||||
separateSources: false,
|
||||
sortBy: "name",
|
||||
verbosity: Detailed,
|
||||
wantResults: []string{"linux-zen", "linux-ck", "linux"},
|
||||
wantOutput: []string{
|
||||
"\x1b[1m\x1b[33mcore\x1b[0m\x1b[0m/\x1b[1mlinux-zen\x1b[0m \x1b[36m5.16.0\x1b[0m\x1b[1m (1.0 B 1.0 B) \x1b[0m\n The Linux ZEN kernel and modules\n",
|
||||
"\x1b[1m\x1b[34maur\x1b[0m\x1b[0m/\x1b[1mlinux-ck\x1b[0m \x1b[36m5.16.12-1\x1b[0m\x1b[1m (+450\x1b[0m \x1b[1m1.51) \x1b[0m\n The Linux-ck kernel and modules with ck's hrtimer patches\n",
|
||||
"\x1b[1m\x1b[33mcore\x1b[0m\x1b[0m/\x1b[1mlinux\x1b[0m \x1b[36m5.16.0\x1b[0m\x1b[1m (1.0 B 1.0 B) \x1b[0m\n The Linux kernel and modules\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "sort-by-votes bottomup separatesources number-menu",
|
||||
search: []string{"linux"},
|
||||
bottomUp: true,
|
||||
separateSources: true,
|
||||
sortBy: "votes",
|
||||
verbosity: NumberMenu,
|
||||
wantResults: []string{"linux-ck", "linux-zen", "linux"},
|
||||
wantOutput: []string{
|
||||
"\x1b[35m3\x1b[0m \x1b[1m\x1b[34maur\x1b[0m\x1b[0m/\x1b[1mlinux-ck\x1b[0m \x1b[36m5.16.12-1\x1b[0m\x1b[1m (+450\x1b[0m \x1b[1m1.51) \x1b[0m\n The Linux-ck kernel and modules with ck's hrtimer patches\n",
|
||||
"\x1b[35m2\x1b[0m \x1b[1m\x1b[33mcore\x1b[0m\x1b[0m/\x1b[1mlinux-zen\x1b[0m \x1b[36m5.16.0\x1b[0m\x1b[1m (1.0 B 1.0 B) \x1b[0m\n The Linux ZEN kernel and modules\n",
|
||||
"\x1b[35m1\x1b[0m \x1b[1m\x1b[33mcore\x1b[0m\x1b[0m/\x1b[1mlinux\x1b[0m \x1b[36m5.16.0\x1b[0m\x1b[1m (1.0 B 1.0 B) \x1b[0m\n The Linux kernel and modules\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "sort-by-votes topdown separatesources number-menu",
|
||||
search: []string{"linux"},
|
||||
bottomUp: false,
|
||||
separateSources: true,
|
||||
sortBy: "votes",
|
||||
verbosity: NumberMenu,
|
||||
wantResults: []string{"linux", "linux-zen", "linux-ck"},
|
||||
wantOutput: []string{
|
||||
"\x1b[35m1\x1b[0m \x1b[1m\x1b[33mcore\x1b[0m\x1b[0m/\x1b[1mlinux\x1b[0m \x1b[36m5.16.0\x1b[0m\x1b[1m (1.0 B 1.0 B) \x1b[0m\n The Linux kernel and modules\n",
|
||||
"\x1b[35m2\x1b[0m \x1b[1m\x1b[33mcore\x1b[0m\x1b[0m/\x1b[1mlinux-zen\x1b[0m \x1b[36m5.16.0\x1b[0m\x1b[1m (1.0 B 1.0 B) \x1b[0m\n The Linux ZEN kernel and modules\n",
|
||||
"\x1b[35m3\x1b[0m \x1b[1m\x1b[34maur\x1b[0m\x1b[0m/\x1b[1mlinux-ck\x1b[0m \x1b[36m5.16.12-1\x1b[0m\x1b[1m (+450\x1b[0m \x1b[1m1.51) \x1b[0m\n The Linux-ck kernel and modules with ck's hrtimer patches\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "sort-by-name bottomup separatesources number-menu",
|
||||
search: []string{"linux"},
|
||||
bottomUp: true,
|
||||
separateSources: true,
|
||||
sortBy: "name",
|
||||
verbosity: NumberMenu,
|
||||
wantResults: []string{"linux-ck", "linux", "linux-zen"},
|
||||
wantOutput: []string{
|
||||
"\x1b[35m3\x1b[0m \x1b[1m\x1b[34maur\x1b[0m\x1b[0m/\x1b[1mlinux-ck\x1b[0m \x1b[36m5.16.12-1\x1b[0m\x1b[1m (+450\x1b[0m \x1b[1m1.51) \x1b[0m\n The Linux-ck kernel and modules with ck's hrtimer patches\n",
|
||||
"\x1b[35m2\x1b[0m \x1b[1m\x1b[33mcore\x1b[0m\x1b[0m/\x1b[1mlinux\x1b[0m \x1b[36m5.16.0\x1b[0m\x1b[1m (1.0 B 1.0 B) \x1b[0m\n The Linux kernel and modules\n",
|
||||
"\x1b[35m1\x1b[0m \x1b[1m\x1b[33mcore\x1b[0m\x1b[0m/\x1b[1mlinux-zen\x1b[0m \x1b[36m5.16.0\x1b[0m\x1b[1m (1.0 B 1.0 B) \x1b[0m\n The Linux ZEN kernel and modules\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "sort-by-name topdown separatesources number-menu",
|
||||
search: []string{"linux"},
|
||||
bottomUp: false,
|
||||
separateSources: true,
|
||||
sortBy: "name",
|
||||
verbosity: NumberMenu,
|
||||
wantResults: []string{"linux-zen", "linux", "linux-ck"},
|
||||
wantOutput: []string{
|
||||
"\x1b[35m1\x1b[0m \x1b[1m\x1b[33mcore\x1b[0m\x1b[0m/\x1b[1mlinux-zen\x1b[0m \x1b[36m5.16.0\x1b[0m\x1b[1m (1.0 B 1.0 B) \x1b[0m\n The Linux ZEN kernel and modules\n",
|
||||
"\x1b[35m2\x1b[0m \x1b[1m\x1b[33mcore\x1b[0m\x1b[0m/\x1b[1mlinux\x1b[0m \x1b[36m5.16.0\x1b[0m\x1b[1m (1.0 B 1.0 B) \x1b[0m\n The Linux kernel and modules\n",
|
||||
"\x1b[35m3\x1b[0m \x1b[1m\x1b[34maur\x1b[0m\x1b[0m/\x1b[1mlinux-ck\x1b[0m \x1b[36m5.16.12-1\x1b[0m\x1b[1m (+450\x1b[0m \x1b[1m1.51) \x1b[0m\n The Linux-ck kernel and modules with ck's hrtimer patches\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "sort-by-name bottomup noseparatesources minimal",
|
||||
search: []string{"linux"},
|
||||
bottomUp: true,
|
||||
separateSources: false,
|
||||
sortBy: "name",
|
||||
verbosity: Minimal,
|
||||
wantResults: []string{"linux", "linux-ck", "linux-zen"},
|
||||
wantOutput: []string{
|
||||
"linux\n",
|
||||
"linux-ck\n",
|
||||
"linux-zen\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "only-aur minimal",
|
||||
search: []string{"linux"},
|
||||
bottomUp: true,
|
||||
separateSources: true,
|
||||
sortBy: "name",
|
||||
verbosity: Minimal,
|
||||
targetMode: parser.ModeAUR,
|
||||
wantResults: []string{"linux-ck"},
|
||||
wantOutput: []string{
|
||||
"linux-ck\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "only-repo minimal",
|
||||
search: []string{"linux"},
|
||||
bottomUp: true,
|
||||
separateSources: true,
|
||||
sortBy: "name",
|
||||
verbosity: Minimal,
|
||||
targetMode: parser.ModeRepo,
|
||||
wantResults: []string{"linux", "linux-zen"},
|
||||
wantOutput: []string{
|
||||
"linux\n",
|
||||
"linux-zen\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "sort-by-name singleline",
|
||||
search: []string{"linux"},
|
||||
bottomUp: true,
|
||||
separateSources: true,
|
||||
sortBy: "name",
|
||||
verbosity: Detailed,
|
||||
singleLineResults: true,
|
||||
wantResults: []string{"linux-ck", "linux", "linux-zen"},
|
||||
wantOutput: []string{
|
||||
"\x1b[1m\x1b[34maur\x1b[0m\x1b[0m/\x1b[1mlinux-ck\x1b[0m \x1b[36m5.16.12-1\x1b[0m\x1b[1m (+450\x1b[0m \x1b[1m1.51) \x1b[0m\tThe Linux-ck kernel and modules with ck's hrtimer patches\n",
|
||||
"\x1b[1m\x1b[33mcore\x1b[0m\x1b[0m/\x1b[1mlinux\x1b[0m \x1b[36m5.16.0\x1b[0m\x1b[1m (1.0 B 1.0 B) \x1b[0m\tThe Linux kernel and modules\n",
|
||||
"\x1b[1m\x1b[33mcore\x1b[0m\x1b[0m/\x1b[1mlinux-zen\x1b[0m \x1b[36m5.16.0\x1b[0m\x1b[1m (1.0 B 1.0 B) \x1b[0m\tThe Linux ZEN kernel and modules\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "sort-by-name search-by-name",
|
||||
search: []string{"linux-ck"},
|
||||
bottomUp: true,
|
||||
separateSources: true,
|
||||
sortBy: "name",
|
||||
verbosity: Detailed,
|
||||
searchBy: "name",
|
||||
targetMode: parser.ModeAUR,
|
||||
wantResults: []string{"linux-ck"},
|
||||
wantOutput: []string{
|
||||
"\x1b[1m\x1b[34maur\x1b[0m\x1b[0m/\x1b[1mlinux-ck\x1b[0m \x1b[36m5.16.12-1\x1b[0m\x1b[1m (+450\x1b[0m \x1b[1m1.51) \x1b[0m\n The Linux-ck kernel and modules with ck's hrtimer patches\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "only-aur search-by-several-terms",
|
||||
search: []string{"linux-ck", "hrtimer"},
|
||||
bottomUp: true,
|
||||
separateSources: true,
|
||||
verbosity: Detailed,
|
||||
targetMode: parser.ModeAUR,
|
||||
wantResults: []string{"linux-ck"},
|
||||
wantOutput: []string{
|
||||
"\x1b[1m\x1b[34maur\x1b[0m\x1b[0m/\x1b[1mlinux-ck\x1b[0m \x1b[36m5.16.12-1\x1b[0m\x1b[1m (+450\x1b[0m \x1b[1m1.51) \x1b[0m\n The Linux-ck kernel and modules with ck's hrtimer patches\n",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
mockDB := &mock.DBExecutor{
|
||||
SyncPackagesFn: func(pkgs ...string) []mock.IPackage {
|
||||
mockDB := mock.NewDB("core")
|
||||
return []mock.IPackage{
|
||||
&mock.Package{
|
||||
PName: "linux",
|
||||
PVersion: "5.16.0",
|
||||
PDescription: "The Linux kernel and modules",
|
||||
PSize: 1,
|
||||
PISize: 1,
|
||||
PDB: mockDB,
|
||||
},
|
||||
&mock.Package{
|
||||
PName: "linux-zen",
|
||||
PVersion: "5.16.0",
|
||||
PDescription: "The Linux ZEN kernel and modules",
|
||||
PSize: 1,
|
||||
PISize: 1,
|
||||
PDB: mockDB,
|
||||
},
|
||||
}
|
||||
},
|
||||
LocalPackageFn: func(string) mock.IPackage {
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
mockAUR := &mockaur.MockAUR{
|
||||
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
|
||||
return []aur.Pkg{
|
||||
{
|
||||
Description: "The Linux-ck kernel and modules with ck's hrtimer patches",
|
||||
FirstSubmitted: 1311346274,
|
||||
ID: 1045311,
|
||||
LastModified: 1646250901,
|
||||
Maintainer: "graysky",
|
||||
Name: "linux-ck",
|
||||
NumVotes: 450,
|
||||
OutOfDate: 0,
|
||||
PackageBase: "linux-ck",
|
||||
PackageBaseID: 50911,
|
||||
Popularity: 1.511141,
|
||||
URL: "https://wiki.archlinux.org/index.php/Linux-ck",
|
||||
URLPath: "/cgit/aur.git/snapshot/linux-ck.tar.gz",
|
||||
Version: "5.16.12-1",
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
w := &strings.Builder{}
|
||||
queryBuilder := NewSourceQueryBuilder(mockAUR,
|
||||
text.NewLogger(w, io.Discard, strings.NewReader(""), false, "test"),
|
||||
tc.sortBy, tc.targetMode, tc.searchBy, tc.bottomUp,
|
||||
tc.singleLineResults, tc.separateSources)
|
||||
|
||||
queryBuilder.Execute(context.Background(), mockDB, tc.search)
|
||||
|
||||
assert.Len(t, queryBuilder.results, len(tc.wantResults))
|
||||
assert.Equal(t, len(tc.wantResults), queryBuilder.Len())
|
||||
for i, name := range tc.wantResults {
|
||||
assert.Equal(t, name, queryBuilder.results[i].name)
|
||||
}
|
||||
|
||||
queryBuilder.Results(mockDB, tc.verbosity)
|
||||
|
||||
assert.Equal(t, strings.Join(tc.wantOutput, ""), w.String())
|
||||
})
|
||||
}
|
||||
}
|
@ -2,32 +2,193 @@ package query
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/Jguer/aur"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/Jguer/go-alpm/v2"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
|
||||
"github.com/Jguer/yay/v11/pkg/db"
|
||||
"github.com/Jguer/yay/v11/pkg/intrange"
|
||||
"github.com/Jguer/yay/v11/pkg/settings/parser"
|
||||
"github.com/Jguer/yay/v11/pkg/stringset"
|
||||
"github.com/Jguer/yay/v11/pkg/text"
|
||||
)
|
||||
|
||||
type SearchVerbosity int
|
||||
|
||||
// Verbosity settings for search.
|
||||
const (
|
||||
NumberMenu SearchVerbosity = iota
|
||||
Detailed
|
||||
Minimal
|
||||
)
|
||||
|
||||
type SourceQueryBuilder struct {
|
||||
repoQuery
|
||||
aurQuery
|
||||
sortBy string
|
||||
searchBy string
|
||||
targetMode parser.TargetMode
|
||||
bottomUp bool
|
||||
singleLineResults bool
|
||||
}
|
||||
|
||||
func NewSourceQueryBuilder(
|
||||
sortBy string,
|
||||
targetMode parser.TargetMode,
|
||||
searchBy string,
|
||||
bottomUp,
|
||||
singleLineResults bool,
|
||||
) *SourceQueryBuilder {
|
||||
return &SourceQueryBuilder{
|
||||
repoQuery: []alpm.IPackage{},
|
||||
aurQuery: []aur.Pkg{},
|
||||
bottomUp: bottomUp,
|
||||
sortBy: sortBy,
|
||||
targetMode: targetMode,
|
||||
searchBy: searchBy,
|
||||
singleLineResults: singleLineResults,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SourceQueryBuilder) Execute(ctx context.Context, dbExecutor db.Executor, aurClient aur.ClientInterface, pkgS []string) {
|
||||
var aurErr error
|
||||
|
||||
pkgS = RemoveInvalidTargets(pkgS, s.targetMode)
|
||||
|
||||
if s.targetMode.AtLeastAUR() {
|
||||
s.aurQuery, aurErr = queryAUR(ctx, aurClient, pkgS, s.searchBy)
|
||||
s.aurQuery = filterAURResults(pkgS, s.aurQuery)
|
||||
|
||||
sort.Sort(aurSortable{aurQuery: s.aurQuery, sortBy: s.sortBy, bottomUp: s.bottomUp})
|
||||
}
|
||||
|
||||
if s.targetMode.AtLeastRepo() {
|
||||
s.repoQuery = repoQuery(dbExecutor.SyncPackages(pkgS...))
|
||||
|
||||
if s.bottomUp {
|
||||
s.Reverse()
|
||||
}
|
||||
}
|
||||
|
||||
if aurErr != nil && len(s.repoQuery) != 0 {
|
||||
text.Errorln(ErrAURSearch{inner: aurErr})
|
||||
text.Warnln(gotext.Get("Showing repo packages only"))
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SourceQueryBuilder) Results(w io.Writer, dbExecutor db.Executor, verboseSearch SearchVerbosity) error {
|
||||
if s.aurQuery == nil || s.repoQuery == nil {
|
||||
return ErrNoQuery{}
|
||||
}
|
||||
|
||||
if s.bottomUp {
|
||||
if s.targetMode.AtLeastAUR() {
|
||||
s.aurQuery.printSearch(w, len(s.repoQuery)+1, dbExecutor, verboseSearch, s.bottomUp, s.singleLineResults)
|
||||
}
|
||||
|
||||
if s.targetMode.AtLeastRepo() {
|
||||
s.repoQuery.printSearch(w, dbExecutor, verboseSearch, s.bottomUp, s.singleLineResults)
|
||||
}
|
||||
} else {
|
||||
if s.targetMode.AtLeastRepo() {
|
||||
s.repoQuery.printSearch(w, dbExecutor, verboseSearch, s.bottomUp, s.singleLineResults)
|
||||
}
|
||||
|
||||
if s.targetMode.AtLeastAUR() {
|
||||
s.aurQuery.printSearch(w, len(s.repoQuery)+1, dbExecutor, verboseSearch, s.bottomUp, s.singleLineResults)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SourceQueryBuilder) Len() int {
|
||||
return len(s.repoQuery) + len(s.aurQuery)
|
||||
}
|
||||
|
||||
func (s *SourceQueryBuilder) GetTargets(include, exclude intrange.IntRanges,
|
||||
otherExclude stringset.StringSet,
|
||||
) ([]string, error) {
|
||||
isInclude := len(exclude) == 0 && len(otherExclude) == 0
|
||||
|
||||
var targets []string
|
||||
|
||||
for i, pkg := range s.repoQuery {
|
||||
var target int
|
||||
|
||||
if s.bottomUp {
|
||||
target = len(s.repoQuery) - i
|
||||
} else {
|
||||
target = i + 1
|
||||
}
|
||||
|
||||
if (isInclude && include.Get(target)) || (!isInclude && !exclude.Get(target)) {
|
||||
targets = append(targets, pkg.DB().Name()+"/"+pkg.Name())
|
||||
}
|
||||
}
|
||||
|
||||
for i := range s.aurQuery {
|
||||
var target int
|
||||
|
||||
if s.bottomUp {
|
||||
target = len(s.aurQuery) - i + len(s.repoQuery)
|
||||
} else {
|
||||
target = i + 1 + len(s.repoQuery)
|
||||
}
|
||||
|
||||
if (isInclude && include.Get(target)) || (!isInclude && !exclude.Get(target)) {
|
||||
targets = append(targets, "aur/"+s.aurQuery[i].Name)
|
||||
}
|
||||
}
|
||||
|
||||
return targets, nil
|
||||
}
|
||||
|
||||
// filter AUR results to remove strings that don't contain all of the search terms.
|
||||
func filterAURResults(pkgS []string, results []aur.Pkg) []aur.Pkg {
|
||||
aurPkgs := make([]aur.Pkg, 0, len(results))
|
||||
|
||||
matchesSearchTerms := func(pkg *aur.Pkg, terms []string) bool {
|
||||
for _, pkgN := range terms {
|
||||
name := strings.ToLower(pkg.Name)
|
||||
desc := strings.ToLower(pkg.Description)
|
||||
targ := strings.ToLower(pkgN)
|
||||
|
||||
if !(strings.Contains(name, targ) || strings.Contains(desc, targ)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
for i := range results {
|
||||
if matchesSearchTerms(&results[i], pkgS) {
|
||||
aurPkgs = append(aurPkgs, results[i])
|
||||
}
|
||||
}
|
||||
|
||||
return aurPkgs
|
||||
}
|
||||
|
||||
// queryAUR searches AUR and narrows based on subarguments.
|
||||
func queryAUR(ctx context.Context,
|
||||
aurClient aur.QueryClient,
|
||||
pkgS []string, searchBy string,
|
||||
) ([]aur.Pkg, error) {
|
||||
func queryAUR(ctx context.Context, aurClient aur.ClientInterface, pkgS []string, searchBy string) ([]aur.Pkg, error) {
|
||||
var (
|
||||
err error
|
||||
by = getSearchBy(searchBy)
|
||||
)
|
||||
|
||||
for _, word := range pkgS {
|
||||
r, errM := aurClient.Get(ctx, &aur.Query{
|
||||
Needles: []string{word},
|
||||
By: by,
|
||||
Contains: true,
|
||||
})
|
||||
if errM == nil {
|
||||
var r []aur.Pkg
|
||||
|
||||
r, err = aurClient.Search(ctx, word, by)
|
||||
if err == nil {
|
||||
return r, nil
|
||||
}
|
||||
|
||||
err = multierror.Append(err, errM)
|
||||
}
|
||||
|
||||
return nil, err
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user