diff --git a/go.mod b/go.mod index 9616007..a132b9f 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,8 @@ module gitea.plemya-x.ru/Plemya-x/ALR -go 1.21 +go 1.22 -toolchain go1.21.3 +toolchain go1.23.5 require ( github.com/AlecAivazis/survey/v2 v2.3.7 @@ -34,7 +34,7 @@ require ( golang.org/x/text v0.21.0 gopkg.in/yaml.v3 v3.0.1 modernc.org/sqlite v1.25.0 - mvdan.cc/sh/v3 v3.7.0 + mvdan.cc/sh/v3 v3.10.0 plemya-x.ru/fakeroot v0.0.0-20240601131003-c638a3543283 ) @@ -110,11 +110,11 @@ require ( github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect gitlab.com/digitalxero/go-conventional-commit v1.0.7 // indirect go4.org v0.0.0-20200411211856-f5505b9728dd // indirect - golang.org/x/mod v0.17.0 // indirect - golang.org/x/net v0.25.0 // indirect + golang.org/x/mod v0.18.0 // indirect + golang.org/x/net v0.26.0 // indirect golang.org/x/sync v0.10.0 // indirect - golang.org/x/term v0.24.0 // indirect - golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect + golang.org/x/term v0.25.0 // indirect + golang.org/x/tools v0.22.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect lukechampine.com/uint128 v1.2.0 // indirect modernc.org/cc/v3 v3.40.0 // indirect diff --git a/go.sum b/go.sum index 96340f2..478da9d 100644 --- a/go.sum +++ b/go.sum @@ -97,6 +97,7 @@ github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= +github.com/creack/pty v1.1.23 h1:4M6+isWdcStXEf15G/RbrMPOQj1dZ7HPZCGwE4kOeP0= github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -404,6 +405,8 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= +golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -426,6 +429,8 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -478,6 +483,8 @@ golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8= +golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= +golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -520,6 +527,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= +golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -605,6 +614,8 @@ modernc.org/z v1.7.3 h1:zDJf6iHjrnB+WRD88stbXokugjyc0/pB91ri1gO6LZY= modernc.org/z v1.7.3/go.mod h1:Ipv4tsdxZRbQyLq9Q1M6gdbkxYzdlrciF2Hi/lS7nWE= mvdan.cc/sh/v3 v3.7.0 h1:lSTjdP/1xsddtaKfGg7Myu7DnlHItd3/M2tomOcNNBg= mvdan.cc/sh/v3 v3.7.0/go.mod h1:K2gwkaesF/D7av7Kxl0HbF5kGOd2ArupNTX3X44+8l8= +mvdan.cc/sh/v3 v3.10.0 h1:v9z7N1DLZ7owyLM/SXZQkBSXcwr2IGMm2LY2pmhVXj4= +mvdan.cc/sh/v3 v3.10.0/go.mod h1:z/mSSVyLFGZzqb3ZIKojjyqIx/xbmz/UHdCSv9HmqXY= plemya-x.ru/fakeroot v0.0.0-20240601131003-c638a3543283 h1:BXCLPeA8g2M6qYngicyxyB/2Bo4J54Q9Rb+8jMmE3ik= plemya-x.ru/fakeroot v0.0.0-20240601131003-c638a3543283/go.mod h1:itzL9Jx52VXOhRaucFHuMpa3y7iwjnuLGdNvypoh/S4= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/internal/shutils/decoder/decoder.go b/internal/shutils/decoder/decoder.go index e737ade..906133d 100644 --- a/internal/shutils/decoder/decoder.go +++ b/internal/shutils/decoder/decoder.go @@ -177,7 +177,35 @@ func (d *Decoder) GetFunc(name string) (ScriptFunc, bool) { return func(ctx context.Context, opts ...interp.RunnerOption) error { sub := d.Runner.Subshell() for _, opt := range opts { - opt(sub) + err := opt(sub) + if err != nil { + return err + } + } + return sub.Run(ctx, fn) + }, true +} + +type PrepareFunc func(context.Context, *interp.Runner) error + +func (d *Decoder) GetFuncP(name string, prepare PrepareFunc) (ScriptFunc, bool) { + fn := d.getFunc(name) + if fn == nil { + return nil, false + } + + return func(ctx context.Context, opts ...interp.RunnerOption) error { + sub := d.Runner.Subshell() + for _, opt := range opts { + err := opt(sub) + if err != nil { + return err + } + } + if prepare != nil { + if err := prepare(ctx, sub); err != nil { + return err + } } return sub.Run(ctx, fn) }, true diff --git a/internal/shutils/helpers/helpers.go b/internal/shutils/helpers/helpers.go index 5283381..5e67627 100644 --- a/internal/shutils/helpers/helpers.go +++ b/internal/shutils/helpers/helpers.go @@ -24,6 +24,7 @@ import ( "fmt" "io" "os" + "path" "path/filepath" "strconv" "strings" @@ -55,12 +56,17 @@ var Helpers = handlers.ExecFuncs{ "install-completion": installCompletionCmd, "install-library": installLibraryCmd, "git-version": gitVersionCmd, + + "files-find-lang": filesFindLangCmd, + "files-find-doc": filesFindDocCmd, } // Restricted contains restricted read-only helper commands // that don't modify any state var Restricted = handlers.ExecFuncs{ - "git-version": gitVersionCmd, + "git-version": gitVersionCmd, + "files-find-lang": filesFindLangCmd, + "files-find-doc": filesFindDocCmd, } func installHelperCmd(prefix string, perms os.FileMode) handlers.ExecFunc { @@ -256,6 +262,114 @@ func gitVersionCmd(hc interp.HandlerContext, cmd string, args []string) error { return nil } +func filesFindLangCmd(hc interp.HandlerContext, cmd string, args []string) error { + namePattern := "*.mo" + if len(args) > 0 { + namePattern = args[0] + ".mo" + } + + localePath := "./usr/share/locale/" + realPath := path.Join(hc.Dir, localePath) + + info, err := os.Stat(realPath) + if err != nil { + return fmt.Errorf("files-find-lang: %w", err) + } + if !info.IsDir() { + return fmt.Errorf("files-find-lang: %s is not a directory", localePath) + } + + var langFiles []string + err = filepath.Walk(realPath, func(p string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + if !info.IsDir() && matchNamePattern(info.Name(), namePattern) { + relPath, relErr := filepath.Rel(hc.Dir, p) + if relErr != nil { + return relErr + } + langFiles = append(langFiles, "./"+relPath) + } + return nil + }) + if err != nil { + return fmt.Errorf("files-find-lang: %w", err) + } + + for _, file := range langFiles { + fmt.Fprintln(hc.Stdout, file) + } + + return nil +} + +func filesFindDocCmd(hc interp.HandlerContext, cmd string, args []string) error { + namePattern := "*" + if len(args) > 0 { + namePattern = args[0] + } + + docPath := "./usr/share/doc/" + docRealPath := path.Join(hc.Dir, docPath) + + info, err := os.Stat(docRealPath) + if err != nil { + return fmt.Errorf("files-find-doc: %w", err) + } + if !info.IsDir() { + return fmt.Errorf("files-find-doc: %s is not a directory", docPath) + } + + var docFiles []string + + entries, err := os.ReadDir(docRealPath) + if err != nil { + return err + } + for _, entry := range entries { + if matchNamePattern(entry.Name(), namePattern) { + targetPath := filepath.Join(docRealPath, entry.Name()) + targetInfo, err := os.Stat(targetPath) + if err != nil { + return err + } + if targetInfo.IsDir() { + err := filepath.Walk(targetPath, func(subPath string, subInfo os.FileInfo, subErr error) error { + relPath, err := filepath.Rel(hc.Dir, subPath) + if err != nil { + return err + } + docFiles = append(docFiles, "./"+relPath) + return nil + }) + if err != nil { + return err + } + } + } + } + + if err != nil { + return fmt.Errorf("files-find-doc: %w", err) + } + + for _, file := range docFiles { + fmt.Fprintln(hc.Stdout, file) + } + + return nil +} + +func matchNamePattern(name, pattern string) bool { + matched, err := filepath.Match(pattern, name) + if err != nil { + return false + } + return matched +} + func helperInstall(from, to string, perms os.FileMode) error { err := os.MkdirAll(filepath.Dir(to), 0o755) if err != nil { diff --git a/internal/shutils/helpers/helpers_internal_test.go b/internal/shutils/helpers/helpers_internal_test.go new file mode 100644 index 0000000..3698856 --- /dev/null +++ b/internal/shutils/helpers/helpers_internal_test.go @@ -0,0 +1,216 @@ +// ALR - Any Linux Repository +// Copyright (C) 2025 Евгений Храмов +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package helpers + +import ( + "bytes" + "context" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "mvdan.cc/sh/v3/interp" + "mvdan.cc/sh/v3/syntax" + + "gitea.plemya-x.ru/Plemya-x/ALR/internal/shutils/handlers" +) + +type testCase struct { + name string + dirsToCreate []string + filesToCreate []string + expectedOutput []string + args string +} + +func TestFindFilesDoc(t *testing.T) { + tests := []testCase{ + { + name: "All dirs", + dirsToCreate: []string{ + "usr/share/doc/yandex-browser-stable/subdir", + "usr/share/doc/firefox", + }, + filesToCreate: []string{ + "usr/share/doc/yandex-browser-stable/README.md", + "usr/share/doc/yandex-browser-stable/subdir/nested-file.txt", + "usr/share/doc/firefox/README.md", + }, + expectedOutput: []string{ + "./usr/share/doc/yandex-browser-stable", + "./usr/share/doc/yandex-browser-stable/README.md", + "./usr/share/doc/yandex-browser-stable/subdir", + "./usr/share/doc/yandex-browser-stable/subdir/nested-file.txt", + "./usr/share/doc/firefox", + "./usr/share/doc/firefox/README.md", + }, + args: "", + }, + { + name: "Only selected dir", + dirsToCreate: []string{ + "usr/share/doc/yandex-browser-stable/subdir", + "usr/share/doc/firefox", + "usr/share/doc/foo/yandex-browser-stable", + }, + filesToCreate: []string{ + "usr/share/doc/yandex-browser-stable/README.md", + "usr/share/doc/yandex-browser-stable/subdir/nested-file.txt", + "usr/share/doc/firefox/README.md", + "usr/share/doc/firefox/yandex-browser-stable", + "usr/share/doc/foo/yandex-browser-stable/README.md", + }, + expectedOutput: []string{ + "./usr/share/doc/yandex-browser-stable", + "./usr/share/doc/yandex-browser-stable/README.md", + "./usr/share/doc/yandex-browser-stable/subdir", + "./usr/share/doc/yandex-browser-stable/subdir/nested-file.txt", + }, + args: "yandex-browser-stable", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + tempDir, err := os.MkdirTemp("", "test-files-find-doc") + assert.NoError(t, err) + defer os.RemoveAll(tempDir) + + for _, dir := range tc.dirsToCreate { + dirPath := filepath.Join(tempDir, dir) + err := os.MkdirAll(dirPath, 0o755) + assert.NoError(t, err) + } + + for _, file := range tc.filesToCreate { + filePath := filepath.Join(tempDir, file) + err := os.WriteFile(filePath, []byte("test content"), 0o644) + assert.NoError(t, err) + } + + helpers := handlers.ExecFuncs{ + "files-find-doc": filesFindDocCmd, + } + buf := &bytes.Buffer{} + runner, err := interp.New( + interp.Dir(tempDir), + interp.StdIO(os.Stdin, buf, os.Stderr), + interp.ExecHandler(helpers.ExecHandler(interp.DefaultExecHandler(1000))), + ) + assert.NoError(t, err) + + scriptContent := ` +shopt -s globstar +files-find-doc ` + tc.args + + script, err := syntax.NewParser().Parse(strings.NewReader(scriptContent), "") + assert.NoError(t, err) + + err = runner.Run(context.Background(), script) + assert.NoError(t, err) + + contents := strings.Fields(strings.TrimSpace(buf.String())) + assert.ElementsMatch(t, tc.expectedOutput, contents) + }) + } +} + +func TestFindLang(t *testing.T) { + tests := []testCase{ + { + name: "All dirs", + dirsToCreate: []string{ + "usr/share/locale/ru/LC_MESSAGES", + "usr/share/locale/tr/LC_MESSAGES", + }, + filesToCreate: []string{ + "usr/share/locale/ru/LC_MESSAGES/yandex-disk.mo", + "usr/share/locale/ru/LC_MESSAGES/yandex-disk-indicator.mo", + "usr/share/locale/tr/LC_MESSAGES/yandex-disk.mo", + }, + expectedOutput: []string{ + "./usr/share/locale/ru/LC_MESSAGES/yandex-disk.mo", + "./usr/share/locale/ru/LC_MESSAGES/yandex-disk-indicator.mo", + "./usr/share/locale/tr/LC_MESSAGES/yandex-disk.mo", + }, + args: "", + }, + { + name: "All dirs", + dirsToCreate: []string{ + "usr/share/locale/ru/LC_MESSAGES", + "usr/share/locale/tr/LC_MESSAGES", + }, + filesToCreate: []string{ + "usr/share/locale/ru/LC_MESSAGES/yandex-disk.mo", + "usr/share/locale/ru/LC_MESSAGES/yandex-disk-indicator.mo", + "usr/share/locale/tr/LC_MESSAGES/yandex-disk.mo", + }, + expectedOutput: []string{ + "./usr/share/locale/ru/LC_MESSAGES/yandex-disk.mo", + "./usr/share/locale/tr/LC_MESSAGES/yandex-disk.mo", + }, + args: "yandex-disk", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + tempDir, err := os.MkdirTemp("", "test-files-find-lang") + assert.NoError(t, err) + defer os.RemoveAll(tempDir) + + for _, dir := range tc.dirsToCreate { + dirPath := filepath.Join(tempDir, dir) + err := os.MkdirAll(dirPath, 0o755) + assert.NoError(t, err) + } + + for _, file := range tc.filesToCreate { + filePath := filepath.Join(tempDir, file) + err := os.WriteFile(filePath, []byte("test content"), 0o644) + assert.NoError(t, err) + } + + helpers := handlers.ExecFuncs{ + "files-find-lang": filesFindLangCmd, + } + buf := &bytes.Buffer{} + runner, err := interp.New( + interp.Dir(tempDir), + interp.StdIO(os.Stdin, buf, os.Stderr), + interp.ExecHandler(helpers.ExecHandler(interp.DefaultExecHandler(1000))), + ) + assert.NoError(t, err) + + scriptContent := ` +shopt -s globstar +files-find-lang ` + tc.args + + script, err := syntax.NewParser().Parse(strings.NewReader(scriptContent), "") + assert.NoError(t, err) + + err = runner.Run(context.Background(), script) + assert.NoError(t, err) + + contents := strings.Fields(strings.TrimSpace(buf.String())) + assert.ElementsMatch(t, tc.expectedOutput, contents) + }) + } +} diff --git a/internal/translations/default.pot b/internal/translations/default.pot index 0f12447..1779e82 100644 --- a/internal/translations/default.pot +++ b/internal/translations/default.pot @@ -251,7 +251,7 @@ msgstr "" msgid "Downloading source" msgstr "" -#: internal/logger/log.go:44 +#: internal/logger/log.go:47 msgid "ERROR" msgstr "" @@ -323,35 +323,43 @@ msgstr "" msgid "Installing dependencies" msgstr "" -#: pkg/build/build.go:435 +#: pkg/build/build.go:439 msgid "Executing version()" msgstr "" -#: pkg/build/build.go:460 +#: pkg/build/build.go:459 +msgid "Updating version" +msgstr "" + +#: pkg/build/build.go:464 msgid "Executing prepare()" msgstr "" -#: pkg/build/build.go:470 +#: pkg/build/build.go:474 msgid "Executing build()" msgstr "" -#: pkg/build/build.go:482 +#: pkg/build/build.go:486 msgid "Executing package()" msgstr "" -#: pkg/build/build.go:557 +#: pkg/build/build.go:524 +msgid "Executing files()" +msgstr "" + +#: pkg/build/build.go:601 msgid "AutoProv is not implemented for this package format, so it's skiped" msgstr "" -#: pkg/build/build.go:568 +#: pkg/build/build.go:612 msgid "AutoReq is not implemented for this package format, so it's skiped" msgstr "" -#: pkg/build/build.go:653 +#: pkg/build/build.go:719 msgid "Would you like to remove the build dependencies?" msgstr "" -#: pkg/build/build.go:759 +#: pkg/build/build.go:825 msgid "The checksums array must be the same length as sources" msgstr "" @@ -437,10 +445,14 @@ msgstr "" msgid "Pull all repositories that have changed" msgstr "" -#: upgrade.go:47 +#: upgrade.go:46 msgid "Upgrade all installed packages" msgstr "" -#: upgrade.go:83 +#: upgrade.go:82 msgid "Error checking for updates" msgstr "" + +#: upgrade.go:93 +msgid "There is nothing to do." +msgstr "" diff --git a/internal/translations/po/ru/default.po b/internal/translations/po/ru/default.po index be38515..c5645f5 100644 --- a/internal/translations/po/ru/default.po +++ b/internal/translations/po/ru/default.po @@ -259,7 +259,7 @@ msgstr "" msgid "Downloading source" msgstr "Скачивание источника" -#: internal/logger/log.go:44 +#: internal/logger/log.go:47 msgid "ERROR" msgstr "ОШИБКА" @@ -331,35 +331,43 @@ msgstr "" msgid "Installing dependencies" msgstr "" -#: pkg/build/build.go:435 +#: pkg/build/build.go:439 msgid "Executing version()" msgstr "" -#: pkg/build/build.go:460 +#: pkg/build/build.go:459 +msgid "Updating version" +msgstr "" + +#: pkg/build/build.go:464 msgid "Executing prepare()" msgstr "" -#: pkg/build/build.go:470 +#: pkg/build/build.go:474 msgid "Executing build()" msgstr "" -#: pkg/build/build.go:482 +#: pkg/build/build.go:486 msgid "Executing package()" msgstr "" -#: pkg/build/build.go:557 +#: pkg/build/build.go:524 +msgid "Executing files()" +msgstr "" + +#: pkg/build/build.go:601 msgid "AutoProv is not implemented for this package format, so it's skiped" msgstr "" -#: pkg/build/build.go:568 +#: pkg/build/build.go:612 msgid "AutoReq is not implemented for this package format, so it's skiped" msgstr "" -#: pkg/build/build.go:653 +#: pkg/build/build.go:719 msgid "Would you like to remove the build dependencies?" msgstr "" -#: pkg/build/build.go:759 +#: pkg/build/build.go:825 msgid "The checksums array must be the same length as sources" msgstr "" @@ -446,10 +454,14 @@ msgstr "" msgid "Pull all repositories that have changed" msgstr "" -#: upgrade.go:47 +#: upgrade.go:46 msgid "Upgrade all installed packages" msgstr "" -#: upgrade.go:83 +#: upgrade.go:82 msgid "Error checking for updates" msgstr "" + +#: upgrade.go:93 +msgid "There is nothing to do." +msgstr "" diff --git a/pkg/build/build.go b/pkg/build/build.go index 293fdca..56523e0 100644 --- a/pkg/build/build.go +++ b/pkg/build/build.go @@ -156,7 +156,7 @@ func BuildPackage(ctx context.Context, opts types.BuildOpts) ([]string, []string return nil, nil, err } - err = executeFunctions(ctx, dec, dirs, vars) // Выполняем специальные функции + funcOut, err := executeFunctions(ctx, dec, dirs, vars) // Выполняем специальные функции if err != nil { return nil, nil, err } @@ -165,7 +165,7 @@ func BuildPackage(ctx context.Context, opts types.BuildOpts) ([]string, []string pkgFormat := getPkgFormat(opts.Manager) // Получаем формат пакета - pkgInfo, err := buildPkgMetadata(ctx, vars, dirs, pkgFormat, info, append(repoDeps, builtNames...)) // Собираем метаданные пакета + pkgInfo, err := buildPkgMetadata(ctx, vars, dirs, pkgFormat, info, append(repoDeps, builtNames...), funcOut.Contents) // Собираем метаданные пакета if err != nil { return nil, nil, err } @@ -428,40 +428,44 @@ func buildALRDeps(ctx context.Context, opts types.BuildOpts, vars *types.BuildVa return builtPaths, builtNames, repoDeps, nil } +type FunctionsOutput struct { + Contents *[]string +} + // Функция executeFunctions выполняет специальные функции ALR, такие как version(), prepare() и т.д. -func executeFunctions(ctx context.Context, dec *decoder.Decoder, dirs types.Directories, vars *types.BuildVars) (err error) { +func executeFunctions(ctx context.Context, dec *decoder.Decoder, dirs types.Directories, vars *types.BuildVars) (*FunctionsOutput, error) { version, ok := dec.GetFunc("version") if ok { slog.Info(gotext.Get("Executing version()")) buf := &bytes.Buffer{} - err = version( + err := version( ctx, interp.Dir(dirs.SrcDir), interp.StdIO(os.Stdin, buf, os.Stderr), ) if err != nil { - return err + return nil, err } newVer := strings.TrimSpace(buf.String()) err = setVersion(ctx, dec.Runner, newVer) if err != nil { - return err + return nil, err } vars.Version = newVer - slog.Info("Updating version", "new", newVer) + slog.Info(gotext.Get("Updating version"), "new", newVer) } prepare, ok := dec.GetFunc("prepare") if ok { slog.Info(gotext.Get("Executing prepare()")) - err = prepare(ctx, interp.Dir(dirs.SrcDir)) + err := prepare(ctx, interp.Dir(dirs.SrcDir)) if err != nil { - return err + return nil, err } } @@ -469,9 +473,9 @@ func executeFunctions(ctx context.Context, dec *decoder.Decoder, dirs types.Dire if ok { slog.Info(gotext.Get("Executing build()")) - err = build(ctx, interp.Dir(dirs.SrcDir)) + err := build(ctx, interp.Dir(dirs.SrcDir)) if err != nil { - return err + return nil, err } } @@ -480,9 +484,9 @@ func executeFunctions(ctx context.Context, dec *decoder.Decoder, dirs types.Dire packageFn, ok := dec.GetFunc("package") if ok { slog.Info(gotext.Get("Executing package()")) - err = packageFn(ctx, interp.Dir(dirs.SrcDir)) + err := packageFn(ctx, interp.Dir(dirs.SrcDir)) if err != nil { - return err + return nil, err } } @@ -502,11 +506,51 @@ func executeFunctions(ctx context.Context, dec *decoder.Decoder, dirs types.Dire break } - return nil + output := &FunctionsOutput{} + + files, ok := dec.GetFuncP("files", func(ctx context.Context, s *interp.Runner) error { + // It should be done via interp.RunnerOption, + // but due to the issues below, it cannot be done. + // - https://github.com/mvdan/sh/issues/962 + // - https://github.com/mvdan/sh/issues/1125 + script, err := syntax.NewParser().Parse(strings.NewReader("cd $pkgdir && shopt -s globstar"), "") + if err != nil { + return err + } + return s.Run(ctx, script) + }) + + if ok { + slog.Info(gotext.Get("Executing files()")) + + buf := &bytes.Buffer{} + + err := files( + ctx, + interp.Dir(dirs.PkgDir), + interp.StdIO(os.Stdin, buf, os.Stderr), + ) + if err != nil { + return nil, err + } + + contents := strings.Fields(strings.TrimSpace(buf.String())) + output.Contents = &contents + } + + return output, nil } // Функция buildPkgMetadata создает метаданные для пакета, который будет собран. -func buildPkgMetadata(ctx context.Context, vars *types.BuildVars, dirs types.Directories, pkgFormat string, info *distro.OSRelease, deps []string) (*nfpm.Info, error) { +func buildPkgMetadata( + ctx context.Context, + vars *types.BuildVars, + dirs types.Directories, + pkgFormat string, + info *distro.OSRelease, + deps []string, + preferedContents *[]string, +) (*nfpm.Info, error) { pkgInfo := getBasePkgInfo(vars) pkgInfo.Description = vars.Description pkgInfo.Platform = "linux" @@ -541,7 +585,7 @@ func buildPkgMetadata(ctx context.Context, vars *types.BuildVars, dirs types.Dir pkgInfo.Arch = "all" } - contents, err := buildContents(vars, dirs) + contents, err := buildContents(vars, dirs, preferedContents) if err != nil { return nil, err } @@ -574,21 +618,27 @@ func buildPkgMetadata(ctx context.Context, vars *types.BuildVars, dirs types.Dir // Функция buildContents создает секцию содержимого пакета, которая содержит файлы, // которые будут включены в конечный пакет. -func buildContents(vars *types.BuildVars, dirs types.Directories) ([]*files.Content, error) { +func buildContents(vars *types.BuildVars, dirs types.Directories, preferedContents *[]string) ([]*files.Content, error) { contents := []*files.Content{} - err := filepath.Walk(dirs.PkgDir, func(path string, fi os.FileInfo, err error) error { - trimmed := strings.TrimPrefix(path, dirs.PkgDir) + + processPath := func(path, trimmed string, prefered bool) error { + fi, err := os.Lstat(path) + if err != nil { + return err + } if fi.IsDir() { f, err := os.Open(path) if err != nil { return err } + defer f.Close() - // Если директория пустая, пропускаем её - _, err = f.Readdirnames(1) - if err != io.EOF { - return nil + if !prefered { + _, err = f.Readdirnames(1) + if err != io.EOF { + return nil + } } contents = append(contents, &files.Content{ @@ -599,16 +649,14 @@ func buildContents(vars *types.BuildVars, dirs types.Directories) ([]*files.Cont MTime: fi.ModTime(), }, }) - - return f.Close() + return nil } - // Если файл является символической ссылкой, прорабатываем это + if fi.Mode()&os.ModeSymlink != 0 { link, err := os.Readlink(path) if err != nil { return err } - // Удаляем pkgdir из пути символической ссылки link = strings.TrimPrefix(link, dirs.PkgDir) contents = append(contents, &files.Content{ @@ -620,10 +668,9 @@ func buildContents(vars *types.BuildVars, dirs types.Directories) ([]*files.Cont Mode: fi.Mode(), }, }) - return nil } - // Обрабатываем обычные файлы + fileContent := &files.Content{ Source: path, Destination: trimmed, @@ -634,16 +681,35 @@ func buildContents(vars *types.BuildVars, dirs types.Directories) ([]*files.Cont }, } - // Если файл должен быть сохранен, установите его тип как config|noreplace if slices.Contains(vars.Backup, trimmed) { fileContent.Type = "config|noreplace" } contents = append(contents, fileContent) - return nil - }) - return contents, err + } + + if preferedContents != nil { + for _, trimmed := range *preferedContents { + path := filepath.Join(dirs.PkgDir, trimmed) + if err := processPath(path, trimmed, true); err != nil { + return nil, err + } + } + } else { + err := filepath.Walk(dirs.PkgDir, func(path string, fi os.FileInfo, err error) error { + if err != nil { + return err + } + trimmed := strings.TrimPrefix(path, dirs.PkgDir) + return processPath(path, trimmed, false) + }) + if err != nil { + return nil, err + } + } + + return contents, nil } // Функция removeBuildDeps спрашивает у пользователя, хочет ли он удалить зависимости,