Compare commits
	
		
			162 Commits
		
	
	
		
			v0.0.2
			...
			a785df1ec6
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| a785df1ec6 | |||
| 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 | |||
| 469a05105a | |||
| 70aca61750 | |||
| 4b35f5e4e6 | |||
| 7770675a8d | |||
| bd79d56776 | |||
| fff8b777fe | |||
| 6bee268ea9 | |||
| 4b53e819d8 | |||
| 6c0e8aeb3f | |||
| cbc6b9f452 | |||
| c705c25613 | |||
| 8f4b021a93 | |||
| 5e7d4033e4 | |||
| 4ac2432770 | |||
| 3c37310f0d | |||
| d300ab197b | |||
| eb2cc3c1e6 | |||
| 7a3acfe5c1 | |||
| 9cf8af08ab | |||
| 86940e8962 | |||
| db244204c7 | |||
| 9cb0a5e9ad | |||
| 1a57ccdb83 | |||
| 615cd83fb7 | |||
| 27e2f54653 | |||
| af57165c89 | |||
| 3770c82240 | |||
| 2dff463303 | |||
| 9085e38454 | |||
| a7d016abc9 | |||
| 4a5cca2d0f | |||
| 71000fd3cd | |||
| 71968bbe13 | |||
| 29c1a31066 | |||
| 8f94b61a0e | |||
| ae8e2d2807 | |||
| 0fa288b8a2 | |||
| dcac0b9ee5 | |||
| 4e6e1f524a | |||
| 47a9b9a96c | |||
| 9bb14312bd | |||
| 88b8d2fbf3 | |||
| 04523775f1 | |||
| adc4a42800 | |||
| 81651af20d | |||
| f04ebbaf14 | |||
| be1a137eab | |||
| 719a5b7fe7 | |||
| e05bb07f23 | |||
| ec053f7e6a | |||
| ad1696d507 | |||
| a57602a278 | |||
| 606cd5473a | |||
| d2bcb4e345 | |||
| 1fcb88976c | |||
| 55feea9b25 | |||
| 0d1db212e1 | |||
| 99ec48c4c6 | |||
| d201aae6e0 | |||
| 4463a32ae7 | |||
| 52fd4a5a00 | |||
| c437346957 | |||
| bf1fc0d878 | |||
| c3f879b379 | |||
| aa90dfa983 | |||
| bba1ed52c5 | |||
| dc1fac29d5 | |||
| 99857efb01 | |||
| 19bb87981c | |||
| 1c78adcca1 | |||
| a98bd44305 | |||
| 3deb6c9455 | |||
| 981f49587b | |||
| 35656d63a1 | |||
| 6410f7547b | |||
| 53e783df31 | |||
| fcc9ef5474 | |||
| f6ba4a1c26 | |||
| b5bf6ab61d | |||
| 18e90e4afc | |||
| a09863dfcb | |||
| fd643ea6cd | |||
| 309ecf784f | |||
| 30f95a4cbf | |||
| b9bf908007 | |||
| a6076b1253 | |||
| ac35b4d71d | |||
| 945f920654 | |||
| 84ac2377fb | |||
| de1db25202 | |||
| 2d6504b329 | |||
| 4ca557402a | |||
| e497d41030 | |||
| d46414a67c | |||
| 29e2f85eeb | |||
| c9c872abbc | |||
| fb93864d09 | |||
| 9fcd618a83 | |||
| 1fb9c6b574 | |||
| fb5c875713 | |||
| 3f428ab7b5 | |||
| 5b7af1f6b5 | |||
| 3224d7c6e4 | |||
| e1829c4824 | |||
| 12d83f2015 | |||
| 6bc6bfdcd9 | |||
| eeb25c239b | |||
| 91937a1fc5 | |||
| e827fb8049 | |||
| a13acc5ed0 | |||
| 52d3ab7791 | |||
| a345a24b95 | |||
| 5d1d3d7c45 | |||
| a711edbcc0 | |||
| d5636e8094 | |||
| 5d17875813 | |||
| 41eec2fc98 | |||
| 1273aeae39 | |||
| 49785d4dc8 | |||
| 1890311d11 | |||
| eb1c1a1d8c | |||
| c105cf2cbb | |||
| 94048184c1 | |||
| 3f1c1b6795 | 
							
								
								
									
										49
									
								
								.gitea/workflows/e2e-tests.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								.gitea/workflows/e2e-tests.yaml
									
									
									
									
									
										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/>. | ||||||
|  |  | ||||||
|  | name: E2E | ||||||
|  |  | ||||||
|  | on: | ||||||
|  |   push: | ||||||
|  |     branches: [ main ] | ||||||
|  |   pull_request: | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 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 | ||||||
|  |        | ||||||
|  |       - name: Set up Go | ||||||
|  |         uses: https://github.com/actions/setup-go@v5 | ||||||
|  |         with: | ||||||
|  |           go-version: '1.24' | ||||||
|  |  | ||||||
|  |       - name: Start Podman service | ||||||
|  |         run: nohup podman system service -t 0 unix:/tmp/podman.sock & | ||||||
|  |  | ||||||
|  |       - name: Run E2E tests | ||||||
|  |         env: | ||||||
|  |           DOCKER_HOST: unix:/tmp/podman.sock | ||||||
|  |           IGNORE_ROOT_CHECK: 1 | ||||||
|  |         run: | | ||||||
|  |           make e2e-test | ||||||
							
								
								
									
										49
									
								
								.gitea/workflows/pre-commit.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								.gitea/workflows/pre-commit.yaml
									
									
									
									
									
										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/>. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | name: Pre-commit | ||||||
|  |  | ||||||
|  | on: | ||||||
|  |   push: | ||||||
|  |     branches: [ main ] | ||||||
|  |   pull_request: | ||||||
|  |  | ||||||
|  |  | ||||||
|  | jobs: | ||||||
|  |   pre-commit: | ||||||
|  |     runs-on: 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 | ||||||
							
								
								
									
										6
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -5,3 +5,9 @@ | |||||||
| /internal/config/version.txt | /internal/config/version.txt | ||||||
| .fleet | .fleet | ||||||
| .idea | .idea | ||||||
|  | .gigaide | ||||||
|  |  | ||||||
|  | *.out | ||||||
|  |  | ||||||
|  | e2e-tests/alr | ||||||
|  | commit_msg.txt | ||||||
							
								
								
									
										50
									
								
								.golangci.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								.golangci.yml
									
									
									
									
									
										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/>. | ||||||
|  |  | ||||||
|  | run: | ||||||
|  |   timeout: 5m | ||||||
|  |  | ||||||
|  | linters-settings: | ||||||
|  |   goimports: | ||||||
|  |     local-prefixes: "gitea.plemya-x.ru/Plemya-x/ALR" | ||||||
|  |   gofmt: | ||||||
|  |     simplify: true | ||||||
|  |   gofumpt: | ||||||
|  |     extra-rules: true | ||||||
|  |  | ||||||
|  | linters: | ||||||
|  |   enable: | ||||||
|  |     - gofmt | ||||||
|  |     - gofumpt | ||||||
|  |     - goimports | ||||||
|  |     - gocritic | ||||||
|  |     - govet | ||||||
|  |     - staticcheck | ||||||
|  |     - unused | ||||||
|  |     - errcheck | ||||||
|  |     - typecheck | ||||||
|  | #    - forbidigo | ||||||
|  |  | ||||||
|  | issues: | ||||||
|  |   fix: true | ||||||
|  |   exclude-rules: | ||||||
|  |     - path: _test\.go | ||||||
|  |       linters: | ||||||
|  |         - errcheck | ||||||
|  |     # TODO: remove | ||||||
|  |     - linters: | ||||||
|  |         - staticcheck | ||||||
|  |       text: "SA1019: interp.ExecHandler" | ||||||
| @@ -1,99 +0,0 @@ | |||||||
| 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 |  | ||||||
							
								
								
									
										42
									
								
								.pre-commit-config.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								.pre-commit-config.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | |||||||
|  | # 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/>. | ||||||
|  |  | ||||||
|  | repos: | ||||||
|  |   - repo: local | ||||||
|  |     hooks: | ||||||
|  |       - id: test-coverage | ||||||
|  |         name: Run test coverage | ||||||
|  |         entry: make test-coverage | ||||||
|  |         language: system | ||||||
|  |         pass_filenames: false | ||||||
|  |  | ||||||
|  |       - id: fmt | ||||||
|  |         name: Format code | ||||||
|  |         entry: make fmt | ||||||
|  |         language: system | ||||||
|  |         pass_filenames: false | ||||||
|  |  | ||||||
|  |       - id: update-license | ||||||
|  |         name: Update license | ||||||
|  |         entry: make update-license | ||||||
|  |         language: system | ||||||
|  |         pass_filenames: false | ||||||
|  |  | ||||||
|  |       - id: i18n | ||||||
|  |         name: Update i18n | ||||||
|  |         entry: make i18n | ||||||
|  |         language: system | ||||||
|  |         pass_filenames: false | ||||||
| @@ -1,9 +0,0 @@ | |||||||
| platform: linux/amd64 |  | ||||||
| pipeline: |  | ||||||
|   release: |  | ||||||
|     image: goreleaser/goreleaser |  | ||||||
|     commands: |  | ||||||
|       - goreleaser release |  | ||||||
|     secrets: [ gitea_token, aur_key ] |  | ||||||
|     when: |  | ||||||
|       event: tag |  | ||||||
							
								
								
									
										41
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										41
									
								
								Makefile
									
									
									
									
									
								
							| @@ -1,6 +1,6 @@ | |||||||
| 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) | ||||||
| @@ -11,17 +11,22 @@ 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) | ||||||
|  |  | ||||||
|  | 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 | ||||||
|  | XGOTEXT_BIN := go run github.com/Tom5521/xgotext@v1.2.0 | ||||||
|  |  | ||||||
| .PHONY: build install clean clear uninstall check-no-root | .PHONY: build install clean clear uninstall check-no-root | ||||||
|  |  | ||||||
| build: check-no-root $(BIN) | build: check-no-root $(BIN) | ||||||
|  |  | ||||||
| export CGO_ENABLED := 0 | export CGO_ENABLED := 0 | ||||||
| $(BIN): | $(BIN): | ||||||
| 	go build -ldflags="-X 'gitea.plemya-x.ru/xpamych/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 | ||||||
|  |  | ||||||
| @@ -33,6 +38,7 @@ install: \ | |||||||
|  |  | ||||||
| $(INSTALED_BIN): $(BIN) | $(INSTALED_BIN): $(BIN) | ||||||
| 	install -Dm755 $< $@ | 	install -Dm755 $< $@ | ||||||
|  | 	setcap cap_setuid,cap_setgid+ep $(INSTALED_BIN) | ||||||
|  |  | ||||||
| $(INSTALLED_BASH_COMPLETION): $(BASH_COMPLETION) | $(INSTALLED_BASH_COMPLETION): $(BASH_COMPLETION) | ||||||
| 	install -Dm755 $< $@ | 	install -Dm755 $< $@ | ||||||
| @@ -48,3 +54,32 @@ uninstall: | |||||||
|  |  | ||||||
| clean clear: | clean clear: | ||||||
| 	rm -f $(BIN) | 	rm -f $(BIN) | ||||||
|  |  | ||||||
|  | OLD_FILES=$(shell cat old-files) | ||||||
|  | IGNORE_OLD_FILES := $(foreach file,$(shell cat old-files),-ignore $(file)) | ||||||
|  | update-license: | ||||||
|  | 	$(ADD_LICENSE_BIN) -v -f license-header-old-files.tmpl $(OLD_FILES) | ||||||
|  | 	$(ADD_LICENSE_BIN) -v -f license-header.tmpl $(IGNORE_OLD_FILES) . | ||||||
|  |  | ||||||
|  | fmt: | ||||||
|  | 	$(GOLANGCI_LINT_BIN) run --fix | ||||||
|  |  | ||||||
|  | i18n: | ||||||
|  | 	$(XGOTEXT_BIN)  --output ./internal/translations/default.pot | ||||||
|  | 	msguniq --use-first -o ./internal/translations/default.pot ./internal/translations/default.pot | ||||||
|  | 	msgmerge --backup=off -U ./internal/translations/po/ru/default.po ./internal/translations/default.pot | ||||||
|  | 	bash scripts/i18n-badge.sh | ||||||
|  |  | ||||||
|  | test-coverage: | ||||||
|  | 	go test ./... -v -coverpkg=./... -coverprofile=coverage.out | ||||||
|  | 	bash scripts/coverage-badge.sh | ||||||
|  |  | ||||||
|  | update-deps-cve: | ||||||
|  | 	bash scripts/update-deps-cve.sh | ||||||
|  |  | ||||||
|  | prepare-for-e2e-test: clean build | ||||||
|  | 	rm -f ./e2e-tests/alr | ||||||
|  | 	cp alr e2e-tests | ||||||
|  |  | ||||||
|  | e2e-test: prepare-for-e2e-test | ||||||
|  | 	go test -tags=e2e ./... | ||||||
							
								
								
									
										24
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								README.md
									
									
									
									
									
								
							| @@ -3,11 +3,13 @@ | |||||||
| </p> | </p> | ||||||
| <b></b> | <b></b> | ||||||
|  |  | ||||||
|  | [](https://goreportcard.com/report/gitea.plemya-x.ru/Plemya-x/ALR)   | ||||||
|  |  | ||||||
| # ALR (Any Linux Repository) | # ALR (Any Linux Repository) | ||||||
|  |  | ||||||
| ALR - это независимая от дистрибутива система сборки для Linux, аналогичная [AUR](https://wiki.archlinux.org/title/Arch_User_Repository). В настоящее время она находится в стадии бета-тестирования. Исправлено большинство основных ошибок и добавлено большинство важных функций. alr готов к общему использованию, но все еще может время от времени ломаться или заменяться. | ALR - это независимая от дистрибутива система сборки для Linux (форк [LURE](https://github.com/lure-sh/lure), аналогичная [AUR](https://wiki.archlinux.org/title/Arch_User_Repository). В настоящее время она находится в стадии бета-тестирования. Исправлено большинство основных ошибок и добавлено большинство важных функций. ALR готов к общему использованию, но все еще может время от времени ломаться или изменяться. | ||||||
|  |  | ||||||
| ALR написан на чистом Go и после сборки не имеет зависимостей. Единственное, для повышения привилегий ALR требуется команда, такая как `sudo`, `doas` и т.д., а также поддерживаемый менеджер пакетов. В настоящее время ALR поддерживает `apt`, `pacman`, `apk`, `dnf`, `yum`, and `zypper`. Если в вашей системе существует поддерживаемый менеджер пакетов, он будет обнаружен и использован автоматически. | ALR написан на чистом Go и после сборки не имеет зависимостей. Для повышения привилегий ALR требуется команда, такая как `sudo`, `doas` и т.д., а также поддерживаемый менеджер пакетов. В настоящее время ALR поддерживает `apt`, `apt-get` `pacman`, `apk`, `dnf`, `yum`, and `zypper`. Если в вашей системе используется поддерживаемый менеджер пакетов, то он будет обнаружен и использован автоматически. | ||||||
|  |  | ||||||
| --- | --- | ||||||
|  |  | ||||||
| @@ -21,14 +23,14 @@ ALR написан на чистом Go и после сборки не имее | |||||||
| curl -fsSL plemya-x.ru/alr/install.sh | bash | curl -fsSL plemya-x.ru/alr/install.sh | bash | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| **ВАЖНО**: При этом скрипт будет загружен и запущен с <https://gitea.plemya-x.ru/xpamych/ALR/install>. Пожалуйста, просматривайте любые скрипты, которые вы скачиваете из Интернета (включая этот), прежде чем запускать их. | **ВАЖНО**: При этом скрипт будет загружен и запущен с <https://plemya-x.ru/alr/install.sh>. Пожалуйста, просматривайте любые скрипты, которые вы скачиваете из Интернета (включая этот), прежде чем запускать их. | ||||||
|  |  | ||||||
| ### Сборка из исходного кода | ### Сборка из исходного кода | ||||||
|  |  | ||||||
| Чтобы собрать ALR из исходного кода, вам понадобится версия Go 1.18 или новее. Как только Go будет установлен, клонируйте это репозиторий и запустите: | Чтобы собрать ALR из исходного кода, вам понадобится версия Go 1.18 или новее. Как только Go будет установлен, клонируйте это репозиторий и запустите: | ||||||
|  |  | ||||||
| ```shell | ```shell | ||||||
| make build | make build -B | ||||||
| sudo make install | sudo make install | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| @@ -42,20 +44,23 @@ ALR был создан потому, что упаковка программн | |||||||
|  |  | ||||||
| ## Документация | ## Документация | ||||||
|  |  | ||||||
| Документация по всем этим вопросам находится в [Wiki](https://gitea.plemya-x.ru/xpamych/ALR/wiki/Home). | Документация находится в [Wiki](https://disc.plemya-x.ru/c/alr/wiki-alr). | ||||||
|  |  | ||||||
| --- | --- | ||||||
|  |  | ||||||
| ## Репозитории | ## Репозитории | ||||||
|  |  | ||||||
| Репозитории alr - это git-хранилища, которые содержат каталог для каждого пакета с файлом `alr.sh` внутри. Файл `alr.sh` содержит все инструкции по сборке пакета и информацию о нем. Скрипты `alr.sh` аналогичны скриптам Aur PKGBUILD. Репозиторий [по-умолчанию](https://gitea.plemya-x.ru/xpamych/xpamych-alr-repo.git). | Репозитории 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 addrepo --name alr-repo --url https://gitea.plemya-x.ru/Plemya-x/alr-repo.git | ||||||
|  | ``` | ||||||
|  |  | ||||||
| --- | --- | ||||||
| ## Соцсети | ## Соцсети | ||||||
| VK - https://vk.com/plemya_kh | VK - https://vk.com/plemya_kh | ||||||
|  |  | ||||||
| Discord - https://discord.com/channels/817759634105827358/1261631565084233749 |  | ||||||
|  |  | ||||||
| Telegram - https://t.me/plemyakh | Telegram - https://t.me/plemyakh | ||||||
|  |  | ||||||
| ## Спасибы | ## Спасибы | ||||||
| @@ -68,3 +73,6 @@ Telegram - https://t.me/plemyakh | |||||||
| - <https://github.com/goreleaser/nfpm> | - <https://github.com/goreleaser/nfpm> | ||||||
| - <https://github.com/charmbracelet/bubbletea> | - <https://github.com/charmbracelet/bubbletea> | ||||||
| - <https://gitlab.com/cznic/sqlite> | - <https://gitlab.com/cznic/sqlite> | ||||||
|  |  | ||||||
|  | Благодарим за активное участие в развитии проекта: | ||||||
|  | - Maks1mS <maxim@slipenko.com> | ||||||
							
								
								
									
										17
									
								
								assets/coverage-badge.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								assets/coverage-badge.svg
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | |||||||
|  | <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="109" height="20"> | ||||||
|  |     <linearGradient id="smooth" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/> | ||||||
|  |     <stop offset="1" stop-opacity=".1"/></linearGradient> | ||||||
|  |     <mask id="round"> | ||||||
|  |         <rect width="109" height="20" rx="3" fill="#fff"/> | ||||||
|  |     </mask> | ||||||
|  |     <g mask="url(#round)"><rect width="65" height="20" fill="#555"/> | ||||||
|  |         <rect x="65" width="44" height="20" fill="#e05d44"/> | ||||||
|  |         <rect width="109" height="20" fill="url(#smooth)"/> | ||||||
|  |     </g> | ||||||
|  |     <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="14">coverage</text> | ||||||
|  |         <text x="86" y="15" fill="#010101" fill-opacity=".3">17.0%</text> | ||||||
|  |         <text x="86" y="14">17.0%</text> | ||||||
|  |     </g> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 926 B | 
							
								
								
									
										18
									
								
								assets/i18n-ru-badge.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								assets/i18n-ru-badge.svg
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | |||||||
|  | <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="129" height="20"> | ||||||
|  |     <linearGradient id="smooth" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/> | ||||||
|  |     <stop offset="1" stop-opacity=".1"/></linearGradient> | ||||||
|  |     <mask id="round"> | ||||||
|  |         <rect width="129" height="20" rx="3" fill="#fff"/> | ||||||
|  |     </mask> | ||||||
|  |     <g mask="url(#round)"> | ||||||
|  |         <rect width="75" height="20" fill="#555"/> | ||||||
|  |         <rect x="75" width="64" height="20" fill="#4c1"/> | ||||||
|  |         <rect width="129" height="20" fill="url(#smooth)"/> | ||||||
|  |     </g> | ||||||
|  |     <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="14">ru translate</text> | ||||||
|  |         <text x="100" y="15" fill="#010101" fill-opacity=".3">96.00%</text> | ||||||
|  |         <text x="100" y="14">96.00%</text> | ||||||
|  |     </g> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 940 B | 
							
								
								
									
										310
									
								
								build.go
									
									
									
									
									
								
							
							
						
						
									
										310
									
								
								build.go
									
									
									
									
									
								
							| @@ -1,100 +1,238 @@ | |||||||
| /* | // This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan. | ||||||
|  * ALR - Any Linux Repository | // It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors. | ||||||
|  * Copyright (C) 2024 Евгений Храмов | // | ||||||
|  * | // ALR - Any Linux Repository | ||||||
|  * This program is free software: you can redistribute it and/or modify | // Copyright (C) 2025 The ALR Authors | ||||||
|  * it under the terms of the GNU General Public License as published by | // | ||||||
|  * the Free Software Foundation, either version 3 of the License, or | // This program is free software: you can redistribute it and/or modify | ||||||
|  * (at your option) any later version. | // it under the terms of the GNU General Public License as published by | ||||||
|  * | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  * This program is distributed in the hope that it will be useful, | // (at your option) any later version. | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | // | ||||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | // This program is distributed in the hope that it will be useful, | ||||||
|  * GNU General Public License for more details. | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  * | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  * You should have received a copy of the GNU General Public License | // GNU General Public License for more details. | ||||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | // | ||||||
|  */ | // 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 | package main | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"log/slog" | ||||||
| 	"os" | 	"os" | ||||||
| 	"path/filepath" | 	"path/filepath" | ||||||
|  | 	"strings" | ||||||
|  |  | ||||||
|  | 	"github.com/leonelquinteros/gotext" | ||||||
| 	"github.com/urfave/cli/v2" | 	"github.com/urfave/cli/v2" | ||||||
| 	"plemya-x.ru/alr/internal/config" |  | ||||||
| 	"plemya-x.ru/alr/internal/osutils" | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils" | ||||||
| 	"plemya-x.ru/alr/internal/types" | 	appbuilder "gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils/app_builder" | ||||||
| 	"plemya-x.ru/alr/pkg/build" | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/osutils" | ||||||
| 	"plemya-x.ru/alr/pkg/loggerctx" | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/types" | ||||||
| 	"plemya-x.ru/alr/pkg/manager" | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/utils" | ||||||
| 	"plemya-x.ru/alr/pkg/repos" | 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/build" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| var buildCmd = &cli.Command{ | func BuildCmd() *cli.Command { | ||||||
| 	Name:  "build", | 	return &cli.Command{ | ||||||
| 	Usage: "Build a local package", | 		Name:  "build", | ||||||
| 	Flags: []cli.Flag{ | 		Usage: gotext.Get("Build a local package"), | ||||||
| 		&cli.StringFlag{ | 		Flags: []cli.Flag{ | ||||||
| 			Name:    "script", | 			&cli.StringFlag{ | ||||||
| 			Aliases: []string{"s"}, | 				Name:    "script", | ||||||
| 			Value:   "alr.sh", | 				Aliases: []string{"s"}, | ||||||
| 			Usage:   "Path to the build script", | 				Value:   "alr.sh", | ||||||
|  | 				Usage:   gotext.Get("Path to the build script"), | ||||||
|  | 			}, | ||||||
|  | 			&cli.StringFlag{ | ||||||
|  | 				Name:    "subpackage", | ||||||
|  | 				Aliases: []string{"sb"}, | ||||||
|  | 				Usage:   gotext.Get("Specify subpackage in script (for multi package script only)"), | ||||||
|  | 			}, | ||||||
|  | 			&cli.StringFlag{ | ||||||
|  | 				Name:    "package", | ||||||
|  | 				Aliases: []string{"p"}, | ||||||
|  | 				Usage:   gotext.Get("Name of the package to build and its repo (example: default/go-bin)"), | ||||||
|  | 			}, | ||||||
|  | 			&cli.BoolFlag{ | ||||||
|  | 				Name:    "clean", | ||||||
|  | 				Aliases: []string{"c"}, | ||||||
|  | 				Usage:   gotext.Get("Build package from scratch even if there's an already built package available"), | ||||||
|  | 			}, | ||||||
| 		}, | 		}, | ||||||
| 		&cli.StringFlag{ | 		Action: func(c *cli.Context) error { | ||||||
| 			Name:    "package", | 			if err := utils.EnuseIsPrivilegedGroupMember(); err != nil { | ||||||
| 			Aliases: []string{"p"}, | 				return err | ||||||
| 			Usage:   "Name of the package to build and its repo (example: default/go-bin)", |  | ||||||
| 		}, |  | ||||||
| 		&cli.BoolFlag{ |  | ||||||
| 			Name:    "clean", |  | ||||||
| 			Aliases: []string{"c"}, |  | ||||||
| 			Usage:   "Build package from scratch even if there's an already built package available", |  | ||||||
| 		}, |  | ||||||
| 	}, |  | ||||||
| 	Action: func(c *cli.Context) error { |  | ||||||
| 		ctx := c.Context |  | ||||||
| 		log := loggerctx.From(ctx) |  | ||||||
|  |  | ||||||
| 		script := c.String("script") |  | ||||||
| 		if c.String("package") != "" { |  | ||||||
| 			script = filepath.Join(config.GetPaths(ctx).RepoDir, c.String("package"), "alr.sh") |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		err := repos.Pull(ctx, config.Config(ctx).Repos) |  | ||||||
| 		if err != nil { |  | ||||||
| 			log.Fatal("Error pulling repositories").Err(err).Send() |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		mgr := manager.Detect() |  | ||||||
| 		if mgr == nil { |  | ||||||
| 			log.Fatal("Unable to detect a supported package manager on the system").Send() |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		pkgPaths, _, err := build.BuildPackage(ctx, types.BuildOpts{ |  | ||||||
| 			Script:      script, |  | ||||||
| 			Manager:     mgr, |  | ||||||
| 			Clean:       c.Bool("clean"), |  | ||||||
| 			Interactive: c.Bool("interactive"), |  | ||||||
| 		}) |  | ||||||
| 		if err != nil { |  | ||||||
| 			log.Fatal("Error building package").Err(err).Send() |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		wd, err := os.Getwd() |  | ||||||
| 		if err != nil { |  | ||||||
| 			log.Fatal("Error getting working directory").Err(err).Send() |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		for _, pkgPath := range pkgPaths { |  | ||||||
| 			name := filepath.Base(pkgPath) |  | ||||||
| 			err = osutils.Move(pkgPath, filepath.Join(wd, name)) |  | ||||||
| 			if err != nil { |  | ||||||
| 				log.Fatal("Error moving the package").Err(err).Send() |  | ||||||
| 			} | 			} | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		return nil | 			wd, err := os.Getwd() | ||||||
| 	}, | 			if err != nil { | ||||||
|  | 				return cliutils.FormatCliExit(gotext.Get("Error getting working directory"), err) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			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 packages []string | ||||||
|  |  | ||||||
|  | 			var res *build.BuildResult | ||||||
|  |  | ||||||
|  | 			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 { | ||||||
|  | 			case c.IsSet("script"): | ||||||
|  | 				script, err = filepath.Abs(c.String("script")) | ||||||
|  | 				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"): | ||||||
|  | 				// TODO: handle multiple packages | ||||||
|  | 				packageInput := c.String("package") | ||||||
|  |  | ||||||
|  | 				arr := strings.Split(packageInput, "/") | ||||||
|  | 				var packageSearch string | ||||||
|  | 				if len(arr) == 2 { | ||||||
|  | 					packageSearch = arr[1] | ||||||
|  | 				} else { | ||||||
|  | 					packageSearch = arr[0] | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				pkgs, _, err := deps.Repos.FindPkgs(ctx, []string{packageSearch}) | ||||||
|  | 				if err != nil { | ||||||
|  | 					return cliutils.FormatCliExit("failed to find pkgs", err) | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				pkg := cliutils.FlattenPkgs(ctx, pkgs, "build", c.Bool("interactive")) | ||||||
|  |  | ||||||
|  | 				if len(pkg) < 1 { | ||||||
|  | 					return cliutils.FormatCliExit(gotext.Get("Package not found"), nil) | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				if pkg[0].BasePkgName != "" { | ||||||
|  | 					packages = append(packages, pkg[0].Name) | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				dbArgs = &build.BuildPackageFromDbArgs{ | ||||||
|  | 					Package:   &pkg[0], | ||||||
|  | 					Packages:  packages, | ||||||
|  | 					BuildArgs: *buildArgs, | ||||||
|  | 				} | ||||||
|  | 			default: | ||||||
|  | 				return cliutils.FormatCliExit(gotext.Get("Nothing to build"), nil) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if scriptArgs != nil { | ||||||
|  | 				scriptFile := filepath.Base(scriptArgs.Script) | ||||||
|  | 				newScriptDir, scriptDirCleanup, err := Mount(filepath.Dir(scriptArgs.Script)) | ||||||
|  | 				if err != nil { | ||||||
|  | 					return err | ||||||
|  | 				} | ||||||
|  | 				defer scriptDirCleanup() | ||||||
|  | 				scriptArgs.Script = filepath.Join(newScriptDir, scriptFile) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if err := utils.ExitIfCantDropCapsToAlrUser(); err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			installer, installerClose, err := build.GetSafeInstaller() | ||||||
|  | 			if err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  | 			defer installerClose() | ||||||
|  |  | ||||||
|  | 			if err := utils.ExitIfCantSetNoNewPrivs(); err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			scripter, scripterClose, err := build.GetSafeScriptExecutor() | ||||||
|  | 			if err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  | 			defer scripterClose() | ||||||
|  |  | ||||||
|  | 			builder, err := build.NewMainBuilder( | ||||||
|  | 				deps.Cfg, | ||||||
|  | 				deps.Manager, | ||||||
|  | 				deps.Repos, | ||||||
|  | 				scripter, | ||||||
|  | 				installer, | ||||||
|  | 			) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if scriptArgs != nil { | ||||||
|  | 				res, err = builder.BuildPackageFromScript( | ||||||
|  | 					ctx, | ||||||
|  | 					scriptArgs, | ||||||
|  | 				) | ||||||
|  | 			} else if dbArgs != nil { | ||||||
|  | 				res, err = builder.BuildPackageFromDb( | ||||||
|  | 					ctx, | ||||||
|  | 					dbArgs, | ||||||
|  | 				) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if err != nil { | ||||||
|  | 				return cliutils.FormatCliExit(gotext.Get("Error building package"), err) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			for _, pkgPath := range res.PackagePaths { | ||||||
|  | 				name := filepath.Base(pkgPath) | ||||||
|  | 				err = osutils.Move(pkgPath, filepath.Join(wd, name)) | ||||||
|  | 				if err != nil { | ||||||
|  | 					return cliutils.FormatCliExit(gotext.Get("Error moving the package"), err) | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			slog.Info(gotext.Get("Done")) | ||||||
|  |  | ||||||
|  | 			return nil | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										72
									
								
								e2e-tests/addrepo_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								e2e-tests/addrepo_test.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/>. | ||||||
|  |  | ||||||
|  | //go:build e2e | ||||||
|  |  | ||||||
|  | package e2etests_test | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"testing" | ||||||
|  |  | ||||||
|  | 	"github.com/efficientgo/e2e" | ||||||
|  | 	"github.com/stretchr/testify/assert" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestE2EAlrAddRepo(t *testing.T) { | ||||||
|  | 	dockerMultipleRun( | ||||||
|  | 		t, | ||||||
|  | 		"add-repo-remove-repo", | ||||||
|  | 		COMMON_SYSTEMS, | ||||||
|  | 		func(t *testing.T, r e2e.Runnable) { | ||||||
|  | 			err := r.Exec(e2e.NewCommand( | ||||||
|  | 				"sudo", | ||||||
|  | 				"alr", | ||||||
|  | 				"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", | ||||||
|  | 				"cat /etc/alr/alr.toml", | ||||||
|  | 			)) | ||||||
|  | 			assert.NoError(t, err) | ||||||
|  |  | ||||||
|  | 			err = r.Exec(e2e.NewCommand( | ||||||
|  | 				"sudo", | ||||||
|  | 				"alr", | ||||||
|  | 				"removerepo", | ||||||
|  | 				"--name", | ||||||
|  | 				"alr-repo", | ||||||
|  | 			)) | ||||||
|  | 			assert.NoError(t, err) | ||||||
|  |  | ||||||
|  | 			var buf bytes.Buffer | ||||||
|  | 			err = r.Exec(e2e.NewCommand( | ||||||
|  | 				"bash", | ||||||
|  | 				"-c", | ||||||
|  | 				"cat /etc/alr/alr.toml", | ||||||
|  | 			), e2e.WithExecOptionStdout(&buf)) | ||||||
|  | 			assert.NoError(t, err) | ||||||
|  | 			assert.Contains(t, buf.String(), "rootCmd") | ||||||
|  | 		}, | ||||||
|  | 	) | ||||||
|  | } | ||||||
							
								
								
									
										40
									
								
								e2e-tests/bash_completion_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								e2e-tests/bash_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" | ||||||
|  |  | ||||||
|  | 	"github.com/alecthomas/assert/v2" | ||||||
|  | 	"github.com/efficientgo/e2e" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestE2EBashCompletion(t *testing.T) { | ||||||
|  | 	dockerMultipleRun( | ||||||
|  | 		t, | ||||||
|  | 		"bash-completion", | ||||||
|  | 		COMMON_SYSTEMS, | ||||||
|  | 		func(t *testing.T, r e2e.Runnable) { | ||||||
|  | 			err := r.Exec(e2e.NewCommand( | ||||||
|  | 				"alr", "install", "--generate-bash-completion", | ||||||
|  | 			)) | ||||||
|  | 			assert.NoError(t, err) | ||||||
|  | 		}, | ||||||
|  | 	) | ||||||
|  | } | ||||||
							
								
								
									
										197
									
								
								e2e-tests/common_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										197
									
								
								e2e-tests/common_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,197 @@ | |||||||
|  | // 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 ( | ||||||
|  | 	"crypto/sha256" | ||||||
|  | 	"encoding/hex" | ||||||
|  | 	"fmt" | ||||||
|  | 	"io" | ||||||
|  | 	"log" | ||||||
|  | 	"os" | ||||||
|  | 	"os/exec" | ||||||
|  | 	"testing" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
|  | 	"github.com/efficientgo/e2e" | ||||||
|  | 	"github.com/stretchr/testify/assert" | ||||||
|  |  | ||||||
|  | 	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{ | ||||||
|  | 	"ubuntu-24.04", | ||||||
|  | 	"alt-sisyphus", | ||||||
|  | 	"fedora-41", | ||||||
|  | 	// "archlinux", | ||||||
|  | 	// "alpine", | ||||||
|  | 	// "opensuse-leap", | ||||||
|  | 	// "redos-8", | ||||||
|  | } | ||||||
|  |  | ||||||
|  | var AUTOREQ_AUTOPROV_SYSTEMS []string = []string{ | ||||||
|  | 	// "alt-sisyphus", | ||||||
|  | 	"fedora-41", | ||||||
|  | } | ||||||
|  |  | ||||||
|  | var RPM_SYSTEMS []string = []string{ | ||||||
|  | 	"fedora-41", | ||||||
|  | } | ||||||
|  |  | ||||||
|  | var COMMON_SYSTEMS []string = []string{ | ||||||
|  | 	"ubuntu-24.04", | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func init() { | ||||||
|  | 	for _, id := range ALL_SYSTEMS { | ||||||
|  | 		buildAlrTestImage(id) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func buildAlrTestImage(id string) { | ||||||
|  | 	cmd := exec.Command( | ||||||
|  | 		"docker", | ||||||
|  | 		"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)) { | ||||||
|  | 	t.Run(name, func(t *testing.T) { | ||||||
|  | 		for _, id := range ids { | ||||||
|  | 			t.Run(id, func(t *testing.T) { | ||||||
|  | 				t.Parallel() | ||||||
|  | 				dockerName := fmt.Sprintf("alr-test-%s-%s", name, id) | ||||||
|  | 				hash := sha256.New() | ||||||
|  | 				hash.Write([]byte(dockerName)) | ||||||
|  | 				hashSum := hash.Sum(nil) | ||||||
|  | 				hashString := hex.EncodeToString(hashSum) | ||||||
|  | 				truncatedHash := hashString[:8] | ||||||
|  | 				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) | ||||||
|  | 			}) | ||||||
|  | 		} | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func simpleExec(t *testing.T, r e2e.Runnable, cmd string, args ...string) { | ||||||
|  | 	err := r.Exec(e2e.NewCommand(cmd, args...)) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | 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) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const REPO_FOR_E2E_TESTS = "https://gitea.plemya-x.ru/Maks1mS/repo-for-tests.git" | ||||||
							
								
								
									
										43
									
								
								e2e-tests/fix_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								e2e-tests/fix_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" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
|  | 	"github.com/efficientgo/e2e" | ||||||
|  | 	expect "github.com/tailscale/goexpect" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestE2EAlrFix(t *testing.T) { | ||||||
|  | 	dockerMultipleRun( | ||||||
|  | 		t, | ||||||
|  | 		"run-fix", | ||||||
|  | 		COMMON_SYSTEMS, | ||||||
|  | 		func(t *testing.T, r e2e.Runnable) { | ||||||
|  | 			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$`}, | ||||||
|  | 			}) | ||||||
|  | 		}, | ||||||
|  | 	) | ||||||
|  | } | ||||||
							
								
								
									
										56
									
								
								e2e-tests/group_and_summary_field_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								e2e-tests/group_and_summary_field_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,56 @@ | |||||||
|  | // 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" | ||||||
|  |  | ||||||
|  | 	"github.com/alecthomas/assert/v2" | ||||||
|  | 	"github.com/efficientgo/e2e" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestE2EGroupAndSummaryField(t *testing.T) { | ||||||
|  | 	dockerMultipleRun( | ||||||
|  | 		t, | ||||||
|  | 		"group-and-summary-field", | ||||||
|  | 		RPM_SYSTEMS, | ||||||
|  | 		func(t *testing.T, r e2e.Runnable) { | ||||||
|  | 			err := r.Exec(e2e.NewCommand( | ||||||
|  | 				"sudo", | ||||||
|  | 				"alr", | ||||||
|  | 				"addrepo", | ||||||
|  | 				"--name", | ||||||
|  | 				"alr-repo", | ||||||
|  | 				"--url", | ||||||
|  | 				"https://gitea.plemya-x.ru/Maks1mS/repo-for-tests.git", | ||||||
|  | 			)) | ||||||
|  | 			assert.NoError(t, err) | ||||||
|  |  | ||||||
|  | 			err = r.Exec(e2e.NewCommand( | ||||||
|  | 				"sh", "-c", "alr search --name test-group-and-summary --format \"{{.Group}}\" | grep ^System/Base$", | ||||||
|  | 			)) | ||||||
|  | 			assert.NoError(t, err) | ||||||
|  |  | ||||||
|  | 			err = r.Exec(e2e.NewCommand( | ||||||
|  | 				"sh", "-c", "alr search --name test-group-and-summary --format \"{{.Summary}}\" | grep \"^Custom summary$\"", | ||||||
|  | 			)) | ||||||
|  | 			assert.NoError(t, err) | ||||||
|  | 		}, | ||||||
|  | 	) | ||||||
|  | } | ||||||
							
								
								
									
										4
									
								
								e2e-tests/images/Dockerfile.alpine
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								e2e-tests/images/Dockerfile.alpine
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | |||||||
|  | FROM alpine:latest | ||||||
|  | RUN adduser -s /bin/bash alr-user | ||||||
|  | USER alr-user | ||||||
|  | ENTRYPOINT ["tail", "-f", "/dev/null"] | ||||||
							
								
								
									
										6
									
								
								e2e-tests/images/Dockerfile.alt-sisyphus
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								e2e-tests/images/Dockerfile.alt-sisyphus
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | |||||||
|  | FROM registry.altlinux.org/sisyphus/alt:latest | ||||||
|  | RUN apt-get update && apt-get install -y ca-certificates rpm-build | ||||||
|  | RUN useradd -m -s /bin/bash alr-user | ||||||
|  | USER alr-user | ||||||
|  | WORKDIR /home/alr-user | ||||||
|  | ENTRYPOINT ["tail", "-f", "/dev/null"] | ||||||
							
								
								
									
										4
									
								
								e2e-tests/images/Dockerfile.archlinux
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								e2e-tests/images/Dockerfile.archlinux
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | |||||||
|  | FROM archlinux:latest | ||||||
|  | RUN useradd -m -s /bin/bash alr-user | ||||||
|  | USER alr-user | ||||||
|  | ENTRYPOINT ["tail", "-f", "/dev/null"] | ||||||
							
								
								
									
										18
									
								
								e2e-tests/images/Dockerfile.fedora-41
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								e2e-tests/images/Dockerfile.fedora-41
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | |||||||
|  | FROM fedora:41 | ||||||
|  | RUN dnf install -y ca-certificates sudo rpm-build bindfs | ||||||
|  | RUN <<EOF | ||||||
|  |     useradd -m -s /bin/bash -G wheel user | ||||||
|  |     echo "user ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.d/user | ||||||
|  |     chmod 0440 /etc/sudoers.d/user | ||||||
|  |  | ||||||
|  |     useradd -m -s /bin/bash alr | ||||||
|  |     mkdir -p /var/cache/alr /etc/alr | ||||||
|  |     chown alr:alr /var/cache/alr /etc/alr | ||||||
|  | EOF | ||||||
|  | COPY ./alr /usr/bin | ||||||
|  | RUN <<EOF | ||||||
|  |     setcap cap_setuid,cap_setgid+ep /usr/bin/alr | ||||||
|  | EOF | ||||||
|  | USER user | ||||||
|  | WORKDIR /home/user | ||||||
|  | ENTRYPOINT ["tail", "-f", "/dev/null"] | ||||||
							
								
								
									
										4
									
								
								e2e-tests/images/Dockerfile.opensuse-leap
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								e2e-tests/images/Dockerfile.opensuse-leap
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | |||||||
|  | FROM opensuse/leap:latest | ||||||
|  | RUN useradd -m -s /bin/bash alr-user | ||||||
|  | USER alr-user | ||||||
|  | ENTRYPOINT ["tail", "-f", "/dev/null"] | ||||||
							
								
								
									
										4
									
								
								e2e-tests/images/Dockerfile.redos-8
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								e2e-tests/images/Dockerfile.redos-8
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | |||||||
|  | FROM registry.red-soft.ru/ubi8/ubi:latest | ||||||
|  | RUN useradd -m -s /bin/bash alr-user | ||||||
|  | USER alr-user | ||||||
|  | ENTRYPOINT ["tail", "-f", "/dev/null"] | ||||||
							
								
								
									
										17
									
								
								e2e-tests/images/Dockerfile.ubuntu-24.04
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								e2e-tests/images/Dockerfile.ubuntu-24.04
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | |||||||
|  | FROM ubuntu:24.10 | ||||||
|  | RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates sudo libcap2-bin | ||||||
|  | RUN <<EOF | ||||||
|  |     useradd -m -s /bin/bash user | ||||||
|  |     echo "user ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.d/user | ||||||
|  |     chmod 0440 /etc/sudoers.d/user  | ||||||
|  |      | ||||||
|  |     useradd -m -s /bin/bash alr | ||||||
|  |     mkdir -p /var/cache/alr /etc/alr | ||||||
|  |     chown alr:alr /var/cache/alr /etc/alr | ||||||
|  | EOF | ||||||
|  | COPY ./alr /usr/bin | ||||||
|  | RUN <<EOF | ||||||
|  |     setcap cap_setuid,cap_setgid+ep /usr/bin/alr | ||||||
|  | EOF | ||||||
|  | USER user | ||||||
|  | ENTRYPOINT ["tail", "-f", "/dev/null"] | ||||||
							
								
								
									
										51
									
								
								e2e-tests/issue_32_interactive_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								e2e-tests/issue_32_interactive_test.go
									
									
									
									
									
										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/>. | ||||||
|  |  | ||||||
|  | //go:build e2e | ||||||
|  |  | ||||||
|  | package e2etests_test | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"testing" | ||||||
|  |  | ||||||
|  | 	"github.com/alecthomas/assert/v2" | ||||||
|  | 	"github.com/efficientgo/e2e" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestE2EIssue32Interactive(t *testing.T) { | ||||||
|  | 	dockerMultipleRun( | ||||||
|  | 		t, | ||||||
|  | 		"issue-32-interactive", | ||||||
|  | 		COMMON_SYSTEMS, | ||||||
|  | 		func(t *testing.T, r e2e.Runnable) { | ||||||
|  | 			assert.NoError(t, r.Exec(e2e.NewCommand( | ||||||
|  | 				"sudo", "alr", "--interactive=false", "remove", "ca-certificates", | ||||||
|  | 			))) | ||||||
|  |  | ||||||
|  | 			assert.NoError(t, r.Exec(e2e.NewCommand( | ||||||
|  | 				"sudo", "alr", "--interactive=false", "remove", "openssl", | ||||||
|  | 			))) | ||||||
|  |  | ||||||
|  | 			assert.NoError(t, r.Exec(e2e.NewCommand( | ||||||
|  | 				"alr", "fix", | ||||||
|  | 			))) | ||||||
|  |  | ||||||
|  | 			assert.NoError(t, r.Exec(e2e.NewCommand( | ||||||
|  | 				"sudo", "alr", "--interactive=false", "install", "ca-certificates", | ||||||
|  | 			))) | ||||||
|  | 		}, | ||||||
|  | 	) | ||||||
|  | } | ||||||
							
								
								
									
										81
									
								
								e2e-tests/issue_41_autoreq_skiplist_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								e2e-tests/issue_41_autoreq_skiplist_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,81 @@ | |||||||
|  | // 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" | ||||||
|  |  | ||||||
|  | 	"github.com/alecthomas/assert/v2" | ||||||
|  | 	"github.com/efficientgo/e2e" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestE2EIssue41AutoreqSkiplist(t *testing.T) { | ||||||
|  | 	dockerMultipleRun( | ||||||
|  | 		t, | ||||||
|  | 		"issue-41-autoreq-skiplist", | ||||||
|  | 		AUTOREQ_AUTOPROV_SYSTEMS, | ||||||
|  | 		func(t *testing.T, r e2e.Runnable) { | ||||||
|  | 			err := r.Exec(e2e.NewCommand( | ||||||
|  | 				"sudo", | ||||||
|  | 				"alr", | ||||||
|  | 				"addrepo", | ||||||
|  | 				"--name", | ||||||
|  | 				"alr-repo", | ||||||
|  | 				"--url", | ||||||
|  | 				"https://gitea.plemya-x.ru/Maks1mS/repo-for-tests.git", | ||||||
|  | 			)) | ||||||
|  | 			assert.NoError(t, err) | ||||||
|  |  | ||||||
|  | 			err = r.Exec(e2e.NewCommand( | ||||||
|  | 				"alr", | ||||||
|  | 				"ref", | ||||||
|  | 			)) | ||||||
|  | 			assert.NoError(t, err) | ||||||
|  |  | ||||||
|  | 			err = r.Exec(e2e.NewCommand( | ||||||
|  | 				"alr", | ||||||
|  | 				"build", | ||||||
|  | 				"-p", | ||||||
|  | 				"alr-repo/test-autoreq-autoprov", | ||||||
|  | 			)) | ||||||
|  | 			assert.NoError(t, err) | ||||||
|  |  | ||||||
|  | 			err = r.Exec(e2e.NewCommand( | ||||||
|  | 				"sh", | ||||||
|  | 				"-c", | ||||||
|  | 				"rpm -qp --requires *.rpm | grep \"^/bin/sh$\"", | ||||||
|  | 			)) | ||||||
|  | 			assert.NoError(t, err) | ||||||
|  |  | ||||||
|  | 			err = r.Exec(e2e.NewCommand( | ||||||
|  | 				"sh", | ||||||
|  | 				"-c", | ||||||
|  | 				"rpm -qp --requires *.rpm | grep \"^/bin/bash$\"", | ||||||
|  | 			)) | ||||||
|  | 			assert.Error(t, err) | ||||||
|  |  | ||||||
|  | 			err = r.Exec(e2e.NewCommand( | ||||||
|  | 				"sh", | ||||||
|  | 				"-c", | ||||||
|  | 				"rpm -qp --requires *.rpm | grep \"^/bin/zsh$\"", | ||||||
|  | 			)) | ||||||
|  | 			assert.Error(t, err) | ||||||
|  | 		}, | ||||||
|  | 	) | ||||||
|  | } | ||||||
							
								
								
									
										56
									
								
								e2e-tests/issue_50_install_multiple_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								e2e-tests/issue_50_install_multiple_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,56 @@ | |||||||
|  | // 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" | ||||||
|  |  | ||||||
|  | 	"github.com/alecthomas/assert/v2" | ||||||
|  | 	"github.com/efficientgo/e2e" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestE2EIssue50InstallMultiple(t *testing.T) { | ||||||
|  | 	dockerMultipleRun( | ||||||
|  | 		t, | ||||||
|  | 		"issue-50-install-multiple", | ||||||
|  | 		COMMON_SYSTEMS, | ||||||
|  | 		func(t *testing.T, r e2e.Runnable) { | ||||||
|  | 			err := r.Exec(e2e.NewCommand( | ||||||
|  | 				"sudo", | ||||||
|  | 				"alr", | ||||||
|  | 				"addrepo", | ||||||
|  | 				"--name", | ||||||
|  | 				"alr-repo", | ||||||
|  | 				"--url", | ||||||
|  | 				"https://gitea.plemya-x.ru/Maks1mS/repo-for-tests.git", | ||||||
|  | 			)) | ||||||
|  | 			assert.NoError(t, err) | ||||||
|  |  | ||||||
|  | 			err = r.Exec(e2e.NewCommand( | ||||||
|  | 				"sudo", "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) | ||||||
|  | 		}, | ||||||
|  | 	) | ||||||
|  | } | ||||||
							
								
								
									
										53
									
								
								e2e-tests/issue_53_lc_all_c_info_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								e2e-tests/issue_53_lc_all_c_info_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,53 @@ | |||||||
|  | // 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" | ||||||
|  |  | ||||||
|  | 	"github.com/alecthomas/assert/v2" | ||||||
|  | 	"github.com/efficientgo/e2e" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestE2EIssue53LcAllCInfo(t *testing.T) { | ||||||
|  | 	dockerMultipleRun( | ||||||
|  | 		t, | ||||||
|  | 		"issue-53-lc-all-c-info", | ||||||
|  | 		COMMON_SYSTEMS, | ||||||
|  | 		func(t *testing.T, r e2e.Runnable) { | ||||||
|  | 			err := r.Exec(e2e.NewCommand( | ||||||
|  | 				"sudo", | ||||||
|  | 				"alr", | ||||||
|  | 				"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) | ||||||
|  | 		}, | ||||||
|  | 	) | ||||||
|  | } | ||||||
							
								
								
									
										58
									
								
								e2e-tests/issue_59_rm_completion_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								e2e-tests/issue_59_rm_completion_test.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/>. | ||||||
|  |  | ||||||
|  | //go:build e2e | ||||||
|  |  | ||||||
|  | package e2etests_test | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"testing" | ||||||
|  |  | ||||||
|  | 	"github.com/alecthomas/assert/v2" | ||||||
|  | 	"github.com/efficientgo/e2e" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestE2EIssue59RmCompletion(t *testing.T) { | ||||||
|  | 	dockerMultipleRun( | ||||||
|  | 		t, | ||||||
|  | 		"issue-59-rm-completion", | ||||||
|  | 		COMMON_SYSTEMS, | ||||||
|  | 		func(t *testing.T, r e2e.Runnable) { | ||||||
|  | 			err := r.Exec(e2e.NewCommand( | ||||||
|  | 				"sudo", | ||||||
|  | 				"alr", | ||||||
|  | 				"addrepo", | ||||||
|  | 				"--name", | ||||||
|  | 				"alr-repo", | ||||||
|  | 				"--url", | ||||||
|  | 				"https://gitea.plemya-x.ru/Maks1mS/repo-for-tests.git", | ||||||
|  | 			)) | ||||||
|  | 			assert.NoError(t, err) | ||||||
|  |  | ||||||
|  | 			err = r.Exec(e2e.NewCommand( | ||||||
|  | 				"sudo", "alr", "in", "foo-pkg", "bar-pkg", | ||||||
|  | 			)) | ||||||
|  | 			assert.NoError(t, err) | ||||||
|  |  | ||||||
|  | 			err = r.Exec(e2e.NewCommand("sh", "-c", "alr rm --generate-bash-completion | grep ^foo-pkg$")) | ||||||
|  | 			assert.NoError(t, err) | ||||||
|  | 			err = r.Exec(e2e.NewCommand("sh", "-c", "alr rm --generate-bash-completion | grep ^bar-pkg$")) | ||||||
|  | 			assert.NoError(t, err) | ||||||
|  | 			err = r.Exec(e2e.NewCommand("sh", "-c", "alr rm --generate-bash-completion | grep ^test-autoreq-autoprov$")) | ||||||
|  | 			assert.Error(t, err) | ||||||
|  | 		}, | ||||||
|  | 	) | ||||||
|  | } | ||||||
							
								
								
									
										51
									
								
								e2e-tests/issue_72_install_with_deps_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								e2e-tests/issue_72_install_with_deps_test.go
									
									
									
									
									
										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/>. | ||||||
|  |  | ||||||
|  | //go:build e2e | ||||||
|  |  | ||||||
|  | package e2etests_test | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"testing" | ||||||
|  |  | ||||||
|  | 	"github.com/alecthomas/assert/v2" | ||||||
|  | 	"github.com/efficientgo/e2e" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestE2EIssue72InstallWithDeps(t *testing.T) { | ||||||
|  | 	dockerMultipleRun( | ||||||
|  | 		t, | ||||||
|  | 		"issue-72-install-with-deps", | ||||||
|  | 		COMMON_SYSTEMS, | ||||||
|  | 		func(t *testing.T, r e2e.Runnable) { | ||||||
|  | 			err := r.Exec(e2e.NewCommand( | ||||||
|  | 				"sudo", | ||||||
|  | 				"alr", | ||||||
|  | 				"addrepo", | ||||||
|  | 				"--name", | ||||||
|  | 				"alr-repo", | ||||||
|  | 				"--url", | ||||||
|  | 				"https://gitea.plemya-x.ru/Maks1mS/repo-for-tests.git", | ||||||
|  | 			)) | ||||||
|  | 			assert.NoError(t, err) | ||||||
|  |  | ||||||
|  | 			err = r.Exec(e2e.NewCommand( | ||||||
|  | 				"sudo", "alr", "in", "test-app-with-lib", | ||||||
|  | 			)) | ||||||
|  | 			assert.NoError(t, err) | ||||||
|  | 		}, | ||||||
|  | 	) | ||||||
|  | } | ||||||
							
								
								
									
										50
									
								
								e2e-tests/issue_74_upgradable_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								e2e-tests/issue_74_upgradable_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" | ||||||
|  |  | ||||||
|  | 	"github.com/efficientgo/e2e" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestE2EIssue74Upgradable(t *testing.T) { | ||||||
|  | 	dockerMultipleRun( | ||||||
|  | 		t, | ||||||
|  | 		"issue-74-upgradable", | ||||||
|  | 		COMMON_SYSTEMS, | ||||||
|  | 		func(t *testing.T, r e2e.Runnable) { | ||||||
|  | 			simpleExec(t, r, "sudo", | ||||||
|  | 				"alr", | ||||||
|  | 				"addrepo", | ||||||
|  | 				"--name", | ||||||
|  | 				"alr-repo", | ||||||
|  | 				"--url", | ||||||
|  | 				REPO_FOR_E2E_TESTS, | ||||||
|  | 			) | ||||||
|  | 			simpleExec(t, r, "sudo", "sh", "-c", "sed -i 's/ref = .*/ref = \"bd26236cd7\"/' /etc/alr/alr.toml") | ||||||
|  | 			simpleExec(t, r, "alr", "ref") | ||||||
|  | 			simpleExec(t, r, "sudo", "alr", "in", "bar-pkg") | ||||||
|  | 			simpleExec(t, r, "sh", "-c", "test $(alr list -U | wc -l) -eq 0 || exit 1") | ||||||
|  | 			simpleExec(t, r, "sudo", "sh", "-c", "sed -i 's/ref = .*/ref = \"d9a3541561\"/' /etc/alr/alr.toml") | ||||||
|  | 			simpleExec(t, r, "sudo", "alr", "ref") | ||||||
|  | 			simpleExec(t, r, "sh", "-c", "test $(alr list -U | wc -l) -eq 1 || exit 1") | ||||||
|  | 		}, | ||||||
|  | 	) | ||||||
|  | } | ||||||
							
								
								
									
										62
									
								
								e2e-tests/issue_75_ref_specify_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								e2e-tests/issue_75_ref_specify_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,62 @@ | |||||||
|  | // 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" | ||||||
|  |  | ||||||
|  | 	"github.com/alecthomas/assert/v2" | ||||||
|  | 	"github.com/efficientgo/e2e" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestE2EIssue75InstallWithDeps(t *testing.T) { | ||||||
|  | 	dockerMultipleRun( | ||||||
|  | 		t, | ||||||
|  | 		"issue-75-ref-specify", | ||||||
|  | 		COMMON_SYSTEMS, | ||||||
|  | 		func(t *testing.T, r e2e.Runnable) { | ||||||
|  | 			err := r.Exec(e2e.NewCommand( | ||||||
|  | 				"sudo", | ||||||
|  | 				"alr", | ||||||
|  | 				"addrepo", | ||||||
|  | 				"--name", | ||||||
|  | 				"alr-repo", | ||||||
|  | 				"--url", | ||||||
|  | 				"https://gitea.plemya-x.ru/Maks1mS/repo-for-tests.git", | ||||||
|  | 			)) | ||||||
|  | 			assert.NoError(t, err) | ||||||
|  |  | ||||||
|  | 			err = r.Exec(e2e.NewCommand( | ||||||
|  | 				"sudo", "alr", "ref", | ||||||
|  | 			)) | ||||||
|  | 			assert.NoError(t, err) | ||||||
|  |  | ||||||
|  | 			// TODO: replace with alr command when it be added | ||||||
|  | 			err = r.Exec(e2e.NewCommand( | ||||||
|  | 				"sudo", "sh", "-c", "sed -i 's/ref = .*/ref = \"bd26236cd7\"/' /etc/alr/alr.toml", | ||||||
|  | 			)) | ||||||
|  | 			assert.NoError(t, err) | ||||||
|  |  | ||||||
|  | 			err = r.Exec(e2e.NewCommand( | ||||||
|  | 				"sh", "-c", "test $(alr list | wc -l) -eq 2 || exit 1", | ||||||
|  | 			)) | ||||||
|  | 			assert.NoError(t, err) | ||||||
|  | 		}, | ||||||
|  | 	) | ||||||
|  | } | ||||||
							
								
								
									
										59
									
								
								e2e-tests/issue_81_multiple_packages_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								e2e-tests/issue_81_multiple_packages_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | |||||||
|  | // 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" | ||||||
|  |  | ||||||
|  | 	"github.com/efficientgo/e2e" | ||||||
|  | 	"github.com/stretchr/testify/assert" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestE2EIssue81MultiplePackages(t *testing.T) { | ||||||
|  | 	dockerMultipleRun( | ||||||
|  | 		t, | ||||||
|  | 		"issue-81-multiple-packages", | ||||||
|  | 		COMMON_SYSTEMS, | ||||||
|  | 		func(t *testing.T, r e2e.Runnable) { | ||||||
|  | 			err := r.Exec(e2e.NewCommand( | ||||||
|  | 				"sudo", | ||||||
|  | 				"alr", | ||||||
|  | 				"addrepo", | ||||||
|  | 				"--name", | ||||||
|  | 				"alr-repo", | ||||||
|  | 				"--url", | ||||||
|  | 				REPO_FOR_E2E_TESTS, | ||||||
|  | 			)) | ||||||
|  | 			assert.NoError(t, err) | ||||||
|  |  | ||||||
|  | 			err = r.Exec(e2e.NewCommand( | ||||||
|  | 				"sudo", "alr", "ref", | ||||||
|  | 			)) | ||||||
|  | 			assert.NoError(t, err) | ||||||
|  |  | ||||||
|  | 			err = r.Exec(e2e.NewCommand( | ||||||
|  | 				"sudo", "alr", "in", "first-package-with-dashes", | ||||||
|  | 			)) | ||||||
|  | 			assert.NoError(t, err) | ||||||
|  |  | ||||||
|  | 			err = r.Exec(e2e.NewCommand("cat", "/opt/first-package")) | ||||||
|  | 			assert.NoError(t, err) | ||||||
|  | 		}, | ||||||
|  | 	) | ||||||
|  | } | ||||||
							
								
								
									
										44
									
								
								e2e-tests/version_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								e2e-tests/version_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | |||||||
|  | // 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" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
|  | 	"github.com/efficientgo/e2e" | ||||||
|  |  | ||||||
|  | 	expect "github.com/tailscale/goexpect" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestE2EAlrVersion(t *testing.T) { | ||||||
|  | 	dockerMultipleRun( | ||||||
|  | 		t, | ||||||
|  | 		"check-version", | ||||||
|  | 		COMMON_SYSTEMS, | ||||||
|  | 		func(t *testing.T, r e2e.Runnable) { | ||||||
|  | 			runTestCommands(t, r, time.Second*10, []expect.Batcher{ | ||||||
|  | 				&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$`}, | ||||||
|  | 			}) | ||||||
|  | 		}, | ||||||
|  | 	) | ||||||
|  | } | ||||||
							
								
								
									
										131
									
								
								fix.go
									
									
									
									
									
								
							
							
						
						
									
										131
									
								
								fix.go
									
									
									
									
									
								
							| @@ -1,64 +1,103 @@ | |||||||
| /* | // This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan. | ||||||
|  * ALR - Any Linux Repository | // It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors. | ||||||
|  * Copyright (C) 2024 Евгений Храмов | // | ||||||
|  * | // ALR - Any Linux Repository | ||||||
|  * This program is free software: you can redistribute it and/or modify | // Copyright (C) 2025 The ALR Authors | ||||||
|  * it under the terms of the GNU General Public License as published by | // | ||||||
|  * the Free Software Foundation, either version 3 of the License, or | // This program is free software: you can redistribute it and/or modify | ||||||
|  * (at your option) any later version. | // it under the terms of the GNU General Public License as published by | ||||||
|  * | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  * This program is distributed in the hope that it will be useful, | // (at your option) any later version. | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | // | ||||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | // This program is distributed in the hope that it will be useful, | ||||||
|  * GNU General Public License for more details. | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  * | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  * You should have received a copy of the GNU General Public License | // GNU General Public License for more details. | ||||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | // | ||||||
|  */ | // 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 | package main | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"log/slog" | ||||||
| 	"os" | 	"os" | ||||||
|  | 	"path/filepath" | ||||||
|  |  | ||||||
|  | 	"github.com/leonelquinteros/gotext" | ||||||
| 	"github.com/urfave/cli/v2" | 	"github.com/urfave/cli/v2" | ||||||
| 	"plemya-x.ru/alr/internal/config" |  | ||||||
| 	"plemya-x.ru/alr/internal/db" | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils" | ||||||
| 	"plemya-x.ru/alr/pkg/loggerctx" | 	appbuilder "gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils/app_builder" | ||||||
| 	"plemya-x.ru/alr/pkg/repos" | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/utils" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| var fixCmd = &cli.Command{ | func FixCmd() *cli.Command { | ||||||
| 	Name:  "fix", | 	return &cli.Command{ | ||||||
| 	Usage: "Attempt to fix problems with ALR", | 		Name:  "fix", | ||||||
| 	Action: func(c *cli.Context) error { | 		Usage: gotext.Get("Attempt to fix problems with ALR"), | ||||||
| 		ctx := c.Context | 		Action: func(c *cli.Context) error { | ||||||
| 		log := loggerctx.From(ctx) | 			if err := utils.ExitIfCantDropCapsToAlrUserNoPrivs(); err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  |  | ||||||
| 		db.Close() | 			ctx := c.Context | ||||||
| 		paths := config.GetPaths(ctx) |  | ||||||
|  |  | ||||||
| 		log.Info("Removing cache directory").Send() | 			deps, err := appbuilder. | ||||||
|  | 				New(ctx). | ||||||
|  | 				WithConfig(). | ||||||
|  | 				Build() | ||||||
|  | 			if err != nil { | ||||||
|  | 				return cli.Exit(err, 1) | ||||||
|  | 			} | ||||||
|  | 			defer deps.Defer() | ||||||
|  |  | ||||||
| 		err := os.RemoveAll(paths.CacheDir) | 			cfg := deps.Cfg | ||||||
| 		if err != nil { |  | ||||||
| 			log.Fatal("Unable to remove cache directory").Err(err).Send() |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		log.Info("Rebuilding cache").Send() | 			paths := cfg.GetPaths() | ||||||
|  |  | ||||||
| 		err = os.MkdirAll(paths.CacheDir, 0o755) | 			slog.Info(gotext.Get("Clearing cache directory")) | ||||||
| 		if err != nil { | 			// Remove all nested directories of paths.CacheDir | ||||||
| 			log.Fatal("Unable to create new cache directory").Err(err).Send() |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		err = repos.Pull(ctx, config.Config(ctx).Repos) | 			dir, err := os.Open(paths.CacheDir) | ||||||
| 		if err != nil { | 			if err != nil { | ||||||
| 			log.Fatal("Error pulling repos").Err(err).Send() | 				return cliutils.FormatCliExit(gotext.Get("Unable to open cache directory"), err) | ||||||
| 		} | 			} | ||||||
|  | 			defer dir.Close() | ||||||
|  |  | ||||||
| 		log.Info("Done").Send() | 			entries, err := dir.Readdirnames(-1) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return cliutils.FormatCliExit(gotext.Get("Unable to read cache directory contents"), err) | ||||||
|  | 			} | ||||||
|  |  | ||||||
| 		return nil | 			for _, entry := range entries { | ||||||
| 	}, | 				err = os.RemoveAll(filepath.Join(paths.CacheDir, entry)) | ||||||
|  | 				if err != nil { | ||||||
|  | 					return cliutils.FormatCliExit(gotext.Get("Unable to remove cache item (%s)", entry), err) | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			slog.Info(gotext.Get("Rebuilding cache")) | ||||||
|  |  | ||||||
|  | 			err = os.MkdirAll(paths.CacheDir, 0o755) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return cliutils.FormatCliExit(gotext.Get("Unable to create new cache directory"), err) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			deps, err = appbuilder. | ||||||
|  | 				New(ctx). | ||||||
|  | 				WithConfig(). | ||||||
|  | 				WithDB(). | ||||||
|  | 				WithReposForcePull(). | ||||||
|  | 				Build() | ||||||
|  | 			if err != nil { | ||||||
|  | 				return cli.Exit(err, 1) | ||||||
|  | 			} | ||||||
|  | 			defer deps.Defer() | ||||||
|  |  | ||||||
|  | 			slog.Info(gotext.Get("Done")) | ||||||
|  |  | ||||||
|  | 			return nil | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										93
									
								
								gen.go
									
									
									
									
									
								
							
							
						
						
									
										93
									
								
								gen.go
									
									
									
									
									
								
							| @@ -1,45 +1,66 @@ | |||||||
|  | // 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 main | package main | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"os" | 	"os" | ||||||
|  |  | ||||||
|  | 	"github.com/leonelquinteros/gotext" | ||||||
| 	"github.com/urfave/cli/v2" | 	"github.com/urfave/cli/v2" | ||||||
| 	"plemya-x.ru/alr/pkg/gen" |  | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/gen" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| var genCmd = &cli.Command{ | func GenCmd() *cli.Command { | ||||||
| 	Name:    "generate", | 	return &cli.Command{ | ||||||
| 	Usage:   "Generate a ALR script from a template", | 		Name:    "generate", | ||||||
| 	Aliases: []string{"gen"}, | 		Usage:   gotext.Get("Generate a ALR script from a template"), | ||||||
| 	Subcommands: []*cli.Command{ | 		Aliases: []string{"gen"}, | ||||||
| 		genPipCmd, | 		Subcommands: []*cli.Command{ | ||||||
| 	}, | 			{ | ||||||
| } | 				Name:  "pip", | ||||||
|  | 				Usage: gotext.Get("Generate a ALR script for a pip module"), | ||||||
| var genPipCmd = &cli.Command{ | 				Flags: []cli.Flag{ | ||||||
| 	Name:  "pip", | 					&cli.StringFlag{ | ||||||
| 	Usage: "Generate a ALR script for a pip module", | 						Name:     "name", | ||||||
| 	Flags: []cli.Flag{ | 						Aliases:  []string{"n"}, | ||||||
| 		&cli.StringFlag{ | 						Required: true, | ||||||
| 			Name:     "name", | 					}, | ||||||
| 			Aliases:  []string{"n"}, | 					&cli.StringFlag{ | ||||||
| 			Required: true, | 						Name:     "version", | ||||||
| 		}, | 						Aliases:  []string{"v"}, | ||||||
| 		&cli.StringFlag{ | 						Required: true, | ||||||
| 			Name:     "version", | 					}, | ||||||
| 			Aliases:  []string{"v"}, | 					&cli.StringFlag{ | ||||||
| 			Required: true, | 						Name:    "description", | ||||||
| 		}, | 						Aliases: []string{"d"}, | ||||||
| 		&cli.StringFlag{ | 					}, | ||||||
| 			Name:    "description", | 				}, | ||||||
| 			Aliases: []string{"d"}, | 				Action: func(c *cli.Context) error { | ||||||
| 		}, | 					return gen.Pip(os.Stdout, gen.PipOptions{ | ||||||
| 	}, | 						Name:        c.String("name"), | ||||||
| 	Action: func(c *cli.Context) error { | 						Version:     c.String("version"), | ||||||
| 		return gen.Pip(os.Stdout, gen.PipOptions{ | 						Description: c.String("description"), | ||||||
| 			Name:        c.String("name"), | 					}) | ||||||
| 			Version:     c.String("version"), | 				}, | ||||||
| 			Description: c.String("description"), | 			}, | ||||||
| 		}) | 		}, | ||||||
| 	}, | 	} | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										116
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										116
									
								
								go.mod
									
									
									
									
									
								
							| @@ -1,50 +1,58 @@ | |||||||
| module plemya-x.ru/alr | module gitea.plemya-x.ru/Plemya-x/ALR | ||||||
|  |  | ||||||
| go 1.21 | go 1.23.0 | ||||||
|  |  | ||||||
| toolchain go1.21.3 | toolchain go1.24.2 | ||||||
|  |  | ||||||
| require ( | require ( | ||||||
|  | 	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/charmbracelet/bubbles v0.16.1 | 	github.com/caarlos0/env v3.5.0+incompatible | ||||||
| 	github.com/charmbracelet/bubbletea v0.24.2 | 	github.com/charmbracelet/bubbles v0.20.0 | ||||||
| 	github.com/charmbracelet/lipgloss v0.8.0 | 	github.com/charmbracelet/bubbletea v1.2.4 | ||||||
| 	github.com/go-git/go-billy/v5 v5.5.0 | 	github.com/charmbracelet/lipgloss v1.0.0 | ||||||
| 	github.com/go-git/go-git/v5 v5.9.0 | 	github.com/charmbracelet/log v0.4.0 | ||||||
| 	github.com/goreleaser/nfpm/v2 v2.33.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-git/v5 v5.13.0 | ||||||
|  | 	github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 | ||||||
|  | 	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/jmoiron/sqlx v1.3.5 | 	github.com/jmoiron/sqlx v1.3.5 | ||||||
| 	github.com/mattn/go-isatty v0.0.19 | 	github.com/leonelquinteros/gotext v1.7.0 | ||||||
|  | 	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.1.0 | ||||||
| 	github.com/schollz/progressbar/v3 v3.13.1 | 	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.elara.ws/logger v0.0.0-20230421022458-e80700db2090 |  | ||||||
| 	go.elara.ws/translate v0.0.0-20230421025926-32ccfcd110e6 |  | ||||||
| 	go.elara.ws/vercmp v0.0.0-20230622214216-0b2b067575c4 | 	go.elara.ws/vercmp v0.0.0-20230622214216-0b2b067575c4 | ||||||
| 	golang.org/x/crypto v0.13.0 | 	golang.org/x/crypto v0.36.0 | ||||||
| 	golang.org/x/exp v0.0.0-20230905200255-921286631fa9 | 	golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 | ||||||
| 	golang.org/x/sys v0.12.0 | 	golang.org/x/sys v0.31.0 | ||||||
| 	golang.org/x/text v0.13.0 | 	golang.org/x/text v0.23.0 | ||||||
| 	gopkg.in/yaml.v3 v3.0.1 | 	gopkg.in/yaml.v3 v3.0.1 | ||||||
| 	modernc.org/sqlite v1.25.0 | 	modernc.org/sqlite v1.25.0 | ||||||
| 	mvdan.cc/sh/v3 v3.7.0 | 	mvdan.cc/sh/v3 v3.10.0 | ||||||
| 	plemya-x.ru/fakeroot v0.0.0-20240601131003-c638a3543283 |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| require ( | require ( | ||||||
| 	dario.cat/mergo v1.0.0 // indirect | 	dario.cat/mergo v1.0.1 // indirect | ||||||
| 	github.com/AlekSi/pointer v1.2.0 // indirect | 	github.com/AlekSi/pointer v1.2.0 // indirect | ||||||
| 	github.com/Masterminds/goutils v1.1.1 // indirect | 	github.com/Masterminds/goutils v1.1.1 // indirect | ||||||
| 	github.com/Masterminds/semver/v3 v3.2.1 // 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 v0.0.0-20230828082145-3c4c8a2d2371 // indirect | 	github.com/ProtonMail/go-crypto v1.1.3 // indirect | ||||||
| 	github.com/acomagu/bufpipe v1.0.4 // 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 | ||||||
| @@ -52,67 +60,81 @@ require ( | |||||||
| 	github.com/bodgit/sevenzip v1.3.0 // indirect | 	github.com/bodgit/sevenzip v1.3.0 // indirect | ||||||
| 	github.com/bodgit/windows v1.0.0 // indirect | 	github.com/bodgit/windows v1.0.0 // indirect | ||||||
| 	github.com/cavaliergopher/cpio v1.0.1 // indirect | 	github.com/cavaliergopher/cpio v1.0.1 // indirect | ||||||
| 	github.com/cloudflare/circl v1.3.3 // indirect | 	github.com/charmbracelet/harmonica v0.2.0 // indirect | ||||||
|  | 	github.com/charmbracelet/x/ansi v0.4.5 // indirect | ||||||
|  | 	github.com/charmbracelet/x/term v0.2.1 // indirect | ||||||
|  | 	github.com/cloudflare/circl v1.3.8 // indirect | ||||||
| 	github.com/connesc/cipherio v0.2.1 // indirect | 	github.com/connesc/cipherio v0.2.1 // indirect | ||||||
| 	github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect | 	github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect | ||||||
| 	github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect | 	github.com/creack/pty v1.1.24 // indirect | ||||||
| 	github.com/cyphar/filepath-securejoin v0.2.4 // indirect | 	github.com/cyphar/filepath-securejoin v0.2.5 // 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/fatih/color v1.7.0 // 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/gobwas/glob v0.2.3 // indirect | 	github.com/gobwas/glob v0.2.3 // 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/rpmpack v0.5.0 // indirect | 	github.com/google/goterm v0.0.0-20190703233501-fc88cf888a3f // indirect | ||||||
| 	github.com/google/uuid v1.3.0 // indirect | 	github.com/google/rpmpack v0.6.1-0.20240329070804-c2247cbb881a // indirect | ||||||
| 	github.com/gookit/color v1.5.1 // indirect | 	github.com/google/uuid v1.4.0 // indirect | ||||||
| 	github.com/goreleaser/chglog v0.5.0 // 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/hashicorp/yamux v0.1.1 // indirect | ||||||
|  | 	github.com/hexops/gotextdiff v1.0.3 // 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/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.0 // 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/lucasb-eyer/go-colorful v1.2.0 // indirect | 	github.com/lucasb-eyer/go-colorful v1.2.0 // indirect | ||||||
| 	github.com/mattn/go-colorable v0.1.2 // 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.15 // 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/colorstring v0.0.0-20190213212951-d06e56a500db // 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/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // 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/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect | 	github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect | ||||||
| 	github.com/rivo/uniseg v0.4.4 // indirect | 	github.com/rivo/uniseg v0.4.7 // 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.2.0 // 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.2.0 // indirect | ||||||
| 	github.com/skeema/knownhosts v1.2.0 // indirect | 	github.com/skeema/knownhosts v1.3.0 // indirect | ||||||
| 	github.com/spf13/cast v1.5.1 // indirect | 	github.com/spf13/cast v1.6.0 // indirect | ||||||
| 	github.com/therootcompany/xz v1.0.1 // indirect | 	github.com/therootcompany/xz v1.0.1 // indirect | ||||||
| 	github.com/ulikunitz/xz v0.5.11 // 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/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // 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.12.0 // indirect | 	golang.org/x/mod v0.19.0 // indirect | ||||||
| 	golang.org/x/net v0.15.0 // indirect | 	golang.org/x/net v0.38.0 // indirect | ||||||
| 	golang.org/x/sync v0.3.0 // indirect | 	golang.org/x/sync v0.12.0 // indirect | ||||||
| 	golang.org/x/term v0.12.0 // indirect | 	golang.org/x/term v0.30.0 // indirect | ||||||
| 	golang.org/x/tools v0.13.0 // indirect | 	golang.org/x/tools v0.23.0 // indirect | ||||||
|  | 	google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect | ||||||
|  | 	google.golang.org/grpc v1.58.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 | ||||||
| 	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 | ||||||
|   | |||||||
							
								
								
									
										318
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										318
									
								
								go.sum
									
									
									
									
									
								
							| @@ -14,22 +14,24 @@ cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2k | |||||||
| cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= | cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= | ||||||
| cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= | ||||||
| cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= | cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= | ||||||
| dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= | dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= | ||||||
| dario.cat/mergo v1.0.0/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.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= | ||||||
| github.com/AlekSi/pointer v1.2.0/go.mod h1:gZGfd3dpW4vEc/UlyfKKi1roIqcCgwOIvb0tSNSBle0= | github.com/AlekSi/pointer v1.2.0/go.mod h1:gZGfd3dpW4vEc/UlyfKKi1roIqcCgwOIvb0tSNSBle0= | ||||||
| github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= | ||||||
| github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= | ||||||
| github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= | github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= | ||||||
| github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= | github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= | ||||||
| github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= | github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= | ||||||
| github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= | github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= | ||||||
| github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= | github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= | ||||||
| github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= | github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0= | ||||||
| github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= | github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= | ||||||
| github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= | github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= | ||||||
| github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= | github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= | ||||||
| github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= | github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= | ||||||
| @@ -37,16 +39,14 @@ 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 v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg= | github.com/ProtonMail/go-crypto v1.1.3 h1:nRBOetoydLeUb4nHajyO2bKqMLfWQ/ZPwkXqXxPxCFk= | ||||||
| github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/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= | ||||||
| github.com/ProtonMail/gopenpgp/v2 v2.7.1/go.mod h1:/BU5gfAVwqyd8EfC3Eu7zmuhwYQpKs+cGD8M//iiaxs= | github.com/ProtonMail/gopenpgp/v2 v2.7.1/go.mod h1:/BU5gfAVwqyd8EfC3Eu7zmuhwYQpKs+cGD8M//iiaxs= | ||||||
| github.com/PuerkitoBio/purell v1.2.0 h1:/Jdm5QfyM8zdlqT6WVZU4cfP23sot6CEHA4CS49Ezig= | github.com/PuerkitoBio/purell v1.2.0 h1:/Jdm5QfyM8zdlqT6WVZU4cfP23sot6CEHA4CS49Ezig= | ||||||
| github.com/PuerkitoBio/purell v1.2.0/go.mod h1:OhLRTaaIzhvIyofkJfB24gokC7tM42Px5UhoT32THBk= | github.com/PuerkitoBio/purell v1.2.0/go.mod h1:OhLRTaaIzhvIyofkJfB24gokC7tM42Px5UhoT32THBk= | ||||||
| github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ= |  | ||||||
| github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= |  | ||||||
| github.com/alecthomas/assert/v2 v2.2.1 h1:XivOgYcduV98QCahG8T5XTezV5bylXe+lBxLG2K2ink= | github.com/alecthomas/assert/v2 v2.2.1 h1:XivOgYcduV98QCahG8T5XTezV5bylXe+lBxLG2K2ink= | ||||||
| github.com/alecthomas/assert/v2 v2.2.1/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= | github.com/alecthomas/assert/v2 v2.2.1/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= | ||||||
| github.com/alecthomas/chroma/v2 v2.9.1 h1:0O3lTQh9FxazJ4BYE/MOi/vDGuHn7B+6Bu902N2UZvU= | github.com/alecthomas/chroma/v2 v2.9.1 h1:0O3lTQh9FxazJ4BYE/MOi/vDGuHn7B+6Bu902N2UZvU= | ||||||
| @@ -61,6 +61,8 @@ 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/bodgit/plumbing v1.2.0 h1:gg4haxoKphLjml+tgnecR4yLBV5zo4HAZGCtAh3xCzM= | github.com/bodgit/plumbing v1.2.0 h1:gg4haxoKphLjml+tgnecR4yLBV5zo4HAZGCtAh3xCzM= | ||||||
| @@ -69,37 +71,46 @@ 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/go-rpmutils v0.2.1-0.20211112020245-2cd62ff89b11 h1:IRrDwVlWQr6kS1U8/EtyA1+EHcc4yl8pndcqXWrEamg= | github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8= | ||||||
| github.com/caarlos0/go-rpmutils v0.2.1-0.20211112020245-2cd62ff89b11/go.mod h1:je2KZ+LxaCNvCoKg32jtOIULcFogJKcL1ZWUaIBjKj0= | github.com/caarlos0/env v3.5.0+incompatible h1:Yy0UN8o9Wtr/jGHZDpCBLpNrzcFLLM2yixi/rBrKyJs= | ||||||
|  | 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/charmbracelet/bubbles v0.16.1 h1:6uzpAAaT9ZqKssntbvZMlksWHruQLNxg49H5WdeuYSY= | github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= | ||||||
| github.com/charmbracelet/bubbles v0.16.1/go.mod h1:2QCp9LFlEsBQMvIYERr7Ww2H2bA7xen1idUDIzm/+Xc= | github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= | ||||||
| github.com/charmbracelet/bubbletea v0.24.2 h1:uaQIKx9Ai6Gdh5zpTbGiWpytMU+CfsPp06RaW2cx/SY= | github.com/charmbracelet/bubbles v0.20.0 h1:jSZu6qD8cRQ6k9OMfR1WlM+ruM8fkPWkHvQWD9LIutE= | ||||||
| github.com/charmbracelet/bubbletea v0.24.2/go.mod h1:XdrNrV4J8GiyshTtx3DNuYkR1FDaJmO3l2nejekbsgg= | github.com/charmbracelet/bubbles v0.20.0/go.mod h1:39slydyswPy+uVOHZ5x/GjwVAFkCsV8IIVy+4MhzwwU= | ||||||
| github.com/charmbracelet/lipgloss v0.8.0 h1:IS00fk4XAHcf8uZKc3eHeMUTCxUH6NkaTrdyCQk84RU= | github.com/charmbracelet/bubbletea v1.2.4 h1:KN8aCViA0eps9SCOThb2/XPIlea3ANJLUkv3KnQRNCE= | ||||||
| github.com/charmbracelet/lipgloss v0.8.0/go.mod h1:p4eYUZZJ/0oXTuCQKFF8mqyKCz0ja6y+7DniDDw5KKU= | github.com/charmbracelet/bubbletea v1.2.4/go.mod h1:Qr6fVQw+wX7JkWWkVyXYk/ZUQ92a6XNekLXa3rR18MM= | ||||||
|  | github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ= | ||||||
|  | github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao= | ||||||
|  | github.com/charmbracelet/lipgloss v1.0.0 h1:O7VkGDvqEdGi93X+DeqsQ7PKHDgtQfF8j8/O2qFMQNg= | ||||||
|  | github.com/charmbracelet/lipgloss v1.0.0/go.mod h1:U5fy9Z+C38obMs+T+tJqst9VGzlOYGj4ri9reL3qUlo= | ||||||
|  | github.com/charmbracelet/log v0.4.0 h1:G9bQAcx8rWA2T3pWvx7YtPTPwgqpk7D68BX21IRW8ZM= | ||||||
|  | github.com/charmbracelet/log v0.4.0/go.mod h1:63bXt/djrizTec0l11H20t8FDSvA4CRZJ1KH22MdptM= | ||||||
|  | github.com/charmbracelet/x/ansi v0.4.5 h1:LqK4vwBNaXw2AyGIICa5/29Sbdq58GbGdFngSexTdRM= | ||||||
|  | github.com/charmbracelet/x/ansi v0.4.5/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= | ||||||
|  | github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ= | ||||||
|  | github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= | ||||||
| github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= | ||||||
| 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 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs= | github.com/cloudflare/circl v1.3.8 h1:j+V8jJt09PoeMFIu2uh5JUyEaIHTXVOHslFoLNAKqwI= | ||||||
| github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= | 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/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY= | github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= | ||||||
| github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= | github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= | ||||||
| github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= |  | ||||||
| github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= |  | ||||||
| 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.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= | github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= | ||||||
| github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= | 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= | ||||||
| @@ -110,26 +121,38 @@ 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/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU= | github.com/efficientgo/core v1.0.0-rc.0 h1:jJoA0N+C4/knWYVZ6GrdHOtDyrg8Y/TR4vFpTaqTsqs= | ||||||
| github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= | github.com/efficientgo/core v1.0.0-rc.0/go.mod h1:kQa0V74HNYMfuJH6jiPiwNdpWXl4xd/K4tzlrcvYDQI= | ||||||
|  | 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 v1.2.1 h1:njjgvO6cRG9rIqN2ebkqy6cQz2Njkx7Fsfv/zIZqgug= | ||||||
|  | github.com/elazarl/goproxy v1.2.1/go.mod h1:YfEbZtqP4AetfO6d40vWchF3znWX7C7Vd6ZMfdL8z64= | ||||||
| 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/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA= | github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= | ||||||
| github.com/frankban/quicktest v1.14.5/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= | github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= | ||||||
| github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= | github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= | ||||||
| github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4= | 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/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= | ||||||
|  | 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.20230305113008-0c11038e723f h1:Pz0DHeFij3XFhoBRGUDPzSJ+w2UcK5/0JvF8DRI58r8= | 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.20230305113008-0c11038e723f/go.mod h1:8LHG1a3SRW71ettAD/jW13h8c6AqjVSeL11RAdgaqpo= | 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.9.0 h1:cD9SFA7sHVRdJ7AYck1ZaAa/yeuBvGPxwXDL8cxrObY= | github.com/go-git/go-git/v5 v5.13.0 h1:vLn5wlGIh/X78El6r3Jr+30W16Blk0CTcxTYcYPWi5E= | ||||||
| github.com/go-git/go-git/v5 v5.9.0/go.mod h1:RKIqga24sWdMGZF+1Ekv9kylsDz6LzdTSI2s/OsZWE0= | 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/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= | ||||||
|  | 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-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= | github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= | ||||||
| github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= | github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= | ||||||
| github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= | github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= | ||||||
| @@ -150,6 +173,9 @@ 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.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= | ||||||
|  | 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.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= | ||||||
| @@ -158,8 +184,11 @@ 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.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||||
| github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= | 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/goterm v0.0.0-20190703233501-fc88cf888a3f h1:5CjVwnuUcp5adK4gmY6i72gpVFVnZDP2h5TmPScB6u4= | ||||||
|  | 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= | ||||||
| @@ -167,29 +196,35 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf | |||||||
| github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ= | github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ= | ||||||
| github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= | github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= | ||||||
| github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= | ||||||
| github.com/google/rpmpack v0.5.0 h1:L16KZ3QvkFGpYhmp23iQip+mx1X39foEsqszjMNBm8A= | github.com/google/rpmpack v0.6.1-0.20240329070804-c2247cbb881a h1:JJBdjSfqSy3mnDT0940ASQFghwcZ4y4cb6ttjAoXqwE= | ||||||
| github.com/google/rpmpack v0.5.0/go.mod h1:uqVAUVQLq8UY2hCDfmJ/+rtO3aw7qyhc90rCVEabEfI= | github.com/google/rpmpack v0.6.1-0.20240329070804-c2247cbb881a/go.mod h1:uqVAUVQLq8UY2hCDfmJ/+rtO3aw7qyhc90rCVEabEfI= | ||||||
|  | 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/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.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= | github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= | ||||||
| github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= | github.com/google/uuid v1.4.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/gookit/color v1.5.1 h1:Vjg2VEcdHpwq+oY63s/ksHrgJYCTo0bwWvmmYWdE9fQ= |  | ||||||
| github.com/gookit/color v1.5.1/go.mod h1:wZFzea4X8qN6vHOSP2apMb4/+w/orMznEzYsIHPaqKM= |  | ||||||
| github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g= | github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g= | ||||||
| github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= | github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= | ||||||
| github.com/goreleaser/chglog v0.5.0 h1:Sk6BMIpx8+vpAf8KyPit34OgWui8c7nKTMHhYx88jJ4= | github.com/goreleaser/chglog v0.6.1 h1:NZKiX8l0FTQPRzBgKST7knvNZmZ04f7PEGkN2wInfhE= | ||||||
| github.com/goreleaser/chglog v0.5.0/go.mod h1:Ri46M3lrMuv76FHszs3vtABR8J8k1w9JHYAzxeeOl28= | github.com/goreleaser/chglog v0.6.1/go.mod h1:Bnnfo07jMZkaAb0uRNASMZyOsX6ROW6X1qbXqN3guUo= | ||||||
| github.com/goreleaser/fileglob v1.3.0 h1:/X6J7U8lbDpQtBvGcwwPS6OpzkNVlVEsFUVRx9+k+7I= | github.com/goreleaser/fileglob v1.3.0 h1:/X6J7U8lbDpQtBvGcwwPS6OpzkNVlVEsFUVRx9+k+7I= | ||||||
| github.com/goreleaser/fileglob v1.3.0/go.mod h1:Jx6BoXv3mbYkEzwm9THo7xbr5egkAraxkGorbJb4RxU= | github.com/goreleaser/fileglob v1.3.0/go.mod h1:Jx6BoXv3mbYkEzwm9THo7xbr5egkAraxkGorbJb4RxU= | ||||||
| github.com/goreleaser/nfpm/v2 v2.33.0 h1:yBv6jgkPwih4va/S42rceSjJ2Znt3Og/Ntc76oP0tfI= | github.com/goreleaser/nfpm/v2 v2.41.0 h1:JyMzS/EwqaWbFs+7Z9oZ4Hkk4or00gUTqwm9Dgr8QYg= | ||||||
| github.com/goreleaser/nfpm/v2 v2.33.0/go.mod h1:8wwWWvJWmn84xo/Sqiv0aMvEGTHlHZTXTEuVSgQpkIM= | 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= | ||||||
| @@ -202,21 +237,26 @@ github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= | |||||||
| github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= | github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= | ||||||
| github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= | github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= | ||||||
| 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/go.mod h1:ZDXo8KHryOWSIqnsb/CiDq7hQUYryCgdVnxbj8tDG7o= | ||||||
|  | github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c= | ||||||
|  | github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo= | ||||||
| github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= | github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= | ||||||
| github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= | github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= | ||||||
|  | github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= | ||||||
|  | github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= | ||||||
| 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= | ||||||
| github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= | ||||||
| github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw= |  | ||||||
| github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= | ||||||
| github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= | ||||||
| github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= | github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= | ||||||
| github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= | github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= | ||||||
| github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= | ||||||
| github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= | github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= | ||||||
| github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= | github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= | ||||||
| github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= | github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= | ||||||
| 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= | ||||||
| @@ -227,34 +267,37 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= | |||||||
| github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= | ||||||
| github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= | 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/go.mod h1:qJdoQuERPpccw7L70uoU+K/BvTfRBHYsisCQyFLXyvw= | ||||||
| github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0= | 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/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/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= |  | ||||||
| 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 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= |  | ||||||
| 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/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.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= | github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= | ||||||
| github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= | ||||||
| github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= | 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-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= | github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= | ||||||
| github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= | github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= | ||||||
| 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.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= | github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= | ||||||
| github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= | github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= | ||||||
| github.com/mattn/go-runewidth v0.0.15/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.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= | ||||||
| github.com/mholt/archiver/v4 v4.0.0-alpha.8/go.mod h1:5f7FUYGXdJWUjESffJaYR4R60VhnHxb2X3T1teMyv5A= | github.com/mholt/archiver/v4 v4.0.0-alpha.8/go.mod h1:5f7FUYGXdJWUjESffJaYR4R60VhnHxb2X3T1teMyv5A= | ||||||
| github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= |  | ||||||
| github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= |  | ||||||
| github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= | github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= | ||||||
| github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= | github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= | ||||||
| github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= | github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= | ||||||
| @@ -263,18 +306,22 @@ 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/muesli/ansi v0.0.0-20211018074035-2e021307bc4b h1:1XF24mVaiu7u+CFywTdcDo2ie1pzzhwjt6RHqzpMU34= | github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= | ||||||
| github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho= | 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= | ||||||
| github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= | github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= | ||||||
| github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= | 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/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.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= | github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= | ||||||
| github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= | github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= | ||||||
| github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0= | github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0= | ||||||
| @@ -285,54 +332,63 @@ 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= | ||||||
| github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= | github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= | ||||||
| github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= | github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= | ||||||
| github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= | github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= | ||||||
| github.com/rivo/uniseg v0.4.4/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.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= | github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= | ||||||
| github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= | 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= | ||||||
| github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= | ||||||
| github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= | github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= | ||||||
| github.com/schollz/progressbar/v3 v3.13.1 h1:o8rySDYiQ59Mwzy2FELeHY5ZARXZTVJC7iHD6PEFUiE= | github.com/sassoftware/go-rpmutils v0.4.0 h1:ojND82NYBxgwrV+mX1CWsd5QJvvEZTKddtCdFLPWhpg= | ||||||
| github.com/schollz/progressbar/v3 v3.13.1/go.mod h1:xvrbki8kfT1fzWzBT/UZd9L6GA+jdL7HAgq2RFnO6fQ= | github.com/sassoftware/go-rpmutils v0.4.0/go.mod h1:3goNWi7PGAT3/dlql2lv3+MSN5jNYPjT5mVcQcIsYzI= | ||||||
| github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= | github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= | ||||||
| github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= | 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 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/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.0 h1:h9r9cf0+u7wSE+M183ZtMGgOJKiL96brpaz5ekfJCpM= | github.com/skeema/knownhosts v1.3.0 h1:AM+y0rI04VksttfwjkSTNQorvGqmwATnvnAHpSgc0LY= | ||||||
| github.com/skeema/knownhosts v1.2.0/go.mod h1:g4fPeYpque7P0xefxtGzV81ihjC8sX2IqpAoNkjxbMo= | github.com/skeema/knownhosts v1.3.0/go.mod h1:sPINvnADmT/qYH1kfv+ePMmOBTH6Tbl7b5LvTDjFK7M= | ||||||
| github.com/smartystreets/assertions v1.13.1 h1:Ef7KhSmjZcK6AVf9YbJdvPYG9avaF0ZxudX+ThRdWfU= | github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY= | ||||||
| github.com/smartystreets/assertions v1.13.1/go.mod h1:cXr/IwVfSo/RbCSPhoAPv73p3hlSdrBH/b3SdnW/LMY= | github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec= | ||||||
| github.com/smartystreets/goconvey v1.8.0 h1:Oi49ha/2MURE0WexF052Z0m+BNSGirfjg5RL+JXWq3w= | github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY= | ||||||
| github.com/smartystreets/goconvey v1.8.0/go.mod h1:EdX8jtrTIj26jmjCOVNMVSIYAtgexqXKHOXW2Dx9JLg= | 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.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= | github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= | ||||||
| github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= | github.com/spf13/cast v1.6.0/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.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= | ||||||
| github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= | github.com/stretchr/objx v0.5.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.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= |  | ||||||
| github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= | ||||||
| github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= |  | ||||||
| github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= | ||||||
|  | github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= | ||||||
|  | github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= | ||||||
|  | github.com/tailscale/goexpect v0.0.0-20210902213824-6e8c725cea41 h1:/V2rCMMWcsjYaYO2MeovLw+ClP63OtXgCF2Y1eb8+Ns= | ||||||
|  | github.com/tailscale/goexpect v0.0.0-20210902213824-6e8c725cea41/go.mod h1:/roCdA6gg6lQyw/Oz6gIIGu3ggJKYhF+WC/AQReE5XQ= | ||||||
| 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/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.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= | github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc= | ||||||
| github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= | github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= | ||||||
| github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs= | github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs= | ||||||
| github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= | github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= | ||||||
| github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU= | github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU= | ||||||
| @@ -343,17 +399,11 @@ github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM | |||||||
| github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= | github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= | ||||||
| github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= | github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= | ||||||
| github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= | github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= | ||||||
| github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8= |  | ||||||
| github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= |  | ||||||
| github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= | github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= | ||||||
| github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= | github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= | ||||||
| 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.elara.ws/logger v0.0.0-20230421022458-e80700db2090 h1:RVC8XvWo6Yw4HUshqx4TSzuBDScDghafU6QFRJ4xPZg= |  | ||||||
| go.elara.ws/logger v0.0.0-20230421022458-e80700db2090/go.mod h1:qng49owViqsW5Aey93lwBXONw20oGbJIoLVscB16mPM= |  | ||||||
| go.elara.ws/translate v0.0.0-20230421025926-32ccfcd110e6 h1:4xCBxLPBn3Y2DuIcj8zQ1tQOFLrpu6tEIGUWn/Q6zPM= |  | ||||||
| go.elara.ws/translate v0.0.0-20230421025926-32ccfcd110e6/go.mod h1:NmfCFqwq7X/aqa/ZVkIysj17JyMEY4Bb5E921kMswNo= |  | ||||||
| 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= | ||||||
| @@ -369,10 +419,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.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= |  | ||||||
| golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= |  | ||||||
| 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= | ||||||
| @@ -381,8 +429,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-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= | golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= | ||||||
| golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= | 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= | ||||||
| @@ -401,9 +449,8 @@ 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.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= | golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= | ||||||
| golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= |  | ||||||
| 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-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||||||
| @@ -422,15 +469,15 @@ 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.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= |  | ||||||
| golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= |  | ||||||
| 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.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8= | ||||||
|  | golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= | ||||||
| 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= | ||||||
| @@ -438,9 +485,8 @@ 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.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= | golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= | ||||||
| golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= |  | ||||||
| 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-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= | ||||||
| @@ -451,6 +497,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= | ||||||
| @@ -458,27 +505,22 @@ golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7w | |||||||
| golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
| golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
| golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
| golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |  | ||||||
| golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
| golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||||
|  | golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||||
| golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||||
| golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/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-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.1.0/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.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= | golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= | ||||||
| golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | golang.org/x/sys v0.31.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.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU= |  | ||||||
| golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= |  | ||||||
| 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= | ||||||
| @@ -487,10 +529,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.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= |  | ||||||
| golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= |  | ||||||
| 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= | ||||||
| @@ -518,9 +558,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.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= | golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= | ||||||
| golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= |  | ||||||
| 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= | ||||||
| @@ -537,6 +576,8 @@ 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.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= | ||||||
|  | google.golang.org/appengine v1.6.7/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= | ||||||
| @@ -550,6 +591,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-20230711160842-782d3b101e98 h1:bVf09lpb+OJbByTj913DRJioFFAjf/ZGxEz7MajTp2U= | ||||||
|  | google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM= | ||||||
| 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= | ||||||
| @@ -557,6 +600,12 @@ 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/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ= | ||||||
|  | google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= | ||||||
|  | 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.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= | ||||||
| @@ -566,8 +615,9 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= | |||||||
| 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.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.2.4/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.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= | ||||||
| gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||||||
| @@ -604,10 +654,8 @@ modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg= | |||||||
| modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= | modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= | ||||||
| modernc.org/z v1.7.3 h1:zDJf6iHjrnB+WRD88stbXokugjyc0/pB91ri1gO6LZY= | modernc.org/z v1.7.3 h1:zDJf6iHjrnB+WRD88stbXokugjyc0/pB91ri1gO6LZY= | ||||||
| modernc.org/z v1.7.3/go.mod h1:Ipv4tsdxZRbQyLq9Q1M6gdbkxYzdlrciF2Hi/lS7nWE= | modernc.org/z v1.7.3/go.mod h1:Ipv4tsdxZRbQyLq9Q1M6gdbkxYzdlrciF2Hi/lS7nWE= | ||||||
| mvdan.cc/sh/v3 v3.7.0 h1:lSTjdP/1xsddtaKfGg7Myu7DnlHItd3/M2tomOcNNBg= | mvdan.cc/sh/v3 v3.10.0 h1:v9z7N1DLZ7owyLM/SXZQkBSXcwr2IGMm2LY2pmhVXj4= | ||||||
| mvdan.cc/sh/v3 v3.7.0/go.mod h1:K2gwkaesF/D7av7Kxl0HbF5kGOd2ArupNTX3X44+8l8= | mvdan.cc/sh/v3 v3.10.0/go.mod h1:z/mSSVyLFGZzqb3ZIKojjyqIx/xbmz/UHdCSv9HmqXY= | ||||||
| plemya-x.ru/fakeroot v0.0.0-20240601131003-c638a3543283 h1:BXCLPeA8g2M6qYngicyxyB/2Bo4J54Q9Rb+8jMmE3ik= |  | ||||||
| plemya-x.ru/fakeroot v0.0.0-20240601131003-c638a3543283/go.mod h1:itzL9Jx52VXOhRaucFHuMpa3y7iwjnuLGdNvypoh/S4= |  | ||||||
| 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= | ||||||
|   | |||||||
							
								
								
									
										156
									
								
								helper.go
									
									
									
									
									
								
							
							
						
						
									
										156
									
								
								helper.go
									
									
									
									
									
								
							| @@ -1,86 +1,110 @@ | |||||||
|  | // 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 main | package main | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  | 	"log/slog" | ||||||
| 	"os" | 	"os" | ||||||
| 	"strings" | 	"strings" | ||||||
|  |  | ||||||
|  | 	"github.com/leonelquinteros/gotext" | ||||||
| 	"github.com/urfave/cli/v2" | 	"github.com/urfave/cli/v2" | ||||||
| 	"plemya-x.ru/alr/internal/cpu" |  | ||||||
| 	"plemya-x.ru/alr/internal/shutils/helpers" |  | ||||||
| 	"plemya-x.ru/alr/pkg/distro" |  | ||||||
| 	"plemya-x.ru/alr/pkg/loggerctx" |  | ||||||
| 	"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/shutils/helpers" | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| var helperCmd = &cli.Command{ | func HelperCmd() *cli.Command { | ||||||
| 	Name:        "helper", | 	helperListCmd := &cli.Command{ | ||||||
| 	Usage:       "Run a ALR helper command", | 		Name:    "list", | ||||||
| 	ArgsUsage:   `<helper_name|"list">`, | 		Usage:   gotext.Get("List all the available helper commands"), | ||||||
| 	Subcommands: []*cli.Command{helperListCmd}, | 		Aliases: []string{"ls"}, | ||||||
| 	Flags: []cli.Flag{ | 		Action: func(ctx *cli.Context) error { | ||||||
| 		&cli.StringFlag{ | 			for name := range helpers.Helpers { | ||||||
| 			Name:    "dest-dir", | 				fmt.Println(name) | ||||||
| 			Aliases: []string{"d"}, | 			} | ||||||
| 			Usage:   "The directory that the install commands will install to", | 			return nil | ||||||
| 			Value:   "dest", |  | ||||||
| 		}, | 		}, | ||||||
| 	}, | 	} | ||||||
| 	Action: func(c *cli.Context) error { |  | ||||||
| 		ctx := c.Context |  | ||||||
| 		log := loggerctx.From(ctx) |  | ||||||
|  |  | ||||||
| 		if c.Args().Len() < 1 { | 	return &cli.Command{ | ||||||
| 			cli.ShowSubcommandHelpAndExit(c, 1) | 		Name:        "helper", | ||||||
| 		} | 		Usage:       gotext.Get("Run a ALR helper command"), | ||||||
|  | 		ArgsUsage:   `<helper_name|"list">`, | ||||||
|  | 		Subcommands: []*cli.Command{helperListCmd}, | ||||||
|  | 		Flags: []cli.Flag{ | ||||||
|  | 			&cli.StringFlag{ | ||||||
|  | 				Name:    "dest-dir", | ||||||
|  | 				Aliases: []string{"d"}, | ||||||
|  | 				Usage:   gotext.Get("The directory that the install commands will install to"), | ||||||
|  | 				Value:   "dest", | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		Action: func(c *cli.Context) error { | ||||||
|  | 			ctx := c.Context | ||||||
|  |  | ||||||
| 		helper, ok := helpers.Helpers[c.Args().First()] | 			if c.Args().Len() < 1 { | ||||||
| 		if !ok { | 				cli.ShowSubcommandHelpAndExit(c, 1) | ||||||
| 			log.Fatal("No such helper command").Str("name", c.Args().First()).Send() | 			} | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		wd, err := os.Getwd() | 			helper, ok := helpers.Helpers[c.Args().First()] | ||||||
| 		if err != nil { | 			if !ok { | ||||||
| 			log.Fatal("Error getting working directory").Err(err).Send() | 				slog.Error(gotext.Get("No such helper command"), "name", c.Args().First()) | ||||||
| 		} | 				return cli.Exit(gotext.Get("No such helper command"), 1) | ||||||
|  | 			} | ||||||
|  |  | ||||||
| 		info, err := distro.ParseOSRelease(ctx) | 			wd, err := os.Getwd() | ||||||
| 		if err != nil { | 			if err != nil { | ||||||
| 			log.Fatal("Error getting working directory").Err(err).Send() | 				return cliutils.FormatCliExit(gotext.Get("Error getting working directory"), err) | ||||||
| 		} | 			} | ||||||
|  |  | ||||||
| 		hc := interp.HandlerContext{ | 			info, err := distro.ParseOSRelease(ctx) | ||||||
| 			Env: expand.ListEnviron( | 			if err != nil { | ||||||
| 				"pkgdir="+c.String("dest-dir"), | 				return cliutils.FormatCliExit(gotext.Get("Error parsing os-release file"), err) | ||||||
| 				"DISTRO_ID="+info.ID, | 			} | ||||||
| 				"DISTRO_ID_LIKE="+strings.Join(info.Like, " "), |  | ||||||
| 				"ARCH="+cpu.Arch(), |  | ||||||
| 			), |  | ||||||
| 			Dir:    wd, |  | ||||||
| 			Stdin:  os.Stdin, |  | ||||||
| 			Stdout: os.Stdout, |  | ||||||
| 			Stderr: os.Stderr, |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		return helper(hc, c.Args().First(), c.Args().Slice()[1:]) | 			hc := interp.HandlerContext{ | ||||||
| 	}, | 				Env: expand.ListEnviron( | ||||||
| 	CustomHelpTemplate: cli.CommandHelpTemplate, | 					"pkgdir="+c.String("dest-dir"), | ||||||
| 	BashComplete: func(ctx *cli.Context) { | 					"DISTRO_ID="+info.ID, | ||||||
| 		for name := range helpers.Helpers { | 					"DISTRO_ID_LIKE="+strings.Join(info.Like, " "), | ||||||
| 			fmt.Println(name) | 					"ARCH="+cpu.Arch(), | ||||||
| 		} | 				), | ||||||
| 	}, | 				Dir:    wd, | ||||||
| } | 				Stdin:  os.Stdin, | ||||||
|  | 				Stdout: os.Stdout, | ||||||
| var helperListCmd = &cli.Command{ | 				Stderr: os.Stderr, | ||||||
| 	Name:    "list", | 			} | ||||||
| 	Usage:   "List all the available helper commands", |  | ||||||
| 	Aliases: []string{"ls"}, | 			return helper(hc, c.Args().First(), c.Args().Slice()[1:]) | ||||||
| 	Action: func(ctx *cli.Context) error { | 		}, | ||||||
| 		for name := range helpers.Helpers { | 		CustomHelpTemplate: cli.CommandHelpTemplate, | ||||||
| 			fmt.Println(name) | 		BashComplete: func(ctx *cli.Context) { | ||||||
| 		} | 			for name := range helpers.Helpers { | ||||||
| 		return nil | 				fmt.Println(name) | ||||||
| 	}, | 			} | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										224
									
								
								info.go
									
									
									
									
									
								
							
							
						
						
									
										224
									
								
								info.go
									
									
									
									
									
								
							| @@ -1,20 +1,21 @@ | |||||||
| /* | // This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan. | ||||||
|  * ALR - Any Linux Repository | // It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors. | ||||||
|  * Copyright (C) 2024 Евгений Храмов | // | ||||||
|  * | // ALR - Any Linux Repository | ||||||
|  * This program is free software: you can redistribute it and/or modify | // Copyright (C) 2025 The ALR Authors | ||||||
|  * it under the terms of the GNU General Public License as published by | // | ||||||
|  * the Free Software Foundation, either version 3 of the License, or | // This program is free software: you can redistribute it and/or modify | ||||||
|  * (at your option) any later version. | // it under the terms of the GNU General Public License as published by | ||||||
|  * | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  * This program is distributed in the hope that it will be useful, | // (at your option) any later version. | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | // | ||||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | // This program is distributed in the hope that it will be useful, | ||||||
|  * GNU General Public License for more details. | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  * | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  * You should have received a copy of the GNU General Public License | // GNU General Public License for more details. | ||||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | // | ||||||
|  */ | // 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 | package main | ||||||
|  |  | ||||||
| @@ -22,85 +23,142 @@ import ( | |||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"os" | 	"os" | ||||||
|  |  | ||||||
|  | 	"github.com/jeandeaual/go-locale" | ||||||
|  | 	"github.com/leonelquinteros/gotext" | ||||||
| 	"github.com/urfave/cli/v2" | 	"github.com/urfave/cli/v2" | ||||||
| 	"plemya-x.ru/alr/internal/cliutils" |  | ||||||
| 	"plemya-x.ru/alr/internal/config" |  | ||||||
| 	"plemya-x.ru/alr/internal/overrides" |  | ||||||
| 	"plemya-x.ru/alr/pkg/distro" |  | ||||||
| 	"plemya-x.ru/alr/pkg/loggerctx" |  | ||||||
| 	"plemya-x.ru/alr/pkg/repos" |  | ||||||
| 	"gopkg.in/yaml.v3" | 	"gopkg.in/yaml.v3" | ||||||
|  |  | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils" | ||||||
|  | 	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/utils" | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| var infoCmd = &cli.Command{ | func InfoCmd() *cli.Command { | ||||||
| 	Name:  "info", | 	return &cli.Command{ | ||||||
| 	Usage: "Print information about a package", | 		Name:  "info", | ||||||
| 	Flags: []cli.Flag{ | 		Usage: gotext.Get("Print information about a package"), | ||||||
| 		&cli.BoolFlag{ | 		Flags: []cli.Flag{ | ||||||
| 			Name:    "all", | 			&cli.BoolFlag{ | ||||||
| 			Aliases: []string{"a"}, | 				Name:    "all", | ||||||
| 			Usage:   "Show all information, not just for the current distro", | 				Aliases: []string{"a"}, | ||||||
|  | 				Usage:   gotext.Get("Show all information, not just for the current distro"), | ||||||
|  | 			}, | ||||||
| 		}, | 		}, | ||||||
| 	}, | 		BashComplete: cliutils.BashCompleteWithError(func(c *cli.Context) error { | ||||||
| 	Action: func(c *cli.Context) error { | 			if err := utils.ExitIfCantDropCapsToAlrUser(); err != nil { | ||||||
| 		ctx := c.Context | 				return err | ||||||
| 		log := loggerctx.From(ctx) | 			} | ||||||
|  |  | ||||||
| 		args := c.Args() | 			ctx := c.Context | ||||||
| 		if args.Len() < 1 { | 			deps, err := appbuilder. | ||||||
| 			log.Fatalf("Command info expected at least 1 argument, got %d", args.Len()).Send() | 				New(ctx). | ||||||
| 		} | 				WithConfig(). | ||||||
|  | 				WithDB(). | ||||||
| 		err := repos.Pull(ctx, config.Config(ctx).Repos) | 				Build() | ||||||
| 		if err != nil { | 			if err != nil { | ||||||
| 			log.Fatal("Error pulling repositories").Err(err).Send() | 				return err | ||||||
| 		} | 			} | ||||||
|  | 			defer deps.Defer() | ||||||
| 		found, _, err := repos.FindPkgs(ctx, args.Slice()) |  | ||||||
| 		if err != nil { | 			result, err := deps.DB.GetPkgs(c.Context, "true") | ||||||
| 			log.Fatal("Error finding packages").Err(err).Send() | 			if err != nil { | ||||||
| 		} | 				return cliutils.FormatCliExit(gotext.Get("Error getting packages"), err) | ||||||
|  | 			} | ||||||
| 		if len(found) == 0 { | 			defer result.Close() | ||||||
| 			os.Exit(1) |  | ||||||
| 		} | 			for result.Next() { | ||||||
|  | 				var pkg database.Package | ||||||
| 		pkgs := cliutils.FlattenPkgs(ctx, found, "show", c.Bool("interactive")) | 				err = result.StructScan(&pkg) | ||||||
|  | 				if err != nil { | ||||||
| 		var names []string | 					return cliutils.FormatCliExit(gotext.Get("Error iterating over packages"), err) | ||||||
| 		all := c.Bool("all") | 				} | ||||||
|  |  | ||||||
| 		if !all { | 				fmt.Println(pkg.Name) | ||||||
| 			info, err := distro.ParseOSRelease(ctx) | 			} | ||||||
| 			if err != nil { | 			return nil | ||||||
| 				log.Fatal("Error parsing os-release file").Err(err).Send() | 		}), | ||||||
|  | 		Action: func(c *cli.Context) error { | ||||||
|  | 			if err := utils.ExitIfCantDropCapsToAlrUserNoPrivs(); err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			args := c.Args() | ||||||
|  | 			if args.Len() < 1 { | ||||||
|  | 				return cli.Exit(gotext.Get("Command info expected at least 1 argument, got %d", args.Len()), 1) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			ctx := c.Context | ||||||
|  |  | ||||||
|  | 			deps, err := appbuilder. | ||||||
|  | 				New(ctx). | ||||||
|  | 				WithConfig(). | ||||||
|  | 				WithDB(). | ||||||
|  | 				WithRepos(). | ||||||
|  | 				Build() | ||||||
|  | 			if err != nil { | ||||||
|  | 				return cli.Exit(err, 1) | ||||||
|  | 			} | ||||||
|  | 			defer deps.Defer() | ||||||
|  |  | ||||||
|  | 			rs := deps.Repos | ||||||
|  |  | ||||||
|  | 			found, _, err := rs.FindPkgs(ctx, args.Slice()) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return cliutils.FormatCliExit(gotext.Get("Error finding packages"), err) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if len(found) == 0 { | ||||||
|  | 				return cliutils.FormatCliExit(gotext.Get("Package not found"), err) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			pkgs := cliutils.FlattenPkgs(ctx, found, "show", c.Bool("interactive")) | ||||||
|  |  | ||||||
|  | 			var names []string | ||||||
|  | 			all := c.Bool("all") | ||||||
|  |  | ||||||
|  | 			systemLang, err := locale.GetLanguage() | ||||||
|  | 			if err != nil { | ||||||
|  | 				return cliutils.FormatCliExit(gotext.Get("Can't detect system language"), err) | ||||||
|  | 			} | ||||||
|  | 			if systemLang == "" { | ||||||
|  | 				systemLang = "en" | ||||||
| 			} | 			} | ||||||
| 			names, err = overrides.Resolve( |  | ||||||
| 				info, |  | ||||||
| 				overrides.DefaultOpts. |  | ||||||
| 					WithLanguages([]string{config.SystemLang()}), |  | ||||||
| 			) |  | ||||||
| 			if err != nil { |  | ||||||
| 				log.Fatal("Error resolving overrides").Err(err).Send() |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		for _, pkg := range pkgs { |  | ||||||
| 			if !all { | 			if !all { | ||||||
| 				err = yaml.NewEncoder(os.Stdout).Encode(overrides.ResolvePackage(&pkg, names)) | 				info, err := distro.ParseOSRelease(ctx) | ||||||
| 				if err != nil { | 				if err != nil { | ||||||
| 					log.Fatal("Error encoding script variables").Err(err).Send() | 					return cliutils.FormatCliExit(gotext.Get("Error parsing os-release file"), err) | ||||||
| 				} | 				} | ||||||
| 			} else { | 				names, err = overrides.Resolve( | ||||||
| 				err = yaml.NewEncoder(os.Stdout).Encode(pkg) | 					info, | ||||||
|  | 					overrides.DefaultOpts. | ||||||
|  | 						WithLanguages([]string{systemLang}), | ||||||
|  | 				) | ||||||
| 				if err != nil { | 				if err != nil { | ||||||
| 					log.Fatal("Error encoding script variables").Err(err).Send() | 					return cliutils.FormatCliExit(gotext.Get("Error resolving overrides"), err) | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			fmt.Println("---") | 			for _, pkg := range pkgs { | ||||||
| 		} | 				if !all { | ||||||
|  | 					err = yaml.NewEncoder(os.Stdout).Encode(overrides.ResolvePackage(&pkg, names)) | ||||||
|  | 					if err != nil { | ||||||
|  | 						return cliutils.FormatCliExit(gotext.Get("Error encoding script variables"), err) | ||||||
|  | 					} | ||||||
|  | 				} else { | ||||||
|  | 					err = yaml.NewEncoder(os.Stdout).Encode(pkg) | ||||||
|  | 					if err != nil { | ||||||
|  | 						return cliutils.FormatCliExit(gotext.Get("Error encoding script variables"), err) | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  |  | ||||||
| 		return nil | 				fmt.Println("---") | ||||||
| 	}, | 			} | ||||||
|  |  | ||||||
|  | 			return nil | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										320
									
								
								install.go
									
									
									
									
									
								
							
							
						
						
									
										320
									
								
								install.go
									
									
									
									
									
								
							| @@ -1,122 +1,236 @@ | |||||||
| /* | // This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan. | ||||||
|  * ALR - Any Linux Repository | // It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors. | ||||||
|  * Copyright (C) 2024 Евгений Храмов | // | ||||||
|  * | // ALR - Any Linux Repository | ||||||
|  * This program is free software: you can redistribute it and/or modify | // Copyright (C) 2025 The ALR Authors | ||||||
|  * it under the terms of the GNU General Public License as published by | // | ||||||
|  * the Free Software Foundation, either version 3 of the License, or | // This program is free software: you can redistribute it and/or modify | ||||||
|  * (at your option) any later version. | // it under the terms of the GNU General Public License as published by | ||||||
|  * | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  * This program is distributed in the hope that it will be useful, | // (at your option) any later version. | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | // | ||||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | // This program is distributed in the hope that it will be useful, | ||||||
|  * GNU General Public License for more details. | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  * | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  * You should have received a copy of the GNU General Public License | // GNU General Public License for more details. | ||||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | // | ||||||
|  */ | // 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 | package main | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  |  | ||||||
|  | 	"github.com/leonelquinteros/gotext" | ||||||
| 	"github.com/urfave/cli/v2" | 	"github.com/urfave/cli/v2" | ||||||
| 	"plemya-x.ru/alr/internal/cliutils" |  | ||||||
| 	"plemya-x.ru/alr/internal/config" | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils" | ||||||
| 	"plemya-x.ru/alr/internal/db" | 	appbuilder "gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils/app_builder" | ||||||
| 	"plemya-x.ru/alr/internal/types" | 	database "gitea.plemya-x.ru/Plemya-x/ALR/internal/db" | ||||||
| 	"plemya-x.ru/alr/pkg/build" | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/types" | ||||||
| 	"plemya-x.ru/alr/pkg/loggerctx" | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/utils" | ||||||
| 	"plemya-x.ru/alr/pkg/manager" | 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/build" | ||||||
| 	"plemya-x.ru/alr/pkg/repos" | 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| var installCmd = &cli.Command{ | func InstallCmd() *cli.Command { | ||||||
| 	Name:    "install", | 	return &cli.Command{ | ||||||
| 	Usage:   "Install a new package", | 		Name:    "install", | ||||||
| 	Aliases: []string{"in"}, | 		Usage:   gotext.Get("Install a new package"), | ||||||
| 	Flags: []cli.Flag{ | 		Aliases: []string{"in"}, | ||||||
| 		&cli.BoolFlag{ | 		Flags: []cli.Flag{ | ||||||
| 			Name:    "clean", | 			&cli.BoolFlag{ | ||||||
| 			Aliases: []string{"c"}, | 				Name:    "clean", | ||||||
| 			Usage:   "Build package from scratch even if there's an already built package available", | 				Aliases: []string{"c"}, | ||||||
|  | 				Usage:   gotext.Get("Build package from scratch even if there's an already built package available"), | ||||||
|  | 			}, | ||||||
| 		}, | 		}, | ||||||
| 	}, | 		Action: utils.RootNeededAction(func(c *cli.Context) error { | ||||||
| 	Action: func(c *cli.Context) error { | 			args := c.Args() | ||||||
| 		ctx := c.Context | 			if args.Len() < 1 { | ||||||
| 		log := loggerctx.From(ctx) | 				return cliutils.FormatCliExit(gotext.Get("Command install expected at least 1 argument, got %d", args.Len()), nil) | ||||||
|  |  | ||||||
| 		args := c.Args() |  | ||||||
| 		if args.Len() < 1 { |  | ||||||
| 			log.Fatalf("Command install expected at least 1 argument, got %d", args.Len()).Send() |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		mgr := manager.Detect() |  | ||||||
| 		if mgr == nil { |  | ||||||
| 			log.Fatal("Unable to detect a supported package manager on the system").Send() |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		err := repos.Pull(ctx, config.Config(ctx).Repos) |  | ||||||
| 		if err != nil { |  | ||||||
| 			log.Fatal("Error pulling repositories").Err(err).Send() |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		found, notFound, err := repos.FindPkgs(ctx, args.Slice()) |  | ||||||
| 		if err != nil { |  | ||||||
| 			log.Fatal("Error finding packages").Err(err).Send() |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		pkgs := cliutils.FlattenPkgs(ctx, found, "install", c.Bool("interactive")) |  | ||||||
| 		build.InstallPkgs(ctx, pkgs, notFound, types.BuildOpts{ |  | ||||||
| 			Manager:     mgr, |  | ||||||
| 			Clean:       c.Bool("clean"), |  | ||||||
| 			Interactive: c.Bool("interactive"), |  | ||||||
| 		}) |  | ||||||
| 		return nil |  | ||||||
| 	}, |  | ||||||
| 	BashComplete: func(c *cli.Context) { |  | ||||||
| 		log := loggerctx.From(c.Context) |  | ||||||
| 		result, err := db.GetPkgs(c.Context, "true") |  | ||||||
| 		if err != nil { |  | ||||||
| 			log.Fatal("Error getting packages").Err(err).Send() |  | ||||||
| 		} |  | ||||||
| 		defer result.Close() |  | ||||||
|  |  | ||||||
| 		for result.Next() { |  | ||||||
| 			var pkg db.Package |  | ||||||
| 			err = result.StructScan(&pkg) |  | ||||||
| 			if err != nil { |  | ||||||
| 				log.Fatal("Error iterating over packages").Err(err).Send() |  | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			fmt.Println(pkg.Name) | 			if err := utils.ExitIfCantDropCapsToAlrUser(); err != nil { | ||||||
| 		} | 				return err | ||||||
| 	}, | 			} | ||||||
|  |  | ||||||
|  | 			installer, installerClose, err := build.GetSafeInstaller() | ||||||
|  | 			if err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  | 			defer installerClose() | ||||||
|  |  | ||||||
|  | 			if err := utils.ExitIfCantSetNoNewPrivs(); err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			scripter, scripterClose, err := build.GetSafeScriptExecutor() | ||||||
|  | 			if err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  | 			defer scripterClose() | ||||||
|  |  | ||||||
|  | 			ctx := c.Context | ||||||
|  |  | ||||||
|  | 			deps, err := appbuilder. | ||||||
|  | 				New(ctx). | ||||||
|  | 				WithConfig(). | ||||||
|  | 				WithDB(). | ||||||
|  | 				WithRepos(). | ||||||
|  | 				WithDistroInfo(). | ||||||
|  | 				WithManager(). | ||||||
|  | 				Build() | ||||||
|  | 			if err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  | 			defer deps.Defer() | ||||||
|  |  | ||||||
|  | 			builder, err := build.NewMainBuilder( | ||||||
|  | 				deps.Cfg, | ||||||
|  | 				deps.Manager, | ||||||
|  | 				deps.Repos, | ||||||
|  | 				scripter, | ||||||
|  | 				installer, | ||||||
|  | 			) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			err = builder.InstallPkgs( | ||||||
|  | 				ctx, | ||||||
|  | 				&build.BuildArgs{ | ||||||
|  | 					Opts: &types.BuildOpts{ | ||||||
|  | 						Clean:       c.Bool("clean"), | ||||||
|  | 						Interactive: c.Bool("interactive"), | ||||||
|  | 					}, | ||||||
|  | 					Info:       deps.Info, | ||||||
|  | 					PkgFormat_: build.GetPkgFormat(deps.Manager), | ||||||
|  | 				}, | ||||||
|  | 				args.Slice(), | ||||||
|  | 			) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return cliutils.FormatCliExit(gotext.Get("Error when installing the package"), err) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			return nil | ||||||
|  | 		}), | ||||||
|  | 		BashComplete: cliutils.BashCompleteWithError(func(c *cli.Context) error { | ||||||
|  | 			if err := utils.ExitIfCantDropCapsToAlrUser(); err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			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) | ||||||
|  | 			} | ||||||
|  | 			defer result.Close() | ||||||
|  |  | ||||||
|  | 			for result.Next() { | ||||||
|  | 				var pkg database.Package | ||||||
|  | 				err = result.StructScan(&pkg) | ||||||
|  | 				if err != nil { | ||||||
|  | 					return cliutils.FormatCliExit(gotext.Get("Error iterating over packages"), err) | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				fmt.Println(pkg.Name) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			return nil | ||||||
|  | 		}), | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| var removeCmd = &cli.Command{ | func RemoveCmd() *cli.Command { | ||||||
| 	Name:    "remove", | 	return &cli.Command{ | ||||||
| 	Usage:   "Remove an installed package", | 		Name:    "remove", | ||||||
| 	Aliases: []string{"rm"}, | 		Usage:   gotext.Get("Remove an installed package"), | ||||||
| 	Action: func(c *cli.Context) error { | 		Aliases: []string{"rm"}, | ||||||
| 		log := loggerctx.From(c.Context) | 		BashComplete: cliutils.BashCompleteWithError(func(c *cli.Context) error { | ||||||
|  | 			ctx := c.Context | ||||||
|  |  | ||||||
| 		args := c.Args() | 			deps, err := appbuilder. | ||||||
| 		if args.Len() < 1 { | 				New(ctx). | ||||||
| 			log.Fatalf("Command remove expected at least 1 argument, got %d", args.Len()).Send() | 				WithConfig(). | ||||||
| 		} | 				WithDB(). | ||||||
|  | 				WithManager(). | ||||||
|  | 				Build() | ||||||
|  | 			if err != nil { | ||||||
|  | 				return cli.Exit(err, 1) | ||||||
|  | 			} | ||||||
|  | 			defer deps.Defer() | ||||||
|  |  | ||||||
| 		mgr := manager.Detect() | 			installedAlrPackages := map[string]string{} | ||||||
| 		if mgr == nil { | 			installed, err := deps.Manager.ListInstalled(&manager.Opts{}) | ||||||
| 			log.Fatal("Unable to detect a supported package manager on the system").Send() | 			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 | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  |  | ||||||
| 		err := mgr.Remove(nil, c.Args().Slice()...) | 			result, err := deps.DB.GetPkgs(c.Context, "true") | ||||||
| 		if err != nil { | 			if err != nil { | ||||||
| 			log.Fatal("Error removing packages").Err(err).Send() | 				return cliutils.FormatCliExit(gotext.Get("Error getting packages"), err) | ||||||
| 		} | 			} | ||||||
|  | 			defer result.Close() | ||||||
|  |  | ||||||
| 		return nil | 			for result.Next() { | ||||||
| 	}, | 				var pkg database.Package | ||||||
|  | 				err = result.StructScan(&pkg) | ||||||
|  | 				if err != nil { | ||||||
|  | 					return cliutils.FormatCliExit(gotext.Get("Error iterating over packages"), err) | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				_, ok := installedAlrPackages[fmt.Sprintf("%s/%s", pkg.Repository, pkg.Name)] | ||||||
|  | 				if !ok { | ||||||
|  | 					continue | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				fmt.Println(pkg.Name) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			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 | ||||||
|  | 		}), | ||||||
|  | 	} | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										280
									
								
								internal.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										280
									
								
								internal.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,280 @@ | |||||||
|  | // 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/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/utils" | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/build" | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | 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 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.InstallerPlugin{ | ||||||
|  | 						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 | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										176
									
								
								internal/cliutils/app_builder/builder.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										176
									
								
								internal/cliutils/app_builder/builder.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,176 @@ | |||||||
|  | // 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/pkg/distro" | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager" | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/repos" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | 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 | ||||||
|  | 	if cfg == nil || db == nil { | ||||||
|  | 		b.err = errors.New("config and db 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,34 +1,35 @@ | |||||||
| /* | // This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan. | ||||||
|  * ALR - Any Linux Repository | // It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors. | ||||||
|  * Copyright (C) 2024 Евгений Храмов | // | ||||||
|  * | // ALR - Any Linux Repository | ||||||
|  * This program is free software: you can redistribute it and/or modify | // Copyright (C) 2025 The ALR Authors | ||||||
|  * it under the terms of the GNU General Public License as published by | // | ||||||
|  * the Free Software Foundation, either version 3 of the License, or | // This program is free software: you can redistribute it and/or modify | ||||||
|  * (at your option) any later version. | // it under the terms of the GNU General Public License as published by | ||||||
|  * | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  * This program is distributed in the hope that it will be useful, | // (at your option) any later version. | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | // | ||||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | // This program is distributed in the hope that it will be useful, | ||||||
|  * GNU General Public License for more details. | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  * | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  * You should have received a copy of the GNU General Public License | // GNU General Public License for more details. | ||||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | // | ||||||
|  */ | // 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 | package cliutils | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
|  | 	"log/slog" | ||||||
| 	"os" | 	"os" | ||||||
| 	"strings" | 	"strings" | ||||||
|  |  | ||||||
| 	"github.com/AlecAivazis/survey/v2" | 	"github.com/AlecAivazis/survey/v2" | ||||||
| 	"plemya-x.ru/alr/internal/config" | 	"github.com/leonelquinteros/gotext" | ||||||
| 	"plemya-x.ru/alr/internal/db" |  | ||||||
| 	"plemya-x.ru/alr/internal/pager" | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/db" | ||||||
| 	"plemya-x.ru/alr/internal/translations" | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/pager" | ||||||
| 	"plemya-x.ru/alr/pkg/loggerctx" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // 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 | ||||||
| @@ -37,7 +38,7 @@ func YesNoPrompt(ctx context.Context, msg string, interactive, def bool) (bool, | |||||||
| 		var answer bool | 		var answer bool | ||||||
| 		err := survey.AskOne( | 		err := survey.AskOne( | ||||||
| 			&survey.Confirm{ | 			&survey.Confirm{ | ||||||
| 				Message: translations.Translator(ctx).TranslateTo(msg, config.Language(ctx)), | 				Message: msg, | ||||||
| 				Default: def, | 				Default: def, | ||||||
| 			}, | 			}, | ||||||
| 			&answer, | 			&answer, | ||||||
| @@ -52,14 +53,11 @@ func YesNoPrompt(ctx context.Context, msg string, interactive, def bool) (bool, | |||||||
| // shows it if they answer yes, then asks if they'd still like to | // shows it if they answer yes, then asks if they'd still like to | ||||||
| // continue, and exits if they answer no. | // continue, and exits if they answer no. | ||||||
| func PromptViewScript(ctx context.Context, script, name, style string, interactive bool) error { | func PromptViewScript(ctx context.Context, script, name, style string, interactive bool) error { | ||||||
| 	log := loggerctx.From(ctx) |  | ||||||
|  |  | ||||||
| 	if !interactive { | 	if !interactive { | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	scriptPrompt := translations.Translator(ctx).TranslateTo("Would you like to view the build script for", config.Language(ctx)) + " " + name | 	view, err := YesNoPrompt(ctx, gotext.Get("Would you like to view the build script for %s", name), interactive, false) | ||||||
| 	view, err := YesNoPrompt(ctx, scriptPrompt, interactive, false) |  | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| @@ -70,13 +68,14 @@ func PromptViewScript(ctx context.Context, script, name, style string, interacti | |||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		cont, err := YesNoPrompt(ctx, "Would you still like to continue?", interactive, false) | 		cont, err := YesNoPrompt(ctx, gotext.Get("Would you still like to continue?"), interactive, false) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if !cont { | 		if !cont { | ||||||
| 			log.Fatal(translations.Translator(ctx).TranslateTo("User chose not to continue after reading script", config.Language(ctx))).Send() | 			slog.Error(gotext.Get("User chose not to continue after reading script")) | ||||||
|  | 			os.Exit(1) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -104,13 +103,13 @@ 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][]db.Package, verb string, interactive bool) []db.Package { | ||||||
| 	log := loggerctx.From(ctx) |  | ||||||
| 	var outPkgs []db.Package | 	var outPkgs []db.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) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				log.Fatal("Error prompting for choice of package").Send() | 				slog.Error(gotext.Get("Error prompting for choice of package")) | ||||||
|  | 				os.Exit(1) | ||||||
| 			} | 			} | ||||||
| 			outPkgs = append(outPkgs, choice) | 			outPkgs = append(outPkgs, choice) | ||||||
| 		} else if len(pkgs) == 1 || !interactive { | 		} else if len(pkgs) == 1 || !interactive { | ||||||
| @@ -133,7 +132,7 @@ func PkgPrompt(ctx context.Context, options []db.Package, verb string, interacti | |||||||
|  |  | ||||||
| 	prompt := &survey.Select{ | 	prompt := &survey.Select{ | ||||||
| 		Options: names, | 		Options: names, | ||||||
| 		Message: translations.Translator(ctx).TranslateTo("Choose which package to "+verb, config.Language(ctx)), | 		Message: gotext.Get("Choose which package to %s", verb), | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	var choice int | 	var choice int | ||||||
| @@ -154,7 +153,7 @@ func ChooseOptDepends(ctx context.Context, options []string, verb string, intera | |||||||
|  |  | ||||||
| 	prompt := &survey.MultiSelect{ | 	prompt := &survey.MultiSelect{ | ||||||
| 		Options: options, | 		Options: options, | ||||||
| 		Message: translations.Translator(ctx).TranslateTo("Choose which optional package(s) to install", config.Language(ctx)), | 		Message: gotext.Get("Choose which optional package(s) to install"), | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	var choices []int | 	var choices []int | ||||||
|   | |||||||
							
								
								
									
										102
									
								
								internal/cliutils/template.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								internal/cliutils/template.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,102 @@ | |||||||
|  | // 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 ( | ||||||
|  | 	"fmt" | ||||||
|  |  | ||||||
|  | 	"github.com/leonelquinteros/gotext" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Templates are based on https://github.com/urfave/cli/blob/3b17080d70a630feadadd23dd036cad121dd9a50/template.go | ||||||
|  |  | ||||||
|  | //nolint:unused | ||||||
|  | var ( | ||||||
|  | 	helpNameTemplate    = `{{$v := offset .HelpName 6}}{{wrap .HelpName 3}}{{if .Usage}} - {{wrap .Usage $v}}{{end}}` | ||||||
|  | 	descriptionTemplate = `{{wrap .Description 3}}` | ||||||
|  | 	authorsTemplate     = `{{with $length := len .Authors}}{{if ne 1 $length}}S{{end}}{{end}}: | ||||||
|  |    {{range $index, $author := .Authors}}{{if $index}} | ||||||
|  |    {{end}}{{$author}}{{end}}` | ||||||
|  | 	visibleCommandTemplate = `{{ $cv := offsetCommands .VisibleCommands 5}}{{range .VisibleCommands}} | ||||||
|  |    {{$s := join .Names ", "}}{{$s}}{{ $sp := subtract $cv (offset $s 3) }}{{ indent $sp ""}}{{wrap .Usage $cv}}{{end}}` | ||||||
|  | 	visibleCommandCategoryTemplate = `{{range .VisibleCategories}}{{if .Name}} | ||||||
|  |    {{.Name}}:{{range .VisibleCommands}} | ||||||
|  |      {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{else}}{{template "visibleCommandTemplate" .}}{{end}}{{end}}` | ||||||
|  | 	visibleFlagCategoryTemplate = `{{range .VisibleFlagCategories}} | ||||||
|  |    {{if .Name}}{{.Name}} | ||||||
|  |  | ||||||
|  |    {{end}}{{$flglen := len .Flags}}{{range $i, $e := .Flags}}{{if eq (subtract $flglen $i) 1}}{{$e}} | ||||||
|  | {{else}}{{$e}} | ||||||
|  |    {{end}}{{end}}{{end}}` | ||||||
|  | 	visibleFlagTemplate = `{{range $i, $e := .VisibleFlags}} | ||||||
|  |    {{wrap $e.String 6}}{{end}}` | ||||||
|  | 	copyrightTemplate = `{{wrap .Copyright 3}}` | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func GetAppCliTemplate() string { | ||||||
|  | 	return fmt.Sprintf(`%s: | ||||||
|  | 	{{template "helpNameTemplate" .}} | ||||||
|  |  | ||||||
|  | %s: | ||||||
|  | 	{{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[%s]{{end}}{{if .Commands}} %s [%s]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[%s...]{{end}}{{end}}{{if .Version}}{{if not .HideVersion}} | ||||||
|  |  | ||||||
|  | %s: | ||||||
|  | 	{{.Version}}{{end}}{{end}}{{if .Description}} | ||||||
|  |  | ||||||
|  | %s: | ||||||
|  |    {{template "descriptionTemplate" .}}{{end}} | ||||||
|  | {{- if len .Authors}} | ||||||
|  |  | ||||||
|  | %s{{template "authorsTemplate" .}}{{end}}{{if .VisibleCommands}} | ||||||
|  |  | ||||||
|  | %s:{{template "visibleCommandCategoryTemplate" .}}{{end}}{{if .VisibleFlagCategories}} | ||||||
|  |  | ||||||
|  | %s:{{template "visibleFlagCategoryTemplate" .}}{{else if .VisibleFlags}} | ||||||
|  |  | ||||||
|  | %s:{{template "visibleFlagTemplate" .}}{{end}}{{if .Copyright}} | ||||||
|  |  | ||||||
|  | %s: | ||||||
|  |    {{template "copyrightTemplate" .}}{{end}} | ||||||
|  | `, gotext.Get("NAME"), gotext.Get("USAGE"), gotext.Get("global options"), gotext.Get("command"), gotext.Get("command options"), gotext.Get("arguments"), gotext.Get("VERSION"), gotext.Get("DESCRIPTION"), gotext.Get("AUTHOR"), gotext.Get("COMMANDS"), gotext.Get("GLOBAL OPTIONS"), gotext.Get("GLOBAL OPTIONS"), gotext.Get("COPYRIGHT")) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func GetCommandHelpTemplate() string { | ||||||
|  | 	return fmt.Sprintf(`%s: | ||||||
|  |    {{template "helpNameTemplate" .}} | ||||||
|  |  | ||||||
|  | %s: | ||||||
|  |    {{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}}{{if .VisibleFlags}} [%s]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[%s...]{{end}}{{end}}{{if .Category}} | ||||||
|  |  | ||||||
|  | %s: | ||||||
|  |    {{.Category}}{{end}}{{if .Description}} | ||||||
|  |  | ||||||
|  | %s: | ||||||
|  |    {{template "descriptionTemplate" .}}{{end}}{{if .VisibleFlagCategories}} | ||||||
|  |  | ||||||
|  | %s:{{template "visibleFlagCategoryTemplate" .}}{{else if .VisibleFlags}} | ||||||
|  |  | ||||||
|  | %s:{{template "visibleFlagTemplate" .}}{{end}} | ||||||
|  | `, gotext.Get("NAME"), | ||||||
|  | 		gotext.Get("USAGE"), | ||||||
|  | 		gotext.Get("command options"), | ||||||
|  | 		gotext.Get("arguments"), | ||||||
|  | 		gotext.Get("CATEGORY"), | ||||||
|  | 		gotext.Get("DESCRIPTION"), | ||||||
|  | 		gotext.Get("OPTIONS"), | ||||||
|  | 		gotext.Get("OPTIONS"), | ||||||
|  | 	) | ||||||
|  | } | ||||||
							
								
								
									
										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,79 +1,161 @@ | |||||||
| /* | // This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan. | ||||||
|  * ALR - Any Linux Repository | // It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors. | ||||||
|  * Copyright (C) 2024 Евгений Храмов | // | ||||||
|  * | // ALR - Any Linux Repository | ||||||
|  * This program is free software: you can redistribute it and/or modify | // Copyright (C) 2025 The ALR Authors | ||||||
|  * it under the terms of the GNU General Public License as published by | // | ||||||
|  * the Free Software Foundation, either version 3 of the License, or | // This program is free software: you can redistribute it and/or modify | ||||||
|  * (at your option) any later version. | // it under the terms of the GNU General Public License as published by | ||||||
|  * | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  * This program is distributed in the hope that it will be useful, | // (at your option) any later version. | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | // | ||||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | // This program is distributed in the hope that it will be useful, | ||||||
|  * GNU General Public License for more details. | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  * | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  * You should have received a copy of the GNU General Public License | // GNU General Public License for more details. | ||||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | // | ||||||
|  */ | // 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 | package config | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"log/slog" | ||||||
| 	"os" | 	"os" | ||||||
| 	"sync" | 	"path/filepath" | ||||||
|  | 	"reflect" | ||||||
|  |  | ||||||
|  | 	"github.com/caarlos0/env" | ||||||
| 	"github.com/pelletier/go-toml/v2" | 	"github.com/pelletier/go-toml/v2" | ||||||
| 	"plemya-x.ru/alr/internal/types" |  | ||||||
| 	"plemya-x.ru/alr/pkg/loggerctx" | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/constants" | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/types" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | type ALRConfig struct { | ||||||
|  | 	cfg   *types.Config | ||||||
|  | 	paths *Paths | ||||||
|  | } | ||||||
|  |  | ||||||
| var defaultConfig = &types.Config{ | var defaultConfig = &types.Config{ | ||||||
| 	RootCmd:          "sudo", | 	RootCmd:          "sudo", | ||||||
|  | 	UseRootCmd:       true, | ||||||
| 	PagerStyle:       "native", | 	PagerStyle:       "native", | ||||||
| 	IgnorePkgUpdates: []string{}, | 	IgnorePkgUpdates: []string{}, | ||||||
| 	Repos: []types.Repo{ | 	AutoPull:         true, | ||||||
| 		{ | 	Repos:            []types.Repo{}, | ||||||
| 			Name: "default", |  | ||||||
| 			URL:  "https://gitea.plemya-x.ru/xpamych/xpamych-alr-repo.git", |  | ||||||
| 		}, |  | ||||||
| 	}, |  | ||||||
| } | } | ||||||
|  |  | ||||||
| var ( | func New() *ALRConfig { | ||||||
| 	configMtx sync.Mutex | 	return &ALRConfig{} | ||||||
| 	config    *types.Config | } | ||||||
| ) |  | ||||||
|  |  | ||||||
| // Config returns a ALR configuration struct. | func readConfig(path string) (*types.Config, error) { | ||||||
| // The first time it's called, it'll load the config from a file. | 	file, err := os.Open(path) | ||||||
| // Subsequent calls will just return the same value. | 	if err != nil { | ||||||
| func Config(ctx context.Context) *types.Config { | 		return nil, err | ||||||
| 	configMtx.Lock() | 	} | ||||||
| 	defer configMtx.Unlock() | 	defer file.Close() | ||||||
| 	log := loggerctx.From(ctx) |  | ||||||
|  |  | ||||||
| 	if config == nil { | 	config := types.Config{} | ||||||
| 		cfgFl, err := os.Open(GetPaths(ctx).ConfigPath) |  | ||||||
| 		if err != nil { |  | ||||||
| 			log.Warn("Error opening config file, using defaults").Err(err).Send() |  | ||||||
| 			return defaultConfig |  | ||||||
| 		} |  | ||||||
| 		defer cfgFl.Close() |  | ||||||
|  |  | ||||||
| 		// Copy the default configuration into config | 	if err := toml.NewDecoder(file).Decode(&config); err != nil { | ||||||
| 		defCopy := *defaultConfig | 		return nil, err | ||||||
| 		config = &defCopy |  | ||||||
| 		config.Repos = nil |  | ||||||
|  |  | ||||||
| 		err = toml.NewDecoder(cfgFl).Decode(config) |  | ||||||
| 		if err != nil { |  | ||||||
| 			log.Warn("Error decoding config file, using defaults").Err(err).Send() |  | ||||||
| 			// Set config back to nil so that we try again next time |  | ||||||
| 			config = nil |  | ||||||
| 			return defaultConfig |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return config | 	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) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *ALRConfig) Load() error { | ||||||
|  | 	systemConfig, err := readConfig( | ||||||
|  | 		constants.SystemConfigPath, | ||||||
|  | 	) | ||||||
|  | 	if err != nil { | ||||||
|  | 		slog.Debug("Cannot read system config", "err", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	config := &types.Config{} | ||||||
|  |  | ||||||
|  | 	mergeStructs(config, defaultConfig) | ||||||
|  | 	mergeStructs(config, systemConfig) | ||||||
|  | 	err = env.Parse(config) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	c.cfg = config | ||||||
|  |  | ||||||
|  | 	c.paths = &Paths{} | ||||||
|  | 	c.paths.UserConfigPath = constants.SystemConfigPath | ||||||
|  | 	c.paths.CacheDir = constants.SystemCachePath | ||||||
|  | 	c.paths.RepoDir = filepath.Join(c.paths.CacheDir, "repo") | ||||||
|  | 	c.paths.PkgsDir = filepath.Join(c.paths.CacheDir, "pkgs") | ||||||
|  | 	c.paths.DBPath = filepath.Join(c.paths.CacheDir, "db") | ||||||
|  | 	// c.initPaths() | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *ALRConfig) RootCmd() string { | ||||||
|  | 	return c.cfg.RootCmd | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *ALRConfig) PagerStyle() string { | ||||||
|  | 	return c.cfg.PagerStyle | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *ALRConfig) AutoPull() bool { | ||||||
|  | 	return c.cfg.AutoPull | ||||||
|  | } | ||||||
|  |  | ||||||
|  | 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) UseRootCmd() bool { | ||||||
|  | 	return c.cfg.UseRootCmd | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *ALRConfig) GetPaths() *Paths { | ||||||
|  | 	return c.paths | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *ALRConfig) SaveUserConfig() error { | ||||||
|  | 	f, err := os.Create(c.paths.UserConfigPath) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return toml.NewEncoder(f).Encode(c.cfg) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,67 +0,0 @@ | |||||||
| /* |  | ||||||
|  * ALR - Any Linux Repository |  | ||||||
|  * Copyright (C) 2024 Евгений Храмов |  | ||||||
|  * |  | ||||||
|  * 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 ( |  | ||||||
| 	"context" |  | ||||||
| 	"os" |  | ||||||
| 	"strings" |  | ||||||
| 	"sync" |  | ||||||
|  |  | ||||||
| 	"plemya-x.ru/alr/pkg/loggerctx" |  | ||||||
| 	"golang.org/x/text/language" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| var ( |  | ||||||
| 	langMtx sync.Mutex |  | ||||||
| 	lang    language.Tag |  | ||||||
| 	langSet bool |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // Language returns the system language. |  | ||||||
| // The first time it's called, it'll detect the langauge based on |  | ||||||
| // the $LANG environment variable. |  | ||||||
| // Subsequent calls will just return the same value. |  | ||||||
| func Language(ctx context.Context) language.Tag { |  | ||||||
| 	langMtx.Lock() |  | ||||||
| 	defer langMtx.Unlock() |  | ||||||
| 	log := loggerctx.From(ctx) |  | ||||||
| 	if !langSet { |  | ||||||
| 		syslang := SystemLang() |  | ||||||
| 		tag, err := language.Parse(syslang) |  | ||||||
| 		if err != nil { |  | ||||||
| 			log.Fatal("Error parsing system language").Err(err).Send() |  | ||||||
| 		} |  | ||||||
| 		base, _ := tag.Base() |  | ||||||
| 		lang = language.Make(base.String()) |  | ||||||
| 		langSet = true |  | ||||||
| 	} |  | ||||||
| 	return lang |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // SystemLang returns the system language based on |  | ||||||
| // the $LANG environment variable. |  | ||||||
| func SystemLang() string { |  | ||||||
| 	lang := os.Getenv("LANG") |  | ||||||
| 	lang, _, _ = strings.Cut(lang, ".") |  | ||||||
| 	if lang == "" || lang == "C" { |  | ||||||
| 		lang = "en" |  | ||||||
| 	} |  | ||||||
| 	return lang |  | ||||||
| } |  | ||||||
| @@ -1,108 +1,29 @@ | |||||||
| /* | // This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan. | ||||||
|  * ALR - Any Linux Repository | // It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors. | ||||||
|  * Copyright (C) 2024 Евгений Храмов | // | ||||||
|  * | // ALR - Any Linux Repository | ||||||
|  * This program is free software: you can redistribute it and/or modify | // Copyright (C) 2025 The ALR Authors | ||||||
|  * it under the terms of the GNU General Public License as published by | // | ||||||
|  * the Free Software Foundation, either version 3 of the License, or | // This program is free software: you can redistribute it and/or modify | ||||||
|  * (at your option) any later version. | // it under the terms of the GNU General Public License as published by | ||||||
|  * | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  * This program is distributed in the hope that it will be useful, | // (at your option) any later version. | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | // | ||||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | // This program is distributed in the hope that it will be useful, | ||||||
|  * GNU General Public License for more details. | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  * | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  * You should have received a copy of the GNU General Public License | // GNU General Public License for more details. | ||||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | // | ||||||
|  */ | // 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 | package config | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"context" |  | ||||||
| 	"os" |  | ||||||
| 	"path/filepath" |  | ||||||
| 	"sync" |  | ||||||
|  |  | ||||||
| 	"github.com/pelletier/go-toml/v2" |  | ||||||
| 	"plemya-x.ru/alr/pkg/loggerctx" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // Paths contains various paths used by ALR | // Paths contains various paths used by ALR | ||||||
| type Paths struct { | type Paths struct { | ||||||
| 	ConfigDir  string | 	UserConfigPath string | ||||||
| 	ConfigPath string | 	CacheDir       string | ||||||
| 	CacheDir   string | 	RepoDir        string | ||||||
| 	RepoDir    string | 	PkgsDir        string | ||||||
| 	PkgsDir    string | 	DBPath         string | ||||||
| 	DBPath     string |  | ||||||
| } |  | ||||||
|  |  | ||||||
| var ( |  | ||||||
| 	pathsMtx sync.Mutex |  | ||||||
| 	paths    *Paths |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // GetPaths returns a Paths struct. |  | ||||||
| // The first time it's called, it'll generate the struct |  | ||||||
| // using information from the system. |  | ||||||
| // Subsequent calls will return the same value. |  | ||||||
| func GetPaths(ctx context.Context) *Paths { |  | ||||||
| 	pathsMtx.Lock() |  | ||||||
| 	defer pathsMtx.Unlock() |  | ||||||
|  |  | ||||||
| 	log := loggerctx.From(ctx) |  | ||||||
| 	if paths == nil { |  | ||||||
| 		paths = &Paths{} |  | ||||||
|  |  | ||||||
| 		cfgDir, err := os.UserConfigDir() |  | ||||||
| 		if err != nil { |  | ||||||
| 			log.Fatal("Unable to detect user config directory").Err(err).Send() |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		paths.ConfigDir = filepath.Join(cfgDir, "alr") |  | ||||||
|  |  | ||||||
| 		err = os.MkdirAll(paths.ConfigDir, 0o755) |  | ||||||
| 		if err != nil { |  | ||||||
| 			log.Fatal("Unable to create ALR config directory").Err(err).Send() |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		paths.ConfigPath = filepath.Join(paths.ConfigDir, "alr.toml") |  | ||||||
|  |  | ||||||
| 		if _, err := os.Stat(paths.ConfigPath); err != nil { |  | ||||||
| 			cfgFl, err := os.Create(paths.ConfigPath) |  | ||||||
| 			if err != nil { |  | ||||||
| 				log.Fatal("Unable to create ALR config file").Err(err).Send() |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			err = toml.NewEncoder(cfgFl).Encode(&defaultConfig) |  | ||||||
| 			if err != nil { |  | ||||||
| 				log.Fatal("Error encoding default configuration").Err(err).Send() |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			cfgFl.Close() |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		cacheDir, err := os.UserCacheDir() |  | ||||||
| 		if err != nil { |  | ||||||
| 			log.Fatal("Unable to detect cache directory").Err(err).Send() |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		paths.CacheDir = filepath.Join(cacheDir, "alr") |  | ||||||
| 		paths.RepoDir = filepath.Join(paths.CacheDir, "repo") |  | ||||||
| 		paths.PkgsDir = filepath.Join(paths.CacheDir, "pkgs") |  | ||||||
|  |  | ||||||
| 		err = os.MkdirAll(paths.RepoDir, 0o755) |  | ||||||
| 		if err != nil { |  | ||||||
| 			log.Fatal("Unable to create repo cache directory").Err(err).Send() |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		err = os.MkdirAll(paths.PkgsDir, 0o755) |  | ||||||
| 		if err != nil { |  | ||||||
| 			log.Fatal("Unable to create package cache directory").Err(err).Send() |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		paths.DBPath = filepath.Join(paths.CacheDir, "db") |  | ||||||
| 	} |  | ||||||
| 	return paths |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,3 +1,22 @@ | |||||||
|  | // 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 config | package config | ||||||
|  |  | ||||||
| // Version contains the version of ALR. If the version | // Version contains the version of ALR. If the version | ||||||
|   | |||||||
							
								
								
									
										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,20 +1,21 @@ | |||||||
| /* | // This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan. | ||||||
|  * ALR - Any Linux Repository | // It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors. | ||||||
|  * Copyright (C) 2024 Евгений Храмов | // | ||||||
|  * | // ALR - Any Linux Repository | ||||||
|  * This program is free software: you can redistribute it and/or modify | // Copyright (C) 2025 The ALR Authors | ||||||
|  * it under the terms of the GNU General Public License as published by | // | ||||||
|  * the Free Software Foundation, either version 3 of the License, or | // This program is free software: you can redistribute it and/or modify | ||||||
|  * (at your option) any later version. | // it under the terms of the GNU General Public License as published by | ||||||
|  * | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  * This program is distributed in the hope that it will be useful, | // (at your option) any later version. | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | // | ||||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | // This program is distributed in the hope that it will be useful, | ||||||
|  * GNU General Public License for more details. | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  * | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  * You should have received a copy of the GNU General Public License | // GNU General Public License for more details. | ||||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | // | ||||||
|  */ | // 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 cpu | package cpu | ||||||
|  |  | ||||||
| @@ -37,11 +38,12 @@ func armVariant() string { | |||||||
| 		return armEnv | 		return armEnv | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if cpu.ARM.HasVFPv3 { | 	switch { | ||||||
|  | 	case cpu.ARM.HasVFPv3: | ||||||
| 		return "arm7" | 		return "arm7" | ||||||
| 	} else if cpu.ARM.HasVFP { | 	case cpu.ARM.HasVFP: | ||||||
| 		return "arm6" | 		return "arm6" | ||||||
| 	} else { | 	default: | ||||||
| 		return "arm5" | 		return "arm5" | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,54 +1,48 @@ | |||||||
| /* | // This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan. | ||||||
|  * ALR - Any Linux Repository | // It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors. | ||||||
|  * Copyright (C) 2024 Евгений Храмов | // | ||||||
|  * | // ALR - Any Linux Repository | ||||||
|  * This program is free software: you can redistribute it and/or modify | // Copyright (C) 2025 The ALR Authors | ||||||
|  * it under the terms of the GNU General Public License as published by | // | ||||||
|  * the Free Software Foundation, either version 3 of the License, or | // This program is free software: you can redistribute it and/or modify | ||||||
|  * (at your option) any later version. | // it under the terms of the GNU General Public License as published by | ||||||
|  * | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  * This program is distributed in the hope that it will be useful, | // (at your option) any later version. | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | // | ||||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | // This program is distributed in the hope that it will be useful, | ||||||
|  * GNU General Public License for more details. | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  * | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  * You should have received a copy of the GNU General Public License | // GNU General Public License for more details. | ||||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | // | ||||||
|  */ | // 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 | package db | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| 	"database/sql" | 	"log/slog" | ||||||
| 	"database/sql/driver" |  | ||||||
| 	"encoding/json" |  | ||||||
| 	"errors" |  | ||||||
| 	"fmt" |  | ||||||
| 	"sync" |  | ||||||
|  |  | ||||||
| 	"github.com/jmoiron/sqlx" | 	"github.com/jmoiron/sqlx" | ||||||
| 	"plemya-x.ru/alr/internal/config" | 	"github.com/leonelquinteros/gotext" | ||||||
| 	"plemya-x.ru/alr/pkg/loggerctx" |  | ||||||
| 	"golang.org/x/exp/slices" | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/config" | ||||||
| 	"modernc.org/sqlite" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // CurrentVersion is the current version of the database. | // CurrentVersion is the current version of the database. | ||||||
| // The database is reset if its version doesn't match this. | // The database is reset if its version doesn't match this. | ||||||
| const CurrentVersion = 2 | const CurrentVersion = 4 | ||||||
|  |  | ||||||
| func init() { |  | ||||||
| 	sqlite.MustRegisterScalarFunction("json_array_contains", 2, jsonArrayContains) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Package is a ALR package's database representation | // Package is a ALR package's database representation | ||||||
| type Package struct { | type Package struct { | ||||||
|  | 	BasePkgName   string                    `sh:"base" db:"basepkg_name"` | ||||||
| 	Name          string                    `sh:"name,required" db:"name"` | 	Name          string                    `sh:"name,required" db:"name"` | ||||||
| 	Version       string                    `sh:"version,required" db:"version"` | 	Version       string                    `sh:"version,required" db:"version"` | ||||||
| 	Release       int                       `sh:"release,required" db:"release"` | 	Release       int                       `sh:"release,required" db:"release"` | ||||||
| 	Epoch         uint                      `sh:"epoch" db:"epoch"` | 	Epoch         uint                      `sh:"epoch" db:"epoch"` | ||||||
|  | 	Summary       JSON[map[string]string]   `db:"summary"` | ||||||
| 	Description   JSON[map[string]string]   `db:"description"` | 	Description   JSON[map[string]string]   `db:"description"` | ||||||
|  | 	Group         JSON[map[string]string]   `db:"group_name"` | ||||||
| 	Homepage      JSON[map[string]string]   `db:"homepage"` | 	Homepage      JSON[map[string]string]   `db:"homepage"` | ||||||
| 	Maintainer    JSON[map[string]string]   `db:"maintainer"` | 	Maintainer    JSON[map[string]string]   `db:"maintainer"` | ||||||
| 	Architectures JSON[[]string]            `sh:"architectures" db:"architectures"` | 	Architectures JSON[[]string]            `sh:"architectures" db:"architectures"` | ||||||
| @@ -66,74 +60,57 @@ type version struct { | |||||||
| 	Version int `db:"version"` | 	Version int `db:"version"` | ||||||
| } | } | ||||||
|  |  | ||||||
| var ( | type Config interface { | ||||||
| 	mu     sync.Mutex | 	GetPaths() *config.Paths | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type Database struct { | ||||||
| 	conn   *sqlx.DB | 	conn   *sqlx.DB | ||||||
| 	closed = true | 	config Config | ||||||
| ) | } | ||||||
|  |  | ||||||
| // DB returns the ALR database. | func New(config Config) *Database { | ||||||
| // The first time it's called, it opens the SQLite database file. | 	return &Database{ | ||||||
| // Subsequent calls return the same connection. | 		config: config, | ||||||
| func DB(ctx context.Context) *sqlx.DB { |  | ||||||
| 	log := loggerctx.From(ctx) |  | ||||||
| 	if conn != nil && !closed { |  | ||||||
| 		return getConn() |  | ||||||
| 	} | 	} | ||||||
| 	_, err := open(ctx, config.GetPaths(ctx).DBPath) | } | ||||||
|  |  | ||||||
|  | func (d *Database) Init(ctx context.Context) error { | ||||||
|  | 	err := d.Connect(ctx) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Fatal("Error opening database").Err(err).Send() | 		return err | ||||||
| 	} | 	} | ||||||
| 	return getConn() | 	return d.initDB(ctx) | ||||||
| } | } | ||||||
|  |  | ||||||
| func getConn() *sqlx.DB { | func (d *Database) Connect(ctx context.Context) error { | ||||||
| 	mu.Lock() | 	dsn := d.config.GetPaths().DBPath | ||||||
| 	defer mu.Unlock() |  | ||||||
| 	return conn |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func open(ctx context.Context, dsn string) (*sqlx.DB, error) { |  | ||||||
| 	db, err := sqlx.Open("sqlite", dsn) | 	db, err := sqlx.Open("sqlite", dsn) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return err | ||||||
| 	} | 	} | ||||||
|  | 	d.conn = db | ||||||
| 	mu.Lock() | 	return nil | ||||||
| 	conn = db |  | ||||||
| 	closed = false |  | ||||||
| 	mu.Unlock() |  | ||||||
|  |  | ||||||
| 	err = initDB(ctx, dsn) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return db, nil |  | ||||||
| } | } | ||||||
|  |  | ||||||
| // Close closes the database | func (d *Database) GetConn() *sqlx.DB { | ||||||
| func Close() error { | 	return d.conn | ||||||
| 	closed = true |  | ||||||
| 	if conn != nil { |  | ||||||
| 		return conn.Close() |  | ||||||
| 	} else { |  | ||||||
| 		return nil |  | ||||||
| 	} |  | ||||||
| } | } | ||||||
|  |  | ||||||
| // initDB initializes the database | func (d *Database) initDB(ctx context.Context) error { | ||||||
| func initDB(ctx context.Context, dsn string) error { | 	d.conn = d.conn.Unsafe() | ||||||
| 	log := loggerctx.From(ctx) | 	conn := d.conn | ||||||
| 	conn = conn.Unsafe() |  | ||||||
| 	_, err := conn.ExecContext(ctx, ` | 	_, err := conn.ExecContext(ctx, ` | ||||||
| 		CREATE TABLE IF NOT EXISTS pkgs ( | 		CREATE TABLE IF NOT EXISTS pkgs ( | ||||||
|  | 			basepkg_name  TEXT NOT NULL, | ||||||
| 			name          TEXT NOT NULL, | 			name          TEXT NOT NULL, | ||||||
| 			repository    TEXT NOT NULL, | 			repository    TEXT NOT NULL, | ||||||
| 			version       TEXT NOT NULL, | 			version       TEXT NOT NULL, | ||||||
| 			release       INT  NOT NULL, | 			release       INT  NOT NULL, | ||||||
| 			epoch         INT, | 			epoch         INT, | ||||||
|  | 			summary       TEXT CHECK(summary = 'null' OR (JSON_VALID(summary) AND JSON_TYPE(summary) = 'object')), | ||||||
| 			description   TEXT CHECK(description = 'null' OR (JSON_VALID(description) AND JSON_TYPE(description) = 'object')), | 			description   TEXT CHECK(description = 'null' OR (JSON_VALID(description) AND JSON_TYPE(description) = 'object')), | ||||||
|  | 			group_name    TEXT CHECK(group_name = 'null' OR (JSON_VALID(group_name) AND JSON_TYPE(group_name) = 'object')), | ||||||
| 			homepage      TEXT CHECK(homepage = 'null' OR (JSON_VALID(homepage) AND JSON_TYPE(homepage) = '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')), | 			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')), | 			architectures TEXT CHECK(architectures = 'null' OR (JSON_VALID(architectures) AND JSON_TYPE(architectures) = 'array')), | ||||||
| @@ -155,65 +132,85 @@ func initDB(ctx context.Context, dsn string) error { | |||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	ver, ok := GetVersion(ctx) | 	ver, ok := d.GetVersion(ctx) | ||||||
| 	if ok && ver != CurrentVersion { | 	if ok && ver != CurrentVersion { | ||||||
| 		log.Warn("Database version mismatch; resetting").Int("version", ver).Int("expected", CurrentVersion).Send() | 		slog.Warn(gotext.Get("Database version mismatch; resetting"), "version", ver, "expected", CurrentVersion) | ||||||
| 		reset(ctx) | 		err = d.reset(ctx) | ||||||
| 		return initDB(ctx, dsn) | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 		return d.initDB(ctx) | ||||||
| 	} else if !ok { | 	} else if !ok { | ||||||
| 		log.Warn("Database version does not exist. Run alr fix if something isn't working.").Send() | 		slog.Warn(gotext.Get("Database version does not exist. Run alr fix if something isn't working."), "version", ver, "expected", CurrentVersion) | ||||||
| 		return addVersion(ctx, CurrentVersion) | 		return d.addVersion(ctx, CurrentVersion) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // reset drops all the database tables | func (d *Database) GetVersion(ctx context.Context) (int, bool) { | ||||||
| func reset(ctx context.Context) error { |  | ||||||
| 	_, err := DB(ctx).ExecContext(ctx, "DROP TABLE IF EXISTS pkgs;") |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	_, err = DB(ctx).ExecContext(ctx, "DROP TABLE IF EXISTS alr_db_version;") |  | ||||||
| 	return err |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // IsEmpty returns true if the database has no packages in it, otherwise it returns false. |  | ||||||
| func IsEmpty(ctx context.Context) bool { |  | ||||||
| 	var count int |  | ||||||
| 	err := DB(ctx).GetContext(ctx, &count, "SELECT count(1) FROM pkgs;") |  | ||||||
| 	if err != nil { |  | ||||||
| 		return true |  | ||||||
| 	} |  | ||||||
| 	return count == 0 |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // GetVersion returns the database version and a boolean indicating |  | ||||||
| // whether the database contained a version number |  | ||||||
| func GetVersion(ctx context.Context) (int, bool) { |  | ||||||
| 	var ver version | 	var ver version | ||||||
| 	err := DB(ctx).GetContext(ctx, &ver, "SELECT * FROM alr_db_version LIMIT 1;") | 	err := d.conn.GetContext(ctx, &ver, "SELECT * FROM alr_db_version LIMIT 1;") | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return 0, false | 		return 0, false | ||||||
| 	} | 	} | ||||||
| 	return ver.Version, true | 	return ver.Version, true | ||||||
| } | } | ||||||
|  |  | ||||||
| func addVersion(ctx context.Context, ver int) error { | func (d *Database) addVersion(ctx context.Context, ver int) error { | ||||||
| 	_, err := DB(ctx).ExecContext(ctx, `INSERT INTO alr_db_version(version) VALUES (?);`, ver) | 	_, err := d.conn.ExecContext(ctx, `INSERT INTO alr_db_version(version) VALUES (?);`, ver) | ||||||
| 	return err | 	return err | ||||||
| } | } | ||||||
|  |  | ||||||
| // InsertPackage adds a package to the database | func (d *Database) reset(ctx context.Context) error { | ||||||
| func InsertPackage(ctx context.Context, pkg Package) error { | 	_, err := d.conn.ExecContext(ctx, "DROP TABLE IF EXISTS pkgs;") | ||||||
| 	_, err := DB(ctx).NamedExecContext(ctx, ` | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	_, err = d.conn.ExecContext(ctx, "DROP TABLE IF EXISTS alr_db_version;") | ||||||
|  | 	return err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (d *Database) GetPkgs(ctx context.Context, where string, args ...any) (*sqlx.Rows, error) { | ||||||
|  | 	stream, err := d.conn.QueryxContext(ctx, "SELECT * FROM pkgs WHERE "+where, args...) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	return stream, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (d *Database) GetPkg(ctx context.Context, where string, args ...any) (*Package, error) { | ||||||
|  | 	out := &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 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (d *Database) IsEmpty(ctx context.Context) bool { | ||||||
|  | 	var count int | ||||||
|  | 	err := d.conn.GetContext(ctx, &count, "SELECT count(1) FROM pkgs;") | ||||||
|  | 	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 ( | 		INSERT OR REPLACE INTO pkgs ( | ||||||
|  | 			basepkg_name, | ||||||
| 			name, | 			name, | ||||||
| 			repository, | 			repository, | ||||||
| 			version, | 			version, | ||||||
| 			release, | 			release, | ||||||
| 			epoch, | 			epoch, | ||||||
|  | 			summary, | ||||||
| 			description, | 			description, | ||||||
|  | 			group_name, | ||||||
| 			homepage, | 			homepage, | ||||||
| 			maintainer, | 			maintainer, | ||||||
| 			architectures, | 			architectures, | ||||||
| @@ -225,12 +222,15 @@ func InsertPackage(ctx context.Context, pkg Package) error { | |||||||
| 			builddepends, | 			builddepends, | ||||||
| 			optdepends | 			optdepends | ||||||
| 		) VALUES ( | 		) VALUES ( | ||||||
|  | 		 	:basepkg_name, | ||||||
| 			:name, | 			:name, | ||||||
| 			:repository, | 			:repository, | ||||||
| 			:version, | 			:version, | ||||||
| 			:release, | 			:release, | ||||||
| 			:epoch, | 			:epoch, | ||||||
|  | 			:summary, | ||||||
| 			:description, | 			:description, | ||||||
|  | 			:group_name, | ||||||
| 			:homepage, | 			:homepage, | ||||||
| 			:maintainer, | 			:maintainer, | ||||||
| 			:architectures, | 			:architectures, | ||||||
| @@ -246,101 +246,10 @@ func InsertPackage(ctx context.Context, pkg Package) error { | |||||||
| 	return err | 	return err | ||||||
| } | } | ||||||
|  |  | ||||||
| // GetPkgs returns a result containing packages that match the where conditions | func (d *Database) Close() error { | ||||||
| func GetPkgs(ctx context.Context, where string, args ...any) (*sqlx.Rows, error) { | 	if d.conn != nil { | ||||||
| 	stream, err := DB(ctx).QueryxContext(ctx, "SELECT * FROM pkgs WHERE "+where, args...) | 		return d.conn.Close() | ||||||
| 	if err != nil { | 	} else { | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 	return stream, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // GetPkg returns a single package that matches the where conditions |  | ||||||
| func GetPkg(ctx context.Context, where string, args ...any) (*Package, error) { |  | ||||||
| 	out := &Package{} |  | ||||||
| 	err := DB(ctx).GetContext(ctx, out, "SELECT * FROM pkgs WHERE "+where+" LIMIT 1", args...) |  | ||||||
| 	return out, err |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // DeletePkgs deletes all packages matching the where conditions |  | ||||||
| func DeletePkgs(ctx context.Context, where string, args ...any) error { |  | ||||||
| 	_, err := DB(ctx).ExecContext(ctx, "DELETE FROM pkgs WHERE "+where, args...) |  | ||||||
| 	return err |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // jsonArrayContains is an SQLite function that checks if a JSON array |  | ||||||
| // in the database contains a given value |  | ||||||
| func jsonArrayContains(ctx *sqlite.FunctionContext, args []driver.Value) (driver.Value, error) { |  | ||||||
| 	value, ok := args[0].(string) |  | ||||||
| 	if !ok { |  | ||||||
| 		return nil, errors.New("both arguments to json_array_contains must be strings") |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	item, ok := args[1].(string) |  | ||||||
| 	if !ok { |  | ||||||
| 		return nil, errors.New("both arguments to json_array_contains must be strings") |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	var array []string |  | ||||||
| 	err := json.Unmarshal([]byte(value), &array) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return slices.Contains(array, item), nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // 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 | 		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,32 +1,50 @@ | |||||||
| /* | // This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan. | ||||||
|  * ALR - Any Linux Repository | // It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors. | ||||||
|  * Copyright (C) 2024 Евгений Храмов | // | ||||||
|  * | // ALR - Any Linux Repository | ||||||
|  * This program is free software: you can redistribute it and/or modify | // Copyright (C) 2025 The ALR Authors | ||||||
|  * it under the terms of the GNU General Public License as published by | // | ||||||
|  * the Free Software Foundation, either version 3 of the License, or | // This program is free software: you can redistribute it and/or modify | ||||||
|  * (at your option) any later version. | // it under the terms of the GNU General Public License as published by | ||||||
|  * | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  * This program is distributed in the hope that it will be useful, | // (at your option) any later version. | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | // | ||||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | // This program is distributed in the hope that it will be useful, | ||||||
|  * GNU General Public License for more details. | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  * | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  * You should have received a copy of the GNU General Public License | // GNU General Public License for more details. | ||||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | // | ||||||
|  */ | // 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_test | package db_test | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"context" | ||||||
| 	"reflect" | 	"reflect" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| 	"github.com/jmoiron/sqlx" | 	"github.com/jmoiron/sqlx" | ||||||
| 	"plemya-x.ru/alr/internal/db" |  | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/config" | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/db" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | type TestALRConfig struct{} | ||||||
|  |  | ||||||
|  | func (c *TestALRConfig) GetPaths() *config.Paths { | ||||||
|  | 	return &config.Paths{ | ||||||
|  | 		DBPath: ":memory:", | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func prepareDb() *db.Database { | ||||||
|  | 	database := db.New(&TestALRConfig{}) | ||||||
|  | 	database.Init(context.Background()) | ||||||
|  | 	return database | ||||||
|  | } | ||||||
|  |  | ||||||
| var testPkg = db.Package{ | var testPkg = db.Package{ | ||||||
| 	Name:    "test", | 	Name:    "test", | ||||||
| 	Version: "0.0.1", | 	Version: "0.0.1", | ||||||
| @@ -59,18 +77,11 @@ var testPkg = db.Package{ | |||||||
| } | } | ||||||
|  |  | ||||||
| func TestInit(t *testing.T) { | func TestInit(t *testing.T) { | ||||||
| 	_, err := db.Open(":memory:") | 	ctx := context.Background() | ||||||
| 	if err != nil { | 	database := prepareDb() | ||||||
| 		t.Fatalf("Expected no error, got %s", err) | 	defer database.Close() | ||||||
| 	} |  | ||||||
| 	defer db.Close() |  | ||||||
|  |  | ||||||
| 	_, err = db.DB().Exec("SELECT * FROM pkgs") | 	ver, ok := database.GetVersion(ctx) | ||||||
| 	if err != nil { |  | ||||||
| 		t.Fatalf("Expected no error, got %s", err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	ver, ok := db.GetVersion() |  | ||||||
| 	if !ok { | 	if !ok { | ||||||
| 		t.Errorf("Expected version to be present") | 		t.Errorf("Expected version to be present") | ||||||
| 	} else if ver != db.CurrentVersion { | 	} else if ver != db.CurrentVersion { | ||||||
| @@ -79,19 +90,17 @@ func TestInit(t *testing.T) { | |||||||
| } | } | ||||||
|  |  | ||||||
| func TestInsertPackage(t *testing.T) { | func TestInsertPackage(t *testing.T) { | ||||||
| 	_, err := db.Open(":memory:") | 	ctx := context.Background() | ||||||
| 	if err != nil { | 	database := prepareDb() | ||||||
| 		t.Fatalf("Expected no error, got %s", err) | 	defer database.Close() | ||||||
| 	} |  | ||||||
| 	defer db.Close() |  | ||||||
|  |  | ||||||
| 	err = db.InsertPackage(testPkg) | 	err := database.InsertPackage(ctx, testPkg) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Fatalf("Expected no error, got %s", err) | 		t.Fatalf("Expected no error, got %s", err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	dbPkg := db.Package{} | 	dbPkg := db.Package{} | ||||||
| 	err = sqlx.Get(db.DB(), &dbPkg, "SELECT * FROM pkgs WHERE 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) | ||||||
| 	} | 	} | ||||||
| @@ -102,28 +111,26 @@ func TestInsertPackage(t *testing.T) { | |||||||
| } | } | ||||||
|  |  | ||||||
| func TestGetPkgs(t *testing.T) { | func TestGetPkgs(t *testing.T) { | ||||||
| 	_, err := db.Open(":memory:") | 	ctx := context.Background() | ||||||
| 	if err != nil { | 	database := prepareDb() | ||||||
| 		t.Fatalf("Expected no error, got %s", err) | 	defer database.Close() | ||||||
| 	} |  | ||||||
| 	defer db.Close() |  | ||||||
|  |  | ||||||
| 	x1 := testPkg | 	x1 := testPkg | ||||||
| 	x1.Name = "x1" | 	x1.Name = "x1" | ||||||
| 	x2 := testPkg | 	x2 := testPkg | ||||||
| 	x2.Name = "x2" | 	x2.Name = "x2" | ||||||
|  |  | ||||||
| 	err = db.InsertPackage(x1) | 	err := database.InsertPackage(ctx, x1) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Errorf("Expected no error, got %s", err) | 		t.Errorf("Expected no error, got %s", err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	err = db.InsertPackage(x2) | 	err = database.InsertPackage(ctx, x2) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Errorf("Expected no error, got %s", err) | 		t.Errorf("Expected no error, got %s", err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	result, err := db.GetPkgs("name LIKE 'x%'") | 	result, 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) | ||||||
| 	} | 	} | ||||||
| @@ -142,28 +149,26 @@ func TestGetPkgs(t *testing.T) { | |||||||
| } | } | ||||||
|  |  | ||||||
| func TestGetPkg(t *testing.T) { | func TestGetPkg(t *testing.T) { | ||||||
| 	_, err := db.Open(":memory:") | 	ctx := context.Background() | ||||||
| 	if err != nil { | 	database := prepareDb() | ||||||
| 		t.Fatalf("Expected no error, got %s", err) | 	defer database.Close() | ||||||
| 	} |  | ||||||
| 	defer db.Close() |  | ||||||
|  |  | ||||||
| 	x1 := testPkg | 	x1 := testPkg | ||||||
| 	x1.Name = "x1" | 	x1.Name = "x1" | ||||||
| 	x2 := testPkg | 	x2 := testPkg | ||||||
| 	x2.Name = "x2" | 	x2.Name = "x2" | ||||||
|  |  | ||||||
| 	err = db.InsertPackage(x1) | 	err := database.InsertPackage(ctx, x1) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Errorf("Expected no error, got %s", err) | 		t.Errorf("Expected no error, got %s", err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	err = db.InsertPackage(x2) | 	err = database.InsertPackage(ctx, x2) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Errorf("Expected no error, got %s", err) | 		t.Errorf("Expected no error, got %s", err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	pkg, err := db.GetPkg("name LIKE 'x%' ORDER BY name") | 	pkg, err := database.GetPkg(ctx, "name LIKE 'x%' ORDER BY name") | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Fatalf("Expected no error, got %s", err) | 		t.Fatalf("Expected no error, got %s", err) | ||||||
| 	} | 	} | ||||||
| @@ -178,34 +183,32 @@ func TestGetPkg(t *testing.T) { | |||||||
| } | } | ||||||
|  |  | ||||||
| func TestDeletePkgs(t *testing.T) { | func TestDeletePkgs(t *testing.T) { | ||||||
| 	_, err := db.Open(":memory:") | 	ctx := context.Background() | ||||||
| 	if err != nil { | 	database := prepareDb() | ||||||
| 		t.Fatalf("Expected no error, got %s", err) | 	defer database.Close() | ||||||
| 	} |  | ||||||
| 	defer db.Close() |  | ||||||
|  |  | ||||||
| 	x1 := testPkg | 	x1 := testPkg | ||||||
| 	x1.Name = "x1" | 	x1.Name = "x1" | ||||||
| 	x2 := testPkg | 	x2 := testPkg | ||||||
| 	x2.Name = "x2" | 	x2.Name = "x2" | ||||||
|  |  | ||||||
| 	err = db.InsertPackage(x1) | 	err := database.InsertPackage(ctx, x1) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Errorf("Expected no error, got %s", err) | 		t.Errorf("Expected no error, got %s", err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	err = db.InsertPackage(x2) | 	err = database.InsertPackage(ctx, x2) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Errorf("Expected no error, got %s", err) | 		t.Errorf("Expected no error, got %s", err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	err = db.DeletePkgs("name = 'x1'") | 	err = database.DeletePkgs(ctx, "name = 'x1'") | ||||||
| 	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 | 	var dbPkg db.Package | ||||||
| 	err = db.DB().Get(&dbPkg, "SELECT * FROM pkgs WHERE name LIKE 'x%' ORDER BY name LIMIT 1;") | 	err = database.GetConn().Get(&dbPkg, "SELECT * FROM pkgs WHERE name LIKE 'x%' ORDER BY name LIMIT 1;") | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Errorf("Expected no error, got %s", err) | 		t.Errorf("Expected no error, got %s", err) | ||||||
| 	} | 	} | ||||||
| @@ -216,11 +219,9 @@ func TestDeletePkgs(t *testing.T) { | |||||||
| } | } | ||||||
|  |  | ||||||
| func TestJsonArrayContains(t *testing.T) { | func TestJsonArrayContains(t *testing.T) { | ||||||
| 	_, err := db.Open(":memory:") | 	ctx := context.Background() | ||||||
| 	if err != nil { | 	database := prepareDb() | ||||||
| 		t.Fatalf("Expected no error, got %s", err) | 	defer database.Close() | ||||||
| 	} |  | ||||||
| 	defer db.Close() |  | ||||||
|  |  | ||||||
| 	x1 := testPkg | 	x1 := testPkg | ||||||
| 	x1.Name = "x1" | 	x1.Name = "x1" | ||||||
| @@ -228,18 +229,18 @@ func TestJsonArrayContains(t *testing.T) { | |||||||
| 	x2.Name = "x2" | 	x2.Name = "x2" | ||||||
| 	x2.Provides.Val = append(x2.Provides.Val, "x") | 	x2.Provides.Val = append(x2.Provides.Val, "x") | ||||||
|  |  | ||||||
| 	err = db.InsertPackage(x1) | 	err := database.InsertPackage(ctx, x1) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Errorf("Expected no error, got %s", err) | 		t.Errorf("Expected no error, got %s", err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	err = db.InsertPackage(x2) | 	err = database.InsertPackage(ctx, x2) | ||||||
| 	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 | 	var dbPkg db.Package | ||||||
| 	err = db.DB().Get(&dbPkg, "SELECT * FROM pkgs WHERE json_array_contains(provides, 'x');") | 	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) | ||||||
| 	} | 	} | ||||||
|   | |||||||
							
								
								
									
										80
									
								
								internal/db/json.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								internal/db/json.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,80 @@ | |||||||
|  | // 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 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) | ||||||
|  | } | ||||||
							
								
								
									
										52
									
								
								internal/db/utils.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								internal/db/utils.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 db | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"database/sql/driver" | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"errors" | ||||||
|  |  | ||||||
|  | 	"golang.org/x/exp/slices" | ||||||
|  | 	"modernc.org/sqlite" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func init() { | ||||||
|  | 	sqlite.MustRegisterScalarFunction("json_array_contains", 2, jsonArrayContains) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // jsonArrayContains is an SQLite function that checks if a JSON array | ||||||
|  | // in the database contains a given value | ||||||
|  | func jsonArrayContains(ctx *sqlite.FunctionContext, args []driver.Value) (driver.Value, error) { | ||||||
|  | 	value, ok := args[0].(string) | ||||||
|  | 	if !ok { | ||||||
|  | 		return nil, errors.New("both arguments to json_array_contains must be strings") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	item, ok := args[1].(string) | ||||||
|  | 	if !ok { | ||||||
|  | 		return nil, errors.New("both arguments to json_array_contains must be strings") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var array []string | ||||||
|  | 	err := json.Unmarshal([]byte(value), &array) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return slices.Contains(array, item), nil | ||||||
|  | } | ||||||
| @@ -1,20 +1,21 @@ | |||||||
| /* | // This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan. | ||||||
| * ALR - Any Linux Repository | // It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors. | ||||||
| * Copyright (C) 2024 Евгений Храмов | // | ||||||
| * | // ALR - Any Linux Repository | ||||||
| * This program is free software: you can redistribute it and/or modify | // Copyright (C) 2025 The ALR Authors | ||||||
| * it under the terms of the GNU General Public License as published by | // | ||||||
| * the Free Software Foundation, either version 3 of the License, or | // This program is free software: you can redistribute it and/or modify | ||||||
| * (at your option) any later version. | // it under the terms of the GNU General Public License as published by | ||||||
| * | // the Free Software Foundation, either version 3 of the License, or | ||||||
| * This program is distributed in the hope that it will be useful, | // (at your option) any later version. | ||||||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | // | ||||||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | // This program is distributed in the hope that it will be useful, | ||||||
| * GNU General Public License for more details. | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
| * | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
| * You should have received a copy of the GNU General Public License | // GNU General Public License for more details. | ||||||
| * along with this program.  If not, see <http://www.gnu.org/licenses/>. | // | ||||||
| */ | // You should have received a copy of the GNU General Public License | ||||||
|  | // along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  | ||||||
| // Пакет dl содержит абстракции для загрузки файлов и каталогов | // Пакет dl содержит абстракции для загрузки файлов и каталогов | ||||||
| // из различных источников. | // из различных источников. | ||||||
| @@ -30,17 +31,17 @@ import ( | |||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"hash" | 	"hash" | ||||||
| 	"io" | 	"io" | ||||||
|  | 	"log/slog" | ||||||
| 	"os" | 	"os" | ||||||
| 	"path/filepath" | 	"path/filepath" | ||||||
| 	"strings" | 	"strings" | ||||||
|  |  | ||||||
| 	"github.com/PuerkitoBio/purell" | 	"github.com/PuerkitoBio/purell" | ||||||
|  | 	"github.com/leonelquinteros/gotext" | ||||||
| 	"github.com/vmihailenco/msgpack/v5" | 	"github.com/vmihailenco/msgpack/v5" | ||||||
| 	"golang.org/x/crypto/blake2b" | 	"golang.org/x/crypto/blake2b" | ||||||
| 	"golang.org/x/crypto/blake2s" | 	"golang.org/x/crypto/blake2s" | ||||||
| 	"golang.org/x/exp/slices" | 	"golang.org/x/exp/slices" | ||||||
| 	"plemya-x.ru/alr/internal/dlcache" |  | ||||||
| 	"plemya-x.ru/alr/pkg/loggerctx" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // Константа для имени файла манифеста кэша | // Константа для имени файла манифеста кэша | ||||||
| @@ -79,6 +80,11 @@ func (t Type) String() string { | |||||||
| 	return "<unknown>" | 	return "<unknown>" | ||||||
| } | } | ||||||
|  |  | ||||||
|  | type DlCache interface { | ||||||
|  | 	Get(context.Context, string) (string, bool) | ||||||
|  | 	New(context.Context, string) (string, error) | ||||||
|  | } | ||||||
|  |  | ||||||
| // Структура Options содержит параметры для загрузки файлов и каталогов | // Структура Options содержит параметры для загрузки файлов и каталогов | ||||||
| type Options struct { | type Options struct { | ||||||
| 	Hash             []byte | 	Hash             []byte | ||||||
| @@ -90,6 +96,7 @@ type Options struct { | |||||||
| 	PostprocDisabled bool | 	PostprocDisabled bool | ||||||
| 	Progress         io.Writer | 	Progress         io.Writer | ||||||
| 	LocalDir         string | 	LocalDir         string | ||||||
|  | 	DlCache          DlCache | ||||||
| } | } | ||||||
|  |  | ||||||
| // Метод для создания нового хеша на основе указанного алгоритма хеширования | // Метод для создания нового хеша на основе указанного алгоритма хеширования | ||||||
| @@ -130,7 +137,7 @@ type Manifest struct { | |||||||
| type Downloader interface { | type Downloader interface { | ||||||
| 	Name() string | 	Name() string | ||||||
| 	MatchURL(string) bool | 	MatchURL(string) bool | ||||||
| 	Download(Options) (Type, string, error) | 	Download(context.Context, Options) (Type, string, error) | ||||||
| } | } | ||||||
|  |  | ||||||
| // Интерфейс UpdatingDownloader расширяет Downloader методом Update | // Интерфейс UpdatingDownloader расширяет Downloader методом Update | ||||||
| @@ -141,7 +148,6 @@ type UpdatingDownloader interface { | |||||||
|  |  | ||||||
| // Функция Download загружает файл или каталог с использованием указанных параметров | // Функция Download загружает файл или каталог с использованием указанных параметров | ||||||
| func Download(ctx context.Context, opts Options) (err error) { | func Download(ctx context.Context, opts Options) (err error) { | ||||||
| 	log := loggerctx.From(ctx) |  | ||||||
| 	normalized, err := normalizeURL(opts.URL) | 	normalized, err := normalizeURL(opts.URL) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| @@ -151,16 +157,20 @@ func Download(ctx context.Context, opts Options) (err error) { | |||||||
| 	d := getDownloader(opts.URL) | 	d := getDownloader(opts.URL) | ||||||
|  |  | ||||||
| 	if opts.CacheDisabled { | 	if opts.CacheDisabled { | ||||||
| 		_, _, err = d.Download(opts) | 		_, _, err = d.Download(ctx, opts) | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	var t Type | 	var t Type | ||||||
| 	cacheDir, ok := dlcache.Get(ctx, opts.URL) | 	cacheDir, ok := opts.DlCache.Get(ctx, opts.URL) | ||||||
| 	if ok { | 	if ok { | ||||||
| 		var updated bool | 		var updated bool | ||||||
| 		if d, ok := d.(UpdatingDownloader); ok { | 		if d, ok := d.(UpdatingDownloader); ok { | ||||||
| 			log.Info("Source can be updated, updating if required").Str("source", opts.Name).Str("downloader", d.Name()).Send() | 			slog.Info( | ||||||
|  | 				gotext.Get("Source can be updated, updating if required"), | ||||||
|  | 				"source", opts.Name, | ||||||
|  | 				"downloader", d.Name(), | ||||||
|  | 			) | ||||||
|  |  | ||||||
| 			updated, err = d.Update(Options{ | 			updated, err = d.Update(Options{ | ||||||
| 				Hash:          opts.Hash, | 				Hash:          opts.Hash, | ||||||
| @@ -187,10 +197,18 @@ func Download(ctx context.Context, opts Options) (err error) { | |||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			if ok && !updated { | 			if ok && !updated { | ||||||
| 				log.Info("Source found in cache and linked to destination").Str("source", opts.Name).Stringer("type", t).Send() | 				slog.Info( | ||||||
|  | 					gotext.Get("Source found in cache and linked to destination"), | ||||||
|  | 					"source", opts.Name, | ||||||
|  | 					"type", t, | ||||||
|  | 				) | ||||||
| 				return nil | 				return nil | ||||||
| 			} else if ok { | 			} else if ok { | ||||||
| 				log.Info("Source updated and linked to destination").Str("source", opts.Name).Stringer("type", t).Send() | 				slog.Info( | ||||||
|  | 					gotext.Get("Source updated and linked to destination"), | ||||||
|  | 					"source", opts.Name, | ||||||
|  | 					"type", t, | ||||||
|  | 				) | ||||||
| 				return nil | 				return nil | ||||||
| 			} | 			} | ||||||
| 		} else { | 		} else { | ||||||
| @@ -201,14 +219,14 @@ func Download(ctx context.Context, opts Options) (err error) { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	log.Info("Downloading source").Str("source", opts.Name).Str("downloader", d.Name()).Send() | 	slog.Info(gotext.Get("Downloading source"), "source", opts.Name, "downloader", d.Name()) | ||||||
|  |  | ||||||
| 	cacheDir, err = dlcache.New(ctx, opts.URL) | 	cacheDir, err = opts.DlCache.New(ctx, opts.URL) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	t, name, err := d.Download(Options{ | 	t, name, err := d.Download(ctx, Options{ | ||||||
| 		Hash:          opts.Hash, | 		Hash:          opts.Hash, | ||||||
| 		HashAlgorithm: opts.HashAlgorithm, | 		HashAlgorithm: opts.HashAlgorithm, | ||||||
| 		Name:          opts.Name, | 		Name:          opts.Name, | ||||||
| @@ -299,8 +317,6 @@ func linkDir(src, dest string) error { | |||||||
| 			return nil | 			return nil | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| 		rel, err := filepath.Rel(src, path) | 		rel, err := filepath.Rel(src, path) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return err | 			return err | ||||||
|   | |||||||
							
								
								
									
										176
									
								
								internal/dl/dl_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										176
									
								
								internal/dl/dl_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,176 @@ | |||||||
|  | // 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 dl_test | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"fmt" | ||||||
|  | 	"log" | ||||||
|  | 	"net/http" | ||||||
|  | 	"net/http/httptest" | ||||||
|  | 	"net/http/httputil" | ||||||
|  | 	"net/url" | ||||||
|  | 	"os" | ||||||
|  | 	"path" | ||||||
|  | 	"strings" | ||||||
|  | 	"testing" | ||||||
|  |  | ||||||
|  | 	"github.com/stretchr/testify/assert" | ||||||
|  |  | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/config" | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/dl" | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/dlcache" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type TestALRConfig struct{} | ||||||
|  |  | ||||||
|  | func (c *TestALRConfig) GetPaths() *config.Paths { | ||||||
|  | 	return &config.Paths{ | ||||||
|  | 		CacheDir: "/tmp", | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestDownloadWithoutCache(t *testing.T) { | ||||||
|  | 	type testCase struct { | ||||||
|  | 		name     string | ||||||
|  | 		path     string | ||||||
|  | 		expected func(*testing.T, error, string) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	prepareServer := func() *httptest.Server { | ||||||
|  | 		// URL вашего Git-сервера | ||||||
|  | 		gitServerURL, err := url.Parse("https://gitea.plemya-x.ru") | ||||||
|  | 		if err != nil { | ||||||
|  | 			log.Fatalf("Failed to parse git server URL: %v", err) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		proxy := httputil.NewSingleHostReverseProxy(gitServerURL) | ||||||
|  |  | ||||||
|  | 		return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 			switch { | ||||||
|  | 			case r.URL.Path == "/file-downloader/file": | ||||||
|  | 				w.WriteHeader(http.StatusOK) | ||||||
|  | 				w.Write([]byte("Hello, World!")) | ||||||
|  | 			case strings.HasPrefix(r.URL.Path, "/git-downloader/git"): | ||||||
|  | 				r.URL.Host = gitServerURL.Host | ||||||
|  | 				r.URL.Scheme = gitServerURL.Scheme | ||||||
|  | 				r.Host = gitServerURL.Host | ||||||
|  | 				r.URL.Path, _ = strings.CutPrefix(r.URL.Path, "/git-downloader/git") | ||||||
|  |  | ||||||
|  | 				proxy.ServeHTTP(w, r) | ||||||
|  | 			default: | ||||||
|  | 				w.WriteHeader(http.StatusNotFound) | ||||||
|  | 			} | ||||||
|  | 		})) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, tc := range []testCase{ | ||||||
|  | 		{ | ||||||
|  | 			name: "simple file download", | ||||||
|  | 			path: "%s/file-downloader/file", | ||||||
|  | 			expected: func(t *testing.T, err error, tmpdir string) { | ||||||
|  | 				assert.NoError(t, err) | ||||||
|  |  | ||||||
|  | 				_, err = os.Stat(path.Join(tmpdir, "file")) | ||||||
|  | 				assert.NoError(t, err) | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "git download", | ||||||
|  | 			path: "git+%s/git-downloader/git/Plemya-x/alr-repo", | ||||||
|  | 			expected: func(t *testing.T, err error, tmpdir string) { | ||||||
|  | 				assert.NoError(t, err) | ||||||
|  |  | ||||||
|  | 				_, err = os.Stat(path.Join(tmpdir, "alr-repo.toml")) | ||||||
|  | 				assert.NoError(t, err) | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 	} { | ||||||
|  | 		t.Run(tc.name, func(t *testing.T) { | ||||||
|  | 			server := prepareServer() | ||||||
|  | 			defer server.Close() | ||||||
|  |  | ||||||
|  | 			tmpdir, err := os.MkdirTemp("", "test-download") | ||||||
|  | 			assert.NoError(t, err) | ||||||
|  | 			defer os.RemoveAll(tmpdir) | ||||||
|  |  | ||||||
|  | 			opts := dl.Options{ | ||||||
|  | 				CacheDisabled: true, | ||||||
|  | 				URL:           fmt.Sprintf(tc.path, server.URL), | ||||||
|  | 				Destination:   tmpdir, | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			err = dl.Download(context.Background(), opts) | ||||||
|  |  | ||||||
|  | 			tc.expected(t, err, tmpdir) | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestDownloadFileWithCache(t *testing.T) { | ||||||
|  | 	type testCase struct { | ||||||
|  | 		name string | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, tc := range []testCase{ | ||||||
|  | 		{ | ||||||
|  | 			name: "simple download", | ||||||
|  | 		}, | ||||||
|  | 	} { | ||||||
|  | 		t.Run(tc.name, func(t *testing.T) { | ||||||
|  | 			called := 0 | ||||||
|  | 			server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 				switch { | ||||||
|  | 				case r.URL.Path == "/file": | ||||||
|  | 					called += 1 | ||||||
|  | 					w.WriteHeader(http.StatusOK) | ||||||
|  | 					w.Write([]byte("Hello, World!")) | ||||||
|  | 				default: | ||||||
|  | 					w.WriteHeader(http.StatusNotFound) | ||||||
|  | 				} | ||||||
|  | 			})) | ||||||
|  | 			defer server.Close() | ||||||
|  |  | ||||||
|  | 			tmpdir, err := os.MkdirTemp("", "test-download") | ||||||
|  | 			assert.NoError(t, err) | ||||||
|  | 			defer os.RemoveAll(tmpdir) | ||||||
|  |  | ||||||
|  | 			cfg := &TestALRConfig{} | ||||||
|  |  | ||||||
|  | 			opts := dl.Options{ | ||||||
|  | 				CacheDisabled: false, | ||||||
|  | 				URL:           server.URL + "/file", | ||||||
|  | 				Destination:   tmpdir, | ||||||
|  | 				DlCache:       dlcache.New(cfg), | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			outputFile := path.Join(tmpdir, "file") | ||||||
|  |  | ||||||
|  | 			err = dl.Download(context.Background(), opts) | ||||||
|  | 			assert.NoError(t, err) | ||||||
|  | 			_, err = os.Stat(outputFile) | ||||||
|  | 			assert.NoError(t, err) | ||||||
|  |  | ||||||
|  | 			err = os.Remove(outputFile) | ||||||
|  | 			assert.NoError(t, err) | ||||||
|  |  | ||||||
|  | 			err = dl.Download(context.Background(), opts) | ||||||
|  | 			assert.NoError(t, err) | ||||||
|  | 			assert.Equal(t, 1, called) | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -1,26 +1,28 @@ | |||||||
| /* | // This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan. | ||||||
|  * ALR - Any Linux Repository | // It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors. | ||||||
|  * Copyright (C) 2024 Евгений Храмов | // | ||||||
|  * | // ALR - Any Linux Repository | ||||||
|  * This program is free software: you can redistribute it and/or modify | // Copyright (C) 2025 The ALR Authors | ||||||
|  * it under the terms of the GNU General Public License as published by | // | ||||||
|  * the Free Software Foundation, either version 3 of the License, or | // This program is free software: you can redistribute it and/or modify | ||||||
|  * (at your option) any later version. | // it under the terms of the GNU General Public License as published by | ||||||
|  * | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  * This program is distributed in the hope that it will be useful, | // (at your option) any later version. | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | // | ||||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | // This program is distributed in the hope that it will be useful, | ||||||
|  * GNU General Public License for more details. | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  * | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  * You should have received a copy of the GNU General Public License | // GNU General Public License for more details. | ||||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | // | ||||||
|  */ | // 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 dl | package dl | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"bytes" | 	"bytes" | ||||||
| 	"context" | 	"context" | ||||||
|  | 	"fmt" | ||||||
| 	"io" | 	"io" | ||||||
| 	"mime" | 	"mime" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| @@ -29,11 +31,8 @@ import ( | |||||||
| 	"path" | 	"path" | ||||||
| 	"path/filepath" | 	"path/filepath" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"time" |  | ||||||
|  |  | ||||||
| 	"github.com/mholt/archiver/v4" | 	"github.com/mholt/archiver/v4" | ||||||
| 	"github.com/schollz/progressbar/v3" |  | ||||||
| 	"plemya-x.ru/alr/internal/shutils/handlers" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // FileDownloader загружает файлы с использованием HTTP | // FileDownloader загружает файлы с использованием HTTP | ||||||
| @@ -52,7 +51,7 @@ func (FileDownloader) MatchURL(string) bool { | |||||||
|  |  | ||||||
| // Download загружает файл с использованием HTTP. Если файл | // Download загружает файл с использованием HTTP. Если файл | ||||||
| // сжат в поддерживаемом формате, он будет распакован | // сжат в поддерживаемом формате, он будет распакован | ||||||
| func (FileDownloader) Download(opts Options) (Type, string, error) { | func (FileDownloader) Download(ctx context.Context, opts Options) (Type, string, error) { | ||||||
| 	// Разбор URL | 	// Разбор URL | ||||||
| 	u, err := url.Parse(opts.URL) | 	u, err := url.Parse(opts.URL) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @@ -92,8 +91,12 @@ func (FileDownloader) Download(opts Options) (Type, string, error) { | |||||||
| 		} | 		} | ||||||
| 		r = localFl | 		r = localFl | ||||||
| 	} else { | 	} else { | ||||||
|  | 		req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return 0, "", fmt.Errorf("failed to create request: %w", err) | ||||||
|  | 		} | ||||||
| 		// Выполнение HTTP GET запроса | 		// Выполнение HTTP GET запроса | ||||||
| 		res, err := http.Get(u.String()) | 		res, err := http.DefaultClient.Do(req) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return 0, "", err | 			return 0, "", err | ||||||
| 		} | 		} | ||||||
| @@ -112,30 +115,15 @@ func (FileDownloader) Download(opts Options) (Type, string, error) { | |||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return 0, "", err | 		return 0, "", err | ||||||
| 	} | 	} | ||||||
| 	defer fl.Close() |  | ||||||
|  |  | ||||||
| 	var bar io.WriteCloser | 	var out io.WriteCloser | ||||||
| 	// Настройка индикатора прогресса | 	// Настройка индикатора прогресса | ||||||
| 	if opts.Progress != nil { | 	if opts.Progress != nil { | ||||||
| 		bar = progressbar.NewOptions64( | 		out = NewProgressWriter(fl, size, name, opts.Progress) | ||||||
| 			size, |  | ||||||
| 			progressbar.OptionSetDescription(name), |  | ||||||
| 			progressbar.OptionSetWriter(opts.Progress), |  | ||||||
| 			progressbar.OptionShowBytes(true), |  | ||||||
| 			progressbar.OptionSetWidth(10), |  | ||||||
| 			progressbar.OptionThrottle(65*time.Millisecond), |  | ||||||
| 			progressbar.OptionShowCount(), |  | ||||||
| 			progressbar.OptionOnCompletion(func() { |  | ||||||
| 				_, _ = io.WriteString(opts.Progress, "\n") |  | ||||||
| 			}), |  | ||||||
| 			progressbar.OptionSpinnerType(14), |  | ||||||
| 			progressbar.OptionFullWidth(), |  | ||||||
| 			progressbar.OptionSetRenderBlankState(true), |  | ||||||
| 		) |  | ||||||
| 		defer bar.Close() |  | ||||||
| 	} else { | 	} else { | ||||||
| 		bar = handlers.NopRWC{} | 		out = fl | ||||||
| 	} | 	} | ||||||
|  | 	defer out.Close() | ||||||
|  |  | ||||||
| 	h, err := opts.NewHash() | 	h, err := opts.NewHash() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @@ -145,9 +133,9 @@ func (FileDownloader) Download(opts Options) (Type, string, error) { | |||||||
| 	var w io.Writer | 	var w io.Writer | ||||||
| 	// Настройка MultiWriter для записи в файл, хеш и индикатор прогресса | 	// Настройка MultiWriter для записи в файл, хеш и индикатор прогресса | ||||||
| 	if opts.Hash != nil { | 	if opts.Hash != nil { | ||||||
| 		w = io.MultiWriter(fl, h, bar) | 		w = io.MultiWriter(h, out) | ||||||
| 	} else { | 	} else { | ||||||
| 		w = io.MultiWriter(fl, bar) | 		w = io.MultiWriter(out) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// Копирование содержимого из источника в файл назначения | 	// Копирование содержимого из источника в файл назначения | ||||||
| @@ -222,7 +210,7 @@ func extractFile(r io.Reader, format archiver.Format, name string, opts Options) | |||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			if f.IsDir() { | 			if f.IsDir() { | ||||||
| 				err = os.Mkdir(path, 0o755) | 				err = os.MkdirAll(path, 0o755) | ||||||
| 				if err != nil { | 				if err != nil { | ||||||
| 					return err | 					return err | ||||||
| 				} | 				} | ||||||
|   | |||||||
| @@ -1,24 +1,26 @@ | |||||||
| /* | // This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan. | ||||||
|  * ALR - Any Linux Repository | // It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors. | ||||||
|  * Copyright (C) 2024 Евгений Храмов | // | ||||||
|  * | // ALR - Any Linux Repository | ||||||
|  * This program is free software: you can redistribute it and/or modify | // Copyright (C) 2025 The ALR Authors | ||||||
|  * it under the terms of the GNU General Public License as published by | // | ||||||
|  * the Free Software Foundation, either version 3 of the License, or | // This program is free software: you can redistribute it and/or modify | ||||||
|  * (at your option) any later version. | // it under the terms of the GNU General Public License as published by | ||||||
|  * | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  * This program is distributed in the hope that it will be useful, | // (at your option) any later version. | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | // | ||||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | // This program is distributed in the hope that it will be useful, | ||||||
|  * GNU General Public License for more details. | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  * | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  * You should have received a copy of the GNU General Public License | // GNU General Public License for more details. | ||||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | // | ||||||
|  */ | // 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 dl | package dl | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"context" | ||||||
| 	"errors" | 	"errors" | ||||||
| 	"net/url" | 	"net/url" | ||||||
| 	"path" | 	"path" | ||||||
| @@ -46,7 +48,7 @@ func (GitDownloader) MatchURL(u string) bool { | |||||||
| // Download uses git to clone the repository from the specified URL. | // Download uses git to clone the repository from the specified URL. | ||||||
| // It allows specifying the revision, depth and recursion options | // It allows specifying the revision, depth and recursion options | ||||||
| // via query string | // via query string | ||||||
| func (GitDownloader) Download(opts Options) (Type, string, error) { | func (GitDownloader) Download(ctx context.Context, opts Options) (Type, string, error) { | ||||||
| 	u, err := url.Parse(opts.URL) | 	u, err := url.Parse(opts.URL) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return 0, "", err | 		return 0, "", err | ||||||
| @@ -88,7 +90,7 @@ func (GitDownloader) Download(opts Options) (Type, string, error) { | |||||||
| 		co.RecurseSubmodules = git.DefaultSubmoduleRecursionDepth | 		co.RecurseSubmodules = git.DefaultSubmoduleRecursionDepth | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	r, err := git.PlainClone(opts.Destination, false, co) | 	r, err := git.PlainCloneContext(ctx, opts.Destination, false, co) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return 0, "", err | 		return 0, "", err | ||||||
| 	} | 	} | ||||||
|   | |||||||
							
								
								
									
										247
									
								
								internal/dl/progress_tui.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										247
									
								
								internal/dl/progress_tui.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,247 @@ | |||||||
|  | // 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 dl | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"io" | ||||||
|  | 	"math" | ||||||
|  | 	"os" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
|  | 	"github.com/charmbracelet/bubbles/progress" | ||||||
|  | 	"github.com/charmbracelet/bubbles/spinner" | ||||||
|  | 	tea "github.com/charmbracelet/bubbletea" | ||||||
|  | 	"github.com/leonelquinteros/gotext" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type model struct { | ||||||
|  | 	progress   progress.Model | ||||||
|  | 	spinner    spinner.Model | ||||||
|  | 	percent    float64 | ||||||
|  | 	speed      float64 | ||||||
|  | 	done       bool | ||||||
|  | 	useSpinner bool | ||||||
|  | 	filename   string | ||||||
|  |  | ||||||
|  | 	total      int64 | ||||||
|  | 	downloaded int64 | ||||||
|  | 	elapsed    time.Duration | ||||||
|  | 	remaining  time.Duration | ||||||
|  |  | ||||||
|  | 	width int | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m model) Init() tea.Cmd { | ||||||
|  | 	if m.useSpinner { | ||||||
|  | 		return m.spinner.Tick | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { | ||||||
|  | 	if m.done { | ||||||
|  | 		return m, tea.Quit | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	switch msg := msg.(type) { | ||||||
|  | 	case progressUpdate: | ||||||
|  | 		m.percent = msg.percent | ||||||
|  | 		m.speed = msg.speed | ||||||
|  | 		m.downloaded = msg.downloaded | ||||||
|  | 		m.total = msg.total | ||||||
|  | 		m.elapsed = time.Duration(msg.elapsed) * time.Second | ||||||
|  | 		m.remaining = time.Duration(msg.remaining) * time.Second | ||||||
|  | 		if m.percent >= 1.0 { | ||||||
|  | 			m.done = true | ||||||
|  | 			return m, tea.Quit | ||||||
|  | 		} | ||||||
|  | 		return m, nil | ||||||
|  | 	case tea.WindowSizeMsg: | ||||||
|  | 		m.width = msg.Width | ||||||
|  | 		return m, nil | ||||||
|  | 	case progress.FrameMsg: | ||||||
|  | 		if !m.useSpinner { | ||||||
|  | 			progressModel, cmd := m.progress.Update(msg) | ||||||
|  | 			m.progress = progressModel.(progress.Model) | ||||||
|  | 			return m, cmd | ||||||
|  | 		} | ||||||
|  | 	case spinner.TickMsg: | ||||||
|  | 		if m.useSpinner { | ||||||
|  | 			spinnerModel, cmd := m.spinner.Update(msg) | ||||||
|  | 			m.spinner = spinnerModel | ||||||
|  | 			return m, cmd | ||||||
|  | 		} | ||||||
|  | 	case tea.KeyMsg: | ||||||
|  | 		if msg.String() == "q" { | ||||||
|  | 			return m, tea.Quit | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return m, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m model) View() string { | ||||||
|  | 	if m.done { | ||||||
|  | 		return gotext.Get("%s: done!\n", m.filename) | ||||||
|  | 	} | ||||||
|  | 	if m.useSpinner { | ||||||
|  | 		return gotext.Get( | ||||||
|  | 			"%s %s downloading at %s/s\n", | ||||||
|  | 			m.filename, | ||||||
|  | 			m.spinner.View(), | ||||||
|  | 			prettyByteSize(int64(m.speed)), | ||||||
|  | 		) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	leftPart := m.filename | ||||||
|  |  | ||||||
|  | 	rightPart := fmt.Sprintf("%.2f%% (%s/%s, %s/s) [%v:%v]\n", m.percent*100, | ||||||
|  | 		prettyByteSize(m.downloaded), | ||||||
|  | 		prettyByteSize(m.total), | ||||||
|  | 		prettyByteSize(int64(m.speed)), | ||||||
|  | 		m.elapsed, | ||||||
|  | 		m.remaining, | ||||||
|  | 	) | ||||||
|  |  | ||||||
|  | 	m.progress.Width = m.width - len(leftPart) - len(rightPart) - 6 | ||||||
|  | 	bar := m.progress.ViewAs(m.percent) | ||||||
|  | 	return fmt.Sprintf( | ||||||
|  | 		"%s %s %s", | ||||||
|  | 		leftPart, | ||||||
|  | 		bar, | ||||||
|  | 		rightPart, | ||||||
|  | 	) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func prettyByteSize(b int64) string { | ||||||
|  | 	bf := float64(b) | ||||||
|  | 	for _, unit := range []string{"", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi"} { | ||||||
|  | 		if math.Abs(bf) < 1024.0 { | ||||||
|  | 			return fmt.Sprintf("%3.1f%sB", bf, unit) | ||||||
|  | 		} | ||||||
|  | 		bf /= 1024.0 | ||||||
|  | 	} | ||||||
|  | 	return fmt.Sprintf("%.1fYiB", bf) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type progressUpdate struct { | ||||||
|  | 	percent float64 | ||||||
|  | 	speed   float64 | ||||||
|  | 	total   int64 | ||||||
|  |  | ||||||
|  | 	downloaded int64 | ||||||
|  | 	elapsed    float64 | ||||||
|  | 	remaining  float64 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type ProgressWriter struct { | ||||||
|  | 	baseWriter   io.WriteCloser | ||||||
|  | 	total        int64 | ||||||
|  | 	downloaded   int64 | ||||||
|  | 	startTime    time.Time | ||||||
|  | 	onProgress   func(progressUpdate) | ||||||
|  | 	lastReported time.Time | ||||||
|  | 	doneChan     chan struct{} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (pw *ProgressWriter) Write(p []byte) (int, error) { | ||||||
|  | 	n, err := pw.baseWriter.Write(p) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return n, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	pw.downloaded += int64(n) | ||||||
|  | 	now := time.Now() | ||||||
|  | 	elapsed := now.Sub(pw.startTime).Seconds() | ||||||
|  | 	speed := float64(pw.downloaded) / elapsed | ||||||
|  | 	var remaining, percent float64 | ||||||
|  | 	if pw.total > 0 { | ||||||
|  | 		remaining = (float64(pw.total) - float64(pw.downloaded)) / speed | ||||||
|  | 		percent = float64(pw.downloaded) / float64(pw.total) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if now.Sub(pw.lastReported) > 100*time.Millisecond { | ||||||
|  | 		pw.onProgress(progressUpdate{ | ||||||
|  | 			percent:    percent, | ||||||
|  | 			speed:      speed, | ||||||
|  | 			total:      pw.total, | ||||||
|  | 			downloaded: pw.downloaded, | ||||||
|  | 			elapsed:    elapsed, | ||||||
|  | 			remaining:  remaining, | ||||||
|  | 		}) | ||||||
|  | 		pw.lastReported = now | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return n, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (pw *ProgressWriter) Close() error { | ||||||
|  | 	pw.onProgress(progressUpdate{ | ||||||
|  | 		percent:    1, | ||||||
|  | 		speed:      0, | ||||||
|  | 		downloaded: pw.downloaded, | ||||||
|  | 	}) | ||||||
|  | 	<-pw.doneChan | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func NewProgressWriter(base io.WriteCloser, max int64, filename string, out io.Writer) *ProgressWriter { | ||||||
|  | 	var m *model | ||||||
|  | 	if max == -1 { | ||||||
|  | 		m = &model{ | ||||||
|  | 			spinner:    spinner.New(), | ||||||
|  | 			useSpinner: true, | ||||||
|  | 			filename:   filename, | ||||||
|  | 		} | ||||||
|  | 		m.spinner.Spinner = spinner.Dot | ||||||
|  | 	} else { | ||||||
|  | 		m = &model{ | ||||||
|  | 			progress: progress.New( | ||||||
|  | 				progress.WithDefaultGradient(), | ||||||
|  | 				progress.WithoutPercentage(), | ||||||
|  | 			), | ||||||
|  | 			useSpinner: false, | ||||||
|  | 			filename:   filename, | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	p := tea.NewProgram(m, | ||||||
|  | 		tea.WithInput(nil), | ||||||
|  | 		tea.WithOutput(out), | ||||||
|  | 	) | ||||||
|  |  | ||||||
|  | 	pw := &ProgressWriter{ | ||||||
|  | 		baseWriter: base, | ||||||
|  | 		total:      max, | ||||||
|  | 		startTime:  time.Now(), | ||||||
|  | 		doneChan:   make(chan struct{}), | ||||||
|  | 		onProgress: func(update progressUpdate) { | ||||||
|  | 			p.Send(update) | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	go func() { | ||||||
|  | 		defer close(pw.doneChan) | ||||||
|  | 		if _, err := p.Run(); err != nil { | ||||||
|  | 			fmt.Fprintf(os.Stderr, "Error running progress writer: %v\n", err) | ||||||
|  | 			os.Exit(1) | ||||||
|  | 		} | ||||||
|  | 	}() | ||||||
|  |  | ||||||
|  | 	return pw | ||||||
|  | } | ||||||
| @@ -1,6 +1,26 @@ | |||||||
|  | // 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 dl | package dl | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"context" | ||||||
| 	"errors" | 	"errors" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"os" | 	"os" | ||||||
| @@ -30,7 +50,7 @@ func (TorrentDownloader) MatchURL(u string) bool { | |||||||
| } | } | ||||||
|  |  | ||||||
| // Download downloads a file over the BitTorrent protocol. | // Download downloads a file over the BitTorrent protocol. | ||||||
| func (TorrentDownloader) Download(opts Options) (Type, string, error) { | func (TorrentDownloader) Download(ctx context.Context, opts Options) (Type, string, error) { | ||||||
| 	aria2Path, err := exec.LookPath("aria2c") | 	aria2Path, err := exec.LookPath("aria2c") | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return 0, "", ErrAria2NotFound | 		return 0, "", ErrAria2NotFound | ||||||
| @@ -38,7 +58,7 @@ func (TorrentDownloader) Download(opts Options) (Type, string, error) { | |||||||
|  |  | ||||||
| 	opts.URL = strings.TrimPrefix(opts.URL, "torrent+") | 	opts.URL = strings.TrimPrefix(opts.URL, "torrent+") | ||||||
|  |  | ||||||
| 	cmd := exec.Command(aria2Path, "--summary-interval=0", "--log-level=warn", "--seed-time=0", "--dir="+opts.Destination, opts.URL) | 	cmd := exec.CommandContext(ctx, aria2Path, "--summary-interval=0", "--log-level=warn", "--seed-time=0", "--dir="+opts.Destination, opts.URL) | ||||||
| 	cmd.Stdout = os.Stdout | 	cmd.Stdout = os.Stdout | ||||||
| 	cmd.Stderr = os.Stderr | 	cmd.Stderr = os.Stderr | ||||||
| 	err = cmd.Run() | 	err = cmd.Run() | ||||||
|   | |||||||
| @@ -1,48 +1,61 @@ | |||||||
| /* | // This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan. | ||||||
|  * ALR - Any Linux Repository | // It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors. | ||||||
|  * Copyright (C) 2024 Евгений Храмов | // | ||||||
|  * | // ALR - Any Linux Repository | ||||||
|  * This program is free software: you can redistribute it and/or modify | // Copyright (C) 2025 The ALR Authors | ||||||
|  * it under the terms of the GNU General Public License as published by | // | ||||||
|  * the Free Software Foundation, either version 3 of the License, or | // This program is free software: you can redistribute it and/or modify | ||||||
|  * (at your option) any later version. | // it under the terms of the GNU General Public License as published by | ||||||
|  * | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  * This program is distributed in the hope that it will be useful, | // (at your option) any later version. | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | // | ||||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | // This program is distributed in the hope that it will be useful, | ||||||
|  * GNU General Public License for more details. | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  * | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  * You should have received a copy of the GNU General Public License | // GNU General Public License for more details. | ||||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | // | ||||||
|  */ | // 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 dlcache | package dlcache | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| 	"crypto/sha1" |  | ||||||
| 	"encoding/hex" |  | ||||||
| 	"io" |  | ||||||
| 	"os" | 	"os" | ||||||
| 	"path/filepath" | 	"path/filepath" | ||||||
|  |  | ||||||
| 	"plemya-x.ru/alr/internal/config" | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/config" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // BasePath returns the base path of the download cache | type Config interface { | ||||||
| func BasePath(ctx context.Context) string { | 	GetPaths() *config.Paths | ||||||
| 	return filepath.Join(config.GetPaths(ctx).CacheDir, "dl") | } | ||||||
|  |  | ||||||
|  | type DownloadCache struct { | ||||||
|  | 	cfg Config | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func New(cfg Config) *DownloadCache { | ||||||
|  | 	return &DownloadCache{ | ||||||
|  | 		cfg, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (dc *DownloadCache) BasePath(ctx context.Context) string { | ||||||
|  | 	return filepath.Join( | ||||||
|  | 		dc.cfg.GetPaths().CacheDir, "dl", | ||||||
|  | 	) | ||||||
| } | } | ||||||
|  |  | ||||||
| // New creates a new directory with the given ID in the cache. | // New creates a new directory with the given ID in the cache. | ||||||
| // If a directory with the same ID already exists, | // If a directory with the same ID already exists, | ||||||
| // it will be deleted before creating a new one. | // it will be deleted before creating a new one. | ||||||
| func New(ctx context.Context, id string) (string, error) { | func (dc *DownloadCache) New(ctx context.Context, id string) (string, error) { | ||||||
| 	h, err := hashID(id) | 	h, err := hashID(id) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return "", err | 		return "", err | ||||||
| 	} | 	} | ||||||
| 	itemPath := filepath.Join(BasePath(ctx), h) | 	itemPath := filepath.Join(dc.BasePath(ctx), h) | ||||||
|  |  | ||||||
| 	fi, err := os.Stat(itemPath) | 	fi, err := os.Stat(itemPath) | ||||||
| 	if err == nil || (fi != nil && !fi.IsDir()) { | 	if err == nil || (fi != nil && !fi.IsDir()) { | ||||||
| @@ -65,12 +78,12 @@ func New(ctx context.Context, id string) (string, error) { | |||||||
| // returns the directory and true. If it | // returns the directory and true. If it | ||||||
| // does not exist, it returns an empty string | // does not exist, it returns an empty string | ||||||
| // and false. | // and false. | ||||||
| func Get(ctx context.Context, id string) (string, bool) { | func (dc *DownloadCache) Get(ctx context.Context, id string) (string, bool) { | ||||||
| 	h, err := hashID(id) | 	h, err := hashID(id) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return "", false | 		return "", false | ||||||
| 	} | 	} | ||||||
| 	itemPath := filepath.Join(BasePath(ctx), h) | 	itemPath := filepath.Join(dc.BasePath(ctx), h) | ||||||
|  |  | ||||||
| 	_, err = os.Stat(itemPath) | 	_, err = os.Stat(itemPath) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @@ -79,15 +92,3 @@ func Get(ctx context.Context, id string) (string, bool) { | |||||||
|  |  | ||||||
| 	return itemPath, true | 	return itemPath, true | ||||||
| } | } | ||||||
|  |  | ||||||
| // hashID hashes the input ID with SHA1 |  | ||||||
| // and returns the hex string of the hashed |  | ||||||
| // ID. |  | ||||||
| func hashID(id string) (string, error) { |  | ||||||
| 	h := sha1.New() |  | ||||||
| 	_, err := io.WriteString(h, id) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return "", err |  | ||||||
| 	} |  | ||||||
| 	return hex.EncodeToString(h.Sum(nil)), nil |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -1,20 +1,21 @@ | |||||||
| /* | // This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan. | ||||||
|  * ALR - Any Linux Repository | // It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors. | ||||||
|  * Copyright (C) 2024 Евгений Храмов | // | ||||||
|  * | // ALR - Any Linux Repository | ||||||
|  * This program is free software: you can redistribute it and/or modify | // Copyright (C) 2025 The ALR Authors | ||||||
|  * it under the terms of the GNU General Public License as published by | // | ||||||
|  * the Free Software Foundation, either version 3 of the License, or | // This program is free software: you can redistribute it and/or modify | ||||||
|  * (at your option) any later version. | // it under the terms of the GNU General Public License as published by | ||||||
|  * | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  * This program is distributed in the hope that it will be useful, | // (at your option) any later version. | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | // | ||||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | // This program is distributed in the hope that it will be useful, | ||||||
|  * GNU General Public License for more details. | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  * | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  * You should have received a copy of the GNU General Public License | // GNU General Public License for more details. | ||||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | // | ||||||
|  */ | // 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 dlcache_test | package dlcache_test | ||||||
|  |  | ||||||
| @@ -27,26 +28,53 @@ import ( | |||||||
| 	"path/filepath" | 	"path/filepath" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| 	"plemya-x.ru/alr/internal/config" | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/config" | ||||||
| 	"plemya-x.ru/alr/internal/dlcache" | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/dlcache" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func init() { | type TestALRConfig struct { | ||||||
|  | 	CacheDir string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *TestALRConfig) GetPaths() *config.Paths { | ||||||
|  | 	return &config.Paths{ | ||||||
|  | 		CacheDir: c.CacheDir, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func prepare(t *testing.T) *TestALRConfig { | ||||||
|  | 	t.Helper() | ||||||
|  |  | ||||||
| 	dir, err := os.MkdirTemp("/tmp", "alr-dlcache-test.*") | 	dir, err := os.MkdirTemp("/tmp", "alr-dlcache-test.*") | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		panic(err) | 		panic(err) | ||||||
| 	} | 	} | ||||||
| 	config.GetPaths(context.Background()).RepoDir = dir |  | ||||||
|  | 	return &TestALRConfig{ | ||||||
|  | 		CacheDir: dir, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func cleanup(t *testing.T, cfg *TestALRConfig) { | ||||||
|  | 	t.Helper() | ||||||
|  | 	os.Remove(cfg.CacheDir) | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestNew(t *testing.T) { | func TestNew(t *testing.T) { | ||||||
|  | 	cfg := prepare(t) | ||||||
|  | 	defer cleanup(t, cfg) | ||||||
|  |  | ||||||
|  | 	dc := dlcache.New(cfg) | ||||||
|  |  | ||||||
|  | 	ctx := context.Background() | ||||||
|  |  | ||||||
| 	const id = "https://example.com" | 	const id = "https://example.com" | ||||||
| 	dir, err := dlcache.New(id) | 	dir, err := dc.New(ctx, id) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Errorf("Expected no error, got %s", err) | 		t.Errorf("Expected no error, got %s", err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	exp := filepath.Join(dlcache.BasePath(), sha1sum(id)) | 	exp := filepath.Join(dc.BasePath(ctx), sha1sum(id)) | ||||||
| 	if dir != exp { | 	if dir != exp { | ||||||
| 		t.Errorf("Expected %s, got %s", exp, dir) | 		t.Errorf("Expected %s, got %s", exp, dir) | ||||||
| 	} | 	} | ||||||
| @@ -60,7 +88,7 @@ func TestNew(t *testing.T) { | |||||||
| 		t.Errorf("Expected cache item to be a directory") | 		t.Errorf("Expected cache item to be a directory") | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	dir2, ok := dlcache.Get(id) | 	dir2, ok := dc.Get(ctx, id) | ||||||
| 	if !ok { | 	if !ok { | ||||||
| 		t.Errorf("Expected Get() to return valid value") | 		t.Errorf("Expected Get() to return valid value") | ||||||
| 	} | 	} | ||||||
|   | |||||||
							
								
								
									
										32
									
								
								internal/dlcache/utils.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								internal/dlcache/utils.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | |||||||
|  | // 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 dlcache | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"crypto/sha1" | ||||||
|  | 	"encoding/hex" | ||||||
|  | 	"io" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func hashID(id string) (string, error) { | ||||||
|  | 	h := sha1.New() | ||||||
|  | 	_, err := io.WriteString(h, id) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", err | ||||||
|  | 	} | ||||||
|  | 	return hex.EncodeToString(h.Sum(nil)), nil | ||||||
|  | } | ||||||
							
								
								
									
										152
									
								
								internal/logger/hclog.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								internal/logger/hclog.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,152 @@ | |||||||
|  | // 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 logger | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"io" | ||||||
|  | 	"log" | ||||||
|  | 	"strings" | ||||||
|  |  | ||||||
|  | 	chLog "github.com/charmbracelet/log" | ||||||
|  | 	"github.com/hashicorp/go-hclog" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type HCLoggerAdapter struct { | ||||||
|  | 	logger *Logger | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func hclogLevelTochLog(level hclog.Level) chLog.Level { | ||||||
|  | 	switch level { | ||||||
|  | 	case hclog.Debug: | ||||||
|  | 		return chLog.DebugLevel | ||||||
|  | 	case hclog.Info: | ||||||
|  | 		return chLog.InfoLevel | ||||||
|  | 	case hclog.Warn: | ||||||
|  | 		return chLog.WarnLevel | ||||||
|  | 	case hclog.Error: | ||||||
|  | 		return chLog.ErrorLevel | ||||||
|  | 	} | ||||||
|  | 	return chLog.FatalLevel | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a *HCLoggerAdapter) Log(level hclog.Level, msg string, args ...interface{}) { | ||||||
|  | 	filteredArgs := make([]interface{}, 0, len(args)) | ||||||
|  | 	for i := 0; i < len(args); i += 2 { | ||||||
|  | 		if i+1 >= len(args) { | ||||||
|  | 			filteredArgs = append(filteredArgs, args[i]) | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		key, ok := args[i].(string) | ||||||
|  | 		if !ok || key != "timestamp" { | ||||||
|  | 			filteredArgs = append(filteredArgs, args[i], args[i+1]) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Start ugly hacks | ||||||
|  | 	// Ignore exit messages | ||||||
|  | 	// - https://github.com/hashicorp/go-plugin/issues/331 | ||||||
|  | 	// - https://github.com/hashicorp/go-plugin/issues/203 | ||||||
|  | 	// - https://github.com/hashicorp/go-plugin/issues/192 | ||||||
|  | 	var chLogLevel chLog.Level | ||||||
|  | 	if msg == "plugin process exited" || | ||||||
|  | 		strings.HasPrefix(msg, "[ERR] plugin: stream copy 'stderr' error") || | ||||||
|  | 		strings.HasPrefix(msg, "[DEBUG] plugin") { | ||||||
|  | 		chLogLevel = chLog.DebugLevel | ||||||
|  | 	} else { | ||||||
|  | 		chLogLevel = hclogLevelTochLog(level) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	a.logger.l.Log(chLogLevel, msg, filteredArgs...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a *HCLoggerAdapter) Trace(msg string, args ...interface{}) { | ||||||
|  | 	a.Log(hclog.Trace, msg, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a *HCLoggerAdapter) Debug(msg string, args ...interface{}) { | ||||||
|  | 	a.Log(hclog.Debug, msg, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a *HCLoggerAdapter) Info(msg string, args ...interface{}) { | ||||||
|  | 	a.Log(hclog.Info, msg, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a *HCLoggerAdapter) Warn(msg string, args ...interface{}) { | ||||||
|  | 	a.Log(hclog.Warn, msg, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a *HCLoggerAdapter) Error(msg string, args ...interface{}) { | ||||||
|  | 	a.Log(hclog.Error, msg, args...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a *HCLoggerAdapter) IsTrace() bool { | ||||||
|  | 	return a.logger.l.GetLevel() <= chLog.DebugLevel | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a *HCLoggerAdapter) IsDebug() bool { | ||||||
|  | 	return a.logger.l.GetLevel() <= chLog.DebugLevel | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a *HCLoggerAdapter) IsInfo() bool { | ||||||
|  | 	return a.logger.l.GetLevel() <= chLog.InfoLevel | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a *HCLoggerAdapter) IsWarn() bool { | ||||||
|  | 	return a.logger.l.GetLevel() <= chLog.WarnLevel | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a *HCLoggerAdapter) IsError() bool { | ||||||
|  | 	return a.logger.l.GetLevel() <= chLog.ErrorLevel | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a *HCLoggerAdapter) ImpliedArgs() []interface{} { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a *HCLoggerAdapter) With(args ...interface{}) hclog.Logger { | ||||||
|  | 	return a | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a *HCLoggerAdapter) Name() string { | ||||||
|  | 	return "" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a *HCLoggerAdapter) Named(name string) hclog.Logger { | ||||||
|  | 	return a | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a *HCLoggerAdapter) ResetNamed(name string) hclog.Logger { | ||||||
|  | 	return a | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a *HCLoggerAdapter) SetLevel(level hclog.Level) { | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a *HCLoggerAdapter) StandardLogger(opts *hclog.StandardLoggerOptions) *log.Logger { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (a *HCLoggerAdapter) StandardWriter(opts *hclog.StandardLoggerOptions) io.Writer { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func GetHCLoggerAdapter() *HCLoggerAdapter { | ||||||
|  | 	return &HCLoggerAdapter{ | ||||||
|  | 		logger: logger, | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										111
									
								
								internal/logger/log.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								internal/logger/log.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,111 @@ | |||||||
|  | // 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 logger | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"log/slog" | ||||||
|  | 	"os" | ||||||
|  |  | ||||||
|  | 	"github.com/charmbracelet/lipgloss" | ||||||
|  |  | ||||||
|  | 	chLog "github.com/charmbracelet/log" | ||||||
|  | 	"github.com/leonelquinteros/gotext" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type Logger struct { | ||||||
|  | 	l *chLog.Logger | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func setupLogger() *chLog.Logger { | ||||||
|  | 	styles := chLog.DefaultStyles() | ||||||
|  | 	logger := chLog.New(os.Stderr) | ||||||
|  | 	styles.Levels[chLog.InfoLevel] = lipgloss.NewStyle(). | ||||||
|  | 		SetString("-->"). | ||||||
|  | 		Foreground(lipgloss.Color("35")) | ||||||
|  | 	styles.Levels[chLog.ErrorLevel] = lipgloss.NewStyle(). | ||||||
|  | 		SetString(gotext.Get("ERROR")). | ||||||
|  | 		Padding(0, 1, 0, 1). | ||||||
|  | 		Background(lipgloss.Color("204")). | ||||||
|  | 		Foreground(lipgloss.Color("0")) | ||||||
|  | 	logger.SetStyles(styles) | ||||||
|  | 	return logger | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func New() *Logger { | ||||||
|  | 	return &Logger{ | ||||||
|  | 		l: setupLogger(), | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func slogLevelToLog(level slog.Level) chLog.Level { | ||||||
|  | 	switch level { | ||||||
|  | 	case slog.LevelDebug: | ||||||
|  | 		return chLog.DebugLevel | ||||||
|  | 	case slog.LevelInfo: | ||||||
|  | 		return chLog.InfoLevel | ||||||
|  | 	case slog.LevelWarn: | ||||||
|  | 		return chLog.WarnLevel | ||||||
|  | 	case slog.LevelError: | ||||||
|  | 		return chLog.ErrorLevel | ||||||
|  | 	} | ||||||
|  | 	return chLog.FatalLevel | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (l *Logger) SetLevel(level slog.Level) { | ||||||
|  | 	l.l.SetLevel(slogLevelToLog(level)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (l *Logger) Enabled(ctx context.Context, level slog.Level) bool { | ||||||
|  | 	return l.l.Enabled(ctx, level) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (l *Logger) Handle(ctx context.Context, rec slog.Record) error { | ||||||
|  | 	return l.l.Handle(ctx, rec) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (l *Logger) WithAttrs(attrs []slog.Attr) slog.Handler { | ||||||
|  | 	sl := *l | ||||||
|  | 	sl.l = l.l.WithAttrs(attrs).(*chLog.Logger) | ||||||
|  | 	return &sl | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (l *Logger) WithGroup(name string) slog.Handler { | ||||||
|  | 	sl := *l | ||||||
|  | 	sl.l = l.l.WithGroup(name).(*chLog.Logger) | ||||||
|  | 	return &sl | ||||||
|  | } | ||||||
|  |  | ||||||
|  | var logger *Logger | ||||||
|  |  | ||||||
|  | func SetupDefault() *Logger { | ||||||
|  | 	logger = New() | ||||||
|  | 	slogLogger := slog.New(logger) | ||||||
|  | 	slog.SetDefault(slogLogger) | ||||||
|  | 	return logger | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func SetupForGoPlugin() { | ||||||
|  | 	logger.l.SetFormatter(chLog.JSONFormatter) | ||||||
|  | 	chLog.TimestampKey = "@timestamp" | ||||||
|  | 	chLog.MessageKey = "@message" | ||||||
|  | 	chLog.LevelKey = "@level" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func GetLogger() *Logger { | ||||||
|  | 	return logger | ||||||
|  | } | ||||||
| @@ -1,20 +1,21 @@ | |||||||
| /* | // This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan. | ||||||
|  * ALR - Any Linux Repository | // It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors. | ||||||
|  * Copyright (C) 2024 Евгений Храмов | // | ||||||
|  * | // ALR - Any Linux Repository | ||||||
|  * This program is free software: you can redistribute it and/or modify | // Copyright (C) 2025 The ALR Authors | ||||||
|  * it under the terms of the GNU General Public License as published by | // | ||||||
|  * the Free Software Foundation, either version 3 of the License, or | // This program is free software: you can redistribute it and/or modify | ||||||
|  * (at your option) any later version. | // it under the terms of the GNU General Public License as published by | ||||||
|  * | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  * This program is distributed in the hope that it will be useful, | // (at your option) any later version. | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | // | ||||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | // This program is distributed in the hope that it will be useful, | ||||||
|  * GNU General Public License for more details. | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  * | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  * You should have received a copy of the GNU General Public License | // GNU General Public License for more details. | ||||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | // | ||||||
|  */ | // 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 osutils | package osutils | ||||||
|  |  | ||||||
| @@ -54,12 +55,12 @@ func copyDirOrFile(sourcePath, destPath string) error { | |||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if sourceInfo.IsDir() { | 	switch { | ||||||
|  | 	case sourceInfo.IsDir(): | ||||||
| 		return copyDir(sourcePath, destPath, sourceInfo) | 		return copyDir(sourcePath, destPath, sourceInfo) | ||||||
| 	} else if sourceInfo.Mode().IsRegular() { | 	case sourceInfo.Mode().IsRegular(): | ||||||
| 		return copyFile(sourcePath, destPath, sourceInfo) | 		return copyFile(sourcePath, destPath, sourceInfo) | ||||||
| 	} else { | 	default: | ||||||
| 		// ignore non-regular files |  | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,32 +1,36 @@ | |||||||
| /* | // This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan. | ||||||
|  * ALR - Any Linux Repository | // It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors. | ||||||
|  * Copyright (C) 2024 Евгений Храмов | // | ||||||
|  * | // ALR - Any Linux Repository | ||||||
|  * This program is free software: you can redistribute it and/or modify | // Copyright (C) 2025 The ALR Authors | ||||||
|  * it under the terms of the GNU General Public License as published by | // | ||||||
|  * the Free Software Foundation, either version 3 of the License, or | // This program is free software: you can redistribute it and/or modify | ||||||
|  * (at your option) any later version. | // it under the terms of the GNU General Public License as published by | ||||||
|  * | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  * This program is distributed in the hope that it will be useful, | // (at your option) any later version. | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | // | ||||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | // This program is distributed in the hope that it will be useful, | ||||||
|  * GNU General Public License for more details. | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  * | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  * You should have received a copy of the GNU General Public License | // GNU General Public License for more details. | ||||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | // | ||||||
|  */ | // 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 overrides | package overrides | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"fmt" | ||||||
| 	"reflect" | 	"reflect" | ||||||
|  | 	"regexp" | ||||||
| 	"strings" | 	"strings" | ||||||
|  |  | ||||||
| 	"plemya-x.ru/alr/internal/cpu" |  | ||||||
| 	"plemya-x.ru/alr/internal/db" |  | ||||||
| 	"plemya-x.ru/alr/pkg/distro" |  | ||||||
| 	"golang.org/x/exp/slices" | 	"golang.org/x/exp/slices" | ||||||
| 	"golang.org/x/text/language" | 	"golang.org/x/text/language" | ||||||
|  |  | ||||||
|  | 	"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/pkg/distro" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type Opts struct { | type Opts struct { | ||||||
| @@ -100,7 +104,7 @@ func Resolve(info *distro.OSRelease, opts *Opts) ([]string, error) { | |||||||
| 	out = append(out, opts.Name) | 	out = append(out, opts.Name) | ||||||
|  |  | ||||||
| 	for index, item := range out { | 	for index, item := range out { | ||||||
| 		out[index] = strings.TrimPrefix(strings.ReplaceAll(item, "-", "_"), "_") | 		out[index] = strings.TrimPrefix(item, "_") | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return out, nil | 	return out, nil | ||||||
| @@ -153,6 +157,8 @@ type ResolvedPackage struct { | |||||||
| 	Version       string   `sh:"version"` | 	Version       string   `sh:"version"` | ||||||
| 	Release       int      `sh:"release"` | 	Release       int      `sh:"release"` | ||||||
| 	Epoch         uint     `sh:"epoch"` | 	Epoch         uint     `sh:"epoch"` | ||||||
|  | 	Group         string   `db:"group_name"` | ||||||
|  | 	Summary       string   `db:"summary"` | ||||||
| 	Description   string   `db:"description"` | 	Description   string   `db:"description"` | ||||||
| 	Homepage      string   `db:"homepage"` | 	Homepage      string   `db:"homepage"` | ||||||
| 	Maintainer    string   `db:"maintainer"` | 	Maintainer    string   `db:"maintainer"` | ||||||
| @@ -221,3 +227,19 @@ func parseLangs(langs []string, tags []language.Tag) ([]string, error) { | |||||||
| 	out = slices.Compact(out) | 	out = slices.Compact(out) | ||||||
| 	return out, nil | 	return out, nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func ReleasePlatformSpecific(release int, info *distro.OSRelease) string { | ||||||
|  | 	if info.ID == "altlinux" { | ||||||
|  | 		return fmt.Sprintf("alt%d", release) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if info.ID == "fedora" || slices.Contains(info.Like, "fedora") { | ||||||
|  | 		re := regexp.MustCompile(`platform:(\S+)`) | ||||||
|  | 		match := re.FindStringSubmatch(info.PlatformID) | ||||||
|  | 		if len(match) > 1 { | ||||||
|  | 			return fmt.Sprintf("%d.%s", release, match[1]) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return fmt.Sprintf("%d", release) | ||||||
|  | } | ||||||
|   | |||||||
| @@ -1,20 +1,21 @@ | |||||||
| /* | // This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan. | ||||||
|  * ALR - Any Linux Repository | // It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors. | ||||||
|  * Copyright (C) 2024 Евгений Храмов | // | ||||||
|  * | // ALR - Any Linux Repository | ||||||
|  * This program is free software: you can redistribute it and/or modify | // Copyright (C) 2025 The ALR Authors | ||||||
|  * it under the terms of the GNU General Public License as published by | // | ||||||
|  * the Free Software Foundation, either version 3 of the License, or | // This program is free software: you can redistribute it and/or modify | ||||||
|  * (at your option) any later version. | // it under the terms of the GNU General Public License as published by | ||||||
|  * | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  * This program is distributed in the hope that it will be useful, | // (at your option) any later version. | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | // | ||||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | // This program is distributed in the hope that it will be useful, | ||||||
|  * GNU General Public License for more details. | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  * | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  * You should have received a copy of the GNU General Public License | // GNU General Public License for more details. | ||||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | // | ||||||
|  */ | // 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 overrides_test | package overrides_test | ||||||
|  |  | ||||||
| @@ -23,9 +24,11 @@ import ( | |||||||
| 	"reflect" | 	"reflect" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| 	"plemya-x.ru/alr/internal/overrides" | 	"github.com/stretchr/testify/assert" | ||||||
| 	"plemya-x.ru/alr/pkg/distro" |  | ||||||
| 	"golang.org/x/text/language" | 	"golang.org/x/text/language" | ||||||
|  |  | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/overrides" | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| var info = &distro.OSRelease{ | var info = &distro.OSRelease{ | ||||||
| @@ -193,3 +196,42 @@ func TestResolveLangs(t *testing.T) { | |||||||
| 		t.Errorf("expected %v, got %v", expected, names) | 		t.Errorf("expected %v, got %v", expected, names) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func TestReleasePlatformSpecific(t *testing.T) { | ||||||
|  | 	type testCase struct { | ||||||
|  | 		info     *distro.OSRelease | ||||||
|  | 		expected string | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, tc := range []testCase{ | ||||||
|  | 		{ | ||||||
|  | 			info: &distro.OSRelease{ | ||||||
|  | 				ID:         "centos", | ||||||
|  | 				Like:       []string{"rhel", "fedora"}, | ||||||
|  | 				PlatformID: "platform:el8", | ||||||
|  | 			}, | ||||||
|  | 			expected: "1.el8", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			info: &distro.OSRelease{ | ||||||
|  | 				ID:         "fedora", | ||||||
|  | 				PlatformID: "platform:f42", | ||||||
|  | 			}, | ||||||
|  | 			expected: "1.f42", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			info: &distro.OSRelease{ | ||||||
|  | 				ID: "altlinux", | ||||||
|  | 			}, | ||||||
|  | 			expected: "alt1", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			info: &distro.OSRelease{ | ||||||
|  | 				ID: "ubuntu", | ||||||
|  | 			}, | ||||||
|  | 			expected: "1", | ||||||
|  | 		}, | ||||||
|  | 	} { | ||||||
|  | 		assert.Equal(t, tc.expected, overrides.ReleasePlatformSpecific(1, tc.info)) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|   | |||||||
| @@ -1,20 +1,21 @@ | |||||||
| /* | // This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan. | ||||||
|  * ALR - Any Linux Repository | // It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors. | ||||||
|  * Copyright (C) 2024 Евгений Храмов | // | ||||||
|  * | // ALR - Any Linux Repository | ||||||
|  * This program is free software: you can redistribute it and/or modify | // Copyright (C) 2025 The ALR Authors | ||||||
|  * it under the terms of the GNU General Public License as published by | // | ||||||
|  * the Free Software Foundation, either version 3 of the License, or | // This program is free software: you can redistribute it and/or modify | ||||||
|  * (at your option) any later version. | // it under the terms of the GNU General Public License as published by | ||||||
|  * | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  * This program is distributed in the hope that it will be useful, | // (at your option) any later version. | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | // | ||||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | // This program is distributed in the hope that it will be useful, | ||||||
|  * GNU General Public License for more details. | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  * | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  * You should have received a copy of the GNU General Public License | // GNU General Public License for more details. | ||||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | // | ||||||
|  */ | // 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 pager | package pager | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,20 +1,21 @@ | |||||||
| /* | // This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan. | ||||||
|  * ALR - Any Linux Repository | // It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors. | ||||||
|  * Copyright (C) 2024 Евгений Храмов | // | ||||||
|  * | // ALR - Any Linux Repository | ||||||
|  * This program is free software: you can redistribute it and/or modify | // Copyright (C) 2025 The ALR Authors | ||||||
|  * it under the terms of the GNU General Public License as published by | // | ||||||
|  * the Free Software Foundation, either version 3 of the License, or | // This program is free software: you can redistribute it and/or modify | ||||||
|  * (at your option) any later version. | // it under the terms of the GNU General Public License as published by | ||||||
|  * | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  * This program is distributed in the hope that it will be useful, | // (at your option) any later version. | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | // | ||||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | // This program is distributed in the hope that it will be useful, | ||||||
|  * GNU General Public License for more details. | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  * | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  * You should have received a copy of the GNU General Public License | // GNU General Public License for more details. | ||||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | // | ||||||
|  */ | // 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 pager | package pager | ||||||
|  |  | ||||||
| @@ -40,7 +41,7 @@ func init() { | |||||||
|  |  | ||||||
| 	b2 := lipgloss.RoundedBorder() | 	b2 := lipgloss.RoundedBorder() | ||||||
| 	b2.Left = "\u2524" | 	b2.Left = "\u2524" | ||||||
| 	infoStyle = titleStyle.Copy().BorderStyle(b2) | 	infoStyle = titleStyle.BorderStyle(b2) | ||||||
| } | } | ||||||
|  |  | ||||||
| type Pager struct { | type Pager struct { | ||||||
|   | |||||||
| @@ -1,20 +1,21 @@ | |||||||
| /* | // This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan. | ||||||
|  * ALR - Any Linux Repository | // It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors. | ||||||
|  * Copyright (C) 2024 Евгений Храмов | // | ||||||
|  * | // ALR - Any Linux Repository | ||||||
|  * This program is free software: you can redistribute it and/or modify | // Copyright (C) 2025 The ALR Authors | ||||||
|  * it under the terms of the GNU General Public License as published by | // | ||||||
|  * the Free Software Foundation, either version 3 of the License, or | // This program is free software: you can redistribute it and/or modify | ||||||
|  * (at your option) any later version. | // it under the terms of the GNU General Public License as published by | ||||||
|  * | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  * This program is distributed in the hope that it will be useful, | // (at your option) any later version. | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | // | ||||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | // This program is distributed in the hope that it will be useful, | ||||||
|  * GNU General Public License for more details. | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  * | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  * You should have received a copy of the GNU General Public License | // GNU General Public License for more details. | ||||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | // | ||||||
|  */ | // 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 decoder | package decoder | ||||||
|  |  | ||||||
| @@ -25,12 +26,13 @@ import ( | |||||||
| 	"strings" | 	"strings" | ||||||
|  |  | ||||||
| 	"github.com/mitchellh/mapstructure" | 	"github.com/mitchellh/mapstructure" | ||||||
| 	"plemya-x.ru/alr/internal/overrides" |  | ||||||
| 	"plemya-x.ru/alr/pkg/distro" |  | ||||||
| 	"golang.org/x/exp/slices" | 	"golang.org/x/exp/slices" | ||||||
| 	"mvdan.cc/sh/v3/expand" | 	"mvdan.cc/sh/v3/expand" | ||||||
| 	"mvdan.cc/sh/v3/interp" | 	"mvdan.cc/sh/v3/interp" | ||||||
| 	"mvdan.cc/sh/v3/syntax" | 	"mvdan.cc/sh/v3/syntax" | ||||||
|  |  | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/overrides" | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| var ErrNotPointerToStruct = errors.New("val must be a pointer to a struct") | var ErrNotPointerToStruct = errors.New("val must be a pointer to a struct") | ||||||
| @@ -121,15 +123,29 @@ func (d *Decoder) DecodeVars(val any) error { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	rVal := reflect.ValueOf(val).Elem() | 	rVal := reflect.ValueOf(val).Elem() | ||||||
|  | 	return d.decodeStruct(rVal) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (d *Decoder) decodeStruct(rVal reflect.Value) error { | ||||||
| 	for i := 0; i < rVal.NumField(); i++ { | 	for i := 0; i < rVal.NumField(); i++ { | ||||||
| 		field := rVal.Field(i) | 		field := rVal.Field(i) | ||||||
| 		fieldType := rVal.Type().Field(i) | 		fieldType := rVal.Type().Field(i) | ||||||
|  |  | ||||||
|  | 		// Пропускаем неэкспортируемые поля | ||||||
| 		if !fieldType.IsExported() { | 		if !fieldType.IsExported() { | ||||||
| 			continue | 			continue | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | 		// Обрабатываем встроенные поля рекурсивно | ||||||
|  | 		if fieldType.Anonymous { | ||||||
|  | 			if field.Kind() == reflect.Struct { | ||||||
|  | 				if err := d.decodeStruct(field); err != nil { | ||||||
|  | 					return err | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 		name := fieldType.Name | 		name := fieldType.Name | ||||||
| 		tag := fieldType.Tag.Get("sh") | 		tag := fieldType.Tag.Get("sh") | ||||||
| 		required := false | 		required := false | ||||||
| @@ -158,15 +174,23 @@ func (d *Decoder) DecodeVars(val any) error { | |||||||
|  |  | ||||||
| 		field.Set(newVal.Elem()) | 		field.Set(newVal.Elem()) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| type ScriptFunc func(ctx context.Context, opts ...interp.RunnerOption) error | type ( | ||||||
|  | 	ScriptFunc             func(ctx context.Context, opts ...interp.RunnerOption) error | ||||||
|  | 	ScriptFuncWithSubshell func(ctx context.Context, opts ...interp.RunnerOption) (*interp.Runner, error) | ||||||
|  | ) | ||||||
|  |  | ||||||
| // GetFunc returns a function corresponding to a bash function | // GetFunc returns a function corresponding to a bash function | ||||||
| // with the given name | // with the given name | ||||||
| func (d *Decoder) GetFunc(name string) (ScriptFunc, bool) { | func (d *Decoder) GetFunc(name string) (ScriptFunc, bool) { | ||||||
|  | 	return d.GetFuncP(name, nil) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type PrepareFunc func(context.Context, *interp.Runner) error | ||||||
|  |  | ||||||
|  | func (d *Decoder) GetFuncP(name string, prepare PrepareFunc) (ScriptFunc, bool) { | ||||||
| 	fn := d.getFunc(name) | 	fn := d.getFunc(name) | ||||||
| 	if fn == nil { | 	if fn == nil { | ||||||
| 		return nil, false | 		return nil, false | ||||||
| @@ -175,12 +199,38 @@ func (d *Decoder) GetFunc(name string) (ScriptFunc, bool) { | |||||||
| 	return func(ctx context.Context, opts ...interp.RunnerOption) error { | 	return func(ctx context.Context, opts ...interp.RunnerOption) error { | ||||||
| 		sub := d.Runner.Subshell() | 		sub := d.Runner.Subshell() | ||||||
| 		for _, opt := range opts { | 		for _, opt := range opts { | ||||||
| 			opt(sub) | 			err := opt(sub) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		if prepare != nil { | ||||||
|  | 			if err := prepare(ctx, sub); err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
| 		return sub.Run(ctx, fn) | 		return sub.Run(ctx, fn) | ||||||
| 	}, true | 	}, true | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (d *Decoder) GetFuncWithSubshell(name string) (ScriptFuncWithSubshell, bool) { | ||||||
|  | 	fn := d.getFunc(name) | ||||||
|  | 	if fn == nil { | ||||||
|  | 		return nil, false | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return func(ctx context.Context, opts ...interp.RunnerOption) (*interp.Runner, error) { | ||||||
|  | 		sub := d.Runner.Subshell() | ||||||
|  | 		for _, opt := range opts { | ||||||
|  | 			err := opt(sub) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return nil, err | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		return sub, sub.Run(ctx, fn) | ||||||
|  | 	}, true | ||||||
|  | } | ||||||
|  |  | ||||||
| func (d *Decoder) getFunc(name string) *syntax.Stmt { | func (d *Decoder) getFunc(name string) *syntax.Stmt { | ||||||
| 	names, err := overrides.Resolve(d.info, overrides.DefaultOpts.WithName(name)) | 	names, err := overrides.Resolve(d.info, overrides.DefaultOpts.WithName(name)) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @@ -221,3 +271,8 @@ func (d *Decoder) getVar(name string) *expand.Variable { | |||||||
| 	} | 	} | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func IsTruthy(value string) bool { | ||||||
|  | 	value = strings.ToLower(strings.TrimSpace(value)) | ||||||
|  | 	return value == "true" || value == "yes" || value == "1" | ||||||
|  | } | ||||||
|   | |||||||
| @@ -1,20 +1,21 @@ | |||||||
| /* | // This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan. | ||||||
|  * ALR - Any Linux Repository | // It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors. | ||||||
|  * Copyright (C) 2024 Евгений Храмов | // | ||||||
|  * | // ALR - Any Linux Repository | ||||||
|  * This program is free software: you can redistribute it and/or modify | // Copyright (C) 2025 The ALR Authors | ||||||
|  * it under the terms of the GNU General Public License as published by | // | ||||||
|  * the Free Software Foundation, either version 3 of the License, or | // This program is free software: you can redistribute it and/or modify | ||||||
|  * (at your option) any later version. | // it under the terms of the GNU General Public License as published by | ||||||
|  * | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  * This program is distributed in the hope that it will be useful, | // (at your option) any later version. | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | // | ||||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | // This program is distributed in the hope that it will be useful, | ||||||
|  * GNU General Public License for more details. | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  * | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  * You should have received a copy of the GNU General Public License | // GNU General Public License for more details. | ||||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | // | ||||||
|  */ | // 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 decoder_test | package decoder_test | ||||||
|  |  | ||||||
| @@ -27,10 +28,11 @@ import ( | |||||||
| 	"strings" | 	"strings" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| 	"plemya-x.ru/alr/internal/shutils/decoder" |  | ||||||
| 	"plemya-x.ru/alr/pkg/distro" |  | ||||||
| 	"mvdan.cc/sh/v3/interp" | 	"mvdan.cc/sh/v3/interp" | ||||||
| 	"mvdan.cc/sh/v3/syntax" | 	"mvdan.cc/sh/v3/syntax" | ||||||
|  |  | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/shutils/decoder" | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type BuildVars struct { | type BuildVars struct { | ||||||
| @@ -56,7 +58,7 @@ const testScript = ` | |||||||
| 	release=1 | 	release=1 | ||||||
| 	epoch=2 | 	epoch=2 | ||||||
| 	desc="Test package" | 	desc="Test package" | ||||||
| 	homepage='//https://gitea.plemya-x.ru/xpamych/ALR' | 	homepage='https://gitea.plemya-x.ru/xpamych/ALR' | ||||||
| 	maintainer='Евгений Храмов <xpamych@yandex.ru>' | 	maintainer='Евгений Храмов <xpamych@yandex.ru>' | ||||||
| 	architectures=('arm64' 'amd64') | 	architectures=('arm64' 'amd64') | ||||||
| 	license=('GPL-3.0-or-later') | 	license=('GPL-3.0-or-later') | ||||||
|   | |||||||
| @@ -1,20 +1,21 @@ | |||||||
| /* | // This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan. | ||||||
|  * ALR - Any Linux Repository | // It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors. | ||||||
|  * Copyright (C) 2024 Евгений Храмов | // | ||||||
|  * | // ALR - Any Linux Repository | ||||||
|  * This program is free software: you can redistribute it and/or modify | // Copyright (C) 2025 The ALR Authors | ||||||
|  * it under the terms of the GNU General Public License as published by | // | ||||||
|  * the Free Software Foundation, either version 3 of the License, or | // This program is free software: you can redistribute it and/or modify | ||||||
|  * (at your option) any later version. | // it under the terms of the GNU General Public License as published by | ||||||
|  * | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  * This program is distributed in the hope that it will be useful, | // (at your option) any later version. | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | // | ||||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | // This program is distributed in the hope that it will be useful, | ||||||
|  * GNU General Public License for more details. | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  * | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  * You should have received a copy of the GNU General Public License | // GNU General Public License for more details. | ||||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | // | ||||||
|  */ | // 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 handlers | package handlers | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,20 +1,21 @@ | |||||||
| /* | // This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan. | ||||||
|  * ALR - Any Linux Repository | // It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors. | ||||||
|  * Copyright (C) 2024 Евгений Храмов | // | ||||||
|  * | // ALR - Any Linux Repository | ||||||
|  * This program is free software: you can redistribute it and/or modify | // Copyright (C) 2025 The ALR Authors | ||||||
|  * it under the terms of the GNU General Public License as published by | // | ||||||
|  * the Free Software Foundation, either version 3 of the License, or | // This program is free software: you can redistribute it and/or modify | ||||||
|  * (at your option) any later version. | // it under the terms of the GNU General Public License as published by | ||||||
|  * | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  * This program is distributed in the hope that it will be useful, | // (at your option) any later version. | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | // | ||||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | // This program is distributed in the hope that it will be useful, | ||||||
|  * GNU General Public License for more details. | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  * | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  * You should have received a copy of the GNU General Public License | // GNU General Public License for more details. | ||||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | // | ||||||
|  */ | // 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 handlers_test | package handlers_test | ||||||
|  |  | ||||||
| @@ -23,11 +24,12 @@ import ( | |||||||
| 	"strings" | 	"strings" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| 	"plemya-x.ru/alr/internal/shutils/handlers" |  | ||||||
| 	"plemya-x.ru/alr/internal/shutils/decoder" |  | ||||||
| 	"plemya-x.ru/alr/pkg/distro" |  | ||||||
| 	"mvdan.cc/sh/v3/interp" | 	"mvdan.cc/sh/v3/interp" | ||||||
| 	"mvdan.cc/sh/v3/syntax" | 	"mvdan.cc/sh/v3/syntax" | ||||||
|  |  | ||||||
|  | 	"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/pkg/distro" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| const testScript = ` | const testScript = ` | ||||||
| @@ -89,7 +91,7 @@ func TestExecFuncs(t *testing.T) { | |||||||
| 		t.Fatalf("Expected test() function to exist") | 		t.Fatalf("Expected test() function to exist") | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	eh := shutils.ExecFuncs{ | 	eh := handlers.ExecFuncs{ | ||||||
| 		"test-cmd": func(hc interp.HandlerContext, name string, args []string) error { | 		"test-cmd": func(hc interp.HandlerContext, name string, args []string) error { | ||||||
| 			if name != "test-cmd" { | 			if name != "test-cmd" { | ||||||
| 				t.Errorf("Expected name to be 'test-cmd', got '%s'", name) | 				t.Errorf("Expected name to be 'test-cmd', got '%s'", name) | ||||||
|   | |||||||
| @@ -1,3 +1,22 @@ | |||||||
|  | // 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 handlers | package handlers | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| @@ -10,7 +29,7 @@ import ( | |||||||
| 	"syscall" | 	"syscall" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"plemya-x.ru/fakeroot" | 	"gitea.plemya-x.ru/Plemya-x/fakeroot" | ||||||
| 	"mvdan.cc/sh/v3/expand" | 	"mvdan.cc/sh/v3/expand" | ||||||
| 	"mvdan.cc/sh/v3/interp" | 	"mvdan.cc/sh/v3/interp" | ||||||
| ) | ) | ||||||
|   | |||||||
| @@ -1,30 +1,32 @@ | |||||||
| /* | // This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan. | ||||||
|  * ALR - Any Linux Repository | // It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors. | ||||||
|  * Copyright (C) 2024 Евгений Храмов | // | ||||||
|  * | // ALR - Any Linux Repository | ||||||
|  * This program is free software: you can redistribute it and/or modify | // Copyright (C) 2025 The ALR Authors | ||||||
|  * it under the terms of the GNU General Public License as published by | // | ||||||
|  * the Free Software Foundation, either version 3 of the License, or | // This program is free software: you can redistribute it and/or modify | ||||||
|  * (at your option) any later version. | // it under the terms of the GNU General Public License as published by | ||||||
|  * | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  * This program is distributed in the hope that it will be useful, | // (at your option) any later version. | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | // | ||||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | // This program is distributed in the hope that it will be useful, | ||||||
|  * GNU General Public License for more details. | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  * | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  * You should have received a copy of the GNU General Public License | // GNU General Public License for more details. | ||||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | // | ||||||
|  */ | // 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 handlers | package handlers | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| 	"io" | 	"io" | ||||||
|  | 	"io/fs" | ||||||
| 	"os" | 	"os" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func NopReadDir(context.Context, string) ([]os.FileInfo, error) { | func NopReadDir(context.Context, string) ([]fs.DirEntry, error) { | ||||||
| 	return nil, os.ErrNotExist | 	return nil, os.ErrNotExist | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,20 +1,21 @@ | |||||||
| /* | // This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan. | ||||||
|  * ALR - Any Linux Repository | // It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors. | ||||||
|  * Copyright (C) 2024 Евгений Храмов | // | ||||||
|  * | // ALR - Any Linux Repository | ||||||
|  * This program is free software: you can redistribute it and/or modify | // Copyright (C) 2025 The ALR Authors | ||||||
|  * it under the terms of the GNU General Public License as published by | // | ||||||
|  * the Free Software Foundation, either version 3 of the License, or | // This program is free software: you can redistribute it and/or modify | ||||||
|  * (at your option) any later version. | // it under the terms of the GNU General Public License as published by | ||||||
|  * | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  * This program is distributed in the hope that it will be useful, | // (at your option) any later version. | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | // | ||||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | // This program is distributed in the hope that it will be useful, | ||||||
|  * GNU General Public License for more details. | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  * | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  * You should have received a copy of the GNU General Public License | // GNU General Public License for more details. | ||||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | // | ||||||
|  */ | // 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 handlers_test | package handlers_test | ||||||
|  |  | ||||||
| @@ -25,9 +26,10 @@ import ( | |||||||
| 	"strings" | 	"strings" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| 	"plemya-x.ru/alr/internal/shutils/handlers" |  | ||||||
| 	"mvdan.cc/sh/v3/interp" | 	"mvdan.cc/sh/v3/interp" | ||||||
| 	"mvdan.cc/sh/v3/syntax" | 	"mvdan.cc/sh/v3/syntax" | ||||||
|  |  | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/shutils/handlers" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func TestNopExec(t *testing.T) { | func TestNopExec(t *testing.T) { | ||||||
|   | |||||||
| @@ -1,20 +1,21 @@ | |||||||
| /* | // This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan. | ||||||
|  * ALR - Any Linux Repository | // It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors. | ||||||
|  * Copyright (C) 2024 Евгений Храмов | // | ||||||
|  * | // ALR - Any Linux Repository | ||||||
|  * This program is free software: you can redistribute it and/or modify | // Copyright (C) 2025 The ALR Authors | ||||||
|  * it under the terms of the GNU General Public License as published by | // | ||||||
|  * the Free Software Foundation, either version 3 of the License, or | // This program is free software: you can redistribute it and/or modify | ||||||
|  * (at your option) any later version. | // it under the terms of the GNU General Public License as published by | ||||||
|  * | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  * This program is distributed in the hope that it will be useful, | // (at your option) any later version. | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | // | ||||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | // This program is distributed in the hope that it will be useful, | ||||||
|  * GNU General Public License for more details. | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  * | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  * You should have received a copy of the GNU General Public License | // GNU General Public License for more details. | ||||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | // | ||||||
|  */ | // 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 handlers | package handlers | ||||||
|  |  | ||||||
| @@ -30,12 +31,12 @@ import ( | |||||||
| 	"mvdan.cc/sh/v3/interp" | 	"mvdan.cc/sh/v3/interp" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func RestrictedReadDir(allowedPrefixes ...string) interp.ReadDirHandlerFunc { | func RestrictedReadDir(allowedPrefixes ...string) interp.ReadDirHandlerFunc2 { | ||||||
| 	return func(ctx context.Context, s string) ([]fs.FileInfo, error) { | 	return func(ctx context.Context, s string) ([]fs.DirEntry, error) { | ||||||
| 		path := filepath.Clean(s) | 		path := filepath.Clean(s) | ||||||
| 		for _, allowedPrefix := range allowedPrefixes { | 		for _, allowedPrefix := range allowedPrefixes { | ||||||
| 			if strings.HasPrefix(path, allowedPrefix) { | 			if strings.HasPrefix(path, allowedPrefix) { | ||||||
| 				return interp.DefaultReadDirHandler()(ctx, s) | 				return interp.DefaultReadDirHandler2()(ctx, s) | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,20 +1,21 @@ | |||||||
| /* | // This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan. | ||||||
|  * ALR - Any Linux Repository | // It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors. | ||||||
|  * Copyright (C) 2024 Евгений Храмов | // | ||||||
|  * | // ALR - Any Linux Repository | ||||||
|  * This program is free software: you can redistribute it and/or modify | // Copyright (C) 2025 The ALR Authors | ||||||
|  * it under the terms of the GNU General Public License as published by | // | ||||||
|  * the Free Software Foundation, either version 3 of the License, or | // This program is free software: you can redistribute it and/or modify | ||||||
|  * (at your option) any later version. | // it under the terms of the GNU General Public License as published by | ||||||
|  * | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  * This program is distributed in the hope that it will be useful, | // (at your option) any later version. | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | // | ||||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | // This program is distributed in the hope that it will be useful, | ||||||
|  * GNU General Public License for more details. | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  * | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  * You should have received a copy of the GNU General Public License | // GNU General Public License for more details. | ||||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | // | ||||||
|  */ | // 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 helpers | package helpers | ||||||
|  |  | ||||||
| @@ -23,6 +24,7 @@ import ( | |||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"io" | 	"io" | ||||||
| 	"os" | 	"os" | ||||||
|  | 	"path" | ||||||
| 	"path/filepath" | 	"path/filepath" | ||||||
| 	"strconv" | 	"strconv" | ||||||
| 	"strings" | 	"strings" | ||||||
| @@ -31,8 +33,9 @@ import ( | |||||||
| 	"github.com/go-git/go-git/v5" | 	"github.com/go-git/go-git/v5" | ||||||
| 	"github.com/go-git/go-git/v5/plumbing/object" | 	"github.com/go-git/go-git/v5/plumbing/object" | ||||||
| 	"golang.org/x/exp/slices" | 	"golang.org/x/exp/slices" | ||||||
| 	"plemya-x.ru/alr/internal/shutils/handlers" |  | ||||||
| 	"mvdan.cc/sh/v3/interp" | 	"mvdan.cc/sh/v3/interp" | ||||||
|  |  | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/shutils/handlers" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| var ( | var ( | ||||||
| @@ -53,12 +56,17 @@ var Helpers = handlers.ExecFuncs{ | |||||||
| 	"install-completion":   installCompletionCmd, | 	"install-completion":   installCompletionCmd, | ||||||
| 	"install-library":      installLibraryCmd, | 	"install-library":      installLibraryCmd, | ||||||
| 	"git-version":          gitVersionCmd, | 	"git-version":          gitVersionCmd, | ||||||
|  |  | ||||||
|  | 	"files-find-lang": filesFindLangCmd, | ||||||
|  | 	"files-find-doc":  filesFindDocCmd, | ||||||
| } | } | ||||||
|  |  | ||||||
| // Restricted contains restricted read-only helper commands | // Restricted contains restricted read-only helper commands | ||||||
| // that don't modify any state | // that don't modify any state | ||||||
| var Restricted = handlers.ExecFuncs{ | var Restricted = handlers.ExecFuncs{ | ||||||
| 	"git-version": gitVersionCmd, | 	"git-version":     gitVersionCmd, | ||||||
|  | 	"files-find-lang": filesFindLangCmd, | ||||||
|  | 	"files-find-doc":  filesFindDocCmd, | ||||||
| } | } | ||||||
|  |  | ||||||
| func installHelperCmd(prefix string, perms os.FileMode) handlers.ExecFunc { | func installHelperCmd(prefix string, perms os.FileMode) handlers.ExecFunc { | ||||||
| @@ -237,10 +245,13 @@ func gitVersionCmd(hc interp.HandlerContext, cmd string, args []string) error { | |||||||
| 		return fmt.Errorf("git-version: %w", err) | 		return fmt.Errorf("git-version: %w", err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	commits.ForEach(func(*object.Commit) error { | 	err = commits.ForEach(func(*object.Commit) error { | ||||||
| 		revNum++ | 		revNum++ | ||||||
| 		return nil | 		return nil | ||||||
| 	}) | 	}) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return fmt.Errorf("git-version: %w", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	HEAD, err := r.Head() | 	HEAD, err := r.Head() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @@ -254,6 +265,114 @@ func gitVersionCmd(hc interp.HandlerContext, cmd string, args []string) error { | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func filesFindLangCmd(hc interp.HandlerContext, cmd string, args []string) error { | ||||||
|  | 	namePattern := "*.mo" | ||||||
|  | 	if len(args) > 0 { | ||||||
|  | 		namePattern = args[0] + ".mo" | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	localePath := "./usr/share/locale/" | ||||||
|  | 	realPath := path.Join(hc.Dir, localePath) | ||||||
|  |  | ||||||
|  | 	info, err := os.Stat(realPath) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return fmt.Errorf("files-find-lang: %w", err) | ||||||
|  | 	} | ||||||
|  | 	if !info.IsDir() { | ||||||
|  | 		return fmt.Errorf("files-find-lang: %s is not a directory", localePath) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var langFiles []string | ||||||
|  | 	err = filepath.Walk(realPath, func(p string, info os.FileInfo, err error) error { | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if !info.IsDir() && matchNamePattern(info.Name(), namePattern) { | ||||||
|  | 			relPath, relErr := filepath.Rel(hc.Dir, p) | ||||||
|  | 			if relErr != nil { | ||||||
|  | 				return relErr | ||||||
|  | 			} | ||||||
|  | 			langFiles = append(langFiles, "./"+relPath) | ||||||
|  | 		} | ||||||
|  | 		return nil | ||||||
|  | 	}) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return fmt.Errorf("files-find-lang: %w", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, file := range langFiles { | ||||||
|  | 		fmt.Fprintln(hc.Stdout, file) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func filesFindDocCmd(hc interp.HandlerContext, cmd string, args []string) error { | ||||||
|  | 	namePattern := "*" | ||||||
|  | 	if len(args) > 0 { | ||||||
|  | 		namePattern = args[0] | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	docPath := "./usr/share/doc/" | ||||||
|  | 	docRealPath := path.Join(hc.Dir, docPath) | ||||||
|  |  | ||||||
|  | 	info, err := os.Stat(docRealPath) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return fmt.Errorf("files-find-doc: %w", err) | ||||||
|  | 	} | ||||||
|  | 	if !info.IsDir() { | ||||||
|  | 		return fmt.Errorf("files-find-doc: %s is not a directory", docPath) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var docFiles []string | ||||||
|  |  | ||||||
|  | 	entries, err := os.ReadDir(docRealPath) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	for _, entry := range entries { | ||||||
|  | 		if matchNamePattern(entry.Name(), namePattern) { | ||||||
|  | 			targetPath := filepath.Join(docRealPath, entry.Name()) | ||||||
|  | 			targetInfo, err := os.Stat(targetPath) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  | 			if targetInfo.IsDir() { | ||||||
|  | 				err := filepath.Walk(targetPath, func(subPath string, subInfo os.FileInfo, subErr error) error { | ||||||
|  | 					relPath, err := filepath.Rel(hc.Dir, subPath) | ||||||
|  | 					if err != nil { | ||||||
|  | 						return err | ||||||
|  | 					} | ||||||
|  | 					docFiles = append(docFiles, "./"+relPath) | ||||||
|  | 					return nil | ||||||
|  | 				}) | ||||||
|  | 				if err != nil { | ||||||
|  | 					return err | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		return fmt.Errorf("files-find-doc: %w", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, file := range docFiles { | ||||||
|  | 		fmt.Fprintln(hc.Stdout, file) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func matchNamePattern(name, pattern string) bool { | ||||||
|  | 	matched, err := filepath.Match(pattern, name) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 	return matched | ||||||
|  | } | ||||||
|  |  | ||||||
| func helperInstall(from, to string, perms os.FileMode) error { | func helperInstall(from, to string, perms os.FileMode) error { | ||||||
| 	err := os.MkdirAll(filepath.Dir(to), 0o755) | 	err := os.MkdirAll(filepath.Dir(to), 0o755) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
|   | |||||||
							
								
								
									
										216
									
								
								internal/shutils/helpers/helpers_internal_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										216
									
								
								internal/shutils/helpers/helpers_internal_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,216 @@ | |||||||
|  | // 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 helpers | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"context" | ||||||
|  | 	"os" | ||||||
|  | 	"path/filepath" | ||||||
|  | 	"strings" | ||||||
|  | 	"testing" | ||||||
|  |  | ||||||
|  | 	"github.com/stretchr/testify/assert" | ||||||
|  | 	"mvdan.cc/sh/v3/interp" | ||||||
|  | 	"mvdan.cc/sh/v3/syntax" | ||||||
|  |  | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/shutils/handlers" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type testCase struct { | ||||||
|  | 	name           string | ||||||
|  | 	dirsToCreate   []string | ||||||
|  | 	filesToCreate  []string | ||||||
|  | 	expectedOutput []string | ||||||
|  | 	args           string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestFindFilesDoc(t *testing.T) { | ||||||
|  | 	tests := []testCase{ | ||||||
|  | 		{ | ||||||
|  | 			name: "All dirs", | ||||||
|  | 			dirsToCreate: []string{ | ||||||
|  | 				"usr/share/doc/yandex-browser-stable/subdir", | ||||||
|  | 				"usr/share/doc/firefox", | ||||||
|  | 			}, | ||||||
|  | 			filesToCreate: []string{ | ||||||
|  | 				"usr/share/doc/yandex-browser-stable/README.md", | ||||||
|  | 				"usr/share/doc/yandex-browser-stable/subdir/nested-file.txt", | ||||||
|  | 				"usr/share/doc/firefox/README.md", | ||||||
|  | 			}, | ||||||
|  | 			expectedOutput: []string{ | ||||||
|  | 				"./usr/share/doc/yandex-browser-stable", | ||||||
|  | 				"./usr/share/doc/yandex-browser-stable/README.md", | ||||||
|  | 				"./usr/share/doc/yandex-browser-stable/subdir", | ||||||
|  | 				"./usr/share/doc/yandex-browser-stable/subdir/nested-file.txt", | ||||||
|  | 				"./usr/share/doc/firefox", | ||||||
|  | 				"./usr/share/doc/firefox/README.md", | ||||||
|  | 			}, | ||||||
|  | 			args: "", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "Only selected dir", | ||||||
|  | 			dirsToCreate: []string{ | ||||||
|  | 				"usr/share/doc/yandex-browser-stable/subdir", | ||||||
|  | 				"usr/share/doc/firefox", | ||||||
|  | 				"usr/share/doc/foo/yandex-browser-stable", | ||||||
|  | 			}, | ||||||
|  | 			filesToCreate: []string{ | ||||||
|  | 				"usr/share/doc/yandex-browser-stable/README.md", | ||||||
|  | 				"usr/share/doc/yandex-browser-stable/subdir/nested-file.txt", | ||||||
|  | 				"usr/share/doc/firefox/README.md", | ||||||
|  | 				"usr/share/doc/firefox/yandex-browser-stable", | ||||||
|  | 				"usr/share/doc/foo/yandex-browser-stable/README.md", | ||||||
|  | 			}, | ||||||
|  | 			expectedOutput: []string{ | ||||||
|  | 				"./usr/share/doc/yandex-browser-stable", | ||||||
|  | 				"./usr/share/doc/yandex-browser-stable/README.md", | ||||||
|  | 				"./usr/share/doc/yandex-browser-stable/subdir", | ||||||
|  | 				"./usr/share/doc/yandex-browser-stable/subdir/nested-file.txt", | ||||||
|  | 			}, | ||||||
|  | 			args: "yandex-browser-stable", | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, tc := range tests { | ||||||
|  | 		t.Run(tc.name, func(t *testing.T) { | ||||||
|  | 			tempDir, err := os.MkdirTemp("", "test-files-find-doc") | ||||||
|  | 			assert.NoError(t, err) | ||||||
|  | 			defer os.RemoveAll(tempDir) | ||||||
|  |  | ||||||
|  | 			for _, dir := range tc.dirsToCreate { | ||||||
|  | 				dirPath := filepath.Join(tempDir, dir) | ||||||
|  | 				err := os.MkdirAll(dirPath, 0o755) | ||||||
|  | 				assert.NoError(t, err) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			for _, file := range tc.filesToCreate { | ||||||
|  | 				filePath := filepath.Join(tempDir, file) | ||||||
|  | 				err := os.WriteFile(filePath, []byte("test content"), 0o644) | ||||||
|  | 				assert.NoError(t, err) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			helpers := handlers.ExecFuncs{ | ||||||
|  | 				"files-find-doc": filesFindDocCmd, | ||||||
|  | 			} | ||||||
|  | 			buf := &bytes.Buffer{} | ||||||
|  | 			runner, err := interp.New( | ||||||
|  | 				interp.Dir(tempDir), | ||||||
|  | 				interp.StdIO(os.Stdin, buf, os.Stderr), | ||||||
|  | 				interp.ExecHandler(helpers.ExecHandler(interp.DefaultExecHandler(1000))), | ||||||
|  | 			) | ||||||
|  | 			assert.NoError(t, err) | ||||||
|  |  | ||||||
|  | 			scriptContent := ` | ||||||
|  | shopt -s globstar | ||||||
|  | files-find-doc ` + tc.args | ||||||
|  |  | ||||||
|  | 			script, err := syntax.NewParser().Parse(strings.NewReader(scriptContent), "") | ||||||
|  | 			assert.NoError(t, err) | ||||||
|  |  | ||||||
|  | 			err = runner.Run(context.Background(), script) | ||||||
|  | 			assert.NoError(t, err) | ||||||
|  |  | ||||||
|  | 			contents := strings.Fields(strings.TrimSpace(buf.String())) | ||||||
|  | 			assert.ElementsMatch(t, tc.expectedOutput, contents) | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestFindLang(t *testing.T) { | ||||||
|  | 	tests := []testCase{ | ||||||
|  | 		{ | ||||||
|  | 			name: "All dirs", | ||||||
|  | 			dirsToCreate: []string{ | ||||||
|  | 				"usr/share/locale/ru/LC_MESSAGES", | ||||||
|  | 				"usr/share/locale/tr/LC_MESSAGES", | ||||||
|  | 			}, | ||||||
|  | 			filesToCreate: []string{ | ||||||
|  | 				"usr/share/locale/ru/LC_MESSAGES/yandex-disk.mo", | ||||||
|  | 				"usr/share/locale/ru/LC_MESSAGES/yandex-disk-indicator.mo", | ||||||
|  | 				"usr/share/locale/tr/LC_MESSAGES/yandex-disk.mo", | ||||||
|  | 			}, | ||||||
|  | 			expectedOutput: []string{ | ||||||
|  | 				"./usr/share/locale/ru/LC_MESSAGES/yandex-disk.mo", | ||||||
|  | 				"./usr/share/locale/ru/LC_MESSAGES/yandex-disk-indicator.mo", | ||||||
|  | 				"./usr/share/locale/tr/LC_MESSAGES/yandex-disk.mo", | ||||||
|  | 			}, | ||||||
|  | 			args: "", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "All dirs", | ||||||
|  | 			dirsToCreate: []string{ | ||||||
|  | 				"usr/share/locale/ru/LC_MESSAGES", | ||||||
|  | 				"usr/share/locale/tr/LC_MESSAGES", | ||||||
|  | 			}, | ||||||
|  | 			filesToCreate: []string{ | ||||||
|  | 				"usr/share/locale/ru/LC_MESSAGES/yandex-disk.mo", | ||||||
|  | 				"usr/share/locale/ru/LC_MESSAGES/yandex-disk-indicator.mo", | ||||||
|  | 				"usr/share/locale/tr/LC_MESSAGES/yandex-disk.mo", | ||||||
|  | 			}, | ||||||
|  | 			expectedOutput: []string{ | ||||||
|  | 				"./usr/share/locale/ru/LC_MESSAGES/yandex-disk.mo", | ||||||
|  | 				"./usr/share/locale/tr/LC_MESSAGES/yandex-disk.mo", | ||||||
|  | 			}, | ||||||
|  | 			args: "yandex-disk", | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, tc := range tests { | ||||||
|  | 		t.Run(tc.name, func(t *testing.T) { | ||||||
|  | 			tempDir, err := os.MkdirTemp("", "test-files-find-lang") | ||||||
|  | 			assert.NoError(t, err) | ||||||
|  | 			defer os.RemoveAll(tempDir) | ||||||
|  |  | ||||||
|  | 			for _, dir := range tc.dirsToCreate { | ||||||
|  | 				dirPath := filepath.Join(tempDir, dir) | ||||||
|  | 				err := os.MkdirAll(dirPath, 0o755) | ||||||
|  | 				assert.NoError(t, err) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			for _, file := range tc.filesToCreate { | ||||||
|  | 				filePath := filepath.Join(tempDir, file) | ||||||
|  | 				err := os.WriteFile(filePath, []byte("test content"), 0o644) | ||||||
|  | 				assert.NoError(t, err) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			helpers := handlers.ExecFuncs{ | ||||||
|  | 				"files-find-lang": filesFindLangCmd, | ||||||
|  | 			} | ||||||
|  | 			buf := &bytes.Buffer{} | ||||||
|  | 			runner, err := interp.New( | ||||||
|  | 				interp.Dir(tempDir), | ||||||
|  | 				interp.StdIO(os.Stdin, buf, os.Stderr), | ||||||
|  | 				interp.ExecHandler(helpers.ExecHandler(interp.DefaultExecHandler(1000))), | ||||||
|  | 			) | ||||||
|  | 			assert.NoError(t, err) | ||||||
|  |  | ||||||
|  | 			scriptContent := ` | ||||||
|  | shopt -s globstar | ||||||
|  | files-find-lang ` + tc.args | ||||||
|  |  | ||||||
|  | 			script, err := syntax.NewParser().Parse(strings.NewReader(scriptContent), "") | ||||||
|  | 			assert.NoError(t, err) | ||||||
|  |  | ||||||
|  | 			err = runner.Run(context.Background(), script) | ||||||
|  | 			assert.NoError(t, err) | ||||||
|  |  | ||||||
|  | 			contents := strings.Fields(strings.TrimSpace(buf.String())) | ||||||
|  | 			assert.ElementsMatch(t, tc.expectedOutput, contents) | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										550
									
								
								internal/translations/default.pot
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										550
									
								
								internal/translations/default.pot
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,550 @@ | |||||||
|  | msgid "" | ||||||
|  | msgstr "" | ||||||
|  | "Project-Id-Version: \n" | ||||||
|  | "Last-Translator: Automatically generated\n" | ||||||
|  | "Language-Team: none\n" | ||||||
|  | "Language: en\n" | ||||||
|  | "MIME-Version: 1.0\n" | ||||||
|  | "Content-Type: text/plain; charset=UTF-8\n" | ||||||
|  | "Content-Transfer-Encoding: 8bit\n" | ||||||
|  | "Plural-Forms: nplurals=2; plural=(n != 1);\n" | ||||||
|  |  | ||||||
|  | #: build.go:42 | ||||||
|  | msgid "Build a local package" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: build.go:48 | ||||||
|  | msgid "Path to the build script" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: build.go:53 | ||||||
|  | msgid "Specify subpackage in script (for multi package script only)" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: build.go:58 | ||||||
|  | msgid "Name of the package to build and its repo (example: default/go-bin)" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: build.go:63 | ||||||
|  | msgid "" | ||||||
|  | "Build package from scratch even if there's an already built package available" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: build.go:73 | ||||||
|  | msgid "Error getting working directory" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: build.go:118 | ||||||
|  | msgid "Cannot get absolute script path" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: build.go:152 | ||||||
|  | msgid "Package not found" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: build.go:165 | ||||||
|  | msgid "Nothing to build" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: build.go:222 | ||||||
|  | msgid "Error building package" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: build.go:229 | ||||||
|  | msgid "Error moving the package" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: build.go:233 | ||||||
|  | msgid "Done" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: fix.go:38 | ||||||
|  | msgid "Attempt to fix problems with ALR" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: fix.go:59 | ||||||
|  | msgid "Clearing cache directory" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: fix.go:64 | ||||||
|  | msgid "Unable to open cache directory" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: fix.go:70 | ||||||
|  | msgid "Unable to read cache directory contents" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: fix.go:76 | ||||||
|  | msgid "Unable to remove cache item (%s)" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: fix.go:80 | ||||||
|  | msgid "Rebuilding cache" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: fix.go:84 | ||||||
|  | msgid "Unable to create new cache directory" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: gen.go:34 | ||||||
|  | msgid "Generate a ALR script from a template" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: gen.go:39 | ||||||
|  | msgid "Generate a ALR script for a pip module" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: helper.go:42 | ||||||
|  | msgid "List all the available helper commands" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: helper.go:54 | ||||||
|  | msgid "Run a ALR helper command" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: helper.go:61 | ||||||
|  | msgid "The directory that the install commands will install to" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: helper.go:74 helper.go:75 | ||||||
|  | msgid "No such helper command" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: helper.go:85 | ||||||
|  | msgid "Error parsing os-release file" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: info.go:42 | ||||||
|  | msgid "Print information about a package" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: info.go:47 | ||||||
|  | msgid "Show all information, not just for the current distro" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: info.go:68 | ||||||
|  | msgid "Error getting packages" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: info.go:76 | ||||||
|  | msgid "Error iterating over packages" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: info.go:90 | ||||||
|  | msgid "Command info expected at least 1 argument, got %d" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: info.go:110 | ||||||
|  | msgid "Error finding packages" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: info.go:124 | ||||||
|  | msgid "Can't detect system language" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: info.go:141 | ||||||
|  | msgid "Error resolving overrides" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: info.go:149 info.go:154 | ||||||
|  | msgid "Error encoding script variables" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: install.go:40 | ||||||
|  | msgid "Install a new package" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: install.go:52 | ||||||
|  | msgid "Command install expected at least 1 argument, got %d" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: install.go:114 | ||||||
|  | msgid "Error when installing the package" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: install.go:159 | ||||||
|  | msgid "Remove an installed package" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: install.go:178 | ||||||
|  | msgid "Error listing installed packages" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: install.go:215 | ||||||
|  | msgid "Command remove expected at least 1 argument, got %d" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: install.go:230 | ||||||
|  | msgid "Error removing packages" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/app_builder/builder.go:75 | ||||||
|  | msgid "Error loading config" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/app_builder/builder.go:96 | ||||||
|  | msgid "Error initialization database" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/app_builder/builder.go:135 | ||||||
|  | msgid "Error pulling repositories" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/app_builder/builder.go:152 | ||||||
|  | msgid "Error parsing os release" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/app_builder/builder.go:165 | ||||||
|  | msgid "Unable to detect a supported package manager on the system" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/prompt.go:60 | ||||||
|  | msgid "Would you like to view the build script for %s" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/prompt.go:71 | ||||||
|  | msgid "Would you still like to continue?" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/prompt.go:77 | ||||||
|  | msgid "User chose not to continue after reading script" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/prompt.go:111 | ||||||
|  | msgid "Error prompting for choice of package" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/prompt.go:135 | ||||||
|  | msgid "Choose which package to %s" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/prompt.go:156 | ||||||
|  | msgid "Choose which optional package(s) to install" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/template.go:74 internal/cliutils/template.go:93 | ||||||
|  | msgid "NAME" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/template.go:74 internal/cliutils/template.go:94 | ||||||
|  | msgid "USAGE" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/template.go:74 | ||||||
|  | msgid "global options" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/template.go:74 | ||||||
|  | msgid "command" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/template.go:74 internal/cliutils/template.go:95 | ||||||
|  | msgid "command options" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/template.go:74 internal/cliutils/template.go:96 | ||||||
|  | msgid "arguments" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/template.go:74 | ||||||
|  | msgid "VERSION" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/template.go:74 internal/cliutils/template.go:98 | ||||||
|  | msgid "DESCRIPTION" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/template.go:74 | ||||||
|  | msgid "AUTHOR" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/template.go:74 | ||||||
|  | msgid "COMMANDS" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/template.go:74 | ||||||
|  | msgid "GLOBAL OPTIONS" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/template.go:74 | ||||||
|  | msgid "COPYRIGHT" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/template.go:97 | ||||||
|  | msgid "CATEGORY" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/template.go:99 internal/cliutils/template.go:100 | ||||||
|  | msgid "OPTIONS" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/utils.go:69 | ||||||
|  | msgid "" | ||||||
|  | "This command is deprecated and would be removed in the future, use \"%s\" " | ||||||
|  | "instead!" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: internal/db/db.go:137 | ||||||
|  | msgid "Database version mismatch; resetting" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: internal/db/db.go:144 | ||||||
|  | msgid "" | ||||||
|  | "Database version does not exist. Run alr fix if something isn't working." | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: internal/dl/dl.go:170 | ||||||
|  | msgid "Source can be updated, updating if required" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: internal/dl/dl.go:201 | ||||||
|  | msgid "Source found in cache and linked to destination" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: internal/dl/dl.go:208 | ||||||
|  | msgid "Source updated and linked to destination" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: internal/dl/dl.go:222 | ||||||
|  | msgid "Downloading source" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: internal/dl/progress_tui.go:100 | ||||||
|  | msgid "%s: done!\n" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: internal/dl/progress_tui.go:104 | ||||||
|  | msgid "%s %s downloading at %s/s\n" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: internal/logger/log.go:41 | ||||||
|  | msgid "ERROR" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: internal/utils/cmd.go:97 | ||||||
|  | msgid "Error on dropping capabilities" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: internal/utils/cmd.go:164 | ||||||
|  | msgid "You need to be a %s member to perform this action" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: internal/utils/cmd.go:200 | ||||||
|  | msgid "You need to be root to perform this action" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: list.go:43 | ||||||
|  | msgid "List ALR repo packages" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: list.go:57 | ||||||
|  | msgid "Format output using a Go template" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: list.go:89 | ||||||
|  | msgid "Error getting packages for upgrade" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: list.go:92 | ||||||
|  | msgid "No packages for upgrade" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: list.go:102 list.go:187 | ||||||
|  | msgid "Error parsing format template" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: list.go:108 list.go:191 | ||||||
|  | msgid "Error executing template" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: main.go:45 | ||||||
|  | msgid "Print the current ALR version and exit" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: main.go:61 | ||||||
|  | msgid "Arguments to be passed on to the package manager" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: main.go:67 | ||||||
|  | msgid "Enable interactive questions and prompts" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: main.go:146 | ||||||
|  | msgid "Show help" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: main.go:150 | ||||||
|  | msgid "Error while running app" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: pkg/build/build.go:395 | ||||||
|  | msgid "Building package" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: pkg/build/build.go:424 | ||||||
|  | msgid "The checksums array must be the same length as sources" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: pkg/build/build.go:455 | ||||||
|  | msgid "Downloading sources" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: pkg/build/build.go:549 | ||||||
|  | msgid "Installing dependencies" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: pkg/build/checker.go:43 | ||||||
|  | msgid "" | ||||||
|  | "Your system's CPU architecture doesn't match this package. Do you want to " | ||||||
|  | "build anyway?" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: pkg/build/checker.go:67 | ||||||
|  | msgid "This package is already installed" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: pkg/build/find_deps/alt_linux.go:35 | ||||||
|  | msgid "Command not found on the system" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: pkg/build/find_deps/alt_linux.go:86 | ||||||
|  | msgid "Provided dependency found" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: pkg/build/find_deps/alt_linux.go:93 | ||||||
|  | msgid "Required dependency found" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: pkg/build/find_deps/empty.go:32 | ||||||
|  | msgid "AutoProv is not implemented for this package format, so it's skipped" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: pkg/build/find_deps/empty.go:37 | ||||||
|  | msgid "AutoReq is not implemented for this package format, so it's skipped" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: pkg/build/script_executor.go:241 | ||||||
|  | msgid "Building package metadata" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: pkg/build/script_executor.go:372 | ||||||
|  | msgid "Executing prepare()" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: pkg/build/script_executor.go:381 | ||||||
|  | msgid "Executing build()" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: pkg/build/script_executor.go:410 pkg/build/script_executor.go:430 | ||||||
|  | msgid "Executing %s()" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: pkg/repos/pull.go:77 | ||||||
|  | msgid "Pulling repository" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: pkg/repos/pull.go:113 | ||||||
|  | msgid "Repository up to date" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: pkg/repos/pull.go:204 | ||||||
|  | msgid "Git repository does not appear to be a valid ALR repo" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: pkg/repos/pull.go:220 | ||||||
|  | msgid "" | ||||||
|  | "ALR repo's minimum ALR version is greater than the current version. Try " | ||||||
|  | "updating ALR if something doesn't work." | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: refresh.go:30 | ||||||
|  | msgid "Pull all repositories that have changed" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: repo.go:39 | ||||||
|  | msgid "Manage repos" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: repo.go:50 repo.go:220 | ||||||
|  | msgid "Remove an existing repository" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: repo.go:52 | ||||||
|  | msgid "<name>" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: repo.go:82 | ||||||
|  | msgid "Repo \"%s\" does not exist" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: repo.go:89 | ||||||
|  | msgid "Error removing repo directory" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: repo.go:93 repo.go:160 | ||||||
|  | msgid "Error saving config" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: repo.go:112 | ||||||
|  | msgid "Error removing packages from database" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: repo.go:123 repo.go:190 | ||||||
|  | msgid "Add a new repository" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: repo.go:124 | ||||||
|  | msgid "<name> <url>" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: repo.go:149 | ||||||
|  | msgid "Repo \"%s\" already exists" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: repo.go:197 | ||||||
|  | msgid "Name of the new repo" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: repo.go:203 | ||||||
|  | msgid "URL of the new repo" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: repo.go:227 | ||||||
|  | msgid "Name of the repo to be deleted" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: search.go:40 | ||||||
|  | msgid "Search packages" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: search.go:51 | ||||||
|  | msgid "Search by name" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: search.go:56 | ||||||
|  | msgid "Search by description" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: search.go:61 | ||||||
|  | msgid "Search by repository" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: search.go:66 | ||||||
|  | msgid "Search by provides" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: search.go:130 | ||||||
|  | msgid "Error while executing search" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: upgrade.go:47 | ||||||
|  | msgid "Upgrade all installed packages" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: upgrade.go:105 upgrade.go:122 | ||||||
|  | msgid "Error checking for updates" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: upgrade.go:125 | ||||||
|  | msgid "There is nothing to do." | ||||||
|  | msgstr "" | ||||||
| @@ -1,155 +0,0 @@ | |||||||
| [[translation]] |  | ||||||
| id = 1228660974 |  | ||||||
| value = 'Pulling repository' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 2779805870 |  | ||||||
| value = 'Repository up to date' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 1433222829 |  | ||||||
| value = 'Would you like to view the build script for' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 2470847050 |  | ||||||
| value = 'Failed to prompt user to view build script' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 855659503 |  | ||||||
| value = 'Would you still like to continue?' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 1997041569 |  | ||||||
| value = 'User chose not to continue after reading script' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 2347700990 |  | ||||||
| value = 'Building package' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 2105058868 |  | ||||||
| value = 'Downloading sources' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 1884485082 |  | ||||||
| value = 'Downloading source' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 1519177982 |  | ||||||
| value = 'Error building package' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 2125220917 |  | ||||||
| value = 'Choose which package(s) to install' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 812531604 |  | ||||||
| value = 'Error prompting for choice of package' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 1040982801 |  | ||||||
| value = 'Updating version' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 1014897988 |  | ||||||
| value = 'Remove build dependencies?' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 2205430948 |  | ||||||
| value = 'Installing build dependencies' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 2522710805 |  | ||||||
| value = 'Installing dependencies' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 3602138206 |  | ||||||
| value = 'Error installing package' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 2235794125 |  | ||||||
| value = 'Would you like to remove build dependencies?' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 2562049386 |  | ||||||
| value = "Your system's CPU architecture doesn't match this package. Do you want to build anyway?" |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 4006393493 |  | ||||||
| value = 'The checksums array must be the same length as sources' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 3759891273 |  | ||||||
| value = 'The package() function is required' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 1057080231 |  | ||||||
| value = 'Executing package()' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 2687735200 |  | ||||||
| value = 'Executing prepare()' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 535572372 |  | ||||||
| value = 'Executing version()' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 436644691 |  | ||||||
| value = 'Executing build()' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 1393316459 |  | ||||||
| value = 'This package is already installed' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 1267660189 |  | ||||||
| value = 'Source can be updated, updating if required' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 21753247 |  | ||||||
| value = 'Source found in cache, linked to destination' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 257354570 |  | ||||||
| value = 'Compressing package' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 2952487371 |  | ||||||
| value = 'Building package metadata' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 3121791194 |  | ||||||
| value = 'Running ALR as root is forbidden as it may cause catastrophic damage to your system' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 1256604213 |  | ||||||
| value = 'Waiting for torrent metadata' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 432261354 |  | ||||||
| value = 'Downloading torrent file' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 1579384326 |  | ||||||
| value = 'name' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 3206337475 |  | ||||||
| value = 'version' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 1810056261 |  | ||||||
| value = 'new' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 1602912115 |  | ||||||
| value = 'source' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 2363381545 |  | ||||||
| value = 'type' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 3419504365 |  | ||||||
| value = 'downloader' |  | ||||||
| @@ -1,151 +0,0 @@ | |||||||
| [[translation]] |  | ||||||
| id = 1228660974 |  | ||||||
| value = 'Скачивание репозитория' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 2779805870 |  | ||||||
| value = 'Репозиторий уже обновлен' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 1433222829 |  | ||||||
| value = 'Показать скрипт для пакета' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 2470847050 |  | ||||||
| value = 'Не удалось предложить просмотреть скрипт' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 855659503 |  | ||||||
| value = 'Продолжить?' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 1997041569 |  | ||||||
| value = 'Пользователь решил не продолжать после просмотра скрипта' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 2347700990 |  | ||||||
| value = 'Сборка пакета' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 2105058868 |  | ||||||
| value = 'Скачивание файлов' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 1884485082 |  | ||||||
| value = 'Скачивание источника' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 1519177982 |  | ||||||
| value = 'Ошибка при сборке пакета' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 2125220917 |  | ||||||
| value = 'Выберите, какие пакеты установить' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 812531604 |  | ||||||
| value = 'Ошибка при запросе выбора пакета' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 1040982801 |  | ||||||
| value = 'Обновление версии' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 2235794125 |  | ||||||
| value = 'Удалить зависимости сборки?' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 2205430948 |  | ||||||
| value = 'Установка зависимостей сборки' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 2522710805 |  | ||||||
| value = 'Установка зависимостей' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 3602138206 |  | ||||||
| value = 'Ошибка при установке пакета' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 1057080231 |  | ||||||
| value = 'Вызов функции package()' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 2687735200 |  | ||||||
| value = 'Вызов функции prepare()' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 535572372 |  | ||||||
| value = 'Вызов функции version()' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 436644691 |  | ||||||
| value = 'Вызов функции build()' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 2562049386 |  | ||||||
| value = "Архитектура процессора вашей системы не соответствует этому пакету. Продолжать несмотря на это?" |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 3759891273 |  | ||||||
| value = 'Функция package() необходима' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 4006393493 |  | ||||||
| value = 'Массив checksums должен быть той же длины, что и sources' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 1393316459 |  | ||||||
| value = 'Этот пакет уже установлен' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 1267660189 |  | ||||||
| value = 'Источник может быть обновлен, если требуется, обновляем' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 21753247 |  | ||||||
| value = 'Источник найден в кэше' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 257354570 |  | ||||||
| value = 'Сжатие пакета' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 2952487371 |  | ||||||
| value = 'Создание метаданных пакета' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 3121791194 |  | ||||||
| value = 'Запуск ALR от имени root запрещен, так как это может привести к катастрофическому повреждению вашей системы' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 1256604213 |  | ||||||
| value = 'Ожидание метаданных торрента' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 432261354 |  | ||||||
| value = 'Скачивание торрент-файла' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 1579384326 |  | ||||||
| value = 'название' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 3206337475 |  | ||||||
| value = 'версия' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 1810056261 |  | ||||||
| value = 'новая' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 1602912115 |  | ||||||
| value = 'источник' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 2363381545 |  | ||||||
| value = 'вид' |  | ||||||
|  |  | ||||||
| [[translation]] |  | ||||||
| id = 3419504365 |  | ||||||
| value = 'протокол-скачивание' |  | ||||||
							
								
								
									
										641
									
								
								internal/translations/po/ru/default.po
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										641
									
								
								internal/translations/po/ru/default.po
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,641 @@ | |||||||
|  | # | ||||||
|  | # x1z53 <x1z53@yandex.ru>, 2025. | ||||||
|  | # Maxim Slipenko <maks1ms@alt-gnome.ru>, 2025. | ||||||
|  | # | ||||||
|  | msgid "" | ||||||
|  | msgstr "" | ||||||
|  | "Project-Id-Version: unnamed project\n" | ||||||
|  | "PO-Revision-Date: 2025-05-13 23:24+0300\n" | ||||||
|  | "Last-Translator: Maxim Slipenko <maks1ms@alt-gnome.ru>\n" | ||||||
|  | "Language-Team: Russian\n" | ||||||
|  | "Language: ru\n" | ||||||
|  | "MIME-Version: 1.0\n" | ||||||
|  | "Content-Type: text/plain; charset=UTF-8\n" | ||||||
|  | "Content-Transfer-Encoding: 8bit\n" | ||||||
|  | "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" | ||||||
|  | "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" | ||||||
|  | "X-Generator: Gtranslator 48.0\n" | ||||||
|  |  | ||||||
|  | #: build.go:42 | ||||||
|  | msgid "Build a local package" | ||||||
|  | msgstr "Сборка локального пакета" | ||||||
|  |  | ||||||
|  | #: build.go:48 | ||||||
|  | msgid "Path to the build script" | ||||||
|  | msgstr "Путь к скрипту сборки" | ||||||
|  |  | ||||||
|  | #: build.go:53 | ||||||
|  | msgid "Specify subpackage in script (for multi package script only)" | ||||||
|  | msgstr "Укажите подпакет в скрипте (только для многопакетного скрипта)" | ||||||
|  |  | ||||||
|  | #: build.go:58 | ||||||
|  | msgid "Name of the package to build and its repo (example: default/go-bin)" | ||||||
|  | msgstr "Имя пакета для сборки и его репозиторий (пример: default/go-bin)" | ||||||
|  |  | ||||||
|  | #: build.go:63 | ||||||
|  | msgid "" | ||||||
|  | "Build package from scratch even if there's an already built package available" | ||||||
|  | msgstr "Создайте пакет с нуля, даже если уже имеется готовый пакет" | ||||||
|  |  | ||||||
|  | #: build.go:73 | ||||||
|  | msgid "Error getting working directory" | ||||||
|  | msgstr "Ошибка при получении рабочего каталога" | ||||||
|  |  | ||||||
|  | #: build.go:118 | ||||||
|  | msgid "Cannot get absolute script path" | ||||||
|  | msgstr "Невозможно получить абсолютный путь к скрипту" | ||||||
|  |  | ||||||
|  | #: build.go:152 | ||||||
|  | msgid "Package not found" | ||||||
|  | msgstr "Пакет не найден" | ||||||
|  |  | ||||||
|  | #: build.go:165 | ||||||
|  | msgid "Nothing to build" | ||||||
|  | msgstr "Нечего собирать" | ||||||
|  |  | ||||||
|  | #: build.go:222 | ||||||
|  | msgid "Error building package" | ||||||
|  | msgstr "Ошибка при сборке пакета" | ||||||
|  |  | ||||||
|  | #: build.go:229 | ||||||
|  | msgid "Error moving the package" | ||||||
|  | msgstr "Ошибка при перемещении пакета" | ||||||
|  |  | ||||||
|  | #: build.go:233 | ||||||
|  | msgid "Done" | ||||||
|  | msgstr "Сделано" | ||||||
|  |  | ||||||
|  | #: fix.go:38 | ||||||
|  | msgid "Attempt to fix problems with ALR" | ||||||
|  | msgstr "Попытка устранить проблемы с ALR" | ||||||
|  |  | ||||||
|  | #: fix.go:59 | ||||||
|  | msgid "Clearing cache directory" | ||||||
|  | msgstr "Очистка каталога кэша" | ||||||
|  |  | ||||||
|  | #: fix.go:64 | ||||||
|  | msgid "Unable to open cache directory" | ||||||
|  | msgstr "Невозможно открыть каталог кэша" | ||||||
|  |  | ||||||
|  | #: fix.go:70 | ||||||
|  | msgid "Unable to read cache directory contents" | ||||||
|  | msgstr "Невозможно прочитать содержимое каталога кэша" | ||||||
|  |  | ||||||
|  | #: fix.go:76 | ||||||
|  | msgid "Unable to remove cache item (%s)" | ||||||
|  | msgstr "Невозможно удалить элемент кэша (%s)" | ||||||
|  |  | ||||||
|  | #: fix.go:80 | ||||||
|  | msgid "Rebuilding cache" | ||||||
|  | msgstr "Восстановление кэша" | ||||||
|  |  | ||||||
|  | #: fix.go:84 | ||||||
|  | msgid "Unable to create new cache directory" | ||||||
|  | msgstr "Не удалось создать новый каталог кэша" | ||||||
|  |  | ||||||
|  | #: gen.go:34 | ||||||
|  | msgid "Generate a ALR script from a template" | ||||||
|  | msgstr "Генерация скрипта ALR из шаблона" | ||||||
|  |  | ||||||
|  | #: gen.go:39 | ||||||
|  | msgid "Generate a ALR script for a pip module" | ||||||
|  | msgstr "Генерация скрипта ALR для модуля pip" | ||||||
|  |  | ||||||
|  | #: helper.go:42 | ||||||
|  | msgid "List all the available helper commands" | ||||||
|  | msgstr "Список всех доступных вспомогательных команды" | ||||||
|  |  | ||||||
|  | #: helper.go:54 | ||||||
|  | msgid "Run a ALR helper command" | ||||||
|  | msgstr "Запустить вспомогательную команду ALR" | ||||||
|  |  | ||||||
|  | #: helper.go:61 | ||||||
|  | msgid "The directory that the install commands will install to" | ||||||
|  | msgstr "Каталог, в который будут устанавливать команды установки" | ||||||
|  |  | ||||||
|  | #: helper.go:74 helper.go:75 | ||||||
|  | msgid "No such helper command" | ||||||
|  | msgstr "Такой вспомогательной команды нет" | ||||||
|  |  | ||||||
|  | #: helper.go:85 | ||||||
|  | msgid "Error parsing os-release file" | ||||||
|  | msgstr "Ошибка при разборе файла выпуска операционной системы" | ||||||
|  |  | ||||||
|  | #: info.go:42 | ||||||
|  | msgid "Print information about a package" | ||||||
|  | msgstr "Отобразить информацию о пакете" | ||||||
|  |  | ||||||
|  | #: info.go:47 | ||||||
|  | msgid "Show all information, not just for the current distro" | ||||||
|  | msgstr "Показывать всю информацию, не только для текущего дистрибутива" | ||||||
|  |  | ||||||
|  | #: info.go:68 | ||||||
|  | msgid "Error getting packages" | ||||||
|  | msgstr "Ошибка при получении пакетов" | ||||||
|  |  | ||||||
|  | #: info.go:76 | ||||||
|  | msgid "Error iterating over packages" | ||||||
|  | msgstr "Ошибка при переборе пакетов" | ||||||
|  |  | ||||||
|  | #: info.go:90 | ||||||
|  | msgid "Command info expected at least 1 argument, got %d" | ||||||
|  | msgstr "Для команды info ожидался хотя бы 1 аргумент, получено %d" | ||||||
|  |  | ||||||
|  | #: info.go:110 | ||||||
|  | msgid "Error finding packages" | ||||||
|  | msgstr "Ошибка при поиске пакетов" | ||||||
|  |  | ||||||
|  | #: info.go:124 | ||||||
|  | msgid "Can't detect system language" | ||||||
|  | msgstr "Ошибка при определении языка системы" | ||||||
|  |  | ||||||
|  | #: info.go:141 | ||||||
|  | msgid "Error resolving overrides" | ||||||
|  | msgstr "Ошибка устранения переорпеделений" | ||||||
|  |  | ||||||
|  | #: info.go:149 info.go:154 | ||||||
|  | msgid "Error encoding script variables" | ||||||
|  | msgstr "Ошибка кодирования переменных скрита" | ||||||
|  |  | ||||||
|  | #: install.go:40 | ||||||
|  | msgid "Install a new package" | ||||||
|  | msgstr "Установить новый пакет" | ||||||
|  |  | ||||||
|  | #: install.go:52 | ||||||
|  | msgid "Command install expected at least 1 argument, got %d" | ||||||
|  | msgstr "Для команды install ожидался хотя бы 1 аргумент, получено %d" | ||||||
|  |  | ||||||
|  | #: install.go:114 | ||||||
|  | msgid "Error when installing the package" | ||||||
|  | msgstr "Ошибка при установке пакета" | ||||||
|  |  | ||||||
|  | #: install.go:159 | ||||||
|  | msgid "Remove an installed package" | ||||||
|  | msgstr "Удалить установленный пакет" | ||||||
|  |  | ||||||
|  | #: install.go:178 | ||||||
|  | msgid "Error listing installed packages" | ||||||
|  | msgstr "Ошибка при составлении списка установленных пакетов" | ||||||
|  |  | ||||||
|  | #: install.go:215 | ||||||
|  | msgid "Command remove expected at least 1 argument, got %d" | ||||||
|  | msgstr "Для команды remove ожидался хотя бы 1 аргумент, получено %d" | ||||||
|  |  | ||||||
|  | #: install.go:230 | ||||||
|  | msgid "Error removing packages" | ||||||
|  | msgstr "Ошибка при удалении пакетов" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/app_builder/builder.go:75 | ||||||
|  | msgid "Error loading config" | ||||||
|  | msgstr "Ошибка при загрузке" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/app_builder/builder.go:96 | ||||||
|  | msgid "Error initialization database" | ||||||
|  | msgstr "Ошибка инициализации базы данных" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/app_builder/builder.go:135 | ||||||
|  | msgid "Error pulling repositories" | ||||||
|  | msgstr "Ошибка при извлечении репозиториев" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/app_builder/builder.go:152 | ||||||
|  | msgid "Error parsing os release" | ||||||
|  | msgstr "Ошибка при разборе файла выпуска операционной системы" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/app_builder/builder.go:165 | ||||||
|  | msgid "Unable to detect a supported package manager on the system" | ||||||
|  | msgstr "Не удалось обнаружить поддерживаемый менеджер пакетов в системе" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/prompt.go:60 | ||||||
|  | msgid "Would you like to view the build script for %s" | ||||||
|  | msgstr "Показать скрипт для пакета %s" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/prompt.go:71 | ||||||
|  | msgid "Would you still like to continue?" | ||||||
|  | msgstr "Продолжить?" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/prompt.go:77 | ||||||
|  | msgid "User chose not to continue after reading script" | ||||||
|  | msgstr "Пользователь решил не продолжать после просмотра скрипта" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/prompt.go:111 | ||||||
|  | msgid "Error prompting for choice of package" | ||||||
|  | msgstr "Ошибка при запросе выбора пакета" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/prompt.go:135 | ||||||
|  | msgid "Choose which package to %s" | ||||||
|  | msgstr "Выберите, какой пакет использовать для %s" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/prompt.go:156 | ||||||
|  | msgid "Choose which optional package(s) to install" | ||||||
|  | msgstr "Выберите, какой дополнительный пакет(ы) следует установить" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/template.go:74 internal/cliutils/template.go:93 | ||||||
|  | msgid "NAME" | ||||||
|  | msgstr "НАЗВАНИЕ" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/template.go:74 internal/cliutils/template.go:94 | ||||||
|  | msgid "USAGE" | ||||||
|  | msgstr "ИСПОЛЬЗОВАНИЕ" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/template.go:74 | ||||||
|  | msgid "global options" | ||||||
|  | msgstr "глобальные опции" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/template.go:74 | ||||||
|  | msgid "command" | ||||||
|  | msgstr "команда" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/template.go:74 internal/cliutils/template.go:95 | ||||||
|  | msgid "command options" | ||||||
|  | msgstr "опции команды" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/template.go:74 internal/cliutils/template.go:96 | ||||||
|  | msgid "arguments" | ||||||
|  | msgstr "аргументы" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/template.go:74 | ||||||
|  | msgid "VERSION" | ||||||
|  | msgstr "ВЕРСИЯ" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/template.go:74 internal/cliutils/template.go:98 | ||||||
|  | msgid "DESCRIPTION" | ||||||
|  | msgstr "ОПИСАНИЕ" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/template.go:74 | ||||||
|  | msgid "AUTHOR" | ||||||
|  | msgstr "АВТОР" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/template.go:74 | ||||||
|  | msgid "COMMANDS" | ||||||
|  | msgstr "КОМАНДЫ" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/template.go:74 | ||||||
|  | msgid "GLOBAL OPTIONS" | ||||||
|  | msgstr "ГЛОБАЛЬНЫЕ ОПЦИИ" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/template.go:74 | ||||||
|  | msgid "COPYRIGHT" | ||||||
|  | msgstr "АВТОРСКОЕ ПРАВО" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/template.go:97 | ||||||
|  | msgid "CATEGORY" | ||||||
|  | msgstr "КАТЕГОРИЯ" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/template.go:99 internal/cliutils/template.go:100 | ||||||
|  | msgid "OPTIONS" | ||||||
|  | msgstr "ПАРАМЕТРЫ" | ||||||
|  |  | ||||||
|  | #: internal/cliutils/utils.go:69 | ||||||
|  | msgid "" | ||||||
|  | "This command is deprecated and would be removed in the future, use \"%s\" " | ||||||
|  | "instead!" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: internal/db/db.go:137 | ||||||
|  | msgid "Database version mismatch; resetting" | ||||||
|  | msgstr "Несоответствие версий базы данных; сброс настроек" | ||||||
|  |  | ||||||
|  | #: internal/db/db.go:144 | ||||||
|  | msgid "" | ||||||
|  | "Database version does not exist. Run alr fix if something isn't working." | ||||||
|  | msgstr "" | ||||||
|  | "Версия базы данных не существует. Запустите alr fix, если что-то не работает." | ||||||
|  |  | ||||||
|  | #: internal/dl/dl.go:170 | ||||||
|  | msgid "Source can be updated, updating if required" | ||||||
|  | msgstr "Исходный код можно обновлять, обновляя при необходимости" | ||||||
|  |  | ||||||
|  | #: internal/dl/dl.go:201 | ||||||
|  | msgid "Source found in cache and linked to destination" | ||||||
|  | msgstr "Источник найден в кэше и связан с пунктом назначения" | ||||||
|  |  | ||||||
|  | #: internal/dl/dl.go:208 | ||||||
|  | msgid "Source updated and linked to destination" | ||||||
|  | msgstr "Источник обновлён и связан с пунктом назначения" | ||||||
|  |  | ||||||
|  | #: internal/dl/dl.go:222 | ||||||
|  | msgid "Downloading source" | ||||||
|  | msgstr "Скачивание источника" | ||||||
|  |  | ||||||
|  | #: internal/dl/progress_tui.go:100 | ||||||
|  | msgid "%s: done!\n" | ||||||
|  | msgstr "%s: выполнено!\n" | ||||||
|  |  | ||||||
|  | #: internal/dl/progress_tui.go:104 | ||||||
|  | msgid "%s %s downloading at %s/s\n" | ||||||
|  | msgstr "%s %s загружается — %s/с\n" | ||||||
|  |  | ||||||
|  | #: internal/logger/log.go:41 | ||||||
|  | msgid "ERROR" | ||||||
|  | msgstr "ОШИБКА" | ||||||
|  |  | ||||||
|  | #: internal/utils/cmd.go:97 | ||||||
|  | msgid "Error on dropping capabilities" | ||||||
|  | msgstr "Ошибка при понижении привилегий" | ||||||
|  |  | ||||||
|  | #: internal/utils/cmd.go:164 | ||||||
|  | msgid "You need to be a %s member to perform this action" | ||||||
|  | msgstr "Вы должны быть членом %s чтобы выполнить это" | ||||||
|  |  | ||||||
|  | #: internal/utils/cmd.go:200 | ||||||
|  | msgid "You need to be root to perform this action" | ||||||
|  | msgstr "Вы должны быть root чтобы выполнить это" | ||||||
|  |  | ||||||
|  | #: list.go:43 | ||||||
|  | msgid "List ALR repo packages" | ||||||
|  | msgstr "Список пакетов репозитория ALR" | ||||||
|  |  | ||||||
|  | #: list.go:57 | ||||||
|  | msgid "Format output using a Go template" | ||||||
|  | msgstr "Формат выходных данных с использованием шаблона Go" | ||||||
|  |  | ||||||
|  | #: list.go:89 | ||||||
|  | msgid "Error getting packages for upgrade" | ||||||
|  | msgstr "Ошибка при получении пакетов для обновления" | ||||||
|  |  | ||||||
|  | #: list.go:92 | ||||||
|  | msgid "No packages for upgrade" | ||||||
|  | msgstr "Нет пакетов к обновлению" | ||||||
|  |  | ||||||
|  | #: list.go:102 list.go:187 | ||||||
|  | msgid "Error parsing format template" | ||||||
|  | msgstr "Ошибка при разборе шаблона" | ||||||
|  |  | ||||||
|  | #: list.go:108 list.go:191 | ||||||
|  | msgid "Error executing template" | ||||||
|  | msgstr "Ошибка при выполнении шаблона" | ||||||
|  |  | ||||||
|  | #: main.go:45 | ||||||
|  | msgid "Print the current ALR version and exit" | ||||||
|  | msgstr "Показать текущую версию ALR и выйти" | ||||||
|  |  | ||||||
|  | #: main.go:61 | ||||||
|  | msgid "Arguments to be passed on to the package manager" | ||||||
|  | msgstr "Аргументы, которые будут переданы менеджеру пакетов" | ||||||
|  |  | ||||||
|  | #: main.go:67 | ||||||
|  | msgid "Enable interactive questions and prompts" | ||||||
|  | msgstr "Включение интерактивных вопросов и запросов" | ||||||
|  |  | ||||||
|  | #: main.go:146 | ||||||
|  | msgid "Show help" | ||||||
|  | msgstr "Показать справку" | ||||||
|  |  | ||||||
|  | #: main.go:150 | ||||||
|  | msgid "Error while running app" | ||||||
|  | msgstr "Ошибка при запуске приложения" | ||||||
|  |  | ||||||
|  | #: pkg/build/build.go:395 | ||||||
|  | msgid "Building package" | ||||||
|  | msgstr "Сборка пакета" | ||||||
|  |  | ||||||
|  | #: pkg/build/build.go:424 | ||||||
|  | msgid "The checksums array must be the same length as sources" | ||||||
|  | msgstr "Массив контрольных сумм должен быть той же длины, что и источники" | ||||||
|  |  | ||||||
|  | #: pkg/build/build.go:455 | ||||||
|  | msgid "Downloading sources" | ||||||
|  | msgstr "Скачивание источников" | ||||||
|  |  | ||||||
|  | #: pkg/build/build.go:549 | ||||||
|  | msgid "Installing dependencies" | ||||||
|  | msgstr "Установка зависимостей" | ||||||
|  |  | ||||||
|  | #: pkg/build/checker.go:43 | ||||||
|  | msgid "" | ||||||
|  | "Your system's CPU architecture doesn't match this package. Do you want to " | ||||||
|  | "build anyway?" | ||||||
|  | msgstr "" | ||||||
|  | "Архитектура процессора вашей системы не соответствует этому пакету. Вы все " | ||||||
|  | "равно хотите выполнить сборку?" | ||||||
|  |  | ||||||
|  | #: pkg/build/checker.go:67 | ||||||
|  | msgid "This package is already installed" | ||||||
|  | msgstr "Этот пакет уже установлен" | ||||||
|  |  | ||||||
|  | #: pkg/build/find_deps/alt_linux.go:35 | ||||||
|  | msgid "Command not found on the system" | ||||||
|  | msgstr "Команда не найдена в системе" | ||||||
|  |  | ||||||
|  | #: pkg/build/find_deps/alt_linux.go:86 | ||||||
|  | msgid "Provided dependency found" | ||||||
|  | msgstr "Найденная предоставленная зависимость" | ||||||
|  |  | ||||||
|  | #: pkg/build/find_deps/alt_linux.go:93 | ||||||
|  | msgid "Required dependency found" | ||||||
|  | msgstr "Найдена требуемая зависимость" | ||||||
|  |  | ||||||
|  | #: pkg/build/find_deps/empty.go:32 | ||||||
|  | msgid "AutoProv is not implemented for this package format, so it's skipped" | ||||||
|  | msgstr "" | ||||||
|  | "AutoProv не реализовано для этого формата пакета, поэтому будет пропущено" | ||||||
|  |  | ||||||
|  | #: pkg/build/find_deps/empty.go:37 | ||||||
|  | msgid "AutoReq is not implemented for this package format, so it's skipped" | ||||||
|  | msgstr "" | ||||||
|  | "AutoReq не реализовано для этого формата пакета, поэтому будет пропущено" | ||||||
|  |  | ||||||
|  | #: pkg/build/script_executor.go:241 | ||||||
|  | msgid "Building package metadata" | ||||||
|  | msgstr "Сборка метаданных пакета" | ||||||
|  |  | ||||||
|  | #: pkg/build/script_executor.go:372 | ||||||
|  | msgid "Executing prepare()" | ||||||
|  | msgstr "Выполнение prepare()" | ||||||
|  |  | ||||||
|  | #: pkg/build/script_executor.go:381 | ||||||
|  | msgid "Executing build()" | ||||||
|  | msgstr "Выполнение build()" | ||||||
|  |  | ||||||
|  | #: pkg/build/script_executor.go:410 pkg/build/script_executor.go:430 | ||||||
|  | msgid "Executing %s()" | ||||||
|  | msgstr "Выполнение %s()" | ||||||
|  |  | ||||||
|  | #: pkg/repos/pull.go:77 | ||||||
|  | msgid "Pulling repository" | ||||||
|  | msgstr "Скачивание репозитория" | ||||||
|  |  | ||||||
|  | #: pkg/repos/pull.go:113 | ||||||
|  | msgid "Repository up to date" | ||||||
|  | msgstr "Репозиторий уже обновлён" | ||||||
|  |  | ||||||
|  | #: pkg/repos/pull.go:204 | ||||||
|  | msgid "Git repository does not appear to be a valid ALR repo" | ||||||
|  | msgstr "Репозиторий Git не поддерживается репозиторием ALR" | ||||||
|  |  | ||||||
|  | #: pkg/repos/pull.go:220 | ||||||
|  | msgid "" | ||||||
|  | "ALR repo's minimum ALR version is greater than the current version. Try " | ||||||
|  | "updating ALR if something doesn't work." | ||||||
|  | msgstr "" | ||||||
|  | "Минимальная версия ALR для ALR-репозитория выше текущей версии. Попробуйте " | ||||||
|  | "обновить ALR, если что-то не работает." | ||||||
|  |  | ||||||
|  | #: refresh.go:30 | ||||||
|  | msgid "Pull all repositories that have changed" | ||||||
|  | msgstr "Скачать все изменённые репозитории" | ||||||
|  |  | ||||||
|  | #: repo.go:39 | ||||||
|  | msgid "Manage repos" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: repo.go:50 repo.go:220 | ||||||
|  | msgid "Remove an existing repository" | ||||||
|  | msgstr "Удалить существующий репозиторий" | ||||||
|  |  | ||||||
|  | #: repo.go:52 | ||||||
|  | msgid "<name>" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: repo.go:82 | ||||||
|  | msgid "Repo \"%s\" does not exist" | ||||||
|  | msgstr "Репозитория \"%s\" не существует" | ||||||
|  |  | ||||||
|  | #: repo.go:89 | ||||||
|  | msgid "Error removing repo directory" | ||||||
|  | msgstr "Ошибка при удалении каталога репозитория" | ||||||
|  |  | ||||||
|  | #: repo.go:93 repo.go:160 | ||||||
|  | msgid "Error saving config" | ||||||
|  | msgstr "Ошибка при сохранении конфигурации" | ||||||
|  |  | ||||||
|  | #: repo.go:112 | ||||||
|  | msgid "Error removing packages from database" | ||||||
|  | msgstr "Ошибка при удалении пакетов из базы данных" | ||||||
|  |  | ||||||
|  | #: repo.go:123 repo.go:190 | ||||||
|  | msgid "Add a new repository" | ||||||
|  | msgstr "Добавить новый репозиторий" | ||||||
|  |  | ||||||
|  | #: repo.go:124 | ||||||
|  | msgid "<name> <url>" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: repo.go:149 | ||||||
|  | msgid "Repo \"%s\" already exists" | ||||||
|  | msgstr "Репозиторий \"%s\" уже существует" | ||||||
|  |  | ||||||
|  | #: repo.go:197 | ||||||
|  | msgid "Name of the new repo" | ||||||
|  | msgstr "Название нового репозитория" | ||||||
|  |  | ||||||
|  | #: repo.go:203 | ||||||
|  | msgid "URL of the new repo" | ||||||
|  | msgstr "URL-адрес нового репозитория" | ||||||
|  |  | ||||||
|  | #: repo.go:227 | ||||||
|  | msgid "Name of the repo to be deleted" | ||||||
|  | msgstr "Название репозитория  удалён" | ||||||
|  |  | ||||||
|  | #: search.go:40 | ||||||
|  | msgid "Search packages" | ||||||
|  | msgstr "Поиск пакетов" | ||||||
|  |  | ||||||
|  | #: search.go:51 | ||||||
|  | msgid "Search by name" | ||||||
|  | msgstr "Искать по имени" | ||||||
|  |  | ||||||
|  | #: search.go:56 | ||||||
|  | msgid "Search by description" | ||||||
|  | msgstr "Искать по описанию" | ||||||
|  |  | ||||||
|  | #: search.go:61 | ||||||
|  | msgid "Search by repository" | ||||||
|  | msgstr "Искать по репозиторию" | ||||||
|  |  | ||||||
|  | #: search.go:66 | ||||||
|  | msgid "Search by provides" | ||||||
|  | msgstr "Иcкать по provides" | ||||||
|  |  | ||||||
|  | #: search.go:130 | ||||||
|  | msgid "Error while executing search" | ||||||
|  | msgstr "Ошибка при выполнении поиска" | ||||||
|  |  | ||||||
|  | #: upgrade.go:47 | ||||||
|  | msgid "Upgrade all installed packages" | ||||||
|  | msgstr "Обновить все установленные пакеты" | ||||||
|  |  | ||||||
|  | #: upgrade.go:105 upgrade.go:122 | ||||||
|  | msgid "Error checking for updates" | ||||||
|  | msgstr "Ошибка при проверке обновлений" | ||||||
|  |  | ||||||
|  | #: upgrade.go:125 | ||||||
|  | msgid "There is nothing to do." | ||||||
|  | msgstr "Здесь нечего делать." | ||||||
|  |  | ||||||
|  | #~ msgid "Error pulling repos" | ||||||
|  | #~ msgstr "Ошибка при извлечении репозиториев" | ||||||
|  |  | ||||||
|  | #, fuzzy | ||||||
|  | #~ msgid "Error getting current executable" | ||||||
|  | #~ msgstr "Ошибка при получении рабочего каталога" | ||||||
|  |  | ||||||
|  | #, fuzzy | ||||||
|  | #~ msgid "Error mounting" | ||||||
|  | #~ msgstr "Ошибка при кодировании конфигурации" | ||||||
|  |  | ||||||
|  | #, fuzzy | ||||||
|  | #~ msgid "Unable to create config directory" | ||||||
|  | #~ msgstr "Не удалось создать каталог конфигурации ALR" | ||||||
|  |  | ||||||
|  | #~ msgid "Unable to create repo cache directory" | ||||||
|  | #~ msgstr "Не удалось создать каталог кэша репозитория" | ||||||
|  |  | ||||||
|  | #~ msgid "Unable to create package cache directory" | ||||||
|  | #~ msgstr "Не удалось создать каталог кэша пакетов" | ||||||
|  |  | ||||||
|  | #~ msgid "" | ||||||
|  | #~ "Running ALR as root is forbidden as it may cause catastrophic damage to " | ||||||
|  | #~ "your system" | ||||||
|  | #~ msgstr "" | ||||||
|  | #~ "Запуск ALR от имени root запрещён, так как это может привести к " | ||||||
|  | #~ "катастрофическому повреждению вашей системы" | ||||||
|  |  | ||||||
|  | #~ msgid "Failed to prompt user to view build script" | ||||||
|  | #~ msgstr "Не удалось предложить пользователю просмотреть скрипт сборки" | ||||||
|  |  | ||||||
|  | #~ msgid "Compressing package" | ||||||
|  | #~ msgstr "Сжатие пакета" | ||||||
|  |  | ||||||
|  | #~ msgid "Installing build dependencies" | ||||||
|  | #~ msgstr "Установка зависимостей сборки" | ||||||
|  |  | ||||||
|  | #~ msgid "Would you like to remove the build dependencies?" | ||||||
|  | #~ msgstr "Хотели бы вы удалить зависимости сборки?" | ||||||
|  |  | ||||||
|  | #~ msgid "Error installing native packages" | ||||||
|  | #~ msgstr "Ошибка при установке нативных пакетов" | ||||||
|  |  | ||||||
|  | #~ msgid "Error opening config file, using defaults" | ||||||
|  | #~ msgstr "" | ||||||
|  | #~ "Ошибка при открытии конфигурационного файла, используются значения по " | ||||||
|  | #~ "умолчанию" | ||||||
|  |  | ||||||
|  | #~ msgid "Error decoding config file, using defaults" | ||||||
|  | #~ msgstr "" | ||||||
|  | #~ "Ошибка при декодировании конфигурационного файла, используются значения " | ||||||
|  | #~ "по умолчанию" | ||||||
|  |  | ||||||
|  | #~ msgid "Unable to detect user config directory" | ||||||
|  | #~ msgstr "Не удалось обнаружить каталог конфигурации пользователя" | ||||||
|  |  | ||||||
|  | #~ msgid "Unable to create ALR config file" | ||||||
|  | #~ msgstr "Не удалось создать конфигурационный файл ALR" | ||||||
|  |  | ||||||
|  | #~ msgid "Error encoding default configuration" | ||||||
|  | #~ msgstr "Ошибка кодирования конфигурации по умолчанию" | ||||||
|  |  | ||||||
|  | #~ msgid "Unable to detect cache directory" | ||||||
|  | #~ msgstr "Не удалось обнаружить каталог кэша" | ||||||
|  |  | ||||||
|  | #~ msgid "Error opening config file" | ||||||
|  | #~ msgstr "Ошибка при открытии конфигурационного файла" | ||||||
|  |  | ||||||
|  | #~ msgid "Executing version()" | ||||||
|  | #~ msgstr "Исполнение версия()" | ||||||
|  |  | ||||||
|  | #~ msgid "Updating version" | ||||||
|  | #~ msgstr "Обновление версии" | ||||||
|  |  | ||||||
|  | #~ msgid "Executing package()" | ||||||
|  | #~ msgstr "Исполнение package()" | ||||||
| @@ -1,56 +1,52 @@ | |||||||
| /* | // This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan. | ||||||
|  * ALR - Any Linux Repository | // It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors. | ||||||
|  * Copyright (C) 2024 Евгений Храмов | // | ||||||
|  * | // ALR - Any Linux Repository | ||||||
|  * This program is free software: you can redistribute it and/or modify | // Copyright (C) 2025 The ALR Authors | ||||||
|  * it under the terms of the GNU General Public License as published by | // | ||||||
|  * the Free Software Foundation, either version 3 of the License, or | // This program is free software: you can redistribute it and/or modify | ||||||
|  * (at your option) any later version. | // it under the terms of the GNU General Public License as published by | ||||||
|  * | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  * This program is distributed in the hope that it will be useful, | // (at your option) any later version. | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | // | ||||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | // This program is distributed in the hope that it will be useful, | ||||||
|  * GNU General Public License for more details. | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  * | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  * You should have received a copy of the GNU General Public License | // GNU General Public License for more details. | ||||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | // | ||||||
|  */ | // 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 translations | package translations | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"context" |  | ||||||
| 	"embed" | 	"embed" | ||||||
| 	"sync" | 	"io/fs" | ||||||
|  | 	"os" | ||||||
|  | 	"path" | ||||||
|  |  | ||||||
| 	"go.elara.ws/logger" | 	"github.com/jeandeaual/go-locale" | ||||||
| 	"plemya-x.ru/alr/pkg/loggerctx" | 	"github.com/leonelquinteros/gotext" | ||||||
| 	"go.elara.ws/translate" |  | ||||||
| 	"golang.org/x/text/language" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| //go:embed files | //go:embed po | ||||||
| var translationFS embed.FS | var poFS embed.FS | ||||||
|  |  | ||||||
| var ( | func Setup() { | ||||||
| 	mu         sync.Mutex | 	userLanguage, err := locale.GetLanguage() | ||||||
| 	translator *translate.Translator | 	if err != nil { | ||||||
| ) | 		panic(err) | ||||||
|  |  | ||||||
| func Translator(ctx context.Context) *translate.Translator { |  | ||||||
| 	mu.Lock() |  | ||||||
| 	defer mu.Unlock() |  | ||||||
| 	log := loggerctx.From(ctx) |  | ||||||
| 	if translator == nil { |  | ||||||
| 		t, err := translate.NewFromFS(translationFS) |  | ||||||
| 		if err != nil { |  | ||||||
| 			log.Fatal("Error creating new translator").Err(err).Send() |  | ||||||
| 		} |  | ||||||
| 		translator = &t |  | ||||||
| 	} | 	} | ||||||
| 	return translator |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func NewLogger(ctx context.Context, l logger.Logger, lang language.Tag) *translate.TranslatedLogger { | 	_, err = fs.Stat(poFS, path.Join("po", userLanguage)) | ||||||
| 	return translate.NewLogger(l, *Translator(ctx), lang) | 	if err != nil { | ||||||
|  | 		if os.IsNotExist(err) { | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		panic(err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	loc := gotext.NewLocaleFSWithPath(userLanguage, &poFS, "po") | ||||||
|  | 	loc.SetDomain("default") | ||||||
|  | 	gotext.SetLocales([]*gotext.Locale{loc}) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,54 +1,70 @@ | |||||||
| /* | // This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan. | ||||||
|  * ALR - Any Linux Repository | // It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors. | ||||||
|  * Copyright (C) 2024 Евгений Храмов | // | ||||||
|  * | // ALR - Any Linux Repository | ||||||
|  * This program is free software: you can redistribute it and/or modify | // Copyright (C) 2025 The ALR Authors | ||||||
|  * it under the terms of the GNU General Public License as published by | // | ||||||
|  * the Free Software Foundation, either version 3 of the License, or | // This program is free software: you can redistribute it and/or modify | ||||||
|  * (at your option) any later version. | // it under the terms of the GNU General Public License as published by | ||||||
|  * | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  * This program is distributed in the hope that it will be useful, | // (at your option) any later version. | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | // | ||||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | // This program is distributed in the hope that it will be useful, | ||||||
|  * GNU General Public License for more details. | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  * | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  * You should have received a copy of the GNU General Public License | // GNU General Public License for more details. | ||||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | // | ||||||
|  */ | // 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 types | package types | ||||||
|  |  | ||||||
| import "plemya-x.ru/alr/pkg/manager" |  | ||||||
|  |  | ||||||
| type BuildOpts struct { | type BuildOpts struct { | ||||||
| 	Script      string |  | ||||||
| 	Manager     manager.Manager |  | ||||||
| 	Clean       bool | 	Clean       bool | ||||||
| 	Interactive bool | 	Interactive bool | ||||||
| } | } | ||||||
|  |  | ||||||
|  | type BuildVarsPre struct { | ||||||
|  | 	Version          string   `sh:"version,required"` | ||||||
|  | 	Release          int      `sh:"release,required"` | ||||||
|  | 	Epoch            uint     `sh:"epoch"` | ||||||
|  | 	Summary          string   `sh:"summary"` | ||||||
|  | 	Description      string   `sh:"desc"` | ||||||
|  | 	Group            string   `sh:"group"` | ||||||
|  | 	Homepage         string   `sh:"homepage"` | ||||||
|  | 	Maintainer       string   `sh:"maintainer"` | ||||||
|  | 	Architectures    []string `sh:"architectures"` | ||||||
|  | 	Licenses         []string `sh:"license"` | ||||||
|  | 	Provides         []string `sh:"provides"` | ||||||
|  | 	Conflicts        []string `sh:"conflicts"` | ||||||
|  | 	Depends          []string `sh:"deps"` | ||||||
|  | 	BuildDepends     []string `sh:"build_deps"` | ||||||
|  | 	OptDepends       []string `sh:"opt_deps"` | ||||||
|  | 	Replaces         []string `sh:"replaces"` | ||||||
|  | 	Sources          []string `sh:"sources"` | ||||||
|  | 	Checksums        []string `sh:"checksums"` | ||||||
|  | 	Backup           []string `sh:"backup"` | ||||||
|  | 	Scripts          Scripts  `sh:"scripts"` | ||||||
|  | 	AutoReq          []string `sh:"auto_req"` | ||||||
|  | 	AutoProv         []string `sh:"auto_prov"` | ||||||
|  | 	AutoReqSkipList  []string `sh:"auto_req_skiplist"` | ||||||
|  | 	AutoProvSkipList []string `sh:"auto_prov_skiplist"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (bv *BuildVarsPre) ToBuildVars() BuildVars { | ||||||
|  | 	return BuildVars{ | ||||||
|  | 		Name:         "", | ||||||
|  | 		Base:         "", | ||||||
|  | 		BuildVarsPre: *bv, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| // BuildVars represents the script variables required | // BuildVars represents the script variables required | ||||||
| // to build a package | // to build a package | ||||||
| type BuildVars struct { | type BuildVars struct { | ||||||
| 	Name          string   `sh:"name,required"` | 	Name string `sh:"name,required"` | ||||||
| 	Version       string   `sh:"version,required"` | 	Base string | ||||||
| 	Release       int      `sh:"release,required"` | 	BuildVarsPre | ||||||
| 	Epoch         uint     `sh:"epoch"` |  | ||||||
| 	Description   string   `sh:"desc"` |  | ||||||
| 	Homepage      string   `sh:"homepage"` |  | ||||||
| 	Maintainer    string   `sh:"maintainer"` |  | ||||||
| 	Architectures []string `sh:"architectures"` |  | ||||||
| 	Licenses      []string `sh:"license"` |  | ||||||
| 	Provides      []string `sh:"provides"` |  | ||||||
| 	Conflicts     []string `sh:"conflicts"` |  | ||||||
| 	Depends       []string `sh:"deps"` |  | ||||||
| 	BuildDepends  []string `sh:"build_deps"` |  | ||||||
| 	OptDepends    []string `sh:"opt_deps"` |  | ||||||
| 	Replaces      []string `sh:"replaces"` |  | ||||||
| 	Sources       []string `sh:"sources"` |  | ||||||
| 	Checksums     []string `sh:"checksums"` |  | ||||||
| 	Backup        []string `sh:"backup"` |  | ||||||
| 	Scripts       Scripts  `sh:"scripts"` |  | ||||||
| } | } | ||||||
|  |  | ||||||
| type Scripts struct { | type Scripts struct { | ||||||
|   | |||||||
| @@ -1,38 +1,38 @@ | |||||||
| /* | // This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan. | ||||||
|  * ALR - Any Linux Repository | // It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors. | ||||||
|  * Copyright (C) 2024 Евгений Храмов | // | ||||||
|  * | // ALR - Any Linux Repository | ||||||
|  * This program is free software: you can redistribute it and/or modify | // Copyright (C) 2025 The ALR Authors | ||||||
|  * it under the terms of the GNU General Public License as published by | // | ||||||
|  * the Free Software Foundation, either version 3 of the License, or | // This program is free software: you can redistribute it and/or modify | ||||||
|  * (at your option) any later version. | // it under the terms of the GNU General Public License as published by | ||||||
|  * | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  * This program is distributed in the hope that it will be useful, | // (at your option) any later version. | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | // | ||||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | // This program is distributed in the hope that it will be useful, | ||||||
|  * GNU General Public License for more details. | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  * | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  * You should have received a copy of the GNU General Public License | // GNU General Public License for more details. | ||||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | // | ||||||
|  */ | // 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 types | package types | ||||||
|  |  | ||||||
| // Config represents the ALR configuration file | // Config represents the ALR configuration file | ||||||
| type Config struct { | type Config struct { | ||||||
| 	RootCmd          string   `toml:"rootCmd"` | 	RootCmd          string   `toml:"rootCmd" env:"ALR_ROOT_CMD"` | ||||||
| 	PagerStyle       string   `toml:"pagerStyle"` | 	UseRootCmd       bool     `toml:"useRootCmd"` | ||||||
|  | 	PagerStyle       string   `toml:"pagerStyle" env:"ALR_PAGER_STYLE"` | ||||||
| 	IgnorePkgUpdates []string `toml:"ignorePkgUpdates"` | 	IgnorePkgUpdates []string `toml:"ignorePkgUpdates"` | ||||||
| 	Repos            []Repo   `toml:"repo"` | 	Repos            []Repo   `toml:"repo"` | ||||||
| 	Unsafe           Unsafe   `toml:"unsafe"` | 	AutoPull         bool     `toml:"autoPull" env:"ALR_AUTOPULL"` | ||||||
|  | 	LogLevel         string   `toml:"logLevel" env:"ALR_LOG_LEVEL"` | ||||||
| } | } | ||||||
|  |  | ||||||
| // Repo represents a ALR repo within a configuration file | // Repo represents a ALR repo within a configuration file | ||||||
| type Repo struct { | type Repo struct { | ||||||
| 	Name string `toml:"name"` | 	Name string `toml:"name"` | ||||||
| 	URL  string `toml:"url"` | 	URL  string `toml:"url"` | ||||||
| } | 	Ref  string `toml:"ref"` | ||||||
|  |  | ||||||
| type Unsafe struct { |  | ||||||
| 	AllowRunAsRoot bool `toml:"allowRunAsRoot"` |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,20 +1,21 @@ | |||||||
| /* | // This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan. | ||||||
|  * ALR - Any Linux Repository | // It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors. | ||||||
|  * Copyright (C) 2024 Евгений Храмов | // | ||||||
|  * | // ALR - Any Linux Repository | ||||||
|  * This program is free software: you can redistribute it and/or modify | // Copyright (C) 2025 The ALR Authors | ||||||
|  * it under the terms of the GNU General Public License as published by | // | ||||||
|  * the Free Software Foundation, either version 3 of the License, or | // This program is free software: you can redistribute it and/or modify | ||||||
|  * (at your option) any later version. | // it under the terms of the GNU General Public License as published by | ||||||
|  * | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  * This program is distributed in the hope that it will be useful, | // (at your option) any later version. | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | // | ||||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | // This program is distributed in the hope that it will be useful, | ||||||
|  * GNU General Public License for more details. | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  * | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  * You should have received a copy of the GNU General Public License | // GNU General Public License for more details. | ||||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | // | ||||||
|  */ | // 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 types | package types | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										215
									
								
								internal/utils/cmd.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										215
									
								
								internal/utils/cmd.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,215 @@ | |||||||
|  | // 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 utils | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"errors" | ||||||
|  | 	"os" | ||||||
|  | 	"os/exec" | ||||||
|  | 	"os/user" | ||||||
|  | 	"strconv" | ||||||
|  | 	"syscall" | ||||||
|  |  | ||||||
|  | 	"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/constants" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func GetUidGidAlrUserString() (string, string, error) { | ||||||
|  | 	u, err := user.Lookup("alr") | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", "", err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return u.Uid, u.Gid, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func GetUidGidAlrUser() (int, int, error) { | ||||||
|  | 	strUid, strGid, err := GetUidGidAlrUserString() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return 0, 0, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	uid, err := strconv.Atoi(strUid) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return 0, 0, err | ||||||
|  | 	} | ||||||
|  | 	gid, err := strconv.Atoi(strGid) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return 0, 0, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return uid, gid, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func DropCapsToAlrUser() error { | ||||||
|  | 	uid, gid, err := GetUidGidAlrUser() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	err = syscall.Setgid(gid) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	err = syscall.Setuid(uid) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	return EnsureIsAlrUser() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func ExitIfCantDropGidToAlr() cli.ExitCoder { | ||||||
|  | 	_, gid, err := GetUidGidAlrUser() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return cliutils.FormatCliExit("cannot get gid alr", err) | ||||||
|  | 	} | ||||||
|  | 	err = syscall.Setgid(gid) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return cliutils.FormatCliExit("cannot get setgid alr", err) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ExitIfCantDropCapsToAlrUser attempts to drop capabilities to the already | ||||||
|  | // running user. Returns a cli.ExitCoder with an error if the operation fails. | ||||||
|  | // See also [ExitIfCantDropCapsToAlrUserNoPrivs] for a version that also applies | ||||||
|  | // no-new-privs. | ||||||
|  | func ExitIfCantDropCapsToAlrUser() cli.ExitCoder { | ||||||
|  | 	err := DropCapsToAlrUser() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return cliutils.FormatCliExit(gotext.Get("Error on dropping capabilities"), err) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func ExitIfCantSetNoNewPrivs() cli.ExitCoder { | ||||||
|  | 	if err := NoNewPrivs(); err != nil { | ||||||
|  | 		return cliutils.FormatCliExit("error on NoNewPrivs", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ExitIfCantDropCapsToAlrUserNoPrivs combines [ExitIfCantDropCapsToAlrUser] with [ExitIfCantSetNoNewPrivs] | ||||||
|  | func ExitIfCantDropCapsToAlrUserNoPrivs() cli.ExitCoder { | ||||||
|  | 	if err := ExitIfCantDropCapsToAlrUser(); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err := ExitIfCantSetNoNewPrivs(); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func IsNotRoot() bool { | ||||||
|  | 	return os.Getuid() != 0 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func EnsureIsAlrUser() error { | ||||||
|  | 	uid, gid, err := GetUidGidAlrUser() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	newUid := syscall.Getuid() | ||||||
|  | 	if newUid != uid { | ||||||
|  | 		return errors.New("new uid don't matches requested") | ||||||
|  | 	} | ||||||
|  | 	newGid := syscall.Getgid() | ||||||
|  | 	if newGid != gid { | ||||||
|  | 		return errors.New("new gid don't matches requested") | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func EnuseIsPrivilegedGroupMember() error { | ||||||
|  | 	currentUser, err := user.Current() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	group, err := user.LookupGroup(constants.PrivilegedGroup) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	groups, err := currentUser.GroupIds() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, gid := range groups { | ||||||
|  | 		if gid == group.Gid { | ||||||
|  | 			return nil | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return cliutils.FormatCliExit(gotext.Get("You need to be a %s member to perform this action", constants.PrivilegedGroup), nil) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func EscalateToRootGid() error { | ||||||
|  | 	return syscall.Setgid(0) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func EscalateToRootUid() error { | ||||||
|  | 	return syscall.Setuid(0) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func EscalateToRoot() error { | ||||||
|  | 	err := EscalateToRootUid() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	err = EscalateToRootGid() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func RootNeededAction(f cli.ActionFunc) cli.ActionFunc { | ||||||
|  | 	return func(ctx *cli.Context) error { | ||||||
|  | 		deps, err := appbuilder. | ||||||
|  | 			New(ctx.Context). | ||||||
|  | 			WithConfig(). | ||||||
|  | 			Build() | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 		defer deps.Defer() | ||||||
|  |  | ||||||
|  | 		if IsNotRoot() { | ||||||
|  | 			if !deps.Cfg.UseRootCmd() { | ||||||
|  | 				return cli.Exit(gotext.Get("You need to be root to perform this action"), 1) | ||||||
|  | 			} | ||||||
|  | 			executable, err := os.Executable() | ||||||
|  | 			if err != nil { | ||||||
|  | 				return cliutils.FormatCliExit("failed to get executable path", err) | ||||||
|  | 			} | ||||||
|  | 			args := append([]string{executable}, os.Args[1:]...) | ||||||
|  | 			cmd := exec.Command(deps.Cfg.RootCmd(), args...) | ||||||
|  | 			cmd.Stdin = os.Stdin | ||||||
|  | 			cmd.Stdout = os.Stdout | ||||||
|  | 			cmd.Stderr = os.Stderr | ||||||
|  | 			return cmd.Run() | ||||||
|  | 		} | ||||||
|  | 		return f(ctx) | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										23
									
								
								internal/utils/utils.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								internal/utils/utils.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | |||||||
|  | // 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 utils | ||||||
|  |  | ||||||
|  | import "golang.org/x/sys/unix" | ||||||
|  |  | ||||||
|  | func NoNewPrivs() error { | ||||||
|  | 	return unix.Prctl(unix.PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) | ||||||
|  | } | ||||||
							
								
								
									
										18
									
								
								license-header-old-files.tmpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								license-header-old-files.tmpl
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | |||||||
|  | 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) {{ .Year }} 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/>. | ||||||
							
								
								
									
										15
									
								
								license-header.tmpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								license-header.tmpl
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | |||||||
|  | ALR - Any Linux Repository | ||||||
|  | Copyright (C) {{ .Year }} 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/>. | ||||||
							
								
								
									
										262
									
								
								list.go
									
									
									
									
									
								
							
							
						
						
									
										262
									
								
								list.go
									
									
									
									
									
								
							| @@ -1,108 +1,198 @@ | |||||||
| /* | // This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan. | ||||||
|  * ALR - Any Linux Repository | // It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors. | ||||||
|  * Copyright (C) 2024 Евгений Храмов | // | ||||||
|  * | // ALR - Any Linux Repository | ||||||
|  * This program is free software: you can redistribute it and/or modify | // Copyright (C) 2025 The ALR Authors | ||||||
|  * it under the terms of the GNU General Public License as published by | // | ||||||
|  * the Free Software Foundation, either version 3 of the License, or | // This program is free software: you can redistribute it and/or modify | ||||||
|  * (at your option) any later version. | // it under the terms of the GNU General Public License as published by | ||||||
|  * | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  * This program is distributed in the hope that it will be useful, | // (at your option) any later version. | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | // | ||||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | // This program is distributed in the hope that it will be useful, | ||||||
|  * GNU General Public License for more details. | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  * | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  * You should have received a copy of the GNU General Public License | // GNU General Public License for more details. | ||||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | // | ||||||
|  */ | // 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 | package main | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  | 	"log/slog" | ||||||
|  | 	"os" | ||||||
|  | 	"slices" | ||||||
|  | 	"text/template" | ||||||
|  |  | ||||||
|  | 	"github.com/leonelquinteros/gotext" | ||||||
| 	"github.com/urfave/cli/v2" | 	"github.com/urfave/cli/v2" | ||||||
| 	"plemya-x.ru/alr/internal/config" |  | ||||||
| 	"plemya-x.ru/alr/internal/db" | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils" | ||||||
| 	"plemya-x.ru/alr/pkg/loggerctx" | 	appbuilder "gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils/app_builder" | ||||||
| 	"plemya-x.ru/alr/pkg/manager" | 	database "gitea.plemya-x.ru/Plemya-x/ALR/internal/db" | ||||||
| 	"plemya-x.ru/alr/pkg/repos" | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/utils" | ||||||
| 	"golang.org/x/exp/slices" | 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/build" | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| var listCmd = &cli.Command{ | func ListCmd() *cli.Command { | ||||||
| 	Name:    "list", | 	return &cli.Command{ | ||||||
| 	Usage:   "List ALR repo packages", | 		Name:    "list", | ||||||
| 	Aliases: []string{"ls"}, | 		Usage:   gotext.Get("List ALR repo packages"), | ||||||
| 	Flags: []cli.Flag{ | 		Aliases: []string{"ls"}, | ||||||
| 		&cli.BoolFlag{ | 		Flags: []cli.Flag{ | ||||||
| 			Name:    "installed", | 			&cli.BoolFlag{ | ||||||
| 			Aliases: []string{"I"}, | 				Name:    "installed", | ||||||
|  | 				Aliases: []string{"I"}, | ||||||
|  | 			}, | ||||||
|  | 			&cli.BoolFlag{ | ||||||
|  | 				Name:    "upgradable", | ||||||
|  | 				Aliases: []string{"U"}, | ||||||
|  | 			}, | ||||||
|  | 			&cli.StringFlag{ | ||||||
|  | 				Name:    "format", | ||||||
|  | 				Aliases: []string{"f"}, | ||||||
|  | 				Usage:   gotext.Get("Format output using a Go template"), | ||||||
|  | 			}, | ||||||
| 		}, | 		}, | ||||||
| 	}, | 		Action: func(c *cli.Context) error { | ||||||
| 	Action: func(c *cli.Context) error { | 			if err := utils.ExitIfCantDropCapsToAlrUserNoPrivs(); err != nil { | ||||||
| 		ctx := c.Context |  | ||||||
| 		log := loggerctx.From(ctx) |  | ||||||
|  |  | ||||||
| 		err := repos.Pull(ctx, config.Config(ctx).Repos) |  | ||||||
| 		if err != nil { |  | ||||||
| 			log.Fatal("Error pulling repositories").Err(err).Send() |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		where := "true" |  | ||||||
| 		args := []any(nil) |  | ||||||
| 		if c.NArg() > 0 { |  | ||||||
| 			where = "name LIKE ? OR json_array_contains(provides, ?)" |  | ||||||
| 			args = []any{c.Args().First(), c.Args().First()} |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		result, err := db.GetPkgs(ctx, where, args...) |  | ||||||
| 		if err != nil { |  | ||||||
| 			log.Fatal("Error getting packages").Err(err).Send() |  | ||||||
| 		} |  | ||||||
| 		defer result.Close() |  | ||||||
|  |  | ||||||
| 		var installed map[string]string |  | ||||||
| 		if c.Bool("installed") { |  | ||||||
| 			mgr := manager.Detect() |  | ||||||
| 			if mgr == nil { |  | ||||||
| 				log.Fatal("Unable to detect a supported package manager on the system").Send() |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			installed, err = mgr.ListInstalled(&manager.Opts{AsRoot: false}) |  | ||||||
| 			if err != nil { |  | ||||||
| 				log.Fatal("Error listing installed packages").Err(err).Send() |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		for result.Next() { |  | ||||||
| 			var pkg db.Package |  | ||||||
| 			err := result.StructScan(&pkg) |  | ||||||
| 			if err != nil { |  | ||||||
| 				return err | 				return err | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			if slices.Contains(config.Config(ctx).IgnorePkgUpdates, pkg.Name) { | 			ctx := c.Context | ||||||
| 				continue |  | ||||||
|  | 			deps, err := appbuilder. | ||||||
|  | 				New(ctx). | ||||||
|  | 				WithConfig(). | ||||||
|  | 				WithDB(). | ||||||
|  | 				WithManager(). | ||||||
|  | 				// autoPull only | ||||||
|  | 				WithRepos(). | ||||||
|  | 				WithDistroInfo(). | ||||||
|  | 				Build() | ||||||
|  | 			if err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  | 			defer deps.Defer() | ||||||
|  |  | ||||||
|  | 			cfg := deps.Cfg | ||||||
|  | 			db := deps.DB | ||||||
|  | 			mgr := deps.Manager | ||||||
|  | 			info := deps.Info | ||||||
|  |  | ||||||
|  | 			if c.Bool("upgradable") { | ||||||
|  | 				updates, err := checkForUpdates(ctx, mgr, db, info) | ||||||
|  | 				if err != nil { | ||||||
|  | 					return cliutils.FormatCliExit(gotext.Get("Error getting packages for upgrade"), err) | ||||||
|  | 				} | ||||||
|  | 				if len(updates) == 0 { | ||||||
|  | 					slog.Info(gotext.Get("No packages for upgrade")) | ||||||
|  | 					return nil | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				format := c.String("format") | ||||||
|  | 				if format == "" { | ||||||
|  | 					format = "{{.Package.Repository}}/{{.Package.Name}} {{.FromVersion}} -> {{.ToVersion}}\n" | ||||||
|  | 				} | ||||||
|  | 				tmpl, err := template.New("format").Parse(format) | ||||||
|  | 				if err != nil { | ||||||
|  | 					return cliutils.FormatCliExit(gotext.Get("Error parsing format template"), err) | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				for _, updateInfo := range updates { | ||||||
|  | 					err = tmpl.Execute(os.Stdout, updateInfo) | ||||||
|  | 					if err != nil { | ||||||
|  | 						return cliutils.FormatCliExit(gotext.Get("Error executing template"), err) | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				return nil | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			version := pkg.Version | 			// TODO: refactor code below | ||||||
|  |  | ||||||
|  | 			where := "true" | ||||||
|  | 			args := []any(nil) | ||||||
|  | 			if c.NArg() > 0 { | ||||||
|  | 				where = "name LIKE ? OR json_array_contains(provides, ?)" | ||||||
|  | 				args = []any{c.Args().First(), c.Args().First()} | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			result, err := db.GetPkgs(ctx, where, args...) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return cliutils.FormatCliExit(gotext.Get("Error getting packages"), err) | ||||||
|  | 			} | ||||||
|  | 			defer result.Close() | ||||||
|  |  | ||||||
|  | 			installedAlrPackages := map[string]string{} | ||||||
| 			if c.Bool("installed") { | 			if c.Bool("installed") { | ||||||
| 				instVersion, ok := installed[pkg.Name] | 				mgr := manager.Detect() | ||||||
| 				if !ok { | 				if mgr == nil { | ||||||
| 					continue | 					return cli.Exit(gotext.Get("Unable to detect a supported package manager on the system"), 1) | ||||||
| 				} else { | 				} | ||||||
| 					version = instVersion |  | ||||||
|  | 				installed, err := mgr.ListInstalled(&manager.Opts{}) | ||||||
|  | 				if err != nil { | ||||||
|  | 					slog.Error(gotext.Get("Error listing installed packages"), "err", err) | ||||||
|  | 					return cli.Exit(err, 1) | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				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 | ||||||
|  | 					} | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			fmt.Printf("%s/%s %s\n", pkg.Repository, pkg.Name, version) | 			for result.Next() { | ||||||
| 		} | 				var pkg database.Package | ||||||
|  | 				err := result.StructScan(&pkg) | ||||||
|  | 				if err != nil { | ||||||
|  | 					return cli.Exit(err, 1) | ||||||
|  | 				} | ||||||
|  |  | ||||||
| 		if err != nil { | 				if slices.Contains(cfg.IgnorePkgUpdates(), pkg.Name) { | ||||||
| 			log.Fatal("Error iterating over packages").Err(err).Send() | 					continue | ||||||
| 		} | 				} | ||||||
|  |  | ||||||
| 		return nil | 				type packageInfo struct { | ||||||
| 	}, | 					Package *database.Package | ||||||
|  | 					Version string | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				pkgInfo := &packageInfo{} | ||||||
|  | 				pkgInfo.Package = &pkg | ||||||
|  | 				pkgInfo.Version = pkg.Version | ||||||
|  | 				if c.Bool("installed") { | ||||||
|  | 					instVersion, ok := installedAlrPackages[fmt.Sprintf("%s/%s", pkg.Repository, pkg.Name)] | ||||||
|  | 					if !ok { | ||||||
|  | 						continue | ||||||
|  | 					} else { | ||||||
|  | 						pkgInfo.Version = instVersion | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				format := c.String("format") | ||||||
|  | 				if format == "" { | ||||||
|  | 					format = "{{.Package.Repository}}/{{.Package.Name}} {{.Version}}\n" | ||||||
|  | 				} | ||||||
|  | 				tmpl, err := template.New("format").Parse(format) | ||||||
|  | 				if err != nil { | ||||||
|  | 					return cliutils.FormatCliExit(gotext.Get("Error parsing format template"), err) | ||||||
|  | 				} | ||||||
|  | 				err = tmpl.Execute(os.Stdout, pkgInfo) | ||||||
|  | 				if err != nil { | ||||||
|  | 					return cliutils.FormatCliExit(gotext.Get("Error executing template"), err) | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			return nil | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										211
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										211
									
								
								main.go
									
									
									
									
									
								
							| @@ -1,115 +1,152 @@ | |||||||
| /* | // This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan. | ||||||
|  * ALR - Any Linux Repository | // It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors. | ||||||
|  * Copyright (C) 2024 Евгений Храмов | // | ||||||
|  * | // ALR - Any Linux Repository | ||||||
|  * This program is free software: you can redistribute it and/or modify | // Copyright (C) 2025 The ALR Authors | ||||||
|  * it under the terms of the GNU General Public License as published by | // | ||||||
|  * the Free Software Foundation, either version 3 of the License, or | // This program is free software: you can redistribute it and/or modify | ||||||
|  * (at your option) any later version. | // it under the terms of the GNU General Public License as published by | ||||||
|  * | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  * This program is distributed in the hope that it will be useful, | // (at your option) any later version. | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | // | ||||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | // This program is distributed in the hope that it will be useful, | ||||||
|  * GNU General Public License for more details. | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  * | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  * You should have received a copy of the GNU General Public License | // GNU General Public License for more details. | ||||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | // | ||||||
|  */ | // 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 | package main | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
|  | 	"log/slog" | ||||||
| 	"os" | 	"os" | ||||||
| 	"os/signal" | 	"os/signal" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"syscall" | 	"syscall" | ||||||
|  |  | ||||||
|  | 	"github.com/leonelquinteros/gotext" | ||||||
| 	"github.com/mattn/go-isatty" | 	"github.com/mattn/go-isatty" | ||||||
| 	"github.com/urfave/cli/v2" | 	"github.com/urfave/cli/v2" | ||||||
| 	"go.elara.ws/logger" |  | ||||||
| 	"plemya-x.ru/alr/internal/config" | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils" | ||||||
| 	"plemya-x.ru/alr/internal/db" | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/config" | ||||||
| 	"plemya-x.ru/alr/internal/translations" | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/translations" | ||||||
| 	"plemya-x.ru/alr/pkg/loggerctx" | 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager" | ||||||
| 	"plemya-x.ru/alr/pkg/manager" |  | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/logger" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| var app = &cli.App{ | func VersionCmd() *cli.Command { | ||||||
| 	Name:  "alr", | 	return &cli.Command{ | ||||||
| 	Usage: "Any Linux Repository", | 		Name:  "version", | ||||||
| 	Flags: []cli.Flag{ | 		Usage: gotext.Get("Print the current ALR version and exit"), | ||||||
| 		&cli.StringFlag{ | 		Action: func(ctx *cli.Context) error { | ||||||
| 			Name:    "pm-args", | 			println(config.Version) | ||||||
| 			Aliases: []string{"P"}, | 			return nil | ||||||
| 			Usage:   "Arguments to be passed on to the package manager", |  | ||||||
| 		}, | 		}, | ||||||
| 		&cli.BoolFlag{ | 	} | ||||||
| 			Name:    "interactive", |  | ||||||
| 			Aliases: []string{"i"}, |  | ||||||
| 			Value:   isatty.IsTerminal(os.Stdin.Fd()), |  | ||||||
| 			Usage:   "Enable interactive questions and prompts", |  | ||||||
| 		}, |  | ||||||
| 	}, |  | ||||||
| 	Commands: []*cli.Command{ |  | ||||||
| 		installCmd, |  | ||||||
| 		removeCmd, |  | ||||||
| 		upgradeCmd, |  | ||||||
| 		infoCmd, |  | ||||||
| 		listCmd, |  | ||||||
| 		buildCmd, |  | ||||||
| 		addrepoCmd, |  | ||||||
| 		removerepoCmd, |  | ||||||
| 		refreshCmd, |  | ||||||
| 		fixCmd, |  | ||||||
| 		genCmd, |  | ||||||
| 		helperCmd, |  | ||||||
| 		versionCmd, |  | ||||||
| 	}, |  | ||||||
| 	Before: func(c *cli.Context) error { |  | ||||||
| 		ctx := c.Context |  | ||||||
| 		log := loggerctx.From(ctx) |  | ||||||
|  |  | ||||||
| 		cmd := c.Args().First() |  | ||||||
| 		if cmd != "helper" && !config.Config(ctx).Unsafe.AllowRunAsRoot && os.Geteuid() == 0 { |  | ||||||
| 			log.Fatal("Running ALR as root is forbidden as it may cause catastrophic damage to your system").Send() |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		if trimmed := strings.TrimSpace(c.String("pm-args")); trimmed != "" { |  | ||||||
| 			args := strings.Split(trimmed, " ") |  | ||||||
| 			manager.Args = append(manager.Args, args...) |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		return nil |  | ||||||
| 	}, |  | ||||||
| 	After: func(ctx *cli.Context) error { |  | ||||||
| 		return db.Close() |  | ||||||
| 	}, |  | ||||||
| 	EnableBashCompletion: true, |  | ||||||
| } | } | ||||||
|  |  | ||||||
| var versionCmd = &cli.Command{ | func GetApp() *cli.App { | ||||||
| 	Name:  "version", | 	return &cli.App{ | ||||||
| 	Usage: "Print the current ALR version and exit", | 		Name:  "alr", | ||||||
| 	Action: func(ctx *cli.Context) error { | 		Usage: "Any Linux Repository", | ||||||
| 		println(config.Version) | 		Flags: []cli.Flag{ | ||||||
| 		return nil | 			&cli.StringFlag{ | ||||||
| 	}, | 				Name:    "pm-args", | ||||||
|  | 				Aliases: []string{"P"}, | ||||||
|  | 				Usage:   gotext.Get("Arguments to be passed on to the package manager"), | ||||||
|  | 			}, | ||||||
|  | 			&cli.BoolFlag{ | ||||||
|  | 				Name:    "interactive", | ||||||
|  | 				Aliases: []string{"i"}, | ||||||
|  | 				Value:   isatty.IsTerminal(os.Stdin.Fd()), | ||||||
|  | 				Usage:   gotext.Get("Enable interactive questions and prompts"), | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		Commands: []*cli.Command{ | ||||||
|  | 			InstallCmd(), | ||||||
|  | 			RemoveCmd(), | ||||||
|  | 			UpgradeCmd(), | ||||||
|  | 			InfoCmd(), | ||||||
|  | 			ListCmd(), | ||||||
|  | 			BuildCmd(), | ||||||
|  | 			LegacyAddRepoCmd(), | ||||||
|  | 			LegacyRemoveRepoCmd(), | ||||||
|  | 			RefreshCmd(), | ||||||
|  | 			FixCmd(), | ||||||
|  | 			GenCmd(), | ||||||
|  | 			HelperCmd(), | ||||||
|  | 			VersionCmd(), | ||||||
|  | 			SearchCmd(), | ||||||
|  | 			RepoCmd(), | ||||||
|  | 			// Internal commands | ||||||
|  | 			InternalBuildCmd(), | ||||||
|  | 			InternalInstallCmd(), | ||||||
|  | 			InternalMountCmd(), | ||||||
|  | 		}, | ||||||
|  | 		Before: func(c *cli.Context) error { | ||||||
|  | 			if trimmed := strings.TrimSpace(c.String("pm-args")); trimmed != "" { | ||||||
|  | 				args := strings.Split(trimmed, " ") | ||||||
|  | 				manager.Args = append(manager.Args, args...) | ||||||
|  | 			} | ||||||
|  | 			return nil | ||||||
|  | 		}, | ||||||
|  | 		EnableBashCompletion: true, | ||||||
|  | 		ExitErrHandler: func(cCtx *cli.Context, err error) { | ||||||
|  | 			cliutils.HandleExitCoder(err) | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func setLogLevel(newLevel string) { | ||||||
|  | 	level := slog.LevelInfo | ||||||
|  | 	switch newLevel { | ||||||
|  | 	case "DEBUG": | ||||||
|  | 		level = slog.LevelDebug | ||||||
|  | 	case "INFO": | ||||||
|  | 		level = slog.LevelInfo | ||||||
|  | 	case "WARN": | ||||||
|  | 		level = slog.LevelWarn | ||||||
|  | 	case "ERROR": | ||||||
|  | 		level = slog.LevelError | ||||||
|  | 	} | ||||||
|  | 	logger, ok := slog.Default().Handler().(*logger.Logger) | ||||||
|  | 	if !ok { | ||||||
|  | 		panic("unexpected") | ||||||
|  | 	} | ||||||
|  | 	logger.SetLevel(level) | ||||||
| } | } | ||||||
|  |  | ||||||
| func main() { | func main() { | ||||||
| 	ctx := context.Background() | 	logger.SetupDefault() | ||||||
| 	log := translations.NewLogger(ctx, logger.NewCLI(os.Stderr), config.Language(ctx)) | 	setLogLevel(os.Getenv("ALR_LOG_LEVEL")) | ||||||
| 	ctx = loggerctx.With(ctx, log) | 	translations.Setup() | ||||||
|  |  | ||||||
| 	// Set the root command to the one set in the ALR config | 	ctx := context.Background() | ||||||
| 	manager.DefaultRootCmd = config.Config(ctx).RootCmd |  | ||||||
|  | 	app := GetApp() | ||||||
|  | 	cfg := config.New() | ||||||
|  | 	err := cfg.Load() | ||||||
|  | 	if err != nil { | ||||||
|  | 		slog.Error(gotext.Get("Error loading config"), "err", err) | ||||||
|  | 		os.Exit(1) | ||||||
|  | 	} | ||||||
|  | 	setLogLevel(cfg.LogLevel()) | ||||||
|  |  | ||||||
| 	ctx, cancel := signal.NotifyContext(ctx, syscall.SIGINT, syscall.SIGTERM) | 	ctx, cancel := signal.NotifyContext(ctx, syscall.SIGINT, syscall.SIGTERM) | ||||||
| 	defer cancel() | 	defer cancel() | ||||||
|  |  | ||||||
| 	err := app.RunContext(ctx, os.Args) | 	// Make the application more internationalized | ||||||
|  | 	cli.AppHelpTemplate = cliutils.GetAppCliTemplate() | ||||||
|  | 	cli.CommandHelpTemplate = cliutils.GetCommandHelpTemplate() | ||||||
|  | 	cli.HelpFlag.(*cli.BoolFlag).Usage = gotext.Get("Show help") | ||||||
|  |  | ||||||
|  | 	err = app.RunContext(ctx, os.Args) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Error("Error while running app").Err(err).Send() | 		slog.Error(gotext.Get("Error while running app"), "err", err) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										83
									
								
								old-files
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								old-files
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,83 @@ | |||||||
|  | ./.github/FUNDING.yml | ||||||
|  | ./.gitignore | ||||||
|  | ./.goreleaser.yaml | ||||||
|  | ./.woodpecker.yml | ||||||
|  | ./LICENSE | ||||||
|  | ./Makefile | ||||||
|  | ./README.md | ||||||
|  | ./assets/logo.png | ||||||
|  | ./build.go | ||||||
|  | ./docs/README.md | ||||||
|  | ./docs/configuration.md | ||||||
|  | ./docs/packages/README.md | ||||||
|  | ./docs/packages/adding-packages.md | ||||||
|  | ./docs/packages/build-scripts.md | ||||||
|  | ./docs/packages/conventions.md | ||||||
|  | ./docs/usage.md | ||||||
|  | ./fix.go | ||||||
|  | ./gen.go | ||||||
|  | ./go.mod | ||||||
|  | ./go.sum | ||||||
|  | ./helper.go | ||||||
|  | ./info.go | ||||||
|  | ./install.go | ||||||
|  | ./internal/cliutils/prompt.go | ||||||
|  | ./internal/config/config.go | ||||||
|  | ./internal/config/lang.go | ||||||
|  | ./internal/config/paths.go | ||||||
|  | ./internal/config/version.go | ||||||
|  | ./internal/cpu/cpu.go | ||||||
|  | ./internal/db/db.go | ||||||
|  | ./internal/db/db_test.go | ||||||
|  | ./internal/dl/dl.go | ||||||
|  | ./internal/dl/file.go | ||||||
|  | ./internal/dl/git.go | ||||||
|  | ./internal/dl/torrent.go | ||||||
|  | ./internal/dlcache/dlcache.go | ||||||
|  | ./internal/dlcache/dlcache_test.go | ||||||
|  | ./internal/osutils/move.go | ||||||
|  | ./internal/overrides/overrides.go | ||||||
|  | ./internal/overrides/overrides_test.go | ||||||
|  | ./internal/pager/highlighting.go | ||||||
|  | ./internal/pager/pager.go | ||||||
|  | ./internal/shutils/decoder/decoder.go | ||||||
|  | ./internal/shutils/decoder/decoder_test.go | ||||||
|  | ./internal/shutils/handlers/exec.go | ||||||
|  | ./internal/shutils/handlers/exec_test.go | ||||||
|  | ./internal/shutils/handlers/fakeroot.go | ||||||
|  | ./internal/shutils/handlers/nop.go | ||||||
|  | ./internal/shutils/handlers/nop_test.go | ||||||
|  | ./internal/shutils/handlers/restricted.go | ||||||
|  | ./internal/shutils/helpers/helpers.go | ||||||
|  | ./internal/translations/files/lure.en.toml | ||||||
|  | ./internal/translations/files/lure.ru.toml | ||||||
|  | ./internal/translations/translations.go | ||||||
|  | ./internal/types/build.go | ||||||
|  | ./internal/types/config.go | ||||||
|  | ./internal/types/repo.go | ||||||
|  | ./list.go | ||||||
|  | ./main.go | ||||||
|  | ./pkg/build/build.go | ||||||
|  | ./pkg/build/install.go | ||||||
|  | ./pkg/distro/osrelease.go | ||||||
|  | ./pkg/gen/funcs.go | ||||||
|  | ./pkg/gen/pip.go | ||||||
|  | ./pkg/gen/tmpls/pip.tmpl.sh | ||||||
|  | ./pkg/loggerctx/log.go | ||||||
|  | ./pkg/manager/apk.go | ||||||
|  | ./pkg/manager/apt.go | ||||||
|  | ./pkg/manager/dnf.go | ||||||
|  | ./pkg/manager/managers.go | ||||||
|  | ./pkg/manager/pacman.go | ||||||
|  | ./pkg/manager/yum.go | ||||||
|  | ./pkg/manager/zypper.go | ||||||
|  | ./pkg/repos/find.go | ||||||
|  | ./pkg/repos/find_test.go | ||||||
|  | ./pkg/repos/pull.go | ||||||
|  | ./pkg/repos/pull_test.go | ||||||
|  | ./pkg/search/search.go | ||||||
|  | ./repo.go | ||||||
|  | ./scripts/completion/bash | ||||||
|  | ./scripts/completion/zsh | ||||||
|  | ./scripts/install.sh | ||||||
|  | ./upgrade.go | ||||||
							
								
								
									
										1404
									
								
								pkg/build/build.go
									
									
									
									
									
								
							
							
						
						
									
										1404
									
								
								pkg/build/build.go
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										286
									
								
								pkg/build/build_internal_test.need-to-update
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										286
									
								
								pkg/build/build_internal_test.need-to-update
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,286 @@ | |||||||
|  | // 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" | ||||||
|  | 	"fmt" | ||||||
|  | 	"os" | ||||||
|  | 	"strings" | ||||||
|  | 	"testing" | ||||||
|  |  | ||||||
|  | 	"github.com/stretchr/testify/assert" | ||||||
|  | 	"mvdan.cc/sh/v3/syntax" | ||||||
|  |  | ||||||
|  | 	"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/types" | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro" | ||||||
|  | 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type TestPackageFinder struct { | ||||||
|  | 	FindPkgsFunc func(ctx context.Context, pkgs []string) (map[string][]db.Package, []string, error) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (pf *TestPackageFinder) FindPkgs(ctx context.Context, pkgs []string) (map[string][]db.Package, []string, error) { | ||||||
|  | 	if pf.FindPkgsFunc != nil { | ||||||
|  | 		return pf.FindPkgsFunc(ctx, pkgs) | ||||||
|  | 	} | ||||||
|  | 	return map[string][]db.Package{}, []string{}, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type TestManager struct { | ||||||
|  | 	NameFunc          func() string | ||||||
|  | 	FormatFunc        func() string | ||||||
|  | 	ExistsFunc        func() bool | ||||||
|  | 	SetRootCmdFunc    func(cmd string) | ||||||
|  | 	SyncFunc          func(opts *manager.Opts) error | ||||||
|  | 	InstallFunc       func(opts *manager.Opts, pkgs ...string) error | ||||||
|  | 	RemoveFunc        func(opts *manager.Opts, pkgs ...string) error | ||||||
|  | 	UpgradeFunc       func(opts *manager.Opts, pkgs ...string) error | ||||||
|  | 	InstallLocalFunc  func(opts *manager.Opts, files ...string) error | ||||||
|  | 	UpgradeAllFunc    func(opts *manager.Opts) error | ||||||
|  | 	ListInstalledFunc func(opts *manager.Opts) (map[string]string, error) | ||||||
|  | 	IsInstalledFunc   func(pkg string) (bool, error) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *TestManager) Name() string { | ||||||
|  | 	if m.NameFunc != nil { | ||||||
|  | 		return m.NameFunc() | ||||||
|  | 	} | ||||||
|  | 	return "TestManager" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *TestManager) Format() string { | ||||||
|  | 	if m.FormatFunc != nil { | ||||||
|  | 		return m.FormatFunc() | ||||||
|  | 	} | ||||||
|  | 	return "testpkg" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *TestManager) Exists() bool { | ||||||
|  | 	if m.ExistsFunc != nil { | ||||||
|  | 		return m.ExistsFunc() | ||||||
|  | 	} | ||||||
|  | 	return true | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *TestManager) SetRootCmd(cmd string) { | ||||||
|  | 	if m.SetRootCmdFunc != nil { | ||||||
|  | 		m.SetRootCmdFunc(cmd) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *TestManager) Sync(opts *manager.Opts) error { | ||||||
|  | 	if m.SyncFunc != nil { | ||||||
|  | 		return m.SyncFunc(opts) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *TestManager) Install(opts *manager.Opts, pkgs ...string) error { | ||||||
|  | 	if m.InstallFunc != nil { | ||||||
|  | 		return m.InstallFunc(opts, pkgs...) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *TestManager) Remove(opts *manager.Opts, pkgs ...string) error { | ||||||
|  | 	if m.RemoveFunc != nil { | ||||||
|  | 		return m.RemoveFunc(opts, pkgs...) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *TestManager) Upgrade(opts *manager.Opts, pkgs ...string) error { | ||||||
|  | 	if m.UpgradeFunc != nil { | ||||||
|  | 		return m.UpgradeFunc(opts, pkgs...) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *TestManager) InstallLocal(opts *manager.Opts, files ...string) error { | ||||||
|  | 	if m.InstallLocalFunc != nil { | ||||||
|  | 		return m.InstallLocalFunc(opts, files...) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *TestManager) UpgradeAll(opts *manager.Opts) error { | ||||||
|  | 	if m.UpgradeAllFunc != nil { | ||||||
|  | 		return m.UpgradeAllFunc(opts) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *TestManager) ListInstalled(opts *manager.Opts) (map[string]string, error) { | ||||||
|  | 	if m.ListInstalledFunc != nil { | ||||||
|  | 		return m.ListInstalledFunc(opts) | ||||||
|  | 	} | ||||||
|  | 	return map[string]string{}, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *TestManager) IsInstalled(pkg string) (bool, error) { | ||||||
|  | 	if m.IsInstalledFunc != nil { | ||||||
|  | 		return m.IsInstalledFunc(pkg) | ||||||
|  | 	} | ||||||
|  | 	return true, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type TestConfig struct{} | ||||||
|  |  | ||||||
|  | func (c *TestConfig) PagerStyle() string { | ||||||
|  | 	return "native" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *TestConfig) GetPaths() *config.Paths { | ||||||
|  | 	return &config.Paths{ | ||||||
|  | 		CacheDir: "/tmp", | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestExecuteFirstPassIsSecure(t *testing.T) { | ||||||
|  | 	cfg := &TestConfig{} | ||||||
|  | 	pf := &TestPackageFinder{} | ||||||
|  | 	info := &distro.OSRelease{} | ||||||
|  | 	m := &TestManager{} | ||||||
|  |  | ||||||
|  | 	opts := types.BuildOpts{ | ||||||
|  | 		Manager:     m, | ||||||
|  | 		Interactive: false, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ctx := context.Background() | ||||||
|  |  | ||||||
|  | 	b := NewBuilder( | ||||||
|  | 		ctx, | ||||||
|  | 		opts, | ||||||
|  | 		pf, | ||||||
|  | 		info, | ||||||
|  | 		cfg, | ||||||
|  | 	) | ||||||
|  |  | ||||||
|  | 	tmpFile, err := os.CreateTemp("", "testfile-") | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 	tmpFilePath := tmpFile.Name() | ||||||
|  | 	defer os.Remove(tmpFilePath) | ||||||
|  |  | ||||||
|  | 	_, err = os.Stat(tmpFilePath) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  |  | ||||||
|  | 	testScript := fmt.Sprintf(`name='test' | ||||||
|  | version=1.0.0 | ||||||
|  | release=1 | ||||||
|  | rm -f %s`, tmpFilePath) | ||||||
|  |  | ||||||
|  | 	fl, err := syntax.NewParser().Parse(strings.NewReader(testScript), "alr.sh") | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  |  | ||||||
|  | 	_, _, err = b.executeFirstPass(fl) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  |  | ||||||
|  | 	_, err = os.Stat(tmpFilePath) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestExecuteFirstPassIsCorrect(t *testing.T) { | ||||||
|  | 	type testCase struct { | ||||||
|  | 		Name     string | ||||||
|  | 		Script   string | ||||||
|  | 		Opts     types.BuildOpts | ||||||
|  | 		Expected func(t *testing.T, vars []*types.BuildVars) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, tc := range []testCase{{ | ||||||
|  | 		Name: "single package", | ||||||
|  | 		Script: `name='test' | ||||||
|  | version='1.0.0' | ||||||
|  | release=1 | ||||||
|  | epoch=2 | ||||||
|  | desc="Test package" | ||||||
|  | homepage='https://example.com' | ||||||
|  | maintainer='Ivan Ivanov' | ||||||
|  | `, | ||||||
|  | 		Opts: types.BuildOpts{ | ||||||
|  | 			Manager:     &TestManager{}, | ||||||
|  | 			Interactive: false, | ||||||
|  | 		}, | ||||||
|  | 		Expected: func(t *testing.T, vars []*types.BuildVars) { | ||||||
|  | 			assert.Equal(t, 1, len(vars)) | ||||||
|  | 			assert.Equal(t, vars[0].Name, "test") | ||||||
|  | 			assert.Equal(t, vars[0].Version, "1.0.0") | ||||||
|  | 			assert.Equal(t, vars[0].Release, int(1)) | ||||||
|  | 			assert.Equal(t, vars[0].Epoch, uint(2)) | ||||||
|  | 			assert.Equal(t, vars[0].Description, "Test package") | ||||||
|  | 		}, | ||||||
|  | 	}, { | ||||||
|  | 		Name: "multiple packages", | ||||||
|  | 		Script: `name=( | ||||||
|  | 	foo | ||||||
|  | 	bar | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | version='0.0.1' | ||||||
|  | release=1 | ||||||
|  | epoch=2 | ||||||
|  | desc="Test package" | ||||||
|  |  | ||||||
|  | meta_foo() { | ||||||
|  | 	desc="Foo package" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | meta_bar() { | ||||||
|  |  | ||||||
|  | } | ||||||
|  | `, | ||||||
|  | 		Opts: types.BuildOpts{ | ||||||
|  | 			Packages:    []string{"foo"}, | ||||||
|  | 			Manager:     &TestManager{}, | ||||||
|  | 			Interactive: false, | ||||||
|  | 		}, | ||||||
|  | 		Expected: func(t *testing.T, vars []*types.BuildVars) { | ||||||
|  | 			assert.Equal(t, 1, len(vars)) | ||||||
|  | 			assert.Equal(t, vars[0].Name, "foo") | ||||||
|  | 			assert.Equal(t, vars[0].Description, "Foo package") | ||||||
|  | 		}, | ||||||
|  | 	}} { | ||||||
|  | 		t.Run(tc.Name, func(t *testing.T) { | ||||||
|  | 			cfg := &TestConfig{} | ||||||
|  | 			pf := &TestPackageFinder{} | ||||||
|  | 			info := &distro.OSRelease{} | ||||||
|  |  | ||||||
|  | 			ctx := context.Background() | ||||||
|  |  | ||||||
|  | 			b := NewBuilder( | ||||||
|  | 				ctx, | ||||||
|  | 				tc.Opts, | ||||||
|  | 				pf, | ||||||
|  | 				info, | ||||||
|  | 				cfg, | ||||||
|  | 			) | ||||||
|  |  | ||||||
|  | 			fl, err := syntax.NewParser().Parse(strings.NewReader(tc.Script), "alr.sh") | ||||||
|  | 			assert.NoError(t, err) | ||||||
|  |  | ||||||
|  | 			_, allVars, err := b.scriptExecutor.ExecuteSecondPass(fl) | ||||||
|  | 			assert.NoError(t, err) | ||||||
|  |  | ||||||
|  | 			tc.Expected(t, allVars) | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user