Sources accepts variables which represent arrays.#2671
Sources accepts variables which represent arrays.#2671trulede wants to merge 3 commits intogo-task:mainfrom
Conversation
|
It might be worthwhile combining changes from #2624 including/extending test coverage. |
|
Until this PR is merged, I use a work-around which I explain here: skip this task unless files in this path changed since the last successful run (feedback is welcome). |
vmaerten
left a comment
There was a problem hiding this comment.
Thanks for the PR! I have some concerns about the string-parsing approach in ReplaceGlobs.
The detection logic strings.HasPrefix(v, "[") && strings.HasSuffix(v, "]") conflicts with valid glob syntax. [a-z] is a standard glob character class, but this code would interpret it as an array:
tasks:
example:
sources:
- '[a-z]' # valid glob matching single-char files a, b, c... z
cmds:
- echo {{.ITEM}}Currently this works (in main) and matches files like a, b, c. With this PR, [a-z] would be parsed as an array containing a-z.
I know glob are not widely used and it can be edge cases.
I think relying on string representation of arrays is fragile.
IMHO, Go's string representation of slices ([a b c]) is a debug format, no escaping, no quoting, no delimiter.
strings.Fields may also break on elements containing spaces.
I think we can work on a type-aware resolution
We already have template.Resolve() which returns typed values (slices, strings, etc.) instead of stringifying them. This is the same mechanism used by ResolveRef for matrix variables.
If we try Resolve() before falling back to Replace(), we can detect actual arrays at the type level.
I can try to make some suggestion in the code if you want
| if evaluateShVars { | ||
| return templater.ReplaceGlobs(globs, cache) | ||
| } else { | ||
| return origTask.Sources |
There was a problem hiding this comment.
I think this should be globs because if it's call for generates it'll return always sources
There was a problem hiding this comment.
Regarding the glob array type. I think this is for the case (#88):
GLOB_DYNAMIC:
sh: 'echo "[*.txt *.text]"'
where the glob pattern really is a string encoded array (i.e. JSON), not a Go type. By the time this code is hit the globs have already been decoded from arrays to strings, so its only a matter of decoding the string itself ... if it seems like an array.
Of course, this might be better as a real JSON string, perhaps, or dropped from the PR altogether.
There was a problem hiding this comment.
I think reading in globs from a command is interesting, but a JSON encoding might be better and also not collide with the glob expressions?
So, code like this:
func appendGlobsFromJSON(v string, g *ast.Glob, new []*ast.Glob) []*ast.Glob {
var decoded any
if err := json.Unmarshal([]byte(v), &decoded); err != nil {
return new
}
switch x := decoded.(type) {
case []any:
for _, item := range x {
if glob, ok := item.(string); ok {
new = append(new, &ast.Glob{Glob: glob, Negate: g.Negate})
}
}
case string:
return appendGlobsFromJSON(x, g, new)
}
return new
}
If a source (or generate) glob is a templated variable, and that variable is/was an array, then add that array of globs. The following example demonstrates.
:separator to define multiple paths on a singlesourcesentry #1018sourcescause glob expansion to hang due to unpopulated placeholders and symlink loops #2400sourcesfrom dynamic list of files #2483sh#88ignore, and looking at the opposite of what's ignored #709