From 4b06809a39c7077e67c24a50baa8b207c6161e90 Mon Sep 17 00:00:00 2001 From: Maxim Slipenko Date: Fri, 27 Jun 2025 20:22:10 +0000 Subject: [PATCH] fix: quote files-find output and fail on pattern not exists (#123) closes #122 closes #121 Reviewed-on: https://gitea.plemya-x.ru/Plemya-x/ALR/pulls/123 Co-authored-by: Maxim Slipenko Co-committed-by: Maxim Slipenko --- internal/shutils/helpers/files_find.go | 27 +++++++++-------- .../shutils/helpers/helpers_internal_test.go | 30 +++++++++++++++---- 2 files changed, 39 insertions(+), 18 deletions(-) diff --git a/internal/shutils/helpers/files_find.go b/internal/shutils/helpers/files_find.go index 2ef0df1..86da65a 100644 --- a/internal/shutils/helpers/files_find.go +++ b/internal/shutils/helpers/files_find.go @@ -18,13 +18,13 @@ package helpers import ( "fmt" - "log/slog" "os" "path" "path/filepath" "github.com/bmatcuk/doublestar/v4" "mvdan.cc/sh/v3/interp" + "mvdan.cc/sh/v3/syntax" ) func matchNamePattern(name, pattern string) bool { @@ -46,10 +46,15 @@ func validateDir(dirPath, commandName string) error { return nil } -func outputFiles(hc interp.HandlerContext, files []string) { +func outputFiles(hc interp.HandlerContext, files []string) error { for _, file := range files { - fmt.Fprintln(hc.Stdout, file) + v, err := syntax.Quote(file, syntax.LangAuto) + if err != nil { + return err + } + fmt.Fprintln(hc.Stdout, v) } + return nil } func makeRelativePath(basePath, fullPath string) (string, error) { @@ -92,8 +97,7 @@ func filesFindLangCmd(hc interp.HandlerContext, cmd string, args []string) error return fmt.Errorf("files-find-lang: %w", err) } - outputFiles(hc, langFiles) - return nil + return outputFiles(hc, langFiles) } func filesFindDocCmd(hc interp.HandlerContext, cmd string, args []string) error { @@ -142,13 +146,12 @@ func filesFindDocCmd(hc interp.HandlerContext, cmd string, args []string) error } } - outputFiles(hc, docFiles) - return nil + return outputFiles(hc, docFiles) } func filesFindCmd(hc interp.HandlerContext, cmd string, args []string) error { if len(args) == 0 { - return fmt.Errorf("find-files: at least one glob pattern is required") + return fmt.Errorf("files-find: at least one glob pattern is required") } var foundFiles []string @@ -158,10 +161,9 @@ func filesFindCmd(hc interp.HandlerContext, cmd string, args []string) error { basepath, pattern := doublestar.SplitPattern(searchPath) fsys := os.DirFS(basepath) - matches, err := doublestar.Glob(fsys, pattern, doublestar.WithNoFollow()) + matches, err := doublestar.Glob(fsys, pattern, doublestar.WithNoFollow(), doublestar.WithFailOnPatternNotExist()) if err != nil { - slog.Warn("find-files: invalid glob pattern", "pattern", globPattern, "error", err) - continue + return fmt.Errorf("files-find: glob pattern error: %w", err) } for _, match := range matches { @@ -173,6 +175,5 @@ func filesFindCmd(hc interp.HandlerContext, cmd string, args []string) error { } } - outputFiles(hc, foundFiles) - return nil + return outputFiles(hc, foundFiles) } diff --git a/internal/shutils/helpers/helpers_internal_test.go b/internal/shutils/helpers/helpers_internal_test.go index 1f25d1d..f45386e 100644 --- a/internal/shutils/helpers/helpers_internal_test.go +++ b/internal/shutils/helpers/helpers_internal_test.go @@ -24,6 +24,8 @@ import ( "strings" "testing" + "github.com/bmatcuk/doublestar/v4" + "github.com/google/shlex" "github.com/stretchr/testify/assert" "mvdan.cc/sh/v3/interp" "mvdan.cc/sh/v3/syntax" @@ -43,6 +45,7 @@ type testCase struct { expectedOutput []string symlinksToCreate []symlink args string + expectedError error } func TestFindFilesDoc(t *testing.T) { @@ -131,7 +134,8 @@ files-find-doc ` + tc.args err = runner.Run(context.Background(), script) assert.NoError(t, err) - contents := strings.Fields(strings.TrimSpace(buf.String())) + contents, err := shlex.Split(buf.String()) + assert.NoError(t, err) assert.ElementsMatch(t, tc.expectedOutput, contents) }) } @@ -215,7 +219,8 @@ files-find-lang ` + tc.args err = runner.Run(context.Background(), script) assert.NoError(t, err) - contents := strings.Fields(strings.TrimSpace(buf.String())) + contents, err := shlex.Split(buf.String()) + assert.NoError(t, err) assert.ElementsMatch(t, tc.expectedOutput, contents) }) } @@ -230,12 +235,14 @@ func TestFindFiles(t *testing.T) { "usr/share/locale/tr/LC_MESSAGES", "opt/app", "opt/app/internal", + "opt/app/with space", }, 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", "opt/app/internal/test", + "opt/app/with space/file", }, symlinksToCreate: []symlink{ { @@ -250,8 +257,16 @@ func TestFindFiles(t *testing.T) { "./opt/app/etc", "./opt/app/internal", "./opt/app/internal/test", + "./opt/app/with space", + "./opt/app/with space/file", }, - args: "\"/usr/share/locale/*/LC_MESSAGES/*.mo\" \"/opt/app/**/*\"", + args: "\"/usr/share/locale/*/LC_MESSAGES/*.mo\" \"/opt/app/**/*\"", + expectedError: nil, + }, + { + name: "Not existing paths should throw error", + args: "\"/opt/test/not-existing\"", + expectedError: doublestar.ErrPatternNotExist, }, } @@ -304,9 +319,14 @@ files-find ` + tc.args assert.NoError(t, err) err = runner.Run(context.Background(), script) - assert.NoError(t, err) + if tc.expectedError != nil { + assert.ErrorAs(t, err, &tc.expectedError) + } else { + assert.NoError(t, err) + } - contents := strings.Fields(strings.TrimSpace(buf.String())) + contents, err := shlex.Split(buf.String()) + assert.NoError(t, err) assert.ElementsMatch(t, tc.expectedOutput, contents) }) }