fix: quote files-find output and fail on pattern not exists #123
| @@ -18,13 +18,13 @@ package helpers | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"log/slog" |  | ||||||
| 	"os" | 	"os" | ||||||
| 	"path" | 	"path" | ||||||
| 	"path/filepath" | 	"path/filepath" | ||||||
|  |  | ||||||
| 	"github.com/bmatcuk/doublestar/v4" | 	"github.com/bmatcuk/doublestar/v4" | ||||||
| 	"mvdan.cc/sh/v3/interp" | 	"mvdan.cc/sh/v3/interp" | ||||||
|  | 	"mvdan.cc/sh/v3/syntax" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func matchNamePattern(name, pattern string) bool { | func matchNamePattern(name, pattern string) bool { | ||||||
| @@ -46,10 +46,15 @@ func validateDir(dirPath, commandName string) error { | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func outputFiles(hc interp.HandlerContext, files []string) { | func outputFiles(hc interp.HandlerContext, files []string) error { | ||||||
| 	for _, file := range files { | 	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) { | 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) | 		return fmt.Errorf("files-find-lang: %w", err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	outputFiles(hc, langFiles) | 	return outputFiles(hc, langFiles) | ||||||
| 	return nil |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func filesFindDocCmd(hc interp.HandlerContext, cmd string, args []string) error { | 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 outputFiles(hc, docFiles) | ||||||
| 	return nil |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func filesFindCmd(hc interp.HandlerContext, cmd string, args []string) error { | func filesFindCmd(hc interp.HandlerContext, cmd string, args []string) error { | ||||||
| 	if len(args) == 0 { | 	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 | 	var foundFiles []string | ||||||
| @@ -158,10 +161,9 @@ func filesFindCmd(hc interp.HandlerContext, cmd string, args []string) error { | |||||||
|  |  | ||||||
| 		basepath, pattern := doublestar.SplitPattern(searchPath) | 		basepath, pattern := doublestar.SplitPattern(searchPath) | ||||||
| 		fsys := os.DirFS(basepath) | 		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 { | 		if err != nil { | ||||||
| 			slog.Warn("find-files: invalid glob pattern", "pattern", globPattern, "error", err) | 			return fmt.Errorf("files-find: glob pattern error: %w", err) | ||||||
| 			continue |  | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		for _, match := range matches { | 		for _, match := range matches { | ||||||
| @@ -173,6 +175,5 @@ func filesFindCmd(hc interp.HandlerContext, cmd string, args []string) error { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	outputFiles(hc, foundFiles) | 	return outputFiles(hc, foundFiles) | ||||||
| 	return nil |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -24,6 +24,8 @@ import ( | |||||||
| 	"strings" | 	"strings" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	"github.com/bmatcuk/doublestar/v4" | ||||||
|  | 	"github.com/google/shlex" | ||||||
| 	"github.com/stretchr/testify/assert" | 	"github.com/stretchr/testify/assert" | ||||||
| 	"mvdan.cc/sh/v3/interp" | 	"mvdan.cc/sh/v3/interp" | ||||||
| 	"mvdan.cc/sh/v3/syntax" | 	"mvdan.cc/sh/v3/syntax" | ||||||
| @@ -43,6 +45,7 @@ type testCase struct { | |||||||
| 	expectedOutput   []string | 	expectedOutput   []string | ||||||
| 	symlinksToCreate []symlink | 	symlinksToCreate []symlink | ||||||
| 	args             string | 	args             string | ||||||
|  | 	expectedError    error | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestFindFilesDoc(t *testing.T) { | func TestFindFilesDoc(t *testing.T) { | ||||||
| @@ -131,7 +134,8 @@ files-find-doc ` + tc.args | |||||||
| 			err = runner.Run(context.Background(), script) | 			err = runner.Run(context.Background(), script) | ||||||
| 			assert.NoError(t, err) | 			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) | 			assert.ElementsMatch(t, tc.expectedOutput, contents) | ||||||
| 		}) | 		}) | ||||||
| 	} | 	} | ||||||
| @@ -215,7 +219,8 @@ files-find-lang ` + tc.args | |||||||
| 			err = runner.Run(context.Background(), script) | 			err = runner.Run(context.Background(), script) | ||||||
| 			assert.NoError(t, err) | 			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) | 			assert.ElementsMatch(t, tc.expectedOutput, contents) | ||||||
| 		}) | 		}) | ||||||
| 	} | 	} | ||||||
| @@ -230,12 +235,14 @@ func TestFindFiles(t *testing.T) { | |||||||
| 				"usr/share/locale/tr/LC_MESSAGES", | 				"usr/share/locale/tr/LC_MESSAGES", | ||||||
| 				"opt/app", | 				"opt/app", | ||||||
| 				"opt/app/internal", | 				"opt/app/internal", | ||||||
|  | 				"opt/app/with space", | ||||||
| 			}, | 			}, | ||||||
| 			filesToCreate: []string{ | 			filesToCreate: []string{ | ||||||
| 				"usr/share/locale/ru/LC_MESSAGES/yandex-disk.mo", | 				"usr/share/locale/ru/LC_MESSAGES/yandex-disk.mo", | ||||||
| 				"usr/share/locale/ru/LC_MESSAGES/yandex-disk-indicator.mo", | 				"usr/share/locale/ru/LC_MESSAGES/yandex-disk-indicator.mo", | ||||||
| 				"usr/share/locale/tr/LC_MESSAGES/yandex-disk.mo", | 				"usr/share/locale/tr/LC_MESSAGES/yandex-disk.mo", | ||||||
| 				"opt/app/internal/test", | 				"opt/app/internal/test", | ||||||
|  | 				"opt/app/with space/file", | ||||||
| 			}, | 			}, | ||||||
| 			symlinksToCreate: []symlink{ | 			symlinksToCreate: []symlink{ | ||||||
| 				{ | 				{ | ||||||
| @@ -250,8 +257,16 @@ func TestFindFiles(t *testing.T) { | |||||||
| 				"./opt/app/etc", | 				"./opt/app/etc", | ||||||
| 				"./opt/app/internal", | 				"./opt/app/internal", | ||||||
| 				"./opt/app/internal/test", | 				"./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) | 			assert.NoError(t, err) | ||||||
|  |  | ||||||
| 			err = runner.Run(context.Background(), script) | 			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) | 			assert.ElementsMatch(t, tc.expectedOutput, contents) | ||||||
| 		}) | 		}) | ||||||
| 	} | 	} | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user