1
0
mirror of https://github.com/go-task/task.git synced 2025-05-27 23:08:16 +02:00

fix: sources brace expansion (#2075)

This commit is contained in:
Pete Davison 2025-04-19 11:51:31 +01:00 committed by GitHub
parent f789c57624
commit a60c2ec3f8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 63 additions and 32 deletions

1
go.mod
View File

@ -17,7 +17,6 @@ require (
github.com/go-task/slim-sprig/v3 v3.0.0
github.com/go-task/template v0.1.0
github.com/joho/godotenv v1.5.1
github.com/mattn/go-zglob v0.0.6
github.com/mitchellh/hashstructure/v2 v2.0.2
github.com/otiai10/copy v1.14.1
github.com/puzpuzpuz/xsync/v3 v3.5.1

2
go.sum
View File

@ -98,8 +98,6 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-zglob v0.0.6 h1:mP8RnmCgho4oaUYDIDn6GNxYk+qJGUs8fJLn+twYj2A=
github.com/mattn/go-zglob v0.0.6/go.mod h1:MxxjyoXXnMxfIpxTK2GAkw1w8glPsQILx3N5wrKakiY=
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=

View File

@ -11,13 +11,15 @@ import (
"mvdan.cc/sh/v3/expand"
"mvdan.cc/sh/v3/interp"
"mvdan.cc/sh/v3/shell"
"mvdan.cc/sh/v3/syntax"
"github.com/go-task/task/v3/errors"
)
// RunCommandOptions is the options for the RunCommand func
// ErrNilOptions is returned when a nil options is given
var ErrNilOptions = errors.New("execext: nil options given")
// RunCommandOptions is the options for the [RunCommand] func.
type RunCommandOptions struct {
Command string
Dir string
@ -29,9 +31,6 @@ type RunCommandOptions struct {
Stderr io.Writer
}
// ErrNilOptions is returned when a nil options is given
var ErrNilOptions = errors.New("execext: nil options given")
// RunCommand runs a shell command
func RunCommand(ctx context.Context, opts *RunCommandOptions) error {
if opts == nil {
@ -91,22 +90,64 @@ func RunCommand(ctx context.Context, opts *RunCommandOptions) error {
return r.Run(ctx, p)
}
// Expand is a helper to mvdan.cc/shell.Fields that returns the first field
// if available.
func Expand(s string) (string, 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, ")", `\)`)
fields, err := shell.Fields(s, nil)
return s
}
// ExpandLiteral is a wrapper around [expand.Literal]. It will escape the input
// string, expand any shell symbols (such as '~') and resolve any environment
// variables.
func ExpandLiteral(s string) (string, error) {
if s == "" {
return "", nil
}
s = escape(s)
p := syntax.NewParser()
var words []*syntax.Word
err := p.Words(strings.NewReader(s), func(w *syntax.Word) bool {
words = append(words, w)
return true
})
if err != nil {
return "", err
}
if len(fields) > 0 {
return fields[0], nil
if len(words) == 0 {
return "", nil
}
return "", nil
cfg := &expand.Config{
Env: expand.FuncEnviron(os.Getenv),
ReadDir2: os.ReadDir,
GlobStar: true,
}
return expand.Literal(cfg, words[0])
}
// ExpandFields is a wrapper around [expand.Fields]. It will escape the input
// string, expand any shell symbols (such as '~') and resolve any environment
// variables. It also expands brace expressions ({a.b}) and globs (*/**) and
// returns the results as a list of strings.
func ExpandFields(s string) ([]string, error) {
s = escape(s)
p := syntax.NewParser()
var words []*syntax.Word
err := p.Words(strings.NewReader(s), func(w *syntax.Word) bool {
words = append(words, w)
return true
})
if err != nil {
return nil, err
}
cfg := &expand.Config{
Env: expand.FuncEnviron(os.Getenv),
ReadDir2: os.ReadDir,
GlobStar: true,
}
return expand.Fields(cfg, words...)
}
func execHandler(next interp.ExecHandlerFunc) interp.ExecHandlerFunc {

View File

@ -4,8 +4,6 @@ import (
"os"
"sort"
"github.com/mattn/go-zglob"
"github.com/go-task/task/v3/internal/execext"
"github.com/go-task/task/v3/internal/filepathext"
"github.com/go-task/task/v3/taskfile/ast"
@ -28,12 +26,7 @@ func Globs(dir string, globs []*ast.Glob) ([]string, error) {
func glob(dir string, g string) ([]string, error) {
g = filepathext.SmartJoin(dir, g)
g, err := execext.Expand(g)
if err != nil {
return nil, err
}
fs, err := zglob.GlobFollowSymlinks(g)
fs, err := execext.ExpandFields(g)
if err != nil {
return nil, err
}

View File

@ -120,7 +120,7 @@ func (e *Executor) setupTempDir() error {
Fingerprint: filepathext.SmartJoin(e.Dir, ".task"),
}
} else if filepath.IsAbs(tempDir) || strings.HasPrefix(tempDir, "~") {
tempDir, err := execext.Expand(tempDir)
tempDir, err := execext.ExpandLiteral(tempDir)
if err != nil {
return err
}
@ -141,7 +141,7 @@ func (e *Executor) setupTempDir() error {
remoteDir := env.GetTaskEnv("REMOTE_DIR")
if remoteDir != "" {
if filepath.IsAbs(remoteDir) || strings.HasPrefix(remoteDir, "~") {
remoteTempDir, err := execext.Expand(remoteDir)
remoteTempDir, err := execext.ExpandLiteral(remoteDir)
if err != nil {
return err
}

View File

@ -84,7 +84,7 @@ func (node *FileNode) ResolveEntrypoint(entrypoint string) (string, error) {
return entrypoint, nil
}
path, err := execext.Expand(entrypoint)
path, err := execext.ExpandLiteral(entrypoint)
if err != nil {
return "", err
}
@ -100,7 +100,7 @@ func (node *FileNode) ResolveEntrypoint(entrypoint string) (string, error) {
}
func (node *FileNode) ResolveDir(dir string) (string, error) {
path, err := execext.Expand(dir)
path, err := execext.ExpandLiteral(dir)
if err != nil {
return "", err
}

View File

@ -106,7 +106,7 @@ func (node *GitNode) ResolveEntrypoint(entrypoint string) (string, error) {
}
func (node *GitNode) ResolveDir(dir string) (string, error) {
path, err := execext.Expand(dir)
path, err := execext.ExpandLiteral(dir)
if err != nil {
return "", err
}

View File

@ -97,7 +97,7 @@ func (node *HTTPNode) ResolveEntrypoint(entrypoint string) (string, error) {
}
func (node *HTTPNode) ResolveDir(dir string) (string, error) {
path, err := execext.Expand(dir)
path, err := execext.ExpandLiteral(dir)
if err != nil {
return "", err
}

View File

@ -48,7 +48,7 @@ func (node *StdinNode) ResolveEntrypoint(entrypoint string) (string, error) {
return entrypoint, nil
}
path, err := execext.Expand(entrypoint)
path, err := execext.ExpandLiteral(entrypoint)
if err != nil {
return "", err
}
@ -61,7 +61,7 @@ func (node *StdinNode) ResolveEntrypoint(entrypoint string) (string, error) {
}
func (node *StdinNode) ResolveDir(dir string) (string, error) {
path, err := execext.Expand(dir)
path, err := execext.ExpandLiteral(dir)
if err != nil {
return "", err
}

View File

@ -77,7 +77,7 @@ func (e *Executor) compiledTask(call *Call, evaluateShVars bool) (*ast.Task, err
Watch: origTask.Watch,
Namespace: origTask.Namespace,
}
new.Dir, err = execext.Expand(new.Dir)
new.Dir, err = execext.ExpandLiteral(new.Dir)
if err != nil {
return nil, err
}