refactor(artifacts): retain admitted Godot pack location

This commit is contained in:
morluto committed 2026-10-27 08:06:23 +08:00
1 parent 69b328eec8
commit 5d25aa4f52
6 files changed
+130 -32

No files matched your search

+73 -21
View File
@@ -17,6 +17,7 @@ import { createTestTempDirectory } from "../../tests/fixtures/temporaryDirectory
import { openRegularFile } from "../filesystem/RegularFile.js";
import type { ArtifactEntry } from "./ArtifactReader.js";
import { ArtifactEmbeddedFormatProbes } from "./ArtifactEmbeddedFormatProbes.js";
import { GodotPckArtifactReader } from "./GodotPckArtifactReader.js";
const writePck = async (bytes: Buffer): Promise<string> => {
@@ -262,32 +263,83 @@ describe("GodotPckArtifactReader member streams", () => {
);
});
it("reads members from the admitted descriptor after the path is replaced", async () => {
const root = await createTestTempDirectory("rea-godot-borrowed-source-");
const selected = join(root, "selected");
await mkdir(selected);
const path = join(selected, "game");
await writeFile(path, buildGodotPck(3, members));
it("rejects stale located facts after the admitted carrier changes", async () => {
const path = await writePck(buildGodotPck(3, members));
const descriptor = await openRegularFile(path, { symlinks: "reject" });
const { handle } = descriptor;
try {
// Replace the selected directory without changing the admitted inode's
// metadata. Moving the file itself would change its validated ctime.
await rename(selected, join(root, "original"));
await mkdir(selected);
await writeFile(path, buildGodotPck(3, [{ path: "other.gd", data: "" }]));
const reader = new GodotPckArtifactReader(path, descriptor);
const entries = await list(reader);
expect(entries.map(({ path: member }) => member)).toEqual([
"scenes/main.gd",
"project.binary",
"data/é.json",
]);
await reader.close();
const location = await new ArtifactEmbeddedFormatProbes(
path,
descriptor,
).godotPck();
if (location === undefined) throw new Error("expected a pack location");
const reader = new GodotPckArtifactReader(path, {
kind: "godot-pck",
source: descriptor,
location,
});
try {
await writeFile(path, Buffer.from("replaced carrier"));
await expect(list(reader)).rejects.toMatchObject({
reason: "integrity",
});
} finally {
await reader.close();
}
} finally {
await handle.close();
await descriptor.handle.close();
}
});
it.each([false, true])(
"reads located pack facts from the admitted descriptor after path replacement (embedded: %s)",
async (embedded) => {
const root = await createTestTempDirectory("rea-godot-borrowed-source-");
const selected = join(root, "selected");
await mkdir(selected);
const path = join(selected, "game");
const bytes = buildGodotPck(3, members);
await writeFile(path, embedded ? embedGodotPck(bytes) : bytes);
const descriptor = await openRegularFile(path, { symlinks: "reject" });
const { handle } = descriptor;
try {
const location = await new ArtifactEmbeddedFormatProbes(
path,
descriptor,
).godotPck();
if (location === undefined) throw new Error("expected a pack location");
// Replace the selected directory without changing the admitted inode's
// metadata. Moving the file itself would change its validated ctime.
await rename(selected, join(root, "original"));
await mkdir(selected);
await writeFile(
path,
buildGodotPck(3, [{ path: "other.gd", data: "" }]),
);
const reader = new GodotPckArtifactReader(path, {
kind: "godot-pck",
source: descriptor,
location,
});
const cancelled = new AbortController();
cancelled.abort();
await expect(
reader.entries(cancelled.signal)[Symbol.asyncIterator]().next(),
).rejects.toMatchObject({
reason: "cancelled",
});
const entries = await list(reader);
expect(reader.formatMetadata()?.embedded).toBe(embedded);
expect(entries.map(({ path: member }) => member)).toEqual([
"scenes/main.gd",
"project.binary",
"data/é.json",
]);
await reader.close();
} finally {
await handle.close();
}
},
);
});
describe("Godot PCK logical-byte admission", () => {
+28 -5
View File
@@ -13,7 +13,12 @@ import {
} from "./ArtifactReader.js";
import { ArtifactResourceScope } from "./ArtifactResourceScope.js";
import { assertArtifactReadActive } from "./ArtifactFileOperation.js";
import { locateGodotPck, readPckBytes } from "./GodotPckLocation.js";
import {
locateGodotPck,
readPckBytes,
type GodotPckAdmission,
type GodotPckLocation,
} from "./GodotPckLocation.js";
import type { GodotPckMetadata } from "../domain/artifactFormatMetadata.js";
import { jsonValueByteLength } from "../domain/jsonValueByteLength.js";
import { readFileHandleChunks } from "../filesystem/readFileHandleChunks.js";
@@ -37,12 +42,23 @@ export class GodotPckArtifactReader implements ArtifactReader {
readonly #entries = new Map<string, ArtifactEntry>();
readonly #streams = new Set<Readable>();
readonly #source: ArtifactFileSource;
readonly #location: GodotPckLocation | undefined;
#prepared: Promise<void> | undefined;
#header: GodotPckMetadata | undefined;
#closing = false;
constructor(path: string, admitted?: StableRegularFileDescriptor) {
this.#source = new ArtifactFileSource(path, "Godot PCK", admitted);
constructor(
path: string,
admitted?: StableRegularFileDescriptor | GodotPckAdmission,
) {
const admission =
admitted !== undefined && "kind" in admitted ? admitted : undefined;
this.#source = new ArtifactFileSource(
path,
"Godot PCK",
admitted !== undefined && "kind" in admitted ? admitted.source : admitted,
);
this.#location = admission?.location;
}
formatMetadata(): GodotPckMetadata | undefined {
@@ -174,7 +190,12 @@ export class GodotPckArtifactReader implements ArtifactReader {
async #prepare(signal?: AbortSignal): Promise<void> {
const { handle, initial } = await this.#source.read(signal);
const header = await readGodotPckHeader(handle, initial.size, signal);
const header = await readGodotPckHeader(
handle,
initial.size,
signal,
this.#location,
);
this.#header = header;
if (header.directory_status !== "available") return;
const cursor = new PckCursor(
@@ -298,8 +319,10 @@ const readGodotPckHeader = async (
handle: FileHandle,
size: number,
signal?: AbortSignal,
admittedLocation?: GodotPckLocation,
): Promise<GodotPckMetadata> => {
const location = await locateGodotPck(handle, size, signal);
const location =
admittedLocation ?? (await locateGodotPck(handle, size, signal));
if (location === undefined)
throw formatFailure("no standalone header or executable trailer");
const { start, end, embedded } = location;
+8
View File
@@ -1,3 +1,4 @@
import type { StableRegularFileDescriptor } from "../filesystem/RegularFile.js";
import type { FileHandle } from "node:fs/promises";
import {
hasGodotPckHeader,
@@ -13,6 +14,13 @@ export interface GodotPckLocation {
readonly embedded: boolean;
}
/** Located pack facts borrowed with the descriptor that produced them. */
export interface GodotPckAdmission {
readonly kind: "godot-pck";
readonly source: StableRegularFileDescriptor;
readonly location: GodotPckLocation;
}
type DeclaredGodotPckTrailer = Extract<
GodotPckTrailerFacts,
{ readonly state: "declared" }
+13 -4
View File
@@ -1,3 +1,4 @@
import type { GodotPckAdmission } from "./GodotPckLocation.js";
import type { PyInstallerAdmission } from "./pyinstaller/PyInstallerAdmission.js";
import type { NodeSeaAdmission } from "./nodeSea/NodeSeaAdmission.js";
import type { AsarHeaderAdmission } from "../config/asarHeaderAdmission.js";
@@ -35,6 +36,7 @@ interface ArtifactReaderConstruction {
| StableRegularFileDescriptor
| NodeSeaAdmission
| PyInstallerAdmission
| GodotPckAdmission
| undefined;
readonly asarHeaderAdmission?: AsarHeaderAdmission | undefined;
/** Caller-selected root spelling; only ASAR companion lookup consumes this. */
@@ -90,12 +92,19 @@ export function createArtifactReader(
path,
admission !== undefined &&
"kind" in admission &&
admission.kind === "pyinstaller"
? rootSource
: admission,
admission.kind === "node-sea"
? admission
: rootSource,
);
case "godot-pck":
return new GodotPckArtifactReader(path, rootSource);
return new GodotPckArtifactReader(
path,
admission !== undefined &&
"kind" in admission &&
admission.kind === "godot-pck"
? admission
: rootSource,
);
case "mach-o-universal":
return new MachOSliceArtifactReader(
path,
+5 -1
View File
@@ -1,3 +1,4 @@
import type { GodotPckAdmission } from "../GodotPckLocation.js";
import type { PyInstallerAdmission } from "../pyinstaller/PyInstallerAdmission.js";
import { assertArtifactSelectedSource } from "../ArtifactSelectedSource.js";
import { artifactNameForClassification } from "../../domain/artifactNameSelection.js";
@@ -53,6 +54,7 @@ type RootInventoryClassification = RootClassification & {
| StableRegularFileDescriptor
| NodeSeaAdmission
| PyInstallerAdmission
| GodotPckAdmission
| undefined;
};
@@ -208,6 +210,7 @@ const classifyRootFormat = async (
| ArtifactOccurrence["artifact_format"]
| NodeSeaAdmission
| PyInstallerAdmission
| GodotPckAdmission
> => {
const { handle } = source;
const probe = createArtifactClassificationProbe(asarHeaderAdmission);
@@ -261,7 +264,8 @@ const classifyRootFormat = async (
(await hasDiskImageSignature(archiveBytes, read))
)
return "dmg";
if (await embeddedProbes.godotPck(evidence.prefix)) return "godot-pck";
const pck = await embeddedProbes.godotPck(evidence.prefix);
if (pck !== undefined) return { kind: "godot-pck", source, location: pck };
const fatEvidence = await readRootFatSliceHeaders(handle, evidence, signal);
const mzWindowsHeaderSignature = await readRootMzWindowsHeaderSignature(
handle,
+3 -1
View File
@@ -1,3 +1,4 @@
import type { GodotPckAdmission } from "../GodotPckLocation.js";
import type { PyInstallerAdmission } from "../pyinstaller/PyInstallerAdmission.js";
import type { NodeSeaAdmission } from "../nodeSea/NodeSeaAdmission.js";
import type { AsarHeaderAdmission } from "../../config/asarHeaderAdmission.js";
@@ -23,7 +24,8 @@ export const createReader = (
readerAdmission?:
| StableRegularFileDescriptor
| NodeSeaAdmission
| PyInstallerAdmission,
| PyInstallerAdmission
| GodotPckAdmission,
asarHeaderAdmission?: AsarHeaderAdmission,
selectedPath?: string,
): ArtifactReader | undefined => {