From f42be105ad8de1fd97a88ef2b1b72b89a5e6b05b Mon Sep 17 00:00:00 2001 From: Maxim Slipenko Date: Mon, 7 Jul 2025 17:45:20 +0300 Subject: [PATCH] feat: add import info from alr-repo.toml --- ...oml-import-test_ubuntu-24.04_1.snap.stdout | 5 ++ e2e-tests/issue_129_repo_toml_import_test.go | 40 +++++++++++ generators/plugin-generator/main.go | 28 +++++++- go.mod | 15 +++- go.sum | 27 +++++++- internal.go | 37 ++++++++++ internal/build/plugins.go | 19 ++++++ internal/build/plugins_executors.go | 7 +- internal/build/plugins_executors_gen.go | 51 ++++++++++++++ internal/build/repos_executor.go | 37 ++++++++++ internal/repos/pull.go | 33 +++++++-- internal/translations/default.pot | 36 +++++----- internal/translations/po/ru/default.po | 36 +++++----- internal/utils/cmd.go | 4 +- main.go | 1 + pkg/dl/git.go | 68 +++++++++++++++---- pkg/dl/git_test.go | 2 +- pkg/types/repo.go | 5 +- repo.go | 34 +++++----- 19 files changed, 403 insertions(+), 82 deletions(-) create mode 100755 e2e-tests/__snapshots__/TestE2EIssue129RepoTomlImportTest_issue-129-repo-toml-import-test_ubuntu-24.04_1.snap.stdout create mode 100644 e2e-tests/issue_129_repo_toml_import_test.go create mode 100644 internal/build/repos_executor.go 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 }), }