Compare commits
	
		
			10 Commits
		
	
	
		
			v0.0.13
			...
			a785df1ec6
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| a785df1ec6 | |||
| 131f455eff | |||
| 1e52d30f4c | |||
| 40ec0ac6e1 | |||
| 443e481561 | |||
| c892310f69 | |||
| 750513b119 | |||
| ce1836b646 | |||
| 56b9f3211c | |||
| fae63e28f9 | 
							
								
								
									
										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 | ||||
							
								
								
									
										11
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								Makefile
									
									
									
									
									
								
							| @@ -1,6 +1,6 @@ | ||||
| NAME := alr | ||||
| GIT_VERSION = $(shell git describe --tags ) | ||||
|  | ||||
| IGNORE_ROOT_CHECK ?= 0 | ||||
| DESTDIR ?= | ||||
| PREFIX ?= /usr/local | ||||
| BIN := ./$(NAME) | ||||
| @@ -24,8 +24,9 @@ $(BIN): | ||||
| 	go build -ldflags="-X 'gitea.plemya-x.ru/Plemya-x/ALR/internal/config.Version=$(GIT_VERSION)'" -o $@ | ||||
|  | ||||
| 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 "Set IGNORE_ROOT_CHECK=1 to override" 1>&2; \ | ||||
| 		exit 1; \ | ||||
| 	fi | ||||
|  | ||||
| @@ -54,7 +55,7 @@ uninstall: | ||||
| clean clear: | ||||
| 	rm -f $(BIN) | ||||
|  | ||||
| OLD_FILES=$$(< old-files) | ||||
| 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) | ||||
| @@ -76,7 +77,9 @@ test-coverage: | ||||
| update-deps-cve: | ||||
| 	bash scripts/update-deps-cve.sh | ||||
|  | ||||
| e2e-test: clean build | ||||
| 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 ./... | ||||
| @@ -12,7 +12,7 @@ | ||||
|     <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">100.00%</text> | ||||
|         <text x="100" y="14">100.00%</text> | ||||
|         <text x="100" y="15" fill="#010101" fill-opacity=".3">96.00%</text> | ||||
|         <text x="100" y="14">96.00%</text> | ||||
|     </g> | ||||
| </svg> | ||||
|   | ||||
| Before Width: | Height: | Size: 942 B After Width: | Height: | Size: 940 B | 
							
								
								
									
										6
									
								
								build.go
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								build.go
									
									
									
									
									
								
							| @@ -118,7 +118,11 @@ func BuildCmd() *cli.Command { | ||||
| 					return cliutils.FormatCliExit(gotext.Get("Cannot get absolute script path"), err) | ||||
| 				} | ||||
|  | ||||
| 				packages = append(packages, c.String("script-package")) | ||||
| 				subpackage := c.String("subpackage") | ||||
|  | ||||
| 				if subpackage != "" { | ||||
| 					packages = append(packages, subpackage) | ||||
| 				} | ||||
|  | ||||
| 				scriptArgs = &build.BuildPackageFromScriptArgs{ | ||||
| 					Script:    script, | ||||
|   | ||||
| @@ -175,6 +175,11 @@ func dockerMultipleRun(t *testing.T, name string, ids []string, f func(t *testin | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| 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, | ||||
| @@ -188,3 +193,5 @@ func runTestCommands(t *testing.T, r e2e.Runnable, timeout time.Duration, expect | ||||
| 	) | ||||
| 	assert.NoError(t, err) | ||||
| } | ||||
|  | ||||
| const REPO_FOR_E2E_TESTS = "https://gitea.plemya-x.ru/Maks1mS/repo-for-tests.git" | ||||
|   | ||||
							
								
								
									
										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") | ||||
| 		}, | ||||
| 	) | ||||
| } | ||||
							
								
								
									
										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) | ||||
| 		}, | ||||
| 	) | ||||
| } | ||||
| @@ -21,6 +21,7 @@ import ( | ||||
| 	"fmt" | ||||
| 	"log/slog" | ||||
|  | ||||
| 	"github.com/leonelquinteros/gotext" | ||||
| 	"github.com/urfave/cli/v2" | ||||
| ) | ||||
|  | ||||
| @@ -61,3 +62,11 @@ func FormatCliExitWithCode(msg string, err error, exitCode int) cli.ExitCoder { | ||||
| 	} | ||||
| 	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, | ||||
| 		), | ||||
| 	) | ||||
| } | ||||
|   | ||||
| @@ -104,7 +104,7 @@ func Resolve(info *distro.OSRelease, opts *Opts) ([]string, error) { | ||||
| 	out = append(out, opts.Name) | ||||
|  | ||||
| 	for index, item := range out { | ||||
| 		out[index] = strings.TrimPrefix(strings.ReplaceAll(item, "-", "_"), "_") | ||||
| 		out[index] = strings.TrimPrefix(item, "_") | ||||
| 	} | ||||
|  | ||||
| 	return out, nil | ||||
|   | ||||
| @@ -38,23 +38,23 @@ msgstr "" | ||||
| msgid "Cannot get absolute script path" | ||||
| msgstr "" | ||||
|  | ||||
| #: build.go:148 | ||||
| #: build.go:152 | ||||
| msgid "Package not found" | ||||
| msgstr "" | ||||
|  | ||||
| #: build.go:161 | ||||
| #: build.go:165 | ||||
| msgid "Nothing to build" | ||||
| msgstr "" | ||||
|  | ||||
| #: build.go:218 | ||||
| #: build.go:222 | ||||
| msgid "Error building package" | ||||
| msgstr "" | ||||
|  | ||||
| #: build.go:225 | ||||
| #: build.go:229 | ||||
| msgid "Error moving the package" | ||||
| msgstr "" | ||||
|  | ||||
| #: build.go:229 | ||||
| #: build.go:233 | ||||
| msgid "Done" | ||||
| msgstr "" | ||||
|  | ||||
| @@ -278,6 +278,12 @@ msgstr "" | ||||
| 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 "" | ||||
| @@ -327,10 +333,30 @@ msgstr "" | ||||
| msgid "You need to be root to perform this action" | ||||
| msgstr "" | ||||
|  | ||||
| #: list.go:41 | ||||
| #: 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 "" | ||||
| @@ -343,11 +369,11 @@ msgstr "" | ||||
| msgid "Enable interactive questions and prompts" | ||||
| msgstr "" | ||||
|  | ||||
| #: main.go:145 | ||||
| #: main.go:146 | ||||
| msgid "Show help" | ||||
| msgstr "" | ||||
|  | ||||
| #: main.go:149 | ||||
| #: main.go:150 | ||||
| msgid "Error while running app" | ||||
| msgstr "" | ||||
|  | ||||
| @@ -397,82 +423,94 @@ msgstr "" | ||||
| msgid "AutoReq is not implemented for this package format, so it's skipped" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/script_executor.go:237 | ||||
| #: pkg/build/script_executor.go:241 | ||||
| msgid "Building package metadata" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/script_executor.go:368 | ||||
| #: pkg/build/script_executor.go:372 | ||||
| msgid "Executing prepare()" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/script_executor.go:377 | ||||
| #: pkg/build/script_executor.go:381 | ||||
| msgid "Executing build()" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/build/script_executor.go:406 pkg/build/script_executor.go:426 | ||||
| #: pkg/build/script_executor.go:410 pkg/build/script_executor.go:430 | ||||
| msgid "Executing %s()" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/repos/pull.go:80 | ||||
| #: pkg/repos/pull.go:77 | ||||
| msgid "Pulling repository" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/repos/pull.go:116 | ||||
| #: pkg/repos/pull.go:113 | ||||
| msgid "Repository up to date" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/repos/pull.go:207 | ||||
| #: pkg/repos/pull.go:204 | ||||
| msgid "Git repository does not appear to be a valid ALR repo" | ||||
| msgstr "" | ||||
|  | ||||
| #: pkg/repos/pull.go:223 | ||||
| #: 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 "" | ||||
|  | ||||
| #: repo.go:39 | ||||
| msgid "Add a new repository" | ||||
| #: refresh.go:30 | ||||
| msgid "Pull all repositories that have changed" | ||||
| msgstr "" | ||||
|  | ||||
| #: repo.go:46 | ||||
| msgid "Name of the new repo" | ||||
| #: repo.go:39 | ||||
| msgid "Manage repos" | ||||
| msgstr "" | ||||
|  | ||||
| #: repo.go:50 repo.go:220 | ||||
| msgid "Remove an existing repository" | ||||
| msgstr "" | ||||
|  | ||||
| #: repo.go:52 | ||||
| msgid "URL of the new repo" | ||||
| msgid "<name>" | ||||
| msgstr "" | ||||
|  | ||||
| #: repo.go:75 | ||||
| msgid "Repo \"%s\" already exists" | ||||
| #: repo.go:82 | ||||
| msgid "Repo \"%s\" does not exist" | ||||
| msgstr "" | ||||
|  | ||||
| #: repo.go:86 repo.go:159 | ||||
| #: repo.go:89 | ||||
| msgid "Error removing repo directory" | ||||
| msgstr "" | ||||
|  | ||||
| #: repo.go:93 repo.go:160 | ||||
| msgid "Error saving config" | ||||
| msgstr "" | ||||
|  | ||||
| #: repo.go:112 | ||||
| msgid "Remove an existing repository" | ||||
| msgstr "" | ||||
|  | ||||
| #: repo.go:119 | ||||
| msgid "Name of the repo to be deleted" | ||||
| msgstr "" | ||||
|  | ||||
| #: repo.go:148 | ||||
| msgid "Repo \"%s\" does not exist" | ||||
| msgstr "" | ||||
|  | ||||
| #: repo.go:155 | ||||
| msgid "Error removing repo directory" | ||||
| msgstr "" | ||||
|  | ||||
| #: repo.go:178 | ||||
| msgid "Error removing packages from database" | ||||
| msgstr "" | ||||
|  | ||||
| #: repo.go:189 | ||||
| msgid "Pull all repositories that have changed" | ||||
| #: 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 | ||||
| @@ -495,22 +533,10 @@ msgstr "" | ||||
| msgid "Search by provides" | ||||
| msgstr "" | ||||
|  | ||||
| #: search.go:71 | ||||
| msgid "Format output using a Go template" | ||||
| msgstr "" | ||||
|  | ||||
| #: search.go:130 | ||||
| msgid "Error while executing search" | ||||
| msgstr "" | ||||
|  | ||||
| #: search.go:138 | ||||
| msgid "Error parsing format template" | ||||
| msgstr "" | ||||
|  | ||||
| #: search.go:153 | ||||
| msgid "Error executing template" | ||||
| msgstr "" | ||||
|  | ||||
| #: upgrade.go:47 | ||||
| msgid "Upgrade all installed packages" | ||||
| msgstr "" | ||||
|   | ||||
| @@ -5,7 +5,7 @@ | ||||
| msgid "" | ||||
| msgstr "" | ||||
| "Project-Id-Version: unnamed project\n" | ||||
| "PO-Revision-Date: 2025-04-27 18:27+0300\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" | ||||
| @@ -45,23 +45,23 @@ msgstr "Ошибка при получении рабочего каталога | ||||
| msgid "Cannot get absolute script path" | ||||
| msgstr "Невозможно получить абсолютный путь к скрипту" | ||||
|  | ||||
| #: build.go:148 | ||||
| #: build.go:152 | ||||
| msgid "Package not found" | ||||
| msgstr "Пакет не найден" | ||||
|  | ||||
| #: build.go:161 | ||||
| #: build.go:165 | ||||
| msgid "Nothing to build" | ||||
| msgstr "Нечего собирать" | ||||
|  | ||||
| #: build.go:218 | ||||
| #: build.go:222 | ||||
| msgid "Error building package" | ||||
| msgstr "Ошибка при сборке пакета" | ||||
|  | ||||
| #: build.go:225 | ||||
| #: build.go:229 | ||||
| msgid "Error moving the package" | ||||
| msgstr "Ошибка при перемещении пакета" | ||||
|  | ||||
| #: build.go:229 | ||||
| #: build.go:233 | ||||
| msgid "Done" | ||||
| msgstr "Сделано" | ||||
|  | ||||
| @@ -285,6 +285,12 @@ msgstr "КАТЕГОРИЯ" | ||||
| 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 "Несоответствие версий базы данных; сброс настроек" | ||||
| @@ -335,10 +341,30 @@ msgstr "Вы должны быть членом %s чтобы выполнить | ||||
| msgid "You need to be root to perform this action" | ||||
| msgstr "Вы должны быть root чтобы выполнить это" | ||||
|  | ||||
| #: list.go:41 | ||||
| #: 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 и выйти" | ||||
| @@ -351,11 +377,11 @@ msgstr "Аргументы, которые будут переданы мене | ||||
| msgid "Enable interactive questions and prompts" | ||||
| msgstr "Включение интерактивных вопросов и запросов" | ||||
|  | ||||
| #: main.go:145 | ||||
| #: main.go:146 | ||||
| msgid "Show help" | ||||
| msgstr "Показать справку" | ||||
|  | ||||
| #: main.go:149 | ||||
| #: main.go:150 | ||||
| msgid "Error while running app" | ||||
| msgstr "Ошибка при запуске приложения" | ||||
|  | ||||
| @@ -409,35 +435,35 @@ msgid "AutoReq is not implemented for this package format, so it's skipped" | ||||
| msgstr "" | ||||
| "AutoReq не реализовано для этого формата пакета, поэтому будет пропущено" | ||||
|  | ||||
| #: pkg/build/script_executor.go:237 | ||||
| #: pkg/build/script_executor.go:241 | ||||
| msgid "Building package metadata" | ||||
| msgstr "Сборка метаданных пакета" | ||||
|  | ||||
| #: pkg/build/script_executor.go:368 | ||||
| #: pkg/build/script_executor.go:372 | ||||
| msgid "Executing prepare()" | ||||
| msgstr "Выполнение prepare()" | ||||
|  | ||||
| #: pkg/build/script_executor.go:377 | ||||
| #: pkg/build/script_executor.go:381 | ||||
| msgid "Executing build()" | ||||
| msgstr "Выполнение build()" | ||||
|  | ||||
| #: pkg/build/script_executor.go:406 pkg/build/script_executor.go:426 | ||||
| #: pkg/build/script_executor.go:410 pkg/build/script_executor.go:430 | ||||
| msgid "Executing %s()" | ||||
| msgstr "Выполнение %s()" | ||||
|  | ||||
| #: pkg/repos/pull.go:80 | ||||
| #: pkg/repos/pull.go:77 | ||||
| msgid "Pulling repository" | ||||
| msgstr "Скачивание репозитория" | ||||
|  | ||||
| #: pkg/repos/pull.go:116 | ||||
| #: pkg/repos/pull.go:113 | ||||
| msgid "Repository up to date" | ||||
| msgstr "Репозиторий уже обновлён" | ||||
|  | ||||
| #: pkg/repos/pull.go:207 | ||||
| #: pkg/repos/pull.go:204 | ||||
| msgid "Git repository does not appear to be a valid ALR repo" | ||||
| msgstr "Репозиторий Git не поддерживается репозиторием ALR" | ||||
|  | ||||
| #: pkg/repos/pull.go:223 | ||||
| #: 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." | ||||
| @@ -445,49 +471,61 @@ msgstr "" | ||||
| "Минимальная версия ALR для ALR-репозитория выше текущей версии. Попробуйте " | ||||
| "обновить ALR, если что-то не работает." | ||||
|  | ||||
| #: repo.go:39 | ||||
| msgid "Add a new repository" | ||||
| msgstr "Добавить новый репозиторий" | ||||
| #: refresh.go:30 | ||||
| msgid "Pull all repositories that have changed" | ||||
| msgstr "Скачать все изменённые репозитории" | ||||
|  | ||||
| #: repo.go:46 | ||||
| msgid "Name of the new repo" | ||||
| msgstr "Название нового репозитория" | ||||
| #: repo.go:39 | ||||
| msgid "Manage repos" | ||||
| msgstr "" | ||||
|  | ||||
| #: repo.go:50 repo.go:220 | ||||
| msgid "Remove an existing repository" | ||||
| msgstr "Удалить существующий репозиторий" | ||||
|  | ||||
| #: repo.go:52 | ||||
| msgid "URL of the new repo" | ||||
| msgstr "URL-адрес нового репозитория" | ||||
| msgid "<name>" | ||||
| msgstr "" | ||||
|  | ||||
| #: repo.go:75 | ||||
| msgid "Repo \"%s\" already exists" | ||||
| msgstr "Репозиторий \"%s\" уже существует" | ||||
| #: repo.go:82 | ||||
| msgid "Repo \"%s\" does not exist" | ||||
| msgstr "Репозитория \"%s\" не существует" | ||||
|  | ||||
| #: repo.go:86 repo.go:159 | ||||
| #: repo.go:89 | ||||
| msgid "Error removing repo directory" | ||||
| msgstr "Ошибка при удалении каталога репозитория" | ||||
|  | ||||
| #: repo.go:93 repo.go:160 | ||||
| msgid "Error saving config" | ||||
| msgstr "Ошибка при сохранении конфигурации" | ||||
|  | ||||
| #: repo.go:112 | ||||
| msgid "Remove an existing repository" | ||||
| msgstr "Удалить существующий репозиторий" | ||||
|  | ||||
| #: repo.go:119 | ||||
| msgid "Name of the repo to be deleted" | ||||
| msgstr "Название репозитория  удалён" | ||||
|  | ||||
| #: repo.go:148 | ||||
| msgid "Repo \"%s\" does not exist" | ||||
| msgstr "Репозитория \"%s\" не существует" | ||||
|  | ||||
| #: repo.go:155 | ||||
| msgid "Error removing repo directory" | ||||
| msgstr "Ошибка при удалении каталога репозитория" | ||||
|  | ||||
| #: repo.go:178 | ||||
| msgid "Error removing packages from database" | ||||
| msgstr "Ошибка при удалении пакетов из базы данных" | ||||
|  | ||||
| #: repo.go:189 | ||||
| msgid "Pull all repositories that have changed" | ||||
| 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" | ||||
| @@ -509,22 +547,10 @@ msgstr "Искать по репозиторию" | ||||
| msgid "Search by provides" | ||||
| msgstr "Иcкать по provides" | ||||
|  | ||||
| #: search.go:71 | ||||
| msgid "Format output using a Go template" | ||||
| msgstr "Формат выходных данных с использованием шаблона Go" | ||||
|  | ||||
| #: search.go:130 | ||||
| msgid "Error while executing search" | ||||
| msgstr "Ошибка при выполнении поиска" | ||||
|  | ||||
| #: search.go:138 | ||||
| msgid "Error parsing format template" | ||||
| msgstr "Ошибка при разборе шаблона" | ||||
|  | ||||
| #: search.go:153 | ||||
| msgid "Error executing template" | ||||
| msgstr "Ошибка при выполнении шаблона" | ||||
|  | ||||
| #: upgrade.go:47 | ||||
| msgid "Upgrade all installed packages" | ||||
| msgstr "Обновить все установленные пакеты" | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| ALR - Any Linux Repository | ||||
| Copyright (C) {{ .Year }} Евгений Храмов | ||||
| 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 | ||||
|   | ||||
							
								
								
									
										72
									
								
								list.go
									
									
									
									
									
								
							
							
						
						
									
										72
									
								
								list.go
									
									
									
									
									
								
							| @@ -22,10 +22,12 @@ package main | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"log/slog" | ||||
| 	"os" | ||||
| 	"slices" | ||||
| 	"text/template" | ||||
|  | ||||
| 	"github.com/leonelquinteros/gotext" | ||||
| 	"github.com/urfave/cli/v2" | ||||
| 	"golang.org/x/exp/slices" | ||||
|  | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils" | ||||
| 	appbuilder "gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils/app_builder" | ||||
| @@ -45,6 +47,15 @@ func ListCmd() *cli.Command { | ||||
| 				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 { | ||||
| 			if err := utils.ExitIfCantDropCapsToAlrUserNoPrivs(); err != nil { | ||||
| @@ -57,8 +68,10 @@ func ListCmd() *cli.Command { | ||||
| 				New(ctx). | ||||
| 				WithConfig(). | ||||
| 				WithDB(). | ||||
| 				WithManager(). | ||||
| 				// autoPull only | ||||
| 				WithRepos(). | ||||
| 				WithDistroInfo(). | ||||
| 				Build() | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| @@ -67,6 +80,39 @@ func ListCmd() *cli.Command { | ||||
|  | ||||
| 			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 | ||||
| 			} | ||||
|  | ||||
| 			// TODO: refactor code below | ||||
|  | ||||
| 			where := "true" | ||||
| 			args := []any(nil) | ||||
| @@ -115,17 +161,35 @@ func ListCmd() *cli.Command { | ||||
| 					continue | ||||
| 				} | ||||
|  | ||||
| 				version := pkg.Version | ||||
| 				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 { | ||||
| 						version = instVersion | ||||
| 						pkgInfo.Version = instVersion | ||||
| 					} | ||||
| 				} | ||||
|  | ||||
| 				fmt.Printf("%s/%s %s\n", pkg.Repository, pkg.Name, version) | ||||
| 				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 | ||||
|   | ||||
							
								
								
									
										5
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								main.go
									
									
									
									
									
								
							| @@ -74,14 +74,15 @@ func GetApp() *cli.App { | ||||
| 			InfoCmd(), | ||||
| 			ListCmd(), | ||||
| 			BuildCmd(), | ||||
| 			AddRepoCmd(), | ||||
| 			RemoveRepoCmd(), | ||||
| 			LegacyAddRepoCmd(), | ||||
| 			LegacyRemoveRepoCmd(), | ||||
| 			RefreshCmd(), | ||||
| 			FixCmd(), | ||||
| 			GenCmd(), | ||||
| 			HelperCmd(), | ||||
| 			VersionCmd(), | ||||
| 			SearchCmd(), | ||||
| 			RepoCmd(), | ||||
| 			// Internal commands | ||||
| 			InternalBuildCmd(), | ||||
| 			InternalInstallCmd(), | ||||
|   | ||||
| @@ -118,16 +118,20 @@ func (e *LocalScriptExecutor) ExecuteFirstPass(ctx context.Context, input *Build | ||||
| 		return vars.Name, varsOfPackages, nil | ||||
| 	} | ||||
|  | ||||
| 	if len(input.packages) == 0 { | ||||
| 		return "", nil, errors.New("script has multiple packages but package is not specified") | ||||
| 	var pkgNames []string | ||||
|  | ||||
| 	if len(input.packages) != 0 { | ||||
| 		pkgNames = input.packages | ||||
| 	} else { | ||||
| 		pkgNames = pkgs.Names | ||||
| 	} | ||||
|  | ||||
| 	for _, pkgName := range input.packages { | ||||
| 	for _, pkgName := range pkgNames { | ||||
| 		var preVars types.BuildVarsPre | ||||
| 		funcName := fmt.Sprintf("meta_%s", pkgName) | ||||
| 		meta, ok := dec.GetFuncWithSubshell(funcName) | ||||
| 		if !ok { | ||||
| 			return "", nil, errors.New("func is missing") | ||||
| 			return "", nil, fmt.Errorf("func %s is missing", funcName) | ||||
| 		} | ||||
| 		r, err := meta(ctx) | ||||
| 		if err != nil { | ||||
|   | ||||
							
								
								
									
										38
									
								
								pkg/parser/parser.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								pkg/parser/parser.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | ||||
| // ALR - Any Linux Repository | ||||
| // Copyright (C) 2025 The ALR Authors | ||||
| // | ||||
| // This program is free software: you can redistribute it and/or modify | ||||
| // it under the terms of the GNU General Public License as published by | ||||
| // the Free Software Foundation, either version 3 of the License, or | ||||
| // (at your option) any later version. | ||||
| // | ||||
| // This program is distributed in the hope that it will be useful, | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| // GNU General Public License for more details. | ||||
| // | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  | ||||
| package parser | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
|  | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/shutils/decoder" | ||||
| ) | ||||
|  | ||||
| type PackageNames struct { | ||||
| 	BasePkgName string   `sh:"basepkg_name"` | ||||
| 	Names       []string `sh:"name"` | ||||
| } | ||||
|  | ||||
| func ParseNames(dec *decoder.Decoder) (*PackageNames, error) { | ||||
| 	var pkgs PackageNames | ||||
| 	err := dec.DecodeVars(&pkgs) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("fail parse names: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	return &pkgs, nil | ||||
| } | ||||
| @@ -43,11 +43,8 @@ import ( | ||||
| 	"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/shutils/decoder" | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/shutils/handlers" | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/types" | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro" | ||||
| ) | ||||
|  | ||||
| type actionType uint8 | ||||
| @@ -231,79 +228,19 @@ func (rs *Repos) Pull(ctx context.Context, repos []types.Repo) error { | ||||
| func (rs *Repos) updatePkg(ctx context.Context, repo types.Repo, runner *interp.Runner, scriptFl io.ReadCloser) error { | ||||
| 	parser := syntax.NewParser() | ||||
|  | ||||
| 	defer scriptFl.Close() | ||||
| 	fl, err := parser.Parse(scriptFl, "alr.sh") | ||||
| 	pkgs, err := parseScript(ctx, repo, parser, runner, scriptFl) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	runner.Reset() | ||||
| 	err = runner.Run(ctx, fl) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	type packages struct { | ||||
| 		BasePkgName string   `sh:"basepkg_name"` | ||||
| 		Names       []string `sh:"name"` | ||||
| 	} | ||||
|  | ||||
| 	var pkgs packages | ||||
|  | ||||
| 	d := decoder.New(&distro.OSRelease{}, runner) | ||||
| 	d.Overrides = false | ||||
| 	d.LikeDistros = false | ||||
| 	err = d.DecodeVars(&pkgs) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	if len(pkgs.Names) > 1 { | ||||
| 		if pkgs.BasePkgName == "" { | ||||
| 			pkgs.BasePkgName = pkgs.Names[0] | ||||
| 		} | ||||
| 		for _, pkgName := range pkgs.Names { | ||||
| 			pkgInfo := PackageInfo{} | ||||
| 			funcName := fmt.Sprintf("meta_%s", pkgName) | ||||
| 			runner.Reset() | ||||
| 			err = runner.Run(ctx, fl) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			meta, ok := d.GetFuncWithSubshell(funcName) | ||||
| 			if !ok { | ||||
| 				return errors.New("func is missing") | ||||
| 			} | ||||
| 			r, err := meta(ctx) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			d := decoder.New(&distro.OSRelease{}, r) | ||||
| 			d.Overrides = false | ||||
| 			d.LikeDistros = false | ||||
| 			err = d.DecodeVars(&pkgInfo) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			pkg := pkgInfo.ToPackage(repo.Name) | ||||
| 			resolveOverrides(r, pkg) | ||||
| 			pkg.Name = pkgName | ||||
| 			pkg.BasePkgName = pkgs.BasePkgName | ||||
| 	for _, pkg := range pkgs { | ||||
| 		err = rs.db.InsertPackage(ctx, *pkg) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	pkg := EmptyPackage(repo.Name) | ||||
| 	err = d.DecodeVars(pkg) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	resolveOverrides(runner, pkg) | ||||
| 	return rs.db.InsertPackage(ctx, *pkg) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (rs *Repos) processRepoChangesRunner(repoDir, scriptDir string) (*interp.Runner, error) { | ||||
| @@ -399,16 +336,17 @@ func (rs *Repos) processRepoChanges(ctx context.Context, repo types.Repo, r *git | ||||
| 				return nil | ||||
| 			} | ||||
|  | ||||
| 			var pkg db.Package | ||||
| 			err = parseScript(ctx, parser, runner, r, &pkg) | ||||
| 			pkgs, err := parseScript(ctx, repo, parser, runner, r) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
|  | ||||
| 			for _, pkg := range pkgs { | ||||
| 				err = rs.db.DeletePkgs(ctx, "name = ? AND repository = ?", pkg.Name, repo.Name) | ||||
| 				if err != nil { | ||||
| 					return err | ||||
| 				} | ||||
| 			} | ||||
| 		case actionUpdate: | ||||
| 			if filepath.Base(action.File) != "alr.sh" { | ||||
| 				action.File = filepath.Join(filepath.Dir(action.File), "alr.sh") | ||||
|   | ||||
| @@ -18,6 +18,7 @@ package repos | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"path/filepath" | ||||
| @@ -35,7 +36,9 @@ import ( | ||||
|  | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/db" | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/shutils/decoder" | ||||
| 	"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/parser" | ||||
| ) | ||||
|  | ||||
| // isValid makes sure the path of the file being updated is valid. | ||||
| @@ -54,23 +57,85 @@ func isValid(from, to diff.File) bool { | ||||
| 	return match | ||||
| } | ||||
|  | ||||
| func parseScript(ctx context.Context, parser *syntax.Parser, runner *interp.Runner, r io.ReadCloser, pkg *db.Package) error { | ||||
| 	defer r.Close() | ||||
| 	fl, err := parser.Parse(r, "alr.sh") | ||||
| func parseScript( | ||||
| 	ctx context.Context, | ||||
| 	repo types.Repo, | ||||
| 	syntaxParser *syntax.Parser, | ||||
| 	runner *interp.Runner, | ||||
| 	r io.ReadCloser, | ||||
| ) ([]*db.Package, error) { | ||||
| 	fl, err := syntaxParser.Parse(r, "alr.sh") | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	runner.Reset() | ||||
| 	err = runner.Run(ctx, fl) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	d := decoder.New(&distro.OSRelease{}, runner) | ||||
| 	d.Overrides = false | ||||
| 	d.LikeDistros = false | ||||
| 	return d.DecodeVars(pkg) | ||||
|  | ||||
| 	pkgNames, err := parser.ParseNames(d) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed parsing package names: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	if len(pkgNames.Names) == 0 { | ||||
| 		return nil, errors.New("package name is missing") | ||||
| 	} | ||||
|  | ||||
| 	var dbPkgs []*db.Package | ||||
|  | ||||
| 	if len(pkgNames.Names) > 1 { | ||||
| 		if pkgNames.BasePkgName == "" { | ||||
| 			pkgNames.BasePkgName = pkgNames.Names[0] | ||||
| 		} | ||||
| 		for _, pkgName := range pkgNames.Names { | ||||
| 			pkgInfo := PackageInfo{} | ||||
| 			funcName := fmt.Sprintf("meta_%s", pkgName) | ||||
| 			runner.Reset() | ||||
| 			err = runner.Run(ctx, fl) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			meta, ok := d.GetFuncWithSubshell(funcName) | ||||
| 			if !ok { | ||||
| 				return nil, fmt.Errorf("func %s is missing", funcName) | ||||
| 			} | ||||
| 			r, err := meta(ctx) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			d := decoder.New(&distro.OSRelease{}, r) | ||||
| 			d.Overrides = false | ||||
| 			d.LikeDistros = false | ||||
| 			err = d.DecodeVars(&pkgInfo) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			pkg := pkgInfo.ToPackage(repo.Name) | ||||
| 			resolveOverrides(r, pkg) | ||||
| 			pkg.Name = pkgName | ||||
| 			pkg.BasePkgName = pkgNames.BasePkgName | ||||
| 			dbPkgs = append(dbPkgs, pkg) | ||||
| 		} | ||||
|  | ||||
| 		return dbPkgs, nil | ||||
| 	} | ||||
|  | ||||
| 	pkg := EmptyPackage(repo.Name) | ||||
| 	err = d.DecodeVars(pkg) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	resolveOverrides(runner, pkg) | ||||
| 	dbPkgs = append(dbPkgs, pkg) | ||||
|  | ||||
| 	return dbPkgs, nil | ||||
| } | ||||
|  | ||||
| type PackageInfo struct { | ||||
|   | ||||
							
								
								
									
										52
									
								
								refresh.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								refresh.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 main | ||||
|  | ||||
| import ( | ||||
| 	"github.com/leonelquinteros/gotext" | ||||
| 	"github.com/urfave/cli/v2" | ||||
|  | ||||
| 	appbuilder "gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils/app_builder" | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/utils" | ||||
| ) | ||||
|  | ||||
| func RefreshCmd() *cli.Command { | ||||
| 	return &cli.Command{ | ||||
| 		Name:    "refresh", | ||||
| 		Usage:   gotext.Get("Pull all repositories that have changed"), | ||||
| 		Aliases: []string{"ref"}, | ||||
| 		Action: func(c *cli.Context) error { | ||||
| 			if err := utils.ExitIfCantDropCapsToAlrUser(); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
|  | ||||
| 			ctx := c.Context | ||||
|  | ||||
| 			deps, err := appbuilder. | ||||
| 				New(ctx). | ||||
| 				WithConfig(). | ||||
| 				WithDB(). | ||||
| 				WithReposForcePull(). | ||||
| 				Build() | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			defer deps.Defer() | ||||
| 			return nil | ||||
| 		}, | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										198
									
								
								repo.go
									
									
									
									
									
								
							
							
						
						
									
										198
									
								
								repo.go
									
									
									
									
									
								
							| @@ -33,96 +33,30 @@ import ( | ||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/utils" | ||||
| ) | ||||
|  | ||||
| func AddRepoCmd() *cli.Command { | ||||
| func RepoCmd() *cli.Command { | ||||
| 	return &cli.Command{ | ||||
| 		Name:    "addrepo", | ||||
| 		Usage:   gotext.Get("Add a new repository"), | ||||
| 		Aliases: []string{"ar"}, | ||||
| 		Flags: []cli.Flag{ | ||||
| 			&cli.StringFlag{ | ||||
| 				Name:     "name", | ||||
| 				Aliases:  []string{"n"}, | ||||
| 				Required: true, | ||||
| 				Usage:    gotext.Get("Name of the new repo"), | ||||
| 		Name:  "repo", | ||||
| 		Usage: gotext.Get("Manage repos"), | ||||
| 		Subcommands: []*cli.Command{ | ||||
| 			RemoveRepoCmd(), | ||||
| 			AddRepoCmd(), | ||||
| 		}, | ||||
| 			&cli.StringFlag{ | ||||
| 				Name:     "url", | ||||
| 				Aliases:  []string{"u"}, | ||||
| 				Required: true, | ||||
| 				Usage:    gotext.Get("URL of the new repo"), | ||||
| 			}, | ||||
| 		}, | ||||
| 		Action: utils.RootNeededAction(func(c *cli.Context) error { | ||||
| 			name := c.String("name") | ||||
| 			repoURL := c.String("url") | ||||
|  | ||||
| 			ctx := c.Context | ||||
|  | ||||
| 			deps, err := appbuilder. | ||||
| 				New(ctx). | ||||
| 				WithConfig(). | ||||
| 				Build() | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			defer deps.Defer() | ||||
|  | ||||
| 			cfg := deps.Cfg | ||||
|  | ||||
| 			reposSlice := cfg.Repos() | ||||
| 			for _, repo := range reposSlice { | ||||
| 				if repo.URL == repoURL || repo.Name == name { | ||||
| 					return cliutils.FormatCliExit(gotext.Get("Repo \"%s\" already exists", repo.Name), nil) | ||||
| 				} | ||||
| 			} | ||||
| 			reposSlice = append(reposSlice, types.Repo{ | ||||
| 				Name: name, | ||||
| 				URL:  repoURL, | ||||
| 			}) | ||||
| 			cfg.SetRepos(reposSlice) | ||||
|  | ||||
| 			err = cfg.SaveUserConfig() | ||||
| 			if err != nil { | ||||
| 				return cliutils.FormatCliExit(gotext.Get("Error saving config"), err) | ||||
| 			} | ||||
|  | ||||
| 			if err := utils.ExitIfCantDropCapsToAlrUserNoPrivs(); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
|  | ||||
| 			deps, err = appbuilder. | ||||
| 				New(ctx). | ||||
| 				UseConfig(cfg). | ||||
| 				WithDB(). | ||||
| 				WithReposForcePull(). | ||||
| 				Build() | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			defer deps.Defer() | ||||
|  | ||||
| 			return nil | ||||
| 		}), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func RemoveRepoCmd() *cli.Command { | ||||
| 	return &cli.Command{ | ||||
| 		Name:    "removerepo", | ||||
| 		Name:      "remove", | ||||
| 		Usage:     gotext.Get("Remove an existing repository"), | ||||
| 		Aliases: []string{"rr"}, | ||||
| 		Flags: []cli.Flag{ | ||||
| 			&cli.StringFlag{ | ||||
| 				Name:     "name", | ||||
| 				Aliases:  []string{"n"}, | ||||
| 				Required: true, | ||||
| 				Usage:    gotext.Get("Name of the repo to be deleted"), | ||||
| 			}, | ||||
| 		}, | ||||
| 		Aliases:   []string{"rm"}, | ||||
| 		ArgsUsage: gotext.Get("<name>"), | ||||
| 		Action: utils.RootNeededAction(func(c *cli.Context) error { | ||||
| 			ctx := c.Context | ||||
| 			if c.Args().Len() < 1 { | ||||
| 				return cliutils.FormatCliExit("missing args", nil) | ||||
| 			} | ||||
| 			name := c.Args().Get(0) | ||||
|  | ||||
| 			name := c.String("name") | ||||
| 			ctx := c.Context | ||||
|  | ||||
| 			deps, err := appbuilder. | ||||
| 				New(ctx). | ||||
| @@ -183,21 +117,56 @@ func RemoveRepoCmd() *cli.Command { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func RefreshCmd() *cli.Command { | ||||
| func AddRepoCmd() *cli.Command { | ||||
| 	return &cli.Command{ | ||||
| 		Name:    "refresh", | ||||
| 		Usage:   gotext.Get("Pull all repositories that have changed"), | ||||
| 		Aliases: []string{"ref"}, | ||||
| 		Action: func(c *cli.Context) error { | ||||
| 			if err := utils.ExitIfCantDropCapsToAlrUser(); err != nil { | ||||
| 				return err | ||||
| 		Name:      "add", | ||||
| 		Usage:     gotext.Get("Add a new repository"), | ||||
| 		ArgsUsage: gotext.Get("<name> <url>"), | ||||
| 		Action: utils.RootNeededAction(func(c *cli.Context) error { | ||||
| 			if c.Args().Len() < 2 { | ||||
| 				return cliutils.FormatCliExit("missing args", nil) | ||||
| 			} | ||||
|  | ||||
| 			name := c.Args().Get(0) | ||||
| 			repoURL := c.Args().Get(1) | ||||
|  | ||||
| 			ctx := c.Context | ||||
|  | ||||
| 			deps, err := appbuilder. | ||||
| 				New(ctx). | ||||
| 				WithConfig(). | ||||
| 				Build() | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			defer deps.Defer() | ||||
|  | ||||
| 			cfg := deps.Cfg | ||||
|  | ||||
| 			reposSlice := cfg.Repos() | ||||
| 			for _, repo := range reposSlice { | ||||
| 				if repo.URL == repoURL || repo.Name == name { | ||||
| 					return cliutils.FormatCliExit(gotext.Get("Repo \"%s\" already exists", repo.Name), nil) | ||||
| 				} | ||||
| 			} | ||||
| 			reposSlice = append(reposSlice, types.Repo{ | ||||
| 				Name: name, | ||||
| 				URL:  repoURL, | ||||
| 			}) | ||||
| 			cfg.SetRepos(reposSlice) | ||||
|  | ||||
| 			err = cfg.SaveUserConfig() | ||||
| 			if err != nil { | ||||
| 				return cliutils.FormatCliExit(gotext.Get("Error saving config"), err) | ||||
| 			} | ||||
|  | ||||
| 			if err := utils.ExitIfCantDropCapsToAlrUserNoPrivs(); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
|  | ||||
| 			deps, err = appbuilder. | ||||
| 				New(ctx). | ||||
| 				UseConfig(cfg). | ||||
| 				WithDB(). | ||||
| 				WithReposForcePull(). | ||||
| 				Build() | ||||
| @@ -205,7 +174,62 @@ func RefreshCmd() *cli.Command { | ||||
| 				return err | ||||
| 			} | ||||
| 			defer deps.Defer() | ||||
|  | ||||
| 			return nil | ||||
| 		}), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // TODO: remove | ||||
| // | ||||
| // Deprecated: use "alr repo add" | ||||
| func LegacyAddRepoCmd() *cli.Command { | ||||
| 	return &cli.Command{ | ||||
| 		Hidden:  true, | ||||
| 		Name:    "addrepo", | ||||
| 		Usage:   gotext.Get("Add a new repository"), | ||||
| 		Aliases: []string{"ar"}, | ||||
| 		Flags: []cli.Flag{ | ||||
| 			&cli.StringFlag{ | ||||
| 				Name:     "name", | ||||
| 				Aliases:  []string{"n"}, | ||||
| 				Required: true, | ||||
| 				Usage:    gotext.Get("Name of the new repo"), | ||||
| 			}, | ||||
| 			&cli.StringFlag{ | ||||
| 				Name:     "url", | ||||
| 				Aliases:  []string{"u"}, | ||||
| 				Required: true, | ||||
| 				Usage:    gotext.Get("URL of the new repo"), | ||||
| 			}, | ||||
| 		}, | ||||
| 		Action: utils.RootNeededAction(func(c *cli.Context) error { | ||||
| 			cliutils.WarnLegacyCommand("alr repo add <name> <url>") | ||||
| 			return c.App.RunContext(c.Context, []string{"", "repo", "add", c.String("name"), c.String("url")}) | ||||
| 		}), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // TODO: remove | ||||
| // | ||||
| // Deprecated: use "alr repo rm" | ||||
| func LegacyRemoveRepoCmd() *cli.Command { | ||||
| 	return &cli.Command{ | ||||
| 		Hidden:  true, | ||||
| 		Name:    "removerepo", | ||||
| 		Usage:   gotext.Get("Remove an existing repository"), | ||||
| 		Aliases: []string{"rr"}, | ||||
| 		Flags: []cli.Flag{ | ||||
| 			&cli.StringFlag{ | ||||
| 				Name:     "name", | ||||
| 				Aliases:  []string{"n"}, | ||||
| 				Required: true, | ||||
| 				Usage:    gotext.Get("Name of the repo to be deleted"), | ||||
| 			}, | ||||
| 		}, | ||||
| 		Action: utils.RootNeededAction(func(c *cli.Context) error { | ||||
| 			cliutils.WarnLegacyCommand("alr repo remove <name>") | ||||
| 			return c.App.RunContext(c.Context, []string{"", "repo", "remove", c.String("name")}) | ||||
| 		}), | ||||
| 	} | ||||
| } | ||||
|   | ||||
							
								
								
									
										32
									
								
								upgrade.go
									
									
									
									
									
								
							
							
						
						
									
										32
									
								
								upgrade.go
									
									
									
									
									
								
							| @@ -116,7 +116,7 @@ func UpgradeCmd() *cli.Command { | ||||
| 						Info:       deps.Info, | ||||
| 						PkgFormat_: build.GetPkgFormat(deps.Manager), | ||||
| 					}, | ||||
| 					updates, | ||||
| 					mapUptatesInfoToPackages(updates), | ||||
| 				) | ||||
| 				if err != nil { | ||||
| 					return cliutils.FormatCliExit(gotext.Get("Error checking for updates"), err) | ||||
| @@ -130,12 +130,27 @@ func UpgradeCmd() *cli.Command { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func mapUptatesInfoToPackages(updates []UpdateInfo) []database.Package { | ||||
| 	var pkgs []database.Package | ||||
| 	for _, info := range updates { | ||||
| 		pkgs = append(pkgs, *info.Package) | ||||
| 	} | ||||
| 	return pkgs | ||||
| } | ||||
|  | ||||
| type UpdateInfo struct { | ||||
| 	Package *database.Package | ||||
|  | ||||
| 	FromVersion string | ||||
| 	ToVersion   string | ||||
| } | ||||
|  | ||||
| func checkForUpdates( | ||||
| 	ctx context.Context, | ||||
| 	mgr manager.Manager, | ||||
| 	db *database.Database, | ||||
| 	info *distro.OSRelease, | ||||
| ) ([]database.Package, error) { | ||||
| ) ([]UpdateInfo, error) { | ||||
| 	installed, err := mgr.ListInstalled(nil) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| @@ -145,7 +160,7 @@ func checkForUpdates( | ||||
|  | ||||
| 	s := search.New(db) | ||||
|  | ||||
| 	var out []database.Package | ||||
| 	var out []UpdateInfo | ||||
| 	for _, pkgName := range pkgNames { | ||||
| 		matches := build.RegexpALRPackageName.FindStringSubmatch(pkgName) | ||||
| 		if matches != nil { | ||||
| @@ -179,10 +194,13 @@ func checkForUpdates( | ||||
| 			} | ||||
|  | ||||
| 			c := vercmp.Compare(repoVer, installed[pkgName]) | ||||
| 			if c == 0 || c == -1 { | ||||
| 				continue | ||||
| 			} else if c == 1 { | ||||
| 				out = append(out, pkg) | ||||
|  | ||||
| 			if c == 1 { | ||||
| 				out = append(out, UpdateInfo{ | ||||
| 					Package:     &pkg, | ||||
| 					FromVersion: installed[pkgName], | ||||
| 					ToVersion:   repoVer, | ||||
| 				}) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user