Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
aa08c04e0c | |||
f42be105ad | |||
1cc408ad7d | |||
4899e203bb | |||
67a6cb31de | |||
5e24940ef8 |
@ -36,11 +36,14 @@ linters:
|
||||
- unused
|
||||
- errcheck
|
||||
- typecheck
|
||||
# - forbidigo
|
||||
- wrapcheck
|
||||
|
||||
issues:
|
||||
fix: true
|
||||
exclude-rules:
|
||||
- linters:
|
||||
- wrapcheck
|
||||
path-except: "internal/repos/find.go"
|
||||
- path: _test\.go
|
||||
linters:
|
||||
- errcheck
|
||||
|
@ -11,7 +11,7 @@
|
||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
||||
<text x="33.5" y="15" fill="#010101" fill-opacity=".3">coverage</text>
|
||||
<text x="33.5" y="14">coverage</text>
|
||||
<text x="86" y="15" fill="#010101" fill-opacity=".3">19.2%</text>
|
||||
<text x="86" y="14">19.2%</text>
|
||||
<text x="86" y="15" fill="#010101" fill-opacity=".3">18.9%</text>
|
||||
<text x="86" y="14">18.9%</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 926 B After Width: | Height: | Size: 926 B |
11
build.go
11
build.go
@ -23,7 +23,6 @@ import (
|
||||
"log/slog"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/leonelquinteros/gotext"
|
||||
"github.com/urfave/cli/v2"
|
||||
@ -133,15 +132,7 @@ func BuildCmd() *cli.Command {
|
||||
// TODO: handle multiple packages
|
||||
packageInput := c.String("package")
|
||||
|
||||
arr := strings.Split(packageInput, "/")
|
||||
var packageSearch string
|
||||
if len(arr) == 2 {
|
||||
packageSearch = arr[1]
|
||||
} else {
|
||||
packageSearch = arr[0]
|
||||
}
|
||||
|
||||
pkgs, _, err := deps.Repos.FindPkgs(ctx, []string{packageSearch})
|
||||
pkgs, _, err := deps.Repos.FindPkgs(ctx, []string{packageInput})
|
||||
if err != nil {
|
||||
return cliutils.FormatCliExit("failed to find pkgs", err)
|
||||
}
|
||||
|
@ -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
|
@ -0,0 +1 @@
|
||||
alr-repo/foo-pkg 1.0.0-1
|
@ -0,0 +1,2 @@
|
||||
alr-repo/bar-pkg 1.0.0-1
|
||||
alr-repo/foo-pkg 1.0.0-1
|
@ -19,54 +19,24 @@
|
||||
package e2etests_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/efficientgo/e2e"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.alt-gnome.ru/capytest"
|
||||
)
|
||||
|
||||
func TestE2EAlrAddRepo(t *testing.T) {
|
||||
dockerMultipleRun(
|
||||
runMatrixSuite(
|
||||
t,
|
||||
"add-repo-remove-repo",
|
||||
COMMON_SYSTEMS,
|
||||
func(t *testing.T, r e2e.Runnable) {
|
||||
err := r.Exec(e2e.NewCommand(
|
||||
"sudo",
|
||||
"alr",
|
||||
"addrepo",
|
||||
"--name",
|
||||
"alr-repo",
|
||||
"--url",
|
||||
"https://gitea.plemya-x.ru/Plemya-x/alr-repo.git",
|
||||
))
|
||||
assert.NoError(t, err)
|
||||
func(t *testing.T, r capytest.Runner) {
|
||||
execShouldNoError(t, r, "sudo", "alr", "addrepo", "--name", "alr-repo", "--url", "https://gitea.plemya-x.ru/Plemya-x/alr-repo.git")
|
||||
execShouldNoError(t, r, "bash", "-c", "cat /etc/alr/alr.toml")
|
||||
execShouldNoError(t, r, "sudo", "alr", "removerepo", "--name", "alr-repo")
|
||||
|
||||
err = r.Exec(e2e.NewCommand(
|
||||
"bash",
|
||||
"-c",
|
||||
"cat /etc/alr/alr.toml",
|
||||
))
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = r.Exec(e2e.NewCommand(
|
||||
"sudo",
|
||||
"alr",
|
||||
"removerepo",
|
||||
"--name",
|
||||
"alr-repo",
|
||||
))
|
||||
assert.NoError(t, err)
|
||||
|
||||
var buf bytes.Buffer
|
||||
err = r.Exec(e2e.NewCommand(
|
||||
"bash",
|
||||
"-c",
|
||||
"cat /etc/alr/alr.toml",
|
||||
), e2e.WithExecOptionStdout(&buf))
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, buf.String(), "repo = []")
|
||||
r.Command("bash", "-c", "cat /etc/alr/alr.toml").
|
||||
ExpectStdoutContains("repo = []").
|
||||
Run(t)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -21,15 +21,15 @@ package e2etests_test
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/efficientgo/e2e"
|
||||
"go.alt-gnome.ru/capytest"
|
||||
)
|
||||
|
||||
func TestE2EBashCompletion(t *testing.T) {
|
||||
dockerMultipleRun(
|
||||
runMatrixSuite(
|
||||
t,
|
||||
"bash-completion",
|
||||
COMMON_SYSTEMS,
|
||||
func(t *testing.T, r e2e.Runnable) {
|
||||
func(t *testing.T, r capytest.Runner) {
|
||||
execShouldNoError(t, r, "alr", "install", "--generate-bash-completion")
|
||||
},
|
||||
)
|
||||
|
@ -19,84 +19,13 @@
|
||||
package e2etests_test
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/efficientgo/e2e"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
expect "github.com/tailscale/goexpect"
|
||||
"go.alt-gnome.ru/capytest"
|
||||
"go.alt-gnome.ru/capytest/providers/podman"
|
||||
)
|
||||
|
||||
// DebugWriter оборачивает io.Writer и логирует все записываемые данные.
|
||||
type DebugWriter struct {
|
||||
prefix string
|
||||
writer io.Writer
|
||||
}
|
||||
|
||||
func (d *DebugWriter) Write(p []byte) (n int, err error) {
|
||||
log.Printf("%s: Writing data: %q", d.prefix, p) // Логируем данные
|
||||
return d.writer.Write(p)
|
||||
}
|
||||
|
||||
// DebugReader оборачивает io.Reader и логирует все читаемые данные.
|
||||
type DebugReader struct {
|
||||
prefix string
|
||||
reader io.Reader
|
||||
}
|
||||
|
||||
func (d *DebugReader) Read(p []byte) (n int, err error) {
|
||||
n, err = d.reader.Read(p)
|
||||
if n > 0 {
|
||||
log.Printf("%s: Read data: %q", d.prefix, p[:n]) // Логируем данные
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func e2eSpawn(runnable e2e.Runnable, command e2e.Command, timeout time.Duration, opts ...expect.Option) (expect.Expecter, <-chan error, error, *io.PipeWriter) {
|
||||
resCh := make(chan error)
|
||||
|
||||
// Создаем pipe для stdin и stdout
|
||||
stdinReader, stdinWriter := io.Pipe()
|
||||
stdoutReader, stdoutWriter := io.Pipe()
|
||||
|
||||
debugStdinReader := &DebugReader{prefix: "STDIN", reader: stdinReader}
|
||||
debugStdoutWriter := &DebugWriter{prefix: "STDOUT", writer: stdoutWriter}
|
||||
|
||||
go func() {
|
||||
err := runnable.Exec(
|
||||
command,
|
||||
e2e.WithExecOptionStdout(debugStdoutWriter),
|
||||
e2e.WithExecOptionStdin(debugStdinReader),
|
||||
e2e.WithExecOptionStderr(debugStdoutWriter),
|
||||
)
|
||||
|
||||
resCh <- err
|
||||
}()
|
||||
|
||||
exp, chnErr, err := expect.SpawnGeneric(&expect.GenOptions{
|
||||
In: stdinWriter,
|
||||
Out: stdoutReader,
|
||||
Wait: func() error {
|
||||
return <-resCh
|
||||
},
|
||||
Close: func() error {
|
||||
stdinWriter.Close()
|
||||
stdoutReader.Close()
|
||||
return nil
|
||||
},
|
||||
Check: func() bool { return true },
|
||||
}, timeout, expect.Verbose(true), expect.VerboseWriter(os.Stdout))
|
||||
|
||||
return exp, chnErr, err, stdinWriter
|
||||
}
|
||||
|
||||
var ALL_SYSTEMS []string = []string{
|
||||
"ubuntu-24.04",
|
||||
"alt-sisyphus",
|
||||
@ -120,71 +49,20 @@ var COMMON_SYSTEMS []string = []string{
|
||||
"ubuntu-24.04",
|
||||
}
|
||||
|
||||
func dockerMultipleRun(t *testing.T, name string, ids []string, f func(t *testing.T, runnable e2e.Runnable)) {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
for _, id := range ids {
|
||||
t.Run(id, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
dockerName := fmt.Sprintf("alr-test-%s-%s", name, id)
|
||||
hash := sha256.New()
|
||||
hash.Write([]byte(dockerName))
|
||||
hashSum := hash.Sum(nil)
|
||||
hashString := hex.EncodeToString(hashSum)
|
||||
truncatedHash := hashString[:8]
|
||||
e, err := e2e.New(e2e.WithVerbose(), e2e.WithName(fmt.Sprintf("alr-%s", truncatedHash)))
|
||||
assert.NoError(t, err)
|
||||
t.Cleanup(e.Close)
|
||||
imageId := fmt.Sprintf("ghcr.io/maks1ms/alr-e2e-test-image-%s", id)
|
||||
runnable := e.Runnable(dockerName).Init(
|
||||
e2e.StartOptions{
|
||||
Image: imageId,
|
||||
Volumes: []string{
|
||||
"./alr:/tmp/alr",
|
||||
},
|
||||
Privileged: true,
|
||||
},
|
||||
)
|
||||
assert.NoError(t, e2e.StartAndWaitReady(runnable))
|
||||
err = runnable.Exec(e2e.NewCommand("/bin/alr-test-setup", "alr-install"))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = runnable.Exec(e2e.NewCommand("/bin/alr-test-setup", "passwordless-sudo-setup"))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
f(t, runnable)
|
||||
})
|
||||
}
|
||||
})
|
||||
func execShouldNoError(t *testing.T, r capytest.Runner, cmd string, args ...string) {
|
||||
t.Helper()
|
||||
r.Command(cmd, args...).ExpectSuccess().Run(t)
|
||||
}
|
||||
|
||||
func execShouldNoError(t *testing.T, r e2e.Runnable, cmd string, args ...string) {
|
||||
assert.NoError(t, r.Exec(e2e.NewCommand(cmd, args...)))
|
||||
}
|
||||
|
||||
func execShouldError(t *testing.T, r e2e.Runnable, cmd string, args ...string) {
|
||||
assert.Error(t, r.Exec(e2e.NewCommand(cmd, args...)))
|
||||
}
|
||||
|
||||
func runTestCommands(t *testing.T, r e2e.Runnable, timeout time.Duration, expects []expect.Batcher) {
|
||||
exp, _, err, _ := e2eSpawn(
|
||||
r,
|
||||
e2e.NewCommand("/bin/bash"), 25*time.Second,
|
||||
expect.Verbose(true),
|
||||
)
|
||||
assert.NoError(t, err)
|
||||
_, err = exp.ExpectBatch(
|
||||
expects,
|
||||
timeout,
|
||||
)
|
||||
assert.NoError(t, err)
|
||||
func execShouldError(t *testing.T, r capytest.Runner, cmd string, args ...string) {
|
||||
t.Helper()
|
||||
r.Command(cmd, args...).ExpectFailure().Run(t)
|
||||
}
|
||||
|
||||
const REPO_NAME_FOR_E2E_TESTS = "alr-repo"
|
||||
const REPO_URL_FOR_E2E_TESTS = "https://gitea.plemya-x.ru/Plemya-x/repo-for-tests.git"
|
||||
|
||||
func defaultPrepare(t *testing.T, r e2e.Runnable) {
|
||||
func defaultPrepare(t *testing.T, r capytest.Runner) {
|
||||
execShouldNoError(t, r,
|
||||
"sudo",
|
||||
"alr",
|
||||
@ -200,3 +78,19 @@ func defaultPrepare(t *testing.T, r e2e.Runnable) {
|
||||
"ref",
|
||||
)
|
||||
}
|
||||
|
||||
func runMatrixSuite(t *testing.T, name string, images []string, test func(t *testing.T, r capytest.Runner)) {
|
||||
t.Helper()
|
||||
for _, image := range images {
|
||||
ts := capytest.NewTestSuite(t, podman.Provider(
|
||||
podman.WithImage(fmt.Sprintf("ghcr.io/maks1ms/alr-e2e-test-image-%s", image)),
|
||||
podman.WithVolumes("./alr:/tmp/alr"),
|
||||
podman.WithPrivileged(true),
|
||||
))
|
||||
ts.BeforeEach(func(t *testing.T, r capytest.Runner) {
|
||||
execShouldNoError(t, r, "/bin/alr-test-setup", "alr-install")
|
||||
execShouldNoError(t, r, "/bin/alr-test-setup", "passwordless-sudo-setup")
|
||||
})
|
||||
ts.Run(fmt.Sprintf("%s/%s", name, image), test)
|
||||
}
|
||||
}
|
||||
|
@ -22,15 +22,15 @@ import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/efficientgo/e2e"
|
||||
"go.alt-gnome.ru/capytest"
|
||||
)
|
||||
|
||||
func TestE2EFirejailedPackage(t *testing.T) {
|
||||
dockerMultipleRun(
|
||||
runMatrixSuite(
|
||||
t,
|
||||
"firejailed-package",
|
||||
COMMON_SYSTEMS,
|
||||
func(t *testing.T, r e2e.Runnable) {
|
||||
func(t *testing.T, r capytest.Runner) {
|
||||
defaultPrepare(t, r)
|
||||
execShouldNoError(t, r, "alr", "build", "-p", fmt.Sprintf("%s/firejailed-pkg", REPO_NAME_FOR_E2E_TESTS))
|
||||
execShouldError(t, r, "alr", "build", "-p", fmt.Sprintf("%s/firejailed-pkg-incorrect", REPO_NAME_FOR_E2E_TESTS))
|
||||
|
@ -20,24 +20,15 @@ package e2etests_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/efficientgo/e2e"
|
||||
expect "github.com/tailscale/goexpect"
|
||||
"go.alt-gnome.ru/capytest"
|
||||
)
|
||||
|
||||
func TestE2EAlrFix(t *testing.T) {
|
||||
dockerMultipleRun(
|
||||
t,
|
||||
"run-fix",
|
||||
COMMON_SYSTEMS,
|
||||
func(t *testing.T, r e2e.Runnable) {
|
||||
runTestCommands(t, r, time.Second*30, []expect.Batcher{
|
||||
&expect.BSnd{S: "alr fix\n"},
|
||||
&expect.BExp{R: `--> Done`},
|
||||
&expect.BSnd{S: "echo $?\n"},
|
||||
&expect.BExp{R: `^0\n$`},
|
||||
runMatrixSuite(t, "run-fix", COMMON_SYSTEMS, func(t *testing.T, r capytest.Runner) {
|
||||
r.Command("alr", "fix").
|
||||
ExpectStderrContains("--> Done").
|
||||
ExpectSuccess().
|
||||
Run(t)
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -21,15 +21,15 @@ package e2etests_test
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/efficientgo/e2e"
|
||||
"go.alt-gnome.ru/capytest"
|
||||
)
|
||||
|
||||
func TestE2EGroupAndSummaryField(t *testing.T) {
|
||||
dockerMultipleRun(
|
||||
runMatrixSuite(
|
||||
t,
|
||||
"group-and-summary-field",
|
||||
RPM_SYSTEMS,
|
||||
func(t *testing.T, r e2e.Runnable) {
|
||||
func(t *testing.T, r capytest.Runner) {
|
||||
defaultPrepare(t, r)
|
||||
execShouldNoError(t, r, "sh", "-c", "alr search --name test-group-and-summary --format \"{{.Group.Resolved}}\" | grep ^System/Base$")
|
||||
execShouldNoError(t, r, "sh", "-c", "alr search --name test-group-and-summary --format \"{{.Summary.Resolved}}\" | grep \"^Custom summary$\"")
|
||||
|
40
e2e-tests/issue_129_repo_toml_import_test.go
Normal file
40
e2e-tests/issue_129_repo_toml_import_test.go
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
//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)
|
||||
},
|
||||
)
|
||||
}
|
63
e2e-tests/issue_130_install_test.go
Normal file
63
e2e-tests/issue_130_install_test.go
Normal file
@ -0,0 +1,63 @@
|
||||
// ALR - Any Linux Repository
|
||||
// Copyright (C) 2025 The ALR Authors
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//go:build e2e
|
||||
|
||||
package e2etests_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"go.alt-gnome.ru/capytest"
|
||||
)
|
||||
|
||||
func TestE2EIssue130Install(t *testing.T) {
|
||||
runMatrixSuite(
|
||||
t,
|
||||
"alr install {repo}/{package}",
|
||||
COMMON_SYSTEMS,
|
||||
func(t *testing.T, r capytest.Runner) {
|
||||
t.Parallel()
|
||||
defaultPrepare(t, r)
|
||||
|
||||
r.Command("sudo", "alr", "in", fmt.Sprintf("%s/foo-pkg", REPO_NAME_FOR_E2E_TESTS)).
|
||||
ExpectSuccess().
|
||||
Run(t)
|
||||
|
||||
r.Command("sudo", "alr", "in", fmt.Sprintf("%s/bar-pkg", "NOT_REPO_NAME_FOR_E2E_TESTS")).
|
||||
ExpectFailure().
|
||||
Run(t)
|
||||
},
|
||||
)
|
||||
runMatrixSuite(
|
||||
t,
|
||||
"alr install {package}+alr-{repo}",
|
||||
COMMON_SYSTEMS,
|
||||
func(t *testing.T, r capytest.Runner) {
|
||||
t.Parallel()
|
||||
defaultPrepare(t, r)
|
||||
|
||||
r.Command("sudo", "alr", "in", fmt.Sprintf("foo-pkg+alr-%s", REPO_NAME_FOR_E2E_TESTS)).
|
||||
ExpectSuccess().
|
||||
Run(t)
|
||||
|
||||
r.Command("sudo", "alr", "in", fmt.Sprintf("bar-pkg+alr-%s", "NOT_REPO_NAME_FOR_E2E_TESTS")).
|
||||
ExpectFailure().
|
||||
Run(t)
|
||||
},
|
||||
)
|
||||
}
|
@ -21,15 +21,15 @@ package e2etests_test
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/efficientgo/e2e"
|
||||
"go.alt-gnome.ru/capytest"
|
||||
)
|
||||
|
||||
func TestE2EIssue32Interactive(t *testing.T) {
|
||||
dockerMultipleRun(
|
||||
runMatrixSuite(
|
||||
t,
|
||||
"issue-32-interactive",
|
||||
COMMON_SYSTEMS,
|
||||
func(t *testing.T, r e2e.Runnable) {
|
||||
func(t *testing.T, r capytest.Runner) {
|
||||
execShouldNoError(t, r, "alr", "--interactive=false", "remove", "ca-certificates")
|
||||
execShouldNoError(t, r, "sudo", "alr", "--interactive=false", "remove", "openssl")
|
||||
execShouldNoError(t, r, "alr", "fix")
|
||||
|
@ -21,15 +21,15 @@ package e2etests_test
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/efficientgo/e2e"
|
||||
"go.alt-gnome.ru/capytest"
|
||||
)
|
||||
|
||||
func TestE2EIssue41AutoreqSkiplist(t *testing.T) {
|
||||
dockerMultipleRun(
|
||||
runMatrixSuite(
|
||||
t,
|
||||
"issue-41-autoreq-skiplist",
|
||||
AUTOREQ_AUTOPROV_SYSTEMS,
|
||||
func(t *testing.T, r e2e.Runnable) {
|
||||
func(t *testing.T, r capytest.Runner) {
|
||||
defaultPrepare(t, r)
|
||||
execShouldNoError(t, r, "alr", "build", "-p", "alr-repo/test-autoreq-autoprov")
|
||||
execShouldNoError(t, r, "sh", "-c", "rpm -qp --requires *.rpm | grep \"^/bin/sh$\"")
|
||||
|
@ -21,15 +21,15 @@ package e2etests_test
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/efficientgo/e2e"
|
||||
"go.alt-gnome.ru/capytest"
|
||||
)
|
||||
|
||||
func TestE2EIssue50InstallMultiple(t *testing.T) {
|
||||
dockerMultipleRun(
|
||||
runMatrixSuite(
|
||||
t,
|
||||
"issue-50-install-multiple",
|
||||
COMMON_SYSTEMS,
|
||||
func(t *testing.T, r e2e.Runnable) {
|
||||
func(t *testing.T, r capytest.Runner) {
|
||||
defaultPrepare(t, r)
|
||||
execShouldNoError(t, r, "sudo", "alr", "in", "foo-pkg", "bar-pkg")
|
||||
execShouldNoError(t, r, "cat", "/opt/foo")
|
||||
|
@ -21,15 +21,15 @@ package e2etests_test
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/efficientgo/e2e"
|
||||
"go.alt-gnome.ru/capytest"
|
||||
)
|
||||
|
||||
func TestE2EIssue53LcAllCInfo(t *testing.T) {
|
||||
dockerMultipleRun(
|
||||
runMatrixSuite(
|
||||
t,
|
||||
"issue-53-lc-all-c-info",
|
||||
COMMON_SYSTEMS,
|
||||
func(t *testing.T, r e2e.Runnable) {
|
||||
func(t *testing.T, r capytest.Runner) {
|
||||
defaultPrepare(t, r)
|
||||
execShouldNoError(t, r, "bash", "-c", "LANG=C alr info foo-pkg")
|
||||
},
|
||||
|
@ -21,15 +21,15 @@ package e2etests_test
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/efficientgo/e2e"
|
||||
"go.alt-gnome.ru/capytest"
|
||||
)
|
||||
|
||||
func TestE2EIssue59RmCompletion(t *testing.T) {
|
||||
dockerMultipleRun(
|
||||
runMatrixSuite(
|
||||
t,
|
||||
"issue-59-rm-completion",
|
||||
COMMON_SYSTEMS,
|
||||
func(t *testing.T, r e2e.Runnable) {
|
||||
func(t *testing.T, r capytest.Runner) {
|
||||
defaultPrepare(t, r)
|
||||
execShouldNoError(t, r, "sudo", "alr", "in", "foo-pkg", "bar-pkg")
|
||||
execShouldNoError(t, r, "sh", "-c", "alr rm --generate-bash-completion | grep ^foo-pkg$")
|
||||
|
50
e2e-tests/issue_62_list_test.go
Normal file
50
e2e-tests/issue_62_list_test.go
Normal file
@ -0,0 +1,50 @@
|
||||
// ALR - Any Linux Repository
|
||||
// Copyright (C) 2025 The ALR Authors
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//go:build e2e
|
||||
|
||||
package e2etests_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"go.alt-gnome.ru/capytest"
|
||||
)
|
||||
|
||||
func TestE2EIssue62List(t *testing.T) {
|
||||
runMatrixSuite(
|
||||
t,
|
||||
"issue-62-list",
|
||||
COMMON_SYSTEMS,
|
||||
func(t *testing.T, r capytest.Runner) {
|
||||
defaultPrepare(t, r)
|
||||
execShouldNoError(t, r, "sudo", "alr", "repo", "set-ref", "alr-repo", "bd26236cd7")
|
||||
execShouldNoError(t, r, "alr", "ref")
|
||||
|
||||
execShouldNoError(t, r, "sudo", "alr", "in", "foo-pkg")
|
||||
|
||||
r.Command("alr", "list", "-I").
|
||||
ExpectSuccess().
|
||||
ExpectStdoutMatchesSnapshot().
|
||||
Run(t)
|
||||
|
||||
r.Command("alr", "list").
|
||||
ExpectSuccess().
|
||||
ExpectStdoutMatchesSnapshot().
|
||||
Run(t)
|
||||
},
|
||||
)
|
||||
}
|
@ -21,15 +21,15 @@ package e2etests_test
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/efficientgo/e2e"
|
||||
"go.alt-gnome.ru/capytest"
|
||||
)
|
||||
|
||||
func TestE2EIssue72InstallWithDeps(t *testing.T) {
|
||||
dockerMultipleRun(
|
||||
runMatrixSuite(
|
||||
t,
|
||||
"issue-72-install-with-deps",
|
||||
COMMON_SYSTEMS,
|
||||
func(t *testing.T, r e2e.Runnable) {
|
||||
func(t *testing.T, r capytest.Runner) {
|
||||
defaultPrepare(t, r)
|
||||
execShouldNoError(t, r, "sudo", "alr", "in", "test-app-with-lib")
|
||||
},
|
||||
|
@ -21,15 +21,15 @@ package e2etests_test
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/efficientgo/e2e"
|
||||
"go.alt-gnome.ru/capytest"
|
||||
)
|
||||
|
||||
func TestE2EIssue74Upgradable(t *testing.T) {
|
||||
dockerMultipleRun(
|
||||
runMatrixSuite(
|
||||
t,
|
||||
"issue-74-upgradable",
|
||||
COMMON_SYSTEMS,
|
||||
func(t *testing.T, r e2e.Runnable) {
|
||||
func(t *testing.T, r capytest.Runner) {
|
||||
defaultPrepare(t, r)
|
||||
execShouldNoError(t, r, "sudo", "alr", "repo", "set-ref", "alr-repo", "bd26236cd7")
|
||||
execShouldNoError(t, r, "alr", "ref")
|
||||
|
@ -21,15 +21,15 @@ package e2etests_test
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/efficientgo/e2e"
|
||||
"go.alt-gnome.ru/capytest"
|
||||
)
|
||||
|
||||
func TestE2EIssue75InstallWithDeps(t *testing.T) {
|
||||
dockerMultipleRun(
|
||||
runMatrixSuite(
|
||||
t,
|
||||
"issue-75-ref-specify",
|
||||
COMMON_SYSTEMS,
|
||||
func(t *testing.T, r e2e.Runnable) {
|
||||
func(t *testing.T, r capytest.Runner) {
|
||||
defaultPrepare(t, r)
|
||||
execShouldNoError(t, r, "sudo", "alr", "repo", "set-ref", "alr-repo", "bd26236cd7")
|
||||
execShouldNoError(t, r, "sh", "-c", "test $(alr list | wc -l) -eq 2 || exit 1")
|
||||
|
@ -21,15 +21,15 @@ package e2etests_test
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/efficientgo/e2e"
|
||||
"go.alt-gnome.ru/capytest"
|
||||
)
|
||||
|
||||
func Test75SinglePackageRepo(t *testing.T) {
|
||||
dockerMultipleRun(
|
||||
runMatrixSuite(
|
||||
t,
|
||||
"issue-76-single-package-repo",
|
||||
COMMON_SYSTEMS,
|
||||
func(t *testing.T, r e2e.Runnable) {
|
||||
func(t *testing.T, r capytest.Runner) {
|
||||
execShouldNoError(t, r,
|
||||
"sudo",
|
||||
"alr",
|
||||
@ -38,8 +38,9 @@ func Test75SinglePackageRepo(t *testing.T) {
|
||||
REPO_NAME_FOR_E2E_TESTS,
|
||||
"https://gitea.plemya-x.ru/Maks1mS/test-single-package-alr-repo.git",
|
||||
)
|
||||
execShouldNoError(t, r, "sudo", "alr", "ref")
|
||||
execShouldNoError(t, r, "sudo", "alr", "repo", "set-ref", REPO_NAME_FOR_E2E_TESTS, "1075c918be")
|
||||
execShouldNoError(t, r, "alr", "ref")
|
||||
execShouldNoError(t, r, "alr", "fix")
|
||||
execShouldNoError(t, r, "sudo", "alr", "in", "test-single-repo")
|
||||
execShouldNoError(t, r, "sh", "-c", "alr list -U")
|
||||
execShouldNoError(t, r, "sh", "-c", "test $(alr list -U | wc -l) -eq 0 || exit 1")
|
||||
|
@ -21,15 +21,11 @@ package e2etests_test
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/efficientgo/e2e"
|
||||
"go.alt-gnome.ru/capytest"
|
||||
)
|
||||
|
||||
func TestE2EIssue78Mirrors(t *testing.T) {
|
||||
dockerMultipleRun(
|
||||
t,
|
||||
"issue-78-mirrors",
|
||||
COMMON_SYSTEMS,
|
||||
func(t *testing.T, r e2e.Runnable) {
|
||||
runMatrixSuite(t, "issue-78-mirrors", COMMON_SYSTEMS, func(t *testing.T, r capytest.Runner) {
|
||||
defaultPrepare(t, r)
|
||||
execShouldNoError(t, r, "sudo", "alr", "repo", "mirror", "add", REPO_NAME_FOR_E2E_TESTS, "https://gitea.plemya-x.ru/Maks1mS/repo-for-tests.git")
|
||||
execShouldNoError(t, r, "sudo", "alr", "repo", "set-url", REPO_NAME_FOR_E2E_TESTS, "https://example.com")
|
||||
|
@ -21,15 +21,15 @@ package e2etests_test
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/efficientgo/e2e"
|
||||
"go.alt-gnome.ru/capytest"
|
||||
)
|
||||
|
||||
func TestE2EIssue81MultiplePackages(t *testing.T) {
|
||||
dockerMultipleRun(
|
||||
runMatrixSuite(
|
||||
t,
|
||||
"issue-81-multiple-packages",
|
||||
COMMON_SYSTEMS,
|
||||
func(t *testing.T, r e2e.Runnable) {
|
||||
func(t *testing.T, r capytest.Runner) {
|
||||
defaultPrepare(t, r)
|
||||
execShouldNoError(t, r, "sudo", "alr", "in", "first-package-with-dashes")
|
||||
execShouldNoError(t, r, "cat", "/opt/first-package")
|
||||
|
@ -21,15 +21,15 @@ package e2etests_test
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/efficientgo/e2e"
|
||||
"go.alt-gnome.ru/capytest"
|
||||
)
|
||||
|
||||
func TestE2EIssue91MultiplePackages(t *testing.T) {
|
||||
dockerMultipleRun(
|
||||
runMatrixSuite(
|
||||
t,
|
||||
"issue-91-set-repo-ref",
|
||||
COMMON_SYSTEMS,
|
||||
func(t *testing.T, r e2e.Runnable) {
|
||||
func(t *testing.T, r capytest.Runner) {
|
||||
defaultPrepare(t, r)
|
||||
execShouldError(t, r, "sudo", "alr", "repo", "set-ref")
|
||||
execShouldError(t, r, "sudo", "alr", "repo", "set-ref", "alr-repo")
|
||||
|
@ -23,27 +23,26 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/efficientgo/e2e"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.alt-gnome.ru/capytest"
|
||||
)
|
||||
|
||||
func TestE2EIssue94TwiceBuild(t *testing.T) {
|
||||
dockerMultipleRun(
|
||||
runMatrixSuite(
|
||||
t,
|
||||
"issue-94-twice-build",
|
||||
COMMON_SYSTEMS,
|
||||
func(t *testing.T, r e2e.Runnable) {
|
||||
func(t *testing.T, r capytest.Runner) {
|
||||
defaultPrepare(t, r)
|
||||
|
||||
var stderr bytes.Buffer
|
||||
err := r.Exec(
|
||||
e2e.NewCommand("sudo", "alr", "in", "test-94-app"),
|
||||
e2e.WithExecOptionStderr(&stderr),
|
||||
)
|
||||
assert.NoError(t, err, "command failed")
|
||||
|
||||
output := stderr.String()
|
||||
assert.Equal(t, 1, strings.Count(output, "Building package name=test-94-dep"))
|
||||
r.Command("sudo", "alr", "in", "test-94-app").
|
||||
WithCaptureStderr(&stderr).
|
||||
ExpectSuccess().
|
||||
Run(t)
|
||||
|
||||
assert.Equal(t, 1, strings.Count(stderr.String(), "Building package name=test-94-dep"))
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -21,15 +21,15 @@ package e2etests_test
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/efficientgo/e2e"
|
||||
"go.alt-gnome.ru/capytest"
|
||||
)
|
||||
|
||||
func TestE2EIssue95ConfigCommand(t *testing.T) {
|
||||
dockerMultipleRun(
|
||||
runMatrixSuite(
|
||||
t,
|
||||
"issue-95-config-command",
|
||||
COMMON_SYSTEMS,
|
||||
func(t *testing.T, r e2e.Runnable) {
|
||||
func(t *testing.T, r capytest.Runner) {
|
||||
defaultPrepare(t, r)
|
||||
execShouldNoError(t, r, "sh", "-c", "alr config show | grep \"autoPull: true\"")
|
||||
execShouldNoError(t, r, "sh", "-c", "alr config get | grep \"autoPull: true\"")
|
||||
|
@ -20,25 +20,16 @@ package e2etests_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/efficientgo/e2e"
|
||||
|
||||
expect "github.com/tailscale/goexpect"
|
||||
"go.alt-gnome.ru/capytest"
|
||||
)
|
||||
|
||||
func TestE2EAlrVersion(t *testing.T) {
|
||||
dockerMultipleRun(
|
||||
t,
|
||||
"check-version",
|
||||
COMMON_SYSTEMS,
|
||||
func(t *testing.T, r e2e.Runnable) {
|
||||
runTestCommands(t, r, time.Second*10, []expect.Batcher{
|
||||
&expect.BSnd{S: "alr version\n"},
|
||||
&expect.BExp{R: `^v\d+\.\d+\.\d+(?:-\d+-g[a-f0-9]+)?\n$`},
|
||||
&expect.BSnd{S: "echo $?\n"},
|
||||
&expect.BExp{R: `^0\n$`},
|
||||
runMatrixSuite(t, "version", COMMON_SYSTEMS, func(t *testing.T, r capytest.Runner) {
|
||||
r.Command("alr", "version").
|
||||
ExpectStderrRegex(`^v\d+\.\d+\.\d+(?:-\d+-g[a-f0-9]+)?\n$`).
|
||||
ExpectStdoutEmpty().
|
||||
ExpectSuccess().
|
||||
Run(t)
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
416
generators/plugin-generator/main.go
Normal file
416
generators/plugin-generator/main.go
Normal file
@ -0,0 +1,416 @@
|
||||
// ALR - Any Linux Repository
|
||||
// Copyright (C) 2025 The ALR Authors
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/format"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
"text/template"
|
||||
"unicode"
|
||||
|
||||
"golang.org/x/text/cases"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
type MethodInfo struct {
|
||||
Name string
|
||||
Params []ParamInfo
|
||||
Results []ResultInfo
|
||||
EntityName string
|
||||
}
|
||||
|
||||
type ParamInfo struct {
|
||||
Name string
|
||||
Type string
|
||||
}
|
||||
|
||||
type ResultInfo struct {
|
||||
Name string
|
||||
Type string
|
||||
Index int
|
||||
}
|
||||
|
||||
func extractImports(node *ast.File) []string {
|
||||
var imports []string
|
||||
for _, imp := range node.Imports {
|
||||
if imp.Path.Value != "" {
|
||||
imports = append(imports, imp.Path.Value)
|
||||
}
|
||||
}
|
||||
return imports
|
||||
}
|
||||
|
||||
func output(path string, buf bytes.Buffer) {
|
||||
formatted, err := format.Source(buf.Bytes())
|
||||
if err != nil {
|
||||
log.Fatalf("formatting: %v", err)
|
||||
}
|
||||
|
||||
outPath := strings.TrimSuffix(path, ".go") + "_gen.go"
|
||||
outFile, err := os.Create(outPath)
|
||||
if err != nil {
|
||||
log.Fatalf("create file: %v", err)
|
||||
}
|
||||
_, err = outFile.Write(formatted)
|
||||
if err != nil {
|
||||
log.Fatalf("writing output: %v", err)
|
||||
}
|
||||
outFile.Close()
|
||||
}
|
||||
|
||||
func main() {
|
||||
path := os.Getenv("GOFILE")
|
||||
if path == "" {
|
||||
log.Fatal("GOFILE must be set")
|
||||
}
|
||||
|
||||
if len(os.Args) < 2 {
|
||||
log.Fatal("At least one entity name must be provided")
|
||||
}
|
||||
|
||||
entityNames := os.Args[1:]
|
||||
|
||||
fset := token.NewFileSet()
|
||||
node, err := parser.ParseFile(fset, path, nil, parser.AllErrors)
|
||||
if err != nil {
|
||||
log.Fatalf("parsing file: %v", err)
|
||||
}
|
||||
|
||||
packageName := node.Name.Name
|
||||
|
||||
// Find all specified entities
|
||||
entityData := make(map[string][]*ast.Field)
|
||||
|
||||
for _, decl := range node.Decls {
|
||||
genDecl, ok := decl.(*ast.GenDecl)
|
||||
if !ok || genDecl.Tok != token.TYPE {
|
||||
continue
|
||||
}
|
||||
for _, spec := range genDecl.Specs {
|
||||
typeSpec := spec.(*ast.TypeSpec)
|
||||
for _, entityName := range entityNames {
|
||||
if typeSpec.Name.Name == entityName {
|
||||
interfaceType, ok := typeSpec.Type.(*ast.InterfaceType)
|
||||
if !ok {
|
||||
log.Fatalf("entity %s is not an interface", entityName)
|
||||
}
|
||||
entityData[entityName] = interfaceType.Methods.List
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Verify all entities were found
|
||||
for _, entityName := range entityNames {
|
||||
if _, found := entityData[entityName]; !found {
|
||||
log.Fatalf("interface %s not found", entityName)
|
||||
}
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
|
||||
buf.WriteString(`
|
||||
// DO NOT EDIT MANUALLY. This file is generated.
|
||||
|
||||
// ALR - Any Linux Repository
|
||||
// Copyright (C) 2025 The ALR Authors
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
`)
|
||||
|
||||
buf.WriteString(fmt.Sprintf("package %s\n", packageName))
|
||||
|
||||
// Generate base structures for all entities
|
||||
baseStructs(&buf, entityNames, extractImports(node))
|
||||
|
||||
// Generate method-specific code for each entity
|
||||
for _, entityName := range entityNames {
|
||||
methods := parseMethodsFromFields(entityName, entityData[entityName])
|
||||
argsGen(&buf, methods)
|
||||
}
|
||||
|
||||
output(path, buf)
|
||||
}
|
||||
|
||||
func parseMethodsFromFields(entityName string, fields []*ast.Field) []MethodInfo {
|
||||
var methods []MethodInfo
|
||||
|
||||
for _, field := range fields {
|
||||
if len(field.Names) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
methodName := field.Names[0].Name
|
||||
funcType, ok := field.Type.(*ast.FuncType)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
method := MethodInfo{
|
||||
Name: methodName,
|
||||
EntityName: entityName,
|
||||
}
|
||||
|
||||
// Parse parameters, excluding context.Context
|
||||
if funcType.Params != nil {
|
||||
for i, param := range funcType.Params.List {
|
||||
paramType := typeToString(param.Type)
|
||||
// Skip context.Context parameters
|
||||
if paramType == "context.Context" {
|
||||
continue
|
||||
}
|
||||
if len(param.Names) == 0 {
|
||||
method.Params = append(method.Params, ParamInfo{
|
||||
Name: fmt.Sprintf("Arg%d", i),
|
||||
Type: paramType,
|
||||
})
|
||||
} else {
|
||||
for _, name := range param.Names {
|
||||
method.Params = append(method.Params, ParamInfo{
|
||||
Name: cases.Title(language.Und, cases.NoLower).String(name.Name),
|
||||
Type: paramType,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse results
|
||||
if funcType.Results != nil {
|
||||
resultIndex := 0
|
||||
for _, result := range funcType.Results.List {
|
||||
resultType := typeToString(result.Type)
|
||||
if resultType == "error" {
|
||||
continue // Skip error in response struct
|
||||
}
|
||||
|
||||
if len(result.Names) == 0 {
|
||||
method.Results = append(method.Results, ResultInfo{
|
||||
Name: fmt.Sprintf("Result%d", resultIndex),
|
||||
Type: resultType,
|
||||
Index: resultIndex,
|
||||
})
|
||||
} else {
|
||||
for _, name := range result.Names {
|
||||
method.Results = append(method.Results, ResultInfo{
|
||||
Name: cases.Title(language.Und, cases.NoLower).String(name.Name),
|
||||
Type: resultType,
|
||||
Index: resultIndex,
|
||||
})
|
||||
}
|
||||
}
|
||||
resultIndex++
|
||||
}
|
||||
}
|
||||
|
||||
methods = append(methods, method)
|
||||
}
|
||||
|
||||
return methods
|
||||
}
|
||||
|
||||
func argsGen(buf *bytes.Buffer, methods []MethodInfo) {
|
||||
// Add template functions first
|
||||
funcMap := template.FuncMap{
|
||||
"lowerFirst": func(s string) string {
|
||||
if len(s) == 0 {
|
||||
return s
|
||||
}
|
||||
return strings.ToLower(s[:1]) + s[1:]
|
||||
},
|
||||
"zeroValue": func(typeName string) string {
|
||||
typeName = strings.TrimSpace(typeName)
|
||||
|
||||
switch typeName {
|
||||
case "string":
|
||||
return "\"\""
|
||||
case "int", "int8", "int16", "int32", "int64":
|
||||
return "0"
|
||||
case "uint", "uint8", "uint16", "uint32", "uint64":
|
||||
return "0"
|
||||
case "float32", "float64":
|
||||
return "0.0"
|
||||
case "bool":
|
||||
return "false"
|
||||
}
|
||||
|
||||
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"
|
||||
},
|
||||
}
|
||||
|
||||
argsTemplate := template.Must(template.New("args").Funcs(funcMap).Parse(`
|
||||
{{range .}}
|
||||
type {{.EntityName}}{{.Name}}Args struct {
|
||||
{{range .Params}} {{.Name}} {{.Type}}
|
||||
{{end}}}
|
||||
|
||||
type {{.EntityName}}{{.Name}}Resp struct {
|
||||
{{range .Results}} {{.Name}} {{.Type}}
|
||||
{{end}}}
|
||||
|
||||
func (s *{{.EntityName}}RPC) {{.Name}}(ctx context.Context, {{range $i, $p := .Params}}{{if $i}}, {{end}}{{lowerFirst $p.Name}} {{$p.Type}}{{end}}) ({{range $i, $r := .Results}}{{if $i}}, {{end}}{{$r.Type}}{{end}}{{if .Results}}, {{end}}error) {
|
||||
var resp *{{.EntityName}}{{.Name}}Resp
|
||||
err := s.client.Call("Plugin.{{.Name}}", &{{.EntityName}}{{.Name}}Args{
|
||||
{{range .Params}} {{.Name}}: {{lowerFirst .Name}},
|
||||
{{end}} }, &resp)
|
||||
if err != nil {
|
||||
return {{range $i, $r := .Results}}{{if $i}}, {{end}}{{zeroValue $r.Type}}{{end}}{{if .Results}}, {{end}}err
|
||||
}
|
||||
return {{range $i, $r := .Results}}{{if $i}}, {{end}}resp.{{$r.Name}}{{end}}{{if .Results}}, {{end}}nil
|
||||
}
|
||||
|
||||
func (s *{{.EntityName}}RPCServer) {{.Name}}(args *{{.EntityName}}{{.Name}}Args, resp *{{.EntityName}}{{.Name}}Resp) error {
|
||||
{{if .Results}}{{range $i, $r := .Results}}{{if $i}}, {{end}}{{lowerFirst $r.Name}}{{end}}, err := {{else}}err := {{end}}s.Impl.{{.Name}}(context.Background(),{{range $i, $p := .Params}}{{if $i}}, {{end}}args.{{$p.Name}}{{end}})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
{{if .Results}}*resp = {{.EntityName}}{{.Name}}Resp{
|
||||
{{range .Results}} {{.Name}}: {{lowerFirst .Name}},
|
||||
{{end}} }
|
||||
{{else}}*resp = {{.EntityName}}{{.Name}}Resp{}
|
||||
{{end}}return nil
|
||||
}
|
||||
{{end}}
|
||||
`))
|
||||
|
||||
err := argsTemplate.Execute(buf, methods)
|
||||
if err != nil {
|
||||
log.Fatalf("execute args template: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func typeToString(expr ast.Expr) string {
|
||||
switch t := expr.(type) {
|
||||
case *ast.Ident:
|
||||
return t.Name
|
||||
case *ast.StarExpr:
|
||||
return "*" + typeToString(t.X)
|
||||
case *ast.ArrayType:
|
||||
return "[]" + typeToString(t.Elt)
|
||||
case *ast.SelectorExpr:
|
||||
xStr := typeToString(t.X)
|
||||
if xStr == "context" && t.Sel.Name == "Context" {
|
||||
return "context.Context"
|
||||
}
|
||||
return xStr + "." + t.Sel.Name
|
||||
case *ast.InterfaceType:
|
||||
return "interface{}"
|
||||
default:
|
||||
return "interface{}"
|
||||
}
|
||||
}
|
||||
|
||||
func baseStructs(buf *bytes.Buffer, entityNames, imports []string) {
|
||||
// Ensure "context" is included in imports
|
||||
updatedImports := imports
|
||||
hasContext := false
|
||||
for _, imp := range imports {
|
||||
if strings.Contains(imp, `"context"`) {
|
||||
hasContext = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !hasContext {
|
||||
updatedImports = append(updatedImports, `"context"`)
|
||||
}
|
||||
|
||||
contentTemplate := template.Must(template.New("").Parse(`
|
||||
import (
|
||||
"net/rpc"
|
||||
|
||||
"github.com/hashicorp/go-plugin"
|
||||
{{range .Imports}} {{.}}
|
||||
{{end}}
|
||||
)
|
||||
|
||||
{{range .EntityNames}}
|
||||
type {{ . }}Plugin struct {
|
||||
Impl {{ . }}
|
||||
}
|
||||
|
||||
type {{ . }}RPCServer struct {
|
||||
Impl {{ . }}
|
||||
}
|
||||
|
||||
type {{ . }}RPC struct {
|
||||
client *rpc.Client
|
||||
}
|
||||
|
||||
func (p *{{ . }}Plugin) Client(b *plugin.MuxBroker, c *rpc.Client) (interface{}, error) {
|
||||
return &{{ . }}RPC{client: c}, nil
|
||||
}
|
||||
|
||||
func (p *{{ . }}Plugin) Server(*plugin.MuxBroker) (interface{}, error) {
|
||||
return &{{ . }}RPCServer{Impl: p.Impl}, nil
|
||||
}
|
||||
|
||||
{{end}}
|
||||
`))
|
||||
err := contentTemplate.Execute(buf, struct {
|
||||
EntityNames []string
|
||||
Imports []string
|
||||
}{
|
||||
EntityNames: entityNames,
|
||||
Imports: updatedImports,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatalf("execute template: %v", err)
|
||||
}
|
||||
}
|
22
go.mod
22
go.mod
@ -1,8 +1,6 @@
|
||||
module gitea.plemya-x.ru/Plemya-x/ALR
|
||||
|
||||
go 1.23.0
|
||||
|
||||
toolchain go1.24.2
|
||||
go 1.24.4
|
||||
|
||||
require (
|
||||
gitea.plemya-x.ru/Plemya-x/fakeroot v0.0.2-0.20250408104831-427aaa7713c3
|
||||
@ -14,7 +12,6 @@ require (
|
||||
github.com/charmbracelet/bubbletea v1.2.4
|
||||
github.com/charmbracelet/lipgloss v1.0.0
|
||||
github.com/charmbracelet/log v0.4.0
|
||||
github.com/efficientgo/e2e v0.14.1-0.20240418111536-97db25a0c6c0
|
||||
github.com/go-git/go-billy/v5 v5.6.0
|
||||
github.com/go-git/go-git/v5 v5.13.0
|
||||
github.com/goccy/go-yaml v1.18.0
|
||||
@ -35,9 +32,10 @@ require (
|
||||
github.com/muesli/reflow v0.3.0
|
||||
github.com/pelletier/go-toml/v2 v2.2.4
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/tailscale/goexpect v0.0.0-20210902213824-6e8c725cea41
|
||||
github.com/urfave/cli/v2 v2.25.7
|
||||
github.com/vmihailenco/msgpack/v5 v5.3.5
|
||||
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
|
||||
@ -75,11 +73,13 @@ require (
|
||||
github.com/dlclark/regexp2 v1.10.0 // indirect
|
||||
github.com/dsnet/compress v0.0.1 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/efficientgo/core v1.0.0-rc.0 // indirect
|
||||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
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
|
||||
@ -88,7 +88,6 @@ require (
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/goterm v0.0.0-20190703233501-fc88cf888a3f // indirect
|
||||
github.com/google/rpmpack v0.6.1-0.20240329070804-c2247cbb881a // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/goreleaser/chglog v0.6.1 // indirect
|
||||
@ -105,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
|
||||
@ -124,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
|
||||
@ -131,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
|
||||
@ -139,7 +146,6 @@ require (
|
||||
go4.org v0.0.0-20200411211856-f5505b9728dd // indirect
|
||||
golang.org/x/mod v0.19.0 // indirect
|
||||
golang.org/x/net v0.38.0 // indirect
|
||||
golang.org/x/oauth2 v0.25.0 // indirect
|
||||
golang.org/x/sync v0.12.0 // indirect
|
||||
golang.org/x/term v0.30.0 // indirect
|
||||
golang.org/x/tools v0.23.0 // indirect
|
||||
|
56
go.sum
56
go.sum
@ -63,8 +63,6 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb h1:m935MPodAbYS46DG4pJSv7WO+VECIWUQ7OJYSoTrMh4=
|
||||
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI=
|
||||
github.com/bmatcuk/doublestar/v4 v4.8.1 h1:54Bopc5c2cAvhLRAzqOGCYHYyhcDHsFF4wWIR5wKP38=
|
||||
@ -82,8 +80,6 @@ github.com/caarlos0/testfs v0.4.4/go.mod h1:bRN55zgG4XCUVVHZCeU+/Tz1Q6AxEJOEJTli
|
||||
github.com/cavaliergopher/cpio v1.0.1 h1:KQFSeKmZhv0cr+kawA3a0xTQCU4QxXF1vhU7P7av2KM=
|
||||
github.com/cavaliergopher/cpio v1.0.1/go.mod h1:pBdaqQjnvXxdS/6CvNDwIANIFSP0xRKI16PX4xejRQc=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/charmbracelet/bubbles v0.20.0 h1:jSZu6qD8cRQ6k9OMfR1WlM+ruM8fkPWkHvQWD9LIutE=
|
||||
github.com/charmbracelet/bubbles v0.20.0/go.mod h1:39slydyswPy+uVOHZ5x/GjwVAFkCsV8IIVy+4MhzwwU=
|
||||
github.com/charmbracelet/bubbletea v1.2.4 h1:KN8aCViA0eps9SCOThb2/XPIlea3ANJLUkv3KnQRNCE=
|
||||
@ -108,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=
|
||||
@ -123,10 +120,6 @@ github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5Jflh
|
||||
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/efficientgo/core v1.0.0-rc.0 h1:jJoA0N+C4/knWYVZ6GrdHOtDyrg8Y/TR4vFpTaqTsqs=
|
||||
github.com/efficientgo/core v1.0.0-rc.0/go.mod h1:kQa0V74HNYMfuJH6jiPiwNdpWXl4xd/K4tzlrcvYDQI=
|
||||
github.com/efficientgo/e2e v0.14.1-0.20240418111536-97db25a0c6c0 h1:C/FNIs+MtAJgQYLJ9FX/ACFYyDRuLYoXTmueErrOJyA=
|
||||
github.com/efficientgo/e2e v0.14.1-0.20240418111536-97db25a0c6c0/go.mod h1:plsKU0YHE9uX+7utvr7SiDtVBSHJyEfHRO4UnUgDmts=
|
||||
github.com/elazarl/goproxy v1.2.1 h1:njjgvO6cRG9rIqN2ebkqy6cQz2Njkx7Fsfv/zIZqgug=
|
||||
github.com/elazarl/goproxy v1.2.1/go.mod h1:YfEbZtqP4AetfO6d40vWchF3znWX7C7Vd6ZMfdL8z64=
|
||||
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
||||
@ -142,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=
|
||||
@ -200,8 +199,6 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/goterm v0.0.0-20190703233501-fc88cf888a3f h1:5CjVwnuUcp5adK4gmY6i72gpVFVnZDP2h5TmPScB6u4=
|
||||
github.com/google/goterm v0.0.0-20190703233501-fc88cf888a3f/go.mod h1:nOFQdrUlIlx6M6ODdSpBj1NVA+VgLC6kmw60mkw34H4=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
@ -256,8 +253,6 @@ github.com/jeandeaual/go-locale v0.0.0-20241217141322-fcc2cadd6f08 h1:wMeVzrPO3m
|
||||
github.com/jeandeaual/go-locale v0.0.0-20241217141322-fcc2cadd6f08/go.mod h1:ZDXo8KHryOWSIqnsb/CiDq7hQUYryCgdVnxbj8tDG7o=
|
||||
github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c=
|
||||
github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo=
|
||||
github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
@ -298,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=
|
||||
@ -316,8 +313,6 @@ github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6T
|
||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||
github.com/mholt/archiver/v4 v4.0.0-alpha.8 h1:tRGQuDVPh66WCOelqe6LIGh0gwmfwxUrSSDunscGsRM=
|
||||
@ -343,8 +338,6 @@ github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
|
||||
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
|
||||
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
|
||||
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/nwaples/rardecode/v2 v2.0.0-beta.2 h1:e3mzJFJs4k83GXBEiTaQ5HgSc/kOK8q0rDaRO0MPaOk=
|
||||
github.com/nwaples/rardecode/v2 v2.0.0-beta.2/go.mod h1:yntwv/HfMc/Hbvtq9I19D1n58te3h6KsqCf3GxyfBGY=
|
||||
github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
|
||||
@ -361,19 +354,12 @@ 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=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v1.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk=
|
||||
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
|
||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.36.0 h1:78hJTing+BLYLjhXE+Z2BubeEymH5Lr0/Mt8FKkxxYo=
|
||||
github.com/prometheus/common v0.36.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
|
||||
github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU=
|
||||
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
@ -382,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=
|
||||
@ -415,10 +402,18 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
|
||||
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
|
||||
github.com/tailscale/goexpect v0.0.0-20210902213824-6e8c725cea41 h1:/V2rCMMWcsjYaYO2MeovLw+ClP63OtXgCF2Y1eb8+Ns=
|
||||
github.com/tailscale/goexpect v0.0.0-20210902213824-6e8c725cea41/go.mod h1:/roCdA6gg6lQyw/Oz6gIIGu3ggJKYhF+WC/AQReE5XQ=
|
||||
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=
|
||||
@ -437,6 +432,12 @@ github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsr
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
gitlab.com/digitalxero/go-conventional-commit v1.0.7 h1:8/dO6WWG+98PMhlZowt/YjuiKhqhGlOCwlIV8SqqGh8=
|
||||
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 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=
|
||||
@ -510,8 +511,6 @@ golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4Iltr
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70=
|
||||
golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@ -654,7 +653,6 @@ gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRN
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
|
39
internal.go
39
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",
|
||||
@ -125,7 +162,7 @@ func InternalInstallCmd() *cli.Command {
|
||||
plugin.Serve(&plugin.ServeConfig{
|
||||
HandshakeConfig: build.HandshakeConfig,
|
||||
Plugins: map[string]plugin.Plugin{
|
||||
"installer": &build.InstallerPlugin{
|
||||
"installer": &build.InstallerExecutorPlugin{
|
||||
Impl: build.NewInstaller(
|
||||
manager.Detect(),
|
||||
),
|
||||
|
@ -176,25 +176,6 @@ type ScriptResolverExecutor interface {
|
||||
ResolveScript(ctx context.Context, pkg *alrsh.Package) *ScriptInfo
|
||||
}
|
||||
|
||||
type ScriptExecutor interface {
|
||||
ReadScript(ctx context.Context, scriptPath string) (*alrsh.ScriptFile, error)
|
||||
ExecuteFirstPass(ctx context.Context, input *BuildInput, sf *alrsh.ScriptFile) (string, []*alrsh.Package, error)
|
||||
PrepareDirs(
|
||||
ctx context.Context,
|
||||
input *BuildInput,
|
||||
basePkg string,
|
||||
) error
|
||||
ExecuteSecondPass(
|
||||
ctx context.Context,
|
||||
input *BuildInput,
|
||||
sf *alrsh.ScriptFile,
|
||||
varsOfPackages []*alrsh.Package,
|
||||
repoDeps []string,
|
||||
builtDeps []*BuiltDep,
|
||||
basePkg string,
|
||||
) ([]*BuiltDep, error)
|
||||
}
|
||||
|
||||
type CacheExecutor interface {
|
||||
CheckForBuiltPackage(ctx context.Context, input *BuildInput, vars *alrsh.Package) (string, bool, error)
|
||||
}
|
||||
@ -211,14 +192,6 @@ type CheckerExecutor interface {
|
||||
) (bool, error)
|
||||
}
|
||||
|
||||
type InstallerExecutor interface {
|
||||
InstallLocal(paths []string, opts *manager.Opts) error
|
||||
Install(pkgs []string, opts *manager.Opts) error
|
||||
Remove(pkgs []string, opts *manager.Opts) error
|
||||
|
||||
RemoveAlreadyInstalled(pkgs []string) ([]string, error)
|
||||
}
|
||||
|
||||
type SourcesInput struct {
|
||||
Sources []string
|
||||
Checksums []string
|
||||
@ -499,6 +472,7 @@ func (b *Builder) removeBuildDeps(ctx context.Context, input interface {
|
||||
|
||||
if remove {
|
||||
err = b.installerExecutor.Remove(
|
||||
ctx,
|
||||
deps,
|
||||
&manager.Opts{
|
||||
NoConfirm: !input.BuildOpts().Interactive,
|
||||
@ -545,6 +519,7 @@ func (b *Builder) InstallALRPackages(
|
||||
}
|
||||
|
||||
err = b.installerExecutor.InstallLocal(
|
||||
ctx,
|
||||
GetBuiltPaths(res),
|
||||
&manager.Opts{
|
||||
NoConfirm: !input.BuildOpts().Interactive,
|
||||
@ -645,7 +620,7 @@ func (i *Builder) installBuildDeps(
|
||||
var deps []string
|
||||
var err error
|
||||
if len(pkgs) > 0 {
|
||||
deps, err = i.installerExecutor.RemoveAlreadyInstalled(pkgs)
|
||||
deps, err = i.installerExecutor.RemoveAlreadyInstalled(ctx, pkgs)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@ -668,7 +643,7 @@ func (i *Builder) installOptDeps(
|
||||
pkgs []string,
|
||||
) ([]*BuiltDep, error) {
|
||||
var builtDeps []*BuiltDep
|
||||
optDeps, err := i.installerExecutor.RemoveAlreadyInstalled(pkgs)
|
||||
optDeps, err := i.installerExecutor.RemoveAlreadyInstalled(ctx, pkgs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -710,7 +685,7 @@ func (i *Builder) InstallPkgs(
|
||||
}
|
||||
|
||||
if len(builtDeps) > 0 {
|
||||
err = i.installerExecutor.InstallLocal(GetBuiltPaths(builtDeps), &manager.Opts{
|
||||
err = i.installerExecutor.InstallLocal(ctx, GetBuiltPaths(builtDeps), &manager.Opts{
|
||||
NoConfirm: !input.BuildOpts().Interactive,
|
||||
})
|
||||
if err != nil {
|
||||
@ -719,7 +694,7 @@ func (i *Builder) InstallPkgs(
|
||||
}
|
||||
|
||||
if len(repoDeps) > 0 {
|
||||
err = i.installerExecutor.Install(repoDeps, &manager.Opts{
|
||||
err = i.installerExecutor.Install(ctx, repoDeps, &manager.Opts{
|
||||
NoConfirm: !input.BuildOpts().Interactive,
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -240,39 +240,10 @@ func createFirejailedBinary(
|
||||
return nil, fmt.Errorf("failed to create wrapper script: %w", err)
|
||||
}
|
||||
|
||||
profile, err := getContentFromPath(dest, dirs.PkgDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bin, err := getContentFromPath(origFilePath, dirs.PkgDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return []*files.Content{
|
||||
bin,
|
||||
profile,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getContentFromPath(path, base string) (*files.Content, error) {
|
||||
absPath := filepath.Join(base, path)
|
||||
|
||||
fi, err := os.Lstat(absPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get file info: %w", err)
|
||||
}
|
||||
|
||||
return &files.Content{
|
||||
Source: absPath,
|
||||
Destination: path,
|
||||
FileInfo: &files.ContentFileInfo{
|
||||
MTime: fi.ModTime(),
|
||||
Mode: fi.Mode(),
|
||||
Size: fi.Size(),
|
||||
},
|
||||
}, nil
|
||||
return buildContents(pkg, dirs, &[]string{
|
||||
origFilePath,
|
||||
dest,
|
||||
})
|
||||
}
|
||||
|
||||
func generateSafeName(destination string) (string, error) {
|
||||
|
@ -17,6 +17,8 @@
|
||||
package build
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gitea.plemya-x.ru/Plemya-x/ALR/internal/manager"
|
||||
)
|
||||
|
||||
@ -28,19 +30,19 @@ func NewInstaller(mgr manager.Manager) *Installer {
|
||||
|
||||
type Installer struct{ mgr manager.Manager }
|
||||
|
||||
func (i *Installer) InstallLocal(paths []string, opts *manager.Opts) error {
|
||||
func (i *Installer) InstallLocal(ctx context.Context, paths []string, opts *manager.Opts) error {
|
||||
return i.mgr.InstallLocal(opts, paths...)
|
||||
}
|
||||
|
||||
func (i *Installer) Install(pkgs []string, opts *manager.Opts) error {
|
||||
func (i *Installer) Install(ctx context.Context, pkgs []string, opts *manager.Opts) error {
|
||||
return i.mgr.Install(opts, pkgs...)
|
||||
}
|
||||
|
||||
func (i *Installer) Remove(pkgs []string, opts *manager.Opts) error {
|
||||
func (i *Installer) Remove(ctx context.Context, pkgs []string, opts *manager.Opts) error {
|
||||
return i.mgr.Remove(opts, pkgs...)
|
||||
}
|
||||
|
||||
func (i *Installer) RemoveAlreadyInstalled(pkgs []string) ([]string, error) {
|
||||
func (i *Installer) RemoveAlreadyInstalled(ctx context.Context, pkgs []string) ([]string, error) {
|
||||
filteredPackages := []string{}
|
||||
|
||||
for _, dep := range pkgs {
|
||||
|
144
internal/build/plugins.go
Normal file
144
internal/build/plugins.go
Normal file
@ -0,0 +1,144 @@
|
||||
// ALR - Any Linux Repository
|
||||
// Copyright (C) 2025 The ALR Authors
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package build
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/hashicorp/go-hclog"
|
||||
"github.com/hashicorp/go-plugin"
|
||||
|
||||
"gitea.plemya-x.ru/Plemya-x/ALR/internal/logger"
|
||||
)
|
||||
|
||||
var pluginMap = map[string]plugin.Plugin{
|
||||
"script-executor": &ScriptExecutorPlugin{},
|
||||
"installer": &InstallerExecutorPlugin{},
|
||||
"repos": &ReposExecutorPlugin{},
|
||||
}
|
||||
|
||||
var HandshakeConfig = plugin.HandshakeConfig{
|
||||
ProtocolVersion: 1,
|
||||
MagicCookieKey: "ALR_PLUGIN",
|
||||
MagicCookieValue: "-",
|
||||
}
|
||||
|
||||
func setCommonCmdEnv(cmd *exec.Cmd) {
|
||||
cmd.Env = []string{
|
||||
"HOME=/var/cache/alr",
|
||||
"LOGNAME=alr",
|
||||
"USER=alr",
|
||||
"PATH=/usr/bin:/bin:/usr/local/bin",
|
||||
}
|
||||
for _, env := range os.Environ() {
|
||||
if strings.HasPrefix(env, "LANG=") ||
|
||||
strings.HasPrefix(env, "LANGUAGE=") ||
|
||||
strings.HasPrefix(env, "LC_") ||
|
||||
strings.HasPrefix(env, "ALR_LOG_LEVEL=") {
|
||||
cmd.Env = append(cmd.Env, env)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
executable, err := os.Executable()
|
||||
if err != nil {
|
||||
var zero T
|
||||
return zero, nil, err
|
||||
}
|
||||
|
||||
cmd := exec.Command(executable, subCommand)
|
||||
setCommonCmdEnv(cmd)
|
||||
|
||||
client := plugin.NewClient(&plugin.ClientConfig{
|
||||
HandshakeConfig: HandshakeConfig,
|
||||
Plugins: pluginMap,
|
||||
Cmd: cmd,
|
||||
Logger: logger.GetHCLoggerAdapter(),
|
||||
SkipHostEnv: true,
|
||||
UnixSocketConfig: &plugin.UnixSocketConfig{
|
||||
Group: "alr",
|
||||
},
|
||||
SyncStderr: os.Stderr,
|
||||
})
|
||||
rpcClient, err := client.Client()
|
||||
if err != nil {
|
||||
var zero T
|
||||
return zero, nil, err
|
||||
}
|
||||
|
||||
var cleanupOnce sync.Once
|
||||
cleanup := func() {
|
||||
cleanupOnce.Do(func() {
|
||||
client.Kill()
|
||||
})
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err != nil {
|
||||
slog.Debug("close executor")
|
||||
cleanup()
|
||||
}
|
||||
}()
|
||||
|
||||
raw, err := rpcClient.Dispense(pluginName)
|
||||
if err != nil {
|
||||
var zero T
|
||||
return zero, nil, err
|
||||
}
|
||||
|
||||
executor, ok := raw.(T)
|
||||
if !ok {
|
||||
var zero T
|
||||
err = fmt.Errorf("dispensed object is not a %T (got %T)", zero, raw)
|
||||
return zero, nil, err
|
||||
}
|
||||
|
||||
return executor, cleanup, nil
|
||||
}
|
60
internal/build/plugins_executors.go
Normal file
60
internal/build/plugins_executors.go
Normal file
@ -0,0 +1,60 @@
|
||||
// ALR - Any Linux Repository
|
||||
// Copyright (C) 2025 The ALR Authors
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package build
|
||||
|
||||
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"
|
||||
)
|
||||
|
||||
//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.
|
||||
|
||||
type InstallerExecutor interface {
|
||||
InstallLocal(ctx context.Context, paths []string, opts *manager.Opts) error
|
||||
Install(ctx context.Context, pkgs []string, opts *manager.Opts) error
|
||||
Remove(ctx context.Context, pkgs []string, opts *manager.Opts) error
|
||||
RemoveAlreadyInstalled(ctx context.Context, pkgs []string) ([]string, error)
|
||||
}
|
||||
|
||||
type ScriptExecutor interface {
|
||||
ReadScript(ctx context.Context, scriptPath string) (*alrsh.ScriptFile, error)
|
||||
ExecuteFirstPass(ctx context.Context, input *BuildInput, sf *alrsh.ScriptFile) (string, []*alrsh.Package, error)
|
||||
PrepareDirs(
|
||||
ctx context.Context,
|
||||
input *BuildInput,
|
||||
basePkg string,
|
||||
) error
|
||||
ExecuteSecondPass(
|
||||
ctx context.Context,
|
||||
input *BuildInput,
|
||||
sf *alrsh.ScriptFile,
|
||||
varsOfPackages []*alrsh.Package,
|
||||
repoDeps []string,
|
||||
builtDeps []*BuiltDep,
|
||||
basePkg string,
|
||||
) ([]*BuiltDep, error)
|
||||
}
|
||||
|
||||
type ReposExecutor interface {
|
||||
PullOneAndUpdateFromConfig(ctx context.Context, repo *types.Repo) (types.Repo, error)
|
||||
}
|
369
internal/build/plugins_executors_gen.go
Normal file
369
internal/build/plugins_executors_gen.go
Normal file
@ -0,0 +1,369 @@
|
||||
// DO NOT EDIT MANUALLY. This file is generated.
|
||||
|
||||
// ALR - Any Linux Repository
|
||||
// Copyright (C) 2025 The ALR Authors
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package build
|
||||
|
||||
import (
|
||||
"net/rpc"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
type InstallerExecutorPlugin struct {
|
||||
Impl InstallerExecutor
|
||||
}
|
||||
|
||||
type InstallerExecutorRPCServer struct {
|
||||
Impl InstallerExecutor
|
||||
}
|
||||
|
||||
type InstallerExecutorRPC struct {
|
||||
client *rpc.Client
|
||||
}
|
||||
|
||||
func (p *InstallerExecutorPlugin) Client(b *plugin.MuxBroker, c *rpc.Client) (interface{}, error) {
|
||||
return &InstallerExecutorRPC{client: c}, nil
|
||||
}
|
||||
|
||||
func (p *InstallerExecutorPlugin) Server(*plugin.MuxBroker) (interface{}, error) {
|
||||
return &InstallerExecutorRPCServer{Impl: p.Impl}, nil
|
||||
}
|
||||
|
||||
type ScriptExecutorPlugin struct {
|
||||
Impl ScriptExecutor
|
||||
}
|
||||
|
||||
type ScriptExecutorRPCServer struct {
|
||||
Impl ScriptExecutor
|
||||
}
|
||||
|
||||
type ScriptExecutorRPC struct {
|
||||
client *rpc.Client
|
||||
}
|
||||
|
||||
func (p *ScriptExecutorPlugin) Client(b *plugin.MuxBroker, c *rpc.Client) (interface{}, error) {
|
||||
return &ScriptExecutorRPC{client: c}, nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
type InstallerExecutorInstallLocalResp struct {
|
||||
}
|
||||
|
||||
func (s *InstallerExecutorRPC) InstallLocal(ctx context.Context, paths []string, opts *manager.Opts) error {
|
||||
var resp *InstallerExecutorInstallLocalResp
|
||||
err := s.client.Call("Plugin.InstallLocal", &InstallerExecutorInstallLocalArgs{
|
||||
Paths: paths,
|
||||
Opts: opts,
|
||||
}, &resp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *InstallerExecutorRPCServer) InstallLocal(args *InstallerExecutorInstallLocalArgs, resp *InstallerExecutorInstallLocalResp) error {
|
||||
err := s.Impl.InstallLocal(context.Background(), args.Paths, args.Opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*resp = InstallerExecutorInstallLocalResp{}
|
||||
return nil
|
||||
}
|
||||
|
||||
type InstallerExecutorInstallArgs struct {
|
||||
Pkgs []string
|
||||
Opts *manager.Opts
|
||||
}
|
||||
|
||||
type InstallerExecutorInstallResp struct {
|
||||
}
|
||||
|
||||
func (s *InstallerExecutorRPC) Install(ctx context.Context, pkgs []string, opts *manager.Opts) error {
|
||||
var resp *InstallerExecutorInstallResp
|
||||
err := s.client.Call("Plugin.Install", &InstallerExecutorInstallArgs{
|
||||
Pkgs: pkgs,
|
||||
Opts: opts,
|
||||
}, &resp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *InstallerExecutorRPCServer) Install(args *InstallerExecutorInstallArgs, resp *InstallerExecutorInstallResp) error {
|
||||
err := s.Impl.Install(context.Background(), args.Pkgs, args.Opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*resp = InstallerExecutorInstallResp{}
|
||||
return nil
|
||||
}
|
||||
|
||||
type InstallerExecutorRemoveArgs struct {
|
||||
Pkgs []string
|
||||
Opts *manager.Opts
|
||||
}
|
||||
|
||||
type InstallerExecutorRemoveResp struct {
|
||||
}
|
||||
|
||||
func (s *InstallerExecutorRPC) Remove(ctx context.Context, pkgs []string, opts *manager.Opts) error {
|
||||
var resp *InstallerExecutorRemoveResp
|
||||
err := s.client.Call("Plugin.Remove", &InstallerExecutorRemoveArgs{
|
||||
Pkgs: pkgs,
|
||||
Opts: opts,
|
||||
}, &resp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *InstallerExecutorRPCServer) Remove(args *InstallerExecutorRemoveArgs, resp *InstallerExecutorRemoveResp) error {
|
||||
err := s.Impl.Remove(context.Background(), args.Pkgs, args.Opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*resp = InstallerExecutorRemoveResp{}
|
||||
return nil
|
||||
}
|
||||
|
||||
type InstallerExecutorRemoveAlreadyInstalledArgs struct {
|
||||
Pkgs []string
|
||||
}
|
||||
|
||||
type InstallerExecutorRemoveAlreadyInstalledResp struct {
|
||||
Result0 []string
|
||||
}
|
||||
|
||||
func (s *InstallerExecutorRPC) RemoveAlreadyInstalled(ctx context.Context, pkgs []string) ([]string, error) {
|
||||
var resp *InstallerExecutorRemoveAlreadyInstalledResp
|
||||
err := s.client.Call("Plugin.RemoveAlreadyInstalled", &InstallerExecutorRemoveAlreadyInstalledArgs{
|
||||
Pkgs: pkgs,
|
||||
}, &resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp.Result0, nil
|
||||
}
|
||||
|
||||
func (s *InstallerExecutorRPCServer) RemoveAlreadyInstalled(args *InstallerExecutorRemoveAlreadyInstalledArgs, resp *InstallerExecutorRemoveAlreadyInstalledResp) error {
|
||||
result0, err := s.Impl.RemoveAlreadyInstalled(context.Background(), args.Pkgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*resp = InstallerExecutorRemoveAlreadyInstalledResp{
|
||||
Result0: result0,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ScriptExecutorReadScriptArgs struct {
|
||||
ScriptPath string
|
||||
}
|
||||
|
||||
type ScriptExecutorReadScriptResp struct {
|
||||
Result0 *alrsh.ScriptFile
|
||||
}
|
||||
|
||||
func (s *ScriptExecutorRPC) ReadScript(ctx context.Context, scriptPath string) (*alrsh.ScriptFile, error) {
|
||||
var resp *ScriptExecutorReadScriptResp
|
||||
err := s.client.Call("Plugin.ReadScript", &ScriptExecutorReadScriptArgs{
|
||||
ScriptPath: scriptPath,
|
||||
}, &resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp.Result0, nil
|
||||
}
|
||||
|
||||
func (s *ScriptExecutorRPCServer) ReadScript(args *ScriptExecutorReadScriptArgs, resp *ScriptExecutorReadScriptResp) error {
|
||||
result0, err := s.Impl.ReadScript(context.Background(), args.ScriptPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*resp = ScriptExecutorReadScriptResp{
|
||||
Result0: result0,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ScriptExecutorExecuteFirstPassArgs struct {
|
||||
Input *BuildInput
|
||||
Sf *alrsh.ScriptFile
|
||||
}
|
||||
|
||||
type ScriptExecutorExecuteFirstPassResp struct {
|
||||
Result0 string
|
||||
Result1 []*alrsh.Package
|
||||
}
|
||||
|
||||
func (s *ScriptExecutorRPC) ExecuteFirstPass(ctx context.Context, input *BuildInput, sf *alrsh.ScriptFile) (string, []*alrsh.Package, error) {
|
||||
var resp *ScriptExecutorExecuteFirstPassResp
|
||||
err := s.client.Call("Plugin.ExecuteFirstPass", &ScriptExecutorExecuteFirstPassArgs{
|
||||
Input: input,
|
||||
Sf: sf,
|
||||
}, &resp)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
return resp.Result0, resp.Result1, nil
|
||||
}
|
||||
|
||||
func (s *ScriptExecutorRPCServer) ExecuteFirstPass(args *ScriptExecutorExecuteFirstPassArgs, resp *ScriptExecutorExecuteFirstPassResp) error {
|
||||
result0, result1, err := s.Impl.ExecuteFirstPass(context.Background(), args.Input, args.Sf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*resp = ScriptExecutorExecuteFirstPassResp{
|
||||
Result0: result0,
|
||||
Result1: result1,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ScriptExecutorPrepareDirsArgs struct {
|
||||
Input *BuildInput
|
||||
BasePkg string
|
||||
}
|
||||
|
||||
type ScriptExecutorPrepareDirsResp struct {
|
||||
}
|
||||
|
||||
func (s *ScriptExecutorRPC) PrepareDirs(ctx context.Context, input *BuildInput, basePkg string) error {
|
||||
var resp *ScriptExecutorPrepareDirsResp
|
||||
err := s.client.Call("Plugin.PrepareDirs", &ScriptExecutorPrepareDirsArgs{
|
||||
Input: input,
|
||||
BasePkg: basePkg,
|
||||
}, &resp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ScriptExecutorRPCServer) PrepareDirs(args *ScriptExecutorPrepareDirsArgs, resp *ScriptExecutorPrepareDirsResp) error {
|
||||
err := s.Impl.PrepareDirs(context.Background(), args.Input, args.BasePkg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*resp = ScriptExecutorPrepareDirsResp{}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ScriptExecutorExecuteSecondPassArgs struct {
|
||||
Input *BuildInput
|
||||
Sf *alrsh.ScriptFile
|
||||
VarsOfPackages []*alrsh.Package
|
||||
RepoDeps []string
|
||||
BuiltDeps []*BuiltDep
|
||||
BasePkg string
|
||||
}
|
||||
|
||||
type ScriptExecutorExecuteSecondPassResp struct {
|
||||
Result0 []*BuiltDep
|
||||
}
|
||||
|
||||
func (s *ScriptExecutorRPC) ExecuteSecondPass(ctx context.Context, input *BuildInput, sf *alrsh.ScriptFile, varsOfPackages []*alrsh.Package, repoDeps []string, builtDeps []*BuiltDep, basePkg string) ([]*BuiltDep, error) {
|
||||
var resp *ScriptExecutorExecuteSecondPassResp
|
||||
err := s.client.Call("Plugin.ExecuteSecondPass", &ScriptExecutorExecuteSecondPassArgs{
|
||||
Input: input,
|
||||
Sf: sf,
|
||||
VarsOfPackages: varsOfPackages,
|
||||
RepoDeps: repoDeps,
|
||||
BuiltDeps: builtDeps,
|
||||
BasePkg: basePkg,
|
||||
}, &resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp.Result0, nil
|
||||
}
|
||||
|
||||
func (s *ScriptExecutorRPCServer) ExecuteSecondPass(args *ScriptExecutorExecuteSecondPassArgs, resp *ScriptExecutorExecuteSecondPassResp) error {
|
||||
result0, err := s.Impl.ExecuteSecondPass(context.Background(), args.Input, args.Sf, args.VarsOfPackages, args.RepoDeps, args.BuiltDeps, args.BasePkg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*resp = ScriptExecutorExecuteSecondPassResp{
|
||||
Result0: result0,
|
||||
}
|
||||
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
|
||||
}
|
@ -17,24 +17,21 @@
|
||||
package build
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"context"
|
||||
|
||||
"gitea.plemya-x.ru/Plemya-x/ALR/internal/repos"
|
||||
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/types"
|
||||
)
|
||||
|
||||
func setCommonCmdEnv(cmd *exec.Cmd) {
|
||||
cmd.Env = []string{
|
||||
"HOME=/var/cache/alr",
|
||||
"LOGNAME=alr",
|
||||
"USER=alr",
|
||||
"PATH=/usr/bin:/bin:/usr/local/bin",
|
||||
}
|
||||
for _, env := range os.Environ() {
|
||||
if strings.HasPrefix(env, "LANG=") ||
|
||||
strings.HasPrefix(env, "LANGUAGE=") ||
|
||||
strings.HasPrefix(env, "LC_") ||
|
||||
strings.HasPrefix(env, "ALR_LOG_LEVEL=") {
|
||||
cmd.Env = append(cmd.Env, env)
|
||||
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
|
||||
}
|
@ -1,161 +0,0 @@
|
||||
// ALR - Any Linux Repository
|
||||
// Copyright (C) 2025 The ALR Authors
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package build
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net/rpc"
|
||||
"os"
|
||||
"os/exec"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"github.com/hashicorp/go-plugin"
|
||||
|
||||
"gitea.plemya-x.ru/Plemya-x/ALR/internal/logger"
|
||||
"gitea.plemya-x.ru/Plemya-x/ALR/internal/manager"
|
||||
)
|
||||
|
||||
type InstallerPlugin struct {
|
||||
Impl InstallerExecutor
|
||||
}
|
||||
|
||||
type InstallerRPC struct {
|
||||
client *rpc.Client
|
||||
}
|
||||
|
||||
type InstallerRPCServer struct {
|
||||
Impl InstallerExecutor
|
||||
}
|
||||
|
||||
type InstallArgs struct {
|
||||
PackagesOrPaths []string
|
||||
Opts *manager.Opts
|
||||
}
|
||||
|
||||
func (r *InstallerRPC) InstallLocal(paths []string, opts *manager.Opts) error {
|
||||
return r.client.Call("Plugin.InstallLocal", &InstallArgs{
|
||||
PackagesOrPaths: paths,
|
||||
Opts: opts,
|
||||
}, nil)
|
||||
}
|
||||
|
||||
func (s *InstallerRPCServer) InstallLocal(args *InstallArgs, reply *struct{}) error {
|
||||
return s.Impl.InstallLocal(args.PackagesOrPaths, args.Opts)
|
||||
}
|
||||
|
||||
func (r *InstallerRPC) Install(pkgs []string, opts *manager.Opts) error {
|
||||
return r.client.Call("Plugin.Install", &InstallArgs{
|
||||
PackagesOrPaths: pkgs,
|
||||
Opts: opts,
|
||||
}, nil)
|
||||
}
|
||||
|
||||
func (s *InstallerRPCServer) Install(args *InstallArgs, reply *struct{}) error {
|
||||
return s.Impl.Install(args.PackagesOrPaths, args.Opts)
|
||||
}
|
||||
|
||||
func (r *InstallerRPC) Remove(pkgs []string, opts *manager.Opts) error {
|
||||
return r.client.Call("Plugin.Remove", &InstallArgs{
|
||||
PackagesOrPaths: pkgs,
|
||||
Opts: opts,
|
||||
}, nil)
|
||||
}
|
||||
|
||||
func (s *InstallerRPCServer) Remove(args *InstallArgs, reply *struct{}) error {
|
||||
return s.Impl.Remove(args.PackagesOrPaths, args.Opts)
|
||||
}
|
||||
|
||||
func (r *InstallerRPC) RemoveAlreadyInstalled(paths []string) ([]string, error) {
|
||||
var val []string
|
||||
err := r.client.Call("Plugin.RemoveAlreadyInstalled", paths, &val)
|
||||
return val, err
|
||||
}
|
||||
|
||||
func (s *InstallerRPCServer) RemoveAlreadyInstalled(pkgs []string, res *[]string) error {
|
||||
vars, err := s.Impl.RemoveAlreadyInstalled(pkgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*res = vars
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *InstallerPlugin) Client(b *plugin.MuxBroker, c *rpc.Client) (interface{}, error) {
|
||||
return &InstallerRPC{client: c}, nil
|
||||
}
|
||||
|
||||
func (p *InstallerPlugin) Server(*plugin.MuxBroker) (interface{}, error) {
|
||||
return &InstallerRPCServer{Impl: p.Impl}, nil
|
||||
}
|
||||
|
||||
func GetSafeInstaller() (InstallerExecutor, func(), error) {
|
||||
var err error
|
||||
|
||||
executable, err := os.Executable()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
cmd := exec.Command(executable, "_internal-installer")
|
||||
setCommonCmdEnv(cmd)
|
||||
|
||||
slog.Debug("safe installer setup", "uid", syscall.Getuid(), "gid", syscall.Getgid())
|
||||
|
||||
client := plugin.NewClient(&plugin.ClientConfig{
|
||||
HandshakeConfig: HandshakeConfig,
|
||||
Plugins: pluginMap,
|
||||
Cmd: cmd,
|
||||
Logger: logger.GetHCLoggerAdapter(),
|
||||
SkipHostEnv: true,
|
||||
UnixSocketConfig: &plugin.UnixSocketConfig{
|
||||
Group: "alr",
|
||||
},
|
||||
SyncStderr: os.Stderr,
|
||||
})
|
||||
rpcClient, err := client.Client()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var cleanupOnce sync.Once
|
||||
cleanup := func() {
|
||||
cleanupOnce.Do(func() {
|
||||
client.Kill()
|
||||
})
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err != nil {
|
||||
slog.Debug("close installer")
|
||||
cleanup()
|
||||
}
|
||||
}()
|
||||
|
||||
raw, err := rpcClient.Dispense("installer")
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
executor, ok := raw.(InstallerExecutor)
|
||||
if !ok {
|
||||
err = fmt.Errorf("dispensed object is not a ScriptExecutor (got %T)", raw)
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return executor, cleanup, nil
|
||||
}
|
@ -1,273 +0,0 @@
|
||||
// ALR - Any Linux Repository
|
||||
// Copyright (C) 2025 The ALR Authors
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package build
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net/rpc"
|
||||
"os"
|
||||
"os/exec"
|
||||
"sync"
|
||||
|
||||
"github.com/hashicorp/go-plugin"
|
||||
|
||||
"gitea.plemya-x.ru/Plemya-x/ALR/internal/logger"
|
||||
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/alrsh"
|
||||
)
|
||||
|
||||
var HandshakeConfig = plugin.HandshakeConfig{
|
||||
ProtocolVersion: 1,
|
||||
MagicCookieKey: "ALR_PLUGIN",
|
||||
MagicCookieValue: "-",
|
||||
}
|
||||
|
||||
type ScriptExecutorPlugin struct {
|
||||
Impl ScriptExecutor
|
||||
}
|
||||
|
||||
type ScriptExecutorRPCServer struct {
|
||||
Impl ScriptExecutor
|
||||
}
|
||||
|
||||
// =============================
|
||||
//
|
||||
// ReadScript
|
||||
//
|
||||
|
||||
func (s *ScriptExecutorRPC) ReadScript(ctx context.Context, scriptPath string) (*alrsh.ScriptFile, error) {
|
||||
var resp *alrsh.ScriptFile
|
||||
err := s.client.Call("Plugin.ReadScript", scriptPath, &resp)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func (s *ScriptExecutorRPCServer) ReadScript(scriptPath string, resp *alrsh.ScriptFile) error {
|
||||
file, err := s.Impl.ReadScript(context.Background(), scriptPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*resp = *file
|
||||
return nil
|
||||
}
|
||||
|
||||
// =============================
|
||||
//
|
||||
// ExecuteFirstPass
|
||||
//
|
||||
|
||||
type ExecuteFirstPassArgs struct {
|
||||
Input *BuildInput
|
||||
Sf *alrsh.ScriptFile
|
||||
}
|
||||
|
||||
type ExecuteFirstPassResp struct {
|
||||
BasePkg string
|
||||
VarsOfPackages []*alrsh.Package
|
||||
}
|
||||
|
||||
func (s *ScriptExecutorRPC) ExecuteFirstPass(ctx context.Context, input *BuildInput, sf *alrsh.ScriptFile) (string, []*alrsh.Package, error) {
|
||||
var resp *ExecuteFirstPassResp
|
||||
err := s.client.Call("Plugin.ExecuteFirstPass", &ExecuteFirstPassArgs{
|
||||
Input: input,
|
||||
Sf: sf,
|
||||
}, &resp)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
return resp.BasePkg, resp.VarsOfPackages, nil
|
||||
}
|
||||
|
||||
func (s *ScriptExecutorRPCServer) ExecuteFirstPass(args *ExecuteFirstPassArgs, resp *ExecuteFirstPassResp) error {
|
||||
basePkg, varsOfPackages, err := s.Impl.ExecuteFirstPass(context.Background(), args.Input, args.Sf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*resp = ExecuteFirstPassResp{
|
||||
BasePkg: basePkg,
|
||||
VarsOfPackages: varsOfPackages,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// =============================
|
||||
//
|
||||
// PrepareDirs
|
||||
//
|
||||
|
||||
type PrepareDirsArgs struct {
|
||||
Input *BuildInput
|
||||
BasePkg string
|
||||
}
|
||||
|
||||
func (s *ScriptExecutorRPC) PrepareDirs(
|
||||
ctx context.Context,
|
||||
input *BuildInput,
|
||||
basePkg string,
|
||||
) error {
|
||||
err := s.client.Call("Plugin.PrepareDirs", &PrepareDirsArgs{
|
||||
Input: input,
|
||||
BasePkg: basePkg,
|
||||
}, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *ScriptExecutorRPCServer) PrepareDirs(args *PrepareDirsArgs, reply *struct{}) error {
|
||||
err := s.Impl.PrepareDirs(
|
||||
context.Background(),
|
||||
args.Input,
|
||||
args.BasePkg,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// =============================
|
||||
//
|
||||
// ExecuteSecondPass
|
||||
//
|
||||
|
||||
type ExecuteSecondPassArgs struct {
|
||||
Input *BuildInput
|
||||
Sf *alrsh.ScriptFile
|
||||
VarsOfPackages []*alrsh.Package
|
||||
RepoDeps []string
|
||||
BuiltDeps []*BuiltDep
|
||||
BasePkg string
|
||||
}
|
||||
|
||||
func (s *ScriptExecutorRPC) ExecuteSecondPass(
|
||||
ctx context.Context,
|
||||
input *BuildInput,
|
||||
sf *alrsh.ScriptFile,
|
||||
varsOfPackages []*alrsh.Package,
|
||||
repoDeps []string,
|
||||
builtDeps []*BuiltDep,
|
||||
basePkg string,
|
||||
) ([]*BuiltDep, error) {
|
||||
var resp []*BuiltDep
|
||||
err := s.client.Call("Plugin.ExecuteSecondPass", &ExecuteSecondPassArgs{
|
||||
Input: input,
|
||||
Sf: sf,
|
||||
VarsOfPackages: varsOfPackages,
|
||||
RepoDeps: repoDeps,
|
||||
BuiltDeps: builtDeps,
|
||||
BasePkg: basePkg,
|
||||
}, &resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (s *ScriptExecutorRPCServer) ExecuteSecondPass(args *ExecuteSecondPassArgs, resp *[]*BuiltDep) error {
|
||||
res, err := s.Impl.ExecuteSecondPass(
|
||||
context.Background(),
|
||||
args.Input,
|
||||
args.Sf,
|
||||
args.VarsOfPackages,
|
||||
args.RepoDeps,
|
||||
args.BuiltDeps,
|
||||
args.BasePkg,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*resp = res
|
||||
return err
|
||||
}
|
||||
|
||||
//
|
||||
// ============================
|
||||
//
|
||||
|
||||
func (p *ScriptExecutorPlugin) Server(*plugin.MuxBroker) (interface{}, error) {
|
||||
return &ScriptExecutorRPCServer{Impl: p.Impl}, nil
|
||||
}
|
||||
|
||||
func (p *ScriptExecutorPlugin) Client(b *plugin.MuxBroker, c *rpc.Client) (interface{}, error) {
|
||||
return &ScriptExecutorRPC{client: c}, nil
|
||||
}
|
||||
|
||||
type ScriptExecutorRPC struct {
|
||||
client *rpc.Client
|
||||
}
|
||||
|
||||
var pluginMap = map[string]plugin.Plugin{
|
||||
"script-executor": &ScriptExecutorPlugin{},
|
||||
"installer": &InstallerPlugin{},
|
||||
}
|
||||
|
||||
func GetSafeScriptExecutor() (ScriptExecutor, func(), error) {
|
||||
var err error
|
||||
|
||||
executable, err := os.Executable()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
cmd := exec.Command(executable, "_internal-safe-script-executor")
|
||||
setCommonCmdEnv(cmd)
|
||||
|
||||
client := plugin.NewClient(&plugin.ClientConfig{
|
||||
HandshakeConfig: HandshakeConfig,
|
||||
Plugins: pluginMap,
|
||||
Cmd: cmd,
|
||||
Logger: logger.GetHCLoggerAdapter(),
|
||||
SkipHostEnv: true,
|
||||
UnixSocketConfig: &plugin.UnixSocketConfig{
|
||||
Group: "alr",
|
||||
},
|
||||
SyncStderr: os.Stderr,
|
||||
})
|
||||
rpcClient, err := client.Client()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var cleanupOnce sync.Once
|
||||
cleanup := func() {
|
||||
cleanupOnce.Do(func() {
|
||||
client.Kill()
|
||||
})
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err != nil {
|
||||
slog.Debug("close script-executor")
|
||||
cleanup()
|
||||
}
|
||||
}()
|
||||
|
||||
raw, err := rpcClient.Dispense("script-executor")
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
executor, ok := raw.(ScriptExecutor)
|
||||
if !ok {
|
||||
err = fmt.Errorf("dispensed object is not a ScriptExecutor (got %T)", raw)
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return executor, cleanup, nil
|
||||
}
|
@ -22,6 +22,7 @@ package overrides
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/exp/slices"
|
||||
@ -182,3 +183,18 @@ func ReleasePlatformSpecific(release int, info *distro.OSRelease) string {
|
||||
|
||||
return fmt.Sprintf("%d", release)
|
||||
}
|
||||
|
||||
func ParseReleasePlatformSpecific(s string, info *distro.OSRelease) (int, error) {
|
||||
if info.ID == "altlinux" {
|
||||
if strings.HasPrefix(s, "alt") {
|
||||
return strconv.Atoi(s[3:])
|
||||
}
|
||||
}
|
||||
|
||||
if info.ID == "fedora" || slices.Contains(info.Like, "fedora") {
|
||||
parts := strings.SplitN(s, ".", 2)
|
||||
return strconv.Atoi(parts[0])
|
||||
}
|
||||
|
||||
return strconv.Atoi(s)
|
||||
}
|
||||
|
@ -233,5 +233,8 @@ func TestReleasePlatformSpecific(t *testing.T) {
|
||||
},
|
||||
} {
|
||||
assert.Equal(t, tc.expected, overrides.ReleasePlatformSpecific(1, tc.info))
|
||||
release, err := overrides.ParseReleasePlatformSpecific(tc.expected, tc.info)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, release)
|
||||
}
|
||||
}
|
||||
|
@ -21,44 +21,58 @@ package repos
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/alrsh"
|
||||
)
|
||||
|
||||
func (rs *Repos) FindPkgs(ctx context.Context, pkgs []string) (map[string][]alrsh.Package, []string, error) {
|
||||
found := map[string][]alrsh.Package{}
|
||||
notFound := []string(nil)
|
||||
found := make(map[string][]alrsh.Package)
|
||||
var notFound []string
|
||||
|
||||
for _, pkgName := range pkgs {
|
||||
if pkgName == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
result, err := rs.db.GetPkgs(ctx, "json_array_contains(provides, ?)", pkgName)
|
||||
var result []alrsh.Package
|
||||
var err error
|
||||
|
||||
switch {
|
||||
case strings.Contains(pkgName, "/"):
|
||||
// repo/pkg
|
||||
parts := strings.SplitN(pkgName, "/", 2)
|
||||
repo := parts[0]
|
||||
name := parts[1]
|
||||
result, err = rs.db.GetPkgs(ctx, "name = ? AND repository = ?", name, repo)
|
||||
|
||||
case strings.Contains(pkgName, "+alr-"):
|
||||
// pkg+alr-repo
|
||||
parts := strings.SplitN(pkgName, "+alr-", 2)
|
||||
name := parts[0]
|
||||
repo := parts[1]
|
||||
result, err = rs.db.GetPkgs(ctx, "name = ? AND repository = ?", name, repo)
|
||||
|
||||
default:
|
||||
result, err = rs.db.GetPkgs(ctx, "json_array_contains(provides, ?)", pkgName)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, fmt.Errorf("FindPkgs: get by provides: %w", err)
|
||||
}
|
||||
|
||||
added := 0
|
||||
for _, pkg := range result {
|
||||
added++
|
||||
found[pkgName] = append(found[pkgName], pkg)
|
||||
if len(result) == 0 {
|
||||
result, err = rs.db.GetPkgs(ctx, "name LIKE ?", pkgName)
|
||||
}
|
||||
}
|
||||
|
||||
if added == 0 {
|
||||
result, err := rs.db.GetPkgs(ctx, "name LIKE ?", pkgName)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, fmt.Errorf("FindPkgs: lookup for %q failed: %w", pkgName, err)
|
||||
}
|
||||
|
||||
for _, pkg := range result {
|
||||
added++
|
||||
found[pkgName] = append(found[pkgName], pkg)
|
||||
}
|
||||
}
|
||||
|
||||
if added == 0 {
|
||||
if len(result) == 0 {
|
||||
notFound = append(notFound, pkgName)
|
||||
} else {
|
||||
found[pkgName] = result
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -9,52 +9,52 @@ msgstr ""
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: build.go:42
|
||||
#: build.go:41
|
||||
msgid "Build a local package"
|
||||
msgstr ""
|
||||
|
||||
#: build.go:48
|
||||
#: build.go:47
|
||||
msgid "Path to the build script"
|
||||
msgstr ""
|
||||
|
||||
#: build.go:53
|
||||
#: build.go:52
|
||||
msgid "Specify subpackage in script (for multi package script only)"
|
||||
msgstr ""
|
||||
|
||||
#: build.go:58
|
||||
#: build.go:57
|
||||
msgid "Name of the package to build and its repo (example: default/go-bin)"
|
||||
msgstr ""
|
||||
|
||||
#: build.go:63
|
||||
#: build.go:62
|
||||
msgid ""
|
||||
"Build package from scratch even if there's an already built package available"
|
||||
msgstr ""
|
||||
|
||||
#: build.go:73
|
||||
#: build.go:72
|
||||
msgid "Error getting working directory"
|
||||
msgstr ""
|
||||
|
||||
#: build.go:118
|
||||
#: build.go:117
|
||||
msgid "Cannot get absolute script path"
|
||||
msgstr ""
|
||||
|
||||
#: build.go:152
|
||||
#: build.go:143
|
||||
msgid "Package not found"
|
||||
msgstr ""
|
||||
|
||||
#: build.go:165
|
||||
#: build.go:156
|
||||
msgid "Nothing to build"
|
||||
msgstr ""
|
||||
|
||||
#: build.go:222
|
||||
#: build.go:213
|
||||
msgid "Error building package"
|
||||
msgstr ""
|
||||
|
||||
#: build.go:229
|
||||
#: build.go:220
|
||||
msgid "Error moving the package"
|
||||
msgstr ""
|
||||
|
||||
#: build.go:233
|
||||
#: build.go:224
|
||||
msgid "Done"
|
||||
msgstr ""
|
||||
|
||||
@ -218,23 +218,23 @@ msgstr ""
|
||||
msgid "Error removing packages"
|
||||
msgstr ""
|
||||
|
||||
#: internal/build/build.go:378
|
||||
#: internal/build/build.go:351
|
||||
msgid "Building package"
|
||||
msgstr ""
|
||||
|
||||
#: internal/build/build.go:407
|
||||
#: internal/build/build.go:380
|
||||
msgid "The checksums array must be the same length as sources"
|
||||
msgstr ""
|
||||
|
||||
#: internal/build/build.go:449
|
||||
#: internal/build/build.go:422
|
||||
msgid "Downloading sources"
|
||||
msgstr ""
|
||||
|
||||
#: internal/build/build.go:495
|
||||
#: internal/build/build.go:468
|
||||
msgid "Would you like to remove the build dependencies?"
|
||||
msgstr ""
|
||||
|
||||
#: internal/build/build.go:571
|
||||
#: internal/build/build.go:546
|
||||
msgid "Installing dependencies"
|
||||
msgstr ""
|
||||
|
||||
@ -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."
|
||||
@ -445,30 +445,34 @@ msgstr ""
|
||||
msgid "You need to be root to perform this action"
|
||||
msgstr ""
|
||||
|
||||
#: list.go:43
|
||||
#: list.go:45
|
||||
msgid "List ALR repo packages"
|
||||
msgstr ""
|
||||
|
||||
#: list.go:57
|
||||
#: list.go:59
|
||||
msgid "Format output using a Go template"
|
||||
msgstr ""
|
||||
|
||||
#: list.go:89
|
||||
#: list.go:91
|
||||
msgid "Error getting packages for upgrade"
|
||||
msgstr ""
|
||||
|
||||
#: list.go:92
|
||||
#: list.go:94
|
||||
msgid "No packages for upgrade"
|
||||
msgstr ""
|
||||
|
||||
#: list.go:102 list.go:184
|
||||
#: list.go:104 list.go:201
|
||||
msgid "Error parsing format template"
|
||||
msgstr ""
|
||||
|
||||
#: list.go:108 list.go:188
|
||||
#: list.go:110 list.go:205
|
||||
msgid "Error executing template"
|
||||
msgstr ""
|
||||
|
||||
#: list.go:164
|
||||
msgid "Failed to parse release"
|
||||
msgstr ""
|
||||
|
||||
#: main.go:45
|
||||
msgid "Print the current ALR version and exit"
|
||||
msgstr ""
|
||||
@ -481,11 +485,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 +521,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 "<name>"
|
||||
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 "<name> <url>"
|
||||
msgstr ""
|
||||
|
||||
#: repo.go:169
|
||||
#: repo.go:170
|
||||
msgid "Repo \"%s\" already exists"
|
||||
msgstr ""
|
||||
|
||||
|
@ -5,63 +5,63 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: unnamed project\n"
|
||||
"PO-Revision-Date: 2025-06-29 21:05+0300\n"
|
||||
"PO-Revision-Date: 2025-07-09 20:38+0300\n"
|
||||
"Last-Translator: Maxim Slipenko <maks1ms@alt-gnome.ru>\n"
|
||||
"Language-Team: Russian\n"
|
||||
"Language: ru\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
|
||||
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
|
||||
"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
|
||||
"X-Generator: Gtranslator 48.0\n"
|
||||
|
||||
#: build.go:42
|
||||
#: build.go:41
|
||||
msgid "Build a local package"
|
||||
msgstr "Сборка локального пакета"
|
||||
|
||||
#: build.go:48
|
||||
#: build.go:47
|
||||
msgid "Path to the build script"
|
||||
msgstr "Путь к скрипту сборки"
|
||||
|
||||
#: build.go:53
|
||||
#: build.go:52
|
||||
msgid "Specify subpackage in script (for multi package script only)"
|
||||
msgstr "Укажите подпакет в скрипте (только для многопакетного скрипта)"
|
||||
|
||||
#: build.go:58
|
||||
#: build.go:57
|
||||
msgid "Name of the package to build and its repo (example: default/go-bin)"
|
||||
msgstr "Имя пакета для сборки и его репозиторий (пример: default/go-bin)"
|
||||
|
||||
#: build.go:63
|
||||
#: build.go:62
|
||||
msgid ""
|
||||
"Build package from scratch even if there's an already built package available"
|
||||
msgstr "Создайте пакет с нуля, даже если уже имеется готовый пакет"
|
||||
|
||||
#: build.go:73
|
||||
#: build.go:72
|
||||
msgid "Error getting working directory"
|
||||
msgstr "Ошибка при получении рабочего каталога"
|
||||
|
||||
#: build.go:118
|
||||
#: build.go:117
|
||||
msgid "Cannot get absolute script path"
|
||||
msgstr "Невозможно получить абсолютный путь к скрипту"
|
||||
|
||||
#: build.go:152
|
||||
#: build.go:143
|
||||
msgid "Package not found"
|
||||
msgstr "Пакет не найден"
|
||||
|
||||
#: build.go:165
|
||||
#: build.go:156
|
||||
msgid "Nothing to build"
|
||||
msgstr "Нечего собирать"
|
||||
|
||||
#: build.go:222
|
||||
#: build.go:213
|
||||
msgid "Error building package"
|
||||
msgstr "Ошибка при сборке пакета"
|
||||
|
||||
#: build.go:229
|
||||
#: build.go:220
|
||||
msgid "Error moving the package"
|
||||
msgstr "Ошибка при перемещении пакета"
|
||||
|
||||
#: build.go:233
|
||||
#: build.go:224
|
||||
msgid "Done"
|
||||
msgstr "Сделано"
|
||||
|
||||
@ -225,23 +225,23 @@ msgstr "Для команды remove ожидался хотя бы 1 аргум
|
||||
msgid "Error removing packages"
|
||||
msgstr "Ошибка при удалении пакетов"
|
||||
|
||||
#: internal/build/build.go:378
|
||||
#: internal/build/build.go:351
|
||||
msgid "Building package"
|
||||
msgstr "Сборка пакета"
|
||||
|
||||
#: internal/build/build.go:407
|
||||
#: internal/build/build.go:380
|
||||
msgid "The checksums array must be the same length as sources"
|
||||
msgstr "Массив контрольных сумм должен быть той же длины, что и источники"
|
||||
|
||||
#: internal/build/build.go:449
|
||||
#: internal/build/build.go:422
|
||||
msgid "Downloading sources"
|
||||
msgstr "Скачивание источников"
|
||||
|
||||
#: internal/build/build.go:495
|
||||
#: internal/build/build.go:468
|
||||
msgid "Would you like to remove the build dependencies?"
|
||||
msgstr "Хотели бы вы удалить зависимости сборки?"
|
||||
|
||||
#: internal/build/build.go:571
|
||||
#: internal/build/build.go:546
|
||||
msgid "Installing dependencies"
|
||||
msgstr "Установка зависимостей"
|
||||
|
||||
@ -404,8 +404,8 @@ msgid ""
|
||||
"This command is deprecated and would be removed in the future, use \"%s\" "
|
||||
"instead!"
|
||||
msgstr ""
|
||||
"Эта команда устарела и будет удалена в будущем, используйте вместо нее \"%s"
|
||||
"\"!"
|
||||
"Эта команда устарела и будет удалена в будущем, используйте вместо нее "
|
||||
"\"%s\"!"
|
||||
|
||||
#: internal/db/db.go:76
|
||||
msgid "Database version mismatch; resetting"
|
||||
@ -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."
|
||||
@ -461,30 +461,34 @@ msgstr "Вы должны быть членом %s чтобы выполнить
|
||||
msgid "You need to be root to perform this action"
|
||||
msgstr "Вы должны быть root чтобы выполнить это"
|
||||
|
||||
#: list.go:43
|
||||
#: list.go:45
|
||||
msgid "List ALR repo packages"
|
||||
msgstr "Список пакетов репозитория ALR"
|
||||
|
||||
#: list.go:57
|
||||
#: list.go:59
|
||||
msgid "Format output using a Go template"
|
||||
msgstr "Формат выходных данных с использованием шаблона Go"
|
||||
|
||||
#: list.go:89
|
||||
#: list.go:91
|
||||
msgid "Error getting packages for upgrade"
|
||||
msgstr "Ошибка при получении пакетов для обновления"
|
||||
|
||||
#: list.go:92
|
||||
#: list.go:94
|
||||
msgid "No packages for upgrade"
|
||||
msgstr "Нет пакетов к обновлению"
|
||||
|
||||
#: list.go:102 list.go:184
|
||||
#: list.go:104 list.go:201
|
||||
msgid "Error parsing format template"
|
||||
msgstr "Ошибка при разборе шаблона"
|
||||
|
||||
#: list.go:108 list.go:188
|
||||
#: list.go:110 list.go:205
|
||||
msgid "Error executing template"
|
||||
msgstr "Ошибка при выполнении шаблона"
|
||||
|
||||
#: list.go:164
|
||||
msgid "Failed to parse release"
|
||||
msgstr "Не удалось разобрать релиз"
|
||||
|
||||
#: main.go:45
|
||||
msgid "Print the current ALR version and exit"
|
||||
msgstr "Показать текущую версию ALR и выйти"
|
||||
@ -497,11 +501,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 +537,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 "<name>"
|
||||
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 "<name> <url>"
|
||||
msgstr "<имя> <url>"
|
||||
|
||||
#: repo.go:169
|
||||
#: repo.go:170
|
||||
msgid "Repo \"%s\" already exists"
|
||||
msgstr "Репозиторий \"%s\" уже существует"
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
37
list.go
37
list.go
@ -24,6 +24,7 @@ import (
|
||||
"log/slog"
|
||||
"os"
|
||||
"slices"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/leonelquinteros/gotext"
|
||||
@ -33,6 +34,7 @@ import (
|
||||
"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/manager"
|
||||
"gitea.plemya-x.ru/Plemya-x/ALR/internal/overrides"
|
||||
"gitea.plemya-x.ru/Plemya-x/ALR/internal/utils"
|
||||
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/alrsh"
|
||||
)
|
||||
@ -126,7 +128,12 @@ func ListCmd() *cli.Command {
|
||||
return cliutils.FormatCliExit(gotext.Get("Error getting packages"), err)
|
||||
}
|
||||
|
||||
installedAlrPackages := map[string]string{}
|
||||
type verInfo struct {
|
||||
Version string
|
||||
Release int
|
||||
}
|
||||
|
||||
installedAlrPackages := map[string]verInfo{}
|
||||
if c.Bool("installed") {
|
||||
mgr := manager.Detect()
|
||||
if mgr == nil {
|
||||
@ -144,40 +151,50 @@ func ListCmd() *cli.Command {
|
||||
if matches != nil {
|
||||
packageName := matches[build.RegexpALRPackageName.SubexpIndex("package")]
|
||||
repoName := matches[build.RegexpALRPackageName.SubexpIndex("repo")]
|
||||
installedAlrPackages[fmt.Sprintf("%s/%s", repoName, packageName)] = version
|
||||
|
||||
verInfo := verInfo{
|
||||
Version: version,
|
||||
Release: 0,
|
||||
}
|
||||
|
||||
if i := strings.LastIndex(version, "-"); i != -1 {
|
||||
verInfo.Version = version[:i]
|
||||
verInfo.Release, err = overrides.ParseReleasePlatformSpecific(version[i+1:], info)
|
||||
if err != nil {
|
||||
slog.Error(gotext.Get("Failed to parse release"), "err", err)
|
||||
return cli.Exit(err, 1)
|
||||
}
|
||||
}
|
||||
|
||||
installedAlrPackages[fmt.Sprintf("%s/%s", repoName, packageName)] = verInfo
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, pkg := range result {
|
||||
if err != nil {
|
||||
return cli.Exit(err, 1)
|
||||
}
|
||||
|
||||
if slices.Contains(cfg.IgnorePkgUpdates(), pkg.Name) {
|
||||
continue
|
||||
}
|
||||
|
||||
type packageInfo struct {
|
||||
Package *alrsh.Package
|
||||
Version string
|
||||
}
|
||||
|
||||
pkgInfo := &packageInfo{}
|
||||
pkgInfo.Package = &pkg
|
||||
pkgInfo.Version = pkg.Version
|
||||
if c.Bool("installed") {
|
||||
instVersion, ok := installedAlrPackages[fmt.Sprintf("%s/%s", pkg.Repository, pkg.Name)]
|
||||
if !ok {
|
||||
continue
|
||||
} else {
|
||||
pkgInfo.Version = instVersion
|
||||
pkg.Version = instVersion.Version
|
||||
pkg.Release = instVersion.Release
|
||||
}
|
||||
}
|
||||
|
||||
format := c.String("format")
|
||||
if format == "" {
|
||||
format = "{{.Package.Repository}}/{{.Package.Name}} {{.Version}}\n"
|
||||
format = "{{.Package.Repository}}/{{.Package.Name}} {{.Package.Version}}-{{.Package.Release}}\n"
|
||||
}
|
||||
tmpl, err := template.New("format").Parse(format)
|
||||
if err != nil {
|
||||
|
1
main.go
1
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 != "" {
|
||||
|
@ -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,6 +179,48 @@ func (d *GitDownloader) Update(opts Options) (bool, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// 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 = 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,
|
||||
@ -187,9 +231,6 @@ func (d *GitDownloader) Update(opts Options) (bool, error) {
|
||||
po.RecurseSubmodules = git.DefaultSubmoduleRecursionDepth
|
||||
}
|
||||
|
||||
m, err := getManifest(opts.Destination)
|
||||
manifestOK := err == nil
|
||||
|
||||
err = w.Pull(po)
|
||||
if err != nil {
|
||||
if errors.Is(err, git.NoErrAlreadyUpToDate) {
|
||||
@ -197,6 +238,7 @@ func (d *GitDownloader) Update(opts Options) (bool, error) {
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
err = VerifyHashFromLocal("", opts)
|
||||
if err != nil {
|
||||
|
@ -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",
|
||||
|
@ -23,5 +23,8 @@ package types
|
||||
type RepoConfig struct {
|
||||
Repo struct {
|
||||
MinVersion string `toml:"minVersion"`
|
||||
URL string `toml:"url"`
|
||||
Ref string `toml:"ref"`
|
||||
Mirrors []string `toml:"mirrors"`
|
||||
}
|
||||
}
|
||||
|
34
repo.go
34
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
|
||||
}),
|
||||
}
|
||||
|
Reference in New Issue
Block a user