Two-way variable replacement: change path separators to target platform

Two-way variable replacement now also changes the path separators. Since
the two-way replacement is made for paths, it makes sense to also clean up
the path for the target platform.
This commit is contained in:
Sybren A. Stüvel 2022-08-25 12:19:30 +02:00
parent d239350ee4
commit 63c60a5b15
6 changed files with 100 additions and 12 deletions

View File

@ -13,6 +13,7 @@ bugs in actually-released versions.
- Change path inside the Linux and macOS tarballs, so that they contain an - Change path inside the Linux and macOS tarballs, so that they contain an
embedded `flamenco-3.x.y-xxxx/` directory with all the files (instead of embedded `flamenco-3.x.y-xxxx/` directory with all the files (instead of
putting all the files in the root of the tarball). putting all the files in the root of the tarball).
- Two-way variable replacement now also changes the path separators to the target platform.
## 3.0-beta1 - released 2022-08-03 ## 3.0-beta1 - released 2022-08-03

View File

@ -87,14 +87,14 @@ func TestReplacePathsWindows(t *testing.T) {
replacedTask := replaceTaskVariables(&conf, task, worker) replacedTask := replaceTaskVariables(&conf, task, worker)
assert.Equal(t, assert.Equal(t,
"s:/flamenco/jobs/sybren/2017-06-08-181223.625800-sybren-flamenco-test.flamenco/flamenco-test.flamenco.blend", `s:\flamenco\jobs\sybren\2017-06-08-181223.625800-sybren-flamenco-test.flamenco\flamenco-test.flamenco.blend`,
replacedTask.Commands[2].Parameters["filepath"], replacedTask.Commands[2].Parameters["filepath"],
) )
assert.Equal(t, assert.Equal(t,
[]string{"--render-out", "s:/flamenco/render/long/sybren/blender-cloud-addon/flamenco-test__intermediate/render-smpl-0001-0084-frm-######"}, []string{"--render-out", `s:\flamenco\render\long\sybren\blender-cloud-addon\flamenco-test__intermediate\render-smpl-0001-0084-frm-######`},
replacedTask.Commands[2].Parameters["args"], replacedTask.Commands[2].Parameters["args"],
) )
assert.Equal(t, "{hey}/haha", replacedTask.Commands[2].Parameters["otherpath"]) assert.Equal(t, `{hey}\haha`, replacedTask.Commands[2].Parameters["otherpath"])
} }
func TestReplacePathsUnknownOS(t *testing.T) { func TestReplacePathsUnknownOS(t *testing.T) {

View File

@ -428,10 +428,12 @@ func (c *Conf) ExpandVariables(inputChannel <-chan string, outputChannel chan<-
} }
doValueReplacement := func(valueToExpand string) string { doValueReplacement := func(valueToExpand string) string {
expanded := valueToExpand
// Expand variables from {varname} to their value for the target platform. // Expand variables from {varname} to their value for the target platform.
for varname, varvalue := range varsForPlatform { for varname, varvalue := range varsForPlatform {
placeholder := fmt.Sprintf("{%s}", varname) placeholder := fmt.Sprintf("{%s}", varname)
valueToExpand = strings.Replace(valueToExpand, placeholder, varvalue, -1) expanded = strings.Replace(expanded, placeholder, varvalue, -1)
} }
// Go through the two-way variables, to make sure that the result of // Go through the two-way variables, to make sure that the result of
@ -446,13 +448,16 @@ func (c *Conf) ExpandVariables(inputChannel <-chan string, outputChannel chan<-
if !ok { if !ok {
continue continue
} }
if !strings.HasPrefix(valueToExpand, managerValue) { if !strings.HasPrefix(expanded, managerValue) {
continue continue
} }
valueToExpand = targetValue + valueToExpand[len(managerValue):] expanded = targetValue + expanded[len(managerValue):]
} }
return valueToExpand // Since two-way variables are meant for path replacement, we know this
// should be a path. For added bonus, translate it to the target platform's
// path separators.
return crosspath.ToPlatform(expanded, string(platform))
} }
for valueToExpand := range inputChannel { for valueToExpand := range inputChannel {

View File

@ -109,7 +109,7 @@ func TestExpandVariables(t *testing.T) {
Values: []VariableValue{ Values: []VariableValue{
{Value: "/path/to/ffmpeg", Audience: VariableAudienceUsers, Platform: VariablePlatformLinux}, {Value: "/path/to/ffmpeg", Audience: VariableAudienceUsers, Platform: VariablePlatformLinux},
{Value: "/path/to/ffmpeg/on/darwin", Audience: VariableAudienceUsers, Platform: VariablePlatformDarwin}, {Value: "/path/to/ffmpeg/on/darwin", Audience: VariableAudienceUsers, Platform: VariablePlatformDarwin},
{Value: "C:/flamenco/ffmpeg", Audience: VariableAudienceUsers, Platform: VariablePlatformWindows}, {Value: `C:\flamenco\ffmpeg`, Audience: VariableAudienceUsers, Platform: VariablePlatformWindows},
}, },
} }
}) })
@ -128,7 +128,7 @@ func TestExpandVariables(t *testing.T) {
assert.Equal(t, "unchanged value", <-receiver) assert.Equal(t, "unchanged value", <-receiver)
feeder <- "{ffmpeg}" feeder <- "{ffmpeg}"
assert.Equal(t, "C:/flamenco/ffmpeg", <-receiver) assert.Equal(t, `C:\flamenco\ffmpeg`, <-receiver)
feeder <- "{demo}" feeder <- "{demo}"
assert.Equal(t, "{demo}", <-receiver, "missing value on the platform should not be replaced") assert.Equal(t, "{demo}", <-receiver, "missing value on the platform should not be replaced")
@ -156,7 +156,7 @@ func TestExpandVariablesWithTwoWay(t *testing.T) {
Values: []VariableValue{ Values: []VariableValue{
{Value: "/path/on/linux", Platform: VariablePlatformLinux, Audience: VariableAudienceWorkers}, {Value: "/path/on/linux", Platform: VariablePlatformLinux, Audience: VariableAudienceWorkers},
{Value: "/path/on/darwin", Platform: VariablePlatformDarwin, Audience: VariableAudienceWorkers}, {Value: "/path/on/darwin", Platform: VariablePlatformDarwin, Audience: VariableAudienceWorkers},
{Value: "C:/path/on/windows", Platform: VariablePlatformWindows, Audience: VariableAudienceWorkers}, {Value: `C:\path\on\windows`, Platform: VariablePlatformWindows, Audience: VariableAudienceWorkers},
}, },
} }
}) })
@ -174,11 +174,11 @@ func TestExpandVariablesWithTwoWay(t *testing.T) {
// Simple two-way variable replacement. // Simple two-way variable replacement.
feeder <- "/path/on/linux/file.txt" feeder <- "/path/on/linux/file.txt"
assert.Equal(t, "C:/path/on/windows/file.txt", <-receiver) assert.Equal(t, `C:\path\on\windows\file.txt`, <-receiver)
// {locally-set-path} expands to a value that's then further replaced by a two-way variable. // {locally-set-path} expands to a value that's then further replaced by a two-way variable.
feeder <- "{locally-set-path}/should/be/remapped" feeder <- "{locally-set-path}/should/be/remapped"
assert.Equal(t, "C:/path/on/windows/should/be/remapped", <-receiver) assert.Equal(t, `C:\path\on\windows\should\be\remapped`, <-receiver)
close(feeder) close(feeder)
wg.Wait() wg.Wait()

View File

@ -104,3 +104,48 @@ func IsRoot(path string) bool {
runes := []rune(path) runes := []rune(path)
return validDriveLetter(runes[0]) return validDriveLetter(runes[0])
} }
// ToPlatform returns the path, with path separators adjusted for the given platform.
// It is assumed that all forward and backward slashes in the path are path
// separators, and that apart from the style of separators the path makes sense
// on the target platform.
func ToPlatform(path, platform string) string {
if path == "" {
return ""
}
components := strings.FieldsFunc(path, isPathSep)
// FieldsFunc() removes leading path separators, turning an absolute path on
// Linux to a relative path, and turning `\\NAS\share` on Windows into
// `NAS\share`.
extraComponents := []string{}
for _, r := range path {
if !isPathSep(r) {
break
}
extraComponents = append(extraComponents, "")
}
components = append(extraComponents, components...)
pathsep := pathSepForPlatform(platform)
translated := strings.Join(components, pathsep)
return translated
}
// pathSepForPlatform returns the path separator for the given platform.
// This is rather simple, and just returns `\` on Windows and `/` on all other
// platforms.
func pathSepForPlatform(platform string) string {
switch platform {
case "windows":
return `\`
default:
return "/"
}
}
func isPathSep(r rune) bool {
return r == '/' || r == '\\'
}

View File

@ -200,3 +200,40 @@ func TestIsRoot(t *testing.T) {
}) })
} }
} }
func TestToPlatform(t *testing.T) {
type args struct {
path string
platform string
}
tests := []struct {
name string
args args
want string
}{
{"empty-win", args{``, "windows"}, ``},
{"empty-lnx", args{``, "linux"}, ``},
{"single-win", args{`path with spaces`, "windows"}, `path with spaces`},
{"single-lnx", args{`path with spaces`, "linux"}, `path with spaces`},
{"native-win", args{`native\path`, "windows"}, `native\path`},
{"native-lnx", args{`native/path`, "linux"}, `native/path`},
{"opposite-win", args{`opposite/path`, "windows"}, `opposite\path`},
{"opposite-lnx", args{`opposite\path`, "linux"}, `opposite/path`},
{"mixed-win", args{`F:/mixed/path\to\file.blend`, "windows"}, `F:\mixed\path\to\file.blend`},
{"mixed-lnx", args{`F:/mixed/path\to\file.blend`, "linux"}, `F:/mixed/path/to/file.blend`},
{"absolute-win", args{`F:/absolute/path`, "windows"}, `F:\absolute\path`},
{"absolute-lnx", args{`/absolute/path`, "linux"}, `/absolute/path`},
{"drive-relative-win", args{`/absolute/path`, "windows"}, `\absolute\path`},
// UNC notation should survive, even when it no longer makes sense (like on Linux).
{"unc-win", args{`\\NAS\share\path`, "windows"}, `\\NAS\share\path`},
{"unc-lnx", args{`\\NAS\share\path`, "linux"}, `//NAS/share/path`},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := ToPlatform(tt.args.path, tt.args.platform); got != tt.want {
t.Errorf("ToPlatform() = %v, want %v", got, tt.want)
}
})
}
}