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 {