Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions desktop/scripts/tauri-command.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,28 @@ export function runTauriCommand(args) {
// Tauri runs beforeBuildCommand and then consumes frontendDist. Give the
// entire invocation a private directory so concurrent OSS/internal packages
// cannot replace one another's assets between those two operations.
const invocationRoot = mkdtempSync(
let invocationRoot = mkdtempSync(
path.join(tmpdir(), "buzz-tauri-package-assets-"),
);
// `frontendDist` deserializes into an untagged enum whose first variant is a
// URL, and a Windows absolute path parses as one -- `C:` becomes the scheme.
// Tauri then embeds zero assets, exits 0, and the app boots to
// ERR_FILE_NOT_FOUND. Hand it a path relative to the config's own directory,
// which can never parse as a URL. If the temp dir is on another drive there
// is no relative form, so put the scratch root beside the config instead.
const configDir = path.join(desktopRoot, "src-tauri");
const relativeTo = (root) =>
path.relative(configDir, path.join(root, "dist"));
if (path.isAbsolute(relativeTo(invocationRoot))) {
rmSync(invocationRoot, { recursive: true, force: true });
invocationRoot = mkdtempSync(
path.join(desktopRoot, ".buzz-tauri-package-assets-"),
);
}
const frontendDist = path.join(invocationRoot, "dist");
const outputOverride = JSON.stringify({ build: { frontendDist } });
const outputOverride = JSON.stringify({
build: { frontendDist: relativeTo(invocationRoot) },
});

try {
const delimiterIndex = args.indexOf("--");
Expand Down
43 changes: 38 additions & 5 deletions desktop/src/protectedFeatures/tauriCommand.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const desktopRoot = path.resolve(
path.dirname(fileURLToPath(import.meta.url)),
"../..",
);
const configDir = path.join(desktopRoot, "src-tauri");
const wrapper = path.join(desktopRoot, "scripts/tauri-command.mjs");
const fakeCli = path.join(tmpdir(), `buzz-fake-tauri-${process.pid}.mjs`);

Expand All @@ -20,14 +21,21 @@ import path from "node:path";
const args = process.argv.slice(2);
const configIndex = args.lastIndexOf("--config");
const override = JSON.parse(args[configIndex + 1]);
const output = override.build.frontendDist;
mkdirSync(output, { recursive: true });
writeFileSync(path.join(output, "variant.txt"), process.env.VITE_BUZZ_BESTIE);
const configured = override.build.frontendDist;
// Tauri resolves frontendDist against the directory holding tauri.conf.json
// (config_parent.join(path) in tauri-codegen), not against the process cwd.
const output = path.resolve(process.env.BUZZ_TEST_CONFIG_DIR, configured);
// Write through the producer path the wrapper publishes and read back through
// the config-resolved consumer path. Doing both against one path would make the
// fake agree with itself no matter where the wrapper pointed frontendDist.
const producer = process.env.BUZZ_PROTECTED_BUILD_OUTPUT;
mkdirSync(producer, { recursive: true });
writeFileSync(path.join(producer, "variant.txt"), process.env.VITE_BUZZ_BESTIE);
await new Promise((resolve) => setTimeout(resolve, 100));
const observed = readFileSync(path.join(output, "variant.txt"), "utf8");
writeFileSync(
process.env.BUZZ_TEST_RESULT,
JSON.stringify({ args, output, observed }),
JSON.stringify({ args, configured, output, observed }),
);
`,
);
Expand All @@ -42,6 +50,7 @@ function packageVariant(variant, result, runnerArguments = []) {
env: {
...process.env,
BUZZ_TAURI_CLI_ENTRYPOINT: fakeCli,
BUZZ_TEST_CONFIG_DIR: configDir,
BUZZ_TEST_RESULT: result,
VITE_BUZZ_BESTIE: variant,
},
Expand Down Expand Up @@ -92,6 +101,30 @@ test("private config precedes Cargo runner arguments", async () => {
assert.equal(invocation.args[delimiterIndex + 1], "--locked");
assert.equal(
JSON.parse(invocation.args[privateConfigIndex + 1]).build.frontendDist,
invocation.output,
invocation.configured,
);
});

test("private frontendDist is never mistaken for a URL", async () => {
const result = path.join(
tmpdir(),
`buzz-tauri-frontend-dist-${process.pid}.json`,
);
await packageVariant("0", result);
const invocation = JSON.parse(readFileSync(result, "utf8"));

// `FrontendDist` is an untagged enum whose first variant is `Url(Url)`, and
// tauri-codegen embeds *no assets without erroring* for that variant. A
// Windows absolute path parses as a URL -- `C:` becomes the scheme -- so an
// absolute frontendDist produces a UI-less app that still exits 0.
assert.ok(
!path.isAbsolute(invocation.configured),
`frontendDist must stay relative, got ${invocation.configured}`,
);
// Rust's `url` crate and Node's `URL` both implement the WHATWG standard, so
// this is the same parse serde performs. It only rejects absolute paths on
// Windows, which is why the assertion above carries the check on Linux/macOS.
assert.throws(() => new URL(invocation.configured));
// The relative path still has to reach the directory the wrapper published.
assert.equal(invocation.observed, "0");
});
Loading