fix(desktop): accept migrated renderer origin in cross-version startup
The previous formal native already loads edgeever-app://, so the old-to-new gate must accept that origin as well as the legacy file:// renderer.
This commit is contained in:
1 parent
94e082addb
commit
ba56c5883c
4 files changed
+95
-34
No files matched your search
+1
-1
@@ -59,7 +59,7 @@
|
||||
"build:desktop": "EDGE_EVER_DESKTOP_BUILD=1 bun run prepare:desktop:icons && bun run build:web && bun run build:desktop:sidecar && bun run --cwd apps/desktop dist",
|
||||
"benchmark:desktop:sidecar": "bun scripts/benchmark-desktop-sidecar.mjs",
|
||||
"verify:web-performance": "bun scripts/verify-web-performance.mjs",
|
||||
"test:release-planning": "bun test scripts/plan-native-release.test.mjs scripts/check-native-release-assets.test.mjs scripts/create-mac-update-metadata.test.mjs scripts/windows-update-metadata.test.mjs scripts/linux-update-release.test.mjs scripts/desktop-build-workflow.test.mjs scripts/pe-imports.test.mjs scripts/report-native-build-timings.test.mjs scripts/release.test.mjs scripts/validate-store-delivery.test.mjs scripts/store-delivery.test.mjs scripts/download-play-universal-apk.test.mjs scripts/verify-android-apk-signature.test.mjs scripts/desktop-icns.test.mjs scripts/preview-macos-dock-icon.test.mjs apps/web/src/lib/version-check.test.mjs apps/mobile/src/lib/mobile-release.test.ts",
|
||||
"test:release-planning": "bun test scripts/plan-native-release.test.mjs scripts/check-native-release-assets.test.mjs scripts/create-mac-update-metadata.test.mjs scripts/windows-update-metadata.test.mjs scripts/linux-update-release.test.mjs scripts/desktop-build-workflow.test.mjs scripts/pe-imports.test.mjs scripts/report-native-build-timings.test.mjs scripts/release.test.mjs scripts/validate-store-delivery.test.mjs scripts/store-delivery.test.mjs scripts/download-play-universal-apk.test.mjs scripts/verify-android-apk-signature.test.mjs scripts/desktop-icns.test.mjs scripts/verify-desktop-cross-version-startup.test.mjs scripts/preview-macos-dock-icon.test.mjs apps/web/src/lib/version-check.test.mjs apps/mobile/src/lib/mobile-release.test.ts",
|
||||
"release": "bun scripts/release.mjs",
|
||||
"publish:stores": "bun scripts/store-delivery.mjs",
|
||||
"test:desktop:sidecar": "bun scripts/test-desktop-sidecar.mjs",
|
||||
|
||||
@@ -52,6 +52,7 @@ export const RELEASE_VALIDATIONS = [
|
||||
"scripts/store-delivery.test.mjs",
|
||||
"scripts/download-play-universal-apk.test.mjs",
|
||||
"scripts/desktop-icns.test.mjs",
|
||||
"scripts/verify-desktop-cross-version-startup.test.mjs",
|
||||
"apps/web/src/lib/version-check.test.mjs",
|
||||
"apps/mobile/src/lib/mobile-release.test.ts",
|
||||
],
|
||||
|
||||
@@ -4,21 +4,21 @@ import { mkdir, rename, rm } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join, resolve } from "node:path";
|
||||
|
||||
const [oldExecutableInput, newExecutableInput] = process.argv.slice(2);
|
||||
const oldExecutable = resolve(oldExecutableInput || "");
|
||||
const newExecutable = resolve(newExecutableInput || "");
|
||||
for (const [label, executable] of [["old", oldExecutable], ["new", newExecutable]]) {
|
||||
if (!executable || !existsSync(executable) || !statSync(executable).isFile()) {
|
||||
throw new Error(`${label} desktop executable is missing: ${executable || "<not provided>"}`);
|
||||
}
|
||||
}
|
||||
export const LEGACY_RENDERER_URL_PREFIX = "file://";
|
||||
export const CURRENT_RENDERER_URL_PREFIX = "edgeever-app://app/";
|
||||
|
||||
export const isPreviousReleaseRendererUrl = (url) => {
|
||||
const value = String(url || "");
|
||||
return value.startsWith(LEGACY_RENDERER_URL_PREFIX)
|
||||
|| value.startsWith(CURRENT_RENDERER_URL_PREFIX);
|
||||
};
|
||||
|
||||
export const isCurrentReleaseRendererUrl = (url) =>
|
||||
String(url || "").startsWith(CURRENT_RENDERER_URL_PREFIX);
|
||||
|
||||
const testDirectory = mkdtempSync(join(tmpdir(), "edgeever-cross-version-"));
|
||||
const userDataDirectory = join(testDirectory, "profile");
|
||||
const logPath = join(userDataDirectory, "logs", "desktop.log");
|
||||
const wait = (milliseconds) => new Promise((resolveWait) => setTimeout(resolveWait, milliseconds));
|
||||
|
||||
const readEntries = () => {
|
||||
const readEntries = (logPath) => {
|
||||
if (!existsSync(logPath)) return [];
|
||||
return readFileSync(logPath, "utf8").split(/\r?\n/).flatMap((line) => {
|
||||
try { return [JSON.parse(line)]; } catch { return []; }
|
||||
@@ -38,7 +38,14 @@ const stopProcess = async (child) => {
|
||||
]);
|
||||
};
|
||||
|
||||
const launchAndWait = async ({ executable, expectedUrlPrefix, requiredEvents }) => {
|
||||
const launchAndWait = async ({
|
||||
executable,
|
||||
userDataDirectory,
|
||||
logPath,
|
||||
acceptUrl,
|
||||
expectedUrlDescription,
|
||||
requiredEvents,
|
||||
}) => {
|
||||
const output = [];
|
||||
let processError = null;
|
||||
const child = spawn(executable, [`--user-data-dir=${userDataDirectory}`], {
|
||||
@@ -54,10 +61,10 @@ const launchAndWait = async ({ executable, expectedUrlPrefix, requiredEvents })
|
||||
const deadline = Date.now() + timeoutMs;
|
||||
try {
|
||||
while (Date.now() < deadline) {
|
||||
const entries = readEntries();
|
||||
const entries = readEntries(logPath);
|
||||
const events = new Set(entries.map((entry) => entry.event));
|
||||
const loaded = entries.find((entry) =>
|
||||
entry.event === "renderer.loaded" && String(entry.url || "").startsWith(expectedUrlPrefix));
|
||||
entry.event === "renderer.loaded" && acceptUrl(entry.url));
|
||||
if (loaded && requiredEvents.every((event) => events.has(event))) {
|
||||
return { events: [...events], rendererUrl: loaded.url };
|
||||
}
|
||||
@@ -66,7 +73,7 @@ const launchAndWait = async ({ executable, expectedUrlPrefix, requiredEvents })
|
||||
}
|
||||
throw new Error([
|
||||
`Desktop transition startup failed for ${executable} (exit=${child.exitCode ?? "running"}).`,
|
||||
`Expected renderer URL prefix: ${expectedUrlPrefix}`,
|
||||
`Expected renderer URL: ${expectedUrlDescription}`,
|
||||
processError && `Process error: ${processError.message}`,
|
||||
existsSync(logPath) && `Diagnostic log:\n${readFileSync(logPath, "utf8")}`,
|
||||
output.length > 0 && `Process output:\n${output.join("")}`,
|
||||
@@ -76,23 +83,54 @@ const launchAndWait = async ({ executable, expectedUrlPrefix, requiredEvents })
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
await mkdir(userDataDirectory, { recursive: true });
|
||||
const oldResult = await launchAndWait({
|
||||
executable: oldExecutable,
|
||||
expectedUrlPrefix: "file://",
|
||||
requiredEvents: ["sidecar.ready", "renderer.bootstrap-ready"],
|
||||
});
|
||||
await rename(logPath, join(userDataDirectory, "logs", "desktop-old-version.log"));
|
||||
export const verifyDesktopCrossVersionStartup = async ({
|
||||
oldExecutable,
|
||||
newExecutable,
|
||||
}) => {
|
||||
for (const [label, executable] of [["old", oldExecutable], ["new", newExecutable]]) {
|
||||
if (!executable || !existsSync(executable) || !statSync(executable).isFile()) {
|
||||
throw new Error(`${label} desktop executable is missing: ${executable || "<not provided>"}`);
|
||||
}
|
||||
}
|
||||
|
||||
const newResult = await launchAndWait({
|
||||
executable: newExecutable,
|
||||
expectedUrlPrefix: "edgeever-app://app/",
|
||||
requiredEvents: ["renderer.origin-ready", "sidecar.ready", "renderer.bootstrap-ready"],
|
||||
const testDirectory = mkdtempSync(join(tmpdir(), "edgeever-cross-version-"));
|
||||
const userDataDirectory = join(testDirectory, "profile");
|
||||
const logPath = join(userDataDirectory, "logs", "desktop.log");
|
||||
try {
|
||||
await mkdir(userDataDirectory, { recursive: true });
|
||||
const oldResult = await launchAndWait({
|
||||
executable: oldExecutable,
|
||||
userDataDirectory,
|
||||
logPath,
|
||||
acceptUrl: isPreviousReleaseRendererUrl,
|
||||
expectedUrlDescription: `${LEGACY_RENDERER_URL_PREFIX} or ${CURRENT_RENDERER_URL_PREFIX}`,
|
||||
requiredEvents: ["sidecar.ready", "renderer.bootstrap-ready"],
|
||||
});
|
||||
await rename(logPath, join(userDataDirectory, "logs", "desktop-old-version.log"));
|
||||
|
||||
const newResult = await launchAndWait({
|
||||
executable: newExecutable,
|
||||
userDataDirectory,
|
||||
logPath,
|
||||
acceptUrl: isCurrentReleaseRendererUrl,
|
||||
expectedUrlDescription: CURRENT_RENDERER_URL_PREFIX,
|
||||
requiredEvents: ["renderer.origin-ready", "sidecar.ready", "renderer.bootstrap-ready"],
|
||||
});
|
||||
const markerPath = join(userDataDirectory, "renderer-origin-v1-migrated");
|
||||
if (!existsSync(markerPath)) {
|
||||
throw new Error("The upgraded desktop did not persist its renderer origin migration marker");
|
||||
}
|
||||
return { ok: true, oldResult, newResult };
|
||||
} finally {
|
||||
await rm(testDirectory, { recursive: true, force: true });
|
||||
}
|
||||
};
|
||||
|
||||
if (import.meta.main) {
|
||||
const [oldExecutableInput, newExecutableInput] = process.argv.slice(2);
|
||||
const result = await verifyDesktopCrossVersionStartup({
|
||||
oldExecutable: resolve(oldExecutableInput || ""),
|
||||
newExecutable: resolve(newExecutableInput || ""),
|
||||
});
|
||||
const markerPath = join(userDataDirectory, "renderer-origin-v1-migrated");
|
||||
if (!existsSync(markerPath)) throw new Error("The upgraded desktop did not persist its renderer origin migration marker");
|
||||
console.log(JSON.stringify({ ok: true, oldResult, newResult }));
|
||||
} finally {
|
||||
await rm(testDirectory, { recursive: true, force: true });
|
||||
console.log(JSON.stringify(result));
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import {
|
||||
CURRENT_RENDERER_URL_PREFIX,
|
||||
LEGACY_RENDERER_URL_PREFIX,
|
||||
isCurrentReleaseRendererUrl,
|
||||
isPreviousReleaseRendererUrl,
|
||||
} from "./verify-desktop-cross-version-startup.mjs";
|
||||
|
||||
describe("desktop cross-version startup origins", () => {
|
||||
test("accepts both the legacy file origin and the current app origin for the previous native", () => {
|
||||
expect(isPreviousReleaseRendererUrl(`${LEGACY_RENDERER_URL_PREFIX}/Applications/EdgeEver.app/Contents/Resources/web/index.html`)).toBe(true);
|
||||
expect(isPreviousReleaseRendererUrl(`${CURRENT_RENDERER_URL_PREFIX}index.html`)).toBe(true);
|
||||
expect(isPreviousReleaseRendererUrl("https://demo.edgeever.org/")).toBe(false);
|
||||
expect(isPreviousReleaseRendererUrl("")).toBe(false);
|
||||
});
|
||||
|
||||
test("requires the current custom protocol after the upgrade", () => {
|
||||
expect(isCurrentReleaseRendererUrl(`${CURRENT_RENDERER_URL_PREFIX}index.html`)).toBe(true);
|
||||
expect(isCurrentReleaseRendererUrl(`${LEGACY_RENDERER_URL_PREFIX}/Applications/EdgeEver.app/Contents/Resources/web/index.html`)).toBe(false);
|
||||
expect(isCurrentReleaseRendererUrl("edgeever-app://other/index.html")).toBe(false);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user