-
-
Notifications
You must be signed in to change notification settings - Fork 543
Description
Intro
The Vue build example in frontend/vue/
which builds using vite currently escapes the bazel sandbox, causing non-hermetic (and unexpected) behavior when building with sandboxes.
How to reproduce
cd frontend
bazel run //vue:build_test
# This succeeds, and should
rm src/components/HelloWorld.vue
bazel run //vue:build_test
# !!! This succeeds, and should NOT
# We can validate that this should not in fact succeed by doing a bazel clean then rebuilding
bazel clean
bazel run //vue:build_test
# Now it correctly fails!
Why does it fail?
This fails because the vite
build process escapes the sandbox. Specifically by the time vite.config.ts
is evaluated, the @
alias is resolved to an absolute path that is under the workspace execroot rather than the sandbox execroot.
To check this, here's the diff I applied to vite.config.ts
:
diff --git a/frontend/vue/vite.config.ts b/frontend/vue/vite.config.ts
index b86c7aa..678c9eb 100644
--- a/frontend/vue/vite.config.ts
+++ b/frontend/vue/vite.config.ts
@@ -1,4 +1,4 @@
-import { fileURLToPath, URL } from 'url';
+// import { fileURLToPath, URL } from 'url'; // Commented out to avoid sandbox escape
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
@@ -9,7 +9,19 @@ export default defineConfig({
plugins: [vue(), vueJsx()],
resolve: {
alias: {
- '@': fileURLToPath(new URL('./src', import.meta.url)),
+ '@': (() => {
+ // @ts-ignore - We know process exists at runtime
+ console.error(`DEBUG: Vite config - process.cwd(): ${process.cwd()}`);
+ console.error(`DEBUG: Vite config - import.meta.url: ${import.meta.url}`);
+
+ // WORKAROUND: Use process.cwd() instead of import.meta.url to avoid sandbox escape
+ // const resolved = fileURLToPath(new URL('./src', import.meta.url));
+ // @ts-ignore - We know process exists at runtime
+ const resolved = process.cwd() + '/src';
+ console.error(`DEBUG: Vite config - resolved @/ alias to: ${resolved}`);
+
+ return resolved;
+ })(),
},
dedupe: ['vue'],
Now build such that there's a build failure (can do this by just building with HelloWorld.vue
deleted), and we see:
DEBUG: Vite config - process.cwd(): /private/var/tmp/_bazel_tenzinlow/c154fecce8d0e75559a8f01413d9ae9e/sandbox/darwin-sandbox/289/execroot/_main/bazel-out/darwin_arm64-fastbuild/bin/vue
DEBUG: Vite config - import.meta.url: file:///private/var/tmp/_bazel_tenzinlow/c154fecce8d0e75559a8f01413d9ae9e/execroot/_main/bazel-out/darwin_arm64-fastbuild/bin/vue/vite.config.ts
DEBUG: Vite config - resolved @/ alias to: /private/var/tmp/_bazel_tenzinlow/c154fecce8d0e75559a8f01413d9ae9e/sandbox/darwin-sandbox/289/execroot/_main/bazel-out/darwin_arm64-fastbuild/bin/vue/src
Specifically, note that import.meta.url
resolves to the workspace execroot (/private/var/tmp/_bazel_tenzinlow/c154fecce8d0e75559a8f01413d9ae9e/execroot/_main/
)! Not the proper sandbox execroot for the action (/private/var/tmp/_bazel_tenzinlow/c154fecce8d0e75559a8f01413d9ae9e/sandbox/darwin-sandbox/289/execroot/_main/
, which we can see process.cwd()
is correctly in, note the sandbox
in the path).
Potential Solution?
As noted in that diff, at least this specific failure is fixed by using process.cwd()
to get a path that's in the sandbox directory. After applying that diff, doing the above steps to try and repro (build first with file, then with it deleted) will properly fail the build on the second go. I think there are potentially still other paths that vite
may access (e.g.: when importing node modules) that escape the sandbox directory, but this at least solves it for the alias.
This was a big deal for our company, as this sandbox escape was causing some real havoc in our CI as the workspace bazel-out/
as we were changing a build process to use JS instead of TS files, but our CI runners would then have stale *.ts
files next to the new *.js
files in bazel-out/
, and Vite didn't handle this well (since it would look at the old TS files, which could have different content). This was causing our remote builds to fail, with the working solution being to bazel clean
first. After we updated our vite.config.ts
to use process.cwd()
as suggested in this patch it fixed our issue, but it does seem like a bit of a code smell.
Related Docs/Issues
- https://github.com/aspect-build/rules_js/blob/f3d0c94f31f9958d1a234a27e83563f63747ee00/docs/js_binary.md#L76
- This comment on
js_binary
implies that therules_js
team knows that things escape the sandbox, but it still feels strange to me that the example committed to the publicexamples/
repo still suffers from a sandbox escape without at least calling it out in a more obvious way.
- This comment on
- ESM imports escape the sandbox & runfiles aspect-build/rules_js#362
- A longstanding issue within
rules_js
that suggests ESM imports have just been permanently escaping the sandbox? - Last comment in the thread suggests a patch to the Node fs patch
js_binary
applies to Node when running Node, but I tried it and it didn't solve this issue. I don't know howimport.meta.url
is calculated inNode
, but the fs patch doesn't seem to catch it?
- A longstanding issue within