From acdbaa959d222fb21ff36dee880201acf12b9622 Mon Sep 17 00:00:00 2001 From: cyphercodes Date: Sun, 7 Jun 2026 06:42:42 +0300 Subject: [PATCH] fix: escape apostrophes in fingerprint globs --- internal/execext/exec.go | 1 + task_test.go | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/internal/execext/exec.go b/internal/execext/exec.go index a418a2aa11..bf67129d86 100644 --- a/internal/execext/exec.go +++ b/internal/execext/exec.go @@ -93,6 +93,7 @@ func RunCommand(ctx context.Context, opts *RunCommandOptions) error { func escape(s string) string { s = filepath.ToSlash(s) s = strings.ReplaceAll(s, " ", `\ `) + s = strings.ReplaceAll(s, "'", `\'`) s = strings.ReplaceAll(s, "&", `\&`) s = strings.ReplaceAll(s, "(", `\(`) s = strings.ReplaceAll(s, ")", `\)`) diff --git a/task_test.go b/task_test.go index 80915c2c47..ce8dd13619 100644 --- a/task_test.go +++ b/task_test.go @@ -653,6 +653,45 @@ func TestStatusChecksumMissingGenerated(t *testing.T) { // nolint:paralleltest / require.NoError(t, err, "generated.txt should be recreated after third run") } +func TestStatusChecksumAllowsApostropheInWorkingDirectory(t *testing.T) { + t.Parallel() + + dir := filepath.Join(t.TempDir(), "test'd") + require.NoError(t, os.Mkdir(dir, 0o755)) + require.NoError(t, os.WriteFile(filepath.Join(dir, "Taskfile.yml"), []byte(`version: '3' + +tasks: + default: + generates: + - test.out + sources: + - test.in + cmds: + - cp test.in test.out +`), 0o644)) + require.NoError(t, os.WriteFile(filepath.Join(dir, "test.in"), []byte("hello\n"), 0o644)) + + var buff bytes.Buffer + tempDir := task.TempDir{ + Remote: filepath.Join(dir, ".task"), + Fingerprint: filepath.Join(dir, ".task"), + } + e := task.NewExecutor( + task.WithDir(dir), + task.WithStdout(&buff), + task.WithStderr(&buff), + task.WithTempDir(tempDir), + ) + require.NoError(t, e.Setup()) + + require.NoError(t, e.Run(t.Context(), &task.Call{Task: "default"})) + assert.FileExists(t, filepath.Join(dir, "test.out")) + buff.Reset() + + require.NoError(t, e.Run(t.Context(), &task.Call{Task: "default"})) + assert.Equal(t, `task: Task "default" is up to date`+"\n", buff.String()) +} + func TestStatusVariables(t *testing.T) { t.Parallel()