From 1b48a934667faa1c87c591fc3839b953d4af02c1 Mon Sep 17 00:00:00 2001 From: Eric Hauser Date: Tue, 21 Jan 2025 20:11:45 -0700 Subject: [PATCH] fix(cli): unescape runfiles entries from manifest #799 --- pkg/aspect/outputs/BUILD.bazel | 1 + pkg/aspect/outputs/hash.go | 3 ++- pkg/bazel/runfiles/BUILD.bazel | 17 +++++++++++++++ pkg/bazel/runfiles/runfiles.go | 33 +++++++++++++++++++++++++++++ pkg/bazel/runfiles/runfiles_test.go | 32 ++++++++++++++++++++++++++++ 5 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 pkg/bazel/runfiles/BUILD.bazel create mode 100644 pkg/bazel/runfiles/runfiles.go create mode 100644 pkg/bazel/runfiles/runfiles_test.go diff --git a/pkg/aspect/outputs/BUILD.bazel b/pkg/aspect/outputs/BUILD.bazel index 33f61b8f1..65a438d24 100644 --- a/pkg/aspect/outputs/BUILD.bazel +++ b/pkg/aspect/outputs/BUILD.bazel @@ -11,6 +11,7 @@ go_library( visibility = ["//visibility:public"], deps = [ "//pkg/bazel", + "//pkg/bazel/runfiles", "//pkg/ioutils", "@com_github_alphadose_haxmap//:haxmap", "@com_github_rogpeppe_go_internal//dirhash", diff --git a/pkg/aspect/outputs/hash.go b/pkg/aspect/outputs/hash.go index 87e557815..8a2034747 100644 --- a/pkg/aspect/outputs/hash.go +++ b/pkg/aspect/outputs/hash.go @@ -32,6 +32,7 @@ import ( "github.com/alphadose/haxmap" "github.com/aspect-build/aspect-cli/pkg/bazel" + r "github.com/aspect-build/aspect-cli/pkg/bazel/runfiles" "github.com/rogpeppe/go-internal/dirhash" concurrently "github.com/tejzpr/ordered-concurrently/v3" @@ -73,7 +74,7 @@ func AddRunfilesHash(hashFiles map[string][]string, label string, manifestPath s // execroot/path /some/absolute/path entry := strings.Split(fileScanner.Text(), " ") // key := entry[0] - abspath := entry[1] + abspath := r.Unescape(entry[1]) fileinfo, err := os.Stat(abspath) if err != nil { diff --git a/pkg/bazel/runfiles/BUILD.bazel b/pkg/bazel/runfiles/BUILD.bazel new file mode 100644 index 000000000..1dfcbc061 --- /dev/null +++ b/pkg/bazel/runfiles/BUILD.bazel @@ -0,0 +1,17 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "runfiles", + srcs = ["runfiles.go"], + importpath = "github.com/aspect-build/aspect-cli/pkg/bazel/runfiles", + visibility = ["//visibility:public"], +) + +go_test( + name = "runfiles_test", + srcs = ["runfiles_test.go"], + deps = [ + ":runfiles", + "@com_github_stretchr_testify//require", + ], +) diff --git a/pkg/bazel/runfiles/runfiles.go b/pkg/bazel/runfiles/runfiles.go new file mode 100644 index 000000000..ac15e82c5 --- /dev/null +++ b/pkg/bazel/runfiles/runfiles.go @@ -0,0 +1,33 @@ +package runfiles + +// Unescape the behavior of the C++ implementation +// https://github.com/bazelbuild/bazel/blob/09c621e4cf5b968f4c6cdf905ab142d5961f9ddc/src/main/tools/build-runfiles/build-runfiles.cc#L76 +func Unescape(path string) string { + var result []rune + runes := []rune(path) + + for i := 0; i < len(runes); i++ { + if runes[i] == '\\' && i+1 < len(runes) { + switch runes[i+1] { + case 's': + result = append(result, ' ') + case 'n': + result = append(result, '\n') + case 'b': + result = append(result, '\\') + default: + // For escaped backslash (\\), output single backslash + if runes[i+1] == '\\' { + result = append(result, '\\') + } else { + // For any other escaped character, preserve both the backslash and the character + result = append(result, '\\', runes[i+1]) + } + } + i++ // Skip the escaped character + } else { + result = append(result, runes[i]) + } + } + return string(result) +} diff --git a/pkg/bazel/runfiles/runfiles_test.go b/pkg/bazel/runfiles/runfiles_test.go new file mode 100644 index 000000000..5c162314f --- /dev/null +++ b/pkg/bazel/runfiles/runfiles_test.go @@ -0,0 +1,32 @@ +package runfiles_test + +import ( + "github.com/aspect-build/aspect-cli/pkg/bazel/runfiles" + "github.com/stretchr/testify/require" + "testing" +) + +func TestUnescape(t *testing.T) { + tests := []struct { + input string + expected string + }{ + {"hello\\sworld", "hello world"}, + {"new\\nline", "new\nline"}, + {"back\\bslash", "back\\slash"}, + {"double\\\\slash", "double\\slash"}, + {"raw\\xsequence", "raw\\xsequence"}, + {"no\\", "no\\"}, // Handles trailing backslash + {"\\s\\n\\b", " \n\\"}, + {"", ""}, + {"normal text", "normal text"}, + {"\\s\\s\\s", " "}, + } + + for _, tt := range tests { + t.Run(tt.input, func(t *testing.T) { + got := runfiles.Unescape(tt.input) + require.Equal(t, tt.expected, got) + }) + } +}