feat: add files() function
also add files-find-lang and files-find-doc helpers
This commit is contained in:
parent
b5bf6ab61d
commit
f6ba4a1c26
14
go.mod
14
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
|
||||
|
11
go.sum
11
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=
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
216
internal/shutils/helpers/helpers_internal_test.go
Normal file
216
internal/shutils/helpers/helpers_internal_test.go
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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)
|
||||
})
|
||||
}
|
||||
}
|
@ -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 ""
|
||||
|
@ -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 ""
|
||||
|
@ -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 спрашивает у пользователя, хочет ли он удалить зависимости,
|
||||
|
Loading…
Reference in New Issue
Block a user