diff --git a/e2e-tests/__snapshots__/TestE2EIssue129RepoTomlImportTest_issue-129-repo-toml-import-test_ubuntu-24.04_1.snap.stdout b/e2e-tests/__snapshots__/TestE2EIssue129RepoTomlImportTest_issue-129-repo-toml-import-test_ubuntu-24.04_1.snap.stdout
new file mode 100755
index 0000000..2a17b58
--- /dev/null
+++ b/e2e-tests/__snapshots__/TestE2EIssue129RepoTomlImportTest_issue-129-repo-toml-import-test_ubuntu-24.04_1.snap.stdout
@@ -0,0 +1,5 @@
+- name: alr-repo
+ url: https://gitea.plemya-x.ru/Plemya-x/repo-for-tests
+ ref: main
+ mirrors:
+ - https://github.com/example/example.git
diff --git a/e2e-tests/issue_129_repo_toml_import_test.go b/e2e-tests/issue_129_repo_toml_import_test.go
new file mode 100644
index 0000000..33e0d1d
--- /dev/null
+++ b/e2e-tests/issue_129_repo_toml_import_test.go
@@ -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 .
+
+//go:build e2e
+
+package e2etests_test
+
+import (
+ "testing"
+
+ "go.alt-gnome.ru/capytest"
+)
+
+func TestE2EIssue129RepoTomlImportTest(t *testing.T) {
+ runMatrixSuite(
+ t,
+ "issue-129-repo-toml-import-test",
+ COMMON_SYSTEMS,
+ func(t *testing.T, r capytest.Runner) {
+ defaultPrepare(t, r)
+
+ r.Command("alr", "config", "get", "repos").
+ ExpectStdoutMatchesSnapshot().
+ Run(t)
+ },
+ )
+}
diff --git a/generators/plugin-generator/main.go b/generators/plugin-generator/main.go
index 91b7a0c..a488c0e 100644
--- a/generators/plugin-generator/main.go
+++ b/generators/plugin-generator/main.go
@@ -27,6 +27,7 @@ import (
"os"
"strings"
"text/template"
+ "unicode"
"golang.org/x/text/cases"
"golang.org/x/text/language"
@@ -252,6 +253,8 @@ func argsGen(buf *bytes.Buffer, methods []MethodInfo) {
return strings.ToLower(s[:1]) + s[1:]
},
"zeroValue": func(typeName string) string {
+ typeName = strings.TrimSpace(typeName)
+
switch typeName {
case "string":
return "\"\""
@@ -263,9 +266,32 @@ func argsGen(buf *bytes.Buffer, methods []MethodInfo) {
return "0.0"
case "bool":
return "false"
- default:
+ }
+
+ if strings.HasPrefix(typeName, "*") {
return "nil"
}
+ if strings.HasPrefix(typeName, "[]") ||
+ strings.HasPrefix(typeName, "map[") ||
+ strings.HasPrefix(typeName, "chan ") {
+ return "nil"
+ }
+
+ if typeName == "interface{}" {
+ return "nil"
+ }
+
+ // If external type: pkg.Type
+ if strings.Contains(typeName, ".") {
+ return typeName + "{}"
+ }
+
+ // If starts with uppercase — likely struct
+ if len(typeName) > 0 && unicode.IsUpper(rune(typeName[0])) {
+ return typeName + "{}"
+ }
+
+ return "nil"
},
}
diff --git a/go.mod b/go.mod
index fa734da..b0f26ad 100644
--- a/go.mod
+++ b/go.mod
@@ -34,8 +34,8 @@ require (
github.com/stretchr/testify v1.10.0
github.com/urfave/cli/v2 v2.25.7
github.com/vmihailenco/msgpack/v5 v5.3.5
- go.alt-gnome.ru/capytest v0.0.2
- go.alt-gnome.ru/capytest/providers/podman v0.0.2
+ go.alt-gnome.ru/capytest v0.0.3-0.20250706082755-f20413e052f9
+ go.alt-gnome.ru/capytest/providers/podman v0.0.3-0.20250706082755-f20413e052f9
go.elara.ws/vercmp v0.0.0-20230622214216-0b2b067575c4
golang.org/x/crypto v0.36.0
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56
@@ -77,6 +77,9 @@ require (
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
github.com/fatih/color v1.7.0 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
+ github.com/gkampitakis/ciinfo v0.3.2 // indirect
+ github.com/gkampitakis/go-diff v1.3.2 // indirect
+ github.com/gkampitakis/go-snaps v0.5.13 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-logfmt/logfmt v0.6.0 // indirect
github.com/go-viper/mapstructure/v2 v2.3.0 // indirect
@@ -101,7 +104,10 @@ require (
github.com/klauspost/compress v1.17.11 // indirect
github.com/klauspost/pgzip v1.2.6 // indirect
github.com/knadh/koanf/maps v0.1.2 // indirect
+ github.com/kr/pretty v0.3.1 // indirect
+ github.com/kr/text v0.2.0 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
+ github.com/maruel/natural v1.1.1 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-localereader v0.0.1 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
@@ -120,6 +126,7 @@ require (
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rivo/uniseg v0.4.7 // indirect
+ github.com/rogpeppe/go-internal v1.13.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
github.com/shopspring/decimal v1.3.1 // indirect
@@ -127,6 +134,10 @@ require (
github.com/spf13/cast v1.7.1 // indirect
github.com/syndtr/goleveldb v1.0.0 // indirect
github.com/therootcompany/xz v1.0.1 // indirect
+ github.com/tidwall/gjson v1.18.0 // indirect
+ github.com/tidwall/match v1.1.1 // indirect
+ github.com/tidwall/pretty v1.2.1 // indirect
+ github.com/tidwall/sjson v1.2.5 // indirect
github.com/ulikunitz/xz v0.5.12 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
diff --git a/go.sum b/go.sum
index 64d87f9..d77b9ea 100644
--- a/go.sum
+++ b/go.sum
@@ -104,6 +104,7 @@ github.com/connesc/cipherio v0.2.1 h1:FGtpTPMbKNNWByNrr9aEBtaJtXjqOzkIXNYJp6OEyc
github.com/connesc/cipherio v0.2.1/go.mod h1:ukY0MWJDFnJEbXMQtOcn2VmTpRfzcTz4OoVrWGGJZcA=
github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
@@ -134,6 +135,12 @@ github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7z
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
+github.com/gkampitakis/ciinfo v0.3.2 h1:JcuOPk8ZU7nZQjdUhctuhQofk7BGHuIy0c9Ez8BNhXs=
+github.com/gkampitakis/ciinfo v0.3.2/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo=
+github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M=
+github.com/gkampitakis/go-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6OPYerdAAS9tk=
+github.com/gkampitakis/go-snaps v0.5.13 h1:Hhjmvv1WboSCxkR9iU2mj5PQ8tsz/y8ECGrIbjjPF8Q=
+github.com/gkampitakis/go-snaps v0.5.13/go.mod h1:HNpx/9GoKisdhw9AFOBT1N7DBs9DiHo/hGheFGBZ+mc=
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
@@ -286,6 +293,8 @@ github.com/leonelquinteros/gotext v1.7.0 h1:jcJmF4AXqyamP7vuw2MMIKs+O3jAEmvrc5JQ
github.com/leonelquinteros/gotext v1.7.0/go.mod h1:qJdoQuERPpccw7L70uoU+K/BvTfRBHYsisCQyFLXyvw=
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/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo=
+github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg=
github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
@@ -345,6 +354,7 @@ github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0
github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
+github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -358,6 +368,7 @@ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
@@ -393,6 +404,16 @@ github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFd
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw=
github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY=
+github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
+github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
+github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
+github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
+github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
+github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
+github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
+github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
+github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
+github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
@@ -413,8 +434,10 @@ gitlab.com/digitalxero/go-conventional-commit v1.0.7 h1:8/dO6WWG+98PMhlZowt/Yjui
gitlab.com/digitalxero/go-conventional-commit v1.0.7/go.mod h1:05Xc2BFsSyC5tKhK0y+P3bs0AwUtNuTp+mTpbCU/DZ0=
go.alt-gnome.ru/capytest v0.0.2 h1:clmvIqmYS86hhA1rsvivSSPpfOFkJTpbn38EQP7I3E8=
go.alt-gnome.ru/capytest v0.0.2/go.mod h1:lvxPx3H6h+LPnStBFblgoT2wkjv0wbug3S14troykEg=
-go.alt-gnome.ru/capytest/providers/podman v0.0.2 h1:fTQ9fmYiONgL8dJvyMB+irCfuojIVaomnqto6bl6HjU=
-go.alt-gnome.ru/capytest/providers/podman v0.0.2/go.mod h1:Wpq1Ny3eMzADJpMJArA2TZGZbsviUBmawtEPcxnoerg=
+go.alt-gnome.ru/capytest v0.0.3-0.20250706082755-f20413e052f9 h1:NST+V5LV/eLgs0p6PsuvfHiZ4UrIWqftCdifO8zgg0g=
+go.alt-gnome.ru/capytest v0.0.3-0.20250706082755-f20413e052f9/go.mod h1:qiM8LARP+JBZr5mrDoVylOoqjrN0MAzvZ21NR9qMc0Y=
+go.alt-gnome.ru/capytest/providers/podman v0.0.3-0.20250706082755-f20413e052f9 h1:VZclgdJxARvhZ6PIWWW2hQ6Ge4XeE36pzUr/U/y62bE=
+go.alt-gnome.ru/capytest/providers/podman v0.0.3-0.20250706082755-f20413e052f9/go.mod h1:Wpq1Ny3eMzADJpMJArA2TZGZbsviUBmawtEPcxnoerg=
go.elara.ws/vercmp v0.0.0-20230622214216-0b2b067575c4 h1:Ep54XceQlKhcCHl9awG+wWP4kz4kIP3c3Lzw/Gc/zwY=
go.elara.ws/vercmp v0.0.0-20230622214216-0b2b067575c4/go.mod h1:/7PNW7nFnDR5W7UXZVc04gdVLR/wBNgkm33KgIz0OBk=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
diff --git a/internal.go b/internal.go
index 987529c..3080540 100644
--- a/internal.go
+++ b/internal.go
@@ -84,6 +84,43 @@ func InternalBuildCmd() *cli.Command {
}
}
+func InternalReposCmd() *cli.Command {
+ return &cli.Command{
+ Name: "_internal-repos",
+ HideHelp: true,
+ Hidden: true,
+ Action: utils.RootNeededAction(func(ctx *cli.Context) error {
+ logger.SetupForGoPlugin()
+
+ if err := utils.ExitIfCantDropCapsToAlrUser(); err != nil {
+ return err
+ }
+
+ deps, err := appbuilder.
+ New(ctx.Context).
+ WithConfig().
+ WithDB().
+ WithReposNoPull().
+ Build()
+ if err != nil {
+ return err
+ }
+ defer deps.Defer()
+
+ pluginCfg := build.GetPluginServeCommonConfig()
+ pluginCfg.Plugins = map[string]plugin.Plugin{
+ "repos": &build.ReposExecutorPlugin{
+ Impl: build.NewRepos(
+ deps.Repos,
+ ),
+ },
+ }
+ plugin.Serve(pluginCfg)
+ return nil
+ }),
+ }
+}
+
func InternalInstallCmd() *cli.Command {
return &cli.Command{
Name: "_internal-installer",
diff --git a/internal/build/plugins.go b/internal/build/plugins.go
index 62a605a..75e6f3a 100644
--- a/internal/build/plugins.go
+++ b/internal/build/plugins.go
@@ -24,6 +24,7 @@ import (
"strings"
"sync"
+ "github.com/hashicorp/go-hclog"
"github.com/hashicorp/go-plugin"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/logger"
@@ -32,6 +33,7 @@ import (
var pluginMap = map[string]plugin.Plugin{
"script-executor": &ScriptExecutorPlugin{},
"installer": &InstallerExecutorPlugin{},
+ "repos": &ReposExecutorPlugin{},
}
var HandshakeConfig = plugin.HandshakeConfig{
@@ -57,6 +59,19 @@ func setCommonCmdEnv(cmd *exec.Cmd) {
}
}
+func GetPluginServeCommonConfig() *plugin.ServeConfig {
+ return &plugin.ServeConfig{
+ HandshakeConfig: HandshakeConfig,
+ Logger: hclog.New(&hclog.LoggerOptions{
+ Name: "plugin",
+ Output: os.Stderr,
+ Level: hclog.Trace,
+ JSONFormat: true,
+ DisableTime: true,
+ }),
+ }
+}
+
func GetSafeInstaller() (InstallerExecutor, func(), error) {
return getSafeExecutor[InstallerExecutor]("_internal-installer", "installer")
}
@@ -65,6 +80,10 @@ func GetSafeScriptExecutor() (ScriptExecutor, func(), error) {
return getSafeExecutor[ScriptExecutor]("_internal-safe-script-executor", "script-executor")
}
+func GetSafeReposExecutor() (ReposExecutor, func(), error) {
+ return getSafeExecutor[ReposExecutor]("_internal-repos", "repos")
+}
+
func getSafeExecutor[T any](subCommand, pluginName string) (T, func(), error) {
var err error
diff --git a/internal/build/plugins_executors.go b/internal/build/plugins_executors.go
index cd2dfa5..98d388e 100644
--- a/internal/build/plugins_executors.go
+++ b/internal/build/plugins_executors.go
@@ -21,9 +21,10 @@ import (
"gitea.plemya-x.ru/Plemya-x/ALR/internal/manager"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/alrsh"
+ "gitea.plemya-x.ru/Plemya-x/ALR/pkg/types"
)
-//go:generate go run ../../generators/plugin-generator InstallerExecutor ScriptExecutor
+//go:generate go run ../../generators/plugin-generator InstallerExecutor ScriptExecutor ReposExecutor
// The Executors interfaces must use context.Context as the first parameter,
// because the plugin-generator cannot generate code without it.
@@ -53,3 +54,7 @@ type ScriptExecutor interface {
basePkg string,
) ([]*BuiltDep, error)
}
+
+type ReposExecutor interface {
+ PullOneAndUpdateFromConfig(ctx context.Context, repo *types.Repo) (types.Repo, error)
+}
diff --git a/internal/build/plugins_executors_gen.go b/internal/build/plugins_executors_gen.go
index 9469262..b7e3417 100644
--- a/internal/build/plugins_executors_gen.go
+++ b/internal/build/plugins_executors_gen.go
@@ -24,6 +24,7 @@ import (
"context"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/manager"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/alrsh"
+ "gitea.plemya-x.ru/Plemya-x/ALR/pkg/types"
"github.com/hashicorp/go-plugin"
)
@@ -67,6 +68,26 @@ func (p *ScriptExecutorPlugin) Server(*plugin.MuxBroker) (interface{}, error) {
return &ScriptExecutorRPCServer{Impl: p.Impl}, nil
}
+type ReposExecutorPlugin struct {
+ Impl ReposExecutor
+}
+
+type ReposExecutorRPCServer struct {
+ Impl ReposExecutor
+}
+
+type ReposExecutorRPC struct {
+ client *rpc.Client
+}
+
+func (p *ReposExecutorPlugin) Client(b *plugin.MuxBroker, c *rpc.Client) (interface{}, error) {
+ return &ReposExecutorRPC{client: c}, nil
+}
+
+func (p *ReposExecutorPlugin) Server(*plugin.MuxBroker) (interface{}, error) {
+ return &ReposExecutorRPCServer{Impl: p.Impl}, nil
+}
+
type InstallerExecutorInstallLocalArgs struct {
Paths []string
Opts *manager.Opts
@@ -316,3 +337,33 @@ func (s *ScriptExecutorRPCServer) ExecuteSecondPass(args *ScriptExecutorExecuteS
}
return nil
}
+
+type ReposExecutorPullOneAndUpdateFromConfigArgs struct {
+ Repo *types.Repo
+}
+
+type ReposExecutorPullOneAndUpdateFromConfigResp struct {
+ Result0 types.Repo
+}
+
+func (s *ReposExecutorRPC) PullOneAndUpdateFromConfig(ctx context.Context, repo *types.Repo) (types.Repo, error) {
+ var resp *ReposExecutorPullOneAndUpdateFromConfigResp
+ err := s.client.Call("Plugin.PullOneAndUpdateFromConfig", &ReposExecutorPullOneAndUpdateFromConfigArgs{
+ Repo: repo,
+ }, &resp)
+ if err != nil {
+ return types.Repo{}, err
+ }
+ return resp.Result0, nil
+}
+
+func (s *ReposExecutorRPCServer) PullOneAndUpdateFromConfig(args *ReposExecutorPullOneAndUpdateFromConfigArgs, resp *ReposExecutorPullOneAndUpdateFromConfigResp) error {
+ result0, err := s.Impl.PullOneAndUpdateFromConfig(context.Background(), args.Repo)
+ if err != nil {
+ return err
+ }
+ *resp = ReposExecutorPullOneAndUpdateFromConfigResp{
+ Result0: result0,
+ }
+ return nil
+}
diff --git a/internal/build/repos_executor.go b/internal/build/repos_executor.go
new file mode 100644
index 0000000..4336a0b
--- /dev/null
+++ b/internal/build/repos_executor.go
@@ -0,0 +1,37 @@
+// ALR - Any Linux Repository
+// Copyright (C) 2025 The ALR Authors
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+package build
+
+import (
+ "context"
+
+ "gitea.plemya-x.ru/Plemya-x/ALR/internal/repos"
+ "gitea.plemya-x.ru/Plemya-x/ALR/pkg/types"
+)
+
+type reposExecutor struct{ r *repos.Repos }
+
+func NewRepos(r *repos.Repos) ReposExecutor {
+ return &reposExecutor{r}
+}
+
+func (r *reposExecutor) PullOneAndUpdateFromConfig(ctx context.Context, repo *types.Repo) (types.Repo, error) {
+ if err := r.r.PullOneAndUpdateFromConfig(ctx, repo); err != nil {
+ return *repo, err
+ }
+ return *repo, nil
+}
diff --git a/internal/repos/pull.go b/internal/repos/pull.go
index e0944d8..f0da904 100644
--- a/internal/repos/pull.go
+++ b/internal/repos/pull.go
@@ -68,7 +68,7 @@ func (rs *Repos) Pull(ctx context.Context, repos []types.Repo) error {
}
for _, repo := range repos {
- err := rs.pullRepo(ctx, repo)
+ err := rs.pullRepo(ctx, &repo, false)
if err != nil {
return err
}
@@ -77,7 +77,16 @@ func (rs *Repos) Pull(ctx context.Context, repos []types.Repo) error {
return nil
}
-func (rs *Repos) pullRepo(ctx context.Context, repo types.Repo) error {
+func (rs *Repos) PullOneAndUpdateFromConfig(ctx context.Context, repo *types.Repo) error {
+ err := rs.pullRepo(ctx, repo, true)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (rs *Repos) pullRepo(ctx context.Context, repo *types.Repo, updateRepoFromToml bool) error {
urls := []string{repo.URL}
urls = append(urls, repo.Mirrors...)
@@ -88,7 +97,7 @@ func (rs *Repos) pullRepo(ctx context.Context, repo types.Repo) error {
slog.Info(gotext.Get("Trying mirror"), "repo", repo.Name, "mirror", repoURL)
}
- err := rs.pullRepoFromURL(ctx, repoURL, repo)
+ err := rs.pullRepoFromURL(ctx, repoURL, repo, updateRepoFromToml)
if err != nil {
lastErr = err
slog.Warn(gotext.Get("Failed to pull from URL"), "repo", repo.Name, "url", repoURL, "error", err)
@@ -149,7 +158,7 @@ func readGitRepo(repoDir, repoUrl string) (*git.Repository, bool, error) {
return r, true, nil
}
-func (rs *Repos) pullRepoFromURL(ctx context.Context, rawRepoUrl string, repo types.Repo) error {
+func (rs *Repos) pullRepoFromURL(ctx context.Context, rawRepoUrl string, repo *types.Repo, update bool) error {
repoURL, err := url.Parse(rawRepoUrl)
if err != nil {
return fmt.Errorf("invalid URL %s: %w", rawRepoUrl, err)
@@ -214,12 +223,12 @@ func (rs *Repos) pullRepoFromURL(ctx context.Context, rawRepoUrl string, repo ty
// empty. In this case, we need to update the DB fully
// rather than just incrementally.
if rs.db.IsEmpty() || freshGit {
- err = rs.processRepoFull(ctx, repo, repoDir)
+ err = rs.processRepoFull(ctx, *repo, repoDir)
if err != nil {
return err
}
} else {
- err = rs.processRepoChanges(ctx, repo, r, w, old, new)
+ err = rs.processRepoChanges(ctx, *repo, r, w, old, new)
if err != nil {
return err
}
@@ -247,6 +256,18 @@ func (rs *Repos) pullRepoFromURL(ctx context.Context, rawRepoUrl string, repo ty
}
}
+ if update {
+ if repoCfg.Repo.URL != "" {
+ repo.URL = repoCfg.Repo.URL
+ }
+ if repoCfg.Repo.Ref != "" {
+ repo.Ref = repoCfg.Repo.Ref
+ }
+ if len(repoCfg.Repo.Mirrors) > 0 {
+ repo.Mirrors = repoCfg.Repo.Mirrors
+ }
+ }
+
return nil
}
diff --git a/internal/translations/default.pot b/internal/translations/default.pot
index 2d098e1..687cd59 100644
--- a/internal/translations/default.pot
+++ b/internal/translations/default.pot
@@ -407,27 +407,27 @@ msgstr ""
msgid "ERROR"
msgstr ""
-#: internal/repos/pull.go:88
+#: internal/repos/pull.go:97
msgid "Trying mirror"
msgstr ""
-#: internal/repos/pull.go:94
+#: internal/repos/pull.go:103
msgid "Failed to pull from URL"
msgstr ""
-#: internal/repos/pull.go:158
+#: internal/repos/pull.go:167
msgid "Pulling repository"
msgstr ""
-#: internal/repos/pull.go:195
+#: internal/repos/pull.go:204
msgid "Repository up to date"
msgstr ""
-#: internal/repos/pull.go:230
+#: internal/repos/pull.go:239
msgid "Git repository does not appear to be a valid ALR repo"
msgstr ""
-#: internal/repos/pull.go:246
+#: internal/repos/pull.go:255
msgid ""
"ALR repo's minimum ALR version is greater than the current version. Try "
"updating ALR if something doesn't work."
@@ -481,11 +481,11 @@ msgstr ""
msgid "Enable interactive questions and prompts"
msgstr ""
-#: main.go:147
+#: main.go:148
msgid "Show help"
msgstr ""
-#: main.go:151
+#: main.go:152
msgid "Error while running app"
msgstr ""
@@ -517,44 +517,44 @@ msgstr ""
msgid "Pull all repositories that have changed"
msgstr ""
-#: repo.go:41
+#: repo.go:42
msgid "Manage repos"
msgstr ""
-#: repo.go:55 repo.go:625
+#: repo.go:56 repo.go:625
msgid "Remove an existing repository"
msgstr ""
-#: repo.go:57 repo.go:521
+#: repo.go:58 repo.go:521
msgid ""
msgstr ""
-#: repo.go:102 repo.go:465 repo.go:568
+#: repo.go:103 repo.go:465 repo.go:568
msgid "Repo \"%s\" does not exist"
msgstr ""
-#: repo.go:109
+#: repo.go:110
msgid "Error removing repo directory"
msgstr ""
-#: repo.go:113 repo.go:180 repo.go:253 repo.go:316 repo.go:389 repo.go:504
+#: repo.go:114 repo.go:195 repo.go:253 repo.go:316 repo.go:389 repo.go:504
#: repo.go:576
msgid "Error saving config"
msgstr ""
-#: repo.go:132
+#: repo.go:133
msgid "Error removing packages from database"
msgstr ""
-#: repo.go:143 repo.go:595
+#: repo.go:144 repo.go:595
msgid "Add a new repository"
msgstr ""
-#: repo.go:144 repo.go:270 repo.go:345 repo.go:402
+#: repo.go:145 repo.go:270 repo.go:345 repo.go:402
msgid " "
msgstr ""
-#: repo.go:169
+#: repo.go:170
msgid "Repo \"%s\" already exists"
msgstr ""
diff --git a/internal/translations/po/ru/default.po b/internal/translations/po/ru/default.po
index 2c054f1..6d9ba8f 100644
--- a/internal/translations/po/ru/default.po
+++ b/internal/translations/po/ru/default.po
@@ -421,27 +421,27 @@ msgstr ""
msgid "ERROR"
msgstr "ОШИБКА"
-#: internal/repos/pull.go:88
+#: internal/repos/pull.go:97
msgid "Trying mirror"
msgstr "Пробую зеркало"
-#: internal/repos/pull.go:94
+#: internal/repos/pull.go:103
msgid "Failed to pull from URL"
msgstr "Не удалось извлечь из URL"
-#: internal/repos/pull.go:158
+#: internal/repos/pull.go:167
msgid "Pulling repository"
msgstr "Скачивание репозитория"
-#: internal/repos/pull.go:195
+#: internal/repos/pull.go:204
msgid "Repository up to date"
msgstr "Репозиторий уже обновлён"
-#: internal/repos/pull.go:230
+#: internal/repos/pull.go:239
msgid "Git repository does not appear to be a valid ALR repo"
msgstr "Репозиторий Git не поддерживается репозиторием ALR"
-#: internal/repos/pull.go:246
+#: internal/repos/pull.go:255
msgid ""
"ALR repo's minimum ALR version is greater than the current version. Try "
"updating ALR if something doesn't work."
@@ -497,11 +497,11 @@ msgstr "Аргументы, которые будут переданы мене
msgid "Enable interactive questions and prompts"
msgstr "Включение интерактивных вопросов и запросов"
-#: main.go:147
+#: main.go:148
msgid "Show help"
msgstr "Показать справку"
-#: main.go:151
+#: main.go:152
msgid "Error while running app"
msgstr "Ошибка при запуске приложения"
@@ -533,44 +533,44 @@ msgstr "%s %s загружается — %s/с\n"
msgid "Pull all repositories that have changed"
msgstr "Скачать все изменённые репозитории"
-#: repo.go:41
+#: repo.go:42
msgid "Manage repos"
msgstr "Управление репозиториями"
-#: repo.go:55 repo.go:625
+#: repo.go:56 repo.go:625
msgid "Remove an existing repository"
msgstr "Удалить существующий репозиторий"
-#: repo.go:57 repo.go:521
+#: repo.go:58 repo.go:521
msgid ""
msgstr "<имя>"
-#: repo.go:102 repo.go:465 repo.go:568
+#: repo.go:103 repo.go:465 repo.go:568
msgid "Repo \"%s\" does not exist"
msgstr "Репозитория \"%s\" не существует"
-#: repo.go:109
+#: repo.go:110
msgid "Error removing repo directory"
msgstr "Ошибка при удалении каталога репозитория"
-#: repo.go:113 repo.go:180 repo.go:253 repo.go:316 repo.go:389 repo.go:504
+#: repo.go:114 repo.go:195 repo.go:253 repo.go:316 repo.go:389 repo.go:504
#: repo.go:576
msgid "Error saving config"
msgstr "Ошибка при сохранении конфигурации"
-#: repo.go:132
+#: repo.go:133
msgid "Error removing packages from database"
msgstr "Ошибка при удалении пакетов из базы данных"
-#: repo.go:143 repo.go:595
+#: repo.go:144 repo.go:595
msgid "Add a new repository"
msgstr "Добавить новый репозиторий"
-#: repo.go:144 repo.go:270 repo.go:345 repo.go:402
+#: repo.go:145 repo.go:270 repo.go:345 repo.go:402
msgid " "
msgstr "<имя> "
-#: repo.go:169
+#: repo.go:170
msgid "Repo \"%s\" already exists"
msgstr "Репозиторий \"%s\" уже существует"
diff --git a/internal/utils/cmd.go b/internal/utils/cmd.go
index 5753f2f..3b4025f 100644
--- a/internal/utils/cmd.go
+++ b/internal/utils/cmd.go
@@ -131,11 +131,11 @@ func EnsureIsAlrUser() error {
}
newUid := syscall.Getuid()
if newUid != uid {
- return errors.New("new uid don't matches requested")
+ return errors.New("uid don't matches requested")
}
newGid := syscall.Getgid()
if newGid != gid {
- return errors.New("new gid don't matches requested")
+ return errors.New("gid don't matches requested")
}
return nil
}
diff --git a/main.go b/main.go
index af84885..368ec3d 100644
--- a/main.go
+++ b/main.go
@@ -88,6 +88,7 @@ func GetApp() *cli.App {
InternalBuildCmd(),
InternalInstallCmd(),
InternalMountCmd(),
+ InternalReposCmd(),
},
Before: func(c *cli.Context) error {
if trimmed := strings.TrimSpace(c.String("pm-args")); trimmed != "" {
diff --git a/pkg/dl/git.go b/pkg/dl/git.go
index b5380b1..26f8de5 100644
--- a/pkg/dl/git.go
+++ b/pkg/dl/git.go
@@ -22,6 +22,7 @@ package dl
import (
"context"
"errors"
+ "fmt"
"net/url"
"path"
"strconv"
@@ -149,6 +150,7 @@ func (d *GitDownloader) Update(opts Options) (bool, error) {
u.Scheme = strings.TrimPrefix(u.Scheme, "git+")
query := u.Query()
+ rev := query.Get("~rev")
query.Del("~rev")
depthStr := query.Get("~depth")
@@ -177,27 +179,67 @@ func (d *GitDownloader) Update(opts Options) (bool, error) {
}
}
- po := &git.PullOptions{
- Depth: depth,
- Progress: opts.Progress,
- RecurseSubmodules: git.NoRecurseSubmodules,
- }
-
- if recursive == "true" {
- po.RecurseSubmodules = git.DefaultSubmoduleRecursionDepth
+ // First, we do a fetch to get all the revisions.
+ fo := &git.FetchOptions{
+ Depth: depth,
+ Progress: opts.Progress,
}
m, err := getManifest(opts.Destination)
manifestOK := err == nil
- err = w.Pull(po)
- if err != nil {
- if errors.Is(err, git.NoErrAlreadyUpToDate) {
- return false, nil
- }
+ err = r.Fetch(fo)
+ if err != nil && !errors.Is(err, git.NoErrAlreadyUpToDate) {
return false, err
}
+ // If a revision is specified, switch to it.
+ if rev != "" {
+ // We are trying to find the revision as a hash of the commit
+ hash, err := r.ResolveRevision(plumbing.Revision(rev))
+ if err != nil {
+ return false, fmt.Errorf("failed to resolve revision %s: %w", rev, err)
+ }
+
+ err = w.Checkout(&git.CheckoutOptions{
+ Hash: *hash,
+ })
+ if err != nil {
+ return false, fmt.Errorf("failed to checkout revision %s: %w", rev, err)
+ }
+
+ if recursive == "true" {
+ submodules, err := w.Submodules()
+ if err == nil {
+ err = submodules.Update(&git.SubmoduleUpdateOptions{
+ Init: true,
+ })
+ if err != nil {
+ return false, fmt.Errorf("failed to update submodules %s: %w", rev, err)
+ }
+ }
+ }
+ } else {
+ // If the revision is not specified, we do a regular pull.
+ po := &git.PullOptions{
+ Depth: depth,
+ Progress: opts.Progress,
+ RecurseSubmodules: git.NoRecurseSubmodules,
+ }
+
+ if recursive == "true" {
+ po.RecurseSubmodules = git.DefaultSubmoduleRecursionDepth
+ }
+
+ err = w.Pull(po)
+ if err != nil {
+ if errors.Is(err, git.NoErrAlreadyUpToDate) {
+ return false, nil
+ }
+ return false, err
+ }
+ }
+
err = VerifyHashFromLocal("", opts)
if err != nil {
return false, err
diff --git a/pkg/dl/git_test.go b/pkg/dl/git_test.go
index 1c65db4..eaf3fbf 100644
--- a/pkg/dl/git_test.go
+++ b/pkg/dl/git_test.go
@@ -153,7 +153,7 @@ func TestGitDownloaderUpdate(t *testing.T) {
assert.NoError(t, err)
updated, err := d.Update(dl.Options{
- URL: "git+https://gitea.plemya-x.ru/Plemya-x/repo-for-tests.git~rev=test-update-git-downloader",
+ URL: "git+https://gitea.plemya-x.ru/Plemya-x/repo-for-tests.git?~rev=test-update-git-downloader",
Destination: dest,
Hash: hsh,
HashAlgorithm: "sha256",
diff --git a/pkg/types/repo.go b/pkg/types/repo.go
index 43ef92d..4c6ad48 100644
--- a/pkg/types/repo.go
+++ b/pkg/types/repo.go
@@ -22,6 +22,9 @@ package types
// RepoConfig represents a ALR repo's alr-repo.toml file.
type RepoConfig struct {
Repo struct {
- MinVersion string `toml:"minVersion"`
+ MinVersion string `toml:"minVersion"`
+ URL string `toml:"url"`
+ Ref string `toml:"ref"`
+ Mirrors []string `toml:"mirrors"`
}
}
diff --git a/repo.go b/repo.go
index 7fc9e4b..5669e5f 100644
--- a/repo.go
+++ b/repo.go
@@ -29,6 +29,7 @@ import (
"github.com/urfave/cli/v2"
"golang.org/x/exp/slices"
+ "gitea.plemya-x.ru/Plemya-x/ALR/internal/build"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils"
appbuilder "gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils/app_builder"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/utils"
@@ -169,10 +170,24 @@ func AddRepoCmd() *cli.Command {
return cliutils.FormatCliExit(gotext.Get("Repo \"%s\" already exists", repo.Name), nil)
}
}
- reposSlice = append(reposSlice, types.Repo{
+
+ newRepo := types.Repo{
Name: name,
URL: repoURL,
- })
+ }
+
+ r, close, err := build.GetSafeReposExecutor()
+ if err != nil {
+ return err
+ }
+ defer close()
+
+ newRepo, err = r.PullOneAndUpdateFromConfig(c.Context, &newRepo)
+ if err != nil {
+ return err
+ }
+
+ reposSlice = append(reposSlice, newRepo)
cfg.SetRepos(reposSlice)
err = cfg.System.Save()
@@ -180,21 +195,6 @@ func AddRepoCmd() *cli.Command {
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
}),
}