forked from Plemya-x/ALR
		
	Compare commits
	
		
			108 Commits
		
	
	
		
			469a05105a
			...
			master
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| aa08c04e0c | |||
| f42be105ad | |||
| 1cc408ad7d | |||
| 4899e203bb | |||
| 67a6cb31de | |||
| 5e24940ef8 | |||
| a600feb083 | |||
| 7060e4f551 | |||
| d77ca4c384 | |||
| 6355f25089 | |||
| a83561b6a5 | |||
| 4b06809a39 | |||
| 401c41160c | |||
| 5e1eeabd04 | |||
| db19133254 | |||
| e8202060d8 | |||
| c4a92c67d4 | |||
| 85878f69d3 | |||
| 6bccce1db4 | |||
| b5474b1eb4 | |||
| 51fdea781b | |||
| 4c1f2ea90f | |||
| 7fa7f8ba82 | |||
| 25d001c1c9 | |||
| f86b3003b1 | |||
| bd79dcf401 | |||
| d1fe02fa57 | |||
| 1ca7801fba | |||
| 661d79ce24 | |||
| bece64c132 | |||
| 6d29b98cf7 | |||
| d286041864 | |||
| 392a522723 | |||
| e259184a89 | |||
| 65ab4de561 | |||
| 1cdab8dfed | |||
| 237e2c338d | |||
| 703ab8e8c4 | |||
| 06fcab4ce7 | |||
| 7741c7368b | |||
| 69f4af0a4d | |||
| bcf627f176 | |||
| 6ec95e4bd9 | |||
| 578da7ff52 | |||
| c51caf5c52 | |||
| 09dba577c6 | |||
| ca82bf3024 | |||
| c0023db6cd | |||
| 152e5077ec | |||
| 15ba8700e8 | |||
| a8aefc0524 | |||
| 9540030579 | |||
| 4f9d4260b8 | |||
| 38b5e6f581 | |||
| 408bd12302 | |||
| fb83d544de | |||
| 2cb963d4b2 | |||
| e74d74cdf6 | |||
| 5b3d53d253 | |||
| 36e704f735 | |||
| 07356d5e55 | |||
| 52bd6aca93 | |||
| 2f1770b43b | |||
| 9d5b5b51ff | |||
| c88478a450 | |||
| 3e61fec67c | |||
| 6f484a1169 | |||
| dddcb9b7b0 | |||
| b03d94e48b | |||
| f92bd7089a | |||
| eb2356458c | |||
| 131f455eff | |||
| 1e52d30f4c | |||
| 40ec0ac6e1 | |||
| 443e481561 | |||
| c892310f69 | |||
| 750513b119 | |||
| ce1836b646 | |||
| 56b9f3211c | |||
| fae63e28f9 | |||
| c632ddb354 | |||
| 76234bf00d | |||
| f8c510ab9f | |||
| 849a08a791 | |||
| 952dd26f5f | |||
| 080c9f42ff | |||
| 3c3ee286ce | |||
| d0d8930491 | |||
| 93508647e0 | |||
| 6135e55f92 | |||
| 2b7c2bbbb3 | |||
| afe35f407e | |||
| c51d1d9202 | |||
| b46dd41ada | |||
| f623cba5c0 | |||
| e552663442 | |||
| 7bbceb76c9 | |||
| bd6e3bbe27 | |||
| 0d917190ab | |||
| 83b8f3b047 | |||
| 3483cf57f8 | |||
| efa4f59403 | |||
| c59ed6d505 | |||
| 2bbc308810 | |||
| 5ca34a572a | |||
| 67e63d1831 | |||
| 0bfe88beed | |||
| 3ce9f0db35 | 
							
								
								
									
										57
									
								
								.gitea/workflows/e2e-tests.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								.gitea/workflows/e2e-tests.yaml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,57 @@
 | 
				
			|||||||
 | 
					# ALR - Any Linux Repository
 | 
				
			||||||
 | 
					# Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					# it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					# the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					# (at your option) any later version.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					# but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					# GNU General Public License for more details.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					name: E2E
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# on:
 | 
				
			||||||
 | 
					#   push:
 | 
				
			||||||
 | 
					#     branches: [ main ]
 | 
				
			||||||
 | 
					#   pull_request:
 | 
				
			||||||
 | 
					on:
 | 
				
			||||||
 | 
					  workflow_dispatch:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					jobs:
 | 
				
			||||||
 | 
					  tests:
 | 
				
			||||||
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    container:
 | 
				
			||||||
 | 
					      image: altlinux.space/maks1ms/actions-container-runner:latest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    steps:
 | 
				
			||||||
 | 
					      - name: Checkout
 | 
				
			||||||
 | 
					        uses: https://github.com/actions/checkout@v4
 | 
				
			||||||
 | 
					        with:
 | 
				
			||||||
 | 
					          fetch-depth: 0
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      - name: Set up Go
 | 
				
			||||||
 | 
					        uses: https://github.com/actions/setup-go@v5
 | 
				
			||||||
 | 
					        with:
 | 
				
			||||||
 | 
					          go-version: '1.24'
 | 
				
			||||||
 | 
					          cache: false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      # - name: Cache Podman images
 | 
				
			||||||
 | 
					      #   uses: actions/cache@v4
 | 
				
			||||||
 | 
					      #   with:
 | 
				
			||||||
 | 
					      #     path: |
 | 
				
			||||||
 | 
					      #       ~/.local/share/containers/storage
 | 
				
			||||||
 | 
					      #       /var/lib/containers/storage
 | 
				
			||||||
 | 
					      #     key: ${{ runner.os }}-primes            
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      - name: Run E2E tests
 | 
				
			||||||
 | 
					        env:
 | 
				
			||||||
 | 
					          IGNORE_ROOT_CHECK: 1
 | 
				
			||||||
 | 
					        run: |
 | 
				
			||||||
 | 
					          make e2e-test
 | 
				
			||||||
							
								
								
									
										51
									
								
								.gitea/workflows/pre-commit.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								.gitea/workflows/pre-commit.yaml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,51 @@
 | 
				
			|||||||
 | 
					# ALR - Any Linux Repository
 | 
				
			||||||
 | 
					# Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					# it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					# the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					# (at your option) any later version.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					# but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					# GNU General Public License for more details.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					name: Pre-commit
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					on:
 | 
				
			||||||
 | 
					  push:
 | 
				
			||||||
 | 
					    branches: [ main ]
 | 
				
			||||||
 | 
					  pull_request:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					jobs:
 | 
				
			||||||
 | 
					  pre-commit:
 | 
				
			||||||
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
 | 
					    container:
 | 
				
			||||||
 | 
					      image: docker.gitea.com/runner-images:ubuntu-latest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    steps:
 | 
				
			||||||
 | 
					      - name: Checkout
 | 
				
			||||||
 | 
					        uses: https://github.com/actions/checkout@v4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      - name: Set up Go
 | 
				
			||||||
 | 
					        uses: https://github.com/actions/setup-go@v5
 | 
				
			||||||
 | 
					        with:
 | 
				
			||||||
 | 
					          go-version: '1.24'
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      - name: Set up Python for pre-commit
 | 
				
			||||||
 | 
					        uses: https://github.com/actions/setup-python@v5
 | 
				
			||||||
 | 
					        with:
 | 
				
			||||||
 | 
					          python-version: '3.12'
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      - name: Install deps
 | 
				
			||||||
 | 
					        run: apt-get update && apt-get install -y gettext bc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      - run: pip install pre-commit
 | 
				
			||||||
 | 
					      - run: pre-commit install
 | 
				
			||||||
 | 
					      - run: pre-commit run --all-files
 | 
				
			||||||
							
								
								
									
										120
									
								
								.gitea/workflows/release.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								.gitea/workflows/release.yaml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,120 @@
 | 
				
			|||||||
 | 
					# ALR - Any Linux Repository
 | 
				
			||||||
 | 
					# Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					# it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					# the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					# (at your option) any later version.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					# but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					# GNU General Public License for more details.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					name: Create Release
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					on:
 | 
				
			||||||
 | 
					  push:
 | 
				
			||||||
 | 
					    tags:
 | 
				
			||||||
 | 
					      - 'v[0-9]+.[0-9]+.[0-9]+'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					jobs:
 | 
				
			||||||
 | 
					  changelog:
 | 
				
			||||||
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
 | 
					    steps:
 | 
				
			||||||
 | 
					      - name: Checkout this repository
 | 
				
			||||||
 | 
					        uses: actions/checkout@v4
 | 
				
			||||||
 | 
					        with:
 | 
				
			||||||
 | 
					          fetch-depth: 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      - name: Set up Go
 | 
				
			||||||
 | 
					        uses: actions/setup-go@v5
 | 
				
			||||||
 | 
					        with:
 | 
				
			||||||
 | 
					          go-version: '1.24'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      - name: Get Changes between Tags
 | 
				
			||||||
 | 
					        id: changes
 | 
				
			||||||
 | 
					        uses: simbo/changes-between-tags-action@v1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      - name: Set version
 | 
				
			||||||
 | 
					        run: |
 | 
				
			||||||
 | 
					          version=$(echo "${GITHUB_REF##*/}" | sed 's/^v//')
 | 
				
			||||||
 | 
					          echo "Version - $version"
 | 
				
			||||||
 | 
					          echo "VERSION=$version" >> $GITHUB_ENV
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      - name: Prepare for install
 | 
				
			||||||
 | 
					        run: |
 | 
				
			||||||
 | 
					          apt-get update && apt-get install -y libcap2-bin bindfs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      - name: Build alr
 | 
				
			||||||
 | 
					        env:
 | 
				
			||||||
 | 
					          IGNORE_ROOT_CHECK: 1
 | 
				
			||||||
 | 
					        run: |
 | 
				
			||||||
 | 
					          make build
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      - name: Create tar.gz
 | 
				
			||||||
 | 
					        run: |
 | 
				
			||||||
 | 
					          mkdir -p ./out/completion
 | 
				
			||||||
 | 
					          cp alr ./out
 | 
				
			||||||
 | 
					          cp scripts/completion/bash ./out/completion/alr
 | 
				
			||||||
 | 
					          cp scripts/completion/zsh ./out/completion/_alr
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          ( cd out && tar -czvf ../alr-${{ env.VERSION }}-linux-x86_64.tar.gz * )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      - name: Release
 | 
				
			||||||
 | 
					        uses: akkuman/gitea-release-action@v1
 | 
				
			||||||
 | 
					        with:
 | 
				
			||||||
 | 
					          body: ${{ steps.changes.outputs.changes }}
 | 
				
			||||||
 | 
					          files: |-
 | 
				
			||||||
 | 
					            alr-${{ env.VERSION }}-linux-x86_64.tar.gz
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      - name: Checkout alr-default repository
 | 
				
			||||||
 | 
					        uses: actions/checkout@v4
 | 
				
			||||||
 | 
					        with:
 | 
				
			||||||
 | 
					          repository: Plemya-x/alr-default
 | 
				
			||||||
 | 
					          token: ${{ secrets.GITEAPUBLIC }}
 | 
				
			||||||
 | 
					          path: alr-default
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      - name: Update version in alr-bin
 | 
				
			||||||
 | 
					        run: |
 | 
				
			||||||
 | 
					          # Замените значения в файле с конфигурацией
 | 
				
			||||||
 | 
					          sed -i "s/version='[0-9]\+\.[0-9]\+\.[0-9]\+'/version='${{ env.VERSION }}'/g" alr-default/alr-bin/alr.sh
 | 
				
			||||||
 | 
					          sed -i "s/release='[0-9]\+'/release='1'/g" alr-default/alr-bin/alr.sh
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#      - name: Install alr
 | 
				
			||||||
 | 
					#        run: |
 | 
				
			||||||
 | 
					#          make install
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					#          # temporary fix
 | 
				
			||||||
 | 
					#          groupadd wheel
 | 
				
			||||||
 | 
					#          usermod -aG wheel root
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#      - name: Build packages
 | 
				
			||||||
 | 
					#        run: |
 | 
				
			||||||
 | 
					#          SCRIPT_PATH=alr-default/alr-bin/alr.sh
 | 
				
			||||||
 | 
					#          ALR_DISTRO=altlinux ALR_PKG_FORMAT=rpm alr build -s "$SCRIPT_PATH"
 | 
				
			||||||
 | 
					#          ALR_PKG_FORMAT=rpm alr build -s "$SCRIPT_PATH"
 | 
				
			||||||
 | 
					#          ALR_PKG_FORMAT=deb alr build -s "$SCRIPT_PATH"
 | 
				
			||||||
 | 
					#          ALR_PKG_FORMAT=archlinux alr build -s "$SCRIPT_PATH"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#      - name: Upload assets
 | 
				
			||||||
 | 
					#        uses: akkuman/gitea-release-action@v1
 | 
				
			||||||
 | 
					#        with:
 | 
				
			||||||
 | 
					#          body: ${{ steps.changes.outputs.changes }}
 | 
				
			||||||
 | 
					#          files: |-
 | 
				
			||||||
 | 
					#            alr-bin+alr-default_${{ env.VERSION }}-1.red80_amd64.deb \
 | 
				
			||||||
 | 
					#            alr-bin+alr-default-${{ env.VERSION }}-1-x86_64.pkg.tar.zst \
 | 
				
			||||||
 | 
					#            alr-bin+alr-default-${{ env.VERSION }}-1.red80.x86_64.rpm \
 | 
				
			||||||
 | 
					#            alr-bin+alr-default-${{ env.VERSION }}-alt1.x86_64.rpm
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      - name: Commit changes
 | 
				
			||||||
 | 
					        run: |
 | 
				
			||||||
 | 
					          cd alr-default
 | 
				
			||||||
 | 
					          git config user.name "gitea"
 | 
				
			||||||
 | 
					          git config user.email "admin@plemya-x.ru"
 | 
				
			||||||
 | 
					          git add .
 | 
				
			||||||
 | 
					          git commit -m "Обновление версии до ${{ env.VERSION }}"
 | 
				
			||||||
 | 
					          git push
 | 
				
			||||||
							
								
								
									
										69
									
								
								.gitea/workflows/update-alr-git.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								.gitea/workflows/update-alr-git.yaml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,69 @@
 | 
				
			|||||||
 | 
					# ALR - Any Linux Repository
 | 
				
			||||||
 | 
					# Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					# it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					# the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					# (at your option) any later version.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					# but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					# GNU General Public License for more details.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					name: Update alr-git
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					on:
 | 
				
			||||||
 | 
					  push:
 | 
				
			||||||
 | 
					    branches:
 | 
				
			||||||
 | 
					      - master
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					jobs:
 | 
				
			||||||
 | 
					  changelog:
 | 
				
			||||||
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
 | 
					    steps:
 | 
				
			||||||
 | 
					      - name: Install the latest version of uv
 | 
				
			||||||
 | 
					        uses: astral-sh/setup-uv@v6
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      - name: Setup alr-spec
 | 
				
			||||||
 | 
					        run: |
 | 
				
			||||||
 | 
					          uv tool install alr-spec==0.0.5
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      - name: Install alr
 | 
				
			||||||
 | 
					        run: |
 | 
				
			||||||
 | 
					          apt-get update && apt-get install -y libcap2-bin
 | 
				
			||||||
 | 
					          curl -fsS https://gitea.plemya-x.ru/Plemya-x/ALR/raw/branch/master/scripts/install.sh | bash          
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      - name: Checkout this repository
 | 
				
			||||||
 | 
					        uses: actions/checkout@v4
 | 
				
			||||||
 | 
					        with:
 | 
				
			||||||
 | 
					          fetch-depth: 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      - name: Set ALR version
 | 
				
			||||||
 | 
					        run: |
 | 
				
			||||||
 | 
					          echo "NEW_ALR_VERSION=$(alr helper git-version)" >> $GITHUB_ENV
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      - name: Checkout alr-default repository
 | 
				
			||||||
 | 
					        uses: actions/checkout@v4
 | 
				
			||||||
 | 
					        with:
 | 
				
			||||||
 | 
					          repository: Plemya-x/alr-default
 | 
				
			||||||
 | 
					          token: ${{ secrets.GITEAPUBLIC }}
 | 
				
			||||||
 | 
					          path: alr-default
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      - name: Update version
 | 
				
			||||||
 | 
					        working-directory: ./alr-default/alr-git
 | 
				
			||||||
 | 
					        run: |
 | 
				
			||||||
 | 
					          alr-spec set-field version $NEW_ALR_VERSION
 | 
				
			||||||
 | 
					          alr-spec set-field release 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      - name: Commit changes
 | 
				
			||||||
 | 
					        run: |
 | 
				
			||||||
 | 
					          cd alr-default
 | 
				
			||||||
 | 
					          git config user.name "gitea"
 | 
				
			||||||
 | 
					          git config user.email "admin@plemya-x.ru"
 | 
				
			||||||
 | 
					          git add .
 | 
				
			||||||
 | 
					          git commit -m "Обновление версии до $NEW_ALR_VERSION"
 | 
				
			||||||
 | 
					          git push
 | 
				
			||||||
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -10,3 +10,4 @@
 | 
				
			|||||||
*.out
 | 
					*.out
 | 
				
			||||||
 | 
					
 | 
				
			||||||
e2e-tests/alr
 | 
					e2e-tests/alr
 | 
				
			||||||
 | 
					commit_msg.txt
 | 
				
			||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
# ALR - Any Linux Repository
 | 
					# ALR - Any Linux Repository
 | 
				
			||||||
# Copyright (C) 2025 Евгений Храмов
 | 
					# Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is free software: you can redistribute it and/or modify
 | 
					# This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
# it under the terms of the GNU General Public License as published by
 | 
					# it under the terms of the GNU General Public License as published by
 | 
				
			||||||
@@ -36,11 +36,14 @@ linters:
 | 
				
			|||||||
    - unused
 | 
					    - unused
 | 
				
			||||||
    - errcheck
 | 
					    - errcheck
 | 
				
			||||||
    - typecheck
 | 
					    - typecheck
 | 
				
			||||||
#    - forbidigo
 | 
					    - wrapcheck
 | 
				
			||||||
 | 
					
 | 
				
			||||||
issues:
 | 
					issues:
 | 
				
			||||||
  fix: true
 | 
					  fix: true
 | 
				
			||||||
  exclude-rules:
 | 
					  exclude-rules:
 | 
				
			||||||
 | 
					    - linters:
 | 
				
			||||||
 | 
					        - wrapcheck
 | 
				
			||||||
 | 
					      path-except: "internal/repos/find.go"
 | 
				
			||||||
    - path: _test\.go
 | 
					    - path: _test\.go
 | 
				
			||||||
      linters:
 | 
					      linters:
 | 
				
			||||||
        - errcheck
 | 
					        - errcheck
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										118
									
								
								.goreleaser.yaml
									
									
									
									
									
								
							
							
						
						
									
										118
									
								
								.goreleaser.yaml
									
									
									
									
									
								
							@@ -1,118 +0,0 @@
 | 
				
			|||||||
# This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
 | 
					 | 
				
			||||||
# It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# ALR - Any Linux Repository
 | 
					 | 
				
			||||||
# Copyright (C) 2025 Евгений Храмов
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# This program is free software: you can redistribute it and/or modify
 | 
					 | 
				
			||||||
# it under the terms of the GNU General Public License as published by
 | 
					 | 
				
			||||||
# the Free Software Foundation, either version 3 of the License, or
 | 
					 | 
				
			||||||
# (at your option) any later version.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# This program is distributed in the hope that it will be useful,
 | 
					 | 
				
			||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					 | 
				
			||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					 | 
				
			||||||
# GNU General Public License for more details.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# You should have received a copy of the GNU General Public License
 | 
					 | 
				
			||||||
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
before:
 | 
					 | 
				
			||||||
  hooks:
 | 
					 | 
				
			||||||
    - go mod tidy
 | 
					 | 
				
			||||||
builds:
 | 
					 | 
				
			||||||
  - id: alr
 | 
					 | 
				
			||||||
    env:
 | 
					 | 
				
			||||||
      - CGO_ENABLED=0
 | 
					 | 
				
			||||||
    binary: alr
 | 
					 | 
				
			||||||
    ldflags:
 | 
					 | 
				
			||||||
      - -X gitea.plemya-x.ru/xpamych/ALR/src/branch/master/internal/config.Version={{.Version}}
 | 
					 | 
				
			||||||
    goos:
 | 
					 | 
				
			||||||
      - linux
 | 
					 | 
				
			||||||
    goarch:
 | 
					 | 
				
			||||||
      - amd64
 | 
					 | 
				
			||||||
      - 386
 | 
					 | 
				
			||||||
      - arm64
 | 
					 | 
				
			||||||
      - arm
 | 
					 | 
				
			||||||
      - riscv64
 | 
					 | 
				
			||||||
archives:
 | 
					 | 
				
			||||||
  - name_template: >-
 | 
					 | 
				
			||||||
      {{- .ProjectName}}-
 | 
					 | 
				
			||||||
      {{- .Version}}-
 | 
					 | 
				
			||||||
      {{- .Os}}-
 | 
					 | 
				
			||||||
      {{- if .Arch | eq "amd64"}}x86_64
 | 
					 | 
				
			||||||
      {{- else if .Arch | eq "386"}}i386
 | 
					 | 
				
			||||||
      {{- else if .Arch | eq "arm64"}}aarch64
 | 
					 | 
				
			||||||
      {{- else }}{{ .Arch }}{{ end -}}
 | 
					 | 
				
			||||||
    files:
 | 
					 | 
				
			||||||
      - scripts/completion/*
 | 
					 | 
				
			||||||
nfpms:
 | 
					 | 
				
			||||||
  - id: alr
 | 
					 | 
				
			||||||
    package_name: linux-user-repository
 | 
					 | 
				
			||||||
    file_name_template: >-
 | 
					 | 
				
			||||||
      {{- .PackageName}}-
 | 
					 | 
				
			||||||
      {{- .Version}}-
 | 
					 | 
				
			||||||
      {{- .Os}}-
 | 
					 | 
				
			||||||
      {{- if .Arch | eq "amd64"}}x86_64
 | 
					 | 
				
			||||||
      {{- else if .Arch | eq "386"}}i386
 | 
					 | 
				
			||||||
      {{- else if .Arch | eq "arm64"}}aarch64
 | 
					 | 
				
			||||||
      {{- else }}{{ .Arch }}{{ end -}}
 | 
					 | 
				
			||||||
    description: "Any Linux Repository"
 | 
					 | 
				
			||||||
    homepage: 'https://gitea.plemya-x.ru/xpamych/ALR'
 | 
					 | 
				
			||||||
    maintainer: 'Евгений Храмов <xpamych@yandex.ru>'
 | 
					 | 
				
			||||||
    license: GPLv3
 | 
					 | 
				
			||||||
    formats:
 | 
					 | 
				
			||||||
      - apk
 | 
					 | 
				
			||||||
      - deb
 | 
					 | 
				
			||||||
      - rpm
 | 
					 | 
				
			||||||
      - archlinux
 | 
					 | 
				
			||||||
    provides:
 | 
					 | 
				
			||||||
      - linux-user-repository
 | 
					 | 
				
			||||||
    conflicts:
 | 
					 | 
				
			||||||
      - linux-user-repository
 | 
					 | 
				
			||||||
    recommends:
 | 
					 | 
				
			||||||
      - aria2
 | 
					 | 
				
			||||||
    contents:
 | 
					 | 
				
			||||||
      - src: scripts/completion/bash
 | 
					 | 
				
			||||||
        dst: /usr/share/bash-completion/completions/alr
 | 
					 | 
				
			||||||
      - src: scripts/completion/zsh
 | 
					 | 
				
			||||||
        dst: /usr/share/zsh/site-functions/_alr
 | 
					 | 
				
			||||||
aurs:
 | 
					 | 
				
			||||||
  - name: linux-user-repository-bin
 | 
					 | 
				
			||||||
    homepage: 'https://gitea.plemya-x.ru/xpamych/ALR'
 | 
					 | 
				
			||||||
    description: "Any Linux Repository"
 | 
					 | 
				
			||||||
    maintainers:
 | 
					 | 
				
			||||||
      - 'Евгений Храмов <xpamych@yandex.ru>'
 | 
					 | 
				
			||||||
    license: GPLv3
 | 
					 | 
				
			||||||
    private_key: '{{ .Env.AUR_KEY }}'
 | 
					 | 
				
			||||||
    git_url: 'ssh://aur@aur.archlinux.org/linux-user-repository-bin.git'
 | 
					 | 
				
			||||||
    provides:
 | 
					 | 
				
			||||||
      - alr
 | 
					 | 
				
			||||||
    conflicts:
 | 
					 | 
				
			||||||
      - alr
 | 
					 | 
				
			||||||
    depends:
 | 
					 | 
				
			||||||
      - sudo
 | 
					 | 
				
			||||||
      - pacman
 | 
					 | 
				
			||||||
    optdepends:
 | 
					 | 
				
			||||||
      - 'aria2: for downloading torrent sources'
 | 
					 | 
				
			||||||
    package: |-
 | 
					 | 
				
			||||||
      # binaries
 | 
					 | 
				
			||||||
      install -Dm755 ./alr "${pkgdir}/usr/bin/alr"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      # completions
 | 
					 | 
				
			||||||
      install -Dm755 ./scripts/completion/bash ${pkgdir}/usr/share/bash-completion/completions/alr
 | 
					 | 
				
			||||||
      install -Dm755 ./scripts/completion/zsh ${pkgdir}/usr/share/zsh/site-functions/_alr
 | 
					 | 
				
			||||||
release:
 | 
					 | 
				
			||||||
  gitea:
 | 
					 | 
				
			||||||
    owner: alr
 | 
					 | 
				
			||||||
    name: alr
 | 
					 | 
				
			||||||
gitea_urls:
 | 
					 | 
				
			||||||
  api: 'https://gitea.elara.ws/api/v1/'
 | 
					 | 
				
			||||||
  download: 'https://gitea.elara.ws'
 | 
					 | 
				
			||||||
  skip_tls_verify: false
 | 
					 | 
				
			||||||
checksum:
 | 
					 | 
				
			||||||
  name_template: 'checksums.txt'
 | 
					 | 
				
			||||||
snapshot:
 | 
					 | 
				
			||||||
  name_template: "{{ incpatch .Version }}-next"
 | 
					 | 
				
			||||||
changelog:
 | 
					 | 
				
			||||||
  sort: asc
 | 
					 | 
				
			||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
# ALR - Any Linux Repository
 | 
					# ALR - Any Linux Repository
 | 
				
			||||||
# Copyright (C) 2025 Евгений Храмов
 | 
					# Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is free software: you can redistribute it and/or modify
 | 
					# This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
# it under the terms of the GNU General Public License as published by
 | 
					# it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,28 +0,0 @@
 | 
				
			|||||||
# This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
 | 
					 | 
				
			||||||
# It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# ALR - Any Linux Repository
 | 
					 | 
				
			||||||
# Copyright (C) 2025 Евгений Храмов
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# This program is free software: you can redistribute it and/or modify
 | 
					 | 
				
			||||||
# it under the terms of the GNU General Public License as published by
 | 
					 | 
				
			||||||
# the Free Software Foundation, either version 3 of the License, or
 | 
					 | 
				
			||||||
# (at your option) any later version.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# This program is distributed in the hope that it will be useful,
 | 
					 | 
				
			||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					 | 
				
			||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					 | 
				
			||||||
# GNU General Public License for more details.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# You should have received a copy of the GNU General Public License
 | 
					 | 
				
			||||||
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
platform: linux/amd64
 | 
					 | 
				
			||||||
pipeline:
 | 
					 | 
				
			||||||
  release:
 | 
					 | 
				
			||||||
    image: goreleaser/goreleaser
 | 
					 | 
				
			||||||
    commands:
 | 
					 | 
				
			||||||
      - goreleaser release
 | 
					 | 
				
			||||||
    secrets: [ gitea_token, aur_key ]
 | 
					 | 
				
			||||||
    when:
 | 
					 | 
				
			||||||
      event: tag
 | 
					 | 
				
			||||||
							
								
								
									
										47
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										47
									
								
								Makefile
									
									
									
									
									
								
							@@ -1,16 +1,21 @@
 | 
				
			|||||||
NAME := alr
 | 
					NAME := alr
 | 
				
			||||||
GIT_VERSION = $(shell git describe --tags )
 | 
					GIT_VERSION ?= $(shell git describe --tags )
 | 
				
			||||||
 | 
					IGNORE_ROOT_CHECK ?= 0
 | 
				
			||||||
DESTDIR ?=
 | 
					DESTDIR ?=
 | 
				
			||||||
PREFIX ?= /usr/local
 | 
					PREFIX ?= /usr/local
 | 
				
			||||||
BIN := ./$(NAME)
 | 
					BIN := ./$(NAME)
 | 
				
			||||||
INSTALED_BIN := $(DESTDIR)/$(PREFIX)/bin/$(NAME)
 | 
					INSTALLED_BIN := $(DESTDIR)/$(PREFIX)/bin/$(NAME)
 | 
				
			||||||
COMPLETIONS_DIR := ./scripts/completion
 | 
					COMPLETIONS_DIR := ./scripts/completion
 | 
				
			||||||
BASH_COMPLETION := $(COMPLETIONS_DIR)/bash
 | 
					BASH_COMPLETION := $(COMPLETIONS_DIR)/bash
 | 
				
			||||||
ZSH_COMPLETION := $(COMPLETIONS_DIR)/zsh
 | 
					ZSH_COMPLETION := $(COMPLETIONS_DIR)/zsh
 | 
				
			||||||
INSTALLED_BASH_COMPLETION := $(DESTDIR)$(PREFIX)/share/bash-completion/completions/$(NAME)
 | 
					INSTALLED_BASH_COMPLETION := $(DESTDIR)$(PREFIX)/share/bash-completion/completions/$(NAME)
 | 
				
			||||||
INSTALLED_ZSH_COMPLETION := $(DESTDIR)$(PREFIX)/share/zsh/site-functions/_$(NAME)
 | 
					INSTALLED_ZSH_COMPLETION := $(DESTDIR)$(PREFIX)/share/zsh/site-functions/_$(NAME)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					GENERATE ?= 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CREATE_SYSTEM_RESOURCES ?= 1
 | 
				
			||||||
 | 
					ROOT_DIRS := /var/cache/alr /etc/alr
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ADD_LICENSE_BIN := go run github.com/google/addlicense@4caba19b7ed7818bb86bc4cd20411a246aa4a524
 | 
					ADD_LICENSE_BIN := go run github.com/google/addlicense@4caba19b7ed7818bb86bc4cd20411a246aa4a524
 | 
				
			||||||
GOLANGCI_LINT_BIN := go run github.com/golangci/golangci-lint/cmd/golangci-lint@v1.63.4
 | 
					GOLANGCI_LINT_BIN := go run github.com/golangci/golangci-lint/cmd/golangci-lint@v1.63.4
 | 
				
			||||||
XGOTEXT_BIN := go run github.com/Tom5521/xgotext@v1.2.0
 | 
					XGOTEXT_BIN := go run github.com/Tom5521/xgotext@v1.2.0
 | 
				
			||||||
@@ -21,22 +26,41 @@ build: check-no-root $(BIN)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export CGO_ENABLED := 0
 | 
					export CGO_ENABLED := 0
 | 
				
			||||||
$(BIN):
 | 
					$(BIN):
 | 
				
			||||||
 | 
					ifeq ($(GENERATE),1)
 | 
				
			||||||
 | 
						go generate ./...
 | 
				
			||||||
 | 
					else
 | 
				
			||||||
 | 
						@echo "Skipping go generate (GENERATE=0)"
 | 
				
			||||||
 | 
					endif
 | 
				
			||||||
	go build -ldflags="-X 'gitea.plemya-x.ru/Plemya-x/ALR/internal/config.Version=$(GIT_VERSION)'" -o $@
 | 
						go build -ldflags="-X 'gitea.plemya-x.ru/Plemya-x/ALR/internal/config.Version=$(GIT_VERSION)'" -o $@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
check-no-root:
 | 
					check-no-root:
 | 
				
			||||||
	@if [[ "$$(whoami)" == 'root' ]]; then \
 | 
						@if [ "$$IGNORE_ROOT_CHECK" != "1" ] && [ "`whoami`" = "root" ]; then \
 | 
				
			||||||
		echo "This target shouldn't run as root" 1>&2; \
 | 
							echo "This target shouldn't run as root" 1>&2; \
 | 
				
			||||||
 | 
							echo "Set IGNORE_ROOT_CHECK=1 to override" 1>&2; \
 | 
				
			||||||
		exit 1; \
 | 
							exit 1; \
 | 
				
			||||||
	fi
 | 
						fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
install: \
 | 
					install: \
 | 
				
			||||||
	$(INSTALED_BIN) \
 | 
						$(INSTALLED_BIN) \
 | 
				
			||||||
	$(INSTALLED_BASH_COMPLETION) \
 | 
						$(INSTALLED_BASH_COMPLETION) \
 | 
				
			||||||
	$(INSTALLED_ZSH_COMPLETION)
 | 
						$(INSTALLED_ZSH_COMPLETION)
 | 
				
			||||||
	@echo "Installation done!"
 | 
						@echo "Installation done!"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
$(INSTALED_BIN): $(BIN)
 | 
					$(INSTALLED_BIN): $(BIN)
 | 
				
			||||||
	install -Dm755 $< $@
 | 
						install -Dm755 $< $@
 | 
				
			||||||
 | 
					ifeq ($(CREATE_SYSTEM_RESOURCES),1)
 | 
				
			||||||
 | 
						setcap cap_setuid,cap_setgid+ep $(INSTALLED_BIN)
 | 
				
			||||||
 | 
						@if id alr >/dev/null 2>&1; then \
 | 
				
			||||||
 | 
							echo "User 'alr' already exists. Skipping."; \
 | 
				
			||||||
 | 
						else \
 | 
				
			||||||
 | 
							useradd -r -s /usr/sbin/nologin alr; \
 | 
				
			||||||
 | 
						fi
 | 
				
			||||||
 | 
						@for dir in $(ROOT_DIRS); do \
 | 
				
			||||||
 | 
							install -d -o alr -g alr -m 755 $$dir; \
 | 
				
			||||||
 | 
						done
 | 
				
			||||||
 | 
					else
 | 
				
			||||||
 | 
						@echo "Skipping user and root dir creation (CREATE_SYSTEM_RESOURCES=0)"
 | 
				
			||||||
 | 
					endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
$(INSTALLED_BASH_COMPLETION): $(BASH_COMPLETION)
 | 
					$(INSTALLED_BASH_COMPLETION): $(BASH_COMPLETION)
 | 
				
			||||||
	install -Dm755 $< $@
 | 
						install -Dm755 $< $@
 | 
				
			||||||
@@ -46,14 +70,14 @@ $(INSTALLED_ZSH_COMPLETION): $(ZSH_COMPLETION)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
uninstall:
 | 
					uninstall:
 | 
				
			||||||
	rm -f \
 | 
						rm -f \
 | 
				
			||||||
		$(INSTALED_BIN) \
 | 
							$(INSTALLED_BIN) \
 | 
				
			||||||
		$(INSTALLED_BASH_COMPLETION) \
 | 
							$(INSTALLED_BASH_COMPLETION) \
 | 
				
			||||||
		$(INSTALLED_ZSH_COMPLETION)
 | 
							$(INSTALLED_ZSH_COMPLETION)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
clean clear:
 | 
					clean clear:
 | 
				
			||||||
	rm -f $(BIN)
 | 
						rm -f $(BIN)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
OLD_FILES=$$(< old-files)
 | 
					OLD_FILES=$(shell cat old-files)
 | 
				
			||||||
IGNORE_OLD_FILES := $(foreach file,$(shell cat old-files),-ignore $(file))
 | 
					IGNORE_OLD_FILES := $(foreach file,$(shell cat old-files),-ignore $(file))
 | 
				
			||||||
update-license:
 | 
					update-license:
 | 
				
			||||||
	$(ADD_LICENSE_BIN) -v -f license-header-old-files.tmpl $(OLD_FILES)
 | 
						$(ADD_LICENSE_BIN) -v -f license-header-old-files.tmpl $(OLD_FILES)
 | 
				
			||||||
@@ -72,7 +96,12 @@ test-coverage:
 | 
				
			|||||||
	go test ./... -v -coverpkg=./... -coverprofile=coverage.out
 | 
						go test ./... -v -coverpkg=./... -coverprofile=coverage.out
 | 
				
			||||||
	bash scripts/coverage-badge.sh
 | 
						bash scripts/coverage-badge.sh
 | 
				
			||||||
 | 
					
 | 
				
			||||||
e2e-test: clean build
 | 
					update-deps-cve:
 | 
				
			||||||
 | 
						bash scripts/update-deps-cve.sh
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					prepare-for-e2e-test: clean build
 | 
				
			||||||
	rm -f ./e2e-tests/alr
 | 
						rm -f ./e2e-tests/alr
 | 
				
			||||||
	cp alr e2e-tests
 | 
						cp alr e2e-tests
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					e2e-test: prepare-for-e2e-test
 | 
				
			||||||
	go test -tags=e2e ./...
 | 
						go test -tags=e2e ./...
 | 
				
			||||||
							
								
								
									
										16
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								README.md
									
									
									
									
									
								
							@@ -20,10 +20,10 @@ ALR написан на чистом Go и после сборки не имее
 | 
				
			|||||||
Установочный скрипт автоматически загрузит и установит соответствующий пакет ALR в вашей системе. Чтобы использовать его, просто выполните следующую команду:
 | 
					Установочный скрипт автоматически загрузит и установит соответствующий пакет ALR в вашей системе. Чтобы использовать его, просто выполните следующую команду:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```bash
 | 
					```bash
 | 
				
			||||||
curl -fsSL plemya-x.ru/alr/install.sh | bash
 | 
					curl -fsSL https://gitea.plemya-x.ru/Plemya-x/ALR/raw/branch/master/scripts/install.sh | bash
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
**ВАЖНО**: При этом скрипт будет загружен и запущен с <https://plemya-x.ru/alr/install.sh>. Пожалуйста, просматривайте любые скрипты, которые вы скачиваете из Интернета (включая этот), прежде чем запускать их.
 | 
					**ВАЖНО**: При этом скрипт будет загружен и запущен [скрипт](https://gitea.plemya-x.ru/Plemya-x/ALR/src/branch/master/scripts/install.sh). Пожалуйста, просматривайте любые скрипты, которые вы скачиваете из Интернета (включая этот), прежде чем запускать их.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Сборка из исходного кода
 | 
					### Сборка из исходного кода
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -52,9 +52,17 @@ ALR был создан потому, что упаковка программн
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
Репозитории alr - это git-хранилища, которые содержат каталог для каждого пакета с файлом `alr.sh` внутри. Файл `alr.sh` содержит все инструкции по сборке пакета и информацию о нем. Скрипты `alr.sh` аналогичны скриптам Aur PKGBUILD. 
 | 
					Репозитории alr - это git-хранилища, которые содержат каталог для каждого пакета с файлом `alr.sh` внутри. Файл `alr.sh` содержит все инструкции по сборке пакета и информацию о нем. Скрипты `alr.sh` аналогичны скриптам Aur PKGBUILD. 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Например, репозиторий [Plemya-x/alr-repo](https://gitea.plemya-x.ru/Plemya-x/alr-repo.git) можно подключить так:
 | 
					Например, репозиторий с ALR [Plemya-x/alr-default](https://gitea.plemya-x.ru/Plemya-x/alr-default.git)
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
alr addrepo --name alr-repo --url https://gitea.plemya-x.ru/Plemya-x/alr-repo.git
 | 
					alr repo add alr-default https://gitea.plemya-x.ru/Plemya-x/alr-default.git
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					Репозиторий пакетов [Plemya-x/alr-repo](https://gitea.plemya-x.ru/Plemya-x/alr-repo.git) можно подключить так:
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					alr repo add  alr-repo https://gitea.plemya-x.ru/Plemya-x/alr-repo.git
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					Репозиторий Linux-Gaming [Plemya-x/alr-LG](https://gitea.plemya-x.ru/Plemya-x/alr-LG.git) можно подключить так:
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					alr repo add alr-LG https://gitea.plemya-x.ru/Plemya-x/alr-LG.git
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
---
 | 
					---
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,7 +11,7 @@
 | 
				
			|||||||
    <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
 | 
					    <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
 | 
				
			||||||
        <text x="33.5" y="15" fill="#010101" fill-opacity=".3">coverage</text>
 | 
					        <text x="33.5" y="15" fill="#010101" fill-opacity=".3">coverage</text>
 | 
				
			||||||
        <text x="33.5" y="14">coverage</text>
 | 
					        <text x="33.5" y="14">coverage</text>
 | 
				
			||||||
        <text x="86" y="15" fill="#010101" fill-opacity=".3">19.4%</text>
 | 
					        <text x="86" y="15" fill="#010101" fill-opacity=".3">18.9%</text>
 | 
				
			||||||
        <text x="86" y="14">19.4%</text>
 | 
					        <text x="86" y="14">18.9%</text>
 | 
				
			||||||
    </g>
 | 
					    </g>
 | 
				
			||||||
</svg>
 | 
					</svg>
 | 
				
			||||||
 
 | 
				
			|||||||
| 
		 Before Width: | Height: | Size: 926 B After Width: | Height: | Size: 926 B  | 
@@ -12,7 +12,7 @@
 | 
				
			|||||||
    <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
 | 
					    <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
 | 
				
			||||||
        <text x="37" y="15" fill="#010101" fill-opacity=".3">ru translate</text>
 | 
					        <text x="37" y="15" fill="#010101" fill-opacity=".3">ru translate</text>
 | 
				
			||||||
        <text x="37" y="14">ru translate</text>
 | 
					        <text x="37" y="14">ru translate</text>
 | 
				
			||||||
        <text x="100" y="15" fill="#010101" fill-opacity=".3">97.00%</text>
 | 
					        <text x="100" y="15" fill="#010101" fill-opacity=".3">100.00%</text>
 | 
				
			||||||
        <text x="100" y="14">97.00%</text>
 | 
					        <text x="100" y="14">100.00%</text>
 | 
				
			||||||
    </g>
 | 
					    </g>
 | 
				
			||||||
</svg>
 | 
					</svg>
 | 
				
			||||||
 
 | 
				
			|||||||
| 
		 Before Width: | Height: | Size: 940 B After Width: | Height: | Size: 942 B  | 
							
								
								
									
										207
									
								
								build.go
									
									
									
									
									
								
							
							
						
						
									
										207
									
								
								build.go
									
									
									
									
									
								
							@@ -1,8 +1,8 @@
 | 
				
			|||||||
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
 | 
					// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
 | 
				
			||||||
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
 | 
					// It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// ALR - Any Linux Repository
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
// Copyright (C) 2025 Евгений Храмов
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// This program is free software: you can redistribute it and/or modify
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
// it under the terms of the GNU General Public License as published by
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
@@ -23,19 +23,16 @@ import (
 | 
				
			|||||||
	"log/slog"
 | 
						"log/slog"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
	"strings"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/leonelquinteros/gotext"
 | 
						"github.com/leonelquinteros/gotext"
 | 
				
			||||||
	"github.com/urfave/cli/v2"
 | 
						"github.com/urfave/cli/v2"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"gitea.plemya-x.ru/Plemya-x/ALR/internal/config"
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/build"
 | 
				
			||||||
	database "gitea.plemya-x.ru/Plemya-x/ALR/internal/db"
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils"
 | 
				
			||||||
 | 
						appbuilder "gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils/app_builder"
 | 
				
			||||||
	"gitea.plemya-x.ru/Plemya-x/ALR/internal/osutils"
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/osutils"
 | 
				
			||||||
	"gitea.plemya-x.ru/Plemya-x/ALR/internal/types"
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/utils"
 | 
				
			||||||
	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/build"
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/pkg/types"
 | 
				
			||||||
	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro"
 | 
					 | 
				
			||||||
	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager"
 | 
					 | 
				
			||||||
	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/repos"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func BuildCmd() *cli.Command {
 | 
					func BuildCmd() *cli.Command {
 | 
				
			||||||
@@ -66,124 +63,166 @@ func BuildCmd() *cli.Command {
 | 
				
			|||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		Action: func(c *cli.Context) error {
 | 
							Action: func(c *cli.Context) error {
 | 
				
			||||||
			ctx := c.Context
 | 
								if err := utils.EnuseIsPrivilegedGroupMember(); err != nil {
 | 
				
			||||||
			cfg := config.New()
 | 
									return err
 | 
				
			||||||
			err := cfg.Load()
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				slog.Error(gotext.Get("Error loading config"), "err", err)
 | 
					 | 
				
			||||||
				os.Exit(1)
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			db := database.New(cfg)
 | 
								wd, err := os.Getwd()
 | 
				
			||||||
			rs := repos.New(cfg, db)
 | 
					 | 
				
			||||||
			err = db.Init(ctx)
 | 
					 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				slog.Error(gotext.Get("Error initialization database"), "err", err)
 | 
									return cliutils.FormatCliExit(gotext.Get("Error getting working directory"), err)
 | 
				
			||||||
				os.Exit(1)
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								wd, wdCleanup, err := Mount(wd)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								defer wdCleanup()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								ctx := c.Context
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								deps, err := appbuilder.
 | 
				
			||||||
 | 
									New(ctx).
 | 
				
			||||||
 | 
									WithConfig().
 | 
				
			||||||
 | 
									WithDB().
 | 
				
			||||||
 | 
									WithReposNoPull().
 | 
				
			||||||
 | 
									WithDistroInfo().
 | 
				
			||||||
 | 
									WithManager().
 | 
				
			||||||
 | 
									Build()
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return cli.Exit(err, 1)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								defer deps.Defer()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			var script string
 | 
								var script string
 | 
				
			||||||
			var packages []string
 | 
								var packages []string
 | 
				
			||||||
			repository := "default"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
			repoDir := cfg.GetPaths().RepoDir
 | 
								var res []*build.BuiltDep
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								var scriptArgs *build.BuildPackageFromScriptArgs
 | 
				
			||||||
 | 
								var dbArgs *build.BuildPackageFromDbArgs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								buildArgs := &build.BuildArgs{
 | 
				
			||||||
 | 
									Opts: &types.BuildOpts{
 | 
				
			||||||
 | 
										Clean:       c.Bool("clean"),
 | 
				
			||||||
 | 
										Interactive: c.Bool("interactive"),
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									PkgFormat_: build.GetPkgFormat(deps.Manager),
 | 
				
			||||||
 | 
									Info:       deps.Info,
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			switch {
 | 
								switch {
 | 
				
			||||||
			case c.IsSet("script"):
 | 
								case c.IsSet("script"):
 | 
				
			||||||
				script = c.String("script")
 | 
									script, err = filepath.Abs(c.String("script"))
 | 
				
			||||||
				packages = append(packages, c.String("script-package"))
 | 
									if err != nil {
 | 
				
			||||||
 | 
										return cliutils.FormatCliExit(gotext.Get("Cannot get absolute script path"), err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									subpackage := c.String("subpackage")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if subpackage != "" {
 | 
				
			||||||
 | 
										packages = append(packages, subpackage)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									scriptArgs = &build.BuildPackageFromScriptArgs{
 | 
				
			||||||
 | 
										Script:    script,
 | 
				
			||||||
 | 
										Packages:  packages,
 | 
				
			||||||
 | 
										BuildArgs: *buildArgs,
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
			case c.IsSet("package"):
 | 
								case c.IsSet("package"):
 | 
				
			||||||
				// TODO: handle multiple packages
 | 
									// TODO: handle multiple packages
 | 
				
			||||||
				packageInput := c.String("package")
 | 
									packageInput := c.String("package")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				arr := strings.Split(packageInput, "/")
 | 
									pkgs, _, err := deps.Repos.FindPkgs(ctx, []string{packageInput})
 | 
				
			||||||
				var packageSearch string
 | 
									if err != nil {
 | 
				
			||||||
				if len(arr) == 2 {
 | 
										return cliutils.FormatCliExit("failed to find pkgs", err)
 | 
				
			||||||
					packageSearch = arr[1]
 | 
					 | 
				
			||||||
				} else {
 | 
					 | 
				
			||||||
					packageSearch = arr[0]
 | 
					 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				pkgs, _, _ := rs.FindPkgs(ctx, []string{packageSearch})
 | 
									pkg := cliutils.FlattenPkgs(ctx, pkgs, "build", c.Bool("interactive"))
 | 
				
			||||||
				pkg, ok := pkgs[packageSearch]
 | 
					 | 
				
			||||||
				if len(pkg) < 1 || !ok {
 | 
					 | 
				
			||||||
					slog.Error(gotext.Get("Package not found"))
 | 
					 | 
				
			||||||
					os.Exit(1)
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
				repository = pkg[0].Repository
 | 
									if len(pkg) < 1 {
 | 
				
			||||||
 | 
										return cliutils.FormatCliExit(gotext.Get("Package not found"), nil)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				if pkg[0].BasePkgName != "" {
 | 
									if pkg[0].BasePkgName != "" {
 | 
				
			||||||
					script = filepath.Join(repoDir, repository, pkg[0].BasePkgName, "alr.sh")
 | 
					 | 
				
			||||||
					packages = append(packages, pkg[0].Name)
 | 
										packages = append(packages, pkg[0].Name)
 | 
				
			||||||
				} else {
 | 
									}
 | 
				
			||||||
					script = filepath.Join(repoDir, repository, pkg[0].Name, "alr.sh")
 | 
					
 | 
				
			||||||
 | 
									dbArgs = &build.BuildPackageFromDbArgs{
 | 
				
			||||||
 | 
										Package:   &pkg[0],
 | 
				
			||||||
 | 
										Packages:  packages,
 | 
				
			||||||
 | 
										BuildArgs: *buildArgs,
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			default:
 | 
								default:
 | 
				
			||||||
				script = filepath.Join(repoDir, "alr.sh")
 | 
									return cliutils.FormatCliExit(gotext.Get("Nothing to build"), nil)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Проверка автоматического пулла репозиториев
 | 
								if scriptArgs != nil {
 | 
				
			||||||
			if cfg.AutoPull() {
 | 
									scriptFile := filepath.Base(scriptArgs.Script)
 | 
				
			||||||
				err := rs.Pull(ctx, cfg.Repos())
 | 
									newScriptDir, scriptDirCleanup, err := Mount(filepath.Dir(scriptArgs.Script))
 | 
				
			||||||
				if err != nil {
 | 
									if err != nil {
 | 
				
			||||||
					slog.Error(gotext.Get("Error pulling repositories"), "err", err)
 | 
										return err
 | 
				
			||||||
					os.Exit(1)
 | 
					 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
									defer scriptDirCleanup()
 | 
				
			||||||
 | 
									scriptArgs.Script = filepath.Join(newScriptDir, scriptFile)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Обнаружение менеджера пакетов
 | 
								if err := utils.ExitIfCantDropCapsToAlrUser(); err != nil {
 | 
				
			||||||
			mgr := manager.Detect()
 | 
									return err
 | 
				
			||||||
			if mgr == nil {
 | 
					 | 
				
			||||||
				slog.Error(gotext.Get("Unable to detect a supported package manager on the system"))
 | 
					 | 
				
			||||||
				os.Exit(1)
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			info, err := distro.ParseOSRelease(ctx)
 | 
								installer, installerClose, err := build.GetSafeInstaller()
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				slog.Error(gotext.Get("Error parsing os release"), "err", err)
 | 
									return err
 | 
				
			||||||
				os.Exit(1)
 | 
								}
 | 
				
			||||||
 | 
								defer installerClose()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if err := utils.ExitIfCantSetNoNewPrivs(); err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			builder := build.NewBuilder(
 | 
								scripter, scripterClose, err := build.GetSafeScriptExecutor()
 | 
				
			||||||
				ctx,
 | 
								if err != nil {
 | 
				
			||||||
				types.BuildOpts{
 | 
									return err
 | 
				
			||||||
					Packages:    packages,
 | 
								}
 | 
				
			||||||
					Repository:  repository,
 | 
								defer scripterClose()
 | 
				
			||||||
					Script:      script,
 | 
					
 | 
				
			||||||
					Manager:     mgr,
 | 
								builder, err := build.NewMainBuilder(
 | 
				
			||||||
					Clean:       c.Bool("clean"),
 | 
									deps.Cfg,
 | 
				
			||||||
					Interactive: c.Bool("interactive"),
 | 
									deps.Manager,
 | 
				
			||||||
				},
 | 
									deps.Repos,
 | 
				
			||||||
				rs,
 | 
									scripter,
 | 
				
			||||||
				info,
 | 
									installer,
 | 
				
			||||||
				cfg,
 | 
					 | 
				
			||||||
			)
 | 
								)
 | 
				
			||||||
 | 
					 | 
				
			||||||
			// Сборка пакета
 | 
					 | 
				
			||||||
			pkgPaths, _, err := builder.BuildPackage(ctx)
 | 
					 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				slog.Error(gotext.Get("Error building package"), "err", err)
 | 
									return err
 | 
				
			||||||
				os.Exit(1)
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Получение текущей рабочей директории
 | 
								if scriptArgs != nil {
 | 
				
			||||||
			wd, err := os.Getwd()
 | 
									res, err = builder.BuildPackageFromScript(
 | 
				
			||||||
			if err != nil {
 | 
										ctx,
 | 
				
			||||||
				slog.Error(gotext.Get("Error getting working directory"), "err", err)
 | 
										scriptArgs,
 | 
				
			||||||
				os.Exit(1)
 | 
									)
 | 
				
			||||||
 | 
								} else if dbArgs != nil {
 | 
				
			||||||
 | 
									res, err = builder.BuildPackageFromDb(
 | 
				
			||||||
 | 
										ctx,
 | 
				
			||||||
 | 
										dbArgs,
 | 
				
			||||||
 | 
									)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Перемещение собранных пакетов в рабочую директорию
 | 
					 | 
				
			||||||
			for _, pkgPath := range pkgPaths {
 | 
					 | 
				
			||||||
				name := filepath.Base(pkgPath)
 | 
					 | 
				
			||||||
				err = osutils.Move(pkgPath, filepath.Join(wd, name))
 | 
					 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
					slog.Error(gotext.Get("Error moving the package"), "err", err)
 | 
									return cliutils.FormatCliExit(gotext.Get("Error building package"), err)
 | 
				
			||||||
					os.Exit(1)
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								for _, pkg := range res {
 | 
				
			||||||
 | 
									name := filepath.Base(pkg.Path)
 | 
				
			||||||
 | 
									err = osutils.Move(pkg.Path, filepath.Join(wd, name))
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										return cliutils.FormatCliExit(gotext.Get("Error moving the package"), err)
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								slog.Info(gotext.Get("Done"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			return nil
 | 
								return nil
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										227
									
								
								config.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										227
									
								
								config.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,227 @@
 | 
				
			|||||||
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/goccy/go-yaml"
 | 
				
			||||||
 | 
						"github.com/leonelquinteros/gotext"
 | 
				
			||||||
 | 
						"github.com/urfave/cli/v2"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils"
 | 
				
			||||||
 | 
						appbuilder "gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils/app_builder"
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/utils"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func ConfigCmd() *cli.Command {
 | 
				
			||||||
 | 
						return &cli.Command{
 | 
				
			||||||
 | 
							Name:  "config",
 | 
				
			||||||
 | 
							Usage: gotext.Get("Manage config"),
 | 
				
			||||||
 | 
							Subcommands: []*cli.Command{
 | 
				
			||||||
 | 
								ShowCmd(),
 | 
				
			||||||
 | 
								SetConfig(),
 | 
				
			||||||
 | 
								GetConfig(),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func ShowCmd() *cli.Command {
 | 
				
			||||||
 | 
						return &cli.Command{
 | 
				
			||||||
 | 
							Name:  "show",
 | 
				
			||||||
 | 
							Usage: gotext.Get("Show config"),
 | 
				
			||||||
 | 
							BashComplete: cliutils.BashCompleteWithError(func(c *cli.Context) error {
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							}),
 | 
				
			||||||
 | 
							Action: func(c *cli.Context) error {
 | 
				
			||||||
 | 
								deps, err := appbuilder.
 | 
				
			||||||
 | 
									New(c.Context).
 | 
				
			||||||
 | 
									WithConfig().
 | 
				
			||||||
 | 
									Build()
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								defer deps.Defer()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								content, err := deps.Cfg.ToYAML()
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								fmt.Println(content)
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var configKeys = []string{
 | 
				
			||||||
 | 
						"rootCmd",
 | 
				
			||||||
 | 
						"useRootCmd",
 | 
				
			||||||
 | 
						"pagerStyle",
 | 
				
			||||||
 | 
						"autoPull",
 | 
				
			||||||
 | 
						"logLevel",
 | 
				
			||||||
 | 
						"ignorePkgUpdates",
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func SetConfig() *cli.Command {
 | 
				
			||||||
 | 
						return &cli.Command{
 | 
				
			||||||
 | 
							Name:      "set",
 | 
				
			||||||
 | 
							Usage:     gotext.Get("Set config value"),
 | 
				
			||||||
 | 
							ArgsUsage: gotext.Get("<key> <value>"),
 | 
				
			||||||
 | 
							BashComplete: cliutils.BashCompleteWithError(func(c *cli.Context) error {
 | 
				
			||||||
 | 
								if c.Args().Len() == 0 {
 | 
				
			||||||
 | 
									for _, key := range configKeys {
 | 
				
			||||||
 | 
										fmt.Println(key)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									return nil
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							}),
 | 
				
			||||||
 | 
							Action: utils.RootNeededAction(func(c *cli.Context) error {
 | 
				
			||||||
 | 
								if c.Args().Len() < 2 {
 | 
				
			||||||
 | 
									return cliutils.FormatCliExit("missing args", nil)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								key := c.Args().Get(0)
 | 
				
			||||||
 | 
								value := c.Args().Get(1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								deps, err := appbuilder.
 | 
				
			||||||
 | 
									New(c.Context).
 | 
				
			||||||
 | 
									WithConfig().
 | 
				
			||||||
 | 
									Build()
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								defer deps.Defer()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								switch key {
 | 
				
			||||||
 | 
								case "rootCmd":
 | 
				
			||||||
 | 
									deps.Cfg.System.SetRootCmd(value)
 | 
				
			||||||
 | 
								case "useRootCmd":
 | 
				
			||||||
 | 
									boolValue, err := strconv.ParseBool(value)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										return cliutils.FormatCliExit(gotext.Get("invalid boolean value for %s: %s", key, value), err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									deps.Cfg.System.SetUseRootCmd(boolValue)
 | 
				
			||||||
 | 
								case "pagerStyle":
 | 
				
			||||||
 | 
									deps.Cfg.System.SetPagerStyle(value)
 | 
				
			||||||
 | 
								case "autoPull":
 | 
				
			||||||
 | 
									boolValue, err := strconv.ParseBool(value)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										return cliutils.FormatCliExit(gotext.Get("invalid boolean value for %s: %s", key, value), err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									deps.Cfg.System.SetAutoPull(boolValue)
 | 
				
			||||||
 | 
								case "logLevel":
 | 
				
			||||||
 | 
									deps.Cfg.System.SetLogLevel(value)
 | 
				
			||||||
 | 
								case "ignorePkgUpdates":
 | 
				
			||||||
 | 
									var updates []string
 | 
				
			||||||
 | 
									if value != "" {
 | 
				
			||||||
 | 
										updates = strings.Split(value, ",")
 | 
				
			||||||
 | 
										for i, update := range updates {
 | 
				
			||||||
 | 
											updates[i] = strings.TrimSpace(update)
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									deps.Cfg.System.SetIgnorePkgUpdates(updates)
 | 
				
			||||||
 | 
								case "repo", "repos":
 | 
				
			||||||
 | 
									return cliutils.FormatCliExit(gotext.Get("use 'repo add/remove' commands to manage repositories"), nil)
 | 
				
			||||||
 | 
								default:
 | 
				
			||||||
 | 
									return cliutils.FormatCliExit(gotext.Get("unknown config key: %s", key), nil)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if err := deps.Cfg.System.Save(); err != nil {
 | 
				
			||||||
 | 
									return cliutils.FormatCliExit(gotext.Get("failed to save config"), err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								fmt.Println(gotext.Get("Successfully set %s = %s", key, value))
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							}),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func GetConfig() *cli.Command {
 | 
				
			||||||
 | 
						return &cli.Command{
 | 
				
			||||||
 | 
							Name:      "get",
 | 
				
			||||||
 | 
							Usage:     gotext.Get("Get config value"),
 | 
				
			||||||
 | 
							ArgsUsage: gotext.Get("<key>"),
 | 
				
			||||||
 | 
							BashComplete: cliutils.BashCompleteWithError(func(c *cli.Context) error {
 | 
				
			||||||
 | 
								if c.Args().Len() == 0 {
 | 
				
			||||||
 | 
									for _, key := range configKeys {
 | 
				
			||||||
 | 
										fmt.Println(key)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									return nil
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							}),
 | 
				
			||||||
 | 
							Action: func(c *cli.Context) error {
 | 
				
			||||||
 | 
								deps, err := appbuilder.
 | 
				
			||||||
 | 
									New(c.Context).
 | 
				
			||||||
 | 
									WithConfig().
 | 
				
			||||||
 | 
									Build()
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								defer deps.Defer()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if c.Args().Len() == 0 {
 | 
				
			||||||
 | 
									content, err := deps.Cfg.ToYAML()
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										return cliutils.FormatCliExit("failed to serialize config", err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									fmt.Print(content)
 | 
				
			||||||
 | 
									return nil
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								key := c.Args().Get(0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								switch key {
 | 
				
			||||||
 | 
								case "rootCmd":
 | 
				
			||||||
 | 
									fmt.Println(deps.Cfg.RootCmd())
 | 
				
			||||||
 | 
								case "useRootCmd":
 | 
				
			||||||
 | 
									fmt.Println(deps.Cfg.UseRootCmd())
 | 
				
			||||||
 | 
								case "pagerStyle":
 | 
				
			||||||
 | 
									fmt.Println(deps.Cfg.PagerStyle())
 | 
				
			||||||
 | 
								case "autoPull":
 | 
				
			||||||
 | 
									fmt.Println(deps.Cfg.AutoPull())
 | 
				
			||||||
 | 
								case "logLevel":
 | 
				
			||||||
 | 
									fmt.Println(deps.Cfg.LogLevel())
 | 
				
			||||||
 | 
								case "ignorePkgUpdates":
 | 
				
			||||||
 | 
									updates := deps.Cfg.IgnorePkgUpdates()
 | 
				
			||||||
 | 
									if len(updates) == 0 {
 | 
				
			||||||
 | 
										fmt.Println("[]")
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										fmt.Println(strings.Join(updates, ", "))
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								case "repo", "repos":
 | 
				
			||||||
 | 
									repos := deps.Cfg.Repos()
 | 
				
			||||||
 | 
									if len(repos) == 0 {
 | 
				
			||||||
 | 
										fmt.Println("[]")
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										repoData, err := yaml.Marshal(repos)
 | 
				
			||||||
 | 
										if err != nil {
 | 
				
			||||||
 | 
											return cliutils.FormatCliExit("failed to serialize repos", err)
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										fmt.Print(string(repoData))
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								default:
 | 
				
			||||||
 | 
									return cliutils.FormatCliExit(gotext.Get("unknown config key: %s", key), nil)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					- name: alr-repo
 | 
				
			||||||
 | 
					  url: https://gitea.plemya-x.ru/Plemya-x/repo-for-tests
 | 
				
			||||||
 | 
					  ref: main
 | 
				
			||||||
 | 
					  mirrors:
 | 
				
			||||||
 | 
					  - https://github.com/example/example.git
 | 
				
			||||||
@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					alr-repo/foo-pkg 1.0.0-1
 | 
				
			||||||
@@ -0,0 +1,2 @@
 | 
				
			|||||||
 | 
					alr-repo/bar-pkg 1.0.0-1
 | 
				
			||||||
 | 
					alr-repo/foo-pkg 1.0.0-1
 | 
				
			||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
// ALR - Any Linux Repository
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
// Copyright (C) 2025 Евгений Храмов
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// This program is free software: you can redistribute it and/or modify
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
// it under the terms of the GNU General Public License as published by
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
@@ -19,52 +19,24 @@
 | 
				
			|||||||
package e2etests_test
 | 
					package e2etests_test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"bytes"
 | 
					 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/efficientgo/e2e"
 | 
						"go.alt-gnome.ru/capytest"
 | 
				
			||||||
	"github.com/stretchr/testify/assert"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestE2EAlrAddRepo(t *testing.T) {
 | 
					func TestE2EAlrAddRepo(t *testing.T) {
 | 
				
			||||||
	dockerMultipleRun(
 | 
						runMatrixSuite(
 | 
				
			||||||
		t,
 | 
							t,
 | 
				
			||||||
		"add-repo-remove-repo",
 | 
							"add-repo-remove-repo",
 | 
				
			||||||
		COMMON_SYSTEMS,
 | 
							COMMON_SYSTEMS,
 | 
				
			||||||
		func(t *testing.T, r e2e.Runnable) {
 | 
							func(t *testing.T, r capytest.Runner) {
 | 
				
			||||||
			err := r.Exec(e2e.NewCommand(
 | 
								execShouldNoError(t, r, "sudo", "alr", "addrepo", "--name", "alr-repo", "--url", "https://gitea.plemya-x.ru/Plemya-x/alr-repo.git")
 | 
				
			||||||
				"alr",
 | 
								execShouldNoError(t, r, "bash", "-c", "cat /etc/alr/alr.toml")
 | 
				
			||||||
				"addrepo",
 | 
								execShouldNoError(t, r, "sudo", "alr", "removerepo", "--name", "alr-repo")
 | 
				
			||||||
				"--name",
 | 
					 | 
				
			||||||
				"alr-repo",
 | 
					 | 
				
			||||||
				"--url",
 | 
					 | 
				
			||||||
				"https://gitea.plemya-x.ru/Plemya-x/alr-repo.git",
 | 
					 | 
				
			||||||
			))
 | 
					 | 
				
			||||||
			assert.NoError(t, err)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
			err = r.Exec(e2e.NewCommand(
 | 
								r.Command("bash", "-c", "cat /etc/alr/alr.toml").
 | 
				
			||||||
				"bash",
 | 
									ExpectStdoutContains("repo = []").
 | 
				
			||||||
				"-c",
 | 
									Run(t)
 | 
				
			||||||
				"cat $HOME/.config/alr/alr.toml",
 | 
					 | 
				
			||||||
			))
 | 
					 | 
				
			||||||
			assert.NoError(t, err)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			err = r.Exec(e2e.NewCommand(
 | 
					 | 
				
			||||||
				"alr",
 | 
					 | 
				
			||||||
				"removerepo",
 | 
					 | 
				
			||||||
				"--name",
 | 
					 | 
				
			||||||
				"alr-repo",
 | 
					 | 
				
			||||||
			))
 | 
					 | 
				
			||||||
			assert.NoError(t, err)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			var buf bytes.Buffer
 | 
					 | 
				
			||||||
			err = r.Exec(e2e.NewCommand(
 | 
					 | 
				
			||||||
				"bash",
 | 
					 | 
				
			||||||
				"-c",
 | 
					 | 
				
			||||||
				"cat $HOME/.config/alr/alr.toml",
 | 
					 | 
				
			||||||
			), e2e.WithExecOptionStdout(&buf))
 | 
					 | 
				
			||||||
			assert.NoError(t, err)
 | 
					 | 
				
			||||||
			assert.Contains(t, buf.String(), "rootCmd")
 | 
					 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
// ALR - Any Linux Repository
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
// Copyright (C) 2025 Евгений Храмов
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// This program is free software: you can redistribute it and/or modify
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
// it under the terms of the GNU General Public License as published by
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
@@ -21,20 +21,16 @@ package e2etests_test
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/alecthomas/assert/v2"
 | 
						"go.alt-gnome.ru/capytest"
 | 
				
			||||||
	"github.com/efficientgo/e2e"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestE2EBashCompletion(t *testing.T) {
 | 
					func TestE2EBashCompletion(t *testing.T) {
 | 
				
			||||||
	dockerMultipleRun(
 | 
						runMatrixSuite(
 | 
				
			||||||
		t,
 | 
							t,
 | 
				
			||||||
		"bash-completion",
 | 
							"bash-completion",
 | 
				
			||||||
		COMMON_SYSTEMS,
 | 
							COMMON_SYSTEMS,
 | 
				
			||||||
		func(t *testing.T, r e2e.Runnable) {
 | 
							func(t *testing.T, r capytest.Runner) {
 | 
				
			||||||
			err := r.Exec(e2e.NewCommand(
 | 
								execShouldNoError(t, r, "alr", "install", "--generate-bash-completion")
 | 
				
			||||||
				"alr", "install", "--generate-bash-completion",
 | 
					 | 
				
			||||||
			))
 | 
					 | 
				
			||||||
			assert.NoError(t, err)
 | 
					 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
// ALR - Any Linux Repository
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
// Copyright (C) 2025 Евгений Храмов
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// This program is free software: you can redistribute it and/or modify
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
// it under the terms of the GNU General Public License as published by
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
@@ -19,162 +19,78 @@
 | 
				
			|||||||
package e2etests_test
 | 
					package e2etests_test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"crypto/sha256"
 | 
					 | 
				
			||||||
	"encoding/hex"
 | 
					 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"io"
 | 
					 | 
				
			||||||
	"log"
 | 
					 | 
				
			||||||
	"os"
 | 
					 | 
				
			||||||
	"os/exec"
 | 
					 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
	"time"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/efficientgo/e2e"
 | 
						"go.alt-gnome.ru/capytest"
 | 
				
			||||||
	"github.com/stretchr/testify/assert"
 | 
						"go.alt-gnome.ru/capytest/providers/podman"
 | 
				
			||||||
 | 
					 | 
				
			||||||
	expect "github.com/tailscale/goexpect"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// DebugWriter оборачивает io.Writer и логирует все записываемые данные.
 | 
					 | 
				
			||||||
type DebugWriter struct {
 | 
					 | 
				
			||||||
	prefix string
 | 
					 | 
				
			||||||
	writer io.Writer
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (d *DebugWriter) Write(p []byte) (n int, err error) {
 | 
					 | 
				
			||||||
	log.Printf("%s: Writing data: %q", d.prefix, p) // Логируем данные
 | 
					 | 
				
			||||||
	return d.writer.Write(p)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// DebugReader оборачивает io.Reader и логирует все читаемые данные.
 | 
					 | 
				
			||||||
type DebugReader struct {
 | 
					 | 
				
			||||||
	prefix string
 | 
					 | 
				
			||||||
	reader io.Reader
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (d *DebugReader) Read(p []byte) (n int, err error) {
 | 
					 | 
				
			||||||
	n, err = d.reader.Read(p)
 | 
					 | 
				
			||||||
	if n > 0 {
 | 
					 | 
				
			||||||
		log.Printf("%s: Read data: %q", d.prefix, p[:n]) // Логируем данные
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return n, err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func e2eSpawn(runnable e2e.Runnable, command e2e.Command, timeout time.Duration, opts ...expect.Option) (expect.Expecter, <-chan error, error, *io.PipeWriter) {
 | 
					 | 
				
			||||||
	resCh := make(chan error)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Создаем pipe для stdin и stdout
 | 
					 | 
				
			||||||
	stdinReader, stdinWriter := io.Pipe()
 | 
					 | 
				
			||||||
	stdoutReader, stdoutWriter := io.Pipe()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	debugStdinReader := &DebugReader{prefix: "STDIN", reader: stdinReader}
 | 
					 | 
				
			||||||
	debugStdoutWriter := &DebugWriter{prefix: "STDOUT", writer: stdoutWriter}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	go func() {
 | 
					 | 
				
			||||||
		err := runnable.Exec(
 | 
					 | 
				
			||||||
			command,
 | 
					 | 
				
			||||||
			e2e.WithExecOptionStdout(debugStdoutWriter),
 | 
					 | 
				
			||||||
			e2e.WithExecOptionStdin(debugStdinReader),
 | 
					 | 
				
			||||||
			e2e.WithExecOptionStderr(debugStdoutWriter),
 | 
					 | 
				
			||||||
		)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		resCh <- err
 | 
					 | 
				
			||||||
	}()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	exp, chnErr, err := expect.SpawnGeneric(&expect.GenOptions{
 | 
					 | 
				
			||||||
		In:  stdinWriter,
 | 
					 | 
				
			||||||
		Out: stdoutReader,
 | 
					 | 
				
			||||||
		Wait: func() error {
 | 
					 | 
				
			||||||
			return <-resCh
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		Close: func() error {
 | 
					 | 
				
			||||||
			stdinWriter.Close()
 | 
					 | 
				
			||||||
			stdoutReader.Close()
 | 
					 | 
				
			||||||
			return nil
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		Check: func() bool { return true },
 | 
					 | 
				
			||||||
	}, timeout, expect.Verbose(true), expect.VerboseWriter(os.Stdout))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return exp, chnErr, err, stdinWriter
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var ALL_SYSTEMS []string = []string{
 | 
					var ALL_SYSTEMS []string = []string{
 | 
				
			||||||
	"ubuntu-24.04",
 | 
						"ubuntu-24.04",
 | 
				
			||||||
	// "alt-sisyphus",
 | 
						"alt-sisyphus",
 | 
				
			||||||
 | 
						"fedora-41",
 | 
				
			||||||
	// "archlinux",
 | 
						// "archlinux",
 | 
				
			||||||
	// "alpine",
 | 
						// "alpine",
 | 
				
			||||||
	// "opensuse-leap",
 | 
						// "opensuse-leap",
 | 
				
			||||||
	// "redos-8",
 | 
						// "redos-8",
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var AUTOREQ_AUTOPROV_SYSTEMS []string = []string{
 | 
				
			||||||
 | 
						// "alt-sisyphus",
 | 
				
			||||||
 | 
						"fedora-41",
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var RPM_SYSTEMS []string = []string{
 | 
				
			||||||
 | 
						"fedora-41",
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var COMMON_SYSTEMS []string = []string{
 | 
					var COMMON_SYSTEMS []string = []string{
 | 
				
			||||||
	"ubuntu-24.04",
 | 
						"ubuntu-24.04",
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func init() {
 | 
					func execShouldNoError(t *testing.T, r capytest.Runner, cmd string, args ...string) {
 | 
				
			||||||
	for _, id := range ALL_SYSTEMS {
 | 
						t.Helper()
 | 
				
			||||||
		buildAlrTestImage(id)
 | 
						r.Command(cmd, args...).ExpectSuccess().Run(t)
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func buildAlrTestImage(id string) {
 | 
					func execShouldError(t *testing.T, r capytest.Runner, cmd string, args ...string) {
 | 
				
			||||||
	cmd := exec.Command(
 | 
						t.Helper()
 | 
				
			||||||
		"docker",
 | 
						r.Command(cmd, args...).ExpectFailure().Run(t)
 | 
				
			||||||
		"build",
 | 
					 | 
				
			||||||
		"-t", fmt.Sprintf("alr-testimage-%s", id),
 | 
					 | 
				
			||||||
		"-f", fmt.Sprintf("images/Dockerfile.%s", id),
 | 
					 | 
				
			||||||
		".",
 | 
					 | 
				
			||||||
	)
 | 
					 | 
				
			||||||
	cmd.Stdout = os.Stdout
 | 
					 | 
				
			||||||
	cmd.Stderr = os.Stderr
 | 
					 | 
				
			||||||
	err := cmd.Run()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		fmt.Println("Error:", err)
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func dockerMultipleRun(t *testing.T, name string, ids []string, f func(t *testing.T, runnable e2e.Runnable)) {
 | 
					const REPO_NAME_FOR_E2E_TESTS = "alr-repo"
 | 
				
			||||||
	t.Run(name, func(t *testing.T) {
 | 
					const REPO_URL_FOR_E2E_TESTS = "https://gitea.plemya-x.ru/Plemya-x/repo-for-tests.git"
 | 
				
			||||||
		for _, id := range ids {
 | 
					
 | 
				
			||||||
			t.Run(id, func(t *testing.T) {
 | 
					func defaultPrepare(t *testing.T, r capytest.Runner) {
 | 
				
			||||||
				t.Parallel()
 | 
						execShouldNoError(t, r,
 | 
				
			||||||
				dockerName := fmt.Sprintf("alr-test-%s-%s", name, id)
 | 
							"sudo",
 | 
				
			||||||
				hash := sha256.New()
 | 
							"alr",
 | 
				
			||||||
				hash.Write([]byte(dockerName))
 | 
							"repo",
 | 
				
			||||||
				hashSum := hash.Sum(nil)
 | 
							"add",
 | 
				
			||||||
				hashString := hex.EncodeToString(hashSum)
 | 
							REPO_NAME_FOR_E2E_TESTS,
 | 
				
			||||||
				truncatedHash := hashString[:8]
 | 
							REPO_URL_FOR_E2E_TESTS,
 | 
				
			||||||
				e, err := e2e.New(e2e.WithVerbose(), e2e.WithName(fmt.Sprintf("alr-%s", truncatedHash)))
 | 
					 | 
				
			||||||
				assert.NoError(t, err)
 | 
					 | 
				
			||||||
				t.Cleanup(e.Close)
 | 
					 | 
				
			||||||
				imageId := fmt.Sprintf("alr-testimage-%s", id)
 | 
					 | 
				
			||||||
				runnable := e.Runnable(dockerName).Init(
 | 
					 | 
				
			||||||
					e2e.StartOptions{
 | 
					 | 
				
			||||||
						Image: imageId,
 | 
					 | 
				
			||||||
						Volumes: []string{
 | 
					 | 
				
			||||||
							"./alr:/usr/bin/alr",
 | 
					 | 
				
			||||||
						},
 | 
					 | 
				
			||||||
						Privileged: true,
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
				assert.NoError(t, e2e.StartAndWaitReady(runnable))
 | 
					
 | 
				
			||||||
				f(t, runnable)
 | 
						execShouldNoError(t, r,
 | 
				
			||||||
 | 
							"sudo",
 | 
				
			||||||
 | 
							"alr",
 | 
				
			||||||
 | 
							"ref",
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func runMatrixSuite(t *testing.T, name string, images []string, test func(t *testing.T, r capytest.Runner)) {
 | 
				
			||||||
 | 
						t.Helper()
 | 
				
			||||||
 | 
						for _, image := range images {
 | 
				
			||||||
 | 
							ts := capytest.NewTestSuite(t, podman.Provider(
 | 
				
			||||||
 | 
								podman.WithImage(fmt.Sprintf("ghcr.io/maks1ms/alr-e2e-test-image-%s", image)),
 | 
				
			||||||
 | 
								podman.WithVolumes("./alr:/tmp/alr"),
 | 
				
			||||||
 | 
								podman.WithPrivileged(true),
 | 
				
			||||||
 | 
							))
 | 
				
			||||||
 | 
							ts.BeforeEach(func(t *testing.T, r capytest.Runner) {
 | 
				
			||||||
 | 
								execShouldNoError(t, r, "/bin/alr-test-setup", "alr-install")
 | 
				
			||||||
 | 
								execShouldNoError(t, r, "/bin/alr-test-setup", "passwordless-sudo-setup")
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
 | 
							ts.Run(fmt.Sprintf("%s/%s", name, image), test)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func runTestCommands(t *testing.T, r e2e.Runnable, timeout time.Duration, expects []expect.Batcher) {
 | 
					 | 
				
			||||||
	exp, _, err, _ := e2eSpawn(
 | 
					 | 
				
			||||||
		r,
 | 
					 | 
				
			||||||
		e2e.NewCommand("/bin/bash"), 25*time.Second,
 | 
					 | 
				
			||||||
		expect.Verbose(true),
 | 
					 | 
				
			||||||
	)
 | 
					 | 
				
			||||||
	assert.NoError(t, err)
 | 
					 | 
				
			||||||
	_, err = exp.ExpectBatch(
 | 
					 | 
				
			||||||
		expects,
 | 
					 | 
				
			||||||
		timeout,
 | 
					 | 
				
			||||||
	)
 | 
					 | 
				
			||||||
	assert.NoError(t, err)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										41
									
								
								e2e-tests/firejailed_package_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								e2e-tests/firejailed_package_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,41 @@
 | 
				
			|||||||
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//go:build e2e
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package e2etests_test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"go.alt-gnome.ru/capytest"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestE2EFirejailedPackage(t *testing.T) {
 | 
				
			||||||
 | 
						runMatrixSuite(
 | 
				
			||||||
 | 
							t,
 | 
				
			||||||
 | 
							"firejailed-package",
 | 
				
			||||||
 | 
							COMMON_SYSTEMS,
 | 
				
			||||||
 | 
							func(t *testing.T, r capytest.Runner) {
 | 
				
			||||||
 | 
								defaultPrepare(t, r)
 | 
				
			||||||
 | 
								execShouldNoError(t, r, "alr", "build", "-p", fmt.Sprintf("%s/firejailed-pkg", REPO_NAME_FOR_E2E_TESTS))
 | 
				
			||||||
 | 
								execShouldError(t, r, "alr", "build", "-p", fmt.Sprintf("%s/firejailed-pkg-incorrect", REPO_NAME_FOR_E2E_TESTS))
 | 
				
			||||||
 | 
								execShouldNoError(t, r, "sh", "-c", "dpkg -c *.deb | grep -q '/usr/lib/alr/firejailed/_usr_bin_danger.sh'")
 | 
				
			||||||
 | 
								execShouldNoError(t, r, "sh", "-c", "dpkg -c *.deb | grep -q '/usr/lib/alr/firejailed/_usr_bin_danger.sh.profile'")
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
// ALR - Any Linux Repository
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
// Copyright (C) 2025 Евгений Храмов
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// This program is free software: you can redistribute it and/or modify
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
// it under the terms of the GNU General Public License as published by
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
@@ -20,24 +20,15 @@ package e2etests_test
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
	"time"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/efficientgo/e2e"
 | 
						"go.alt-gnome.ru/capytest"
 | 
				
			||||||
	expect "github.com/tailscale/goexpect"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestE2EAlrFix(t *testing.T) {
 | 
					func TestE2EAlrFix(t *testing.T) {
 | 
				
			||||||
	dockerMultipleRun(
 | 
						runMatrixSuite(t, "run-fix", COMMON_SYSTEMS, func(t *testing.T, r capytest.Runner) {
 | 
				
			||||||
		t,
 | 
							r.Command("alr", "fix").
 | 
				
			||||||
		"run-fix",
 | 
								ExpectStderrContains("--> Done").
 | 
				
			||||||
		COMMON_SYSTEMS,
 | 
								ExpectSuccess().
 | 
				
			||||||
		func(t *testing.T, r e2e.Runnable) {
 | 
								Run(t)
 | 
				
			||||||
			runTestCommands(t, r, time.Second*30, []expect.Batcher{
 | 
					 | 
				
			||||||
				&expect.BSnd{S: "alr fix\n"},
 | 
					 | 
				
			||||||
				&expect.BExp{R: `--> Done`},
 | 
					 | 
				
			||||||
				&expect.BSnd{S: "echo $?\n"},
 | 
					 | 
				
			||||||
				&expect.BExp{R: `^0\n$`},
 | 
					 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										38
									
								
								e2e-tests/group_and_summary_field_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								e2e-tests/group_and_summary_field_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,38 @@
 | 
				
			|||||||
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//go:build e2e
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package e2etests_test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"go.alt-gnome.ru/capytest"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestE2EGroupAndSummaryField(t *testing.T) {
 | 
				
			||||||
 | 
						runMatrixSuite(
 | 
				
			||||||
 | 
							t,
 | 
				
			||||||
 | 
							"group-and-summary-field",
 | 
				
			||||||
 | 
							RPM_SYSTEMS,
 | 
				
			||||||
 | 
							func(t *testing.T, r capytest.Runner) {
 | 
				
			||||||
 | 
								defaultPrepare(t, r)
 | 
				
			||||||
 | 
								execShouldNoError(t, r, "sh", "-c", "alr search --name test-group-and-summary --format \"{{.Group.Resolved}}\" | grep ^System/Base$")
 | 
				
			||||||
 | 
								execShouldNoError(t, r, "sh", "-c", "alr search --name test-group-and-summary --format \"{{.Summary.Resolved}}\" | grep \"^Custom summary$\"")
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,4 +0,0 @@
 | 
				
			|||||||
FROM alpine:latest
 | 
					 | 
				
			||||||
RUN adduser -s /bin/bash alr-user
 | 
					 | 
				
			||||||
USER alr-user
 | 
					 | 
				
			||||||
ENTRYPOINT ["tail", "-f", "/dev/null"]
 | 
					 | 
				
			||||||
@@ -1,5 +0,0 @@
 | 
				
			|||||||
FROM registry.altlinux.org/sisyphus/alt:latest
 | 
					 | 
				
			||||||
RUN apt-get update && apt-get install -y ca-certificates
 | 
					 | 
				
			||||||
RUN useradd -m -s /bin/bash alr-user
 | 
					 | 
				
			||||||
USER alr-user
 | 
					 | 
				
			||||||
ENTRYPOINT ["tail", "-f", "/dev/null"]
 | 
					 | 
				
			||||||
@@ -1,4 +0,0 @@
 | 
				
			|||||||
FROM archlinux:latest
 | 
					 | 
				
			||||||
RUN useradd -m -s /bin/bash alr-user
 | 
					 | 
				
			||||||
USER alr-user
 | 
					 | 
				
			||||||
ENTRYPOINT ["tail", "-f", "/dev/null"]
 | 
					 | 
				
			||||||
@@ -1,4 +0,0 @@
 | 
				
			|||||||
FROM opensuse/leap:latest
 | 
					 | 
				
			||||||
RUN useradd -m -s /bin/bash alr-user
 | 
					 | 
				
			||||||
USER alr-user
 | 
					 | 
				
			||||||
ENTRYPOINT ["tail", "-f", "/dev/null"]
 | 
					 | 
				
			||||||
@@ -1,4 +0,0 @@
 | 
				
			|||||||
FROM registry.red-soft.ru/ubi8/ubi:latest
 | 
					 | 
				
			||||||
RUN useradd -m -s /bin/bash alr-user
 | 
					 | 
				
			||||||
USER alr-user
 | 
					 | 
				
			||||||
ENTRYPOINT ["tail", "-f", "/dev/null"]
 | 
					 | 
				
			||||||
@@ -1,7 +0,0 @@
 | 
				
			|||||||
FROM ubuntu:24.10
 | 
					 | 
				
			||||||
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates sudo
 | 
					 | 
				
			||||||
RUN useradd -m -s /bin/bash alr-user && \
 | 
					 | 
				
			||||||
    echo "alr-user ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.d/alr-user && \
 | 
					 | 
				
			||||||
    chmod 0440 /etc/sudoers.d/alr-user
 | 
					 | 
				
			||||||
USER alr-user
 | 
					 | 
				
			||||||
ENTRYPOINT ["tail", "-f", "/dev/null"]
 | 
					 | 
				
			||||||
							
								
								
									
										40
									
								
								e2e-tests/issue_129_repo_toml_import_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								e2e-tests/issue_129_repo_toml_import_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,40 @@
 | 
				
			|||||||
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//go:build e2e
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package e2etests_test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"go.alt-gnome.ru/capytest"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestE2EIssue129RepoTomlImportTest(t *testing.T) {
 | 
				
			||||||
 | 
						runMatrixSuite(
 | 
				
			||||||
 | 
							t,
 | 
				
			||||||
 | 
							"issue-129-repo-toml-import-test",
 | 
				
			||||||
 | 
							COMMON_SYSTEMS,
 | 
				
			||||||
 | 
							func(t *testing.T, r capytest.Runner) {
 | 
				
			||||||
 | 
								defaultPrepare(t, r)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								r.Command("alr", "config", "get", "repos").
 | 
				
			||||||
 | 
									ExpectStdoutMatchesSnapshot().
 | 
				
			||||||
 | 
									Run(t)
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										63
									
								
								e2e-tests/issue_130_install_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								e2e-tests/issue_130_install_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,63 @@
 | 
				
			|||||||
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//go:build e2e
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package e2etests_test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"go.alt-gnome.ru/capytest"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestE2EIssue130Install(t *testing.T) {
 | 
				
			||||||
 | 
						runMatrixSuite(
 | 
				
			||||||
 | 
							t,
 | 
				
			||||||
 | 
							"alr install {repo}/{package}",
 | 
				
			||||||
 | 
							COMMON_SYSTEMS,
 | 
				
			||||||
 | 
							func(t *testing.T, r capytest.Runner) {
 | 
				
			||||||
 | 
								t.Parallel()
 | 
				
			||||||
 | 
								defaultPrepare(t, r)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								r.Command("sudo", "alr", "in", fmt.Sprintf("%s/foo-pkg", REPO_NAME_FOR_E2E_TESTS)).
 | 
				
			||||||
 | 
									ExpectSuccess().
 | 
				
			||||||
 | 
									Run(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								r.Command("sudo", "alr", "in", fmt.Sprintf("%s/bar-pkg", "NOT_REPO_NAME_FOR_E2E_TESTS")).
 | 
				
			||||||
 | 
									ExpectFailure().
 | 
				
			||||||
 | 
									Run(t)
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
						runMatrixSuite(
 | 
				
			||||||
 | 
							t,
 | 
				
			||||||
 | 
							"alr install {package}+alr-{repo}",
 | 
				
			||||||
 | 
							COMMON_SYSTEMS,
 | 
				
			||||||
 | 
							func(t *testing.T, r capytest.Runner) {
 | 
				
			||||||
 | 
								t.Parallel()
 | 
				
			||||||
 | 
								defaultPrepare(t, r)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								r.Command("sudo", "alr", "in", fmt.Sprintf("foo-pkg+alr-%s", REPO_NAME_FOR_E2E_TESTS)).
 | 
				
			||||||
 | 
									ExpectSuccess().
 | 
				
			||||||
 | 
									Run(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								r.Command("sudo", "alr", "in", fmt.Sprintf("bar-pkg+alr-%s", "NOT_REPO_NAME_FOR_E2E_TESTS")).
 | 
				
			||||||
 | 
									ExpectFailure().
 | 
				
			||||||
 | 
									Run(t)
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
// ALR - Any Linux Repository
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
// Copyright (C) 2025 Евгений Храмов
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// This program is free software: you can redistribute it and/or modify
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
// it under the terms of the GNU General Public License as published by
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
@@ -21,20 +21,20 @@ package e2etests_test
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/alecthomas/assert/v2"
 | 
						"go.alt-gnome.ru/capytest"
 | 
				
			||||||
	"github.com/efficientgo/e2e"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestE2EIssue32Interactive(t *testing.T) {
 | 
					func TestE2EIssue32Interactive(t *testing.T) {
 | 
				
			||||||
	dockerMultipleRun(
 | 
						runMatrixSuite(
 | 
				
			||||||
		t,
 | 
							t,
 | 
				
			||||||
		"issue-32-interactive",
 | 
							"issue-32-interactive",
 | 
				
			||||||
		COMMON_SYSTEMS,
 | 
							COMMON_SYSTEMS,
 | 
				
			||||||
		func(t *testing.T, r e2e.Runnable) {
 | 
							func(t *testing.T, r capytest.Runner) {
 | 
				
			||||||
			err := r.Exec(e2e.NewCommand(
 | 
								execShouldNoError(t, r, "alr", "--interactive=false", "remove", "ca-certificates")
 | 
				
			||||||
				"alr", "--interactive=false", "remove", "ca-certificates",
 | 
								execShouldNoError(t, r, "sudo", "alr", "--interactive=false", "remove", "openssl")
 | 
				
			||||||
			))
 | 
								execShouldNoError(t, r, "alr", "fix")
 | 
				
			||||||
			assert.NoError(t, err)
 | 
								execShouldNoError(t, r, "sudo", "apt-get", "update")
 | 
				
			||||||
 | 
								execShouldNoError(t, r, "sudo", "alr", "--interactive=false", "install", "ca-certificates")
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										40
									
								
								e2e-tests/issue_41_autoreq_skiplist_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								e2e-tests/issue_41_autoreq_skiplist_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,40 @@
 | 
				
			|||||||
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//go:build e2e
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package e2etests_test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"go.alt-gnome.ru/capytest"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestE2EIssue41AutoreqSkiplist(t *testing.T) {
 | 
				
			||||||
 | 
						runMatrixSuite(
 | 
				
			||||||
 | 
							t,
 | 
				
			||||||
 | 
							"issue-41-autoreq-skiplist",
 | 
				
			||||||
 | 
							AUTOREQ_AUTOPROV_SYSTEMS,
 | 
				
			||||||
 | 
							func(t *testing.T, r capytest.Runner) {
 | 
				
			||||||
 | 
								defaultPrepare(t, r)
 | 
				
			||||||
 | 
								execShouldNoError(t, r, "alr", "build", "-p", "alr-repo/test-autoreq-autoprov")
 | 
				
			||||||
 | 
								execShouldNoError(t, r, "sh", "-c", "rpm -qp --requires *.rpm | grep \"^/bin/sh$\"")
 | 
				
			||||||
 | 
								execShouldError(t, r, "sh", "-c", "rpm -qp --requires *.rpm | grep \"^/bin/bash$\"")
 | 
				
			||||||
 | 
								execShouldError(t, r, "sh", "-c", "rpm -qp --requires *.rpm | grep \"^/bin/zsh$\"")
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
// ALR - Any Linux Repository
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
// Copyright (C) 2025 Евгений Храмов
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// This program is free software: you can redistribute it and/or modify
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
// it under the terms of the GNU General Public License as published by
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
@@ -21,35 +21,19 @@ package e2etests_test
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/alecthomas/assert/v2"
 | 
						"go.alt-gnome.ru/capytest"
 | 
				
			||||||
	"github.com/efficientgo/e2e"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestE2EIssue50InstallMultiple(t *testing.T) {
 | 
					func TestE2EIssue50InstallMultiple(t *testing.T) {
 | 
				
			||||||
	dockerMultipleRun(
 | 
						runMatrixSuite(
 | 
				
			||||||
		t,
 | 
							t,
 | 
				
			||||||
		"issue-50-install-multiple",
 | 
							"issue-50-install-multiple",
 | 
				
			||||||
		COMMON_SYSTEMS,
 | 
							COMMON_SYSTEMS,
 | 
				
			||||||
		func(t *testing.T, r e2e.Runnable) {
 | 
							func(t *testing.T, r capytest.Runner) {
 | 
				
			||||||
			err := r.Exec(e2e.NewCommand(
 | 
								defaultPrepare(t, r)
 | 
				
			||||||
				"alr",
 | 
								execShouldNoError(t, r, "sudo", "alr", "in", "foo-pkg", "bar-pkg")
 | 
				
			||||||
				"addrepo",
 | 
								execShouldNoError(t, r, "cat", "/opt/foo")
 | 
				
			||||||
				"--name",
 | 
								execShouldNoError(t, r, "cat", "/opt/bar")
 | 
				
			||||||
				"alr-repo",
 | 
					 | 
				
			||||||
				"--url",
 | 
					 | 
				
			||||||
				"https://gitea.plemya-x.ru/Maks1mS/repo-for-tests.git",
 | 
					 | 
				
			||||||
			))
 | 
					 | 
				
			||||||
			assert.NoError(t, err)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			err = r.Exec(e2e.NewCommand(
 | 
					 | 
				
			||||||
				"alr", "in", "foo-pkg", "bar-pkg",
 | 
					 | 
				
			||||||
			))
 | 
					 | 
				
			||||||
			assert.NoError(t, err)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			err = r.Exec(e2e.NewCommand("cat", "/opt/foo"))
 | 
					 | 
				
			||||||
			assert.NoError(t, err)
 | 
					 | 
				
			||||||
			err = r.Exec(e2e.NewCommand("cat", "/opt/bar"))
 | 
					 | 
				
			||||||
			assert.NoError(t, err)
 | 
					 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
// ALR - Any Linux Repository
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
// Copyright (C) 2025 Евгений Храмов
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// This program is free software: you can redistribute it and/or modify
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
// it under the terms of the GNU General Public License as published by
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
@@ -21,32 +21,17 @@ package e2etests_test
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/alecthomas/assert/v2"
 | 
						"go.alt-gnome.ru/capytest"
 | 
				
			||||||
	"github.com/efficientgo/e2e"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestE2EIssue53LcAllCInfo(t *testing.T) {
 | 
					func TestE2EIssue53LcAllCInfo(t *testing.T) {
 | 
				
			||||||
	dockerMultipleRun(
 | 
						runMatrixSuite(
 | 
				
			||||||
		t,
 | 
							t,
 | 
				
			||||||
		"issue-53-lc-all-c-info",
 | 
							"issue-53-lc-all-c-info",
 | 
				
			||||||
		COMMON_SYSTEMS,
 | 
							COMMON_SYSTEMS,
 | 
				
			||||||
		func(t *testing.T, r e2e.Runnable) {
 | 
							func(t *testing.T, r capytest.Runner) {
 | 
				
			||||||
			err := r.Exec(e2e.NewCommand(
 | 
								defaultPrepare(t, r)
 | 
				
			||||||
				"alr",
 | 
								execShouldNoError(t, r, "bash", "-c", "LANG=C alr info foo-pkg")
 | 
				
			||||||
				"addrepo",
 | 
					 | 
				
			||||||
				"--name",
 | 
					 | 
				
			||||||
				"alr-repo",
 | 
					 | 
				
			||||||
				"--url",
 | 
					 | 
				
			||||||
				"https://gitea.plemya-x.ru/Plemya-x/alr-repo.git",
 | 
					 | 
				
			||||||
			))
 | 
					 | 
				
			||||||
			assert.NoError(t, err)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			err = r.Exec(e2e.NewCommand(
 | 
					 | 
				
			||||||
				"bash",
 | 
					 | 
				
			||||||
				"-c",
 | 
					 | 
				
			||||||
				"LANG=C alr info alr-bin",
 | 
					 | 
				
			||||||
			))
 | 
					 | 
				
			||||||
			assert.NoError(t, err)
 | 
					 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										40
									
								
								e2e-tests/issue_59_rm_completion_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								e2e-tests/issue_59_rm_completion_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,40 @@
 | 
				
			|||||||
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//go:build e2e
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package e2etests_test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"go.alt-gnome.ru/capytest"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestE2EIssue59RmCompletion(t *testing.T) {
 | 
				
			||||||
 | 
						runMatrixSuite(
 | 
				
			||||||
 | 
							t,
 | 
				
			||||||
 | 
							"issue-59-rm-completion",
 | 
				
			||||||
 | 
							COMMON_SYSTEMS,
 | 
				
			||||||
 | 
							func(t *testing.T, r capytest.Runner) {
 | 
				
			||||||
 | 
								defaultPrepare(t, r)
 | 
				
			||||||
 | 
								execShouldNoError(t, r, "sudo", "alr", "in", "foo-pkg", "bar-pkg")
 | 
				
			||||||
 | 
								execShouldNoError(t, r, "sh", "-c", "alr rm --generate-bash-completion | grep ^foo-pkg$")
 | 
				
			||||||
 | 
								execShouldNoError(t, r, "sh", "-c", "alr rm --generate-bash-completion | grep ^bar-pkg$")
 | 
				
			||||||
 | 
								execShouldError(t, r, "sh", "-c", "alr rm --generate-bash-completion | grep ^test-autoreq-autoprov$")
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										50
									
								
								e2e-tests/issue_62_list_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								e2e-tests/issue_62_list_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,50 @@
 | 
				
			|||||||
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//go:build e2e
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package e2etests_test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"go.alt-gnome.ru/capytest"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestE2EIssue62List(t *testing.T) {
 | 
				
			||||||
 | 
						runMatrixSuite(
 | 
				
			||||||
 | 
							t,
 | 
				
			||||||
 | 
							"issue-62-list",
 | 
				
			||||||
 | 
							COMMON_SYSTEMS,
 | 
				
			||||||
 | 
							func(t *testing.T, r capytest.Runner) {
 | 
				
			||||||
 | 
								defaultPrepare(t, r)
 | 
				
			||||||
 | 
								execShouldNoError(t, r, "sudo", "alr", "repo", "set-ref", "alr-repo", "bd26236cd7")
 | 
				
			||||||
 | 
								execShouldNoError(t, r, "alr", "ref")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								execShouldNoError(t, r, "sudo", "alr", "in", "foo-pkg")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								r.Command("alr", "list", "-I").
 | 
				
			||||||
 | 
									ExpectSuccess().
 | 
				
			||||||
 | 
									ExpectStdoutMatchesSnapshot().
 | 
				
			||||||
 | 
									Run(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								r.Command("alr", "list").
 | 
				
			||||||
 | 
									ExpectSuccess().
 | 
				
			||||||
 | 
									ExpectStdoutMatchesSnapshot().
 | 
				
			||||||
 | 
									Run(t)
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										37
									
								
								e2e-tests/issue_72_install_with_deps_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								e2e-tests/issue_72_install_with_deps_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
				
			|||||||
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//go:build e2e
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package e2etests_test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"go.alt-gnome.ru/capytest"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestE2EIssue72InstallWithDeps(t *testing.T) {
 | 
				
			||||||
 | 
						runMatrixSuite(
 | 
				
			||||||
 | 
							t,
 | 
				
			||||||
 | 
							"issue-72-install-with-deps",
 | 
				
			||||||
 | 
							COMMON_SYSTEMS,
 | 
				
			||||||
 | 
							func(t *testing.T, r capytest.Runner) {
 | 
				
			||||||
 | 
								defaultPrepare(t, r)
 | 
				
			||||||
 | 
								execShouldNoError(t, r, "sudo", "alr", "in", "test-app-with-lib")
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										43
									
								
								e2e-tests/issue_74_upgradable_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								e2e-tests/issue_74_upgradable_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,43 @@
 | 
				
			|||||||
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//go:build e2e
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package e2etests_test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"go.alt-gnome.ru/capytest"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestE2EIssue74Upgradable(t *testing.T) {
 | 
				
			||||||
 | 
						runMatrixSuite(
 | 
				
			||||||
 | 
							t,
 | 
				
			||||||
 | 
							"issue-74-upgradable",
 | 
				
			||||||
 | 
							COMMON_SYSTEMS,
 | 
				
			||||||
 | 
							func(t *testing.T, r capytest.Runner) {
 | 
				
			||||||
 | 
								defaultPrepare(t, r)
 | 
				
			||||||
 | 
								execShouldNoError(t, r, "sudo", "alr", "repo", "set-ref", "alr-repo", "bd26236cd7")
 | 
				
			||||||
 | 
								execShouldNoError(t, r, "alr", "ref")
 | 
				
			||||||
 | 
								execShouldNoError(t, r, "sudo", "alr", "in", "bar-pkg")
 | 
				
			||||||
 | 
								execShouldNoError(t, r, "sh", "-c", "test $(alr list -U | wc -l) -eq 0 || exit 1")
 | 
				
			||||||
 | 
								execShouldNoError(t, r, "sudo", "alr", "repo", "set-ref", "alr-repo", "d9a3541561")
 | 
				
			||||||
 | 
								execShouldNoError(t, r, "sudo", "alr", "ref")
 | 
				
			||||||
 | 
								execShouldNoError(t, r, "sh", "-c", "test $(alr list -U | wc -l) -eq 1 || exit 1")
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										38
									
								
								e2e-tests/issue_75_ref_specify_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								e2e-tests/issue_75_ref_specify_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,38 @@
 | 
				
			|||||||
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//go:build e2e
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package e2etests_test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"go.alt-gnome.ru/capytest"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestE2EIssue75InstallWithDeps(t *testing.T) {
 | 
				
			||||||
 | 
						runMatrixSuite(
 | 
				
			||||||
 | 
							t,
 | 
				
			||||||
 | 
							"issue-75-ref-specify",
 | 
				
			||||||
 | 
							COMMON_SYSTEMS,
 | 
				
			||||||
 | 
							func(t *testing.T, r capytest.Runner) {
 | 
				
			||||||
 | 
								defaultPrepare(t, r)
 | 
				
			||||||
 | 
								execShouldNoError(t, r, "sudo", "alr", "repo", "set-ref", "alr-repo", "bd26236cd7")
 | 
				
			||||||
 | 
								execShouldNoError(t, r, "sh", "-c", "test $(alr list | wc -l) -eq 2 || exit 1")
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										54
									
								
								e2e-tests/issue_76_single_package_repo_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								e2e-tests/issue_76_single_package_repo_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,54 @@
 | 
				
			|||||||
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//go:build e2e
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package e2etests_test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"go.alt-gnome.ru/capytest"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func Test75SinglePackageRepo(t *testing.T) {
 | 
				
			||||||
 | 
						runMatrixSuite(
 | 
				
			||||||
 | 
							t,
 | 
				
			||||||
 | 
							"issue-76-single-package-repo",
 | 
				
			||||||
 | 
							COMMON_SYSTEMS,
 | 
				
			||||||
 | 
							func(t *testing.T, r capytest.Runner) {
 | 
				
			||||||
 | 
								execShouldNoError(t, r,
 | 
				
			||||||
 | 
									"sudo",
 | 
				
			||||||
 | 
									"alr",
 | 
				
			||||||
 | 
									"repo",
 | 
				
			||||||
 | 
									"add",
 | 
				
			||||||
 | 
									REPO_NAME_FOR_E2E_TESTS,
 | 
				
			||||||
 | 
									"https://gitea.plemya-x.ru/Maks1mS/test-single-package-alr-repo.git",
 | 
				
			||||||
 | 
								)
 | 
				
			||||||
 | 
								execShouldNoError(t, r, "sudo", "alr", "ref")
 | 
				
			||||||
 | 
								execShouldNoError(t, r, "sudo", "alr", "repo", "set-ref", REPO_NAME_FOR_E2E_TESTS, "1075c918be")
 | 
				
			||||||
 | 
								execShouldNoError(t, r, "alr", "fix")
 | 
				
			||||||
 | 
								execShouldNoError(t, r, "sudo", "alr", "in", "test-single-repo")
 | 
				
			||||||
 | 
								execShouldNoError(t, r, "sh", "-c", "alr list -U")
 | 
				
			||||||
 | 
								execShouldNoError(t, r, "sh", "-c", "test $(alr list -U | wc -l) -eq 0 || exit 1")
 | 
				
			||||||
 | 
								execShouldNoError(t, r, "sudo", "alr", "repo", "set-ref", REPO_NAME_FOR_E2E_TESTS, "5e361c50d7")
 | 
				
			||||||
 | 
								execShouldNoError(t, r, "sudo", "alr", "ref")
 | 
				
			||||||
 | 
								execShouldNoError(t, r, "sh", "-c", "test $(alr list -U | wc -l) -eq 1 || exit 1")
 | 
				
			||||||
 | 
								execShouldNoError(t, r, "sudo", "alr", "up")
 | 
				
			||||||
 | 
								execShouldNoError(t, r, "sh", "-c", "test $(alr list -U | wc -l) -eq 0 || exit 1")
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										49
									
								
								e2e-tests/issue_78_mirrors_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								e2e-tests/issue_78_mirrors_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,49 @@
 | 
				
			|||||||
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//go:build e2e
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package e2etests_test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"go.alt-gnome.ru/capytest"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestE2EIssue78Mirrors(t *testing.T) {
 | 
				
			||||||
 | 
						runMatrixSuite(t, "issue-78-mirrors", COMMON_SYSTEMS, func(t *testing.T, r capytest.Runner) {
 | 
				
			||||||
 | 
							defaultPrepare(t, r)
 | 
				
			||||||
 | 
							execShouldNoError(t, r, "sudo", "alr", "repo", "mirror", "add", REPO_NAME_FOR_E2E_TESTS, "https://gitea.plemya-x.ru/Maks1mS/repo-for-tests.git")
 | 
				
			||||||
 | 
							execShouldNoError(t, r, "sudo", "alr", "repo", "set-url", REPO_NAME_FOR_E2E_TESTS, "https://example.com")
 | 
				
			||||||
 | 
							execShouldNoError(t, r, "sudo", "alr", "ref")
 | 
				
			||||||
 | 
							execShouldNoError(t, r, "sudo", "alr", "repo", "mirror", "clear", REPO_NAME_FOR_E2E_TESTS)
 | 
				
			||||||
 | 
							execShouldError(t, r, "sudo", "alr", "ref")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							execShouldNoError(t, r, "sudo", "alr", "repo", "mirror", "add", REPO_NAME_FOR_E2E_TESTS, "https://gitea.plemya-x.ru/Maks1mS/repo-for-tests.git")
 | 
				
			||||||
 | 
							execShouldNoError(t, r, "sudo", "alr", "repo", "mirror", "rm", "--partial", REPO_NAME_FOR_E2E_TESTS, "gitea.plemya-x.ru/Maks1mS")
 | 
				
			||||||
 | 
							execShouldError(t, r, "sudo", "alr", "ref")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							execShouldNoError(t, r, "sudo", "alr", "repo", "mirror", "add", REPO_NAME_FOR_E2E_TESTS, "https://gitea.plemya-x.ru/Maks1mS/repo-for-tests.git")
 | 
				
			||||||
 | 
							execShouldNoError(t, r, "sudo", "alr", "repo", "mirror", "rm", REPO_NAME_FOR_E2E_TESTS, "https://gitea.plemya-x.ru/Maks1mS/repo-for-tests.git")
 | 
				
			||||||
 | 
							execShouldError(t, r, "sudo", "alr", "ref")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							execShouldNoError(t, r, "sudo", "alr", "repo", "mirror", "add", REPO_NAME_FOR_E2E_TESTS, "https://gitea.plemya-x.ru/Maks1mS/repo-for-tests.git")
 | 
				
			||||||
 | 
							execShouldNoError(t, r, "sudo", "alr", "repo", "mirror", "rm", REPO_NAME_FOR_E2E_TESTS, "https://gitea.plemya-x.ru/Maks1mS/repo-for-tests.git")
 | 
				
			||||||
 | 
							execShouldError(t, r, "sudo", "alr", "ref")
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										38
									
								
								e2e-tests/issue_81_multiple_packages_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								e2e-tests/issue_81_multiple_packages_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,38 @@
 | 
				
			|||||||
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//go:build e2e
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package e2etests_test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"go.alt-gnome.ru/capytest"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestE2EIssue81MultiplePackages(t *testing.T) {
 | 
				
			||||||
 | 
						runMatrixSuite(
 | 
				
			||||||
 | 
							t,
 | 
				
			||||||
 | 
							"issue-81-multiple-packages",
 | 
				
			||||||
 | 
							COMMON_SYSTEMS,
 | 
				
			||||||
 | 
							func(t *testing.T, r capytest.Runner) {
 | 
				
			||||||
 | 
								defaultPrepare(t, r)
 | 
				
			||||||
 | 
								execShouldNoError(t, r, "sudo", "alr", "in", "first-package-with-dashes")
 | 
				
			||||||
 | 
								execShouldNoError(t, r, "cat", "/opt/first-package")
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										40
									
								
								e2e-tests/issue_91_set_repo_ref_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								e2e-tests/issue_91_set_repo_ref_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,40 @@
 | 
				
			|||||||
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//go:build e2e
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package e2etests_test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"go.alt-gnome.ru/capytest"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestE2EIssue91MultiplePackages(t *testing.T) {
 | 
				
			||||||
 | 
						runMatrixSuite(
 | 
				
			||||||
 | 
							t,
 | 
				
			||||||
 | 
							"issue-91-set-repo-ref",
 | 
				
			||||||
 | 
							COMMON_SYSTEMS,
 | 
				
			||||||
 | 
							func(t *testing.T, r capytest.Runner) {
 | 
				
			||||||
 | 
								defaultPrepare(t, r)
 | 
				
			||||||
 | 
								execShouldError(t, r, "sudo", "alr", "repo", "set-ref")
 | 
				
			||||||
 | 
								execShouldError(t, r, "sudo", "alr", "repo", "set-ref", "alr-repo")
 | 
				
			||||||
 | 
								execShouldNoError(t, r, "sudo", "alr", "repo", "set-ref", "alr-repo", "bd26236cd7")
 | 
				
			||||||
 | 
								execShouldNoError(t, r, "sh", "-c", "test $(alr list | wc -l) -eq 2 || exit 1")
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										48
									
								
								e2e-tests/issue_94_twice_build_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								e2e-tests/issue_94_twice_build_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,48 @@
 | 
				
			|||||||
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//go:build e2e
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package e2etests_test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
 | 
						"go.alt-gnome.ru/capytest"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestE2EIssue94TwiceBuild(t *testing.T) {
 | 
				
			||||||
 | 
						runMatrixSuite(
 | 
				
			||||||
 | 
							t,
 | 
				
			||||||
 | 
							"issue-94-twice-build",
 | 
				
			||||||
 | 
							COMMON_SYSTEMS,
 | 
				
			||||||
 | 
							func(t *testing.T, r capytest.Runner) {
 | 
				
			||||||
 | 
								defaultPrepare(t, r)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								var stderr bytes.Buffer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								r.Command("sudo", "alr", "in", "test-94-app").
 | 
				
			||||||
 | 
									WithCaptureStderr(&stderr).
 | 
				
			||||||
 | 
									ExpectSuccess().
 | 
				
			||||||
 | 
									Run(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								assert.Equal(t, 1, strings.Count(stderr.String(), "Building package name=test-94-dep"))
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										47
									
								
								e2e-tests/issue_95_config_command_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								e2e-tests/issue_95_config_command_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,47 @@
 | 
				
			|||||||
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//go:build e2e
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package e2etests_test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"go.alt-gnome.ru/capytest"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestE2EIssue95ConfigCommand(t *testing.T) {
 | 
				
			||||||
 | 
						runMatrixSuite(
 | 
				
			||||||
 | 
							t,
 | 
				
			||||||
 | 
							"issue-95-config-command",
 | 
				
			||||||
 | 
							COMMON_SYSTEMS,
 | 
				
			||||||
 | 
							func(t *testing.T, r capytest.Runner) {
 | 
				
			||||||
 | 
								defaultPrepare(t, r)
 | 
				
			||||||
 | 
								execShouldNoError(t, r, "sh", "-c", "alr config show | grep \"autoPull: true\"")
 | 
				
			||||||
 | 
								execShouldNoError(t, r, "sh", "-c", "alr config get | grep \"autoPull: true\"")
 | 
				
			||||||
 | 
								execShouldError(t, r, "sh", "-c", "cat /etc/alr/alr.toml | grep \"autoPull\"")
 | 
				
			||||||
 | 
								execShouldNoError(t, r, "alr", "config", "get", "autoPull")
 | 
				
			||||||
 | 
								execShouldError(t, r, "alr", "config", "set", "autoPull")
 | 
				
			||||||
 | 
								execShouldNoError(t, r, "sudo", "alr", "config", "set", "autoPull", "false")
 | 
				
			||||||
 | 
								execShouldNoError(t, r, "sh", "-c", "alr config show | grep \"autoPull: false\"")
 | 
				
			||||||
 | 
								execShouldNoError(t, r, "sh", "-c", "alr config get | grep \"autoPull: false\"")
 | 
				
			||||||
 | 
								execShouldNoError(t, r, "sh", "-c", "cat /etc/alr/alr.toml | grep \"autoPull = false\"")
 | 
				
			||||||
 | 
								execShouldNoError(t, r, "alr", "config", "set", "autoPull", "true")
 | 
				
			||||||
 | 
								execShouldNoError(t, r, "sh", "-c", "cat /etc/alr/alr.toml | grep \"autoPull = true\"")
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
// ALR - Any Linux Repository
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
// Copyright (C) 2025 Евгений Храмов
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// This program is free software: you can redistribute it and/or modify
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
// it under the terms of the GNU General Public License as published by
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
@@ -20,25 +20,16 @@ package e2etests_test
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
	"time"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/efficientgo/e2e"
 | 
						"go.alt-gnome.ru/capytest"
 | 
				
			||||||
 | 
					 | 
				
			||||||
	expect "github.com/tailscale/goexpect"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestE2EAlrVersion(t *testing.T) {
 | 
					func TestE2EAlrVersion(t *testing.T) {
 | 
				
			||||||
	dockerMultipleRun(
 | 
						runMatrixSuite(t, "version", COMMON_SYSTEMS, func(t *testing.T, r capytest.Runner) {
 | 
				
			||||||
		t,
 | 
							r.Command("alr", "version").
 | 
				
			||||||
		"check-version",
 | 
								ExpectStderrRegex(`^v\d+\.\d+\.\d+(?:-\d+-g[a-f0-9]+)?\n$`).
 | 
				
			||||||
		COMMON_SYSTEMS,
 | 
								ExpectStdoutEmpty().
 | 
				
			||||||
		func(t *testing.T, r e2e.Runnable) {
 | 
								ExpectSuccess().
 | 
				
			||||||
			runTestCommands(t, r, time.Second*10, []expect.Batcher{
 | 
								Run(t)
 | 
				
			||||||
				&expect.BSnd{S: "alr version\n"},
 | 
					 | 
				
			||||||
				&expect.BExp{R: `^v\d+\.\d+\.\d+(?:-\d+-g[a-f0-9]+)?\n$`},
 | 
					 | 
				
			||||||
				&expect.BSnd{S: "echo $?\n"},
 | 
					 | 
				
			||||||
				&expect.BExp{R: `^0\n$`},
 | 
					 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										107
									
								
								fix.go
									
									
									
									
									
								
							
							
						
						
									
										107
									
								
								fix.go
									
									
									
									
									
								
							@@ -1,8 +1,8 @@
 | 
				
			|||||||
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
 | 
					// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
 | 
				
			||||||
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
 | 
					// It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// ALR - Any Linux Repository
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
// Copyright (C) 2025 Евгений Храмов
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// This program is free software: you can redistribute it and/or modify
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
// it under the terms of the GNU General Public License as published by
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
@@ -20,15 +20,17 @@
 | 
				
			|||||||
package main
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"io/fs"
 | 
				
			||||||
	"log/slog"
 | 
						"log/slog"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
 | 
						"path/filepath"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/leonelquinteros/gotext"
 | 
						"github.com/leonelquinteros/gotext"
 | 
				
			||||||
	"github.com/urfave/cli/v2"
 | 
						"github.com/urfave/cli/v2"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"gitea.plemya-x.ru/Plemya-x/ALR/internal/config"
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils"
 | 
				
			||||||
	database "gitea.plemya-x.ru/Plemya-x/ALR/internal/db"
 | 
						appbuilder "gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils/app_builder"
 | 
				
			||||||
	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/repos"
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/utils"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func FixCmd() *cli.Command {
 | 
					func FixCmd() *cli.Command {
 | 
				
			||||||
@@ -36,51 +38,68 @@ func FixCmd() *cli.Command {
 | 
				
			|||||||
		Name:  "fix",
 | 
							Name:  "fix",
 | 
				
			||||||
		Usage: gotext.Get("Attempt to fix problems with ALR"),
 | 
							Usage: gotext.Get("Attempt to fix problems with ALR"),
 | 
				
			||||||
		Action: func(c *cli.Context) error {
 | 
							Action: func(c *cli.Context) error {
 | 
				
			||||||
			ctx := c.Context
 | 
								if err := utils.ExitIfCantDropCapsToAlrUserNoPrivs(); err != nil {
 | 
				
			||||||
			cfg := config.New()
 | 
									return err
 | 
				
			||||||
			err := cfg.Load()
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				slog.Error(gotext.Get("Error loading config"), "err", err)
 | 
					 | 
				
			||||||
				os.Exit(1)
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								ctx := c.Context
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								deps, err := appbuilder.
 | 
				
			||||||
 | 
									New(ctx).
 | 
				
			||||||
 | 
									WithConfig().
 | 
				
			||||||
 | 
									Build()
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return cli.Exit(err, 1)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								defer deps.Defer()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								cfg := deps.Cfg
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			paths := cfg.GetPaths()
 | 
								paths := cfg.GetPaths()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			slog.Info(gotext.Get("Removing cache directory"))
 | 
								slog.Info(gotext.Get("Clearing cache directory"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			err = os.RemoveAll(paths.CacheDir)
 | 
								dir, err := os.Open(paths.CacheDir)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				slog.Error(gotext.Get("Unable to remove cache directory"), "err", err)
 | 
									return cliutils.FormatCliExit(gotext.Get("Unable to open cache directory"), err)
 | 
				
			||||||
				os.Exit(1)
 | 
								}
 | 
				
			||||||
 | 
								defer dir.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								entries, err := dir.Readdirnames(-1)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return cliutils.FormatCliExit(gotext.Get("Unable to read cache directory contents"), err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								for _, entry := range entries {
 | 
				
			||||||
 | 
									fullPath := filepath.Join(paths.CacheDir, entry)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if err := makeWritableRecursive(fullPath); err != nil {
 | 
				
			||||||
 | 
										slog.Debug("Failed to make path writable", "path", fullPath, "error", err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									err = os.RemoveAll(fullPath)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										return cliutils.FormatCliExit(gotext.Get("Unable to remove cache item (%s)", entry), err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			slog.Info(gotext.Get("Rebuilding cache"))
 | 
								slog.Info(gotext.Get("Rebuilding cache"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			err = os.MkdirAll(paths.CacheDir, 0o755)
 | 
								err = os.MkdirAll(paths.CacheDir, 0o755)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				slog.Error(gotext.Get("Unable to create new cache directory"), "err", err)
 | 
									return cliutils.FormatCliExit(gotext.Get("Unable to create new cache directory"), err)
 | 
				
			||||||
				os.Exit(1)
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			cfg = config.New()
 | 
								deps, err = appbuilder.
 | 
				
			||||||
			err = cfg.Load()
 | 
									New(ctx).
 | 
				
			||||||
 | 
									WithConfig().
 | 
				
			||||||
 | 
									WithDB().
 | 
				
			||||||
 | 
									WithReposForcePull().
 | 
				
			||||||
 | 
									Build()
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				slog.Error(gotext.Get("Error loading config"), "err", err)
 | 
									return cli.Exit(err, 1)
 | 
				
			||||||
				os.Exit(1)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			db := database.New(cfg)
 | 
					 | 
				
			||||||
			err = db.Init(ctx)
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				slog.Error(gotext.Get("Error initialization database"), "err", err)
 | 
					 | 
				
			||||||
				os.Exit(1)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			rs := repos.New(cfg, db)
 | 
					 | 
				
			||||||
			err = rs.Pull(ctx, cfg.Repos())
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				slog.Error(gotext.Get("Error pulling repos"), "err", err)
 | 
					 | 
				
			||||||
				os.Exit(1)
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
								defer deps.Defer()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			slog.Info(gotext.Get("Done"))
 | 
								slog.Info(gotext.Get("Done"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -88,3 +107,23 @@ func FixCmd() *cli.Command {
 | 
				
			|||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func makeWritableRecursive(path string) error {
 | 
				
			||||||
 | 
						return filepath.WalkDir(path, func(path string, d fs.DirEntry, err error) error {
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							info, err := d.Info()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							newMode := info.Mode() | 0o200
 | 
				
			||||||
 | 
							if d.IsDir() {
 | 
				
			||||||
 | 
								newMode |= 0o100
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return os.Chmod(path, newMode)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										6
									
								
								gen.go
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								gen.go
									
									
									
									
									
								
							@@ -1,8 +1,8 @@
 | 
				
			|||||||
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
 | 
					// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
 | 
				
			||||||
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
 | 
					// It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// ALR - Any Linux Repository
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
// Copyright (C) 2025 Евгений Храмов
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// This program is free software: you can redistribute it and/or modify
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
// it under the terms of the GNU General Public License as published by
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
@@ -25,7 +25,7 @@ import (
 | 
				
			|||||||
	"github.com/leonelquinteros/gotext"
 | 
						"github.com/leonelquinteros/gotext"
 | 
				
			||||||
	"github.com/urfave/cli/v2"
 | 
						"github.com/urfave/cli/v2"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/gen"
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/gen"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func GenCmd() *cli.Command {
 | 
					func GenCmd() *cli.Command {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										251
									
								
								generators/alrsh-package/main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										251
									
								
								generators/alrsh-package/main.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,251 @@
 | 
				
			|||||||
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"go/ast"
 | 
				
			||||||
 | 
						"go/format"
 | 
				
			||||||
 | 
						"go/parser"
 | 
				
			||||||
 | 
						"go/token"
 | 
				
			||||||
 | 
						"log"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"reflect"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"text/template"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func resolvedStructGenerator(buf *bytes.Buffer, fields []*ast.Field) {
 | 
				
			||||||
 | 
						contentTemplate := template.Must(template.New("").Parse(`
 | 
				
			||||||
 | 
					type {{ .EntityNameLower }}Resolved struct {
 | 
				
			||||||
 | 
					{{ .StructFields }}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					`))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var structFieldsBuilder strings.Builder
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, field := range fields {
 | 
				
			||||||
 | 
							for _, name := range field.Names {
 | 
				
			||||||
 | 
								// Поле с типом
 | 
				
			||||||
 | 
								fieldTypeStr := exprToString(field.Type)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Структура поля
 | 
				
			||||||
 | 
								var buf bytes.Buffer
 | 
				
			||||||
 | 
								buf.WriteString("\t")
 | 
				
			||||||
 | 
								buf.WriteString(name.Name)
 | 
				
			||||||
 | 
								buf.WriteString(" ")
 | 
				
			||||||
 | 
								buf.WriteString(fieldTypeStr)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Обработка json-тега
 | 
				
			||||||
 | 
								jsonTag := ""
 | 
				
			||||||
 | 
								if field.Tag != nil {
 | 
				
			||||||
 | 
									raw := strings.Trim(field.Tag.Value, "`")
 | 
				
			||||||
 | 
									tag := reflect.StructTag(raw)
 | 
				
			||||||
 | 
									if val := tag.Get("json"); val != "" {
 | 
				
			||||||
 | 
										jsonTag = val
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if jsonTag == "" {
 | 
				
			||||||
 | 
									jsonTag = strings.ToLower(name.Name)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								buf.WriteString(fmt.Sprintf(" `json:\"%s\"`", jsonTag))
 | 
				
			||||||
 | 
								buf.WriteString("\n")
 | 
				
			||||||
 | 
								structFieldsBuilder.Write(buf.Bytes())
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						params := struct {
 | 
				
			||||||
 | 
							EntityNameLower string
 | 
				
			||||||
 | 
							StructFields    string
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							EntityNameLower: "package",
 | 
				
			||||||
 | 
							StructFields:    structFieldsBuilder.String(),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err := contentTemplate.Execute(buf, params)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Fatalf("execute template: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func toResolvedFuncGenerator(buf *bytes.Buffer, fields []*ast.Field) {
 | 
				
			||||||
 | 
						contentTemplate := template.Must(template.New("").Parse(`
 | 
				
			||||||
 | 
					func {{ .EntityName }}ToResolved(src *{{ .EntityName }}) {{ .EntityNameLower }}Resolved {
 | 
				
			||||||
 | 
						return {{ .EntityNameLower }}Resolved{
 | 
				
			||||||
 | 
					{{ .Assignments }}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					`))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var assignmentsBuilder strings.Builder
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, field := range fields {
 | 
				
			||||||
 | 
							for _, name := range field.Names {
 | 
				
			||||||
 | 
								var assignBuf bytes.Buffer
 | 
				
			||||||
 | 
								assignBuf.WriteString("\t\t")
 | 
				
			||||||
 | 
								assignBuf.WriteString(name.Name)
 | 
				
			||||||
 | 
								assignBuf.WriteString(": ")
 | 
				
			||||||
 | 
								if isOverridableField(field.Type) {
 | 
				
			||||||
 | 
									assignBuf.WriteString(fmt.Sprintf("src.%s.Resolved()", name.Name))
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									assignBuf.WriteString(fmt.Sprintf("src.%s", name.Name))
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								assignBuf.WriteString(",\n")
 | 
				
			||||||
 | 
								assignmentsBuilder.Write(assignBuf.Bytes())
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						params := struct {
 | 
				
			||||||
 | 
							EntityName      string
 | 
				
			||||||
 | 
							EntityNameLower string
 | 
				
			||||||
 | 
							Assignments     string
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							EntityName:      "Package",
 | 
				
			||||||
 | 
							EntityNameLower: "package",
 | 
				
			||||||
 | 
							Assignments:     assignmentsBuilder.String(),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err := contentTemplate.Execute(buf, params)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Fatalf("execute template: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func resolveFuncGenerator(buf *bytes.Buffer, fields []*ast.Field) {
 | 
				
			||||||
 | 
						contentTemplate := template.Must(template.New("").Parse(`
 | 
				
			||||||
 | 
					func Resolve{{ .EntityName }}(pkg *{{ .EntityName }}, overrides []string) {
 | 
				
			||||||
 | 
					{{.Code}}}
 | 
				
			||||||
 | 
					`))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var codeBuilder strings.Builder
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, field := range fields {
 | 
				
			||||||
 | 
							for _, name := range field.Names {
 | 
				
			||||||
 | 
								if isOverridableField(field.Type) {
 | 
				
			||||||
 | 
									var buf bytes.Buffer
 | 
				
			||||||
 | 
									buf.WriteString(fmt.Sprintf("\t\tpkg.%s.Resolve(overrides)\n", name.Name))
 | 
				
			||||||
 | 
									codeBuilder.Write(buf.Bytes())
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						params := struct {
 | 
				
			||||||
 | 
							EntityName string
 | 
				
			||||||
 | 
							Code       string
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							EntityName: "Package",
 | 
				
			||||||
 | 
							Code:       codeBuilder.String(),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err := contentTemplate.Execute(buf, params)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Fatalf("execute template: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func main() {
 | 
				
			||||||
 | 
						path := os.Getenv("GOFILE")
 | 
				
			||||||
 | 
						if path == "" {
 | 
				
			||||||
 | 
							log.Fatal("GOFILE must be set")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fset := token.NewFileSet()
 | 
				
			||||||
 | 
						node, err := parser.ParseFile(fset, path, nil, parser.AllErrors)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Fatalf("parsing file: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						entityName := "Package" // имя структуры, которую анализируем
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						found := false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fields := make([]*ast.Field, 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Ищем структуру с нужным именем
 | 
				
			||||||
 | 
						for _, decl := range node.Decls {
 | 
				
			||||||
 | 
							genDecl, ok := decl.(*ast.GenDecl)
 | 
				
			||||||
 | 
							if !ok || genDecl.Tok != token.TYPE {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							for _, spec := range genDecl.Specs {
 | 
				
			||||||
 | 
								typeSpec := spec.(*ast.TypeSpec)
 | 
				
			||||||
 | 
								if typeSpec.Name.Name != entityName {
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								structType, ok := typeSpec.Type.(*ast.StructType)
 | 
				
			||||||
 | 
								if !ok {
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								fields = structType.Fields.List
 | 
				
			||||||
 | 
								found = true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !found {
 | 
				
			||||||
 | 
							log.Fatalf("struct %s not found", entityName)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var buf bytes.Buffer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						buf.WriteString("// DO NOT EDIT MANUALLY. This file is generated.\n")
 | 
				
			||||||
 | 
						buf.WriteString("package alrsh")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resolvedStructGenerator(&buf, fields)
 | 
				
			||||||
 | 
						toResolvedFuncGenerator(&buf, fields)
 | 
				
			||||||
 | 
						resolveFuncGenerator(&buf, fields)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Форматируем вывод
 | 
				
			||||||
 | 
						formatted, err := format.Source(buf.Bytes())
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Fatalf("formatting: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						outPath := strings.TrimSuffix(path, ".go") + "_gen.go"
 | 
				
			||||||
 | 
						outFile, err := os.Create(outPath)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Fatalf("create file: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						_, err = outFile.Write(formatted)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Fatalf("writing output: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						outFile.Close()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func exprToString(expr ast.Expr) string {
 | 
				
			||||||
 | 
						if t, ok := expr.(*ast.IndexExpr); ok {
 | 
				
			||||||
 | 
							if ident, ok := t.X.(*ast.Ident); ok && ident.Name == "OverridableField" {
 | 
				
			||||||
 | 
								return exprToString(t.Index) // T
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var buf bytes.Buffer
 | 
				
			||||||
 | 
						if err := format.Node(&buf, token.NewFileSet(), expr); err != nil {
 | 
				
			||||||
 | 
							return "<invalid>"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return buf.String()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func isOverridableField(expr ast.Expr) bool {
 | 
				
			||||||
 | 
						indexExpr, ok := expr.(*ast.IndexExpr)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ident, ok := indexExpr.X.(*ast.Ident)
 | 
				
			||||||
 | 
						return ok && ident.Name == "OverridableField"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										416
									
								
								generators/plugin-generator/main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										416
									
								
								generators/plugin-generator/main.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,416 @@
 | 
				
			|||||||
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"go/ast"
 | 
				
			||||||
 | 
						"go/format"
 | 
				
			||||||
 | 
						"go/parser"
 | 
				
			||||||
 | 
						"go/token"
 | 
				
			||||||
 | 
						"log"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"text/template"
 | 
				
			||||||
 | 
						"unicode"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"golang.org/x/text/cases"
 | 
				
			||||||
 | 
						"golang.org/x/text/language"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type MethodInfo struct {
 | 
				
			||||||
 | 
						Name       string
 | 
				
			||||||
 | 
						Params     []ParamInfo
 | 
				
			||||||
 | 
						Results    []ResultInfo
 | 
				
			||||||
 | 
						EntityName string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ParamInfo struct {
 | 
				
			||||||
 | 
						Name string
 | 
				
			||||||
 | 
						Type string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ResultInfo struct {
 | 
				
			||||||
 | 
						Name  string
 | 
				
			||||||
 | 
						Type  string
 | 
				
			||||||
 | 
						Index int
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func extractImports(node *ast.File) []string {
 | 
				
			||||||
 | 
						var imports []string
 | 
				
			||||||
 | 
						for _, imp := range node.Imports {
 | 
				
			||||||
 | 
							if imp.Path.Value != "" {
 | 
				
			||||||
 | 
								imports = append(imports, imp.Path.Value)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return imports
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func output(path string, buf bytes.Buffer) {
 | 
				
			||||||
 | 
						formatted, err := format.Source(buf.Bytes())
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Fatalf("formatting: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						outPath := strings.TrimSuffix(path, ".go") + "_gen.go"
 | 
				
			||||||
 | 
						outFile, err := os.Create(outPath)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Fatalf("create file: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						_, err = outFile.Write(formatted)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Fatalf("writing output: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						outFile.Close()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func main() {
 | 
				
			||||||
 | 
						path := os.Getenv("GOFILE")
 | 
				
			||||||
 | 
						if path == "" {
 | 
				
			||||||
 | 
							log.Fatal("GOFILE must be set")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(os.Args) < 2 {
 | 
				
			||||||
 | 
							log.Fatal("At least one entity name must be provided")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						entityNames := os.Args[1:]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fset := token.NewFileSet()
 | 
				
			||||||
 | 
						node, err := parser.ParseFile(fset, path, nil, parser.AllErrors)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Fatalf("parsing file: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						packageName := node.Name.Name
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Find all specified entities
 | 
				
			||||||
 | 
						entityData := make(map[string][]*ast.Field)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, decl := range node.Decls {
 | 
				
			||||||
 | 
							genDecl, ok := decl.(*ast.GenDecl)
 | 
				
			||||||
 | 
							if !ok || genDecl.Tok != token.TYPE {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							for _, spec := range genDecl.Specs {
 | 
				
			||||||
 | 
								typeSpec := spec.(*ast.TypeSpec)
 | 
				
			||||||
 | 
								for _, entityName := range entityNames {
 | 
				
			||||||
 | 
									if typeSpec.Name.Name == entityName {
 | 
				
			||||||
 | 
										interfaceType, ok := typeSpec.Type.(*ast.InterfaceType)
 | 
				
			||||||
 | 
										if !ok {
 | 
				
			||||||
 | 
											log.Fatalf("entity %s is not an interface", entityName)
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										entityData[entityName] = interfaceType.Methods.List
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Verify all entities were found
 | 
				
			||||||
 | 
						for _, entityName := range entityNames {
 | 
				
			||||||
 | 
							if _, found := entityData[entityName]; !found {
 | 
				
			||||||
 | 
								log.Fatalf("interface %s not found", entityName)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var buf bytes.Buffer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						buf.WriteString(`
 | 
				
			||||||
 | 
					// DO NOT EDIT MANUALLY. This file is generated.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						buf.WriteString(fmt.Sprintf("package %s\n", packageName))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Generate base structures for all entities
 | 
				
			||||||
 | 
						baseStructs(&buf, entityNames, extractImports(node))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Generate method-specific code for each entity
 | 
				
			||||||
 | 
						for _, entityName := range entityNames {
 | 
				
			||||||
 | 
							methods := parseMethodsFromFields(entityName, entityData[entityName])
 | 
				
			||||||
 | 
							argsGen(&buf, methods)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						output(path, buf)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func parseMethodsFromFields(entityName string, fields []*ast.Field) []MethodInfo {
 | 
				
			||||||
 | 
						var methods []MethodInfo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, field := range fields {
 | 
				
			||||||
 | 
							if len(field.Names) == 0 {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							methodName := field.Names[0].Name
 | 
				
			||||||
 | 
							funcType, ok := field.Type.(*ast.FuncType)
 | 
				
			||||||
 | 
							if !ok {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							method := MethodInfo{
 | 
				
			||||||
 | 
								Name:       methodName,
 | 
				
			||||||
 | 
								EntityName: entityName,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Parse parameters, excluding context.Context
 | 
				
			||||||
 | 
							if funcType.Params != nil {
 | 
				
			||||||
 | 
								for i, param := range funcType.Params.List {
 | 
				
			||||||
 | 
									paramType := typeToString(param.Type)
 | 
				
			||||||
 | 
									// Skip context.Context parameters
 | 
				
			||||||
 | 
									if paramType == "context.Context" {
 | 
				
			||||||
 | 
										continue
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if len(param.Names) == 0 {
 | 
				
			||||||
 | 
										method.Params = append(method.Params, ParamInfo{
 | 
				
			||||||
 | 
											Name: fmt.Sprintf("Arg%d", i),
 | 
				
			||||||
 | 
											Type: paramType,
 | 
				
			||||||
 | 
										})
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										for _, name := range param.Names {
 | 
				
			||||||
 | 
											method.Params = append(method.Params, ParamInfo{
 | 
				
			||||||
 | 
												Name: cases.Title(language.Und, cases.NoLower).String(name.Name),
 | 
				
			||||||
 | 
												Type: paramType,
 | 
				
			||||||
 | 
											})
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Parse results
 | 
				
			||||||
 | 
							if funcType.Results != nil {
 | 
				
			||||||
 | 
								resultIndex := 0
 | 
				
			||||||
 | 
								for _, result := range funcType.Results.List {
 | 
				
			||||||
 | 
									resultType := typeToString(result.Type)
 | 
				
			||||||
 | 
									if resultType == "error" {
 | 
				
			||||||
 | 
										continue // Skip error in response struct
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if len(result.Names) == 0 {
 | 
				
			||||||
 | 
										method.Results = append(method.Results, ResultInfo{
 | 
				
			||||||
 | 
											Name:  fmt.Sprintf("Result%d", resultIndex),
 | 
				
			||||||
 | 
											Type:  resultType,
 | 
				
			||||||
 | 
											Index: resultIndex,
 | 
				
			||||||
 | 
										})
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										for _, name := range result.Names {
 | 
				
			||||||
 | 
											method.Results = append(method.Results, ResultInfo{
 | 
				
			||||||
 | 
												Name:  cases.Title(language.Und, cases.NoLower).String(name.Name),
 | 
				
			||||||
 | 
												Type:  resultType,
 | 
				
			||||||
 | 
												Index: resultIndex,
 | 
				
			||||||
 | 
											})
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									resultIndex++
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							methods = append(methods, method)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return methods
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func argsGen(buf *bytes.Buffer, methods []MethodInfo) {
 | 
				
			||||||
 | 
						// Add template functions first
 | 
				
			||||||
 | 
						funcMap := template.FuncMap{
 | 
				
			||||||
 | 
							"lowerFirst": func(s string) string {
 | 
				
			||||||
 | 
								if len(s) == 0 {
 | 
				
			||||||
 | 
									return s
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return strings.ToLower(s[:1]) + s[1:]
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"zeroValue": func(typeName string) string {
 | 
				
			||||||
 | 
								typeName = strings.TrimSpace(typeName)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								switch typeName {
 | 
				
			||||||
 | 
								case "string":
 | 
				
			||||||
 | 
									return "\"\""
 | 
				
			||||||
 | 
								case "int", "int8", "int16", "int32", "int64":
 | 
				
			||||||
 | 
									return "0"
 | 
				
			||||||
 | 
								case "uint", "uint8", "uint16", "uint32", "uint64":
 | 
				
			||||||
 | 
									return "0"
 | 
				
			||||||
 | 
								case "float32", "float64":
 | 
				
			||||||
 | 
									return "0.0"
 | 
				
			||||||
 | 
								case "bool":
 | 
				
			||||||
 | 
									return "false"
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if strings.HasPrefix(typeName, "*") {
 | 
				
			||||||
 | 
									return "nil"
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if strings.HasPrefix(typeName, "[]") ||
 | 
				
			||||||
 | 
									strings.HasPrefix(typeName, "map[") ||
 | 
				
			||||||
 | 
									strings.HasPrefix(typeName, "chan ") {
 | 
				
			||||||
 | 
									return "nil"
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if typeName == "interface{}" {
 | 
				
			||||||
 | 
									return "nil"
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// If external type: pkg.Type
 | 
				
			||||||
 | 
								if strings.Contains(typeName, ".") {
 | 
				
			||||||
 | 
									return typeName + "{}"
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// If starts with uppercase — likely struct
 | 
				
			||||||
 | 
								if len(typeName) > 0 && unicode.IsUpper(rune(typeName[0])) {
 | 
				
			||||||
 | 
									return typeName + "{}"
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								return "nil"
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						argsTemplate := template.Must(template.New("args").Funcs(funcMap).Parse(`
 | 
				
			||||||
 | 
					{{range .}}
 | 
				
			||||||
 | 
					type {{.EntityName}}{{.Name}}Args struct {
 | 
				
			||||||
 | 
					{{range .Params}}	{{.Name}} {{.Type}}
 | 
				
			||||||
 | 
					{{end}}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type {{.EntityName}}{{.Name}}Resp struct {
 | 
				
			||||||
 | 
					{{range .Results}}	{{.Name}} {{.Type}}
 | 
				
			||||||
 | 
					{{end}}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *{{.EntityName}}RPC) {{.Name}}(ctx context.Context, {{range $i, $p := .Params}}{{if $i}}, {{end}}{{lowerFirst $p.Name}} {{$p.Type}}{{end}}) ({{range $i, $r := .Results}}{{if $i}}, {{end}}{{$r.Type}}{{end}}{{if .Results}}, {{end}}error) {
 | 
				
			||||||
 | 
						var resp *{{.EntityName}}{{.Name}}Resp
 | 
				
			||||||
 | 
						err := s.client.Call("Plugin.{{.Name}}", &{{.EntityName}}{{.Name}}Args{
 | 
				
			||||||
 | 
					{{range .Params}}		{{.Name}}: {{lowerFirst .Name}},
 | 
				
			||||||
 | 
					{{end}}	}, &resp)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return {{range $i, $r := .Results}}{{if $i}}, {{end}}{{zeroValue $r.Type}}{{end}}{{if .Results}}, {{end}}err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return {{range $i, $r := .Results}}{{if $i}}, {{end}}resp.{{$r.Name}}{{end}}{{if .Results}}, {{end}}nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *{{.EntityName}}RPCServer) {{.Name}}(args *{{.EntityName}}{{.Name}}Args, resp *{{.EntityName}}{{.Name}}Resp) error {
 | 
				
			||||||
 | 
						{{if .Results}}{{range $i, $r := .Results}}{{if $i}}, {{end}}{{lowerFirst $r.Name}}{{end}}, err := {{else}}err := {{end}}s.Impl.{{.Name}}(context.Background(),{{range $i, $p := .Params}}{{if $i}}, {{end}}args.{{$p.Name}}{{end}})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						{{if .Results}}*resp = {{.EntityName}}{{.Name}}Resp{
 | 
				
			||||||
 | 
					{{range .Results}}		{{.Name}}: {{lowerFirst .Name}},
 | 
				
			||||||
 | 
					{{end}}	}
 | 
				
			||||||
 | 
						{{else}}*resp = {{.EntityName}}{{.Name}}Resp{}
 | 
				
			||||||
 | 
						{{end}}return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					{{end}}
 | 
				
			||||||
 | 
					`))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err := argsTemplate.Execute(buf, methods)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Fatalf("execute args template: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func typeToString(expr ast.Expr) string {
 | 
				
			||||||
 | 
						switch t := expr.(type) {
 | 
				
			||||||
 | 
						case *ast.Ident:
 | 
				
			||||||
 | 
							return t.Name
 | 
				
			||||||
 | 
						case *ast.StarExpr:
 | 
				
			||||||
 | 
							return "*" + typeToString(t.X)
 | 
				
			||||||
 | 
						case *ast.ArrayType:
 | 
				
			||||||
 | 
							return "[]" + typeToString(t.Elt)
 | 
				
			||||||
 | 
						case *ast.SelectorExpr:
 | 
				
			||||||
 | 
							xStr := typeToString(t.X)
 | 
				
			||||||
 | 
							if xStr == "context" && t.Sel.Name == "Context" {
 | 
				
			||||||
 | 
								return "context.Context"
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return xStr + "." + t.Sel.Name
 | 
				
			||||||
 | 
						case *ast.InterfaceType:
 | 
				
			||||||
 | 
							return "interface{}"
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							return "interface{}"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func baseStructs(buf *bytes.Buffer, entityNames, imports []string) {
 | 
				
			||||||
 | 
						// Ensure "context" is included in imports
 | 
				
			||||||
 | 
						updatedImports := imports
 | 
				
			||||||
 | 
						hasContext := false
 | 
				
			||||||
 | 
						for _, imp := range imports {
 | 
				
			||||||
 | 
							if strings.Contains(imp, `"context"`) {
 | 
				
			||||||
 | 
								hasContext = true
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if !hasContext {
 | 
				
			||||||
 | 
							updatedImports = append(updatedImports, `"context"`)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						contentTemplate := template.Must(template.New("").Parse(`
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"net/rpc"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/hashicorp/go-plugin"
 | 
				
			||||||
 | 
					{{range .Imports}}	{{.}}
 | 
				
			||||||
 | 
					{{end}}
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{{range .EntityNames}}
 | 
				
			||||||
 | 
					type {{ . }}Plugin struct {
 | 
				
			||||||
 | 
						Impl {{ . }}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type {{ . }}RPCServer struct {
 | 
				
			||||||
 | 
						Impl {{ . }}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type {{ . }}RPC struct {
 | 
				
			||||||
 | 
						client *rpc.Client
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *{{ . }}Plugin) Client(b *plugin.MuxBroker, c *rpc.Client) (interface{}, error) {
 | 
				
			||||||
 | 
						return &{{ . }}RPC{client: c}, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *{{ . }}Plugin) Server(*plugin.MuxBroker) (interface{}, error) {
 | 
				
			||||||
 | 
						return &{{ . }}RPCServer{Impl: p.Impl}, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{{end}}
 | 
				
			||||||
 | 
					`))
 | 
				
			||||||
 | 
						err := contentTemplate.Execute(buf, struct {
 | 
				
			||||||
 | 
							EntityNames []string
 | 
				
			||||||
 | 
							Imports     []string
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							EntityNames: entityNames,
 | 
				
			||||||
 | 
							Imports:     updatedImports,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Fatalf("execute template: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										95
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										95
									
								
								go.mod
									
									
									
									
									
								
							@@ -1,45 +1,49 @@
 | 
				
			|||||||
module gitea.plemya-x.ru/Plemya-x/ALR
 | 
					module gitea.plemya-x.ru/Plemya-x/ALR
 | 
				
			||||||
 | 
					
 | 
				
			||||||
go 1.22
 | 
					go 1.24.4
 | 
				
			||||||
 | 
					 | 
				
			||||||
toolchain go1.23.5
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
require (
 | 
					require (
 | 
				
			||||||
	gitea.plemya-x.ru/Plemya-x/fakeroot v0.0.1
 | 
						gitea.plemya-x.ru/Plemya-x/fakeroot v0.0.2-0.20250408104831-427aaa7713c3
 | 
				
			||||||
	github.com/AlecAivazis/survey/v2 v2.3.7
 | 
						github.com/AlecAivazis/survey/v2 v2.3.7
 | 
				
			||||||
	github.com/PuerkitoBio/purell v1.2.0
 | 
						github.com/PuerkitoBio/purell v1.2.0
 | 
				
			||||||
	github.com/alecthomas/assert/v2 v2.2.1
 | 
					 | 
				
			||||||
	github.com/alecthomas/chroma/v2 v2.9.1
 | 
						github.com/alecthomas/chroma/v2 v2.9.1
 | 
				
			||||||
	github.com/caarlos0/env v3.5.0+incompatible
 | 
						github.com/bmatcuk/doublestar/v4 v4.8.1
 | 
				
			||||||
	github.com/charmbracelet/bubbles v0.20.0
 | 
						github.com/charmbracelet/bubbles v0.20.0
 | 
				
			||||||
	github.com/charmbracelet/bubbletea v1.2.4
 | 
						github.com/charmbracelet/bubbletea v1.2.4
 | 
				
			||||||
	github.com/charmbracelet/lipgloss v1.0.0
 | 
						github.com/charmbracelet/lipgloss v1.0.0
 | 
				
			||||||
	github.com/charmbracelet/log v0.4.0
 | 
						github.com/charmbracelet/log v0.4.0
 | 
				
			||||||
	github.com/efficientgo/e2e v0.14.1-0.20240418111536-97db25a0c6c0
 | 
						github.com/go-git/go-billy/v5 v5.6.0
 | 
				
			||||||
	github.com/go-git/go-billy/v5 v5.5.0
 | 
						github.com/go-git/go-git/v5 v5.13.0
 | 
				
			||||||
	github.com/go-git/go-git/v5 v5.12.0
 | 
						github.com/goccy/go-yaml v1.18.0
 | 
				
			||||||
	github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
 | 
						github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
 | 
				
			||||||
	github.com/goreleaser/nfpm/v2 v2.41.0
 | 
						github.com/goreleaser/nfpm/v2 v2.41.0
 | 
				
			||||||
 | 
						github.com/hashicorp/go-hclog v0.14.1
 | 
				
			||||||
 | 
						github.com/hashicorp/go-plugin v1.6.3
 | 
				
			||||||
	github.com/jeandeaual/go-locale v0.0.0-20241217141322-fcc2cadd6f08
 | 
						github.com/jeandeaual/go-locale v0.0.0-20241217141322-fcc2cadd6f08
 | 
				
			||||||
	github.com/jmoiron/sqlx v1.3.5
 | 
						github.com/knadh/koanf/parsers/toml/v2 v2.2.0
 | 
				
			||||||
 | 
						github.com/knadh/koanf/providers/confmap v1.0.0
 | 
				
			||||||
 | 
						github.com/knadh/koanf/providers/env v1.1.0
 | 
				
			||||||
 | 
						github.com/knadh/koanf/providers/file v1.2.0
 | 
				
			||||||
 | 
						github.com/knadh/koanf/v2 v2.2.1
 | 
				
			||||||
	github.com/leonelquinteros/gotext v1.7.0
 | 
						github.com/leonelquinteros/gotext v1.7.0
 | 
				
			||||||
	github.com/mattn/go-isatty v0.0.20
 | 
						github.com/mattn/go-isatty v0.0.20
 | 
				
			||||||
	github.com/mholt/archiver/v4 v4.0.0-alpha.8
 | 
						github.com/mholt/archiver/v4 v4.0.0-alpha.8
 | 
				
			||||||
	github.com/mitchellh/mapstructure v1.5.0
 | 
						github.com/mitchellh/mapstructure v1.5.0
 | 
				
			||||||
	github.com/muesli/reflow v0.3.0
 | 
						github.com/muesli/reflow v0.3.0
 | 
				
			||||||
	github.com/pelletier/go-toml/v2 v2.1.0
 | 
						github.com/pelletier/go-toml/v2 v2.2.4
 | 
				
			||||||
	github.com/stretchr/testify v1.10.0
 | 
						github.com/stretchr/testify v1.10.0
 | 
				
			||||||
	github.com/tailscale/goexpect v0.0.0-20210902213824-6e8c725cea41
 | 
					 | 
				
			||||||
	github.com/urfave/cli/v2 v2.25.7
 | 
						github.com/urfave/cli/v2 v2.25.7
 | 
				
			||||||
	github.com/vmihailenco/msgpack/v5 v5.3.5
 | 
						github.com/vmihailenco/msgpack/v5 v5.3.5
 | 
				
			||||||
 | 
						go.alt-gnome.ru/capytest v0.0.3-0.20250706082755-f20413e052f9
 | 
				
			||||||
 | 
						go.alt-gnome.ru/capytest/providers/podman v0.0.3-0.20250706082755-f20413e052f9
 | 
				
			||||||
	go.elara.ws/vercmp v0.0.0-20230622214216-0b2b067575c4
 | 
						go.elara.ws/vercmp v0.0.0-20230622214216-0b2b067575c4
 | 
				
			||||||
	golang.org/x/crypto v0.27.0
 | 
						golang.org/x/crypto v0.36.0
 | 
				
			||||||
	golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb
 | 
						golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56
 | 
				
			||||||
	golang.org/x/sys v0.29.0
 | 
						golang.org/x/sys v0.33.0
 | 
				
			||||||
	golang.org/x/text v0.21.0
 | 
						golang.org/x/text v0.23.0
 | 
				
			||||||
	gopkg.in/yaml.v3 v3.0.1
 | 
					 | 
				
			||||||
	modernc.org/sqlite v1.25.0
 | 
						modernc.org/sqlite v1.25.0
 | 
				
			||||||
	mvdan.cc/sh/v3 v3.10.0
 | 
						mvdan.cc/sh/v3 v3.10.0
 | 
				
			||||||
 | 
						xorm.io/xorm v1.3.9
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
require (
 | 
					require (
 | 
				
			||||||
@@ -49,8 +53,7 @@ require (
 | 
				
			|||||||
	github.com/Masterminds/semver/v3 v3.3.0 // indirect
 | 
						github.com/Masterminds/semver/v3 v3.3.0 // indirect
 | 
				
			||||||
	github.com/Masterminds/sprig/v3 v3.2.3 // indirect
 | 
						github.com/Masterminds/sprig/v3 v3.2.3 // indirect
 | 
				
			||||||
	github.com/Microsoft/go-winio v0.6.1 // indirect
 | 
						github.com/Microsoft/go-winio v0.6.1 // indirect
 | 
				
			||||||
	github.com/ProtonMail/go-crypto v1.0.0 // indirect
 | 
						github.com/ProtonMail/go-crypto v1.1.3 // indirect
 | 
				
			||||||
	github.com/alecthomas/repr v0.2.0 // indirect
 | 
					 | 
				
			||||||
	github.com/andybalholm/brotli v1.0.4 // indirect
 | 
						github.com/andybalholm/brotli v1.0.4 // indirect
 | 
				
			||||||
	github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
 | 
						github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
 | 
				
			||||||
	github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb // indirect
 | 
						github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb // indirect
 | 
				
			||||||
@@ -61,71 +64,96 @@ require (
 | 
				
			|||||||
	github.com/charmbracelet/harmonica v0.2.0 // indirect
 | 
						github.com/charmbracelet/harmonica v0.2.0 // indirect
 | 
				
			||||||
	github.com/charmbracelet/x/ansi v0.4.5 // indirect
 | 
						github.com/charmbracelet/x/ansi v0.4.5 // indirect
 | 
				
			||||||
	github.com/charmbracelet/x/term v0.2.1 // indirect
 | 
						github.com/charmbracelet/x/term v0.2.1 // indirect
 | 
				
			||||||
	github.com/cloudflare/circl v1.3.8 // indirect
 | 
						github.com/cloudflare/circl v1.6.1 // indirect
 | 
				
			||||||
	github.com/connesc/cipherio v0.2.1 // indirect
 | 
						github.com/connesc/cipherio v0.2.1 // indirect
 | 
				
			||||||
	github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
 | 
						github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
 | 
				
			||||||
	github.com/cyphar/filepath-securejoin v0.2.4 // indirect
 | 
						github.com/creack/pty v1.1.24 // indirect
 | 
				
			||||||
 | 
						github.com/cyphar/filepath-securejoin v0.2.5 // indirect
 | 
				
			||||||
	github.com/davecgh/go-spew v1.1.1 // indirect
 | 
						github.com/davecgh/go-spew v1.1.1 // indirect
 | 
				
			||||||
	github.com/dlclark/regexp2 v1.10.0 // indirect
 | 
						github.com/dlclark/regexp2 v1.10.0 // indirect
 | 
				
			||||||
	github.com/dsnet/compress v0.0.1 // indirect
 | 
						github.com/dsnet/compress v0.0.1 // indirect
 | 
				
			||||||
	github.com/dustin/go-humanize v1.0.1 // indirect
 | 
						github.com/dustin/go-humanize v1.0.1 // indirect
 | 
				
			||||||
	github.com/efficientgo/core v1.0.0-rc.0 // indirect
 | 
					 | 
				
			||||||
	github.com/emirpasic/gods v1.18.1 // indirect
 | 
						github.com/emirpasic/gods v1.18.1 // indirect
 | 
				
			||||||
	github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
 | 
						github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
 | 
				
			||||||
 | 
						github.com/fatih/color v1.7.0 // indirect
 | 
				
			||||||
 | 
						github.com/fsnotify/fsnotify v1.9.0 // indirect
 | 
				
			||||||
 | 
						github.com/gkampitakis/ciinfo v0.3.2 // indirect
 | 
				
			||||||
 | 
						github.com/gkampitakis/go-diff v1.3.2 // indirect
 | 
				
			||||||
 | 
						github.com/gkampitakis/go-snaps v0.5.13 // indirect
 | 
				
			||||||
	github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
 | 
						github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
 | 
				
			||||||
	github.com/go-logfmt/logfmt v0.6.0 // indirect
 | 
						github.com/go-logfmt/logfmt v0.6.0 // indirect
 | 
				
			||||||
 | 
						github.com/go-viper/mapstructure/v2 v2.3.0 // indirect
 | 
				
			||||||
	github.com/gobwas/glob v0.2.3 // indirect
 | 
						github.com/gobwas/glob v0.2.3 // indirect
 | 
				
			||||||
 | 
						github.com/goccy/go-json v0.8.1 // indirect
 | 
				
			||||||
	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
 | 
						github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
 | 
				
			||||||
 | 
						github.com/golang/protobuf v1.5.3 // indirect
 | 
				
			||||||
	github.com/golang/snappy v0.0.4 // indirect
 | 
						github.com/golang/snappy v0.0.4 // indirect
 | 
				
			||||||
	github.com/google/goterm v0.0.0-20190703233501-fc88cf888a3f // indirect
 | 
					 | 
				
			||||||
	github.com/google/rpmpack v0.6.1-0.20240329070804-c2247cbb881a // indirect
 | 
						github.com/google/rpmpack v0.6.1-0.20240329070804-c2247cbb881a // indirect
 | 
				
			||||||
	github.com/google/uuid v1.4.0 // indirect
 | 
						github.com/google/uuid v1.6.0 // indirect
 | 
				
			||||||
	github.com/goreleaser/chglog v0.6.1 // indirect
 | 
						github.com/goreleaser/chglog v0.6.1 // indirect
 | 
				
			||||||
	github.com/goreleaser/fileglob v1.3.0 // indirect
 | 
						github.com/goreleaser/fileglob v1.3.0 // indirect
 | 
				
			||||||
	github.com/hashicorp/errwrap v1.0.0 // indirect
 | 
						github.com/hashicorp/errwrap v1.0.0 // indirect
 | 
				
			||||||
	github.com/hashicorp/go-multierror v1.1.1 // indirect
 | 
						github.com/hashicorp/go-multierror v1.1.1 // indirect
 | 
				
			||||||
	github.com/hexops/gotextdiff v1.0.3 // indirect
 | 
						github.com/hashicorp/yamux v0.1.1 // indirect
 | 
				
			||||||
	github.com/huandu/xstrings v1.3.3 // indirect
 | 
						github.com/huandu/xstrings v1.3.3 // indirect
 | 
				
			||||||
	github.com/imdario/mergo v0.3.16 // indirect
 | 
						github.com/imdario/mergo v0.3.16 // indirect
 | 
				
			||||||
	github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
 | 
						github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
 | 
				
			||||||
 | 
						github.com/json-iterator/go v1.1.12 // indirect
 | 
				
			||||||
	github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
 | 
						github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
 | 
				
			||||||
	github.com/kevinburke/ssh_config v1.2.0 // indirect
 | 
						github.com/kevinburke/ssh_config v1.2.0 // indirect
 | 
				
			||||||
	github.com/klauspost/compress v1.17.11 // indirect
 | 
						github.com/klauspost/compress v1.17.11 // indirect
 | 
				
			||||||
	github.com/klauspost/pgzip v1.2.6 // indirect
 | 
						github.com/klauspost/pgzip v1.2.6 // indirect
 | 
				
			||||||
 | 
						github.com/knadh/koanf/maps v0.1.2 // indirect
 | 
				
			||||||
 | 
						github.com/kr/pretty v0.3.1 // indirect
 | 
				
			||||||
 | 
						github.com/kr/text v0.2.0 // indirect
 | 
				
			||||||
	github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
 | 
						github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
 | 
				
			||||||
 | 
						github.com/maruel/natural v1.1.1 // indirect
 | 
				
			||||||
	github.com/mattn/go-colorable v0.1.13 // indirect
 | 
						github.com/mattn/go-colorable v0.1.13 // indirect
 | 
				
			||||||
	github.com/mattn/go-localereader v0.0.1 // indirect
 | 
						github.com/mattn/go-localereader v0.0.1 // indirect
 | 
				
			||||||
	github.com/mattn/go-runewidth v0.0.16 // indirect
 | 
						github.com/mattn/go-runewidth v0.0.16 // indirect
 | 
				
			||||||
	github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
 | 
						github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
 | 
				
			||||||
	github.com/mitchellh/copystructure v1.2.0 // indirect
 | 
						github.com/mitchellh/copystructure v1.2.0 // indirect
 | 
				
			||||||
	github.com/mitchellh/reflectwalk v1.0.2 // indirect
 | 
						github.com/mitchellh/reflectwalk v1.0.2 // indirect
 | 
				
			||||||
 | 
						github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
 | 
				
			||||||
 | 
						github.com/modern-go/reflect2 v1.0.2 // indirect
 | 
				
			||||||
	github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
 | 
						github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
 | 
				
			||||||
	github.com/muesli/cancelreader v0.2.2 // indirect
 | 
						github.com/muesli/cancelreader v0.2.2 // indirect
 | 
				
			||||||
	github.com/muesli/termenv v0.15.2 // indirect
 | 
						github.com/muesli/termenv v0.15.2 // indirect
 | 
				
			||||||
	github.com/nwaples/rardecode/v2 v2.0.0-beta.2 // indirect
 | 
						github.com/nwaples/rardecode/v2 v2.0.0-beta.2 // indirect
 | 
				
			||||||
 | 
						github.com/oklog/run v1.0.0 // indirect
 | 
				
			||||||
	github.com/pierrec/lz4/v4 v4.1.15 // indirect
 | 
						github.com/pierrec/lz4/v4 v4.1.15 // indirect
 | 
				
			||||||
	github.com/pjbgf/sha1cd v0.3.0 // indirect
 | 
						github.com/pjbgf/sha1cd v0.3.0 // indirect
 | 
				
			||||||
	github.com/pmezard/go-difflib v1.0.0 // indirect
 | 
						github.com/pmezard/go-difflib v1.0.0 // indirect
 | 
				
			||||||
	github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
 | 
						github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
 | 
				
			||||||
	github.com/rivo/uniseg v0.4.7 // indirect
 | 
						github.com/rivo/uniseg v0.4.7 // indirect
 | 
				
			||||||
 | 
						github.com/rogpeppe/go-internal v1.13.1 // indirect
 | 
				
			||||||
	github.com/russross/blackfriday/v2 v2.1.0 // indirect
 | 
						github.com/russross/blackfriday/v2 v2.1.0 // indirect
 | 
				
			||||||
	github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
 | 
						github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
 | 
				
			||||||
	github.com/shopspring/decimal v1.2.0 // indirect
 | 
						github.com/shopspring/decimal v1.3.1 // indirect
 | 
				
			||||||
	github.com/skeema/knownhosts v1.2.2 // indirect
 | 
						github.com/skeema/knownhosts v1.3.0 // indirect
 | 
				
			||||||
	github.com/spf13/cast v1.6.0 // indirect
 | 
						github.com/spf13/cast v1.7.1 // indirect
 | 
				
			||||||
 | 
						github.com/syndtr/goleveldb v1.0.0 // indirect
 | 
				
			||||||
	github.com/therootcompany/xz v1.0.1 // indirect
 | 
						github.com/therootcompany/xz v1.0.1 // indirect
 | 
				
			||||||
 | 
						github.com/tidwall/gjson v1.18.0 // indirect
 | 
				
			||||||
 | 
						github.com/tidwall/match v1.1.1 // indirect
 | 
				
			||||||
 | 
						github.com/tidwall/pretty v1.2.1 // indirect
 | 
				
			||||||
 | 
						github.com/tidwall/sjson v1.2.5 // indirect
 | 
				
			||||||
	github.com/ulikunitz/xz v0.5.12 // indirect
 | 
						github.com/ulikunitz/xz v0.5.12 // indirect
 | 
				
			||||||
	github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
 | 
						github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
 | 
				
			||||||
	github.com/xanzy/ssh-agent v0.3.3 // indirect
 | 
						github.com/xanzy/ssh-agent v0.3.3 // indirect
 | 
				
			||||||
	github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
 | 
						github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
 | 
				
			||||||
	gitlab.com/digitalxero/go-conventional-commit v1.0.7 // indirect
 | 
						gitlab.com/digitalxero/go-conventional-commit v1.0.7 // indirect
 | 
				
			||||||
	go4.org v0.0.0-20200411211856-f5505b9728dd // indirect
 | 
						go4.org v0.0.0-20200411211856-f5505b9728dd // indirect
 | 
				
			||||||
	golang.org/x/mod v0.18.0 // indirect
 | 
						golang.org/x/mod v0.19.0 // indirect
 | 
				
			||||||
	golang.org/x/net v0.26.0 // indirect
 | 
						golang.org/x/net v0.38.0 // indirect
 | 
				
			||||||
	golang.org/x/sync v0.10.0 // indirect
 | 
						golang.org/x/sync v0.12.0 // indirect
 | 
				
			||||||
	golang.org/x/term v0.28.0 // indirect
 | 
						golang.org/x/term v0.30.0 // indirect
 | 
				
			||||||
	golang.org/x/tools v0.22.0 // indirect
 | 
						golang.org/x/tools v0.23.0 // indirect
 | 
				
			||||||
 | 
						google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8 // indirect
 | 
				
			||||||
 | 
						google.golang.org/grpc v1.67.3 // indirect
 | 
				
			||||||
 | 
						google.golang.org/protobuf v1.36.1 // indirect
 | 
				
			||||||
	gopkg.in/warnings.v0 v0.1.2 // indirect
 | 
						gopkg.in/warnings.v0 v0.1.2 // indirect
 | 
				
			||||||
 | 
						gopkg.in/yaml.v3 v3.0.1 // indirect
 | 
				
			||||||
	lukechampine.com/uint128 v1.2.0 // indirect
 | 
						lukechampine.com/uint128 v1.2.0 // indirect
 | 
				
			||||||
	modernc.org/cc/v3 v3.40.0 // indirect
 | 
						modernc.org/cc/v3 v3.40.0 // indirect
 | 
				
			||||||
	modernc.org/ccgo/v3 v3.16.13 // indirect
 | 
						modernc.org/ccgo/v3 v3.16.13 // indirect
 | 
				
			||||||
@@ -135,4 +163,5 @@ require (
 | 
				
			|||||||
	modernc.org/opt v0.1.3 // indirect
 | 
						modernc.org/opt v0.1.3 // indirect
 | 
				
			||||||
	modernc.org/strutil v1.1.3 // indirect
 | 
						modernc.org/strutil v1.1.3 // indirect
 | 
				
			||||||
	modernc.org/token v1.0.1 // indirect
 | 
						modernc.org/token v1.0.1 // indirect
 | 
				
			||||||
 | 
						xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978 // indirect
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										268
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										268
									
								
								go.sum
									
									
									
									
									
								
							@@ -17,8 +17,10 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo
 | 
				
			|||||||
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
 | 
					dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
 | 
				
			||||||
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
 | 
					dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
 | 
				
			||||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
 | 
					dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
 | 
				
			||||||
gitea.plemya-x.ru/Plemya-x/fakeroot v0.0.1 h1:c7F4OsyQbiVpSOrYGMrNsRL37BwoOfrgoKxAwULBKZo=
 | 
					gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s=
 | 
				
			||||||
gitea.plemya-x.ru/Plemya-x/fakeroot v0.0.1/go.mod h1:iKQM6uttMJgE5CFrPw6SQqAV7TKtlJNICRAie/dTciw=
 | 
					gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU=
 | 
				
			||||||
 | 
					gitea.plemya-x.ru/Plemya-x/fakeroot v0.0.2-0.20250408104831-427aaa7713c3 h1:56BjRJJ2Sv50DfSvNUydUMJwwFuiBMWC1uYtH2GYjk8=
 | 
				
			||||||
 | 
					gitea.plemya-x.ru/Plemya-x/fakeroot v0.0.2-0.20250408104831-427aaa7713c3/go.mod h1:iKQM6uttMJgE5CFrPw6SQqAV7TKtlJNICRAie/dTciw=
 | 
				
			||||||
github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ=
 | 
					github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ=
 | 
				
			||||||
github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo=
 | 
					github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo=
 | 
				
			||||||
github.com/AlekSi/pointer v1.2.0 h1:glcy/gc4h8HnG2Z3ZECSzZ1IX1x2JxRVuDzaJwQE0+w=
 | 
					github.com/AlekSi/pointer v1.2.0 h1:glcy/gc4h8HnG2Z3ZECSzZ1IX1x2JxRVuDzaJwQE0+w=
 | 
				
			||||||
@@ -39,8 +41,8 @@ github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migc
 | 
				
			|||||||
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
 | 
					github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
 | 
				
			||||||
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s=
 | 
					github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s=
 | 
				
			||||||
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
 | 
					github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
 | 
				
			||||||
github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78=
 | 
					github.com/ProtonMail/go-crypto v1.1.3 h1:nRBOetoydLeUb4nHajyO2bKqMLfWQ/ZPwkXqXxPxCFk=
 | 
				
			||||||
github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
 | 
					github.com/ProtonMail/go-crypto v1.1.3/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
 | 
				
			||||||
github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f h1:tCbYj7/299ekTTXpdwKYF8eBlsYsDVoggDAuAjoK66k=
 | 
					github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f h1:tCbYj7/299ekTTXpdwKYF8eBlsYsDVoggDAuAjoK66k=
 | 
				
			||||||
github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f/go.mod h1:gcr0kNtGBqin9zDW9GOHcVntrwnjrK+qdJ06mWYBybw=
 | 
					github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f/go.mod h1:gcr0kNtGBqin9zDW9GOHcVntrwnjrK+qdJ06mWYBybw=
 | 
				
			||||||
github.com/ProtonMail/gopenpgp/v2 v2.7.1 h1:Awsg7MPc2gD3I7IFac2qE3Gdls0lZW8SzrFZ3k1oz0s=
 | 
					github.com/ProtonMail/gopenpgp/v2 v2.7.1 h1:Awsg7MPc2gD3I7IFac2qE3Gdls0lZW8SzrFZ3k1oz0s=
 | 
				
			||||||
@@ -61,26 +63,23 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd
 | 
				
			|||||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
 | 
					github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
 | 
				
			||||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
 | 
					github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
 | 
				
			||||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
 | 
					github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
 | 
				
			||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
 | 
					 | 
				
			||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
 | 
					 | 
				
			||||||
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb h1:m935MPodAbYS46DG4pJSv7WO+VECIWUQ7OJYSoTrMh4=
 | 
					github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb h1:m935MPodAbYS46DG4pJSv7WO+VECIWUQ7OJYSoTrMh4=
 | 
				
			||||||
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI=
 | 
					github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI=
 | 
				
			||||||
 | 
					github.com/bmatcuk/doublestar/v4 v4.8.1 h1:54Bopc5c2cAvhLRAzqOGCYHYyhcDHsFF4wWIR5wKP38=
 | 
				
			||||||
 | 
					github.com/bmatcuk/doublestar/v4 v4.8.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
 | 
				
			||||||
github.com/bodgit/plumbing v1.2.0 h1:gg4haxoKphLjml+tgnecR4yLBV5zo4HAZGCtAh3xCzM=
 | 
					github.com/bodgit/plumbing v1.2.0 h1:gg4haxoKphLjml+tgnecR4yLBV5zo4HAZGCtAh3xCzM=
 | 
				
			||||||
github.com/bodgit/plumbing v1.2.0/go.mod h1:b9TeRi7Hvc6Y05rjm8VML3+47n4XTZPtQ/5ghqic2n8=
 | 
					github.com/bodgit/plumbing v1.2.0/go.mod h1:b9TeRi7Hvc6Y05rjm8VML3+47n4XTZPtQ/5ghqic2n8=
 | 
				
			||||||
github.com/bodgit/sevenzip v1.3.0 h1:1ljgELgtHqvgIp8W8kgeEGHIWP4ch3xGI8uOBZgLVKY=
 | 
					github.com/bodgit/sevenzip v1.3.0 h1:1ljgELgtHqvgIp8W8kgeEGHIWP4ch3xGI8uOBZgLVKY=
 | 
				
			||||||
github.com/bodgit/sevenzip v1.3.0/go.mod h1:omwNcgZTEooWM8gA/IJ2Nk/+ZQ94+GsytRzOJJ8FBlM=
 | 
					github.com/bodgit/sevenzip v1.3.0/go.mod h1:omwNcgZTEooWM8gA/IJ2Nk/+ZQ94+GsytRzOJJ8FBlM=
 | 
				
			||||||
github.com/bodgit/windows v1.0.0 h1:rLQ/XjsleZvx4fR1tB/UxQrK+SJ2OFHzfPjLWWOhDIA=
 | 
					github.com/bodgit/windows v1.0.0 h1:rLQ/XjsleZvx4fR1tB/UxQrK+SJ2OFHzfPjLWWOhDIA=
 | 
				
			||||||
github.com/bodgit/windows v1.0.0/go.mod h1:a6JLwrB4KrTR5hBpp8FI9/9W9jJfeQ2h4XDXU74ZCdM=
 | 
					github.com/bodgit/windows v1.0.0/go.mod h1:a6JLwrB4KrTR5hBpp8FI9/9W9jJfeQ2h4XDXU74ZCdM=
 | 
				
			||||||
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
 | 
					github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA=
 | 
				
			||||||
github.com/caarlos0/env v3.5.0+incompatible h1:Yy0UN8o9Wtr/jGHZDpCBLpNrzcFLLM2yixi/rBrKyJs=
 | 
					github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8=
 | 
				
			||||||
github.com/caarlos0/env v3.5.0+incompatible/go.mod h1:tdCsowwCzMLdkqRYDlHpZCp2UooDD3MspDBjZ2AD02Y=
 | 
					 | 
				
			||||||
github.com/caarlos0/testfs v0.4.4 h1:3PHvzHi5Lt+g332CiShwS8ogTgS3HjrmzZxCm6JCDr8=
 | 
					github.com/caarlos0/testfs v0.4.4 h1:3PHvzHi5Lt+g332CiShwS8ogTgS3HjrmzZxCm6JCDr8=
 | 
				
			||||||
github.com/caarlos0/testfs v0.4.4/go.mod h1:bRN55zgG4XCUVVHZCeU+/Tz1Q6AxEJOEJTliBy+1DMk=
 | 
					github.com/caarlos0/testfs v0.4.4/go.mod h1:bRN55zgG4XCUVVHZCeU+/Tz1Q6AxEJOEJTliBy+1DMk=
 | 
				
			||||||
github.com/cavaliergopher/cpio v1.0.1 h1:KQFSeKmZhv0cr+kawA3a0xTQCU4QxXF1vhU7P7av2KM=
 | 
					github.com/cavaliergopher/cpio v1.0.1 h1:KQFSeKmZhv0cr+kawA3a0xTQCU4QxXF1vhU7P7av2KM=
 | 
				
			||||||
github.com/cavaliergopher/cpio v1.0.1/go.mod h1:pBdaqQjnvXxdS/6CvNDwIANIFSP0xRKI16PX4xejRQc=
 | 
					github.com/cavaliergopher/cpio v1.0.1/go.mod h1:pBdaqQjnvXxdS/6CvNDwIANIFSP0xRKI16PX4xejRQc=
 | 
				
			||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
 | 
					github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
 | 
				
			||||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
 | 
					 | 
				
			||||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 | 
					 | 
				
			||||||
github.com/charmbracelet/bubbles v0.20.0 h1:jSZu6qD8cRQ6k9OMfR1WlM+ruM8fkPWkHvQWD9LIutE=
 | 
					github.com/charmbracelet/bubbles v0.20.0 h1:jSZu6qD8cRQ6k9OMfR1WlM+ruM8fkPWkHvQWD9LIutE=
 | 
				
			||||||
github.com/charmbracelet/bubbles v0.20.0/go.mod h1:39slydyswPy+uVOHZ5x/GjwVAFkCsV8IIVy+4MhzwwU=
 | 
					github.com/charmbracelet/bubbles v0.20.0/go.mod h1:39slydyswPy+uVOHZ5x/GjwVAFkCsV8IIVy+4MhzwwU=
 | 
				
			||||||
github.com/charmbracelet/bubbletea v1.2.4 h1:KN8aCViA0eps9SCOThb2/XPIlea3ANJLUkv3KnQRNCE=
 | 
					github.com/charmbracelet/bubbletea v1.2.4 h1:KN8aCViA0eps9SCOThb2/XPIlea3ANJLUkv3KnQRNCE=
 | 
				
			||||||
@@ -99,18 +98,18 @@ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWR
 | 
				
			|||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
 | 
					github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
 | 
				
			||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
 | 
					github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
 | 
				
			||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 | 
					github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 | 
				
			||||||
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
 | 
					github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
 | 
				
			||||||
github.com/cloudflare/circl v1.3.8 h1:j+V8jJt09PoeMFIu2uh5JUyEaIHTXVOHslFoLNAKqwI=
 | 
					github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
 | 
				
			||||||
github.com/cloudflare/circl v1.3.8/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU=
 | 
					 | 
				
			||||||
github.com/connesc/cipherio v0.2.1 h1:FGtpTPMbKNNWByNrr9aEBtaJtXjqOzkIXNYJp6OEycw=
 | 
					github.com/connesc/cipherio v0.2.1 h1:FGtpTPMbKNNWByNrr9aEBtaJtXjqOzkIXNYJp6OEycw=
 | 
				
			||||||
github.com/connesc/cipherio v0.2.1/go.mod h1:ukY0MWJDFnJEbXMQtOcn2VmTpRfzcTz4OoVrWGGJZcA=
 | 
					github.com/connesc/cipherio v0.2.1/go.mod h1:ukY0MWJDFnJEbXMQtOcn2VmTpRfzcTz4OoVrWGGJZcA=
 | 
				
			||||||
github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
 | 
					github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
 | 
				
			||||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
 | 
					github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
 | 
				
			||||||
 | 
					github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 | 
				
			||||||
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
 | 
					github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
 | 
				
			||||||
github.com/creack/pty v1.1.23 h1:4M6+isWdcStXEf15G/RbrMPOQj1dZ7HPZCGwE4kOeP0=
 | 
					github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
 | 
				
			||||||
github.com/creack/pty v1.1.23/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
 | 
					github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
 | 
				
			||||||
github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
 | 
					github.com/cyphar/filepath-securejoin v0.2.5 h1:6iR5tXJ/e6tJZzzdMc1km3Sa7RRIVBKAK32O2s7AYfo=
 | 
				
			||||||
github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
 | 
					github.com/cyphar/filepath-securejoin v0.2.5/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
 | 
				
			||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
					github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
				
			||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 | 
					github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 | 
				
			||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
					github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
				
			||||||
@@ -121,40 +120,53 @@ github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5Jflh
 | 
				
			|||||||
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
 | 
					github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
 | 
				
			||||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
 | 
					github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
 | 
				
			||||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
 | 
					github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
 | 
				
			||||||
github.com/efficientgo/core v1.0.0-rc.0 h1:jJoA0N+C4/knWYVZ6GrdHOtDyrg8Y/TR4vFpTaqTsqs=
 | 
					github.com/elazarl/goproxy v1.2.1 h1:njjgvO6cRG9rIqN2ebkqy6cQz2Njkx7Fsfv/zIZqgug=
 | 
				
			||||||
github.com/efficientgo/core v1.0.0-rc.0/go.mod h1:kQa0V74HNYMfuJH6jiPiwNdpWXl4xd/K4tzlrcvYDQI=
 | 
					github.com/elazarl/goproxy v1.2.1/go.mod h1:YfEbZtqP4AetfO6d40vWchF3znWX7C7Vd6ZMfdL8z64=
 | 
				
			||||||
github.com/efficientgo/e2e v0.14.1-0.20240418111536-97db25a0c6c0 h1:C/FNIs+MtAJgQYLJ9FX/ACFYyDRuLYoXTmueErrOJyA=
 | 
					 | 
				
			||||||
github.com/efficientgo/e2e v0.14.1-0.20240418111536-97db25a0c6c0/go.mod h1:plsKU0YHE9uX+7utvr7SiDtVBSHJyEfHRO4UnUgDmts=
 | 
					 | 
				
			||||||
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU=
 | 
					 | 
				
			||||||
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
 | 
					 | 
				
			||||||
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
 | 
					github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
 | 
				
			||||||
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
 | 
					github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
 | 
				
			||||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 | 
					github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 | 
				
			||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
 | 
					github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
 | 
				
			||||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
 | 
					github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
 | 
				
			||||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
 | 
					github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
 | 
				
			||||||
 | 
					github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
 | 
				
			||||||
 | 
					github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
 | 
				
			||||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
 | 
					github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
 | 
				
			||||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
 | 
					github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
 | 
				
			||||||
github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE=
 | 
					github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 | 
				
			||||||
github.com/gliderlabs/ssh v0.3.7/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8=
 | 
					github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
 | 
				
			||||||
 | 
					github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
 | 
				
			||||||
 | 
					github.com/gkampitakis/ciinfo v0.3.2 h1:JcuOPk8ZU7nZQjdUhctuhQofk7BGHuIy0c9Ez8BNhXs=
 | 
				
			||||||
 | 
					github.com/gkampitakis/ciinfo v0.3.2/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo=
 | 
				
			||||||
 | 
					github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M=
 | 
				
			||||||
 | 
					github.com/gkampitakis/go-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6OPYerdAAS9tk=
 | 
				
			||||||
 | 
					github.com/gkampitakis/go-snaps v0.5.13 h1:Hhjmvv1WboSCxkR9iU2mj5PQ8tsz/y8ECGrIbjjPF8Q=
 | 
				
			||||||
 | 
					github.com/gkampitakis/go-snaps v0.5.13/go.mod h1:HNpx/9GoKisdhw9AFOBT1N7DBs9DiHo/hGheFGBZ+mc=
 | 
				
			||||||
 | 
					github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
 | 
				
			||||||
 | 
					github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
 | 
				
			||||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
 | 
					github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
 | 
				
			||||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
 | 
					github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
 | 
				
			||||||
github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU=
 | 
					github.com/go-git/go-billy/v5 v5.6.0 h1:w2hPNtoehvJIxR00Vb4xX94qHQi/ApZfX+nBE2Cjio8=
 | 
				
			||||||
github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow=
 | 
					github.com/go-git/go-billy/v5 v5.6.0/go.mod h1:sFDq7xD3fn3E0GOwUSZqHo9lrkmx8xJhA0ZrfvjBRGM=
 | 
				
			||||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
 | 
					github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
 | 
				
			||||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
 | 
					github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
 | 
				
			||||||
github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys=
 | 
					github.com/go-git/go-git/v5 v5.13.0 h1:vLn5wlGIh/X78El6r3Jr+30W16Blk0CTcxTYcYPWi5E=
 | 
				
			||||||
github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY=
 | 
					github.com/go-git/go-git/v5 v5.13.0/go.mod h1:Wjo7/JyVKtQgUNdXYXIepzWfJQkUEIGvkvVkiXRR/zw=
 | 
				
			||||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
 | 
					github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
 | 
				
			||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
 | 
					github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
 | 
				
			||||||
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
 | 
					github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
 | 
				
			||||||
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
 | 
					github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
 | 
				
			||||||
github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
 | 
					github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
 | 
				
			||||||
github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
 | 
					github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
 | 
				
			||||||
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
 | 
					github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
 | 
				
			||||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
 | 
					github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
 | 
				
			||||||
 | 
					github.com/go-viper/mapstructure/v2 v2.3.0 h1:27XbWsHIqhbdR5TIC911OfYvgSaW93HM+dX7970Q7jk=
 | 
				
			||||||
 | 
					github.com/go-viper/mapstructure/v2 v2.3.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
 | 
				
			||||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
 | 
					github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
 | 
				
			||||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
 | 
					github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
 | 
				
			||||||
 | 
					github.com/goccy/go-json v0.8.1 h1:4/Wjm0JIJaTDm8K1KcGrLHJoa8EsJ13YWeX+6Kfq6uI=
 | 
				
			||||||
 | 
					github.com/goccy/go-json v0.8.1/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
 | 
				
			||||||
 | 
					github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
 | 
				
			||||||
 | 
					github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
 | 
				
			||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 | 
					github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 | 
				
			||||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 | 
					github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 | 
				
			||||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 | 
					github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 | 
				
			||||||
@@ -171,8 +183,10 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
 | 
				
			|||||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 | 
					github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 | 
				
			||||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 | 
					github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 | 
				
			||||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
 | 
					github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
 | 
				
			||||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
 | 
					github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
 | 
				
			||||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
 | 
					github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
 | 
				
			||||||
 | 
					github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
 | 
				
			||||||
 | 
					github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 | 
				
			||||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
 | 
					github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
 | 
				
			||||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 | 
					github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 | 
				
			||||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 | 
					github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 | 
				
			||||||
@@ -181,10 +195,10 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a
 | 
				
			|||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 | 
					github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 | 
				
			||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 | 
					github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 | 
				
			||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 | 
					github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 | 
				
			||||||
 | 
					github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 | 
				
			||||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
 | 
					github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
 | 
				
			||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 | 
					github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 | 
				
			||||||
github.com/google/goterm v0.0.0-20190703233501-fc88cf888a3f h1:5CjVwnuUcp5adK4gmY6i72gpVFVnZDP2h5TmPScB6u4=
 | 
					github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 | 
				
			||||||
github.com/google/goterm v0.0.0-20190703233501-fc88cf888a3f/go.mod h1:nOFQdrUlIlx6M6ODdSpBj1NVA+VgLC6kmw60mkw34H4=
 | 
					 | 
				
			||||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
 | 
					github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
 | 
				
			||||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
 | 
					github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
 | 
				
			||||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
 | 
					github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
 | 
				
			||||||
@@ -197,8 +211,8 @@ github.com/google/rpmpack v0.6.1-0.20240329070804-c2247cbb881a/go.mod h1:uqVAUVQ
 | 
				
			|||||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
 | 
					github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
 | 
				
			||||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
 | 
					github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
 | 
				
			||||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 | 
					github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 | 
				
			||||||
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
 | 
					github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 | 
				
			||||||
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 | 
					github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 | 
				
			||||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
 | 
					github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
 | 
				
			||||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
 | 
					github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
 | 
				
			||||||
github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g=
 | 
					github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g=
 | 
				
			||||||
@@ -211,14 +225,22 @@ github.com/goreleaser/nfpm/v2 v2.41.0 h1:JyMzS/EwqaWbFs+7Z9oZ4Hkk4or00gUTqwm9Dgr
 | 
				
			|||||||
github.com/goreleaser/nfpm/v2 v2.41.0/go.mod h1:VPc5kF5OgfA+BosV/A2aB+Vg34honjWvp0Vt8ogsSi0=
 | 
					github.com/goreleaser/nfpm/v2 v2.41.0/go.mod h1:VPc5kF5OgfA+BosV/A2aB+Vg34honjWvp0Vt8ogsSi0=
 | 
				
			||||||
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
 | 
					github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
 | 
				
			||||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
 | 
					github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
 | 
				
			||||||
 | 
					github.com/hashicorp/go-hclog v0.14.1 h1:nQcJDQwIAGnmoUWp8ubocEX40cCml/17YkF6csQLReU=
 | 
				
			||||||
 | 
					github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
 | 
				
			||||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
 | 
					github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
 | 
				
			||||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
 | 
					github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
 | 
				
			||||||
 | 
					github.com/hashicorp/go-plugin v1.6.3 h1:xgHB+ZUSYeuJi96WtxEjzi23uh7YQpznjGh0U0UUrwg=
 | 
				
			||||||
 | 
					github.com/hashicorp/go-plugin v1.6.3/go.mod h1:MRobyh+Wc/nYy1V4KAXUiYfzxoYhs7V1mlH1Z7iY2h0=
 | 
				
			||||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 | 
					github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 | 
				
			||||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 | 
					github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 | 
				
			||||||
 | 
					github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
 | 
				
			||||||
 | 
					github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
 | 
				
			||||||
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
 | 
					github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
 | 
				
			||||||
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
 | 
					github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
 | 
				
			||||||
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
 | 
					github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
 | 
				
			||||||
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
 | 
					github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
 | 
				
			||||||
 | 
					github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
 | 
				
			||||||
 | 
					github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
 | 
				
			||||||
github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4=
 | 
					github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4=
 | 
				
			||||||
github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
 | 
					github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
 | 
				
			||||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
 | 
					github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
 | 
				
			||||||
@@ -229,10 +251,10 @@ github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOl
 | 
				
			|||||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
 | 
					github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
 | 
				
			||||||
github.com/jeandeaual/go-locale v0.0.0-20241217141322-fcc2cadd6f08 h1:wMeVzrPO3mfHIWLZtDcSaGAe2I4PW9B/P5nMkRSwCAc=
 | 
					github.com/jeandeaual/go-locale v0.0.0-20241217141322-fcc2cadd6f08 h1:wMeVzrPO3mfHIWLZtDcSaGAe2I4PW9B/P5nMkRSwCAc=
 | 
				
			||||||
github.com/jeandeaual/go-locale v0.0.0-20241217141322-fcc2cadd6f08/go.mod h1:ZDXo8KHryOWSIqnsb/CiDq7hQUYryCgdVnxbj8tDG7o=
 | 
					github.com/jeandeaual/go-locale v0.0.0-20241217141322-fcc2cadd6f08/go.mod h1:ZDXo8KHryOWSIqnsb/CiDq7hQUYryCgdVnxbj8tDG7o=
 | 
				
			||||||
github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
 | 
					github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c=
 | 
				
			||||||
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
 | 
					github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo=
 | 
				
			||||||
github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
 | 
					github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
 | 
				
			||||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
 | 
					github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
 | 
				
			||||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
 | 
					github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
 | 
				
			||||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
 | 
					github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
 | 
				
			||||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
 | 
					github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
 | 
				
			||||||
@@ -248,6 +270,18 @@ github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90
 | 
				
			|||||||
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
 | 
					github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
 | 
				
			||||||
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
 | 
					github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
 | 
				
			||||||
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
 | 
					github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
 | 
				
			||||||
 | 
					github.com/knadh/koanf/maps v0.1.2 h1:RBfmAW5CnZT+PJ1CVc1QSJKf4Xu9kxfQgYVQSu8hpbo=
 | 
				
			||||||
 | 
					github.com/knadh/koanf/maps v0.1.2/go.mod h1:npD/QZY3V6ghQDdcQzl1W4ICNVTkohC8E73eI2xW4yI=
 | 
				
			||||||
 | 
					github.com/knadh/koanf/parsers/toml/v2 v2.2.0 h1:2nV7tHYJ5OZy2BynQ4mOJ6k5bDqbbCzRERLUKBytz3A=
 | 
				
			||||||
 | 
					github.com/knadh/koanf/parsers/toml/v2 v2.2.0/go.mod h1:JpjTeK1Ge1hVX0wbof5DMCuDBriR8bWgeQP98eeOZpI=
 | 
				
			||||||
 | 
					github.com/knadh/koanf/providers/confmap v1.0.0 h1:mHKLJTE7iXEys6deO5p6olAiZdG5zwp8Aebir+/EaRE=
 | 
				
			||||||
 | 
					github.com/knadh/koanf/providers/confmap v1.0.0/go.mod h1:txHYHiI2hAtF0/0sCmcuol4IDcuQbKTybiB1nOcUo1A=
 | 
				
			||||||
 | 
					github.com/knadh/koanf/providers/env v1.1.0 h1:U2VXPY0f+CsNDkvdsG8GcsnK4ah85WwWyJgef9oQMSc=
 | 
				
			||||||
 | 
					github.com/knadh/koanf/providers/env v1.1.0/go.mod h1:QhHHHZ87h9JxJAn2czdEl6pdkNnDh/JS1Vtsyt65hTY=
 | 
				
			||||||
 | 
					github.com/knadh/koanf/providers/file v1.2.0 h1:hrUJ6Y9YOA49aNu/RSYzOTFlqzXSCpmYIDXI7OJU6+U=
 | 
				
			||||||
 | 
					github.com/knadh/koanf/providers/file v1.2.0/go.mod h1:bp1PM5f83Q+TOUu10J/0ApLBd9uIzg+n9UgthfY+nRA=
 | 
				
			||||||
 | 
					github.com/knadh/koanf/v2 v2.2.1 h1:jaleChtw85y3UdBnI0wCqcg1sj1gPoz6D3caGNHtrNE=
 | 
				
			||||||
 | 
					github.com/knadh/koanf/v2 v2.2.1/go.mod h1:PSFru3ufQgTsI7IF+95rf9s8XA1+aHxKuO/W+dPoHEY=
 | 
				
			||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 | 
					github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 | 
				
			||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
 | 
					github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
 | 
				
			||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
 | 
					github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
 | 
				
			||||||
@@ -257,16 +291,18 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 | 
				
			|||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 | 
					github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 | 
				
			||||||
github.com/leonelquinteros/gotext v1.7.0 h1:jcJmF4AXqyamP7vuw2MMIKs+O3jAEmvrc5JQiI8Ht/8=
 | 
					github.com/leonelquinteros/gotext v1.7.0 h1:jcJmF4AXqyamP7vuw2MMIKs+O3jAEmvrc5JQiI8Ht/8=
 | 
				
			||||||
github.com/leonelquinteros/gotext v1.7.0/go.mod h1:qJdoQuERPpccw7L70uoU+K/BvTfRBHYsisCQyFLXyvw=
 | 
					github.com/leonelquinteros/gotext v1.7.0/go.mod h1:qJdoQuERPpccw7L70uoU+K/BvTfRBHYsisCQyFLXyvw=
 | 
				
			||||||
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
 | 
					 | 
				
			||||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
 | 
					 | 
				
			||||||
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
 | 
					github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
 | 
				
			||||||
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
 | 
					github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
 | 
				
			||||||
 | 
					github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo=
 | 
				
			||||||
 | 
					github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg=
 | 
				
			||||||
github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
 | 
					github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
 | 
				
			||||||
github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
 | 
					github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
 | 
				
			||||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
 | 
					github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
 | 
				
			||||||
 | 
					github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
 | 
				
			||||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
 | 
					github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
 | 
				
			||||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
 | 
					github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
 | 
				
			||||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 | 
					github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 | 
				
			||||||
 | 
					github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
 | 
				
			||||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
 | 
					github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
 | 
				
			||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
 | 
					github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
 | 
				
			||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
 | 
					github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
 | 
				
			||||||
@@ -275,11 +311,8 @@ github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+Ei
 | 
				
			|||||||
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
 | 
					github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
 | 
				
			||||||
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
 | 
					github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
 | 
				
			||||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
 | 
					github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
 | 
				
			||||||
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
 | 
					 | 
				
			||||||
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
 | 
					github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
 | 
				
			||||||
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
 | 
					github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
 | 
				
			||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
 | 
					 | 
				
			||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
 | 
					 | 
				
			||||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
 | 
					github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
 | 
				
			||||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
 | 
					github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
 | 
				
			||||||
github.com/mholt/archiver/v4 v4.0.0-alpha.8 h1:tRGQuDVPh66WCOelqe6LIGh0gwmfwxUrSSDunscGsRM=
 | 
					github.com/mholt/archiver/v4 v4.0.0-alpha.8 h1:tRGQuDVPh66WCOelqe6LIGh0gwmfwxUrSSDunscGsRM=
 | 
				
			||||||
@@ -292,6 +325,11 @@ github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR
 | 
				
			|||||||
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
 | 
					github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
 | 
				
			||||||
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
 | 
					github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
 | 
				
			||||||
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
 | 
					github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
 | 
				
			||||||
 | 
					github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 | 
				
			||||||
 | 
					github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
 | 
				
			||||||
 | 
					github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 | 
				
			||||||
 | 
					github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
 | 
				
			||||||
 | 
					github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
 | 
				
			||||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
 | 
					github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
 | 
				
			||||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
 | 
					github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
 | 
				
			||||||
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
 | 
					github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
 | 
				
			||||||
@@ -300,31 +338,28 @@ github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
 | 
				
			|||||||
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
 | 
					github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
 | 
				
			||||||
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
 | 
					github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
 | 
				
			||||||
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
 | 
					github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
 | 
				
			||||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU=
 | 
					 | 
				
			||||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 | 
					 | 
				
			||||||
github.com/nwaples/rardecode/v2 v2.0.0-beta.2 h1:e3mzJFJs4k83GXBEiTaQ5HgSc/kOK8q0rDaRO0MPaOk=
 | 
					github.com/nwaples/rardecode/v2 v2.0.0-beta.2 h1:e3mzJFJs4k83GXBEiTaQ5HgSc/kOK8q0rDaRO0MPaOk=
 | 
				
			||||||
github.com/nwaples/rardecode/v2 v2.0.0-beta.2/go.mod h1:yntwv/HfMc/Hbvtq9I19D1n58te3h6KsqCf3GxyfBGY=
 | 
					github.com/nwaples/rardecode/v2 v2.0.0-beta.2/go.mod h1:yntwv/HfMc/Hbvtq9I19D1n58te3h6KsqCf3GxyfBGY=
 | 
				
			||||||
github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
 | 
					github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
 | 
				
			||||||
github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M=
 | 
					github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
 | 
				
			||||||
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
 | 
					github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 | 
				
			||||||
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
 | 
					github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
 | 
				
			||||||
 | 
					github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 | 
				
			||||||
 | 
					github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 | 
				
			||||||
 | 
					github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
 | 
				
			||||||
 | 
					github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
 | 
				
			||||||
 | 
					github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
 | 
				
			||||||
 | 
					github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
 | 
				
			||||||
github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0=
 | 
					github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0=
 | 
				
			||||||
github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
 | 
					github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
 | 
				
			||||||
github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
 | 
					github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
 | 
				
			||||||
github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
 | 
					github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
 | 
				
			||||||
 | 
					github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
 | 
				
			||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 | 
					github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 | 
				
			||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 | 
					github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 | 
				
			||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 | 
					github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 | 
				
			||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 | 
					github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 | 
				
			||||||
github.com/prometheus/client_golang v1.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk=
 | 
					 | 
				
			||||||
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
 | 
					 | 
				
			||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 | 
					github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 | 
				
			||||||
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
 | 
					 | 
				
			||||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 | 
					 | 
				
			||||||
github.com/prometheus/common v0.36.0 h1:78hJTing+BLYLjhXE+Z2BubeEymH5Lr0/Mt8FKkxxYo=
 | 
					 | 
				
			||||||
github.com/prometheus/common v0.36.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
 | 
					 | 
				
			||||||
github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU=
 | 
					 | 
				
			||||||
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
 | 
					 | 
				
			||||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
 | 
					github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
 | 
				
			||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
 | 
					github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
 | 
				
			||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
 | 
					github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
 | 
				
			||||||
@@ -333,6 +368,7 @@ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ
 | 
				
			|||||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
 | 
					github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
 | 
				
			||||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
 | 
					github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
 | 
				
			||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 | 
					github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 | 
				
			||||||
 | 
					github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
 | 
				
			||||||
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
 | 
					github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
 | 
				
			||||||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
 | 
					github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
 | 
				
			||||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
 | 
					github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
 | 
				
			||||||
@@ -342,34 +378,42 @@ github.com/sassoftware/go-rpmutils v0.4.0 h1:ojND82NYBxgwrV+mX1CWsd5QJvvEZTKddtC
 | 
				
			|||||||
github.com/sassoftware/go-rpmutils v0.4.0/go.mod h1:3goNWi7PGAT3/dlql2lv3+MSN5jNYPjT5mVcQcIsYzI=
 | 
					github.com/sassoftware/go-rpmutils v0.4.0/go.mod h1:3goNWi7PGAT3/dlql2lv3+MSN5jNYPjT5mVcQcIsYzI=
 | 
				
			||||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
 | 
					github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
 | 
				
			||||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
 | 
					github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
 | 
				
			||||||
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
 | 
					 | 
				
			||||||
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
 | 
					github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
 | 
				
			||||||
 | 
					github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
 | 
				
			||||||
 | 
					github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
 | 
				
			||||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
 | 
					github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
 | 
				
			||||||
github.com/skeema/knownhosts v1.2.2 h1:Iug2P4fLmDw9f41PB6thxUkNUkJzB5i+1/exaj40L3A=
 | 
					github.com/skeema/knownhosts v1.3.0 h1:AM+y0rI04VksttfwjkSTNQorvGqmwATnvnAHpSgc0LY=
 | 
				
			||||||
github.com/skeema/knownhosts v1.2.2/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo=
 | 
					github.com/skeema/knownhosts v1.3.0/go.mod h1:sPINvnADmT/qYH1kfv+ePMmOBTH6Tbl7b5LvTDjFK7M=
 | 
				
			||||||
github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY=
 | 
					github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY=
 | 
				
			||||||
github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec=
 | 
					github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec=
 | 
				
			||||||
github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY=
 | 
					github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY=
 | 
				
			||||||
github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60=
 | 
					github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60=
 | 
				
			||||||
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
 | 
					github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
 | 
				
			||||||
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
 | 
					github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
 | 
				
			||||||
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
 | 
					github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
 | 
				
			||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 | 
					github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 | 
				
			||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
 | 
					 | 
				
			||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
 | 
					 | 
				
			||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 | 
					github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 | 
				
			||||||
 | 
					github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 | 
				
			||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 | 
					github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 | 
				
			||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
 | 
					github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
 | 
				
			||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 | 
					github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 | 
				
			||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 | 
					github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 | 
				
			||||||
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 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
 | 
				
			||||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
 | 
					github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
 | 
				
			||||||
github.com/tailscale/goexpect v0.0.0-20210902213824-6e8c725cea41 h1:/V2rCMMWcsjYaYO2MeovLw+ClP63OtXgCF2Y1eb8+Ns=
 | 
					github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
 | 
				
			||||||
github.com/tailscale/goexpect v0.0.0-20210902213824-6e8c725cea41/go.mod h1:/roCdA6gg6lQyw/Oz6gIIGu3ggJKYhF+WC/AQReE5XQ=
 | 
					github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
 | 
				
			||||||
github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw=
 | 
					github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw=
 | 
				
			||||||
github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY=
 | 
					github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY=
 | 
				
			||||||
 | 
					github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
 | 
				
			||||||
 | 
					github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
 | 
				
			||||||
 | 
					github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
 | 
				
			||||||
 | 
					github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
 | 
				
			||||||
 | 
					github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
 | 
				
			||||||
 | 
					github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
 | 
				
			||||||
 | 
					github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
 | 
				
			||||||
 | 
					github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
 | 
				
			||||||
 | 
					github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
 | 
				
			||||||
 | 
					github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
 | 
				
			||||||
github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
 | 
					github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
 | 
				
			||||||
github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
 | 
					github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
 | 
				
			||||||
github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
 | 
					github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
 | 
				
			||||||
@@ -388,6 +432,12 @@ github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsr
 | 
				
			|||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
 | 
					github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
 | 
				
			||||||
gitlab.com/digitalxero/go-conventional-commit v1.0.7 h1:8/dO6WWG+98PMhlZowt/YjuiKhqhGlOCwlIV8SqqGh8=
 | 
					gitlab.com/digitalxero/go-conventional-commit v1.0.7 h1:8/dO6WWG+98PMhlZowt/YjuiKhqhGlOCwlIV8SqqGh8=
 | 
				
			||||||
gitlab.com/digitalxero/go-conventional-commit v1.0.7/go.mod h1:05Xc2BFsSyC5tKhK0y+P3bs0AwUtNuTp+mTpbCU/DZ0=
 | 
					gitlab.com/digitalxero/go-conventional-commit v1.0.7/go.mod h1:05Xc2BFsSyC5tKhK0y+P3bs0AwUtNuTp+mTpbCU/DZ0=
 | 
				
			||||||
 | 
					go.alt-gnome.ru/capytest v0.0.2 h1:clmvIqmYS86hhA1rsvivSSPpfOFkJTpbn38EQP7I3E8=
 | 
				
			||||||
 | 
					go.alt-gnome.ru/capytest v0.0.2/go.mod h1:lvxPx3H6h+LPnStBFblgoT2wkjv0wbug3S14troykEg=
 | 
				
			||||||
 | 
					go.alt-gnome.ru/capytest v0.0.3-0.20250706082755-f20413e052f9 h1:NST+V5LV/eLgs0p6PsuvfHiZ4UrIWqftCdifO8zgg0g=
 | 
				
			||||||
 | 
					go.alt-gnome.ru/capytest v0.0.3-0.20250706082755-f20413e052f9/go.mod h1:qiM8LARP+JBZr5mrDoVylOoqjrN0MAzvZ21NR9qMc0Y=
 | 
				
			||||||
 | 
					go.alt-gnome.ru/capytest/providers/podman v0.0.3-0.20250706082755-f20413e052f9 h1:VZclgdJxARvhZ6PIWWW2hQ6Ge4XeE36pzUr/U/y62bE=
 | 
				
			||||||
 | 
					go.alt-gnome.ru/capytest/providers/podman v0.0.3-0.20250706082755-f20413e052f9/go.mod h1:Wpq1Ny3eMzADJpMJArA2TZGZbsviUBmawtEPcxnoerg=
 | 
				
			||||||
go.elara.ws/vercmp v0.0.0-20230622214216-0b2b067575c4 h1:Ep54XceQlKhcCHl9awG+wWP4kz4kIP3c3Lzw/Gc/zwY=
 | 
					go.elara.ws/vercmp v0.0.0-20230622214216-0b2b067575c4 h1:Ep54XceQlKhcCHl9awG+wWP4kz4kIP3c3Lzw/Gc/zwY=
 | 
				
			||||||
go.elara.ws/vercmp v0.0.0-20230622214216-0b2b067575c4/go.mod h1:/7PNW7nFnDR5W7UXZVc04gdVLR/wBNgkm33KgIz0OBk=
 | 
					go.elara.ws/vercmp v0.0.0-20230622214216-0b2b067575c4/go.mod h1:/7PNW7nFnDR5W7UXZVc04gdVLR/wBNgkm33KgIz0OBk=
 | 
				
			||||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
 | 
					go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
 | 
				
			||||||
@@ -403,10 +453,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
 | 
				
			|||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 | 
					golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 | 
					golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 | 
				
			||||||
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
 | 
					golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
 | 
				
			||||||
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
 | 
					golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
 | 
				
			||||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
 | 
					golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
 | 
				
			||||||
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
 | 
					 | 
				
			||||||
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
 | 
					 | 
				
			||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 | 
					golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 | 
				
			||||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 | 
					golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 | 
				
			||||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
 | 
					golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
 | 
				
			||||||
@@ -415,8 +463,8 @@ golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE
 | 
				
			|||||||
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
 | 
					golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
 | 
				
			||||||
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
 | 
					golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
 | 
				
			||||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
 | 
					golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
 | 
				
			||||||
golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb h1:c0vyKkb6yr3KR7jEfJaOSv4lG7xPkbN6r52aJz1d8a8=
 | 
					golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
 | 
				
			||||||
golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
 | 
					golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
 | 
				
			||||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
 | 
					golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
 | 
				
			||||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
 | 
					golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
 | 
				
			||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 | 
					golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 | 
				
			||||||
@@ -435,11 +483,11 @@ golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
 | 
				
			|||||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
 | 
					golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
 | 
				
			||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 | 
					golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 | 
				
			||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 | 
					golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 | 
				
			||||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
 | 
					golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8=
 | 
				
			||||||
golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
 | 
					golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
 | 
				
			||||||
golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
 | 
					 | 
				
			||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
					golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
				
			||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
					golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
				
			||||||
 | 
					golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
				
			||||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
					golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
				
			||||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
					golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
				
			||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
					golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
				
			||||||
@@ -456,17 +504,13 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
 | 
				
			|||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 | 
					golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 | 
				
			||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 | 
					golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 | 
				
			||||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
 | 
					golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
 | 
				
			||||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
 | 
					golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
 | 
				
			||||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
 | 
					golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
 | 
				
			||||||
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
 | 
					 | 
				
			||||||
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
 | 
					 | 
				
			||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 | 
					golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 | 
				
			||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 | 
					golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 | 
				
			||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 | 
					golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 | 
				
			||||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 | 
					golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 | 
				
			||||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 | 
					golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 | 
				
			||||||
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b h1:clP8eMhB30EHdc0bd2Twtq6kgU7yl5ub2cQLSdrv1Dg=
 | 
					 | 
				
			||||||
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
 | 
					 | 
				
			||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
				
			||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
				
			||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
				
			||||||
@@ -474,10 +518,10 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ
 | 
				
			|||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
				
			||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
				
			||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
				
			||||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
 | 
				
			||||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
 | 
					golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
 | 
				
			||||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
 | 
					 | 
				
			||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
					golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
				
			||||||
 | 
					golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
					golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
					golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
@@ -487,6 +531,7 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w
 | 
				
			|||||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
 | 
					golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
@@ -502,18 +547,14 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
 | 
				
			|||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
					golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
					golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
				
			||||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
					golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
				
			||||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
					 | 
				
			||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
					 | 
				
			||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
					golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
				
			||||||
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
 | 
					golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
 | 
				
			||||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 | 
					golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
 | 
				
			||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 | 
					golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 | 
				
			||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 | 
					golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 | 
				
			||||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
 | 
					golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
 | 
				
			||||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
 | 
					golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
 | 
				
			||||||
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
 | 
					golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
 | 
				
			||||||
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
 | 
					 | 
				
			||||||
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
 | 
					 | 
				
			||||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
					golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
				
			||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
					golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
				
			||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
					golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
				
			||||||
@@ -522,10 +563,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 | 
				
			|||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 | 
					golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 | 
				
			||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 | 
					golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 | 
				
			||||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 | 
					golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 | 
				
			||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 | 
					golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
 | 
				
			||||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
 | 
					golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
 | 
				
			||||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
 | 
					 | 
				
			||||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
 | 
					 | 
				
			||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 | 
					golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 | 
				
			||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 | 
					golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 | 
				
			||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
					golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
				
			||||||
@@ -553,9 +592,8 @@ golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapK
 | 
				
			|||||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
 | 
					golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
 | 
				
			||||||
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
 | 
					golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
 | 
				
			||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
 | 
					golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
 | 
				
			||||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
 | 
					golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg=
 | 
				
			||||||
golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
 | 
					golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI=
 | 
				
			||||||
golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
 | 
					 | 
				
			||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
					golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
				
			||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
					golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
				
			||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
					golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
				
			||||||
@@ -572,8 +610,6 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
 | 
				
			|||||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 | 
					google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 | 
				
			||||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
 | 
					google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
 | 
				
			||||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
 | 
					google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
 | 
				
			||||||
google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
 | 
					 | 
				
			||||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
 | 
					 | 
				
			||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
 | 
					google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
 | 
				
			||||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 | 
					google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 | 
				
			||||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 | 
					google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 | 
				
			||||||
@@ -587,6 +623,8 @@ google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvx
 | 
				
			|||||||
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
 | 
					google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
 | 
				
			||||||
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
 | 
					google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
 | 
				
			||||||
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
 | 
					google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
 | 
				
			||||||
 | 
					google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8 h1:TqExAhdPaB60Ux47Cn0oLV07rGnxZzIsaRhQaqS666A=
 | 
				
			||||||
 | 
					google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8/go.mod h1:lcTa1sDdWEIHMWlITnIczmw5w60CF9ffkb8Z+DVmmjA=
 | 
				
			||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 | 
					google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 | 
				
			||||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
 | 
					google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
 | 
				
			||||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
 | 
					google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
 | 
				
			||||||
@@ -594,19 +632,27 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac
 | 
				
			|||||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
 | 
					google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
 | 
				
			||||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
 | 
					google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
 | 
				
			||||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
 | 
					google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
 | 
				
			||||||
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
 | 
					google.golang.org/grpc v1.67.3 h1:OgPcDAFKHnH8X3O4WcO4XUc8GRDeKsKReqbQtiCj7N8=
 | 
				
			||||||
 | 
					google.golang.org/grpc v1.67.3/go.mod h1:YGaHCc6Oap+FzBJTZLBzkGSYt/cvGPFTPxkn7QfSU8s=
 | 
				
			||||||
 | 
					google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
 | 
				
			||||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
 | 
					google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
 | 
				
			||||||
 | 
					google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
 | 
				
			||||||
 | 
					google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
 | 
				
			||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
					gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
				
			||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
					gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
				
			||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
					gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
				
			||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 | 
					gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 | 
				
			||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
 | 
					gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
 | 
				
			||||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 | 
					gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 | 
				
			||||||
 | 
					gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
 | 
				
			||||||
 | 
					gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
 | 
				
			||||||
 | 
					gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
 | 
				
			||||||
 | 
					gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
 | 
				
			||||||
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
 | 
					gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
 | 
				
			||||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
 | 
					gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
 | 
				
			||||||
 | 
					gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
				
			||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
					gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
				
			||||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
					gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
				
			||||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
 | 
					 | 
				
			||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
 | 
					gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
 | 
				
			||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
					gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
				
			||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 | 
					gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 | 
				
			||||||
@@ -649,3 +695,7 @@ mvdan.cc/sh/v3 v3.10.0/go.mod h1:z/mSSVyLFGZzqb3ZIKojjyqIx/xbmz/UHdCSv9HmqXY=
 | 
				
			|||||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
 | 
					rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
 | 
				
			||||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
 | 
					rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
 | 
				
			||||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
 | 
					rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
 | 
				
			||||||
 | 
					xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978 h1:bvLlAPW1ZMTWA32LuZMBEGHAUOcATZjzHcotf3SWweM=
 | 
				
			||||||
 | 
					xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
 | 
				
			||||||
 | 
					xorm.io/xorm v1.3.9 h1:TUovzS0ko+IQ1XnNLfs5dqK1cJl1H5uHpWbWqAQ04nU=
 | 
				
			||||||
 | 
					xorm.io/xorm v1.3.9/go.mod h1:LsCCffeeYp63ssk0pKumP6l96WZcHix7ChpurcLNuMw=
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										13
									
								
								helper.go
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								helper.go
									
									
									
									
									
								
							@@ -1,8 +1,8 @@
 | 
				
			|||||||
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
 | 
					// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
 | 
				
			||||||
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
 | 
					// It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// ALR - Any Linux Repository
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
// Copyright (C) 2025 Евгений Храмов
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// This program is free software: you can redistribute it and/or modify
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
// it under the terms of the GNU General Public License as published by
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
@@ -30,6 +30,7 @@ import (
 | 
				
			|||||||
	"mvdan.cc/sh/v3/expand"
 | 
						"mvdan.cc/sh/v3/expand"
 | 
				
			||||||
	"mvdan.cc/sh/v3/interp"
 | 
						"mvdan.cc/sh/v3/interp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils"
 | 
				
			||||||
	"gitea.plemya-x.ru/Plemya-x/ALR/internal/cpu"
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/cpu"
 | 
				
			||||||
	"gitea.plemya-x.ru/Plemya-x/ALR/internal/shutils/helpers"
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/shutils/helpers"
 | 
				
			||||||
	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro"
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro"
 | 
				
			||||||
@@ -71,19 +72,17 @@ func HelperCmd() *cli.Command {
 | 
				
			|||||||
			helper, ok := helpers.Helpers[c.Args().First()]
 | 
								helper, ok := helpers.Helpers[c.Args().First()]
 | 
				
			||||||
			if !ok {
 | 
								if !ok {
 | 
				
			||||||
				slog.Error(gotext.Get("No such helper command"), "name", c.Args().First())
 | 
									slog.Error(gotext.Get("No such helper command"), "name", c.Args().First())
 | 
				
			||||||
				os.Exit(1)
 | 
									return cli.Exit(gotext.Get("No such helper command"), 1)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			wd, err := os.Getwd()
 | 
								wd, err := os.Getwd()
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				slog.Error(gotext.Get("Error getting working directory"), "err", err)
 | 
									return cliutils.FormatCliExit(gotext.Get("Error getting working directory"), err)
 | 
				
			||||||
				os.Exit(1)
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			info, err := distro.ParseOSRelease(ctx)
 | 
								info, err := distro.ParseOSRelease(ctx)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				slog.Error(gotext.Get("Error getting working directory"), "err", err)
 | 
									return cliutils.FormatCliExit(gotext.Get("Error parsing os-release file"), err)
 | 
				
			||||||
				os.Exit(1)
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			hc := interp.HandlerContext{
 | 
								hc := interp.HandlerContext{
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										132
									
								
								info.go
									
									
									
									
									
								
							
							
						
						
									
										132
									
								
								info.go
									
									
									
									
									
								
							@@ -1,8 +1,8 @@
 | 
				
			|||||||
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
 | 
					// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
 | 
				
			||||||
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
 | 
					// It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// ALR - Any Linux Repository
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
// Copyright (C) 2025 Евгений Храмов
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// This program is free software: you can redistribute it and/or modify
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
// it under the terms of the GNU General Public License as published by
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
@@ -21,20 +21,19 @@ package main
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"log/slog"
 | 
					 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/goccy/go-yaml"
 | 
				
			||||||
	"github.com/jeandeaual/go-locale"
 | 
						"github.com/jeandeaual/go-locale"
 | 
				
			||||||
	"github.com/leonelquinteros/gotext"
 | 
						"github.com/leonelquinteros/gotext"
 | 
				
			||||||
	"github.com/urfave/cli/v2"
 | 
						"github.com/urfave/cli/v2"
 | 
				
			||||||
	"gopkg.in/yaml.v3"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils"
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils"
 | 
				
			||||||
	"gitea.plemya-x.ru/Plemya-x/ALR/internal/config"
 | 
						appbuilder "gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils/app_builder"
 | 
				
			||||||
	database "gitea.plemya-x.ru/Plemya-x/ALR/internal/db"
 | 
					 | 
				
			||||||
	"gitea.plemya-x.ru/Plemya-x/ALR/internal/overrides"
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/overrides"
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/utils"
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/pkg/alrsh"
 | 
				
			||||||
	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro"
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro"
 | 
				
			||||||
	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/repos"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func InfoCmd() *cli.Command {
 | 
					func InfoCmd() *cli.Command {
 | 
				
			||||||
@@ -48,80 +47,65 @@ func InfoCmd() *cli.Command {
 | 
				
			|||||||
				Usage:   gotext.Get("Show all information, not just for the current distro"),
 | 
									Usage:   gotext.Get("Show all information, not just for the current distro"),
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		BashComplete: func(c *cli.Context) {
 | 
							BashComplete: cliutils.BashCompleteWithError(func(c *cli.Context) error {
 | 
				
			||||||
 | 
								if err := utils.ExitIfCantDropCapsToAlrUser(); err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			ctx := c.Context
 | 
								ctx := c.Context
 | 
				
			||||||
			cfg := config.New()
 | 
								deps, err := appbuilder.
 | 
				
			||||||
			err := cfg.Load()
 | 
									New(ctx).
 | 
				
			||||||
 | 
									WithConfig().
 | 
				
			||||||
 | 
									WithDB().
 | 
				
			||||||
 | 
									Build()
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				slog.Error(gotext.Get("Error loading config"), "err", err)
 | 
									return err
 | 
				
			||||||
				os.Exit(1)
 | 
								}
 | 
				
			||||||
			}
 | 
								defer deps.Defer()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			db := database.New(cfg)
 | 
								result, err := deps.DB.GetPkgs(c.Context, "true")
 | 
				
			||||||
			err = db.Init(ctx)
 | 
								if err != nil {
 | 
				
			||||||
			if err != nil {
 | 
									return cliutils.FormatCliExit(gotext.Get("Error getting packages"), err)
 | 
				
			||||||
				slog.Error(gotext.Get("Error initialization database"), "err", err)
 | 
					 | 
				
			||||||
				os.Exit(1)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			result, err := db.GetPkgs(c.Context, "true")
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				slog.Error(gotext.Get("Error getting packages"), "err", err)
 | 
					 | 
				
			||||||
				os.Exit(1)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			defer result.Close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			for result.Next() {
 | 
					 | 
				
			||||||
				var pkg database.Package
 | 
					 | 
				
			||||||
				err = result.StructScan(&pkg)
 | 
					 | 
				
			||||||
				if err != nil {
 | 
					 | 
				
			||||||
					slog.Error(gotext.Get("Error iterating over packages"), "err", err)
 | 
					 | 
				
			||||||
					os.Exit(1)
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								for _, pkg := range result {
 | 
				
			||||||
				fmt.Println(pkg.Name)
 | 
									fmt.Println(pkg.Name)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		},
 | 
								return nil
 | 
				
			||||||
 | 
							}),
 | 
				
			||||||
		Action: func(c *cli.Context) error {
 | 
							Action: func(c *cli.Context) error {
 | 
				
			||||||
			ctx := c.Context
 | 
								if err := utils.ExitIfCantDropCapsToAlrUserNoPrivs(); err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
			cfg := config.New()
 | 
					 | 
				
			||||||
			err := cfg.Load()
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				slog.Error(gotext.Get("Error loading config"), "err", err)
 | 
					 | 
				
			||||||
				os.Exit(1)
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			db := database.New(cfg)
 | 
					 | 
				
			||||||
			err = db.Init(ctx)
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				slog.Error(gotext.Get("Error initialization database"), "err", err)
 | 
					 | 
				
			||||||
				os.Exit(1)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			rs := repos.New(cfg, db)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			args := c.Args()
 | 
								args := c.Args()
 | 
				
			||||||
			if args.Len() < 1 {
 | 
								if args.Len() < 1 {
 | 
				
			||||||
				slog.Error(gotext.Get("Command info expected at least 1 argument, got %d", args.Len()))
 | 
									return cli.Exit(gotext.Get("Command info expected at least 1 argument, got %d", args.Len()), 1)
 | 
				
			||||||
				os.Exit(1)
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if cfg.AutoPull() {
 | 
								ctx := c.Context
 | 
				
			||||||
				err := rs.Pull(ctx, cfg.Repos())
 | 
					
 | 
				
			||||||
 | 
								deps, err := appbuilder.
 | 
				
			||||||
 | 
									New(ctx).
 | 
				
			||||||
 | 
									WithConfig().
 | 
				
			||||||
 | 
									WithDB().
 | 
				
			||||||
 | 
									WithDistroInfo().
 | 
				
			||||||
 | 
									WithRepos().
 | 
				
			||||||
 | 
									Build()
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
					slog.Error(gotext.Get("Error pulling repos"), "err", err)
 | 
									return cli.Exit(err, 1)
 | 
				
			||||||
					os.Exit(1)
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
								defer deps.Defer()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								rs := deps.Repos
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			found, _, err := rs.FindPkgs(ctx, args.Slice())
 | 
								found, _, err := rs.FindPkgs(ctx, args.Slice())
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				slog.Error(gotext.Get("Error finding packages"), "err", err)
 | 
									return cliutils.FormatCliExit(gotext.Get("Error finding packages"), err)
 | 
				
			||||||
				os.Exit(1)
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if len(found) == 0 {
 | 
								if len(found) == 0 {
 | 
				
			||||||
				os.Exit(1)
 | 
									return cliutils.FormatCliExit(gotext.Get("Package not found"), err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			pkgs := cliutils.FlattenPkgs(ctx, found, "show", c.Bool("interactive"))
 | 
								pkgs := cliutils.FlattenPkgs(ctx, found, "show", c.Bool("interactive"))
 | 
				
			||||||
@@ -131,18 +115,15 @@ func InfoCmd() *cli.Command {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
			systemLang, err := locale.GetLanguage()
 | 
								systemLang, err := locale.GetLanguage()
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				slog.Error("Can't detect system language", "err", err)
 | 
									return cliutils.FormatCliExit(gotext.Get("Can't detect system language"), err)
 | 
				
			||||||
				os.Exit(1)
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if systemLang == "" {
 | 
								if systemLang == "" {
 | 
				
			||||||
				systemLang = "en"
 | 
									systemLang = "en"
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if !all {
 | 
					 | 
				
			||||||
			info, err := distro.ParseOSRelease(ctx)
 | 
								info, err := distro.ParseOSRelease(ctx)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
					slog.Error(gotext.Get("Error parsing os-release file"), "err", err)
 | 
									return cliutils.FormatCliExit(gotext.Get("Error parsing os-release file"), err)
 | 
				
			||||||
					os.Exit(1)
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			names, err = overrides.Resolve(
 | 
								names, err = overrides.Resolve(
 | 
				
			||||||
				info,
 | 
									info,
 | 
				
			||||||
@@ -150,26 +131,17 @@ func InfoCmd() *cli.Command {
 | 
				
			|||||||
					WithLanguages([]string{systemLang}),
 | 
										WithLanguages([]string{systemLang}),
 | 
				
			||||||
			)
 | 
								)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
					slog.Error(gotext.Get("Error resolving overrides"), "err", err)
 | 
									return cliutils.FormatCliExit(gotext.Get("Error resolving overrides"), err)
 | 
				
			||||||
					os.Exit(1)
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			for _, pkg := range pkgs {
 | 
								for _, pkg := range pkgs {
 | 
				
			||||||
				if !all {
 | 
									alrsh.ResolvePackage(&pkg, names)
 | 
				
			||||||
					err = yaml.NewEncoder(os.Stdout).Encode(overrides.ResolvePackage(&pkg, names))
 | 
									view := alrsh.NewPackageView(pkg)
 | 
				
			||||||
 | 
									view.Resolved = !all
 | 
				
			||||||
 | 
									err = yaml.NewEncoder(os.Stdout, yaml.UseJSONMarshaler(), yaml.OmitEmpty()).Encode(view)
 | 
				
			||||||
				if err != nil {
 | 
									if err != nil {
 | 
				
			||||||
						slog.Error(gotext.Get("Error encoding script variables"), "err", err)
 | 
										return cliutils.FormatCliExit(gotext.Get("Error encoding script variables"), err)
 | 
				
			||||||
						os.Exit(1)
 | 
					 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				} else {
 | 
					 | 
				
			||||||
					err = yaml.NewEncoder(os.Stdout).Encode(pkg)
 | 
					 | 
				
			||||||
					if err != nil {
 | 
					 | 
				
			||||||
						slog.Error(gotext.Get("Error encoding script variables"), "err", err)
 | 
					 | 
				
			||||||
						os.Exit(1)
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				fmt.Println("---")
 | 
									fmt.Println("---")
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										246
									
								
								install.go
									
									
									
									
									
								
							
							
						
						
									
										246
									
								
								install.go
									
									
									
									
									
								
							@@ -1,8 +1,8 @@
 | 
				
			|||||||
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
 | 
					// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
 | 
				
			||||||
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
 | 
					// It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// ALR - Any Linux Repository
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
// Copyright (C) 2025 Евгений Храмов
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// This program is free software: you can redistribute it and/or modify
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
// it under the terms of the GNU General Public License as published by
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
@@ -21,20 +21,16 @@ package main
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"log/slog"
 | 
					 | 
				
			||||||
	"os"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/leonelquinteros/gotext"
 | 
						"github.com/leonelquinteros/gotext"
 | 
				
			||||||
	"github.com/urfave/cli/v2"
 | 
						"github.com/urfave/cli/v2"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/build"
 | 
				
			||||||
	"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils"
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils"
 | 
				
			||||||
	"gitea.plemya-x.ru/Plemya-x/ALR/internal/config"
 | 
						appbuilder "gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils/app_builder"
 | 
				
			||||||
	database "gitea.plemya-x.ru/Plemya-x/ALR/internal/db"
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/manager"
 | 
				
			||||||
	"gitea.plemya-x.ru/Plemya-x/ALR/internal/types"
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/utils"
 | 
				
			||||||
	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/build"
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/pkg/types"
 | 
				
			||||||
	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro"
 | 
					 | 
				
			||||||
	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager"
 | 
					 | 
				
			||||||
	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/repos"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func InstallCmd() *cli.Command {
 | 
					func InstallCmd() *cli.Command {
 | 
				
			||||||
@@ -49,111 +45,103 @@ func InstallCmd() *cli.Command {
 | 
				
			|||||||
				Usage:   gotext.Get("Build package from scratch even if there's an already built package available"),
 | 
									Usage:   gotext.Get("Build package from scratch even if there's an already built package available"),
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		Action: func(c *cli.Context) error {
 | 
							Action: utils.RootNeededAction(func(c *cli.Context) error {
 | 
				
			||||||
			ctx := c.Context
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			args := c.Args()
 | 
								args := c.Args()
 | 
				
			||||||
			if args.Len() < 1 {
 | 
								if args.Len() < 1 {
 | 
				
			||||||
				slog.Error(gotext.Get("Command install expected at least 1 argument, got %d", args.Len()))
 | 
									return cliutils.FormatCliExit(gotext.Get("Command install expected at least 1 argument, got %d", args.Len()), nil)
 | 
				
			||||||
				os.Exit(1)
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			mgr := manager.Detect()
 | 
								if err := utils.ExitIfCantDropCapsToAlrUser(); err != nil {
 | 
				
			||||||
			if mgr == nil {
 | 
									return err
 | 
				
			||||||
				slog.Error(gotext.Get("Unable to detect a supported package manager on the system"))
 | 
					 | 
				
			||||||
				os.Exit(1)
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			cfg := config.New()
 | 
								installer, installerClose, err := build.GetSafeInstaller()
 | 
				
			||||||
			err := cfg.Load()
 | 
					 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				slog.Error(gotext.Get("Error loading config"), "err", err)
 | 
									return err
 | 
				
			||||||
				os.Exit(1)
 | 
								}
 | 
				
			||||||
 | 
								defer installerClose()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if err := utils.ExitIfCantSetNoNewPrivs(); err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			db := database.New(cfg)
 | 
								scripter, scripterClose, err := build.GetSafeScriptExecutor()
 | 
				
			||||||
			rs := repos.New(cfg, db)
 | 
					 | 
				
			||||||
			err = db.Init(ctx)
 | 
					 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				slog.Error(gotext.Get("Error initialization database"), "err", err)
 | 
									return err
 | 
				
			||||||
				os.Exit(1)
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
								defer scripterClose()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if cfg.AutoPull() {
 | 
								ctx := c.Context
 | 
				
			||||||
				err := rs.Pull(ctx, cfg.Repos())
 | 
					
 | 
				
			||||||
 | 
								deps, err := appbuilder.
 | 
				
			||||||
 | 
									New(ctx).
 | 
				
			||||||
 | 
									WithConfig().
 | 
				
			||||||
 | 
									WithDB().
 | 
				
			||||||
 | 
									WithRepos().
 | 
				
			||||||
 | 
									WithDistroInfo().
 | 
				
			||||||
 | 
									WithManager().
 | 
				
			||||||
 | 
									Build()
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
					slog.Error(gotext.Get("Error pulling repositories"), "err", err)
 | 
									return err
 | 
				
			||||||
					os.Exit(1)
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
								defer deps.Defer()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			found, notFound, err := rs.FindPkgs(ctx, args.Slice())
 | 
								builder, err := build.NewMainBuilder(
 | 
				
			||||||
			if err != nil {
 | 
									deps.Cfg,
 | 
				
			||||||
				slog.Error(gotext.Get("Error finding packages"), "err", err)
 | 
									deps.Manager,
 | 
				
			||||||
				os.Exit(1)
 | 
									deps.Repos,
 | 
				
			||||||
			}
 | 
									scripter,
 | 
				
			||||||
 | 
									installer,
 | 
				
			||||||
			pkgs := cliutils.FlattenPkgs(ctx, found, "install", c.Bool("interactive"))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			opts := types.BuildOpts{
 | 
					 | 
				
			||||||
				Manager:     mgr,
 | 
					 | 
				
			||||||
				Clean:       c.Bool("clean"),
 | 
					 | 
				
			||||||
				Interactive: c.Bool("interactive"),
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			info, err := distro.ParseOSRelease(ctx)
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				slog.Error(gotext.Get("Error parsing os release"), "err", err)
 | 
					 | 
				
			||||||
				os.Exit(1)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			builder := build.NewBuilder(
 | 
					 | 
				
			||||||
				ctx,
 | 
					 | 
				
			||||||
				opts,
 | 
					 | 
				
			||||||
				rs,
 | 
					 | 
				
			||||||
				info,
 | 
					 | 
				
			||||||
				cfg,
 | 
					 | 
				
			||||||
			)
 | 
								)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			builder.InstallPkgs(ctx, pkgs, notFound, types.BuildOpts{
 | 
								_, err = builder.InstallPkgs(
 | 
				
			||||||
				Manager:     mgr,
 | 
									ctx,
 | 
				
			||||||
 | 
									&build.BuildArgs{
 | 
				
			||||||
 | 
										Opts: &types.BuildOpts{
 | 
				
			||||||
						Clean:       c.Bool("clean"),
 | 
											Clean:       c.Bool("clean"),
 | 
				
			||||||
						Interactive: c.Bool("interactive"),
 | 
											Interactive: c.Bool("interactive"),
 | 
				
			||||||
			})
 | 
					 | 
				
			||||||
			return nil
 | 
					 | 
				
			||||||
					},
 | 
										},
 | 
				
			||||||
		BashComplete: func(c *cli.Context) {
 | 
										Info:       deps.Info,
 | 
				
			||||||
			cfg := config.New()
 | 
										PkgFormat_: build.GetPkgFormat(deps.Manager),
 | 
				
			||||||
			err := cfg.Load()
 | 
									},
 | 
				
			||||||
 | 
									args.Slice(),
 | 
				
			||||||
 | 
								)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				slog.Error(gotext.Get("Error loading config"), "err", err)
 | 
									return cliutils.FormatCliExit(gotext.Get("Error when installing the package"), err)
 | 
				
			||||||
				os.Exit(1)
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			db := database.New(cfg)
 | 
								return nil
 | 
				
			||||||
			err = db.Init(c.Context)
 | 
							}),
 | 
				
			||||||
			if err != nil {
 | 
							BashComplete: cliutils.BashCompleteWithError(func(c *cli.Context) error {
 | 
				
			||||||
				slog.Error(gotext.Get("Error initialization database"), "err", err)
 | 
								if err := utils.ExitIfCantDropCapsToAlrUser(); err != nil {
 | 
				
			||||||
				os.Exit(1)
 | 
									return err
 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			result, err := db.GetPkgs(c.Context, "true")
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				slog.Error(gotext.Get("Error getting packages"), "err", err)
 | 
					 | 
				
			||||||
				os.Exit(1)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			defer result.Close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			for result.Next() {
 | 
					 | 
				
			||||||
				var pkg database.Package
 | 
					 | 
				
			||||||
				err = result.StructScan(&pkg)
 | 
					 | 
				
			||||||
				if err != nil {
 | 
					 | 
				
			||||||
					slog.Error(gotext.Get("Error iterating over packages"), "err", err)
 | 
					 | 
				
			||||||
					os.Exit(1)
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								ctx := c.Context
 | 
				
			||||||
 | 
								deps, err := appbuilder.
 | 
				
			||||||
 | 
									New(ctx).
 | 
				
			||||||
 | 
									WithConfig().
 | 
				
			||||||
 | 
									WithDB().
 | 
				
			||||||
 | 
									Build()
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								defer deps.Defer()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								result, err := deps.DB.GetPkgs(c.Context, "true")
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return cliutils.FormatCliExit(gotext.Get("Error getting packages"), err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								for _, pkg := range result {
 | 
				
			||||||
				fmt.Println(pkg.Name)
 | 
									fmt.Println(pkg.Name)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		},
 | 
					
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							}),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -162,29 +150,71 @@ func RemoveCmd() *cli.Command {
 | 
				
			|||||||
		Name:    "remove",
 | 
							Name:    "remove",
 | 
				
			||||||
		Usage:   gotext.Get("Remove an installed package"),
 | 
							Usage:   gotext.Get("Remove an installed package"),
 | 
				
			||||||
		Aliases: []string{"rm"},
 | 
							Aliases: []string{"rm"},
 | 
				
			||||||
		Action: func(c *cli.Context) error {
 | 
							BashComplete: cliutils.BashCompleteWithError(func(c *cli.Context) error {
 | 
				
			||||||
			args := c.Args()
 | 
								ctx := c.Context
 | 
				
			||||||
			if args.Len() < 1 {
 | 
					 | 
				
			||||||
				slog.Error(gotext.Get("Command remove expected at least 1 argument, got %d", args.Len()))
 | 
					 | 
				
			||||||
				os.Exit(1)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
			mgr := manager.Detect()
 | 
								deps, err := appbuilder.
 | 
				
			||||||
			if mgr == nil {
 | 
									New(ctx).
 | 
				
			||||||
				slog.Error(gotext.Get("Unable to detect a supported package manager on the system"))
 | 
									WithConfig().
 | 
				
			||||||
				os.Exit(1)
 | 
									WithDB().
 | 
				
			||||||
			}
 | 
									WithManager().
 | 
				
			||||||
 | 
									Build()
 | 
				
			||||||
			err := mgr.Remove(&manager.Opts{
 | 
					 | 
				
			||||||
				AsRoot:    true,
 | 
					 | 
				
			||||||
				NoConfirm: !c.Bool("interactive"),
 | 
					 | 
				
			||||||
			}, c.Args().Slice()...)
 | 
					 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				slog.Error(gotext.Get("Error removing packages"), "err", err)
 | 
									return cli.Exit(err, 1)
 | 
				
			||||||
				os.Exit(1)
 | 
								}
 | 
				
			||||||
 | 
								defer deps.Defer()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								installedAlrPackages := map[string]string{}
 | 
				
			||||||
 | 
								installed, err := deps.Manager.ListInstalled(&manager.Opts{})
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return cliutils.FormatCliExit(gotext.Get("Error listing installed packages"), err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								for pkgName, version := range installed {
 | 
				
			||||||
 | 
									matches := build.RegexpALRPackageName.FindStringSubmatch(pkgName)
 | 
				
			||||||
 | 
									if matches != nil {
 | 
				
			||||||
 | 
										packageName := matches[build.RegexpALRPackageName.SubexpIndex("package")]
 | 
				
			||||||
 | 
										repoName := matches[build.RegexpALRPackageName.SubexpIndex("repo")]
 | 
				
			||||||
 | 
										installedAlrPackages[fmt.Sprintf("%s/%s", repoName, packageName)] = version
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								result, err := deps.DB.GetPkgs(c.Context, "true")
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return cliutils.FormatCliExit(gotext.Get("Error getting packages"), err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								for _, pkg := range result {
 | 
				
			||||||
 | 
									_, ok := installedAlrPackages[fmt.Sprintf("%s/%s", pkg.Repository, pkg.Name)]
 | 
				
			||||||
 | 
									if !ok {
 | 
				
			||||||
 | 
										continue
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									fmt.Println(pkg.Name)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			return nil
 | 
								return nil
 | 
				
			||||||
		},
 | 
							}),
 | 
				
			||||||
 | 
							Action: utils.RootNeededAction(func(c *cli.Context) error {
 | 
				
			||||||
 | 
								args := c.Args()
 | 
				
			||||||
 | 
								if args.Len() < 1 {
 | 
				
			||||||
 | 
									return cliutils.FormatCliExit(gotext.Get("Command remove expected at least 1 argument, got %d", args.Len()), nil)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								deps, err := appbuilder.
 | 
				
			||||||
 | 
									New(c.Context).
 | 
				
			||||||
 | 
									WithManager().
 | 
				
			||||||
 | 
									Build()
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								defer deps.Defer()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if err := deps.Manager.Remove(&manager.Opts{
 | 
				
			||||||
 | 
									NoConfirm: !c.Bool("interactive"),
 | 
				
			||||||
 | 
								}, c.Args().Slice()...); err != nil {
 | 
				
			||||||
 | 
									return cliutils.FormatCliExit(gotext.Get("Error removing packages"), err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							}),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										317
									
								
								internal.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										317
									
								
								internal.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,317 @@
 | 
				
			|||||||
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"bufio"
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"log/slog"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"os/exec"
 | 
				
			||||||
 | 
						"os/user"
 | 
				
			||||||
 | 
						"path/filepath"
 | 
				
			||||||
 | 
						"syscall"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/hashicorp/go-hclog"
 | 
				
			||||||
 | 
						"github.com/hashicorp/go-plugin"
 | 
				
			||||||
 | 
						"github.com/leonelquinteros/gotext"
 | 
				
			||||||
 | 
						"github.com/urfave/cli/v2"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/build"
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils"
 | 
				
			||||||
 | 
						appbuilder "gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils/app_builder"
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/config"
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/constants"
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/logger"
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/manager"
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/utils"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func InternalBuildCmd() *cli.Command {
 | 
				
			||||||
 | 
						return &cli.Command{
 | 
				
			||||||
 | 
							Name:     "_internal-safe-script-executor",
 | 
				
			||||||
 | 
							HideHelp: true,
 | 
				
			||||||
 | 
							Hidden:   true,
 | 
				
			||||||
 | 
							Action: func(c *cli.Context) error {
 | 
				
			||||||
 | 
								logger.SetupForGoPlugin()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								slog.Debug("start _internal-safe-script-executor", "uid", syscall.Getuid(), "gid", syscall.Getgid())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if err := utils.ExitIfCantDropCapsToAlrUser(); err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								cfg := config.New()
 | 
				
			||||||
 | 
								err := cfg.Load()
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return cliutils.FormatCliExit(gotext.Get("Error loading config"), err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								logger := hclog.New(&hclog.LoggerOptions{
 | 
				
			||||||
 | 
									Name:        "plugin",
 | 
				
			||||||
 | 
									Output:      os.Stderr,
 | 
				
			||||||
 | 
									Level:       hclog.Debug,
 | 
				
			||||||
 | 
									JSONFormat:  false,
 | 
				
			||||||
 | 
									DisableTime: true,
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								plugin.Serve(&plugin.ServeConfig{
 | 
				
			||||||
 | 
									HandshakeConfig: build.HandshakeConfig,
 | 
				
			||||||
 | 
									Plugins: map[string]plugin.Plugin{
 | 
				
			||||||
 | 
										"script-executor": &build.ScriptExecutorPlugin{
 | 
				
			||||||
 | 
											Impl: build.NewLocalScriptExecutor(cfg),
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									Logger: logger,
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func InternalReposCmd() *cli.Command {
 | 
				
			||||||
 | 
						return &cli.Command{
 | 
				
			||||||
 | 
							Name:     "_internal-repos",
 | 
				
			||||||
 | 
							HideHelp: true,
 | 
				
			||||||
 | 
							Hidden:   true,
 | 
				
			||||||
 | 
							Action: utils.RootNeededAction(func(ctx *cli.Context) error {
 | 
				
			||||||
 | 
								logger.SetupForGoPlugin()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if err := utils.ExitIfCantDropCapsToAlrUser(); err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								deps, err := appbuilder.
 | 
				
			||||||
 | 
									New(ctx.Context).
 | 
				
			||||||
 | 
									WithConfig().
 | 
				
			||||||
 | 
									WithDB().
 | 
				
			||||||
 | 
									WithReposNoPull().
 | 
				
			||||||
 | 
									Build()
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								defer deps.Defer()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								pluginCfg := build.GetPluginServeCommonConfig()
 | 
				
			||||||
 | 
								pluginCfg.Plugins = map[string]plugin.Plugin{
 | 
				
			||||||
 | 
									"repos": &build.ReposExecutorPlugin{
 | 
				
			||||||
 | 
										Impl: build.NewRepos(
 | 
				
			||||||
 | 
											deps.Repos,
 | 
				
			||||||
 | 
										),
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								plugin.Serve(pluginCfg)
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							}),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func InternalInstallCmd() *cli.Command {
 | 
				
			||||||
 | 
						return &cli.Command{
 | 
				
			||||||
 | 
							Name:     "_internal-installer",
 | 
				
			||||||
 | 
							HideHelp: true,
 | 
				
			||||||
 | 
							Hidden:   true,
 | 
				
			||||||
 | 
							Action: func(c *cli.Context) error {
 | 
				
			||||||
 | 
								logger.SetupForGoPlugin()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if err := utils.EnsureIsAlrUser(); err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Before escalating the rights, we made sure that
 | 
				
			||||||
 | 
								// this is an ALR user, so it looks safe.
 | 
				
			||||||
 | 
								err := utils.EscalateToRootUid()
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return cliutils.FormatCliExit("cannot escalate to root", err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								deps, err := appbuilder.
 | 
				
			||||||
 | 
									New(c.Context).
 | 
				
			||||||
 | 
									WithConfig().
 | 
				
			||||||
 | 
									WithDB().
 | 
				
			||||||
 | 
									WithReposNoPull().
 | 
				
			||||||
 | 
									Build()
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								defer deps.Defer()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								logger := hclog.New(&hclog.LoggerOptions{
 | 
				
			||||||
 | 
									Name:        "plugin",
 | 
				
			||||||
 | 
									Output:      os.Stderr,
 | 
				
			||||||
 | 
									Level:       hclog.Trace,
 | 
				
			||||||
 | 
									JSONFormat:  true,
 | 
				
			||||||
 | 
									DisableTime: true,
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								plugin.Serve(&plugin.ServeConfig{
 | 
				
			||||||
 | 
									HandshakeConfig: build.HandshakeConfig,
 | 
				
			||||||
 | 
									Plugins: map[string]plugin.Plugin{
 | 
				
			||||||
 | 
										"installer": &build.InstallerExecutorPlugin{
 | 
				
			||||||
 | 
											Impl: build.NewInstaller(
 | 
				
			||||||
 | 
												manager.Detect(),
 | 
				
			||||||
 | 
											),
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									Logger: logger,
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func Mount(target string) (string, func(), error) {
 | 
				
			||||||
 | 
						exe, err := os.Executable()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", nil, fmt.Errorf("failed to get executable path: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cmd := exec.Command(exe, "_internal-temporary-mount", target)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						stdoutPipe, err := cmd.StdoutPipe()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", nil, fmt.Errorf("failed to get stdout pipe: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						stdinPipe, err := cmd.StdinPipe()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", nil, fmt.Errorf("failed to get stdin pipe: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cmd.Stderr = os.Stderr
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := cmd.Start(); err != nil {
 | 
				
			||||||
 | 
							return "", nil, fmt.Errorf("failed to start mount: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						scanner := bufio.NewScanner(stdoutPipe)
 | 
				
			||||||
 | 
						var mountPath string
 | 
				
			||||||
 | 
						if scanner.Scan() {
 | 
				
			||||||
 | 
							mountPath = scanner.Text()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := scanner.Err(); err != nil {
 | 
				
			||||||
 | 
							_ = cmd.Process.Kill()
 | 
				
			||||||
 | 
							return "", nil, fmt.Errorf("failed to read mount output: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if mountPath == "" {
 | 
				
			||||||
 | 
							_ = cmd.Process.Kill()
 | 
				
			||||||
 | 
							return "", nil, errors.New("mount failed: no target path returned")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cleanup := func() {
 | 
				
			||||||
 | 
							slog.Debug("cleanup triggered")
 | 
				
			||||||
 | 
							_, _ = fmt.Fprintln(stdinPipe, "")
 | 
				
			||||||
 | 
							_ = cmd.Wait()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return mountPath, cleanup, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func InternalMountCmd() *cli.Command {
 | 
				
			||||||
 | 
						return &cli.Command{
 | 
				
			||||||
 | 
							Name:     "_internal-temporary-mount",
 | 
				
			||||||
 | 
							HideHelp: true,
 | 
				
			||||||
 | 
							Hidden:   true,
 | 
				
			||||||
 | 
							Action: func(c *cli.Context) error {
 | 
				
			||||||
 | 
								logger.SetupForGoPlugin()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								sourceDir := c.Args().First()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								u, err := user.Current()
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return cliutils.FormatCliExit("cannot get current user", err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								_, alrGid, err := utils.GetUidGidAlrUser()
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return cliutils.FormatCliExit("cannot get alr user", err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if _, err := os.Stat(sourceDir); err != nil {
 | 
				
			||||||
 | 
									return cliutils.FormatCliExit(fmt.Sprintf("cannot read %s", sourceDir), err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if err := utils.EnuseIsPrivilegedGroupMember(); err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Before escalating the rights, we made sure that
 | 
				
			||||||
 | 
								// 1. user in wheel group
 | 
				
			||||||
 | 
								// 2. user can access sourceDir
 | 
				
			||||||
 | 
								if err := utils.EscalateToRootUid(); err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if err := syscall.Setgid(alrGid); err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if err := os.MkdirAll(constants.AlrRunDir, 0o770); err != nil {
 | 
				
			||||||
 | 
									return cliutils.FormatCliExit(fmt.Sprintf("failed to create %s", constants.AlrRunDir), err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if err := os.Chown(constants.AlrRunDir, 0, alrGid); err != nil {
 | 
				
			||||||
 | 
									return cliutils.FormatCliExit(fmt.Sprintf("failed to chown %s", constants.AlrRunDir), err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								targetDir := filepath.Join(constants.AlrRunDir, fmt.Sprintf("bindfs-%d", os.Getpid()))
 | 
				
			||||||
 | 
								// 0750: owner (root) and group (alr)
 | 
				
			||||||
 | 
								if err := os.MkdirAll(targetDir, 0o750); err != nil {
 | 
				
			||||||
 | 
									return cliutils.FormatCliExit("error creating bindfs target directory", err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								//  chown AlrRunDir/mounts/bindfs-* to (root:alr),
 | 
				
			||||||
 | 
								//  so alr user can access dir
 | 
				
			||||||
 | 
								if err := os.Chown(targetDir, 0, alrGid); err != nil {
 | 
				
			||||||
 | 
									return cliutils.FormatCliExit("failed to chown bindfs directory", err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								bindfsCmd := exec.Command(
 | 
				
			||||||
 | 
									"bindfs",
 | 
				
			||||||
 | 
									fmt.Sprintf("--map=%s/alr:@%s/@alr", u.Uid, u.Gid),
 | 
				
			||||||
 | 
									sourceDir,
 | 
				
			||||||
 | 
									targetDir,
 | 
				
			||||||
 | 
								)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								bindfsCmd.Stderr = os.Stderr
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if err := bindfsCmd.Run(); err != nil {
 | 
				
			||||||
 | 
									return cliutils.FormatCliExit("failed to strart bindfs", err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								fmt.Println(targetDir)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								_, _ = bufio.NewReader(os.Stdin).ReadString('\n')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								slog.Debug("start unmount", "dir", targetDir)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								umountCmd := exec.Command("umount", targetDir)
 | 
				
			||||||
 | 
								umountCmd.Stderr = os.Stderr
 | 
				
			||||||
 | 
								if err := umountCmd.Run(); err != nil {
 | 
				
			||||||
 | 
									return cliutils.FormatCliExit(fmt.Sprintf("failed to unmount %s", targetDir), err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if err := os.Remove(targetDir); err != nil {
 | 
				
			||||||
 | 
									return cliutils.FormatCliExit(fmt.Sprintf("error removing directory %s", targetDir), err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										706
									
								
								internal/build/build.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										706
									
								
								internal/build/build.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,706 @@
 | 
				
			|||||||
 | 
					// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
 | 
				
			||||||
 | 
					// It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package build
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"encoding/gob"
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"log/slog"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/leonelquinteros/gotext"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils"
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/config"
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/manager"
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/pkg/alrsh"
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro"
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/pkg/types"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type BuildInput struct {
 | 
				
			||||||
 | 
						opts       *types.BuildOpts
 | 
				
			||||||
 | 
						info       *distro.OSRelease
 | 
				
			||||||
 | 
						pkgFormat  string
 | 
				
			||||||
 | 
						script     string
 | 
				
			||||||
 | 
						repository string
 | 
				
			||||||
 | 
						packages   []string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (bi *BuildInput) GobEncode() ([]byte, error) {
 | 
				
			||||||
 | 
						w := new(bytes.Buffer)
 | 
				
			||||||
 | 
						encoder := gob.NewEncoder(w)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := encoder.Encode(bi.opts); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err := encoder.Encode(bi.info); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err := encoder.Encode(bi.pkgFormat); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err := encoder.Encode(bi.script); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err := encoder.Encode(bi.repository); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err := encoder.Encode(bi.packages); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return w.Bytes(), nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (bi *BuildInput) GobDecode(data []byte) error {
 | 
				
			||||||
 | 
						r := bytes.NewBuffer(data)
 | 
				
			||||||
 | 
						decoder := gob.NewDecoder(r)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := decoder.Decode(&bi.opts); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err := decoder.Decode(&bi.info); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err := decoder.Decode(&bi.pkgFormat); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err := decoder.Decode(&bi.script); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err := decoder.Decode(&bi.repository); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err := decoder.Decode(&bi.packages); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *BuildInput) Repository() string {
 | 
				
			||||||
 | 
						return b.repository
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *BuildInput) BuildOpts() *types.BuildOpts {
 | 
				
			||||||
 | 
						return b.opts
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *BuildInput) OSRelease() *distro.OSRelease {
 | 
				
			||||||
 | 
						return b.info
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *BuildInput) PkgFormat() string {
 | 
				
			||||||
 | 
						return b.pkgFormat
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type BuildOptsProvider interface {
 | 
				
			||||||
 | 
						BuildOpts() *types.BuildOpts
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type OsInfoProvider interface {
 | 
				
			||||||
 | 
						OSRelease() *distro.OSRelease
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type PkgFormatProvider interface {
 | 
				
			||||||
 | 
						PkgFormat() string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type RepositoryProvider interface {
 | 
				
			||||||
 | 
						Repository() string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ================================================
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type BuiltDep struct {
 | 
				
			||||||
 | 
						Name string
 | 
				
			||||||
 | 
						Path string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func Map[T, R any](items []T, f func(T) R) []R {
 | 
				
			||||||
 | 
						res := make([]R, len(items))
 | 
				
			||||||
 | 
						for i, item := range items {
 | 
				
			||||||
 | 
							res[i] = f(item)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return res
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func GetBuiltPaths(deps []*BuiltDep) []string {
 | 
				
			||||||
 | 
						return Map(deps, func(dep *BuiltDep) string {
 | 
				
			||||||
 | 
							return dep.Path
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func GetBuiltName(deps []*BuiltDep) []string {
 | 
				
			||||||
 | 
						return Map(deps, func(dep *BuiltDep) string {
 | 
				
			||||||
 | 
							return dep.Name
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type PackageFinder interface {
 | 
				
			||||||
 | 
						FindPkgs(ctx context.Context, pkgs []string) (map[string][]alrsh.Package, []string, error)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Config interface {
 | 
				
			||||||
 | 
						GetPaths() *config.Paths
 | 
				
			||||||
 | 
						PagerStyle() string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type FunctionsOutput struct {
 | 
				
			||||||
 | 
						Contents *[]string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// EXECUTORS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ScriptResolverExecutor interface {
 | 
				
			||||||
 | 
						ResolveScript(ctx context.Context, pkg *alrsh.Package) *ScriptInfo
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type CacheExecutor interface {
 | 
				
			||||||
 | 
						CheckForBuiltPackage(ctx context.Context, input *BuildInput, vars *alrsh.Package) (string, bool, error)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ScriptViewerExecutor interface {
 | 
				
			||||||
 | 
						ViewScript(ctx context.Context, input *BuildInput, sf *alrsh.ScriptFile, basePkg string) error
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type CheckerExecutor interface {
 | 
				
			||||||
 | 
						PerformChecks(
 | 
				
			||||||
 | 
							ctx context.Context,
 | 
				
			||||||
 | 
							input *BuildInput,
 | 
				
			||||||
 | 
							vars *alrsh.Package,
 | 
				
			||||||
 | 
						) (bool, error)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type SourcesInput struct {
 | 
				
			||||||
 | 
						Sources   []string
 | 
				
			||||||
 | 
						Checksums []string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type SourceDownloaderExecutor interface {
 | 
				
			||||||
 | 
						DownloadSources(
 | 
				
			||||||
 | 
							ctx context.Context,
 | 
				
			||||||
 | 
							input *BuildInput,
 | 
				
			||||||
 | 
							basePkg string,
 | 
				
			||||||
 | 
							si SourcesInput,
 | 
				
			||||||
 | 
						) error
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewBuilder(
 | 
				
			||||||
 | 
						scriptResolver ScriptResolverExecutor,
 | 
				
			||||||
 | 
						scriptExecutor ScriptExecutor,
 | 
				
			||||||
 | 
						cacheExecutor CacheExecutor,
 | 
				
			||||||
 | 
						scriptViewerExecutor ScriptViewerExecutor,
 | 
				
			||||||
 | 
						checkerExecutor CheckerExecutor,
 | 
				
			||||||
 | 
						installerExecutor InstallerExecutor,
 | 
				
			||||||
 | 
						sourceExecutor SourceDownloaderExecutor,
 | 
				
			||||||
 | 
					) *Builder {
 | 
				
			||||||
 | 
						return &Builder{
 | 
				
			||||||
 | 
							scriptResolver:       scriptResolver,
 | 
				
			||||||
 | 
							scriptExecutor:       scriptExecutor,
 | 
				
			||||||
 | 
							cacheExecutor:        cacheExecutor,
 | 
				
			||||||
 | 
							scriptViewerExecutor: scriptViewerExecutor,
 | 
				
			||||||
 | 
							checkerExecutor:      checkerExecutor,
 | 
				
			||||||
 | 
							installerExecutor:    installerExecutor,
 | 
				
			||||||
 | 
							sourceExecutor:       sourceExecutor,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Builder struct {
 | 
				
			||||||
 | 
						scriptResolver       ScriptResolverExecutor
 | 
				
			||||||
 | 
						scriptExecutor       ScriptExecutor
 | 
				
			||||||
 | 
						cacheExecutor        CacheExecutor
 | 
				
			||||||
 | 
						scriptViewerExecutor ScriptViewerExecutor
 | 
				
			||||||
 | 
						checkerExecutor      CheckerExecutor
 | 
				
			||||||
 | 
						installerExecutor    InstallerExecutor
 | 
				
			||||||
 | 
						sourceExecutor       SourceDownloaderExecutor
 | 
				
			||||||
 | 
						repos                PackageFinder
 | 
				
			||||||
 | 
						// mgr                  manager.Manager
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type BuildArgs struct {
 | 
				
			||||||
 | 
						Opts       *types.BuildOpts
 | 
				
			||||||
 | 
						Info       *distro.OSRelease
 | 
				
			||||||
 | 
						PkgFormat_ string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *BuildArgs) BuildOpts() *types.BuildOpts {
 | 
				
			||||||
 | 
						return b.Opts
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *BuildArgs) OSRelease() *distro.OSRelease {
 | 
				
			||||||
 | 
						return b.Info
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *BuildArgs) PkgFormat() string {
 | 
				
			||||||
 | 
						return b.PkgFormat_
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type BuildPackageFromDbArgs struct {
 | 
				
			||||||
 | 
						BuildArgs
 | 
				
			||||||
 | 
						Package  *alrsh.Package
 | 
				
			||||||
 | 
						Packages []string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type BuildPackageFromScriptArgs struct {
 | 
				
			||||||
 | 
						BuildArgs
 | 
				
			||||||
 | 
						Script   string
 | 
				
			||||||
 | 
						Packages []string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Builder) BuildPackageFromDb(
 | 
				
			||||||
 | 
						ctx context.Context,
 | 
				
			||||||
 | 
						args *BuildPackageFromDbArgs,
 | 
				
			||||||
 | 
					) ([]*BuiltDep, error) {
 | 
				
			||||||
 | 
						scriptInfo := b.scriptResolver.ResolveScript(ctx, args.Package)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return b.BuildPackage(ctx, &BuildInput{
 | 
				
			||||||
 | 
							script:     scriptInfo.Script,
 | 
				
			||||||
 | 
							repository: scriptInfo.Repository,
 | 
				
			||||||
 | 
							packages:   args.Packages,
 | 
				
			||||||
 | 
							pkgFormat:  args.PkgFormat(),
 | 
				
			||||||
 | 
							opts:       args.Opts,
 | 
				
			||||||
 | 
							info:       args.Info,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Builder) BuildPackageFromScript(
 | 
				
			||||||
 | 
						ctx context.Context,
 | 
				
			||||||
 | 
						args *BuildPackageFromScriptArgs,
 | 
				
			||||||
 | 
					) ([]*BuiltDep, error) {
 | 
				
			||||||
 | 
						return b.BuildPackage(ctx, &BuildInput{
 | 
				
			||||||
 | 
							script:     args.Script,
 | 
				
			||||||
 | 
							repository: "default",
 | 
				
			||||||
 | 
							packages:   args.Packages,
 | 
				
			||||||
 | 
							pkgFormat:  args.PkgFormat(),
 | 
				
			||||||
 | 
							opts:       args.Opts,
 | 
				
			||||||
 | 
							info:       args.Info,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Builder) BuildPackage(
 | 
				
			||||||
 | 
						ctx context.Context,
 | 
				
			||||||
 | 
						input *BuildInput,
 | 
				
			||||||
 | 
					) ([]*BuiltDep, error) {
 | 
				
			||||||
 | 
						scriptPath := input.script
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						slog.Debug("ReadScript")
 | 
				
			||||||
 | 
						sf, err := b.scriptExecutor.ReadScript(ctx, scriptPath)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("failed reading script: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						slog.Debug("ExecuteFirstPass")
 | 
				
			||||||
 | 
						basePkg, varsOfPackages, err := b.scriptExecutor.ExecuteFirstPass(ctx, input, sf)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("failed ExecuteFirstPass: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var builtDeps []*BuiltDep
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !input.opts.Clean {
 | 
				
			||||||
 | 
							var remainingVars []*alrsh.Package
 | 
				
			||||||
 | 
							for _, vars := range varsOfPackages {
 | 
				
			||||||
 | 
								builtPkgPath, ok, err := b.cacheExecutor.CheckForBuiltPackage(ctx, input, vars)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return nil, err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if ok {
 | 
				
			||||||
 | 
									builtDeps = append(builtDeps, &BuiltDep{
 | 
				
			||||||
 | 
										Path: builtPkgPath,
 | 
				
			||||||
 | 
									})
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									remainingVars = append(remainingVars, vars)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if len(remainingVars) == 0 {
 | 
				
			||||||
 | 
								return builtDeps, nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						slog.Debug("ViewScript")
 | 
				
			||||||
 | 
						slog.Debug("", "varsOfPackages", varsOfPackages[0])
 | 
				
			||||||
 | 
						err = b.scriptViewerExecutor.ViewScript(ctx, input, sf, basePkg)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						slog.Info(gotext.Get("Building package"), "name", basePkg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, vars := range varsOfPackages {
 | 
				
			||||||
 | 
							cont, err := b.checkerExecutor.PerformChecks(ctx, input, vars)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if !cont {
 | 
				
			||||||
 | 
								return nil, errors.New("exit...")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						buildDepends := []string{}
 | 
				
			||||||
 | 
						optDepends := []string{}
 | 
				
			||||||
 | 
						depends := []string{}
 | 
				
			||||||
 | 
						sources := []string{}
 | 
				
			||||||
 | 
						checksums := []string{}
 | 
				
			||||||
 | 
						for _, vars := range varsOfPackages {
 | 
				
			||||||
 | 
							buildDepends = append(buildDepends, vars.BuildDepends.Resolved()...)
 | 
				
			||||||
 | 
							optDepends = append(optDepends, vars.OptDepends.Resolved()...)
 | 
				
			||||||
 | 
							depends = append(depends, vars.Depends.Resolved()...)
 | 
				
			||||||
 | 
							sources = append(sources, vars.Sources.Resolved()...)
 | 
				
			||||||
 | 
							checksums = append(checksums, vars.Checksums.Resolved()...)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						buildDepends = removeDuplicates(buildDepends)
 | 
				
			||||||
 | 
						optDepends = removeDuplicates(optDepends)
 | 
				
			||||||
 | 
						depends = removeDuplicates(depends)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(sources) != len(checksums) {
 | 
				
			||||||
 | 
							slog.Error(gotext.Get("The checksums array must be the same length as sources"))
 | 
				
			||||||
 | 
							return nil, errors.New("exit...")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						sources, checksums = removeDuplicatesSources(sources, checksums)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						slog.Debug("installBuildDeps")
 | 
				
			||||||
 | 
						alrBuildDeps, installedBuildDeps, err := b.installBuildDeps(ctx, input, buildDepends)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						slog.Debug("installOptDeps")
 | 
				
			||||||
 | 
						_, err = b.installOptDeps(ctx, input, optDepends)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						depNames := make(map[string]struct{})
 | 
				
			||||||
 | 
						for _, dep := range alrBuildDeps {
 | 
				
			||||||
 | 
							depNames[dep.Name] = struct{}{}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// We filter so as not to re-build what has already been built at the `installBuildDeps` stage.
 | 
				
			||||||
 | 
						var filteredDepends []string
 | 
				
			||||||
 | 
						for _, d := range depends {
 | 
				
			||||||
 | 
							if _, found := depNames[d]; !found {
 | 
				
			||||||
 | 
								filteredDepends = append(filteredDepends, d)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						slog.Debug("BuildALRDeps")
 | 
				
			||||||
 | 
						newBuiltDeps, repoDeps, err := b.BuildALRDeps(ctx, input, filteredDepends)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						slog.Debug("PrepareDirs")
 | 
				
			||||||
 | 
						err = b.scriptExecutor.PrepareDirs(ctx, input, basePkg)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						slog.Info(gotext.Get("Downloading sources"))
 | 
				
			||||||
 | 
						slog.Debug("DownloadSources")
 | 
				
			||||||
 | 
						err = b.sourceExecutor.DownloadSources(
 | 
				
			||||||
 | 
							ctx,
 | 
				
			||||||
 | 
							input,
 | 
				
			||||||
 | 
							basePkg,
 | 
				
			||||||
 | 
							SourcesInput{
 | 
				
			||||||
 | 
								Sources:   sources,
 | 
				
			||||||
 | 
								Checksums: checksums,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						builtDeps = removeDuplicates(append(builtDeps, newBuiltDeps...))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						slog.Debug("ExecuteSecondPass")
 | 
				
			||||||
 | 
						res, err := b.scriptExecutor.ExecuteSecondPass(
 | 
				
			||||||
 | 
							ctx,
 | 
				
			||||||
 | 
							input,
 | 
				
			||||||
 | 
							sf,
 | 
				
			||||||
 | 
							varsOfPackages,
 | 
				
			||||||
 | 
							repoDeps,
 | 
				
			||||||
 | 
							builtDeps,
 | 
				
			||||||
 | 
							basePkg,
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						builtDeps = removeDuplicates(append(builtDeps, res...))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = b.removeBuildDeps(ctx, input, installedBuildDeps)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return builtDeps, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Builder) removeBuildDeps(ctx context.Context, input interface {
 | 
				
			||||||
 | 
						BuildOptsProvider
 | 
				
			||||||
 | 
					}, deps []string,
 | 
				
			||||||
 | 
					) error {
 | 
				
			||||||
 | 
						if len(deps) > 0 {
 | 
				
			||||||
 | 
							remove, err := cliutils.YesNoPrompt(ctx, gotext.Get("Would you like to remove the build dependencies?"), input.BuildOpts().Interactive, false)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if remove {
 | 
				
			||||||
 | 
								err = b.installerExecutor.Remove(
 | 
				
			||||||
 | 
									ctx,
 | 
				
			||||||
 | 
									deps,
 | 
				
			||||||
 | 
									&manager.Opts{
 | 
				
			||||||
 | 
										NoConfirm: !input.BuildOpts().Interactive,
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type InstallPkgsArgs struct {
 | 
				
			||||||
 | 
						BuildArgs
 | 
				
			||||||
 | 
						AlrPkgs    []alrsh.Package
 | 
				
			||||||
 | 
						NativePkgs []string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Builder) InstallALRPackages(
 | 
				
			||||||
 | 
						ctx context.Context,
 | 
				
			||||||
 | 
						input interface {
 | 
				
			||||||
 | 
							OsInfoProvider
 | 
				
			||||||
 | 
							BuildOptsProvider
 | 
				
			||||||
 | 
							PkgFormatProvider
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						alrPkgs []alrsh.Package,
 | 
				
			||||||
 | 
					) error {
 | 
				
			||||||
 | 
						for _, pkg := range alrPkgs {
 | 
				
			||||||
 | 
							res, err := b.BuildPackageFromDb(
 | 
				
			||||||
 | 
								ctx,
 | 
				
			||||||
 | 
								&BuildPackageFromDbArgs{
 | 
				
			||||||
 | 
									Package:  &pkg,
 | 
				
			||||||
 | 
									Packages: []string{},
 | 
				
			||||||
 | 
									BuildArgs: BuildArgs{
 | 
				
			||||||
 | 
										Opts:       input.BuildOpts(),
 | 
				
			||||||
 | 
										Info:       input.OSRelease(),
 | 
				
			||||||
 | 
										PkgFormat_: input.PkgFormat(),
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							err = b.installerExecutor.InstallLocal(
 | 
				
			||||||
 | 
								ctx,
 | 
				
			||||||
 | 
								GetBuiltPaths(res),
 | 
				
			||||||
 | 
								&manager.Opts{
 | 
				
			||||||
 | 
									NoConfirm: !input.BuildOpts().Interactive,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Builder) BuildALRDeps(
 | 
				
			||||||
 | 
						ctx context.Context,
 | 
				
			||||||
 | 
						input interface {
 | 
				
			||||||
 | 
							OsInfoProvider
 | 
				
			||||||
 | 
							BuildOptsProvider
 | 
				
			||||||
 | 
							PkgFormatProvider
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						depends []string,
 | 
				
			||||||
 | 
					) (buildDeps []*BuiltDep, repoDeps []string, err error) {
 | 
				
			||||||
 | 
						if len(depends) > 0 {
 | 
				
			||||||
 | 
							slog.Info(gotext.Get("Installing dependencies"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							found, notFound, err := b.repos.FindPkgs(ctx, depends) // Поиск зависимостей
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, nil, fmt.Errorf("failed FindPkgs: %w", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							repoDeps = notFound
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Если для некоторых пакетов есть несколько опций, упрощаем их все в один срез
 | 
				
			||||||
 | 
							pkgs := cliutils.FlattenPkgs(
 | 
				
			||||||
 | 
								ctx,
 | 
				
			||||||
 | 
								found,
 | 
				
			||||||
 | 
								"install",
 | 
				
			||||||
 | 
								input.BuildOpts().Interactive,
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
 | 
							type item struct {
 | 
				
			||||||
 | 
								pkg      *alrsh.Package
 | 
				
			||||||
 | 
								packages []string
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							pkgsMap := make(map[string]*item)
 | 
				
			||||||
 | 
							for _, pkg := range pkgs {
 | 
				
			||||||
 | 
								name := pkg.BasePkgName
 | 
				
			||||||
 | 
								if name == "" {
 | 
				
			||||||
 | 
									name = pkg.Name
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if pkgsMap[name] == nil {
 | 
				
			||||||
 | 
									pkgsMap[name] = &item{
 | 
				
			||||||
 | 
										pkg: &pkg,
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								pkgsMap[name].packages = append(
 | 
				
			||||||
 | 
									pkgsMap[name].packages,
 | 
				
			||||||
 | 
									pkg.Name,
 | 
				
			||||||
 | 
								)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for basePkgName := range pkgsMap {
 | 
				
			||||||
 | 
								pkg := pkgsMap[basePkgName].pkg
 | 
				
			||||||
 | 
								res, err := b.BuildPackageFromDb(
 | 
				
			||||||
 | 
									ctx,
 | 
				
			||||||
 | 
									&BuildPackageFromDbArgs{
 | 
				
			||||||
 | 
										Package:  pkg,
 | 
				
			||||||
 | 
										Packages: pkgsMap[basePkgName].packages,
 | 
				
			||||||
 | 
										BuildArgs: BuildArgs{
 | 
				
			||||||
 | 
											Opts:       input.BuildOpts(),
 | 
				
			||||||
 | 
											Info:       input.OSRelease(),
 | 
				
			||||||
 | 
											PkgFormat_: input.PkgFormat(),
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return nil, nil, fmt.Errorf("failed build package from db: %w", err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								buildDeps = append(buildDeps, res...)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						repoDeps = removeDuplicates(repoDeps)
 | 
				
			||||||
 | 
						buildDeps = removeDuplicates(buildDeps)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return buildDeps, repoDeps, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (i *Builder) installBuildDeps(
 | 
				
			||||||
 | 
						ctx context.Context,
 | 
				
			||||||
 | 
						input interface {
 | 
				
			||||||
 | 
							OsInfoProvider
 | 
				
			||||||
 | 
							BuildOptsProvider
 | 
				
			||||||
 | 
							PkgFormatProvider
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						pkgs []string,
 | 
				
			||||||
 | 
					) ([]*BuiltDep, []string, error) {
 | 
				
			||||||
 | 
						var builtDeps []*BuiltDep
 | 
				
			||||||
 | 
						var deps []string
 | 
				
			||||||
 | 
						var err error
 | 
				
			||||||
 | 
						if len(pkgs) > 0 {
 | 
				
			||||||
 | 
							deps, err = i.installerExecutor.RemoveAlreadyInstalled(ctx, pkgs)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							builtDeps, err = i.InstallPkgs(ctx, input, deps) // Устанавливаем выбранные пакеты
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return builtDeps, deps, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (i *Builder) installOptDeps(
 | 
				
			||||||
 | 
						ctx context.Context,
 | 
				
			||||||
 | 
						input interface {
 | 
				
			||||||
 | 
							OsInfoProvider
 | 
				
			||||||
 | 
							BuildOptsProvider
 | 
				
			||||||
 | 
							PkgFormatProvider
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						pkgs []string,
 | 
				
			||||||
 | 
					) ([]*BuiltDep, error) {
 | 
				
			||||||
 | 
						var builtDeps []*BuiltDep
 | 
				
			||||||
 | 
						optDeps, err := i.installerExecutor.RemoveAlreadyInstalled(ctx, pkgs)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(optDeps) > 0 {
 | 
				
			||||||
 | 
							optDeps, err := cliutils.ChooseOptDepends(
 | 
				
			||||||
 | 
								ctx,
 | 
				
			||||||
 | 
								optDeps,
 | 
				
			||||||
 | 
								"install",
 | 
				
			||||||
 | 
								input.BuildOpts().Interactive,
 | 
				
			||||||
 | 
							) // Пользователя просят выбрать опциональные зависимости
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if len(optDeps) == 0 {
 | 
				
			||||||
 | 
								return builtDeps, nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							builtDeps, err = i.InstallPkgs(ctx, input, optDeps) // Устанавливаем выбранные пакеты
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return builtDeps, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (i *Builder) InstallPkgs(
 | 
				
			||||||
 | 
						ctx context.Context,
 | 
				
			||||||
 | 
						input interface {
 | 
				
			||||||
 | 
							OsInfoProvider
 | 
				
			||||||
 | 
							BuildOptsProvider
 | 
				
			||||||
 | 
							PkgFormatProvider
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						pkgs []string,
 | 
				
			||||||
 | 
					) ([]*BuiltDep, error) {
 | 
				
			||||||
 | 
						builtDeps, repoDeps, err := i.BuildALRDeps(ctx, input, pkgs)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(builtDeps) > 0 {
 | 
				
			||||||
 | 
							err = i.installerExecutor.InstallLocal(ctx, GetBuiltPaths(builtDeps), &manager.Opts{
 | 
				
			||||||
 | 
								NoConfirm: !input.BuildOpts().Interactive,
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(repoDeps) > 0 {
 | 
				
			||||||
 | 
							err = i.installerExecutor.Install(ctx, repoDeps, &manager.Opts{
 | 
				
			||||||
 | 
								NoConfirm: !input.BuildOpts().Interactive,
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return builtDeps, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
// ALR - Any Linux Repository
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
// Copyright (C) 2025 Евгений Храмов
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// This program is free software: you can redistribute it and/or modify
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
// it under the terms of the GNU General Public License as published by
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
@@ -28,9 +28,9 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	"gitea.plemya-x.ru/Plemya-x/ALR/internal/config"
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/config"
 | 
				
			||||||
	"gitea.plemya-x.ru/Plemya-x/ALR/internal/db"
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/db"
 | 
				
			||||||
	"gitea.plemya-x.ru/Plemya-x/ALR/internal/types"
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/pkg/types"
 | 
				
			||||||
	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro"
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro"
 | 
				
			||||||
	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager"
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/manager"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type TestPackageFinder struct {
 | 
					type TestPackageFinder struct {
 | 
				
			||||||
@@ -277,7 +277,7 @@ meta_bar() {
 | 
				
			|||||||
			fl, err := syntax.NewParser().Parse(strings.NewReader(tc.Script), "alr.sh")
 | 
								fl, err := syntax.NewParser().Parse(strings.NewReader(tc.Script), "alr.sh")
 | 
				
			||||||
			assert.NoError(t, err)
 | 
								assert.NoError(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			_, allVars, err := b.executeFirstPass(fl)
 | 
								_, allVars, err := b.scriptExecutor.ExecuteSecondPass(fl)
 | 
				
			||||||
			assert.NoError(t, err)
 | 
								assert.NoError(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			tc.Expected(t, allVars)
 | 
								tc.Expected(t, allVars)
 | 
				
			||||||
							
								
								
									
										69
									
								
								internal/build/cache.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								internal/build/cache.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,69 @@
 | 
				
			|||||||
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package build
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"path/filepath"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/goreleaser/nfpm/v2"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/pkg/alrsh"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Cache struct {
 | 
				
			||||||
 | 
						cfg Config
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *Cache) CheckForBuiltPackage(
 | 
				
			||||||
 | 
						ctx context.Context,
 | 
				
			||||||
 | 
						input *BuildInput,
 | 
				
			||||||
 | 
						vars *alrsh.Package,
 | 
				
			||||||
 | 
					) (string, bool, error) {
 | 
				
			||||||
 | 
						filename, err := pkgFileName(input, vars)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", false, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pkgPath := filepath.Join(getBaseDir(c.cfg, vars.Name), filename)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_, err = os.Stat(pkgPath)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", false, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return pkgPath, true, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func pkgFileName(
 | 
				
			||||||
 | 
						input interface {
 | 
				
			||||||
 | 
							OsInfoProvider
 | 
				
			||||||
 | 
							PkgFormatProvider
 | 
				
			||||||
 | 
							RepositoryProvider
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						vars *alrsh.Package,
 | 
				
			||||||
 | 
					) (string, error) {
 | 
				
			||||||
 | 
						pkgInfo := getBasePkgInfo(vars, input)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						packager, err := nfpm.Get(input.PkgFormat())
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return packager.ConventionalFileName(pkgInfo), nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										74
									
								
								internal/build/checker.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								internal/build/checker.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,74 @@
 | 
				
			|||||||
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package build
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"log/slog"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/leonelquinteros/gotext"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils"
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/cpu"
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/manager"
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/pkg/alrsh"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Checker struct {
 | 
				
			||||||
 | 
						mgr manager.Manager
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *Checker) PerformChecks(
 | 
				
			||||||
 | 
						ctx context.Context,
 | 
				
			||||||
 | 
						input *BuildInput,
 | 
				
			||||||
 | 
						vars *alrsh.Package,
 | 
				
			||||||
 | 
					) (bool, error) {
 | 
				
			||||||
 | 
						if !cpu.IsCompatibleWith(cpu.Arch(), vars.Architectures) { // Проверяем совместимость архитектуры
 | 
				
			||||||
 | 
							cont, err := cliutils.YesNoPrompt(
 | 
				
			||||||
 | 
								ctx,
 | 
				
			||||||
 | 
								gotext.Get("Your system's CPU architecture doesn't match this package. Do you want to build anyway?"),
 | 
				
			||||||
 | 
								input.opts.Interactive,
 | 
				
			||||||
 | 
								true,
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return false, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if !cont {
 | 
				
			||||||
 | 
								return false, nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						installed, err := c.mgr.ListInstalled(nil)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return false, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						filename, err := pkgFileName(input, vars)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return false, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if instVer, ok := installed[filename]; ok { // Если пакет уже установлен, выводим предупреждение
 | 
				
			||||||
 | 
							slog.Warn(gotext.Get("This package is already installed"),
 | 
				
			||||||
 | 
								"name", vars.Name,
 | 
				
			||||||
 | 
								"version", instVer,
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return true, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										71
									
								
								internal/build/dirs.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								internal/build/dirs.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,71 @@
 | 
				
			|||||||
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package build
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"path/filepath"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/pkg/types"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type BaseDirProvider interface {
 | 
				
			||||||
 | 
						BaseDir() string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type SrcDirProvider interface {
 | 
				
			||||||
 | 
						SrcDir() string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type PkgDirProvider interface {
 | 
				
			||||||
 | 
						PkgDir() string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ScriptDirProvider interface {
 | 
				
			||||||
 | 
						ScriptDir() string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getDirs(
 | 
				
			||||||
 | 
						cfg Config,
 | 
				
			||||||
 | 
						scriptPath string,
 | 
				
			||||||
 | 
						basePkg string,
 | 
				
			||||||
 | 
					) (types.Directories, error) {
 | 
				
			||||||
 | 
						pkgsDir := cfg.GetPaths().PkgsDir
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						scriptPath, err := filepath.Abs(scriptPath)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return types.Directories{}, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						baseDir := filepath.Join(pkgsDir, basePkg)
 | 
				
			||||||
 | 
						return types.Directories{
 | 
				
			||||||
 | 
							BaseDir:   getBaseDir(cfg, basePkg),
 | 
				
			||||||
 | 
							SrcDir:    getSrcDir(cfg, basePkg),
 | 
				
			||||||
 | 
							PkgDir:    filepath.Join(baseDir, "pkg"),
 | 
				
			||||||
 | 
							ScriptDir: getScriptDir(scriptPath),
 | 
				
			||||||
 | 
						}, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getBaseDir(cfg Config, basePkg string) string {
 | 
				
			||||||
 | 
						return filepath.Join(cfg.GetPaths().PkgsDir, basePkg)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getSrcDir(cfg Config, basePkg string) string {
 | 
				
			||||||
 | 
						return filepath.Join(getBaseDir(cfg, basePkg), "src")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getScriptDir(scriptPath string) string {
 | 
				
			||||||
 | 
						return filepath.Dir(scriptPath)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
// ALR - Any Linux Repository
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
// Copyright (C) 2025 Евгений Храмов
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// This program is free software: you can redistribute it and/or modify
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
// it under the terms of the GNU General Public License as published by
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
@@ -14,7 +14,7 @@
 | 
				
			|||||||
// You should have received a copy of the GNU General Public License
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package build
 | 
					package finddeps
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"bytes"
 | 
						"bytes"
 | 
				
			||||||
@@ -27,10 +27,10 @@ import (
 | 
				
			|||||||
	"github.com/goreleaser/nfpm/v2"
 | 
						"github.com/goreleaser/nfpm/v2"
 | 
				
			||||||
	"github.com/leonelquinteros/gotext"
 | 
						"github.com/leonelquinteros/gotext"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"gitea.plemya-x.ru/Plemya-x/ALR/internal/types"
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/pkg/types"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func rpmFindDependencies(ctx context.Context, pkgInfo *nfpm.Info, dirs types.Directories, command string, updateFunc func(string)) error {
 | 
					func rpmFindDependenciesALTLinux(ctx context.Context, pkgInfo *nfpm.Info, dirs types.Directories, command string, envs []string, updateFunc func(string)) error {
 | 
				
			||||||
	if _, err := exec.LookPath(command); err != nil {
 | 
						if _, err := exec.LookPath(command); err != nil {
 | 
				
			||||||
		slog.Info(gotext.Get("Command not found on the system"), "command", command)
 | 
							slog.Info(gotext.Get("Command not found on the system"), "command", command)
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
@@ -49,8 +49,8 @@ func rpmFindDependencies(ctx context.Context, pkgInfo *nfpm.Info, dirs types.Dir
 | 
				
			|||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cmd := exec.Command(command)
 | 
						cmd := exec.CommandContext(ctx, command)
 | 
				
			||||||
	cmd.Stdin = bytes.NewBufferString(strings.Join(paths, "\n"))
 | 
						cmd.Stdin = bytes.NewBufferString(strings.Join(paths, "\n") + "\n")
 | 
				
			||||||
	cmd.Env = append(cmd.Env,
 | 
						cmd.Env = append(cmd.Env,
 | 
				
			||||||
		"RPM_BUILD_ROOT="+dirs.PkgDir,
 | 
							"RPM_BUILD_ROOT="+dirs.PkgDir,
 | 
				
			||||||
		"RPM_FINDPROV_METHOD=",
 | 
							"RPM_FINDPROV_METHOD=",
 | 
				
			||||||
@@ -58,6 +58,7 @@ func rpmFindDependencies(ctx context.Context, pkgInfo *nfpm.Info, dirs types.Dir
 | 
				
			|||||||
		"RPM_DATADIR=",
 | 
							"RPM_DATADIR=",
 | 
				
			||||||
		"RPM_SUBPACKAGE_NAME=",
 | 
							"RPM_SUBPACKAGE_NAME=",
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
 | 
						cmd.Env = append(cmd.Env, envs...)
 | 
				
			||||||
	var out bytes.Buffer
 | 
						var out bytes.Buffer
 | 
				
			||||||
	var stderr bytes.Buffer
 | 
						var stderr bytes.Buffer
 | 
				
			||||||
	cmd.Stdout = &out
 | 
						cmd.Stdout = &out
 | 
				
			||||||
@@ -66,6 +67,7 @@ func rpmFindDependencies(ctx context.Context, pkgInfo *nfpm.Info, dirs types.Dir
 | 
				
			|||||||
		slog.Error(stderr.String())
 | 
							slog.Error(stderr.String())
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						slog.Debug(stderr.String())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	dependencies := strings.Split(strings.TrimSpace(out.String()), "\n")
 | 
						dependencies := strings.Split(strings.TrimSpace(out.String()), "\n")
 | 
				
			||||||
	for _, dep := range dependencies {
 | 
						for _, dep := range dependencies {
 | 
				
			||||||
@@ -77,15 +79,17 @@ func rpmFindDependencies(ctx context.Context, pkgInfo *nfpm.Info, dirs types.Dir
 | 
				
			|||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func rpmFindProvides(ctx context.Context, pkgInfo *nfpm.Info, dirs types.Directories) error {
 | 
					type ALTLinuxFindProvReq struct{}
 | 
				
			||||||
	return rpmFindDependencies(ctx, pkgInfo, dirs, "/usr/lib/rpm/find-provides", func(dep string) {
 | 
					
 | 
				
			||||||
 | 
					func (o *ALTLinuxFindProvReq) FindProvides(ctx context.Context, pkgInfo *nfpm.Info, dirs types.Directories, skiplist []string) error {
 | 
				
			||||||
 | 
						return rpmFindDependenciesALTLinux(ctx, pkgInfo, dirs, "/usr/lib/rpm/find-provides", []string{"RPM_FINDPROV_SKIPLIST=" + strings.Join(skiplist, "\n")}, func(dep string) {
 | 
				
			||||||
		slog.Info(gotext.Get("Provided dependency found"), "dep", dep)
 | 
							slog.Info(gotext.Get("Provided dependency found"), "dep", dep)
 | 
				
			||||||
		pkgInfo.Overridables.Provides = append(pkgInfo.Overridables.Provides, dep)
 | 
							pkgInfo.Overridables.Provides = append(pkgInfo.Overridables.Provides, dep)
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func rpmFindRequires(ctx context.Context, pkgInfo *nfpm.Info, dirs types.Directories) error {
 | 
					func (o *ALTLinuxFindProvReq) FindRequires(ctx context.Context, pkgInfo *nfpm.Info, dirs types.Directories, skiplist []string) error {
 | 
				
			||||||
	return rpmFindDependencies(ctx, pkgInfo, dirs, "/usr/lib/rpm/find-requires", func(dep string) {
 | 
						return rpmFindDependenciesALTLinux(ctx, pkgInfo, dirs, "/usr/lib/rpm/find-requires", []string{"RPM_FINDREQ_SKIPLIST=" + strings.Join(skiplist, "\n")}, func(dep string) {
 | 
				
			||||||
		slog.Info(gotext.Get("Required dependency found"), "dep", dep)
 | 
							slog.Info(gotext.Get("Required dependency found"), "dep", dep)
 | 
				
			||||||
		pkgInfo.Overridables.Depends = append(pkgInfo.Overridables.Depends, dep)
 | 
							pkgInfo.Overridables.Depends = append(pkgInfo.Overridables.Depends, dep)
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
							
								
								
									
										39
									
								
								internal/build/find_deps/empty.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								internal/build/find_deps/empty.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,39 @@
 | 
				
			|||||||
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package finddeps
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"log/slog"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/goreleaser/nfpm/v2"
 | 
				
			||||||
 | 
						"github.com/leonelquinteros/gotext"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/pkg/types"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type EmptyFindProvReq struct{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (o *EmptyFindProvReq) FindProvides(ctx context.Context, pkgInfo *nfpm.Info, dirs types.Directories, skiplist []string) error {
 | 
				
			||||||
 | 
						slog.Info(gotext.Get("AutoProv is not implemented for this package format, so it's skipped"))
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (o *EmptyFindProvReq) FindRequires(ctx context.Context, pkgInfo *nfpm.Info, dirs types.Directories, skiplist []string) error {
 | 
				
			||||||
 | 
						slog.Info(gotext.Get("AutoReq is not implemented for this package format, so it's skipped"))
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										118
									
								
								internal/build/find_deps/fedora.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								internal/build/find_deps/fedora.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,118 @@
 | 
				
			|||||||
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package finddeps
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"log/slog"
 | 
				
			||||||
 | 
						"os/exec"
 | 
				
			||||||
 | 
						"path"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/goreleaser/nfpm/v2"
 | 
				
			||||||
 | 
						"github.com/leonelquinteros/gotext"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/pkg/types"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type FedoraFindProvReq struct{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func rpmFindDependenciesFedora(ctx context.Context, pkgInfo *nfpm.Info, dirs types.Directories, command string, args []string, updateFunc func(string)) error {
 | 
				
			||||||
 | 
						if _, err := exec.LookPath(command); err != nil {
 | 
				
			||||||
 | 
							slog.Info(gotext.Get("Command not found on the system"), "command", command)
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var paths []string
 | 
				
			||||||
 | 
						for _, content := range pkgInfo.Contents {
 | 
				
			||||||
 | 
							if content.Type != "dir" {
 | 
				
			||||||
 | 
								paths = append(paths,
 | 
				
			||||||
 | 
									path.Join(dirs.PkgDir, content.Destination),
 | 
				
			||||||
 | 
								)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(paths) == 0 {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cmd := exec.CommandContext(ctx, command, args...)
 | 
				
			||||||
 | 
						cmd.Stdin = bytes.NewBufferString(strings.Join(paths, "\n") + "\n")
 | 
				
			||||||
 | 
						cmd.Env = append(cmd.Env,
 | 
				
			||||||
 | 
							"RPM_BUILD_ROOT="+dirs.PkgDir,
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
						var out bytes.Buffer
 | 
				
			||||||
 | 
						var stderr bytes.Buffer
 | 
				
			||||||
 | 
						cmd.Stdout = &out
 | 
				
			||||||
 | 
						cmd.Stderr = &stderr
 | 
				
			||||||
 | 
						if err := cmd.Run(); err != nil {
 | 
				
			||||||
 | 
							slog.Error(stderr.String())
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						slog.Debug(stderr.String())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dependencies := strings.Split(strings.TrimSpace(out.String()), "\n")
 | 
				
			||||||
 | 
						for _, dep := range dependencies {
 | 
				
			||||||
 | 
							if dep != "" {
 | 
				
			||||||
 | 
								updateFunc(dep)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (o *FedoraFindProvReq) FindProvides(ctx context.Context, pkgInfo *nfpm.Info, dirs types.Directories, skiplist []string) error {
 | 
				
			||||||
 | 
						return rpmFindDependenciesFedora(
 | 
				
			||||||
 | 
							ctx,
 | 
				
			||||||
 | 
							pkgInfo,
 | 
				
			||||||
 | 
							dirs,
 | 
				
			||||||
 | 
							"/usr/lib/rpm/rpmdeps",
 | 
				
			||||||
 | 
							[]string{
 | 
				
			||||||
 | 
								"--define=_use_internal_dependency_generator 1",
 | 
				
			||||||
 | 
								"--provides",
 | 
				
			||||||
 | 
								fmt.Sprintf(
 | 
				
			||||||
 | 
									"--define=__provides_exclude_from %s\"",
 | 
				
			||||||
 | 
									strings.Join(skiplist, "|"),
 | 
				
			||||||
 | 
								),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							func(dep string) {
 | 
				
			||||||
 | 
								slog.Info(gotext.Get("Provided dependency found"), "dep", dep)
 | 
				
			||||||
 | 
								pkgInfo.Overridables.Provides = append(pkgInfo.Overridables.Provides, dep)
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (o *FedoraFindProvReq) FindRequires(ctx context.Context, pkgInfo *nfpm.Info, dirs types.Directories, skiplist []string) error {
 | 
				
			||||||
 | 
						return rpmFindDependenciesFedora(
 | 
				
			||||||
 | 
							ctx,
 | 
				
			||||||
 | 
							pkgInfo,
 | 
				
			||||||
 | 
							dirs,
 | 
				
			||||||
 | 
							"/usr/lib/rpm/rpmdeps",
 | 
				
			||||||
 | 
							[]string{
 | 
				
			||||||
 | 
								"--define=_use_internal_dependency_generator 1",
 | 
				
			||||||
 | 
								"--requires",
 | 
				
			||||||
 | 
								fmt.Sprintf(
 | 
				
			||||||
 | 
									"--define=__requires_exclude_from %s",
 | 
				
			||||||
 | 
									strings.Join(skiplist, "|"),
 | 
				
			||||||
 | 
								),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							func(dep string) {
 | 
				
			||||||
 | 
								slog.Info(gotext.Get("Required dependency found"), "dep", dep)
 | 
				
			||||||
 | 
								pkgInfo.Overridables.Depends = append(pkgInfo.Overridables.Depends, dep)
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										58
									
								
								internal/build/find_deps/find_deps.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								internal/build/find_deps/find_deps.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,58 @@
 | 
				
			|||||||
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package finddeps
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/goreleaser/nfpm/v2"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro"
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/pkg/types"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ProvReqFinder interface {
 | 
				
			||||||
 | 
						FindProvides(ctx context.Context, pkgInfo *nfpm.Info, dirs types.Directories, skiplist []string) error
 | 
				
			||||||
 | 
						FindRequires(ctx context.Context, pkgInfo *nfpm.Info, dirs types.Directories, skiplist []string) error
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ProvReqService struct {
 | 
				
			||||||
 | 
						finder ProvReqFinder
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func New(info *distro.OSRelease, pkgFormat string) *ProvReqService {
 | 
				
			||||||
 | 
						s := &ProvReqService{
 | 
				
			||||||
 | 
							finder: &EmptyFindProvReq{},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if pkgFormat == "rpm" {
 | 
				
			||||||
 | 
							switch info.ID {
 | 
				
			||||||
 | 
							case "altlinux":
 | 
				
			||||||
 | 
								s.finder = &ALTLinuxFindProvReq{}
 | 
				
			||||||
 | 
							case "fedora":
 | 
				
			||||||
 | 
								s.finder = &FedoraFindProvReq{}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return s
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *ProvReqService) FindProvides(ctx context.Context, pkgInfo *nfpm.Info, dirs types.Directories, skiplist []string) error {
 | 
				
			||||||
 | 
						return s.finder.FindProvides(ctx, pkgInfo, dirs, skiplist)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *ProvReqService) FindRequires(ctx context.Context, pkgInfo *nfpm.Info, dirs types.Directories, skiplist []string) error {
 | 
				
			||||||
 | 
						return s.finder.FindRequires(ctx, pkgInfo, dirs, skiplist)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										296
									
								
								internal/build/firejail.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										296
									
								
								internal/build/firejail.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,296 @@
 | 
				
			|||||||
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package build
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"log/slog"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"path/filepath"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/goreleaser/nfpm/v2/files"
 | 
				
			||||||
 | 
						"github.com/leonelquinteros/gotext"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/pkg/alrsh"
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/pkg/types"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						firejailedDir     = "/usr/lib/alr/firejailed"
 | 
				
			||||||
 | 
						defaultDirMode    = 0o755
 | 
				
			||||||
 | 
						defaultScriptMode = 0o755
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						ErrInvalidDestination = errors.New("invalid destination path")
 | 
				
			||||||
 | 
						ErrMissingProfile     = errors.New("default profile is missing")
 | 
				
			||||||
 | 
						ErrEmptyPackageName   = errors.New("package name cannot be empty")
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var binaryDirectories = []string{
 | 
				
			||||||
 | 
						"/usr/bin/",
 | 
				
			||||||
 | 
						"/bin/",
 | 
				
			||||||
 | 
						"/usr/local/bin/",
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func moveWithSymlinkHandling(src, dst string) error {
 | 
				
			||||||
 | 
						srcInfo, err := os.Lstat(src)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("failed to get source info: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := os.MkdirAll(filepath.Dir(dst), 0o755); err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("failed to create destination directory: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if srcInfo.Mode()&os.ModeSymlink != 0 {
 | 
				
			||||||
 | 
							return moveSymlink(src, dst)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := os.Rename(src, dst); err != nil {
 | 
				
			||||||
 | 
							return copyAndRemove(src, dst)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func moveSymlink(src, dst string) error {
 | 
				
			||||||
 | 
						target, err := os.Readlink(src)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("failed to read symlink: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := os.Symlink(target, dst); err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("failed to create symlink: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := os.Remove(src); err != nil {
 | 
				
			||||||
 | 
							os.Remove(dst)
 | 
				
			||||||
 | 
							return fmt.Errorf("failed to remove original symlink: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func copyAndRemove(src, dst string) error {
 | 
				
			||||||
 | 
						srcFile, err := os.Open(src)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("failed to open source: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer srcFile.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dstFile, err := os.Create(dst)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("failed to create destination: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer dstFile.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if _, err := io.Copy(dstFile, srcFile); err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("failed to copy content: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						srcInfo, err := srcFile.Stat()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("failed to get source stats: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := dstFile.Chmod(srcInfo.Mode()); err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("failed to set permissions: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := os.Remove(src); err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("failed to remove source: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func moveFileWithErrorHandling(src, dst string) error {
 | 
				
			||||||
 | 
						err := moveWithSymlinkHandling(src, dst)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if os.IsPermission(err) {
 | 
				
			||||||
 | 
								return fmt.Errorf("permission denied: %w", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if os.IsNotExist(err) {
 | 
				
			||||||
 | 
								return fmt.Errorf("source file does not exist: %w", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return fmt.Errorf("failed to move file: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func applyFirejailIntegration(
 | 
				
			||||||
 | 
						vars *alrsh.Package,
 | 
				
			||||||
 | 
						dirs types.Directories,
 | 
				
			||||||
 | 
						contents []*files.Content,
 | 
				
			||||||
 | 
					) ([]*files.Content, error) {
 | 
				
			||||||
 | 
						slog.Info(gotext.Get("Applying FireJail integration"), "package", vars.Name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := createFirejailedDirectory(dirs.PkgDir); err != nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("failed to create firejailed directory: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						newContents, err := processBinaryFiles(vars, contents, dirs)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("failed to process binary files: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return append(contents, newContents...), nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func createFirejailedDirectory(pkgDir string) error {
 | 
				
			||||||
 | 
						firejailedPath := filepath.Join(pkgDir, firejailedDir)
 | 
				
			||||||
 | 
						return os.MkdirAll(firejailedPath, defaultDirMode)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func processBinaryFiles(pkg *alrsh.Package, contents []*files.Content, dirs types.Directories) ([]*files.Content, error) {
 | 
				
			||||||
 | 
						var newContents []*files.Content
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, content := range contents {
 | 
				
			||||||
 | 
							if content.Type == "dir" {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if !isBinaryFile(content.Destination) {
 | 
				
			||||||
 | 
								slog.Debug("content not binary file", "content", content)
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							slog.Debug("process content", "content", content)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							newContent, err := createFirejailedBinary(pkg, content, dirs)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, fmt.Errorf("failed to create firejailed binary for %s: %w", content.Destination, err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if newContent != nil {
 | 
				
			||||||
 | 
								newContents = append(newContents, newContent...)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return newContents, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func isBinaryFile(destination string) bool {
 | 
				
			||||||
 | 
						for _, binDir := range binaryDirectories {
 | 
				
			||||||
 | 
							if strings.HasPrefix(destination, binDir) {
 | 
				
			||||||
 | 
								return true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func createFirejailedBinary(
 | 
				
			||||||
 | 
						pkg *alrsh.Package,
 | 
				
			||||||
 | 
						content *files.Content,
 | 
				
			||||||
 | 
						dirs types.Directories,
 | 
				
			||||||
 | 
					) ([]*files.Content, error) {
 | 
				
			||||||
 | 
						origFilePath, err := generateFirejailedPath(content.Destination)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						profiles := pkg.FireJailProfiles.Resolved()
 | 
				
			||||||
 | 
						sourceProfilePath, ok := profiles[content.Destination]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							sourceProfilePath, ok = profiles["default"]
 | 
				
			||||||
 | 
							if !ok {
 | 
				
			||||||
 | 
								return nil, errors.New("default profile is missing")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sourceProfilePath = filepath.Join(dirs.ScriptDir, sourceProfilePath)
 | 
				
			||||||
 | 
						dest, err := createFirejailProfilePath(content.Destination)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = createProfile(filepath.Join(dirs.PkgDir, dest), sourceProfilePath)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := moveFileWithErrorHandling(filepath.Join(dirs.PkgDir, content.Destination), filepath.Join(dirs.PkgDir, origFilePath)); err != nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("failed to move original binary: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						content.Type = "file"
 | 
				
			||||||
 | 
						content.Source = filepath.Join(dirs.PkgDir, content.Destination)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Create wrapper script
 | 
				
			||||||
 | 
						if err := createWrapperScript(filepath.Join(dirs.PkgDir, content.Destination), origFilePath, dest); err != nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("failed to create wrapper script: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return buildContents(pkg, dirs, &[]string{
 | 
				
			||||||
 | 
							origFilePath,
 | 
				
			||||||
 | 
							dest,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func generateSafeName(destination string) (string, error) {
 | 
				
			||||||
 | 
						cleanPath := strings.TrimPrefix(destination, ".")
 | 
				
			||||||
 | 
						if cleanPath == "" {
 | 
				
			||||||
 | 
							return "", fmt.Errorf("invalid destination path: %s", destination)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return strings.ReplaceAll(cleanPath, "/", "_"), nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func generateFirejailedPath(destination string) (string, error) {
 | 
				
			||||||
 | 
						safeName, err := generateSafeName(destination)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return filepath.Join(firejailedDir, safeName), nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func createProfile(destProfilePath, profilePath string) error {
 | 
				
			||||||
 | 
						srcFile, err := os.Open(profilePath)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer srcFile.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						destFile, err := os.Create(destProfilePath)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer destFile.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_, err = io.Copy(destFile, srcFile)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return destFile.Sync()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func createWrapperScript(scriptPath, origFilePath, profilePath string) error {
 | 
				
			||||||
 | 
						scriptContent := fmt.Sprintf("#!/bin/bash\nexec firejail --profile=%q %q \"$@\"\n", profilePath, origFilePath)
 | 
				
			||||||
 | 
						return os.WriteFile(scriptPath, []byte(scriptContent), defaultDirMode)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func createFirejailProfilePath(binaryPath string) (string, error) {
 | 
				
			||||||
 | 
						name, err := generateSafeName(binaryPath)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return filepath.Join(firejailedDir, fmt.Sprintf("%s.profile", name)), nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										322
									
								
								internal/build/firejail_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										322
									
								
								internal/build/firejail_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,322 @@
 | 
				
			|||||||
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package build
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"path/filepath"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/goreleaser/nfpm/v2/files"
 | 
				
			||||||
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/pkg/alrsh"
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/pkg/types"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestIsBinaryFile(t *testing.T) {
 | 
				
			||||||
 | 
						tests := []struct {
 | 
				
			||||||
 | 
							name        string
 | 
				
			||||||
 | 
							destination string
 | 
				
			||||||
 | 
							expected    bool
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{"usr/bin binary", "/usr/bin/test", true},
 | 
				
			||||||
 | 
							{"bin binary", "/bin/test", true},
 | 
				
			||||||
 | 
							{"usr/local/bin binary", "/usr/local/bin/test", true},
 | 
				
			||||||
 | 
							{"lib file", "/usr/lib/test.so", false},
 | 
				
			||||||
 | 
							{"etc file", "/etc/config", false},
 | 
				
			||||||
 | 
							{"empty destination", "", false},
 | 
				
			||||||
 | 
							{"root level file", "./test", false},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, tt := range tests {
 | 
				
			||||||
 | 
							t.Run(tt.name, func(t *testing.T) {
 | 
				
			||||||
 | 
								result := isBinaryFile(tt.destination)
 | 
				
			||||||
 | 
								assert.Equal(t, tt.expected, result)
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestGenerateSafeName(t *testing.T) {
 | 
				
			||||||
 | 
						tests := []struct {
 | 
				
			||||||
 | 
							name        string
 | 
				
			||||||
 | 
							destination string
 | 
				
			||||||
 | 
							expected    string
 | 
				
			||||||
 | 
							expectError bool
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{"usr/bin path", "./usr/bin/test", "_usr_bin_test", false},
 | 
				
			||||||
 | 
							{"bin path", "./bin/test", "_bin_test", false},
 | 
				
			||||||
 | 
							{"nested path", "./usr/local/bin/app", "_usr_local_bin_app", false},
 | 
				
			||||||
 | 
							{"path with spaces", "./usr/bin/my app", "_usr_bin_my app", false},
 | 
				
			||||||
 | 
							{"empty after trim", ".", "", true},
 | 
				
			||||||
 | 
							{"empty string", "", "", true},
 | 
				
			||||||
 | 
							{"only dots", "..", ".", false},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, tt := range tests {
 | 
				
			||||||
 | 
							t.Run(tt.name, func(t *testing.T) {
 | 
				
			||||||
 | 
								result, err := generateSafeName(tt.destination)
 | 
				
			||||||
 | 
								if tt.expectError {
 | 
				
			||||||
 | 
									assert.Error(t, err)
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									assert.NoError(t, err)
 | 
				
			||||||
 | 
									assert.Equal(t, tt.expected, result)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestCreateWrapperScript(t *testing.T) {
 | 
				
			||||||
 | 
						tests := []struct {
 | 
				
			||||||
 | 
							name            string
 | 
				
			||||||
 | 
							origFilePath    string
 | 
				
			||||||
 | 
							profilePath     string
 | 
				
			||||||
 | 
							expectedContent string
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"basic wrapper",
 | 
				
			||||||
 | 
								"/usr/lib/alr/firejailed/_usr_bin_test",
 | 
				
			||||||
 | 
								"/usr/lib/alr/firejailed/_usr_bin_test.profile",
 | 
				
			||||||
 | 
								"#!/bin/bash\nexec firejail --profile=\"/usr/lib/alr/firejailed/_usr_bin_test.profile\" \"/usr/lib/alr/firejailed/_usr_bin_test\" \"$@\"\n",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"path with spaces",
 | 
				
			||||||
 | 
								"/usr/lib/alr/firejailed/_usr_bin_my_app",
 | 
				
			||||||
 | 
								"/usr/lib/alr/firejailed/_usr_bin_my_app.profile",
 | 
				
			||||||
 | 
								"#!/bin/bash\nexec firejail --profile=\"/usr/lib/alr/firejailed/_usr_bin_my_app.profile\" \"/usr/lib/alr/firejailed/_usr_bin_my_app\" \"$@\"\n",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, tt := range tests {
 | 
				
			||||||
 | 
							t.Run(tt.name, func(t *testing.T) {
 | 
				
			||||||
 | 
								tmpDir := t.TempDir()
 | 
				
			||||||
 | 
								scriptPath := filepath.Join(tmpDir, "wrapper.sh")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								err := createWrapperScript(scriptPath, tt.origFilePath, tt.profilePath)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								assert.NoError(t, err)
 | 
				
			||||||
 | 
								assert.FileExists(t, scriptPath)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								content, err := os.ReadFile(scriptPath)
 | 
				
			||||||
 | 
								assert.NoError(t, err)
 | 
				
			||||||
 | 
								assert.Equal(t, tt.expectedContent, string(content))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Check file permissions
 | 
				
			||||||
 | 
								info, err := os.Stat(scriptPath)
 | 
				
			||||||
 | 
								assert.NoError(t, err)
 | 
				
			||||||
 | 
								assert.Equal(t, os.FileMode(defaultDirMode), info.Mode())
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestCreateFirejailedBinary(t *testing.T) {
 | 
				
			||||||
 | 
						tests := []struct {
 | 
				
			||||||
 | 
							name        string
 | 
				
			||||||
 | 
							setupFunc   func(string) (*alrsh.Package, *files.Content, types.Directories)
 | 
				
			||||||
 | 
							expectError bool
 | 
				
			||||||
 | 
							errorMsg    string
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"successful creation with default profile",
 | 
				
			||||||
 | 
								func(tmpDir string) (*alrsh.Package, *files.Content, types.Directories) {
 | 
				
			||||||
 | 
									pkgDir := filepath.Join(tmpDir, "pkg")
 | 
				
			||||||
 | 
									scriptDir := filepath.Join(tmpDir, "scripts")
 | 
				
			||||||
 | 
									os.MkdirAll(pkgDir, 0o755)
 | 
				
			||||||
 | 
									os.MkdirAll(scriptDir, 0o755)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									binDir := filepath.Join(pkgDir, "usr", "bin")
 | 
				
			||||||
 | 
									os.MkdirAll(binDir, 0o755)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									srcBinary := filepath.Join(binDir, "test-binary")
 | 
				
			||||||
 | 
									os.WriteFile(srcBinary, []byte("#!/bin/bash\necho test"), 0o755)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									defaultProfile := filepath.Join(scriptDir, "default.profile")
 | 
				
			||||||
 | 
									os.WriteFile(defaultProfile, []byte("include /etc/firejail/default.profile\nnet none"), 0o644)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									pkg := &alrsh.Package{
 | 
				
			||||||
 | 
										Name: "test-pkg",
 | 
				
			||||||
 | 
										FireJailProfiles: alrsh.OverridableFromMap(map[string]map[string]string{
 | 
				
			||||||
 | 
											"": {"default": "default.profile"},
 | 
				
			||||||
 | 
										}),
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									alrsh.ResolvePackage(pkg, []string{""})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									content := &files.Content{
 | 
				
			||||||
 | 
										Source:      srcBinary,
 | 
				
			||||||
 | 
										Destination: "/usr/bin/test-binary",
 | 
				
			||||||
 | 
										Type:        "file",
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									dirs := types.Directories{PkgDir: pkgDir, ScriptDir: scriptDir}
 | 
				
			||||||
 | 
									return pkg, content, dirs
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								false,
 | 
				
			||||||
 | 
								"",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"successful creation with specific profile",
 | 
				
			||||||
 | 
								func(tmpDir string) (*alrsh.Package, *files.Content, types.Directories) {
 | 
				
			||||||
 | 
									pkgDir := filepath.Join(tmpDir, "pkg")
 | 
				
			||||||
 | 
									scriptDir := filepath.Join(tmpDir, "scripts")
 | 
				
			||||||
 | 
									os.MkdirAll(pkgDir, 0o755)
 | 
				
			||||||
 | 
									os.MkdirAll(scriptDir, 0o755)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									binDir := filepath.Join(pkgDir, "usr", "bin")
 | 
				
			||||||
 | 
									os.MkdirAll(binDir, 0o755)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									srcBinary := filepath.Join(binDir, "special-binary")
 | 
				
			||||||
 | 
									os.WriteFile(srcBinary, []byte("#!/bin/bash\necho special"), 0o755)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									defaultProfile := filepath.Join(scriptDir, "default.profile")
 | 
				
			||||||
 | 
									os.WriteFile(defaultProfile, []byte("include /etc/firejail/default.profile"), 0o644)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									specialProfile := filepath.Join(scriptDir, "special.profile")
 | 
				
			||||||
 | 
									os.WriteFile(specialProfile, []byte("include /etc/firejail/default.profile\nnet none\nprivate-tmp"), 0o644)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									pkg := &alrsh.Package{
 | 
				
			||||||
 | 
										Name: "test-pkg",
 | 
				
			||||||
 | 
										FireJailProfiles: alrsh.OverridableFromMap(map[string]map[string]string{
 | 
				
			||||||
 | 
											"": {"default": "default.profile", "/usr/bin/special-binary": "special.profile"},
 | 
				
			||||||
 | 
										}),
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									alrsh.ResolvePackage(pkg, []string{""})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									content := &files.Content{
 | 
				
			||||||
 | 
										Source:      srcBinary,
 | 
				
			||||||
 | 
										Destination: "/usr/bin/special-binary",
 | 
				
			||||||
 | 
										Type:        "file",
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									dirs := types.Directories{PkgDir: pkgDir, ScriptDir: scriptDir}
 | 
				
			||||||
 | 
									return pkg, content, dirs
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								false,
 | 
				
			||||||
 | 
								"",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"missing default profile",
 | 
				
			||||||
 | 
								func(tmpDir string) (*alrsh.Package, *files.Content, types.Directories) {
 | 
				
			||||||
 | 
									pkgDir := filepath.Join(tmpDir, "pkg")
 | 
				
			||||||
 | 
									scriptDir := filepath.Join(tmpDir, "scripts")
 | 
				
			||||||
 | 
									os.MkdirAll(pkgDir, 0o755)
 | 
				
			||||||
 | 
									os.MkdirAll(scriptDir, 0o755)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									srcBinary := filepath.Join(tmpDir, "test-binary")
 | 
				
			||||||
 | 
									os.WriteFile(srcBinary, []byte("#!/bin/bash\necho test"), 0o755)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									pkg := &alrsh.Package{
 | 
				
			||||||
 | 
										Name:             "test-pkg",
 | 
				
			||||||
 | 
										FireJailProfiles: alrsh.OverridableFromMap(map[string]map[string]string{"": {}}),
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									alrsh.ResolvePackage(pkg, []string{""})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									content := &files.Content{Source: srcBinary, Destination: "./usr/bin/test-binary", Type: "file"}
 | 
				
			||||||
 | 
									dirs := types.Directories{PkgDir: pkgDir, ScriptDir: scriptDir}
 | 
				
			||||||
 | 
									return pkg, content, dirs
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								true,
 | 
				
			||||||
 | 
								"default profile is missing",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"profile file not found",
 | 
				
			||||||
 | 
								func(tmpDir string) (*alrsh.Package, *files.Content, types.Directories) {
 | 
				
			||||||
 | 
									pkgDir := filepath.Join(tmpDir, "pkg")
 | 
				
			||||||
 | 
									scriptDir := filepath.Join(tmpDir, "scripts")
 | 
				
			||||||
 | 
									os.MkdirAll(pkgDir, 0o755)
 | 
				
			||||||
 | 
									os.MkdirAll(scriptDir, 0o755)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									srcBinary := filepath.Join(tmpDir, "test-binary")
 | 
				
			||||||
 | 
									os.WriteFile(srcBinary, []byte("#!/bin/bash\necho test"), 0o755)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									pkg := &alrsh.Package{
 | 
				
			||||||
 | 
										Name:             "test-pkg",
 | 
				
			||||||
 | 
										FireJailProfiles: alrsh.OverridableFromMap(map[string]map[string]string{"": {"default": "nonexistent.profile"}}),
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									alrsh.ResolvePackage(pkg, []string{""})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									content := &files.Content{Source: srcBinary, Destination: "./usr/bin/test-binary", Type: "file"}
 | 
				
			||||||
 | 
									dirs := types.Directories{PkgDir: pkgDir, ScriptDir: scriptDir}
 | 
				
			||||||
 | 
									return pkg, content, dirs
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								true,
 | 
				
			||||||
 | 
								"",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"invalid destination path",
 | 
				
			||||||
 | 
								func(tmpDir string) (*alrsh.Package, *files.Content, types.Directories) {
 | 
				
			||||||
 | 
									pkgDir := filepath.Join(tmpDir, "pkg")
 | 
				
			||||||
 | 
									scriptDir := filepath.Join(tmpDir, "scripts")
 | 
				
			||||||
 | 
									os.MkdirAll(pkgDir, 0o755)
 | 
				
			||||||
 | 
									os.MkdirAll(scriptDir, 0o755)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									srcBinary := filepath.Join(tmpDir, "test-binary")
 | 
				
			||||||
 | 
									os.WriteFile(srcBinary, []byte("#!/bin/bash\necho test"), 0o755)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									defaultProfile := filepath.Join(scriptDir, "default.profile")
 | 
				
			||||||
 | 
									os.WriteFile(defaultProfile, []byte("include /etc/firejail/default.profile"), 0o644)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									pkg := &alrsh.Package{Name: "test-pkg", FireJailProfiles: alrsh.OverridableFromMap(map[string]map[string]string{"": {"default": "default.profile"}})}
 | 
				
			||||||
 | 
									alrsh.ResolvePackage(pkg, []string{""})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									content := &files.Content{Source: srcBinary, Destination: ".", Type: "file"}
 | 
				
			||||||
 | 
									dirs := types.Directories{PkgDir: pkgDir, ScriptDir: scriptDir}
 | 
				
			||||||
 | 
									return pkg, content, dirs
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								true,
 | 
				
			||||||
 | 
								"",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, tt := range tests {
 | 
				
			||||||
 | 
							t.Run(tt.name, func(t *testing.T) {
 | 
				
			||||||
 | 
								tmpDir := t.TempDir()
 | 
				
			||||||
 | 
								pkg, content, dirs := tt.setupFunc(tmpDir)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								err := createFirejailedDirectory(dirs.PkgDir)
 | 
				
			||||||
 | 
								assert.NoError(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								result, err := createFirejailedBinary(pkg, content, dirs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if tt.expectError {
 | 
				
			||||||
 | 
									assert.Error(t, err)
 | 
				
			||||||
 | 
									if tt.errorMsg != "" {
 | 
				
			||||||
 | 
										assert.Contains(t, err.Error(), tt.errorMsg)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									assert.Nil(t, result)
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									assert.NoError(t, err)
 | 
				
			||||||
 | 
									assert.Len(t, result, 2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									binContent := result[0]
 | 
				
			||||||
 | 
									assert.Contains(t, binContent.Destination, "usr/lib/alr/firejailed/")
 | 
				
			||||||
 | 
									assert.FileExists(t, binContent.Source)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									profileContent := result[1]
 | 
				
			||||||
 | 
									assert.Contains(t, profileContent.Destination, "usr/lib/alr/firejailed/")
 | 
				
			||||||
 | 
									assert.Contains(t, profileContent.Destination, ".profile")
 | 
				
			||||||
 | 
									assert.FileExists(t, profileContent.Source)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									assert.FileExists(t, content.Source)
 | 
				
			||||||
 | 
									wrapperBytes, err := os.ReadFile(content.Source)
 | 
				
			||||||
 | 
									assert.NoError(t, err)
 | 
				
			||||||
 | 
									wrapper := string(wrapperBytes)
 | 
				
			||||||
 | 
									assert.Contains(t, wrapper, "#!/bin/bash")
 | 
				
			||||||
 | 
									assert.Contains(t, wrapper, "firejail --profile=")
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										60
									
								
								internal/build/installer.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								internal/build/installer.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,60 @@
 | 
				
			|||||||
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package build
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/manager"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewInstaller(mgr manager.Manager) *Installer {
 | 
				
			||||||
 | 
						return &Installer{
 | 
				
			||||||
 | 
							mgr: mgr,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Installer struct{ mgr manager.Manager }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (i *Installer) InstallLocal(ctx context.Context, paths []string, opts *manager.Opts) error {
 | 
				
			||||||
 | 
						return i.mgr.InstallLocal(opts, paths...)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (i *Installer) Install(ctx context.Context, pkgs []string, opts *manager.Opts) error {
 | 
				
			||||||
 | 
						return i.mgr.Install(opts, pkgs...)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (i *Installer) Remove(ctx context.Context, pkgs []string, opts *manager.Opts) error {
 | 
				
			||||||
 | 
						return i.mgr.Remove(opts, pkgs...)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (i *Installer) RemoveAlreadyInstalled(ctx context.Context, pkgs []string) ([]string, error) {
 | 
				
			||||||
 | 
						filteredPackages := []string{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, dep := range pkgs {
 | 
				
			||||||
 | 
							installed, err := i.mgr.IsInstalled(dep)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if installed {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							filteredPackages = append(filteredPackages, dep)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return filteredPackages, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										52
									
								
								internal/build/main_build.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								internal/build/main_build.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,52 @@
 | 
				
			|||||||
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package build
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/manager"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewMainBuilder(
 | 
				
			||||||
 | 
						cfg Config,
 | 
				
			||||||
 | 
						mgr manager.Manager,
 | 
				
			||||||
 | 
						repos PackageFinder,
 | 
				
			||||||
 | 
						scriptExecutor ScriptExecutor,
 | 
				
			||||||
 | 
						installerExecutor InstallerExecutor,
 | 
				
			||||||
 | 
					) (*Builder, error) {
 | 
				
			||||||
 | 
						builder := &Builder{
 | 
				
			||||||
 | 
							scriptExecutor: scriptExecutor,
 | 
				
			||||||
 | 
							cacheExecutor: &Cache{
 | 
				
			||||||
 | 
								cfg,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							scriptResolver: &ScriptResolver{
 | 
				
			||||||
 | 
								cfg,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							scriptViewerExecutor: &ScriptViewer{
 | 
				
			||||||
 | 
								config: cfg,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							checkerExecutor: &Checker{
 | 
				
			||||||
 | 
								mgr,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							installerExecutor: installerExecutor,
 | 
				
			||||||
 | 
							sourceExecutor: &SourceDownloader{
 | 
				
			||||||
 | 
								cfg,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							repos: repos,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return builder, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										144
									
								
								internal/build/plugins.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								internal/build/plugins.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,144 @@
 | 
				
			|||||||
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package build
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"log/slog"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"os/exec"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/hashicorp/go-hclog"
 | 
				
			||||||
 | 
						"github.com/hashicorp/go-plugin"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/logger"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var pluginMap = map[string]plugin.Plugin{
 | 
				
			||||||
 | 
						"script-executor": &ScriptExecutorPlugin{},
 | 
				
			||||||
 | 
						"installer":       &InstallerExecutorPlugin{},
 | 
				
			||||||
 | 
						"repos":           &ReposExecutorPlugin{},
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var HandshakeConfig = plugin.HandshakeConfig{
 | 
				
			||||||
 | 
						ProtocolVersion:  1,
 | 
				
			||||||
 | 
						MagicCookieKey:   "ALR_PLUGIN",
 | 
				
			||||||
 | 
						MagicCookieValue: "-",
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func setCommonCmdEnv(cmd *exec.Cmd) {
 | 
				
			||||||
 | 
						cmd.Env = []string{
 | 
				
			||||||
 | 
							"HOME=/var/cache/alr",
 | 
				
			||||||
 | 
							"LOGNAME=alr",
 | 
				
			||||||
 | 
							"USER=alr",
 | 
				
			||||||
 | 
							"PATH=/usr/bin:/bin:/usr/local/bin",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for _, env := range os.Environ() {
 | 
				
			||||||
 | 
							if strings.HasPrefix(env, "LANG=") ||
 | 
				
			||||||
 | 
								strings.HasPrefix(env, "LANGUAGE=") ||
 | 
				
			||||||
 | 
								strings.HasPrefix(env, "LC_") ||
 | 
				
			||||||
 | 
								strings.HasPrefix(env, "ALR_LOG_LEVEL=") {
 | 
				
			||||||
 | 
								cmd.Env = append(cmd.Env, env)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func GetPluginServeCommonConfig() *plugin.ServeConfig {
 | 
				
			||||||
 | 
						return &plugin.ServeConfig{
 | 
				
			||||||
 | 
							HandshakeConfig: HandshakeConfig,
 | 
				
			||||||
 | 
							Logger: hclog.New(&hclog.LoggerOptions{
 | 
				
			||||||
 | 
								Name:        "plugin",
 | 
				
			||||||
 | 
								Output:      os.Stderr,
 | 
				
			||||||
 | 
								Level:       hclog.Trace,
 | 
				
			||||||
 | 
								JSONFormat:  true,
 | 
				
			||||||
 | 
								DisableTime: true,
 | 
				
			||||||
 | 
							}),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func GetSafeInstaller() (InstallerExecutor, func(), error) {
 | 
				
			||||||
 | 
						return getSafeExecutor[InstallerExecutor]("_internal-installer", "installer")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func GetSafeScriptExecutor() (ScriptExecutor, func(), error) {
 | 
				
			||||||
 | 
						return getSafeExecutor[ScriptExecutor]("_internal-safe-script-executor", "script-executor")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func GetSafeReposExecutor() (ReposExecutor, func(), error) {
 | 
				
			||||||
 | 
						return getSafeExecutor[ReposExecutor]("_internal-repos", "repos")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getSafeExecutor[T any](subCommand, pluginName string) (T, func(), error) {
 | 
				
			||||||
 | 
						var err error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						executable, err := os.Executable()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							var zero T
 | 
				
			||||||
 | 
							return zero, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cmd := exec.Command(executable, subCommand)
 | 
				
			||||||
 | 
						setCommonCmdEnv(cmd)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						client := plugin.NewClient(&plugin.ClientConfig{
 | 
				
			||||||
 | 
							HandshakeConfig: HandshakeConfig,
 | 
				
			||||||
 | 
							Plugins:         pluginMap,
 | 
				
			||||||
 | 
							Cmd:             cmd,
 | 
				
			||||||
 | 
							Logger:          logger.GetHCLoggerAdapter(),
 | 
				
			||||||
 | 
							SkipHostEnv:     true,
 | 
				
			||||||
 | 
							UnixSocketConfig: &plugin.UnixSocketConfig{
 | 
				
			||||||
 | 
								Group: "alr",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							SyncStderr: os.Stderr,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						rpcClient, err := client.Client()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							var zero T
 | 
				
			||||||
 | 
							return zero, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var cleanupOnce sync.Once
 | 
				
			||||||
 | 
						cleanup := func() {
 | 
				
			||||||
 | 
							cleanupOnce.Do(func() {
 | 
				
			||||||
 | 
								client.Kill()
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						defer func() {
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								slog.Debug("close executor")
 | 
				
			||||||
 | 
								cleanup()
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						raw, err := rpcClient.Dispense(pluginName)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							var zero T
 | 
				
			||||||
 | 
							return zero, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						executor, ok := raw.(T)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							var zero T
 | 
				
			||||||
 | 
							err = fmt.Errorf("dispensed object is not a %T (got %T)", zero, raw)
 | 
				
			||||||
 | 
							return zero, nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return executor, cleanup, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										60
									
								
								internal/build/plugins_executors.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								internal/build/plugins_executors.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,60 @@
 | 
				
			|||||||
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package build
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/manager"
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/pkg/alrsh"
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/pkg/types"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//go:generate go run ../../generators/plugin-generator InstallerExecutor ScriptExecutor ReposExecutor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// The Executors interfaces must use context.Context as the first parameter,
 | 
				
			||||||
 | 
					// because the plugin-generator cannot generate code without it.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type InstallerExecutor interface {
 | 
				
			||||||
 | 
						InstallLocal(ctx context.Context, paths []string, opts *manager.Opts) error
 | 
				
			||||||
 | 
						Install(ctx context.Context, pkgs []string, opts *manager.Opts) error
 | 
				
			||||||
 | 
						Remove(ctx context.Context, pkgs []string, opts *manager.Opts) error
 | 
				
			||||||
 | 
						RemoveAlreadyInstalled(ctx context.Context, pkgs []string) ([]string, error)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ScriptExecutor interface {
 | 
				
			||||||
 | 
						ReadScript(ctx context.Context, scriptPath string) (*alrsh.ScriptFile, error)
 | 
				
			||||||
 | 
						ExecuteFirstPass(ctx context.Context, input *BuildInput, sf *alrsh.ScriptFile) (string, []*alrsh.Package, error)
 | 
				
			||||||
 | 
						PrepareDirs(
 | 
				
			||||||
 | 
							ctx context.Context,
 | 
				
			||||||
 | 
							input *BuildInput,
 | 
				
			||||||
 | 
							basePkg string,
 | 
				
			||||||
 | 
						) error
 | 
				
			||||||
 | 
						ExecuteSecondPass(
 | 
				
			||||||
 | 
							ctx context.Context,
 | 
				
			||||||
 | 
							input *BuildInput,
 | 
				
			||||||
 | 
							sf *alrsh.ScriptFile,
 | 
				
			||||||
 | 
							varsOfPackages []*alrsh.Package,
 | 
				
			||||||
 | 
							repoDeps []string,
 | 
				
			||||||
 | 
							builtDeps []*BuiltDep,
 | 
				
			||||||
 | 
							basePkg string,
 | 
				
			||||||
 | 
						) ([]*BuiltDep, error)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ReposExecutor interface {
 | 
				
			||||||
 | 
						PullOneAndUpdateFromConfig(ctx context.Context, repo *types.Repo) (types.Repo, error)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										369
									
								
								internal/build/plugins_executors_gen.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										369
									
								
								internal/build/plugins_executors_gen.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,369 @@
 | 
				
			|||||||
 | 
					// DO NOT EDIT MANUALLY. This file is generated.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package build
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"net/rpc"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/manager"
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/pkg/alrsh"
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/pkg/types"
 | 
				
			||||||
 | 
						"github.com/hashicorp/go-plugin"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type InstallerExecutorPlugin struct {
 | 
				
			||||||
 | 
						Impl InstallerExecutor
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type InstallerExecutorRPCServer struct {
 | 
				
			||||||
 | 
						Impl InstallerExecutor
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type InstallerExecutorRPC struct {
 | 
				
			||||||
 | 
						client *rpc.Client
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *InstallerExecutorPlugin) Client(b *plugin.MuxBroker, c *rpc.Client) (interface{}, error) {
 | 
				
			||||||
 | 
						return &InstallerExecutorRPC{client: c}, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *InstallerExecutorPlugin) Server(*plugin.MuxBroker) (interface{}, error) {
 | 
				
			||||||
 | 
						return &InstallerExecutorRPCServer{Impl: p.Impl}, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ScriptExecutorPlugin struct {
 | 
				
			||||||
 | 
						Impl ScriptExecutor
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ScriptExecutorRPCServer struct {
 | 
				
			||||||
 | 
						Impl ScriptExecutor
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ScriptExecutorRPC struct {
 | 
				
			||||||
 | 
						client *rpc.Client
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *ScriptExecutorPlugin) Client(b *plugin.MuxBroker, c *rpc.Client) (interface{}, error) {
 | 
				
			||||||
 | 
						return &ScriptExecutorRPC{client: c}, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *ScriptExecutorPlugin) Server(*plugin.MuxBroker) (interface{}, error) {
 | 
				
			||||||
 | 
						return &ScriptExecutorRPCServer{Impl: p.Impl}, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ReposExecutorPlugin struct {
 | 
				
			||||||
 | 
						Impl ReposExecutor
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ReposExecutorRPCServer struct {
 | 
				
			||||||
 | 
						Impl ReposExecutor
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ReposExecutorRPC struct {
 | 
				
			||||||
 | 
						client *rpc.Client
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *ReposExecutorPlugin) Client(b *plugin.MuxBroker, c *rpc.Client) (interface{}, error) {
 | 
				
			||||||
 | 
						return &ReposExecutorRPC{client: c}, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *ReposExecutorPlugin) Server(*plugin.MuxBroker) (interface{}, error) {
 | 
				
			||||||
 | 
						return &ReposExecutorRPCServer{Impl: p.Impl}, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type InstallerExecutorInstallLocalArgs struct {
 | 
				
			||||||
 | 
						Paths []string
 | 
				
			||||||
 | 
						Opts  *manager.Opts
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type InstallerExecutorInstallLocalResp struct {
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *InstallerExecutorRPC) InstallLocal(ctx context.Context, paths []string, opts *manager.Opts) error {
 | 
				
			||||||
 | 
						var resp *InstallerExecutorInstallLocalResp
 | 
				
			||||||
 | 
						err := s.client.Call("Plugin.InstallLocal", &InstallerExecutorInstallLocalArgs{
 | 
				
			||||||
 | 
							Paths: paths,
 | 
				
			||||||
 | 
							Opts:  opts,
 | 
				
			||||||
 | 
						}, &resp)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *InstallerExecutorRPCServer) InstallLocal(args *InstallerExecutorInstallLocalArgs, resp *InstallerExecutorInstallLocalResp) error {
 | 
				
			||||||
 | 
						err := s.Impl.InstallLocal(context.Background(), args.Paths, args.Opts)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						*resp = InstallerExecutorInstallLocalResp{}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type InstallerExecutorInstallArgs struct {
 | 
				
			||||||
 | 
						Pkgs []string
 | 
				
			||||||
 | 
						Opts *manager.Opts
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type InstallerExecutorInstallResp struct {
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *InstallerExecutorRPC) Install(ctx context.Context, pkgs []string, opts *manager.Opts) error {
 | 
				
			||||||
 | 
						var resp *InstallerExecutorInstallResp
 | 
				
			||||||
 | 
						err := s.client.Call("Plugin.Install", &InstallerExecutorInstallArgs{
 | 
				
			||||||
 | 
							Pkgs: pkgs,
 | 
				
			||||||
 | 
							Opts: opts,
 | 
				
			||||||
 | 
						}, &resp)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *InstallerExecutorRPCServer) Install(args *InstallerExecutorInstallArgs, resp *InstallerExecutorInstallResp) error {
 | 
				
			||||||
 | 
						err := s.Impl.Install(context.Background(), args.Pkgs, args.Opts)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						*resp = InstallerExecutorInstallResp{}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type InstallerExecutorRemoveArgs struct {
 | 
				
			||||||
 | 
						Pkgs []string
 | 
				
			||||||
 | 
						Opts *manager.Opts
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type InstallerExecutorRemoveResp struct {
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *InstallerExecutorRPC) Remove(ctx context.Context, pkgs []string, opts *manager.Opts) error {
 | 
				
			||||||
 | 
						var resp *InstallerExecutorRemoveResp
 | 
				
			||||||
 | 
						err := s.client.Call("Plugin.Remove", &InstallerExecutorRemoveArgs{
 | 
				
			||||||
 | 
							Pkgs: pkgs,
 | 
				
			||||||
 | 
							Opts: opts,
 | 
				
			||||||
 | 
						}, &resp)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *InstallerExecutorRPCServer) Remove(args *InstallerExecutorRemoveArgs, resp *InstallerExecutorRemoveResp) error {
 | 
				
			||||||
 | 
						err := s.Impl.Remove(context.Background(), args.Pkgs, args.Opts)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						*resp = InstallerExecutorRemoveResp{}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type InstallerExecutorRemoveAlreadyInstalledArgs struct {
 | 
				
			||||||
 | 
						Pkgs []string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type InstallerExecutorRemoveAlreadyInstalledResp struct {
 | 
				
			||||||
 | 
						Result0 []string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *InstallerExecutorRPC) RemoveAlreadyInstalled(ctx context.Context, pkgs []string) ([]string, error) {
 | 
				
			||||||
 | 
						var resp *InstallerExecutorRemoveAlreadyInstalledResp
 | 
				
			||||||
 | 
						err := s.client.Call("Plugin.RemoveAlreadyInstalled", &InstallerExecutorRemoveAlreadyInstalledArgs{
 | 
				
			||||||
 | 
							Pkgs: pkgs,
 | 
				
			||||||
 | 
						}, &resp)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return resp.Result0, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *InstallerExecutorRPCServer) RemoveAlreadyInstalled(args *InstallerExecutorRemoveAlreadyInstalledArgs, resp *InstallerExecutorRemoveAlreadyInstalledResp) error {
 | 
				
			||||||
 | 
						result0, err := s.Impl.RemoveAlreadyInstalled(context.Background(), args.Pkgs)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						*resp = InstallerExecutorRemoveAlreadyInstalledResp{
 | 
				
			||||||
 | 
							Result0: result0,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ScriptExecutorReadScriptArgs struct {
 | 
				
			||||||
 | 
						ScriptPath string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ScriptExecutorReadScriptResp struct {
 | 
				
			||||||
 | 
						Result0 *alrsh.ScriptFile
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *ScriptExecutorRPC) ReadScript(ctx context.Context, scriptPath string) (*alrsh.ScriptFile, error) {
 | 
				
			||||||
 | 
						var resp *ScriptExecutorReadScriptResp
 | 
				
			||||||
 | 
						err := s.client.Call("Plugin.ReadScript", &ScriptExecutorReadScriptArgs{
 | 
				
			||||||
 | 
							ScriptPath: scriptPath,
 | 
				
			||||||
 | 
						}, &resp)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return resp.Result0, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *ScriptExecutorRPCServer) ReadScript(args *ScriptExecutorReadScriptArgs, resp *ScriptExecutorReadScriptResp) error {
 | 
				
			||||||
 | 
						result0, err := s.Impl.ReadScript(context.Background(), args.ScriptPath)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						*resp = ScriptExecutorReadScriptResp{
 | 
				
			||||||
 | 
							Result0: result0,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ScriptExecutorExecuteFirstPassArgs struct {
 | 
				
			||||||
 | 
						Input *BuildInput
 | 
				
			||||||
 | 
						Sf    *alrsh.ScriptFile
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ScriptExecutorExecuteFirstPassResp struct {
 | 
				
			||||||
 | 
						Result0 string
 | 
				
			||||||
 | 
						Result1 []*alrsh.Package
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *ScriptExecutorRPC) ExecuteFirstPass(ctx context.Context, input *BuildInput, sf *alrsh.ScriptFile) (string, []*alrsh.Package, error) {
 | 
				
			||||||
 | 
						var resp *ScriptExecutorExecuteFirstPassResp
 | 
				
			||||||
 | 
						err := s.client.Call("Plugin.ExecuteFirstPass", &ScriptExecutorExecuteFirstPassArgs{
 | 
				
			||||||
 | 
							Input: input,
 | 
				
			||||||
 | 
							Sf:    sf,
 | 
				
			||||||
 | 
						}, &resp)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return resp.Result0, resp.Result1, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *ScriptExecutorRPCServer) ExecuteFirstPass(args *ScriptExecutorExecuteFirstPassArgs, resp *ScriptExecutorExecuteFirstPassResp) error {
 | 
				
			||||||
 | 
						result0, result1, err := s.Impl.ExecuteFirstPass(context.Background(), args.Input, args.Sf)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						*resp = ScriptExecutorExecuteFirstPassResp{
 | 
				
			||||||
 | 
							Result0: result0,
 | 
				
			||||||
 | 
							Result1: result1,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ScriptExecutorPrepareDirsArgs struct {
 | 
				
			||||||
 | 
						Input   *BuildInput
 | 
				
			||||||
 | 
						BasePkg string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ScriptExecutorPrepareDirsResp struct {
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *ScriptExecutorRPC) PrepareDirs(ctx context.Context, input *BuildInput, basePkg string) error {
 | 
				
			||||||
 | 
						var resp *ScriptExecutorPrepareDirsResp
 | 
				
			||||||
 | 
						err := s.client.Call("Plugin.PrepareDirs", &ScriptExecutorPrepareDirsArgs{
 | 
				
			||||||
 | 
							Input:   input,
 | 
				
			||||||
 | 
							BasePkg: basePkg,
 | 
				
			||||||
 | 
						}, &resp)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *ScriptExecutorRPCServer) PrepareDirs(args *ScriptExecutorPrepareDirsArgs, resp *ScriptExecutorPrepareDirsResp) error {
 | 
				
			||||||
 | 
						err := s.Impl.PrepareDirs(context.Background(), args.Input, args.BasePkg)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						*resp = ScriptExecutorPrepareDirsResp{}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ScriptExecutorExecuteSecondPassArgs struct {
 | 
				
			||||||
 | 
						Input          *BuildInput
 | 
				
			||||||
 | 
						Sf             *alrsh.ScriptFile
 | 
				
			||||||
 | 
						VarsOfPackages []*alrsh.Package
 | 
				
			||||||
 | 
						RepoDeps       []string
 | 
				
			||||||
 | 
						BuiltDeps      []*BuiltDep
 | 
				
			||||||
 | 
						BasePkg        string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ScriptExecutorExecuteSecondPassResp struct {
 | 
				
			||||||
 | 
						Result0 []*BuiltDep
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *ScriptExecutorRPC) ExecuteSecondPass(ctx context.Context, input *BuildInput, sf *alrsh.ScriptFile, varsOfPackages []*alrsh.Package, repoDeps []string, builtDeps []*BuiltDep, basePkg string) ([]*BuiltDep, error) {
 | 
				
			||||||
 | 
						var resp *ScriptExecutorExecuteSecondPassResp
 | 
				
			||||||
 | 
						err := s.client.Call("Plugin.ExecuteSecondPass", &ScriptExecutorExecuteSecondPassArgs{
 | 
				
			||||||
 | 
							Input:          input,
 | 
				
			||||||
 | 
							Sf:             sf,
 | 
				
			||||||
 | 
							VarsOfPackages: varsOfPackages,
 | 
				
			||||||
 | 
							RepoDeps:       repoDeps,
 | 
				
			||||||
 | 
							BuiltDeps:      builtDeps,
 | 
				
			||||||
 | 
							BasePkg:        basePkg,
 | 
				
			||||||
 | 
						}, &resp)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return resp.Result0, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *ScriptExecutorRPCServer) ExecuteSecondPass(args *ScriptExecutorExecuteSecondPassArgs, resp *ScriptExecutorExecuteSecondPassResp) error {
 | 
				
			||||||
 | 
						result0, err := s.Impl.ExecuteSecondPass(context.Background(), args.Input, args.Sf, args.VarsOfPackages, args.RepoDeps, args.BuiltDeps, args.BasePkg)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						*resp = ScriptExecutorExecuteSecondPassResp{
 | 
				
			||||||
 | 
							Result0: result0,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ReposExecutorPullOneAndUpdateFromConfigArgs struct {
 | 
				
			||||||
 | 
						Repo *types.Repo
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ReposExecutorPullOneAndUpdateFromConfigResp struct {
 | 
				
			||||||
 | 
						Result0 types.Repo
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *ReposExecutorRPC) PullOneAndUpdateFromConfig(ctx context.Context, repo *types.Repo) (types.Repo, error) {
 | 
				
			||||||
 | 
						var resp *ReposExecutorPullOneAndUpdateFromConfigResp
 | 
				
			||||||
 | 
						err := s.client.Call("Plugin.PullOneAndUpdateFromConfig", &ReposExecutorPullOneAndUpdateFromConfigArgs{
 | 
				
			||||||
 | 
							Repo: repo,
 | 
				
			||||||
 | 
						}, &resp)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return types.Repo{}, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return resp.Result0, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *ReposExecutorRPCServer) PullOneAndUpdateFromConfig(args *ReposExecutorPullOneAndUpdateFromConfigArgs, resp *ReposExecutorPullOneAndUpdateFromConfigResp) error {
 | 
				
			||||||
 | 
						result0, err := s.Impl.PullOneAndUpdateFromConfig(context.Background(), args.Repo)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						*resp = ReposExecutorPullOneAndUpdateFromConfigResp{
 | 
				
			||||||
 | 
							Result0: result0,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										37
									
								
								internal/build/repos_executor.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								internal/build/repos_executor.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
				
			|||||||
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package build
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/repos"
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/pkg/types"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type reposExecutor struct{ r *repos.Repos }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewRepos(r *repos.Repos) ReposExecutor {
 | 
				
			||||||
 | 
						return &reposExecutor{r}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *reposExecutor) PullOneAndUpdateFromConfig(ctx context.Context, repo *types.Repo) (types.Repo, error) {
 | 
				
			||||||
 | 
						if err := r.r.PullOneAndUpdateFromConfig(ctx, repo); err != nil {
 | 
				
			||||||
 | 
							return *repo, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return *repo, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										364
									
								
								internal/build/script_executor.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										364
									
								
								internal/build/script_executor.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,364 @@
 | 
				
			|||||||
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package build
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"log/slog"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"path/filepath"
 | 
				
			||||||
 | 
						"slices"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/google/shlex"
 | 
				
			||||||
 | 
						"github.com/goreleaser/nfpm/v2"
 | 
				
			||||||
 | 
						"github.com/leonelquinteros/gotext"
 | 
				
			||||||
 | 
						"mvdan.cc/sh/v3/expand"
 | 
				
			||||||
 | 
						"mvdan.cc/sh/v3/interp"
 | 
				
			||||||
 | 
						"mvdan.cc/sh/v3/syntax"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						finddeps "gitea.plemya-x.ru/Plemya-x/ALR/internal/build/find_deps"
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/shutils/decoder"
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/shutils/handlers"
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/shutils/helpers"
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/pkg/alrsh"
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/pkg/types"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type LocalScriptExecutor struct {
 | 
				
			||||||
 | 
						cfg Config
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewLocalScriptExecutor(cfg Config) *LocalScriptExecutor {
 | 
				
			||||||
 | 
						return &LocalScriptExecutor{
 | 
				
			||||||
 | 
							cfg,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *LocalScriptExecutor) ReadScript(ctx context.Context, scriptPath string) (*alrsh.ScriptFile, error) {
 | 
				
			||||||
 | 
						return alrsh.ReadFromLocal(scriptPath)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *LocalScriptExecutor) ExecuteFirstPass(ctx context.Context, input *BuildInput, sf *alrsh.ScriptFile) (string, []*alrsh.Package, error) {
 | 
				
			||||||
 | 
						return sf.ParseBuildVars(ctx, input.info, input.packages)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *LocalScriptExecutor) PrepareDirs(
 | 
				
			||||||
 | 
						ctx context.Context,
 | 
				
			||||||
 | 
						input *BuildInput,
 | 
				
			||||||
 | 
						basePkg string,
 | 
				
			||||||
 | 
					) error {
 | 
				
			||||||
 | 
						dirs, err := getDirs(
 | 
				
			||||||
 | 
							e.cfg,
 | 
				
			||||||
 | 
							input.script,
 | 
				
			||||||
 | 
							basePkg,
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = prepareDirs(dirs)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *LocalScriptExecutor) ExecuteSecondPass(
 | 
				
			||||||
 | 
						ctx context.Context,
 | 
				
			||||||
 | 
						input *BuildInput,
 | 
				
			||||||
 | 
						sf *alrsh.ScriptFile,
 | 
				
			||||||
 | 
						varsOfPackages []*alrsh.Package,
 | 
				
			||||||
 | 
						repoDeps []string,
 | 
				
			||||||
 | 
						builtDeps []*BuiltDep,
 | 
				
			||||||
 | 
						basePkg string,
 | 
				
			||||||
 | 
					) ([]*BuiltDep, error) {
 | 
				
			||||||
 | 
						dirs, err := getDirs(e.cfg, sf.Path(), basePkg)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						env := createBuildEnvVars(input.info, dirs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fakeroot := handlers.FakerootExecHandler(2 * time.Second)
 | 
				
			||||||
 | 
						runner, err := interp.New(
 | 
				
			||||||
 | 
							interp.Env(expand.ListEnviron(env...)),       // Устанавливаем окружение
 | 
				
			||||||
 | 
							interp.StdIO(os.Stdin, os.Stderr, os.Stderr), // Устанавливаем стандартный ввод-вывод
 | 
				
			||||||
 | 
							interp.ExecHandlers(func(next interp.ExecHandlerFunc) interp.ExecHandlerFunc {
 | 
				
			||||||
 | 
								return helpers.Helpers.ExecHandler(fakeroot)
 | 
				
			||||||
 | 
							}), // Обрабатываем выполнение через fakeroot
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = runner.Run(ctx, sf.File())
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dec := decoder.New(input.info, runner)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// var builtPaths []string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = e.ExecuteFunctions(ctx, dirs, dec)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, vars := range varsOfPackages {
 | 
				
			||||||
 | 
							packageName := ""
 | 
				
			||||||
 | 
							if vars.BasePkgName != "" {
 | 
				
			||||||
 | 
								packageName = vars.Name
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							pkgFormat := input.pkgFormat
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							funcOut, err := e.ExecutePackageFunctions(
 | 
				
			||||||
 | 
								ctx,
 | 
				
			||||||
 | 
								dec,
 | 
				
			||||||
 | 
								dirs,
 | 
				
			||||||
 | 
								packageName,
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							slog.Info(gotext.Get("Building package metadata"), "name", basePkg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							pkgInfo, err := buildPkgMetadata(
 | 
				
			||||||
 | 
								ctx,
 | 
				
			||||||
 | 
								input,
 | 
				
			||||||
 | 
								vars,
 | 
				
			||||||
 | 
								dirs,
 | 
				
			||||||
 | 
								append(
 | 
				
			||||||
 | 
									repoDeps,
 | 
				
			||||||
 | 
									GetBuiltName(builtDeps)...,
 | 
				
			||||||
 | 
								),
 | 
				
			||||||
 | 
								funcOut.Contents,
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							packager, err := nfpm.Get(pkgFormat) // Получаем упаковщик для формата пакета
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							pkgName := packager.ConventionalFileName(pkgInfo) // Получаем имя файла пакета
 | 
				
			||||||
 | 
							pkgPath := filepath.Join(dirs.BaseDir, pkgName)   // Определяем путь к пакету
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							pkgFile, err := os.Create(pkgPath)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							err = packager.Package(pkgInfo, pkgFile)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							builtDeps = append(builtDeps, &BuiltDep{
 | 
				
			||||||
 | 
								Name: vars.Name,
 | 
				
			||||||
 | 
								Path: pkgPath,
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return builtDeps, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func buildPkgMetadata(
 | 
				
			||||||
 | 
						ctx context.Context,
 | 
				
			||||||
 | 
						input interface {
 | 
				
			||||||
 | 
							OsInfoProvider
 | 
				
			||||||
 | 
							BuildOptsProvider
 | 
				
			||||||
 | 
							PkgFormatProvider
 | 
				
			||||||
 | 
							RepositoryProvider
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						vars *alrsh.Package,
 | 
				
			||||||
 | 
						dirs types.Directories,
 | 
				
			||||||
 | 
						deps []string,
 | 
				
			||||||
 | 
						preferedContents *[]string,
 | 
				
			||||||
 | 
					) (*nfpm.Info, error) {
 | 
				
			||||||
 | 
						pkgInfo := getBasePkgInfo(vars, input)
 | 
				
			||||||
 | 
						pkgInfo.Description = vars.Description.Resolved()
 | 
				
			||||||
 | 
						pkgInfo.Platform = "linux"
 | 
				
			||||||
 | 
						pkgInfo.Homepage = vars.Homepage.Resolved()
 | 
				
			||||||
 | 
						pkgInfo.License = strings.Join(vars.Licenses, ", ")
 | 
				
			||||||
 | 
						pkgInfo.Maintainer = vars.Maintainer.Resolved()
 | 
				
			||||||
 | 
						pkgInfo.Overridables = nfpm.Overridables{
 | 
				
			||||||
 | 
							Conflicts: append(vars.Conflicts, vars.Name),
 | 
				
			||||||
 | 
							Replaces:  vars.Replaces,
 | 
				
			||||||
 | 
							Provides:  append(vars.Provides, vars.Name),
 | 
				
			||||||
 | 
							Depends:   deps,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						pkgInfo.Section = vars.Group.Resolved()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pkgFormat := input.PkgFormat()
 | 
				
			||||||
 | 
						info := input.OSRelease()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if pkgFormat == "apk" {
 | 
				
			||||||
 | 
							// Alpine отказывается устанавливать пакеты, которые предоставляют сами себя, поэтому удаляем такие элементы
 | 
				
			||||||
 | 
							pkgInfo.Overridables.Provides = slices.DeleteFunc(pkgInfo.Overridables.Provides, func(s string) bool {
 | 
				
			||||||
 | 
								return s == pkgInfo.Name
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if pkgFormat == "rpm" {
 | 
				
			||||||
 | 
							pkgInfo.RPM.Group = vars.Group.Resolved()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if vars.Summary.Resolved() != "" {
 | 
				
			||||||
 | 
								pkgInfo.RPM.Summary = vars.Summary.Resolved()
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								lines := strings.SplitN(vars.Description.Resolved(), "\n", 2)
 | 
				
			||||||
 | 
								pkgInfo.RPM.Summary = lines[0]
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if vars.Epoch != 0 {
 | 
				
			||||||
 | 
							pkgInfo.Epoch = strconv.FormatUint(uint64(vars.Epoch), 10)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						setScripts(vars, pkgInfo, dirs.ScriptDir)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if slices.Contains(vars.Architectures, "all") {
 | 
				
			||||||
 | 
							pkgInfo.Arch = "all"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						contents, err := buildContents(vars, dirs, preferedContents)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						normalizeContents(contents)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if vars.FireJailed.Resolved() {
 | 
				
			||||||
 | 
							contents, err = applyFirejailIntegration(vars, dirs, contents)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pkgInfo.Overridables.Contents = contents
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(vars.AutoProv.Resolved()) == 1 && decoder.IsTruthy(vars.AutoProv.Resolved()[0]) {
 | 
				
			||||||
 | 
							f := finddeps.New(info, pkgFormat)
 | 
				
			||||||
 | 
							err = f.FindProvides(ctx, pkgInfo, dirs, vars.AutoProvSkipList.Resolved())
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(vars.AutoReq.Resolved()) == 1 && decoder.IsTruthy(vars.AutoReq.Resolved()[0]) {
 | 
				
			||||||
 | 
							f := finddeps.New(info, pkgFormat)
 | 
				
			||||||
 | 
							err = f.FindRequires(ctx, pkgInfo, dirs, vars.AutoReqSkipList.Resolved())
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return pkgInfo, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *LocalScriptExecutor) ExecuteFunctions(ctx context.Context, dirs types.Directories, dec *decoder.Decoder) error {
 | 
				
			||||||
 | 
						prepare, ok := dec.GetFunc("prepare")
 | 
				
			||||||
 | 
						if ok {
 | 
				
			||||||
 | 
							slog.Info(gotext.Get("Executing prepare()"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							err := prepare(ctx, interp.Dir(dirs.SrcDir))
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						build, ok := dec.GetFunc("build")
 | 
				
			||||||
 | 
						if ok {
 | 
				
			||||||
 | 
							slog.Info(gotext.Get("Executing build()"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							err := build(ctx, interp.Dir(dirs.SrcDir))
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *LocalScriptExecutor) ExecutePackageFunctions(
 | 
				
			||||||
 | 
						ctx context.Context,
 | 
				
			||||||
 | 
						dec *decoder.Decoder,
 | 
				
			||||||
 | 
						dirs types.Directories,
 | 
				
			||||||
 | 
						packageName string,
 | 
				
			||||||
 | 
					) (*FunctionsOutput, error) {
 | 
				
			||||||
 | 
						output := &FunctionsOutput{}
 | 
				
			||||||
 | 
						var packageFuncName string
 | 
				
			||||||
 | 
						var filesFuncName string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if packageName == "" {
 | 
				
			||||||
 | 
							packageFuncName = "package"
 | 
				
			||||||
 | 
							filesFuncName = "files"
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							packageFuncName = fmt.Sprintf("package_%s", packageName)
 | 
				
			||||||
 | 
							filesFuncName = fmt.Sprintf("files_%s", packageName)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						packageFn, ok := dec.GetFunc(packageFuncName)
 | 
				
			||||||
 | 
						if ok {
 | 
				
			||||||
 | 
							slog.Info(gotext.Get("Executing %s()", packageFuncName))
 | 
				
			||||||
 | 
							err := packageFn(ctx, interp.Dir(dirs.SrcDir))
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						files, ok := dec.GetFuncP(filesFuncName, func(ctx context.Context, s *interp.Runner) error {
 | 
				
			||||||
 | 
							// It should be done via interp.RunnerOption,
 | 
				
			||||||
 | 
							// but due to the issues below, it cannot be done.
 | 
				
			||||||
 | 
							// - https://github.com/mvdan/sh/issues/962
 | 
				
			||||||
 | 
							// - https://github.com/mvdan/sh/issues/1125
 | 
				
			||||||
 | 
							script, err := syntax.NewParser().Parse(strings.NewReader("cd $pkgdir && shopt -s globstar"), "")
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return s.Run(ctx, script)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ok {
 | 
				
			||||||
 | 
							slog.Info(gotext.Get("Executing %s()", filesFuncName))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							buf := &bytes.Buffer{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							err := files(
 | 
				
			||||||
 | 
								ctx,
 | 
				
			||||||
 | 
								interp.Dir(dirs.PkgDir),
 | 
				
			||||||
 | 
								interp.StdIO(os.Stdin, buf, os.Stderr),
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							contents, err := shlex.Split(buf.String())
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							output.Contents = &contents
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return output, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										65
									
								
								internal/build/script_resolver.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								internal/build/script_resolver.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,65 @@
 | 
				
			|||||||
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package build
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"path/filepath"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/pkg/alrsh"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ScriptResolver struct {
 | 
				
			||||||
 | 
						cfg Config
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ScriptInfo struct {
 | 
				
			||||||
 | 
						Script     string
 | 
				
			||||||
 | 
						Repository string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *ScriptResolver) ResolveScript(
 | 
				
			||||||
 | 
						ctx context.Context,
 | 
				
			||||||
 | 
						pkg *alrsh.Package,
 | 
				
			||||||
 | 
					) *ScriptInfo {
 | 
				
			||||||
 | 
						var repository, script string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						repodir := s.cfg.GetPaths().RepoDir
 | 
				
			||||||
 | 
						repository = pkg.Repository
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// First, we check if there is a root alr.sh in the repository
 | 
				
			||||||
 | 
						rootScriptPath := filepath.Join(repodir, repository, "alr.sh")
 | 
				
			||||||
 | 
						if _, err := os.Stat(rootScriptPath); err == nil {
 | 
				
			||||||
 | 
							// A repository with a single alr.sh at the root
 | 
				
			||||||
 | 
							script = rootScriptPath
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							// Multi-package repository - we are looking for alr.sh in the subfolder
 | 
				
			||||||
 | 
							var scriptPath string
 | 
				
			||||||
 | 
							if pkg.BasePkgName != "" {
 | 
				
			||||||
 | 
								scriptPath = filepath.Join(repodir, repository, pkg.BasePkgName, "alr.sh")
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								scriptPath = filepath.Join(repodir, repository, pkg.Name, "alr.sh")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							script = scriptPath
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &ScriptInfo{
 | 
				
			||||||
 | 
							Repository: repository,
 | 
				
			||||||
 | 
							Script:     script,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										47
									
								
								internal/build/script_view.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								internal/build/script_view.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,47 @@
 | 
				
			|||||||
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package build
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils"
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/pkg/alrsh"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ScriptViewerConfig interface {
 | 
				
			||||||
 | 
						PagerStyle() string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ScriptViewer struct {
 | 
				
			||||||
 | 
						config ScriptViewerConfig
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *ScriptViewer) ViewScript(
 | 
				
			||||||
 | 
						ctx context.Context,
 | 
				
			||||||
 | 
						input *BuildInput,
 | 
				
			||||||
 | 
						a *alrsh.ScriptFile,
 | 
				
			||||||
 | 
						basePkg string,
 | 
				
			||||||
 | 
					) error {
 | 
				
			||||||
 | 
						return cliutils.PromptViewScript(
 | 
				
			||||||
 | 
							ctx,
 | 
				
			||||||
 | 
							a.Path(),
 | 
				
			||||||
 | 
							basePkg,
 | 
				
			||||||
 | 
							s.config.PagerStyle(),
 | 
				
			||||||
 | 
							input.opts.Interactive,
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										86
									
								
								internal/build/source_downloader.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								internal/build/source_downloader.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,86 @@
 | 
				
			|||||||
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package build
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"encoding/hex"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/pkg/dl"
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/pkg/dlcache"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type SourceDownloader struct {
 | 
				
			||||||
 | 
						cfg Config
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewSourceDownloader(cfg Config) *SourceDownloader {
 | 
				
			||||||
 | 
						return &SourceDownloader{
 | 
				
			||||||
 | 
							cfg,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *SourceDownloader) DownloadSources(
 | 
				
			||||||
 | 
						ctx context.Context,
 | 
				
			||||||
 | 
						input *BuildInput,
 | 
				
			||||||
 | 
						basePkg string,
 | 
				
			||||||
 | 
						si SourcesInput,
 | 
				
			||||||
 | 
					) error {
 | 
				
			||||||
 | 
						for i, src := range si.Sources {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							opts := dl.Options{
 | 
				
			||||||
 | 
								Name:        fmt.Sprintf("[%d]", i),
 | 
				
			||||||
 | 
								URL:         src,
 | 
				
			||||||
 | 
								Destination: getSrcDir(s.cfg, basePkg),
 | 
				
			||||||
 | 
								Progress:    os.Stderr,
 | 
				
			||||||
 | 
								LocalDir:    getScriptDir(input.script),
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if !strings.EqualFold(si.Checksums[i], "SKIP") {
 | 
				
			||||||
 | 
								// Если контрольная сумма содержит двоеточие, используйте часть до двоеточия
 | 
				
			||||||
 | 
								// как алгоритм, а часть после как фактическую контрольную сумму.
 | 
				
			||||||
 | 
								// В противном случае используйте sha256 по умолчанию с целой строкой как контрольной суммой.
 | 
				
			||||||
 | 
								algo, hashData, ok := strings.Cut(si.Checksums[i], ":")
 | 
				
			||||||
 | 
								if ok {
 | 
				
			||||||
 | 
									checksum, err := hex.DecodeString(hashData)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										return err
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									opts.Hash = checksum
 | 
				
			||||||
 | 
									opts.HashAlgorithm = algo
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									checksum, err := hex.DecodeString(si.Checksums[i])
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										return err
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									opts.Hash = checksum
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							opts.DlCache = dlcache.New(s.cfg.GetPaths().CacheDir)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							err := dl.Download(ctx, opts)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
// ALR - Any Linux Repository
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
// Copyright (C) 2025 Евгений Храмов
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// This program is free software: you can redistribute it and/or modify
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
// it under the terms of the GNU General Public License as published by
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
@@ -33,35 +33,18 @@ import (
 | 
				
			|||||||
	_ "github.com/goreleaser/nfpm/v2/arch"
 | 
						_ "github.com/goreleaser/nfpm/v2/arch"
 | 
				
			||||||
	_ "github.com/goreleaser/nfpm/v2/deb"
 | 
						_ "github.com/goreleaser/nfpm/v2/deb"
 | 
				
			||||||
	_ "github.com/goreleaser/nfpm/v2/rpm"
 | 
						_ "github.com/goreleaser/nfpm/v2/rpm"
 | 
				
			||||||
	"mvdan.cc/sh/v3/syntax"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/goreleaser/nfpm/v2"
 | 
						"github.com/goreleaser/nfpm/v2"
 | 
				
			||||||
	"github.com/goreleaser/nfpm/v2/files"
 | 
						"github.com/goreleaser/nfpm/v2/files"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"gitea.plemya-x.ru/Plemya-x/ALR/internal/cpu"
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/cpu"
 | 
				
			||||||
	"gitea.plemya-x.ru/Plemya-x/ALR/internal/db"
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/manager"
 | 
				
			||||||
	"gitea.plemya-x.ru/Plemya-x/ALR/internal/overrides"
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/overrides"
 | 
				
			||||||
	"gitea.plemya-x.ru/Plemya-x/ALR/internal/types"
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/pkg/alrsh"
 | 
				
			||||||
	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro"
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro"
 | 
				
			||||||
	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager"
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/pkg/types"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Функция readScript анализирует скрипт сборки с использованием встроенной реализации bash
 | 
					 | 
				
			||||||
func readScript(script string) (*syntax.File, error) {
 | 
					 | 
				
			||||||
	fl, err := os.Open(script) // Открываем файл скрипта
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	defer fl.Close() // Закрываем файл после выполнения
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	file, err := syntax.NewParser().Parse(fl, "alr.sh") // Парсим скрипт с помощью синтаксического анализатора
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return file, nil // Возвращаем синтаксическое дерево
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Функция prepareDirs подготавливает директории для сборки.
 | 
					// Функция prepareDirs подготавливает директории для сборки.
 | 
				
			||||||
func prepareDirs(dirs types.Directories) error {
 | 
					func prepareDirs(dirs types.Directories) error {
 | 
				
			||||||
	err := os.RemoveAll(dirs.BaseDir) // Удаляем базовую директорию, если она существует
 | 
						err := os.RemoveAll(dirs.BaseDir) // Удаляем базовую директорию, если она существует
 | 
				
			||||||
@@ -77,7 +60,7 @@ func prepareDirs(dirs types.Directories) error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Функция buildContents создает секцию содержимого пакета, которая содержит файлы,
 | 
					// Функция buildContents создает секцию содержимого пакета, которая содержит файлы,
 | 
				
			||||||
// которые будут включены в конечный пакет.
 | 
					// которые будут включены в конечный пакет.
 | 
				
			||||||
func buildContents(vars *types.BuildVars, dirs types.Directories, preferedContents *[]string) ([]*files.Content, error) {
 | 
					func buildContents(vars *alrsh.Package, dirs types.Directories, preferedContents *[]string) ([]*files.Content, error) {
 | 
				
			||||||
	contents := []*files.Content{}
 | 
						contents := []*files.Content{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	processPath := func(path, trimmed string, prefered bool) error {
 | 
						processPath := func(path, trimmed string, prefered bool) error {
 | 
				
			||||||
@@ -140,7 +123,7 @@ func buildContents(vars *types.BuildVars, dirs types.Directories, preferedConten
 | 
				
			|||||||
			},
 | 
								},
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if slices.Contains(vars.Backup, trimmed) {
 | 
							if slices.Contains(vars.Backup.Resolved(), trimmed) {
 | 
				
			||||||
			fileContent.Type = "config|noreplace"
 | 
								fileContent.Type = "config|noreplace"
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -171,21 +154,31 @@ func buildContents(vars *types.BuildVars, dirs types.Directories, preferedConten
 | 
				
			|||||||
	return contents, nil
 | 
						return contents, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func normalizeContents(contents []*files.Content) {
 | 
				
			||||||
 | 
						for _, content := range contents {
 | 
				
			||||||
 | 
							content.Destination = filepath.Join("/", content.Destination)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var RegexpALRPackageName = regexp.MustCompile(`^(?P<package>[^+]+)\+alr-(?P<repo>.+)$`)
 | 
					var RegexpALRPackageName = regexp.MustCompile(`^(?P<package>[^+]+)\+alr-(?P<repo>.+)$`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func getBasePkgInfo(vars *types.BuildVars, info *distro.OSRelease, opts *types.BuildOpts) *nfpm.Info {
 | 
					func getBasePkgInfo(vars *alrsh.Package, input interface {
 | 
				
			||||||
 | 
						RepositoryProvider
 | 
				
			||||||
 | 
						OsInfoProvider
 | 
				
			||||||
 | 
					},
 | 
				
			||||||
 | 
					) *nfpm.Info {
 | 
				
			||||||
	return &nfpm.Info{
 | 
						return &nfpm.Info{
 | 
				
			||||||
		Name:    fmt.Sprintf("%s+alr-%s", vars.Name, opts.Repository),
 | 
							Name:    fmt.Sprintf("%s+alr-%s", vars.Name, input.Repository()),
 | 
				
			||||||
		Arch:    cpu.Arch(),
 | 
							Arch:    cpu.Arch(),
 | 
				
			||||||
		Version: vars.Version,
 | 
							Version: vars.Version,
 | 
				
			||||||
		Release: overrides.ReleasePlatformSpecific(vars.Release, info),
 | 
							Release: overrides.ReleasePlatformSpecific(vars.Release, input.OSRelease()),
 | 
				
			||||||
		Epoch:   strconv.FormatUint(uint64(vars.Epoch), 10),
 | 
							Epoch:   strconv.FormatUint(uint64(vars.Epoch), 10),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Функция getPkgFormat возвращает формат пакета из менеджера пакетов,
 | 
					// Функция getPkgFormat возвращает формат пакета из менеджера пакетов,
 | 
				
			||||||
// или ALR_PKG_FORMAT, если он установлен.
 | 
					// или ALR_PKG_FORMAT, если он установлен.
 | 
				
			||||||
func getPkgFormat(mgr manager.Manager) string {
 | 
					func GetPkgFormat(mgr manager.Manager) string {
 | 
				
			||||||
	pkgFormat := mgr.Format()
 | 
						pkgFormat := mgr.Format()
 | 
				
			||||||
	if format, ok := os.LookupEnv("ALR_PKG_FORMAT"); ok {
 | 
						if format, ok := os.LookupEnv("ALR_PKG_FORMAT"); ok {
 | 
				
			||||||
		pkgFormat = format
 | 
							pkgFormat = format
 | 
				
			||||||
@@ -225,39 +218,39 @@ func createBuildEnvVars(info *distro.OSRelease, dirs types.Directories) []string
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Функция setScripts добавляет скрипты-перехватчики к метаданным пакета.
 | 
					// Функция setScripts добавляет скрипты-перехватчики к метаданным пакета.
 | 
				
			||||||
func setScripts(vars *types.BuildVars, info *nfpm.Info, scriptDir string) {
 | 
					func setScripts(vars *alrsh.Package, info *nfpm.Info, scriptDir string) {
 | 
				
			||||||
	if vars.Scripts.PreInstall != "" {
 | 
						if vars.Scripts.Resolved().PreInstall != "" {
 | 
				
			||||||
		info.Scripts.PreInstall = filepath.Join(scriptDir, vars.Scripts.PreInstall)
 | 
							info.Scripts.PreInstall = filepath.Join(scriptDir, vars.Scripts.Resolved().PreInstall)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if vars.Scripts.PostInstall != "" {
 | 
						if vars.Scripts.Resolved().PostInstall != "" {
 | 
				
			||||||
		info.Scripts.PostInstall = filepath.Join(scriptDir, vars.Scripts.PostInstall)
 | 
							info.Scripts.PostInstall = filepath.Join(scriptDir, vars.Scripts.Resolved().PostInstall)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if vars.Scripts.PreRemove != "" {
 | 
						if vars.Scripts.Resolved().PreRemove != "" {
 | 
				
			||||||
		info.Scripts.PreRemove = filepath.Join(scriptDir, vars.Scripts.PreRemove)
 | 
							info.Scripts.PreRemove = filepath.Join(scriptDir, vars.Scripts.Resolved().PreRemove)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if vars.Scripts.PostRemove != "" {
 | 
						if vars.Scripts.Resolved().PostRemove != "" {
 | 
				
			||||||
		info.Scripts.PostRemove = filepath.Join(scriptDir, vars.Scripts.PostRemove)
 | 
							info.Scripts.PostRemove = filepath.Join(scriptDir, vars.Scripts.Resolved().PostRemove)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if vars.Scripts.PreUpgrade != "" {
 | 
						if vars.Scripts.Resolved().PreUpgrade != "" {
 | 
				
			||||||
		info.ArchLinux.Scripts.PreUpgrade = filepath.Join(scriptDir, vars.Scripts.PreUpgrade)
 | 
							info.ArchLinux.Scripts.PreUpgrade = filepath.Join(scriptDir, vars.Scripts.Resolved().PreUpgrade)
 | 
				
			||||||
		info.APK.Scripts.PreUpgrade = filepath.Join(scriptDir, vars.Scripts.PreUpgrade)
 | 
							info.APK.Scripts.PreUpgrade = filepath.Join(scriptDir, vars.Scripts.Resolved().PreUpgrade)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if vars.Scripts.PostUpgrade != "" {
 | 
						if vars.Scripts.Resolved().PostUpgrade != "" {
 | 
				
			||||||
		info.ArchLinux.Scripts.PostUpgrade = filepath.Join(scriptDir, vars.Scripts.PostUpgrade)
 | 
							info.ArchLinux.Scripts.PostUpgrade = filepath.Join(scriptDir, vars.Scripts.Resolved().PostUpgrade)
 | 
				
			||||||
		info.APK.Scripts.PostUpgrade = filepath.Join(scriptDir, vars.Scripts.PostUpgrade)
 | 
							info.APK.Scripts.PostUpgrade = filepath.Join(scriptDir, vars.Scripts.Resolved().PostUpgrade)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if vars.Scripts.PreTrans != "" {
 | 
						if vars.Scripts.Resolved().PreTrans != "" {
 | 
				
			||||||
		info.RPM.Scripts.PreTrans = filepath.Join(scriptDir, vars.Scripts.PreTrans)
 | 
							info.RPM.Scripts.PreTrans = filepath.Join(scriptDir, vars.Scripts.Resolved().PreTrans)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if vars.Scripts.PostTrans != "" {
 | 
						if vars.Scripts.Resolved().PostTrans != "" {
 | 
				
			||||||
		info.RPM.Scripts.PostTrans = filepath.Join(scriptDir, vars.Scripts.PostTrans)
 | 
							info.RPM.Scripts.PostTrans = filepath.Join(scriptDir, vars.Scripts.Resolved().PostTrans)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -272,25 +265,9 @@ func setVersion(ctx context.Context, r *interp.Runner, to string) error {
 | 
				
			|||||||
	return r.Run(ctx, fl)
 | 
						return r.Run(ctx, fl)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
// Returns not installed dependencies
 | 
					 | 
				
			||||||
func removeAlreadyInstalled(opts types.BuildOpts, dependencies []string) ([]string, error) {
 | 
					 | 
				
			||||||
	filteredPackages := []string{}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for _, dep := range dependencies {
 | 
					 | 
				
			||||||
		installed, err := opts.Manager.IsInstalled(dep)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return nil, err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if installed {
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		filteredPackages = append(filteredPackages, dep)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return filteredPackages, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Функция packageNames возвращает имена всех предоставленных пакетов.
 | 
					// Функция packageNames возвращает имена всех предоставленных пакетов.
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
func packageNames(pkgs []db.Package) []string {
 | 
					func packageNames(pkgs []db.Package) []string {
 | 
				
			||||||
	names := make([]string, len(pkgs))
 | 
						names := make([]string, len(pkgs))
 | 
				
			||||||
	for i, p := range pkgs {
 | 
						for i, p := range pkgs {
 | 
				
			||||||
@@ -298,16 +275,17 @@ func packageNames(pkgs []db.Package) []string {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	return names
 | 
						return names
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Функция removeDuplicates убирает любые дубликаты из предоставленного среза.
 | 
					// Функция removeDuplicates убирает любые дубликаты из предоставленного среза.
 | 
				
			||||||
func removeDuplicates(slice []string) []string {
 | 
					func removeDuplicates[T comparable](slice []T) []T {
 | 
				
			||||||
	seen := map[string]struct{}{}
 | 
						seen := map[T]struct{}{}
 | 
				
			||||||
	result := []string{}
 | 
						result := []T{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, s := range slice {
 | 
						for _, item := range slice {
 | 
				
			||||||
		if _, ok := seen[s]; !ok {
 | 
							if _, ok := seen[item]; !ok {
 | 
				
			||||||
			seen[s] = struct{}{}
 | 
								seen[item] = struct{}{}
 | 
				
			||||||
			result = append(result, s)
 | 
								result = append(result, item)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
// ALR - Any Linux Repository
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
// Copyright (C) 2025 Евгений Храмов
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// This program is free software: you can redistribute it and/or modify
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
// it under the terms of the GNU General Public License as published by
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
							
								
								
									
										183
									
								
								internal/cliutils/app_builder/builder.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										183
									
								
								internal/cliutils/app_builder/builder.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,183 @@
 | 
				
			|||||||
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package appbuilder
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"log/slog"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/leonelquinteros/gotext"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils"
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/config"
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/db"
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/manager"
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/repos"
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type AppDeps struct {
 | 
				
			||||||
 | 
						Cfg     *config.ALRConfig
 | 
				
			||||||
 | 
						DB      *db.Database
 | 
				
			||||||
 | 
						Repos   *repos.Repos
 | 
				
			||||||
 | 
						Info    *distro.OSRelease
 | 
				
			||||||
 | 
						Manager manager.Manager
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *AppDeps) Defer() {
 | 
				
			||||||
 | 
						if d.DB != nil {
 | 
				
			||||||
 | 
							if err := d.DB.Close(); err != nil {
 | 
				
			||||||
 | 
								slog.Warn("failed to close db", "err", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type AppBuilder struct {
 | 
				
			||||||
 | 
						deps AppDeps
 | 
				
			||||||
 | 
						err  error
 | 
				
			||||||
 | 
						ctx  context.Context
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func New(ctx context.Context) *AppBuilder {
 | 
				
			||||||
 | 
						return &AppBuilder{ctx: ctx}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *AppBuilder) UseConfig(cfg *config.ALRConfig) *AppBuilder {
 | 
				
			||||||
 | 
						if b.err != nil {
 | 
				
			||||||
 | 
							return b
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						b.deps.Cfg = cfg
 | 
				
			||||||
 | 
						return b
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *AppBuilder) WithConfig() *AppBuilder {
 | 
				
			||||||
 | 
						if b.err != nil {
 | 
				
			||||||
 | 
							return b
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cfg := config.New()
 | 
				
			||||||
 | 
						if err := cfg.Load(); err != nil {
 | 
				
			||||||
 | 
							b.err = cliutils.FormatCliExit(gotext.Get("Error loading config"), err)
 | 
				
			||||||
 | 
							return b
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						b.deps.Cfg = cfg
 | 
				
			||||||
 | 
						return b
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *AppBuilder) WithDB() *AppBuilder {
 | 
				
			||||||
 | 
						if b.err != nil {
 | 
				
			||||||
 | 
							return b
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cfg := b.deps.Cfg
 | 
				
			||||||
 | 
						if cfg == nil {
 | 
				
			||||||
 | 
							b.err = errors.New("config is required before initializing DB")
 | 
				
			||||||
 | 
							return b
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						db := db.New(cfg)
 | 
				
			||||||
 | 
						if err := db.Init(b.ctx); err != nil {
 | 
				
			||||||
 | 
							b.err = cliutils.FormatCliExit(gotext.Get("Error initialization database"), err)
 | 
				
			||||||
 | 
							return b
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						b.deps.DB = db
 | 
				
			||||||
 | 
						return b
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *AppBuilder) WithRepos() *AppBuilder {
 | 
				
			||||||
 | 
						b.withRepos(true, false)
 | 
				
			||||||
 | 
						return b
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *AppBuilder) WithReposForcePull() *AppBuilder {
 | 
				
			||||||
 | 
						b.withRepos(true, true)
 | 
				
			||||||
 | 
						return b
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *AppBuilder) WithReposNoPull() *AppBuilder {
 | 
				
			||||||
 | 
						b.withRepos(false, false)
 | 
				
			||||||
 | 
						return b
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *AppBuilder) withRepos(enablePull, forcePull bool) *AppBuilder {
 | 
				
			||||||
 | 
						if b.err != nil {
 | 
				
			||||||
 | 
							return b
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cfg := b.deps.Cfg
 | 
				
			||||||
 | 
						db := b.deps.DB
 | 
				
			||||||
 | 
						info := b.deps.Info
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if info == nil {
 | 
				
			||||||
 | 
							b.WithDistroInfo()
 | 
				
			||||||
 | 
							info = b.deps.Info
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if cfg == nil || db == nil || info == nil {
 | 
				
			||||||
 | 
							b.err = errors.New("config, db and info are required before initializing repos")
 | 
				
			||||||
 | 
							return b
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rs := repos.New(cfg, db)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if enablePull && (forcePull || cfg.AutoPull()) {
 | 
				
			||||||
 | 
							if err := rs.Pull(b.ctx, cfg.Repos()); err != nil {
 | 
				
			||||||
 | 
								b.err = cliutils.FormatCliExit(gotext.Get("Error pulling repositories"), err)
 | 
				
			||||||
 | 
								return b
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						b.deps.Repos = rs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return b
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *AppBuilder) WithDistroInfo() *AppBuilder {
 | 
				
			||||||
 | 
						if b.err != nil {
 | 
				
			||||||
 | 
							return b
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						b.deps.Info, b.err = distro.ParseOSRelease(b.ctx)
 | 
				
			||||||
 | 
						if b.err != nil {
 | 
				
			||||||
 | 
							b.err = cliutils.FormatCliExit(gotext.Get("Error parsing os release"), b.err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return b
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *AppBuilder) WithManager() *AppBuilder {
 | 
				
			||||||
 | 
						if b.err != nil {
 | 
				
			||||||
 | 
							return b
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						b.deps.Manager = manager.Detect()
 | 
				
			||||||
 | 
						if b.deps.Manager == nil {
 | 
				
			||||||
 | 
							b.err = cliutils.FormatCliExit(gotext.Get("Unable to detect a supported package manager on the system"), nil)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return b
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *AppBuilder) Build() (*AppDeps, error) {
 | 
				
			||||||
 | 
						if b.err != nil {
 | 
				
			||||||
 | 
							return nil, b.err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return &b.deps, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,8 +1,8 @@
 | 
				
			|||||||
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
 | 
					// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
 | 
				
			||||||
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
 | 
					// It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// ALR - Any Linux Repository
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
// Copyright (C) 2025 Евгений Храмов
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// This program is free software: you can redistribute it and/or modify
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
// it under the terms of the GNU General Public License as published by
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
@@ -28,8 +28,8 @@ import (
 | 
				
			|||||||
	"github.com/AlecAivazis/survey/v2"
 | 
						"github.com/AlecAivazis/survey/v2"
 | 
				
			||||||
	"github.com/leonelquinteros/gotext"
 | 
						"github.com/leonelquinteros/gotext"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"gitea.plemya-x.ru/Plemya-x/ALR/internal/db"
 | 
					 | 
				
			||||||
	"gitea.plemya-x.ru/Plemya-x/ALR/internal/pager"
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/pager"
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/pkg/alrsh"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// YesNoPrompt asks the user a yes or no question, using def as the default answer
 | 
					// YesNoPrompt asks the user a yes or no question, using def as the default answer
 | 
				
			||||||
@@ -102,8 +102,8 @@ func ShowScript(path, name, style string) error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// FlattenPkgs attempts to flatten the a map of slices of packages into a single slice
 | 
					// FlattenPkgs attempts to flatten the a map of slices of packages into a single slice
 | 
				
			||||||
// of packages by prompting the user if multiple packages match.
 | 
					// of packages by prompting the user if multiple packages match.
 | 
				
			||||||
func FlattenPkgs(ctx context.Context, found map[string][]db.Package, verb string, interactive bool) []db.Package {
 | 
					func FlattenPkgs(ctx context.Context, found map[string][]alrsh.Package, verb string, interactive bool) []alrsh.Package {
 | 
				
			||||||
	var outPkgs []db.Package
 | 
						var outPkgs []alrsh.Package
 | 
				
			||||||
	for _, pkgs := range found {
 | 
						for _, pkgs := range found {
 | 
				
			||||||
		if len(pkgs) > 1 && interactive {
 | 
							if len(pkgs) > 1 && interactive {
 | 
				
			||||||
			choice, err := PkgPrompt(ctx, pkgs, verb, interactive)
 | 
								choice, err := PkgPrompt(ctx, pkgs, verb, interactive)
 | 
				
			||||||
@@ -120,7 +120,7 @@ func FlattenPkgs(ctx context.Context, found map[string][]db.Package, verb string
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// PkgPrompt asks the user to choose between multiple packages.
 | 
					// PkgPrompt asks the user to choose between multiple packages.
 | 
				
			||||||
func PkgPrompt(ctx context.Context, options []db.Package, verb string, interactive bool) (db.Package, error) {
 | 
					func PkgPrompt(ctx context.Context, options []alrsh.Package, verb string, interactive bool) (alrsh.Package, error) {
 | 
				
			||||||
	if !interactive {
 | 
						if !interactive {
 | 
				
			||||||
		return options[0], nil
 | 
							return options[0], nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -138,7 +138,7 @@ func PkgPrompt(ctx context.Context, options []db.Package, verb string, interacti
 | 
				
			|||||||
	var choice int
 | 
						var choice int
 | 
				
			||||||
	err := survey.AskOne(prompt, &choice)
 | 
						err := survey.AskOne(prompt, &choice)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return db.Package{}, err
 | 
							return alrsh.Package{}, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return options[choice], nil
 | 
						return options[choice], nil
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
// ALR - Any Linux Repository
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
// Copyright (C) 2025 Евгений Храмов
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// This program is free software: you can redistribute it and/or modify
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
// it under the terms of the GNU General Public License as published by
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										72
									
								
								internal/cliutils/utils.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								internal/cliutils/utils.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,72 @@
 | 
				
			|||||||
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package cliutils
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"log/slog"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/leonelquinteros/gotext"
 | 
				
			||||||
 | 
						"github.com/urfave/cli/v2"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type BashCompleteWithErrorFunc func(c *cli.Context) error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func BashCompleteWithError(f BashCompleteWithErrorFunc) cli.BashCompleteFunc {
 | 
				
			||||||
 | 
						return func(c *cli.Context) { HandleExitCoder(f(c)) }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func HandleExitCoder(err error) {
 | 
				
			||||||
 | 
						if err == nil {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if exitErr, ok := err.(cli.ExitCoder); ok {
 | 
				
			||||||
 | 
							if err.Error() != "" {
 | 
				
			||||||
 | 
								if _, ok := exitErr.(cli.ErrorFormatter); ok {
 | 
				
			||||||
 | 
									slog.Error(fmt.Sprintf("%+v\n", err))
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									slog.Error(err.Error())
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							cli.OsExiter(exitErr.ExitCode())
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						slog.Error(err.Error())
 | 
				
			||||||
 | 
						cli.OsExiter(1)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func FormatCliExit(msg string, err error) cli.ExitCoder {
 | 
				
			||||||
 | 
						return FormatCliExitWithCode(msg, err, 1)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func FormatCliExitWithCode(msg string, err error, exitCode int) cli.ExitCoder {
 | 
				
			||||||
 | 
						if err == nil {
 | 
				
			||||||
 | 
							return cli.Exit(errors.New(msg), exitCode)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return cli.Exit(fmt.Errorf("%s: %w", msg, err), exitCode)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func WarnLegacyCommand(newSyntax string) {
 | 
				
			||||||
 | 
						slog.Warn(
 | 
				
			||||||
 | 
							gotext.Get(
 | 
				
			||||||
 | 
								"This command is deprecated and would be removed in the future, use \"%s\" instead!", newSyntax,
 | 
				
			||||||
 | 
							),
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,8 +1,8 @@
 | 
				
			|||||||
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
 | 
					// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
 | 
				
			||||||
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
 | 
					// It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// ALR - Any Linux Repository
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
// Copyright (C) 2025 Евгений Храмов
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// This program is free software: you can redistribute it and/or modify
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
// it under the terms of the GNU General Public License as published by
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
@@ -20,181 +20,104 @@
 | 
				
			|||||||
package config
 | 
					package config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"log/slog"
 | 
						"fmt"
 | 
				
			||||||
	"os"
 | 
					 | 
				
			||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
	"reflect"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/caarlos0/env"
 | 
						"github.com/goccy/go-yaml"
 | 
				
			||||||
	"github.com/leonelquinteros/gotext"
 | 
						"github.com/knadh/koanf/providers/confmap"
 | 
				
			||||||
	"github.com/pelletier/go-toml/v2"
 | 
						"github.com/knadh/koanf/v2"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"gitea.plemya-x.ru/Plemya-x/ALR/internal/types"
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/constants"
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/pkg/types"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type ALRConfig struct {
 | 
					type ALRConfig struct {
 | 
				
			||||||
	cfg   *types.Config
 | 
						cfg   *types.Config
 | 
				
			||||||
	paths *Paths
 | 
						paths *Paths
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
var defaultConfig = &types.Config{
 | 
						System *SystemConfig
 | 
				
			||||||
	RootCmd:          "sudo",
 | 
						env    *EnvConfig
 | 
				
			||||||
	PagerStyle:       "native",
 | 
					 | 
				
			||||||
	IgnorePkgUpdates: []string{},
 | 
					 | 
				
			||||||
	AutoPull:         true,
 | 
					 | 
				
			||||||
	Repos:            []types.Repo{},
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func New() *ALRConfig {
 | 
					func New() *ALRConfig {
 | 
				
			||||||
	return &ALRConfig{}
 | 
						return &ALRConfig{
 | 
				
			||||||
}
 | 
							System: NewSystemConfig(),
 | 
				
			||||||
 | 
							env:    NewEnvConfig(),
 | 
				
			||||||
func readConfig(path string) (*types.Config, error) {
 | 
					 | 
				
			||||||
	file, err := os.Open(path)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	defer file.Close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	config := types.Config{}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if err := toml.NewDecoder(file).Decode(&config); err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return &config, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func mergeStructs(dst, src interface{}) {
 | 
					 | 
				
			||||||
	srcVal := reflect.ValueOf(src)
 | 
					 | 
				
			||||||
	if srcVal.IsNil() {
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	srcVal = srcVal.Elem()
 | 
					 | 
				
			||||||
	dstVal := reflect.ValueOf(dst).Elem()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for i := range srcVal.NumField() {
 | 
					 | 
				
			||||||
		srcField := srcVal.Field(i)
 | 
					 | 
				
			||||||
		srcFieldName := srcVal.Type().Field(i).Name
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		dstField := dstVal.FieldByName(srcFieldName)
 | 
					 | 
				
			||||||
		if dstField.IsValid() && dstField.CanSet() {
 | 
					 | 
				
			||||||
			dstField.Set(srcField)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const systemConfigPath = "/etc/alr/alr.toml"
 | 
					func defaultConfigKoanf() *koanf.Koanf {
 | 
				
			||||||
 | 
						k := koanf.New(".")
 | 
				
			||||||
 | 
						defaults := map[string]interface{}{
 | 
				
			||||||
 | 
							"rootCmd":          "sudo",
 | 
				
			||||||
 | 
							"useRootCmd":       true,
 | 
				
			||||||
 | 
							"pagerStyle":       "native",
 | 
				
			||||||
 | 
							"ignorePkgUpdates": []string{},
 | 
				
			||||||
 | 
							"logLevel":         "info",
 | 
				
			||||||
 | 
							"autoPull":         true,
 | 
				
			||||||
 | 
							"repos":            []types.Repo{},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err := k.Load(confmap.Provider(defaults, "."), nil); err != nil {
 | 
				
			||||||
 | 
							panic(k)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return k
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *ALRConfig) Load() error {
 | 
					func (c *ALRConfig) Load() error {
 | 
				
			||||||
	systemConfig, err := readConfig(
 | 
						config := types.Config{}
 | 
				
			||||||
		systemConfigPath,
 | 
					
 | 
				
			||||||
	)
 | 
						merged := koanf.New(".")
 | 
				
			||||||
	if err != nil {
 | 
					
 | 
				
			||||||
		slog.Debug("Cannot read system config", "err", err)
 | 
						if err := c.System.Load(); err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("failed to load system config: %w", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cfgDir, err := os.UserConfigDir()
 | 
						if err := c.env.Load(); err != nil {
 | 
				
			||||||
	if err != nil {
 | 
							return fmt.Errorf("failed to load env config: %w", err)
 | 
				
			||||||
		slog.Debug("Cannot read user config directory")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	userConfigPath := filepath.Join(cfgDir, "alr", "alr.toml")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	userConfig, err := readConfig(
 | 
					 | 
				
			||||||
		userConfigPath,
 | 
					 | 
				
			||||||
	)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		slog.Debug("Cannot read user config")
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	config := &types.Config{}
 | 
						systemK := c.System.koanf()
 | 
				
			||||||
 | 
						envK := c.env.koanf()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mergeStructs(config, defaultConfig)
 | 
						if err := merged.Merge(defaultConfigKoanf()); err != nil {
 | 
				
			||||||
	mergeStructs(config, systemConfig)
 | 
							return fmt.Errorf("failed to merge default config: %w", err)
 | 
				
			||||||
	mergeStructs(config, userConfig)
 | 
						}
 | 
				
			||||||
	err = env.Parse(config)
 | 
						if err := merged.Merge(systemK); err != nil {
 | 
				
			||||||
	if err != nil {
 | 
							return fmt.Errorf("failed to merge system config: %w", err)
 | 
				
			||||||
		return err
 | 
						}
 | 
				
			||||||
 | 
						if err := merged.Merge(envK); err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("failed to merge env config: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err := merged.Unmarshal("", &config); err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("failed to unmarshal merged config: %w", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	c.cfg = config
 | 
						c.cfg = &config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cacheDir, err := os.UserCacheDir()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	c.paths = &Paths{}
 | 
						c.paths = &Paths{}
 | 
				
			||||||
	c.paths.UserConfigPath = userConfigPath
 | 
						c.paths.UserConfigPath = constants.SystemConfigPath
 | 
				
			||||||
	c.paths.CacheDir = filepath.Join(cacheDir, "alr")
 | 
						c.paths.CacheDir = constants.SystemCachePath
 | 
				
			||||||
	c.paths.RepoDir = filepath.Join(c.paths.CacheDir, "repo")
 | 
						c.paths.RepoDir = filepath.Join(c.paths.CacheDir, "repo")
 | 
				
			||||||
	c.paths.PkgsDir = filepath.Join(c.paths.CacheDir, "pkgs")
 | 
						c.paths.PkgsDir = filepath.Join(c.paths.CacheDir, "pkgs")
 | 
				
			||||||
	c.paths.DBPath = filepath.Join(c.paths.CacheDir, "db")
 | 
						c.paths.DBPath = filepath.Join(c.paths.CacheDir, "db")
 | 
				
			||||||
	c.initPaths()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *ALRConfig) RootCmd() string {
 | 
					func (c *ALRConfig) ToYAML() (string, error) {
 | 
				
			||||||
	return c.cfg.RootCmd
 | 
						data, err := yaml.Marshal(c.cfg)
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (c *ALRConfig) PagerStyle() string {
 | 
					 | 
				
			||||||
	return c.cfg.PagerStyle
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (c *ALRConfig) AutoPull() bool {
 | 
					 | 
				
			||||||
	return c.cfg.AutoPull
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (c *ALRConfig) AllowRunAsRoot() bool {
 | 
					 | 
				
			||||||
	return c.cfg.Unsafe.AllowRunAsRoot
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (c *ALRConfig) Repos() []types.Repo {
 | 
					 | 
				
			||||||
	return c.cfg.Repos
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (c *ALRConfig) SetRepos(repos []types.Repo) {
 | 
					 | 
				
			||||||
	c.cfg.Repos = repos
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (c *ALRConfig) IgnorePkgUpdates() []string {
 | 
					 | 
				
			||||||
	return c.cfg.IgnorePkgUpdates
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (c *ALRConfig) LogLevel() string {
 | 
					 | 
				
			||||||
	return c.cfg.LogLevel
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (c *ALRConfig) GetPaths() *Paths {
 | 
					 | 
				
			||||||
	return c.paths
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (c *ALRConfig) initPaths() {
 | 
					 | 
				
			||||||
	err := os.MkdirAll(filepath.Dir(c.paths.UserConfigPath), 0o755)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		slog.Error(gotext.Get("Unable to create config directory"), "err", err)
 | 
							return "", err
 | 
				
			||||||
		os.Exit(1)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	err = os.MkdirAll(c.paths.RepoDir, 0o755)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		slog.Error(gotext.Get("Unable to create repo cache directory"), "err", err)
 | 
					 | 
				
			||||||
		os.Exit(1)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	err = os.MkdirAll(c.paths.PkgsDir, 0o755)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		slog.Error(gotext.Get("Unable to create package cache directory"), "err", err)
 | 
					 | 
				
			||||||
		os.Exit(1)
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						return string(data), nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *ALRConfig) SaveUserConfig() error {
 | 
					func (c *ALRConfig) RootCmd() string             { return c.cfg.RootCmd }
 | 
				
			||||||
	f, err := os.Create(c.paths.UserConfigPath)
 | 
					func (c *ALRConfig) PagerStyle() string          { return c.cfg.PagerStyle }
 | 
				
			||||||
	if err != nil {
 | 
					func (c *ALRConfig) AutoPull() bool              { return c.cfg.AutoPull }
 | 
				
			||||||
		return err
 | 
					func (c *ALRConfig) Repos() []types.Repo         { return c.cfg.Repos }
 | 
				
			||||||
	}
 | 
					func (c *ALRConfig) SetRepos(repos []types.Repo) { c.System.SetRepos(repos) }
 | 
				
			||||||
 | 
					func (c *ALRConfig) IgnorePkgUpdates() []string  { return c.cfg.IgnorePkgUpdates }
 | 
				
			||||||
	return toml.NewEncoder(f).Encode(c.cfg)
 | 
					func (c *ALRConfig) LogLevel() string            { return c.cfg.LogLevel }
 | 
				
			||||||
}
 | 
					func (c *ALRConfig) UseRootCmd() bool            { return c.cfg.UseRootCmd }
 | 
				
			||||||
 | 
					func (c *ALRConfig) GetPaths() *Paths            { return c.paths }
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										76
									
								
								internal/config/env_config.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								internal/config/env_config.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,76 @@
 | 
				
			|||||||
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/knadh/koanf/providers/env"
 | 
				
			||||||
 | 
						"github.com/knadh/koanf/v2"
 | 
				
			||||||
 | 
						"golang.org/x/text/cases"
 | 
				
			||||||
 | 
						"golang.org/x/text/language"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type EnvConfig struct {
 | 
				
			||||||
 | 
						k *koanf.Koanf
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewEnvConfig() *EnvConfig {
 | 
				
			||||||
 | 
						return &EnvConfig{
 | 
				
			||||||
 | 
							k: koanf.New("."),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *EnvConfig) koanf() *koanf.Koanf {
 | 
				
			||||||
 | 
						return c.k
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *EnvConfig) Load() error {
 | 
				
			||||||
 | 
						allowedKeys := map[string]struct{}{
 | 
				
			||||||
 | 
							"ALR_LOG_LEVEL":   {},
 | 
				
			||||||
 | 
							"ALR_PAGER_STYLE": {},
 | 
				
			||||||
 | 
							"ALR_AUTO_PULL":   {},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						err := c.k.Load(env.Provider("ALR_", ".", func(s string) string {
 | 
				
			||||||
 | 
							_, ok := allowedKeys[s]
 | 
				
			||||||
 | 
							if !ok {
 | 
				
			||||||
 | 
								return ""
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							withoutPrefix := strings.TrimPrefix(s, "ALR_")
 | 
				
			||||||
 | 
							lowered := strings.ToLower(withoutPrefix)
 | 
				
			||||||
 | 
							dotted := strings.ReplaceAll(lowered, "__", ".")
 | 
				
			||||||
 | 
							parts := strings.Split(dotted, ".")
 | 
				
			||||||
 | 
							for i, part := range parts {
 | 
				
			||||||
 | 
								if strings.Contains(part, "_") {
 | 
				
			||||||
 | 
									parts[i] = toCamelCase(part)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return strings.Join(parts, ".")
 | 
				
			||||||
 | 
						}), nil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func toCamelCase(s string) string {
 | 
				
			||||||
 | 
						parts := strings.Split(s, "_")
 | 
				
			||||||
 | 
						for i := 1; i < len(parts); i++ {
 | 
				
			||||||
 | 
							if len(parts[i]) > 0 {
 | 
				
			||||||
 | 
								parts[i] = cases.Title(language.Und, cases.NoLower).String(parts[i])
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return strings.Join(parts, "")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,8 +1,8 @@
 | 
				
			|||||||
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
 | 
					// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
 | 
				
			||||||
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
 | 
					// It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// ALR - Any Linux Repository
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
// Copyright (C) 2025 Евгений Храмов
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// This program is free software: you can redistribute it and/or modify
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
// it under the terms of the GNU General Public License as published by
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
@@ -21,6 +21,7 @@ package config
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Paths contains various paths used by ALR
 | 
					// Paths contains various paths used by ALR
 | 
				
			||||||
type Paths struct {
 | 
					type Paths struct {
 | 
				
			||||||
 | 
						SystemConfigPath string
 | 
				
			||||||
	UserConfigPath   string
 | 
						UserConfigPath   string
 | 
				
			||||||
	CacheDir         string
 | 
						CacheDir         string
 | 
				
			||||||
	RepoDir          string
 | 
						RepoDir          string
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										144
									
								
								internal/config/system_config.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								internal/config/system_config.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,144 @@
 | 
				
			|||||||
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ktoml "github.com/knadh/koanf/parsers/toml/v2"
 | 
				
			||||||
 | 
						"github.com/knadh/koanf/providers/file"
 | 
				
			||||||
 | 
						"github.com/knadh/koanf/v2"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/constants"
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/pkg/types"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type SystemConfig struct {
 | 
				
			||||||
 | 
						k   *koanf.Koanf
 | 
				
			||||||
 | 
						cfg *types.Config
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewSystemConfig() *SystemConfig {
 | 
				
			||||||
 | 
						return &SystemConfig{
 | 
				
			||||||
 | 
							k:   koanf.New("."),
 | 
				
			||||||
 | 
							cfg: &types.Config{},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *SystemConfig) koanf() *koanf.Koanf {
 | 
				
			||||||
 | 
						return c.k
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *SystemConfig) Load() error {
 | 
				
			||||||
 | 
						if _, err := os.Stat(constants.SystemConfigPath); errors.Is(err, os.ErrNotExist) {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := c.k.Load(file.Provider(constants.SystemConfigPath), ktoml.Parser()); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return c.k.Unmarshal("", c.cfg)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *SystemConfig) Save() error {
 | 
				
			||||||
 | 
						bytes, err := c.k.Marshal(ktoml.Parser())
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("failed to marshal config: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						file, err := os.Create(constants.SystemConfigPath)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("failed to create config file: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer func() {
 | 
				
			||||||
 | 
							if cerr := file.Close(); cerr != nil && err == nil {
 | 
				
			||||||
 | 
								err = cerr
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if _, err := file.Write(bytes); err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("failed to write config: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := file.Sync(); err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("failed to sync config: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *SystemConfig) SetRootCmd(v string) {
 | 
				
			||||||
 | 
						err := c.k.Set("rootCmd", v)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *SystemConfig) SetUseRootCmd(v bool) {
 | 
				
			||||||
 | 
						err := c.k.Set("useRootCmd", v)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *SystemConfig) SetPagerStyle(v string) {
 | 
				
			||||||
 | 
						err := c.k.Set("pagerStyle", v)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *SystemConfig) SetIgnorePkgUpdates(v []string) {
 | 
				
			||||||
 | 
						err := c.k.Set("ignorePkgUpdates", v)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *SystemConfig) SetAutoPull(v bool) {
 | 
				
			||||||
 | 
						err := c.k.Set("autoPull", v)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *SystemConfig) SetLogLevel(v string) {
 | 
				
			||||||
 | 
						err := c.k.Set("logLevel", v)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *SystemConfig) SetRepos(v []types.Repo) {
 | 
				
			||||||
 | 
						b, err := json.Marshal(v)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						var m []interface{}
 | 
				
			||||||
 | 
						err = json.Unmarshal(b, &m)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						err = c.k.Set("repo", m)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,8 +1,8 @@
 | 
				
			|||||||
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
 | 
					// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
 | 
				
			||||||
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
 | 
					// It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// ALR - Any Linux Repository
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
// Copyright (C) 2025 Евгений Храмов
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// This program is free software: you can redistribute it and/or modify
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
// it under the terms of the GNU General Public License as published by
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										24
									
								
								internal/constants/constants.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								internal/constants/constants.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
				
			|||||||
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package constants
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						SystemConfigPath = "/etc/alr/alr.toml"
 | 
				
			||||||
 | 
						SystemCachePath  = "/var/cache/alr"
 | 
				
			||||||
 | 
						AlrRunDir        = "/var/run/alr"
 | 
				
			||||||
 | 
						PrivilegedGroup  = "wheel"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
@@ -1,8 +1,8 @@
 | 
				
			|||||||
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
 | 
					// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
 | 
				
			||||||
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
 | 
					// It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// ALR - Any Linux Repository
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
// Copyright (C) 2025 Евгений Храмов
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// This program is free software: you can redistribute it and/or modify
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
// it under the terms of the GNU General Public License as published by
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,8 +1,8 @@
 | 
				
			|||||||
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
 | 
					// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
 | 
				
			||||||
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
 | 
					// It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// ALR - Any Linux Repository
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
// Copyright (C) 2025 Евгений Храмов
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// This program is free software: you can redistribute it and/or modify
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
// it under the terms of the GNU General Public License as published by
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
@@ -23,39 +23,18 @@ import (
 | 
				
			|||||||
	"context"
 | 
						"context"
 | 
				
			||||||
	"log/slog"
 | 
						"log/slog"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/jmoiron/sqlx"
 | 
					 | 
				
			||||||
	"github.com/leonelquinteros/gotext"
 | 
						"github.com/leonelquinteros/gotext"
 | 
				
			||||||
 | 
						_ "modernc.org/sqlite"
 | 
				
			||||||
 | 
						"xorm.io/xorm"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"gitea.plemya-x.ru/Plemya-x/ALR/internal/config"
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/config"
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/pkg/alrsh"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// CurrentVersion is the current version of the database.
 | 
					const CurrentVersion = 5
 | 
				
			||||||
// The database is reset if its version doesn't match this.
 | 
					 | 
				
			||||||
const CurrentVersion = 3
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Package is a ALR package's database representation
 | 
					type Version struct {
 | 
				
			||||||
type Package struct {
 | 
						Version int `xorm:"'version'"`
 | 
				
			||||||
	BasePkgName   string                    `sh:"base" db:"basepkg_name"`
 | 
					 | 
				
			||||||
	Name          string                    `sh:"name,required" db:"name"`
 | 
					 | 
				
			||||||
	Version       string                    `sh:"version,required" db:"version"`
 | 
					 | 
				
			||||||
	Release       int                       `sh:"release,required" db:"release"`
 | 
					 | 
				
			||||||
	Epoch         uint                      `sh:"epoch" db:"epoch"`
 | 
					 | 
				
			||||||
	Description   JSON[map[string]string]   `db:"description"`
 | 
					 | 
				
			||||||
	Homepage      JSON[map[string]string]   `db:"homepage"`
 | 
					 | 
				
			||||||
	Maintainer    JSON[map[string]string]   `db:"maintainer"`
 | 
					 | 
				
			||||||
	Architectures JSON[[]string]            `sh:"architectures" db:"architectures"`
 | 
					 | 
				
			||||||
	Licenses      JSON[[]string]            `sh:"license" db:"licenses"`
 | 
					 | 
				
			||||||
	Provides      JSON[[]string]            `sh:"provides" db:"provides"`
 | 
					 | 
				
			||||||
	Conflicts     JSON[[]string]            `sh:"conflicts" db:"conflicts"`
 | 
					 | 
				
			||||||
	Replaces      JSON[[]string]            `sh:"replaces" db:"replaces"`
 | 
					 | 
				
			||||||
	Depends       JSON[map[string][]string] `db:"depends"`
 | 
					 | 
				
			||||||
	BuildDepends  JSON[map[string][]string] `db:"builddepends"`
 | 
					 | 
				
			||||||
	OptDepends    JSON[map[string][]string] `db:"optdepends"`
 | 
					 | 
				
			||||||
	Repository    string                    `db:"repository"`
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type version struct {
 | 
					 | 
				
			||||||
	Version int `db:"version"`
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Config interface {
 | 
					type Config interface {
 | 
				
			||||||
@@ -63,7 +42,7 @@ type Config interface {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Database struct {
 | 
					type Database struct {
 | 
				
			||||||
	conn   *sqlx.DB
 | 
						engine *xorm.Engine
 | 
				
			||||||
	config Config
 | 
						config Config
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -73,175 +52,100 @@ func New(config Config) *Database {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (d *Database) Init(ctx context.Context) error {
 | 
					func (d *Database) Connect() error {
 | 
				
			||||||
	err := d.Connect(ctx)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return d.initDB(ctx)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (d *Database) Connect(ctx context.Context) error {
 | 
					 | 
				
			||||||
	dsn := d.config.GetPaths().DBPath
 | 
						dsn := d.config.GetPaths().DBPath
 | 
				
			||||||
	db, err := sqlx.Open("sqlite", dsn)
 | 
						engine, err := xorm.NewEngine("sqlite", dsn)
 | 
				
			||||||
 | 
						// engine.SetLogLevel(log.LOG_DEBUG)
 | 
				
			||||||
 | 
						// engine.ShowSQL(true)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	d.conn = db
 | 
						d.engine = engine
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (d *Database) GetConn() *sqlx.DB {
 | 
					func (d *Database) Init(ctx context.Context) error {
 | 
				
			||||||
	return d.conn
 | 
						if err := d.Connect(); err != nil {
 | 
				
			||||||
}
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
func (d *Database) initDB(ctx context.Context) error {
 | 
						if err := d.engine.Sync2(new(alrsh.Package), new(Version)); err != nil {
 | 
				
			||||||
	d.conn = d.conn.Unsafe()
 | 
					 | 
				
			||||||
	conn := d.conn
 | 
					 | 
				
			||||||
	_, err := conn.ExecContext(ctx, `
 | 
					 | 
				
			||||||
		CREATE TABLE IF NOT EXISTS pkgs (
 | 
					 | 
				
			||||||
			basepkg_name  TEXT NOT NULL,
 | 
					 | 
				
			||||||
			name          TEXT NOT NULL,
 | 
					 | 
				
			||||||
			repository    TEXT NOT NULL,
 | 
					 | 
				
			||||||
			version       TEXT NOT NULL,
 | 
					 | 
				
			||||||
			release       INT  NOT NULL,
 | 
					 | 
				
			||||||
			epoch         INT,
 | 
					 | 
				
			||||||
			description   TEXT CHECK(description = 'null' OR (JSON_VALID(description) AND JSON_TYPE(description) = 'object')),
 | 
					 | 
				
			||||||
			homepage      TEXT CHECK(homepage = 'null' OR (JSON_VALID(homepage) AND JSON_TYPE(homepage) = 'object')),
 | 
					 | 
				
			||||||
			maintainer    TEXT CHECK(maintainer = 'null' OR (JSON_VALID(maintainer) AND JSON_TYPE(maintainer) = 'object')),
 | 
					 | 
				
			||||||
			architectures TEXT CHECK(architectures = 'null' OR (JSON_VALID(architectures) AND JSON_TYPE(architectures) = 'array')),
 | 
					 | 
				
			||||||
			licenses      TEXT CHECK(licenses = 'null' OR (JSON_VALID(licenses) AND JSON_TYPE(licenses) = 'array')),
 | 
					 | 
				
			||||||
			provides      TEXT CHECK(provides = 'null' OR (JSON_VALID(provides) AND JSON_TYPE(provides) = 'array')),
 | 
					 | 
				
			||||||
			conflicts     TEXT CHECK(conflicts = 'null' OR (JSON_VALID(conflicts) AND JSON_TYPE(conflicts) = 'array')),
 | 
					 | 
				
			||||||
			replaces      TEXT CHECK(replaces = 'null' OR (JSON_VALID(replaces) AND JSON_TYPE(replaces) = 'array')),
 | 
					 | 
				
			||||||
			depends       TEXT CHECK(depends = 'null' OR (JSON_VALID(depends) AND JSON_TYPE(depends) = 'object')),
 | 
					 | 
				
			||||||
			builddepends  TEXT CHECK(builddepends = 'null' OR (JSON_VALID(builddepends) AND JSON_TYPE(builddepends) = 'object')),
 | 
					 | 
				
			||||||
			optdepends    TEXT CHECK(optdepends = 'null' OR (JSON_VALID(optdepends) AND JSON_TYPE(optdepends) = 'object')),
 | 
					 | 
				
			||||||
			UNIQUE(name, repository)
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		CREATE TABLE IF NOT EXISTS alr_db_version (
 | 
					 | 
				
			||||||
			version INT NOT NULL
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
	`)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	ver, ok := d.GetVersion(ctx)
 | 
						ver, ok := d.GetVersion(ctx)
 | 
				
			||||||
	if ok && ver != CurrentVersion {
 | 
						if ok && ver != CurrentVersion {
 | 
				
			||||||
		slog.Warn(gotext.Get("Database version mismatch; resetting"), "version", ver, "expected", CurrentVersion)
 | 
							slog.Warn(gotext.Get("Database version mismatch; resetting"), "version", ver, "expected", CurrentVersion)
 | 
				
			||||||
		err = d.reset(ctx)
 | 
							if err := d.reset(); err != nil {
 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return d.initDB(ctx)
 | 
							return d.Init(ctx)
 | 
				
			||||||
	} else if !ok {
 | 
						} else if !ok {
 | 
				
			||||||
		slog.Warn(gotext.Get("Database version does not exist. Run alr fix if something isn't working."), "version", ver, "expected", CurrentVersion)
 | 
							slog.Warn(gotext.Get("Database version does not exist. Run alr fix if something isn't working."))
 | 
				
			||||||
		return d.addVersion(ctx, CurrentVersion)
 | 
							return d.addVersion(CurrentVersion)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (d *Database) GetVersion(ctx context.Context) (int, bool) {
 | 
					func (d *Database) GetVersion(ctx context.Context) (int, bool) {
 | 
				
			||||||
	var ver version
 | 
						var v Version
 | 
				
			||||||
	err := d.conn.GetContext(ctx, &ver, "SELECT * FROM alr_db_version LIMIT 1;")
 | 
						has, err := d.engine.Get(&v)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil || !has {
 | 
				
			||||||
		return 0, false
 | 
							return 0, false
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return ver.Version, true
 | 
						return v.Version, true
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (d *Database) addVersion(ctx context.Context, ver int) error {
 | 
					func (d *Database) addVersion(ver int) error {
 | 
				
			||||||
	_, err := d.conn.ExecContext(ctx, `INSERT INTO alr_db_version(version) VALUES (?);`, ver)
 | 
						_, err := d.engine.Insert(&Version{Version: ver})
 | 
				
			||||||
	return err
 | 
						return err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (d *Database) reset(ctx context.Context) error {
 | 
					func (d *Database) reset() error {
 | 
				
			||||||
	_, err := d.conn.ExecContext(ctx, "DROP TABLE IF EXISTS pkgs;")
 | 
						return d.engine.DropTables(new(alrsh.Package), new(Version))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *Database) InsertPackage(ctx context.Context, pkg alrsh.Package) error {
 | 
				
			||||||
 | 
						session := d.engine.Context(ctx)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						affected, err := session.Where("name = ? AND repository = ?", pkg.Name, pkg.Repository).Update(&pkg)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	_, err = d.conn.ExecContext(ctx, "DROP TABLE IF EXISTS alr_db_version;")
 | 
					
 | 
				
			||||||
 | 
						if affected == 0 {
 | 
				
			||||||
 | 
							_, err = session.Insert(&pkg)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (d *Database) GetPkgs(ctx context.Context, where string, args ...any) (*sqlx.Rows, error) {
 | 
					func (d *Database) GetPkgs(_ context.Context, where string, args ...any) ([]alrsh.Package, error) {
 | 
				
			||||||
	stream, err := d.conn.QueryxContext(ctx, "SELECT * FROM pkgs WHERE "+where, args...)
 | 
						var pkgs []alrsh.Package
 | 
				
			||||||
	if err != nil {
 | 
						err := d.engine.Where(where, args...).Find(&pkgs)
 | 
				
			||||||
 | 
						return pkgs, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *Database) GetPkg(where string, args ...any) (*alrsh.Package, error) {
 | 
				
			||||||
 | 
						var pkg alrsh.Package
 | 
				
			||||||
 | 
						has, err := d.engine.Where(where, args...).Get(&pkg)
 | 
				
			||||||
 | 
						if err != nil || !has {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return stream, nil
 | 
						return &pkg, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (d *Database) GetPkg(ctx context.Context, where string, args ...any) (*Package, error) {
 | 
					func (d *Database) DeletePkgs(_ context.Context, where string, args ...any) error {
 | 
				
			||||||
	out := &Package{}
 | 
						_, err := d.engine.Where(where, args...).Delete(&alrsh.Package{})
 | 
				
			||||||
	err := d.conn.GetContext(ctx, out, "SELECT * FROM pkgs WHERE "+where+" LIMIT 1", args...)
 | 
					 | 
				
			||||||
	return out, err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (d *Database) DeletePkgs(ctx context.Context, where string, args ...any) error {
 | 
					 | 
				
			||||||
	_, err := d.conn.ExecContext(ctx, "DELETE FROM pkgs WHERE "+where, args...)
 | 
					 | 
				
			||||||
	return err
 | 
						return err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (d *Database) IsEmpty(ctx context.Context) bool {
 | 
					func (d *Database) IsEmpty() bool {
 | 
				
			||||||
	var count int
 | 
						count, err := d.engine.Count(new(alrsh.Package))
 | 
				
			||||||
	err := d.conn.GetContext(ctx, &count, "SELECT count(1) FROM pkgs;")
 | 
						return err != nil || count == 0
 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return true
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return count == 0
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (d *Database) InsertPackage(ctx context.Context, pkg Package) error {
 | 
					 | 
				
			||||||
	_, err := d.conn.NamedExecContext(ctx, `
 | 
					 | 
				
			||||||
		INSERT OR REPLACE INTO pkgs (
 | 
					 | 
				
			||||||
			basepkg_name,
 | 
					 | 
				
			||||||
			name,
 | 
					 | 
				
			||||||
			repository,
 | 
					 | 
				
			||||||
			version,
 | 
					 | 
				
			||||||
			release,
 | 
					 | 
				
			||||||
			epoch,
 | 
					 | 
				
			||||||
			description,
 | 
					 | 
				
			||||||
			homepage,
 | 
					 | 
				
			||||||
			maintainer,
 | 
					 | 
				
			||||||
			architectures,
 | 
					 | 
				
			||||||
			licenses,
 | 
					 | 
				
			||||||
			provides,
 | 
					 | 
				
			||||||
			conflicts,
 | 
					 | 
				
			||||||
			replaces,
 | 
					 | 
				
			||||||
			depends,
 | 
					 | 
				
			||||||
			builddepends,
 | 
					 | 
				
			||||||
			optdepends
 | 
					 | 
				
			||||||
		) VALUES (
 | 
					 | 
				
			||||||
		 	:basepkg_name,
 | 
					 | 
				
			||||||
			:name,
 | 
					 | 
				
			||||||
			:repository,
 | 
					 | 
				
			||||||
			:version,
 | 
					 | 
				
			||||||
			:release,
 | 
					 | 
				
			||||||
			:epoch,
 | 
					 | 
				
			||||||
			:description,
 | 
					 | 
				
			||||||
			:homepage,
 | 
					 | 
				
			||||||
			:maintainer,
 | 
					 | 
				
			||||||
			:architectures,
 | 
					 | 
				
			||||||
			:licenses,
 | 
					 | 
				
			||||||
			:provides,
 | 
					 | 
				
			||||||
			:conflicts,
 | 
					 | 
				
			||||||
			:replaces,
 | 
					 | 
				
			||||||
			:depends,
 | 
					 | 
				
			||||||
			:builddepends,
 | 
					 | 
				
			||||||
			:optdepends
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
	`, pkg)
 | 
					 | 
				
			||||||
	return err
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (d *Database) Close() error {
 | 
					func (d *Database) Close() error {
 | 
				
			||||||
	if d.conn != nil {
 | 
						return d.engine.Close()
 | 
				
			||||||
		return d.conn.Close()
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		return nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,8 +1,8 @@
 | 
				
			|||||||
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
 | 
					// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
 | 
				
			||||||
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
 | 
					// It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// ALR - Any Linux Repository
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
// Copyright (C) 2025 Евгений Храмов
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// This program is free software: you can redistribute it and/or modify
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
// it under the terms of the GNU General Public License as published by
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
@@ -25,10 +25,11 @@ import (
 | 
				
			|||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/jmoiron/sqlx"
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"gitea.plemya-x.ru/Plemya-x/ALR/internal/config"
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/config"
 | 
				
			||||||
	"gitea.plemya-x.ru/Plemya-x/ALR/internal/db"
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/internal/db"
 | 
				
			||||||
 | 
						"gitea.plemya-x.ru/Plemya-x/ALR/pkg/alrsh"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type TestALRConfig struct{}
 | 
					type TestALRConfig struct{}
 | 
				
			||||||
@@ -45,35 +46,38 @@ func prepareDb() *db.Database {
 | 
				
			|||||||
	return database
 | 
						return database
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var testPkg = db.Package{
 | 
					var testPkg = alrsh.Package{
 | 
				
			||||||
	Name:    "test",
 | 
						Name:    "test",
 | 
				
			||||||
	Version: "0.0.1",
 | 
						Version: "0.0.1",
 | 
				
			||||||
	Release: 1,
 | 
						Release: 1,
 | 
				
			||||||
	Epoch:   2,
 | 
						Epoch:   2,
 | 
				
			||||||
	Description: db.NewJSON(map[string]string{
 | 
						Description: alrsh.OverridableFromMap(map[string]string{
 | 
				
			||||||
		"en": "Test package",
 | 
							"en": "Test package",
 | 
				
			||||||
		"ru": "Проверочный пакет",
 | 
							"ru": "Проверочный пакет",
 | 
				
			||||||
	}),
 | 
						}),
 | 
				
			||||||
	Homepage: db.NewJSON(map[string]string{
 | 
						Homepage: alrsh.OverridableFromMap(map[string]string{
 | 
				
			||||||
		"en": "https://gitea.plemya-x.ru/xpamych/ALR",
 | 
							"en": "https://gitea.plemya-x.ru/xpamych/ALR",
 | 
				
			||||||
	}),
 | 
						}),
 | 
				
			||||||
	Maintainer: db.NewJSON(map[string]string{
 | 
						Maintainer: alrsh.OverridableFromMap(map[string]string{
 | 
				
			||||||
		"en": "Evgeniy Khramov <xpamych@yandex.ru>",
 | 
							"en": "Evgeniy Khramov <xpamych@yandex.ru>",
 | 
				
			||||||
		"ru": "Евгений Храмов <xpamych@yandex.ru>",
 | 
							"ru": "Евгений Храмов <xpamych@yandex.ru>",
 | 
				
			||||||
	}),
 | 
						}),
 | 
				
			||||||
	Architectures: db.NewJSON([]string{"arm64", "amd64"}),
 | 
						Architectures: []string{"arm64", "amd64"},
 | 
				
			||||||
	Licenses:      db.NewJSON([]string{"GPL-3.0-or-later"}),
 | 
						Licenses:      []string{"GPL-3.0-or-later"},
 | 
				
			||||||
	Provides:      db.NewJSON([]string{"test"}),
 | 
						Provides:      []string{"test"},
 | 
				
			||||||
	Conflicts:     db.NewJSON([]string{"test"}),
 | 
						Conflicts:     []string{"test"},
 | 
				
			||||||
	Replaces:      db.NewJSON([]string{"test-old"}),
 | 
						Replaces:      []string{"test-old"},
 | 
				
			||||||
	Depends: db.NewJSON(map[string][]string{
 | 
						Depends: alrsh.OverridableFromMap(map[string][]string{
 | 
				
			||||||
		"": {"sudo"},
 | 
							"": {"sudo"},
 | 
				
			||||||
	}),
 | 
						}),
 | 
				
			||||||
	BuildDepends: db.NewJSON(map[string][]string{
 | 
						BuildDepends: alrsh.OverridableFromMap(map[string][]string{
 | 
				
			||||||
		"":     {"golang"},
 | 
							"":     {"golang"},
 | 
				
			||||||
		"arch": {"go"},
 | 
							"arch": {"go"},
 | 
				
			||||||
	}),
 | 
						}),
 | 
				
			||||||
	Repository: "default",
 | 
						Repository: "default",
 | 
				
			||||||
 | 
						Summary:    alrsh.OverridableFromMap(map[string]string{}),
 | 
				
			||||||
 | 
						Group:      alrsh.OverridableFromMap(map[string]string{}),
 | 
				
			||||||
 | 
						OptDepends: alrsh.OverridableFromMap(map[string][]string{}),
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestInit(t *testing.T) {
 | 
					func TestInit(t *testing.T) {
 | 
				
			||||||
@@ -99,15 +103,16 @@ func TestInsertPackage(t *testing.T) {
 | 
				
			|||||||
		t.Fatalf("Expected no error, got %s", err)
 | 
							t.Fatalf("Expected no error, got %s", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	dbPkg := db.Package{}
 | 
						pkgs, err := database.GetPkgs(ctx, "name = 'test' AND repository = 'default'")
 | 
				
			||||||
	err = sqlx.Get(database.GetConn(), &dbPkg, "SELECT * FROM pkgs WHERE name = 'test' AND repository = 'default'")
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatalf("Expected no error, got %s", err)
 | 
							t.Fatalf("Expected no error, got %s", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if !reflect.DeepEqual(testPkg, dbPkg) {
 | 
						if len(pkgs) != 1 {
 | 
				
			||||||
		t.Errorf("Expected test package to be the same as database package")
 | 
							t.Fatalf("Expected 1 package, got %d", len(pkgs))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						assert.Equal(t, testPkg, pkgs[0])
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestGetPkgs(t *testing.T) {
 | 
					func TestGetPkgs(t *testing.T) {
 | 
				
			||||||
@@ -130,18 +135,12 @@ func TestGetPkgs(t *testing.T) {
 | 
				
			|||||||
		t.Errorf("Expected no error, got %s", err)
 | 
							t.Errorf("Expected no error, got %s", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	result, err := database.GetPkgs(ctx, "name LIKE 'x%'")
 | 
						pkgs, err := database.GetPkgs(ctx, "name LIKE 'x%'")
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatalf("Expected no error, got %s", err)
 | 
							t.Fatalf("Expected no error, got %s", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for result.Next() {
 | 
						for _, dbPkg := range pkgs {
 | 
				
			||||||
		var dbPkg db.Package
 | 
					 | 
				
			||||||
		err = result.StructScan(&dbPkg)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			t.Errorf("Expected no error, got %s", err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if !strings.HasPrefix(dbPkg.Name, "x") {
 | 
							if !strings.HasPrefix(dbPkg.Name, "x") {
 | 
				
			||||||
			t.Errorf("Expected package name to start with 'x', got %s", dbPkg.Name)
 | 
								t.Errorf("Expected package name to start with 'x', got %s", dbPkg.Name)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -168,7 +167,7 @@ func TestGetPkg(t *testing.T) {
 | 
				
			|||||||
		t.Errorf("Expected no error, got %s", err)
 | 
							t.Errorf("Expected no error, got %s", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pkg, err := database.GetPkg(ctx, "name LIKE 'x%' ORDER BY name")
 | 
						pkg, err := database.GetPkg("name LIKE 'x%'")
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatalf("Expected no error, got %s", err)
 | 
							t.Fatalf("Expected no error, got %s", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -206,16 +205,6 @@ func TestDeletePkgs(t *testing.T) {
 | 
				
			|||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Errorf("Expected no error, got %s", err)
 | 
							t.Errorf("Expected no error, got %s", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	var dbPkg db.Package
 | 
					 | 
				
			||||||
	err = database.GetConn().Get(&dbPkg, "SELECT * FROM pkgs WHERE name LIKE 'x%' ORDER BY name LIMIT 1;")
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		t.Errorf("Expected no error, got %s", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if dbPkg.Name != "x2" {
 | 
					 | 
				
			||||||
		t.Errorf("Expected x2 package, got %s", dbPkg.Name)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestJsonArrayContains(t *testing.T) {
 | 
					func TestJsonArrayContains(t *testing.T) {
 | 
				
			||||||
@@ -227,7 +216,7 @@ func TestJsonArrayContains(t *testing.T) {
 | 
				
			|||||||
	x1.Name = "x1"
 | 
						x1.Name = "x1"
 | 
				
			||||||
	x2 := testPkg
 | 
						x2 := testPkg
 | 
				
			||||||
	x2.Name = "x2"
 | 
						x2.Name = "x2"
 | 
				
			||||||
	x2.Provides.Val = append(x2.Provides.Val, "x")
 | 
						x2.Provides = append(x2.Provides, "x")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err := database.InsertPackage(ctx, x1)
 | 
						err := database.InsertPackage(ctx, x1)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
@@ -239,13 +228,24 @@ func TestJsonArrayContains(t *testing.T) {
 | 
				
			|||||||
		t.Errorf("Expected no error, got %s", err)
 | 
							t.Errorf("Expected no error, got %s", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var dbPkg db.Package
 | 
						pkgs, err := database.GetPkgs(ctx, "name = 'x2'")
 | 
				
			||||||
	err = database.GetConn().Get(&dbPkg, "SELECT * FROM pkgs WHERE json_array_contains(provides, 'x');")
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatalf("Expected no error, got %s", err)
 | 
							t.Fatalf("Expected no error, got %s", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if dbPkg.Name != "x2" {
 | 
						if len(pkgs) != 1 || pkgs[0].Name != "x2" {
 | 
				
			||||||
		t.Errorf("Expected x2 package, got %s", dbPkg.Name)
 | 
							t.Errorf("Expected x2 package, got %v", pkgs)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Verify the provides field contains 'x'
 | 
				
			||||||
 | 
						found := false
 | 
				
			||||||
 | 
						for _, p := range pkgs[0].Provides {
 | 
				
			||||||
 | 
							if p == "x" {
 | 
				
			||||||
 | 
								found = true
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if !found {
 | 
				
			||||||
 | 
							t.Errorf("Expected provides to contain 'x'")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,80 +0,0 @@
 | 
				
			|||||||
// ALR - Any Linux Repository
 | 
					 | 
				
			||||||
// Copyright (C) 2025 Евгений Храмов
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// This program is free software: you can redistribute it and/or modify
 | 
					 | 
				
			||||||
// it under the terms of the GNU General Public License as published by
 | 
					 | 
				
			||||||
// the Free Software Foundation, either version 3 of the License, or
 | 
					 | 
				
			||||||
// (at your option) any later version.
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// This program is distributed in the hope that it will be useful,
 | 
					 | 
				
			||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					 | 
				
			||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					 | 
				
			||||||
// GNU General Public License for more details.
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// You should have received a copy of the GNU General Public License
 | 
					 | 
				
			||||||
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package db
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"database/sql"
 | 
					 | 
				
			||||||
	"database/sql/driver"
 | 
					 | 
				
			||||||
	"encoding/json"
 | 
					 | 
				
			||||||
	"errors"
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// JSON represents a JSON value in the database
 | 
					 | 
				
			||||||
type JSON[T any] struct {
 | 
					 | 
				
			||||||
	Val T
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// NewJSON creates a new database JSON value
 | 
					 | 
				
			||||||
func NewJSON[T any](v T) JSON[T] {
 | 
					 | 
				
			||||||
	return JSON[T]{Val: v}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (s *JSON[T]) Scan(val any) error {
 | 
					 | 
				
			||||||
	if val == nil {
 | 
					 | 
				
			||||||
		return nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	switch val := val.(type) {
 | 
					 | 
				
			||||||
	case string:
 | 
					 | 
				
			||||||
		err := json.Unmarshal([]byte(val), &s.Val)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	case sql.NullString:
 | 
					 | 
				
			||||||
		if val.Valid {
 | 
					 | 
				
			||||||
			err := json.Unmarshal([]byte(val.String), &s.Val)
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				return err
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		return errors.New("sqlite json types must be strings")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (s JSON[T]) Value() (driver.Value, error) {
 | 
					 | 
				
			||||||
	data, err := json.Marshal(s.Val)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return string(data), nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (s JSON[T]) MarshalYAML() (any, error) {
 | 
					 | 
				
			||||||
	return s.Val, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (s JSON[T]) String() string {
 | 
					 | 
				
			||||||
	return fmt.Sprint(s.Val)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (s JSON[T]) GoString() string {
 | 
					 | 
				
			||||||
	return fmt.Sprintf("%#v", s.Val)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
// ALR - Any Linux Repository
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
// Copyright (C) 2025 Евгений Храмов
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// This program is free software: you can redistribute it and/or modify
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
// it under the terms of the GNU General Public License as published by
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,8 +1,8 @@
 | 
				
			|||||||
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
 | 
					// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
 | 
				
			||||||
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
 | 
					// It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// ALR - Any Linux Repository
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
// Copyright (C) 2025 Евгений Храмов
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// This program is free software: you can redistribute it and/or modify
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
// it under the terms of the GNU General Public License as published by
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
@@ -1,8 +1,8 @@
 | 
				
			|||||||
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
 | 
					// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
 | 
				
			||||||
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
 | 
					// It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// ALR - Any Linux Repository
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
// Copyright (C) 2025 Евгений Храмов
 | 
					// Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// This program is free software: you can redistribute it and/or modify
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
// it under the terms of the GNU General Public License as published by
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
@@ -1,8 +1,8 @@
 | 
				
			|||||||
# This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
 | 
					# This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
 | 
				
			||||||
# It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
 | 
					# It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# ALR - Any Linux Repository
 | 
					# ALR - Any Linux Repository
 | 
				
			||||||
# Copyright (C) 2025 Евгений Храмов
 | 
					# Copyright (C) 2025 The ALR Authors
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is free software: you can redistribute it and/or modify
 | 
					# This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
# it under the terms of the GNU General Public License as published by
 | 
					# it under the terms of the GNU General Public License as published by
 | 
				
			||||||
@@ -17,7 +17,7 @@
 | 
				
			|||||||
# You should have received a copy of the GNU General Public License
 | 
					# You should have received a copy of the GNU General Public License
 | 
				
			||||||
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
name='{{.Info.Name | tolower}}'
 | 
					name='python3-{{.Info.Name | tolower}}'
 | 
				
			||||||
version='{{.Info.Version}}'
 | 
					version='{{.Info.Version}}'
 | 
				
			||||||
release='1'
 | 
					release='1'
 | 
				
			||||||
desc='{{.Info.Summary}}'
 | 
					desc='{{.Info.Summary}}'
 | 
				
			||||||
@@ -41,10 +41,15 @@ checksums=('blake2b-256:{{.SourceURL.Digests.blake2b_256}}')
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
build() {
 | 
					build() {
 | 
				
			||||||
	cd "$srcdir/{{.Info.Name}}-${version}"
 | 
						cd "$srcdir/{{.Info.Name}}-${version}"
 | 
				
			||||||
  python3 -m build
 | 
					  python -m build --wheel --no-isolation
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package() {
 | 
					package() {
 | 
				
			||||||
	cd "$srcdir/{{.Info.Name}}-${version}"
 | 
						cd "$srcdir/{{.Info.Name}}-${version}"
 | 
				
			||||||
	pip install --root="${pkgdir}/" . --no-deps --disable-pip-version-check
 | 
						pip install --root="${pkgdir}/" . --no-deps --ignore-installed --disable-pip-version-check
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					files() {
 | 
				
			||||||
 | 
					  printf '"%s" ' ./usr/local/lib/python3.*/site-packages/{{.Info.Name | tolower}}/*
 | 
				
			||||||
 | 
					  printf '"%s" ' ./usr/local/lib/python3.*/site-packages/{{.Info.Name | tolower}}-${version}.dist-info/*
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user