fix: quote files-find output and fail on pattern not exists #123
| @@ -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) | ||||
| } | ||||
|   | ||||
| @@ -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/**/*\"", | ||||
| 			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) | ||||
| 			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) | ||||
| 		}) | ||||
| 	} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user