Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e2ce6c28a6 |
No files matched your search
@@ -0,0 +1,27 @@
|
||||
import { readFile } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { expect, it } from "vitest";
|
||||
|
||||
it("bakes fallback release identity when the packaging pipeline omits it", async () => {
|
||||
const config = await readFile(
|
||||
path.resolve(__dirname, "../tsup.config.ts"),
|
||||
"utf8",
|
||||
);
|
||||
|
||||
expect(config).toContain("resolveBuildNumber");
|
||||
expect(config).toContain(
|
||||
"const releaseBuildNumber = resolveBuildNumber(process.env)",
|
||||
);
|
||||
expect(config).toMatch(
|
||||
/__ONE_MEMORY_BUILD_NUMBER__: JSON\.stringify\(\s*String\(releaseBuildNumber\)/,
|
||||
);
|
||||
expect(config).toMatch(
|
||||
/process\.env\.ONE_MEMORY_COMMIT\?\.trim\(\) \|\| readSourceCommit\(\)/,
|
||||
);
|
||||
expect(config).toMatch(
|
||||
/execFileSync\("git", \["rev-parse", "HEAD"\],[\s\S]*?encoding: "utf8"/,
|
||||
);
|
||||
expect(config).toContain(
|
||||
"__ONE_MEMORY_COMMIT__: JSON.stringify(releaseCommit)",
|
||||
);
|
||||
});
|
||||
@@ -234,7 +234,9 @@ describe("desktop lifecycle failure paths", () => {
|
||||
await f.flush();
|
||||
expect(f.createWindow).not.toHaveBeenCalled();
|
||||
expect(f.quit().preventDefault).toHaveBeenCalled();
|
||||
f.stopping.resolve();
|
||||
// Teardown can fail after the startup error (for example, while writing a
|
||||
// diagnostic). It must not replace the useful error with a second dialog.
|
||||
f.stopping.reject(new Error("diagnostic_detail_key_invalid"));
|
||||
await f.flush();
|
||||
// Whatever did come up before the failure still has to be torn down.
|
||||
expect(f.stop).toHaveBeenCalledTimes(1);
|
||||
|
||||
@@ -108,7 +108,9 @@ export function startDesktopLifecycle(options: LifecycleOptions): void {
|
||||
shutdown = startup
|
||||
.catch(() => {})
|
||||
.then(options.stop)
|
||||
.catch(options.onError)
|
||||
// Quit-time failures are logged by stop itself. A second modal here can
|
||||
// only obscure the startup error that initiated this shutdown.
|
||||
.catch(report)
|
||||
.finally(() => {
|
||||
stopped = true;
|
||||
app.quit();
|
||||
|
||||
@@ -124,9 +124,10 @@ export function hourlyRuntimeInstanceId(): string {
|
||||
* `build_number` is the only ordering authority the installer compares, so a
|
||||
* package that omits it defaults to 0 and can never outrank an earlier install:
|
||||
* R->R.1 would be refused as a downgrade. The value is baked into the bundle's
|
||||
* `LSEnvironment` at pack time (`apps/desktop/scripts/after-pack.cjs`); this
|
||||
* reads it back and never invents one, so a source checkout that did not pass a
|
||||
* build number stays undefined rather than fabricating order.
|
||||
* `LSEnvironment` at pack time (`apps/desktop/scripts/after-pack.cjs`) and is
|
||||
* also compiled from the canonical root release contract. This reader accepts
|
||||
* either source so Finder launches still carry ordering even when an external
|
||||
* packaging pipeline omitted the plist field.
|
||||
*/
|
||||
export function resolvePackagedBuildNumber(
|
||||
environmentValue: unknown,
|
||||
|
||||
@@ -1404,7 +1404,7 @@ if (desktopVersion !== null) {
|
||||
component: "desktop.lifecycle",
|
||||
event: "desktop.runtime_stop",
|
||||
outcome: event.outcome,
|
||||
details: { runtime: event.detail },
|
||||
details: { runtime_state: event.detail },
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,12 +1,29 @@
|
||||
import { execFileSync } from "node:child_process";
|
||||
import { createRequire } from "node:module";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { defineConfig } from "tsup";
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const { resolveBuildVersion } =
|
||||
const { resolveBuildNumber, resolveBuildVersion } =
|
||||
require("../../scripts/release-version.cjs") as {
|
||||
resolveBuildNumber(environment?: NodeJS.ProcessEnv, root?: string): number;
|
||||
resolveBuildVersion(environment?: NodeJS.ProcessEnv, root?: string): string;
|
||||
};
|
||||
const releaseVersion = resolveBuildVersion(process.env);
|
||||
const releaseBuildNumber = resolveBuildNumber(process.env);
|
||||
const releaseCommit =
|
||||
process.env.ONE_MEMORY_COMMIT?.trim() || readSourceCommit();
|
||||
|
||||
function readSourceCommit(): string {
|
||||
try {
|
||||
return execFileSync("git", ["rev-parse", "HEAD"], {
|
||||
cwd: fileURLToPath(new URL("../..", import.meta.url)),
|
||||
encoding: "utf8",
|
||||
}).trim();
|
||||
} catch {
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
export default defineConfig({
|
||||
entry: ["src/main.ts", "src/preload.ts"],
|
||||
@@ -27,11 +44,7 @@ export default defineConfig({
|
||||
__ONE_MEMORY_TRIAL_PROFILE__: JSON.stringify(
|
||||
process.env.ONE_MEMORY_BUILD_TRIAL_PROFILE ?? null,
|
||||
),
|
||||
__ONE_MEMORY_BUILD_NUMBER__: JSON.stringify(
|
||||
process.env.ONE_MEMORY_BUILD_NUMBER ?? "unknown",
|
||||
),
|
||||
__ONE_MEMORY_COMMIT__: JSON.stringify(
|
||||
process.env.ONE_MEMORY_COMMIT ?? "unknown",
|
||||
),
|
||||
__ONE_MEMORY_BUILD_NUMBER__: JSON.stringify(String(releaseBuildNumber)),
|
||||
__ONE_MEMORY_COMMIT__: JSON.stringify(releaseCommit),
|
||||
},
|
||||
});
|
||||
@@ -217,6 +217,50 @@ describe("local Runtime installation", () => {
|
||||
},
|
||||
);
|
||||
|
||||
it("ignores Finder metadata without weakening payload verification", async () => {
|
||||
const value = await distribution();
|
||||
const plan = await planLocalInstall({
|
||||
source: value.source,
|
||||
installRoot: value.installRoot,
|
||||
platform: process.platform,
|
||||
arch: process.arch,
|
||||
});
|
||||
const first = await applyLocalInstall(plan);
|
||||
await writeFile(path.join(plan.version_directory, ".DS_Store"), "finder");
|
||||
|
||||
await expect(
|
||||
verifyInstalledDistribution(
|
||||
plan.version_directory,
|
||||
process.platform,
|
||||
process.arch,
|
||||
),
|
||||
).resolves.toMatchObject({ version: plan.version });
|
||||
const repeated = await applyLocalInstall(plan);
|
||||
expect(first.created_version).toBe(true);
|
||||
expect(repeated.created_version).toBe(false);
|
||||
|
||||
await writeFile(
|
||||
path.join(plan.version_directory, "unexpected-runtime-file"),
|
||||
"not metadata",
|
||||
);
|
||||
await expect(
|
||||
verifyInstalledDistribution(
|
||||
plan.version_directory,
|
||||
process.platform,
|
||||
process.arch,
|
||||
),
|
||||
).rejects.toThrow("distribution_manifest_incomplete");
|
||||
});
|
||||
|
||||
it("does not let a non-file masquerade as Finder metadata", async () => {
|
||||
const value = await distribution();
|
||||
await symlink("../outside", path.join(value.source, ".DS_Store"));
|
||||
|
||||
await expect(
|
||||
verifyInstalledDistribution(value.source, process.platform, process.arch),
|
||||
).rejects.toThrow("distribution_path_invalid");
|
||||
});
|
||||
|
||||
it.each(["extra", "hash"])(
|
||||
"same-version reuse still rejects %s changes to installed canonical trees",
|
||||
async (change) => {
|
||||
|
||||
@@ -622,6 +622,12 @@ async function treeFiles(root: string, current = root): Promise<string[]> {
|
||||
entries.sort((left, right) => left.name.localeCompare(right.name));
|
||||
const result: string[] = [];
|
||||
for (const entry of entries) {
|
||||
// Finder may add this metadata file after installation. It is not Runtime
|
||||
// payload and must not make an otherwise verified install unreusable.
|
||||
if (entry.name === ".DS_Store") {
|
||||
if (!entry.isFile()) throw new Error("distribution_path_invalid");
|
||||
continue;
|
||||
}
|
||||
const target = path.join(current, entry.name);
|
||||
const relative = path.relative(root, target);
|
||||
if (!safeRelative(relative)) throw new Error("distribution_path_invalid");
|
||||
|
||||
@@ -256,6 +256,7 @@ describe("installPackagedRuntime", () => {
|
||||
});
|
||||
const selectedBefore = await readlink(current);
|
||||
const inodeBefore = (await lstat(current)).ino;
|
||||
await writeFile(path.join(current, ".DS_Store"), "finder metadata");
|
||||
await expect(
|
||||
installPackagedRuntime({
|
||||
resourcesPath: resources,
|
||||
|
||||
@@ -429,6 +429,11 @@ async function treeFiles(root: string, current = root): Promise<string[]> {
|
||||
entries.sort((left, right) => left.name.localeCompare(right.name));
|
||||
const result: string[] = [];
|
||||
for (const entry of entries) {
|
||||
// Finder metadata is not part of the packaged Runtime identity.
|
||||
if (entry.name === ".DS_Store") {
|
||||
if (!entry.isFile()) throw new Error("distribution_path_invalid");
|
||||
continue;
|
||||
}
|
||||
const target = path.join(current, entry.name);
|
||||
const relative = path.relative(root, target);
|
||||
if (entry.isSymbolicLink()) {
|
||||
|
||||
Reference in new issue
Block a user