diff --git a/assets/coverage-badge.svg b/assets/coverage-badge.svg index b891de3..4d357e3 100644 --- a/assets/coverage-badge.svg +++ b/assets/coverage-badge.svg @@ -11,7 +11,7 @@ coverage coverage - 17.0% - 17.0% + 17.2% + 17.2% diff --git a/build.go b/build.go index 783edb9..3a8efb9 100644 --- a/build.go +++ b/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, diff --git a/e2e-tests/common_test.go b/e2e-tests/common_test.go index 9ccb1cc..fd3b893 100644 --- a/e2e-tests/common_test.go +++ b/e2e-tests/common_test.go @@ -188,3 +188,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" diff --git a/e2e-tests/issue_81_multiple_packages_test.go b/e2e-tests/issue_81_multiple_packages_test.go new file mode 100644 index 0000000..2f6d13e --- /dev/null +++ b/e2e-tests/issue_81_multiple_packages_test.go @@ -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 . + +//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) + }, + ) +} diff --git a/internal/overrides/overrides.go b/internal/overrides/overrides.go index a98bbb6..8d42f8a 100644 --- a/internal/overrides/overrides.go +++ b/internal/overrides/overrides.go @@ -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 diff --git a/internal/translations/default.pot b/internal/translations/default.pot index 400e5e4..0b23dfc 100644 --- a/internal/translations/default.pot +++ b/internal/translations/default.pot @@ -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 "" @@ -397,35 +397,35 @@ 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." diff --git a/internal/translations/po/ru/default.po b/internal/translations/po/ru/default.po index 6180148..2c9d802 100644 --- a/internal/translations/po/ru/default.po +++ b/internal/translations/po/ru/default.po @@ -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 "Сделано" @@ -409,35 +409,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." diff --git a/pkg/build/script_executor.go b/pkg/build/script_executor.go index e63c69f..ba402f1 100644 --- a/pkg/build/script_executor.go +++ b/pkg/build/script_executor.go @@ -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 { diff --git a/pkg/parser/parser.go b/pkg/parser/parser.go new file mode 100644 index 0000000..5397b4c --- /dev/null +++ b/pkg/parser/parser.go @@ -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 . + +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 +} diff --git a/pkg/repos/pull.go b/pkg/repos/pull.go index fc254be..d443d75 100644 --- a/pkg/repos/pull.go +++ b/pkg/repos/pull.go @@ -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 _, pkg := range pkgs { + err = rs.db.InsertPackage(ctx, *pkg) + if err != nil { + return err } - 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 - 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,15 +336,16 @@ 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 } - err = rs.db.DeletePkgs(ctx, "name = ? AND repository = ?", pkg.Name, repo.Name) - 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" { diff --git a/pkg/repos/utils.go b/pkg/repos/utils.go index bc2315a..04cc656 100644 --- a/pkg/repos/utils.go +++ b/pkg/repos/utils.go @@ -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 {