fix(artifacts): read every ZIP package family extraction is asked to open (#2493)
extract_artifact carried its own copy of the ZIP package format list with five of the eight members the domain owner declares, and used it both as the guard that refuses a format without an extraction reader and as the selector for the reader itself. A .hap, .hsp or App Pack target was therefore refused with "Artifact format has no extraction reader" even though the next branch builds a ZipArtifactReader for exactly those formats, while inventory_artifact classifies the same path through the owner and reads it. Import isZipPackageFormat from the domain owner and delete the duplicate list and its predicate, removing the second representation instead of reconciling it. The reader branch now narrows to ZipPackageFormat, which is what the ZipArtifactReader constructor already requires. Verified with 41 boundary extraction, ZIP integrity and detection tests, check:fast and verify:test-discovery on macOS arm64. Reverting only the source change fails exactly the three added families and leaves the zip control green. Co-authored-by: 吴佳浩 <wujiahao@supremind.com>
This commit is contained in:
parent
141d2095f5
commit
7017c4d68c
|
|
@ -17,6 +17,7 @@ import { DirectoryArtifactReader } from "../DirectoryArtifactReader.js";
|
|||
import { SafeOutputTree } from "../SafeOutputTree.js";
|
||||
import { SafeOutputTreeCreationFailure } from "../SafeOutputTreeCreationFailure.js";
|
||||
import { ZipArtifactReader } from "../ZipArtifactReader.js";
|
||||
import { isZipPackageFormat } from "../../domain/zipPackageFormat.js";
|
||||
import { MachOSliceArtifactReader } from "../MachOSliceArtifactReader.js";
|
||||
import {
|
||||
artifactExtractionResultSchema,
|
||||
|
|
@ -400,13 +401,6 @@ interface ExtractionInventory {
|
|||
readonly integrityContradictions: readonly IntegrityContradiction[];
|
||||
}
|
||||
|
||||
const ZIP_FORMATS = ["ipa", "apk", "msix", "appx", "zip"] as const;
|
||||
|
||||
const isZipFormat = (
|
||||
format: BinaryTarget["format"],
|
||||
): format is (typeof ZIP_FORMATS)[number] =>
|
||||
(ZIP_FORMATS as readonly string[]).includes(format);
|
||||
|
||||
/**
|
||||
* Refuse a format without an extraction reader before inventory work starts.
|
||||
* The target kind, not the host, is unsupported by extraction. The error keeps
|
||||
|
|
@ -422,7 +416,7 @@ const requireExtractableFormat = async (
|
|||
!directory &&
|
||||
format !== "asar" &&
|
||||
!isMachOFormat(format) &&
|
||||
!isZipFormat(format)
|
||||
!isZipPackageFormat(format)
|
||||
)
|
||||
throw new AnalysisUnsupportedTargetError(
|
||||
"extract_artifact",
|
||||
|
|
@ -443,7 +437,7 @@ const createReader = async (
|
|||
if (await requireExtractableFormat(path, input))
|
||||
return new DirectoryArtifactReader(path);
|
||||
if (format === "asar") return new AsarArtifactReader(path);
|
||||
if (isZipFormat(format)) return new ZipArtifactReader(path, format);
|
||||
if (isZipPackageFormat(format)) return new ZipArtifactReader(path, format);
|
||||
return new MachOSliceArtifactReader(path, input.environment);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -3,10 +3,12 @@ import { join } from "node:path";
|
|||
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { writeOrderedZip } from "../../../fixtures/artifactEntryOrder.js";
|
||||
import { createTestTempDirectory } from "../../../fixtures/temporaryDirectory.js";
|
||||
|
||||
import { ArtifactProvider } from "../../../../src/artifacts/ArtifactProvider.js";
|
||||
import { artifactExtractionExecutionSchema } from "../../../../src/contracts/artifactToolContracts.js";
|
||||
import { artifactExtractionResultSchema } from "../../../../src/domain/artifactGraph.js";
|
||||
import type { BinaryTarget } from "../../../../src/domain/binaryTargetTypes.js";
|
||||
|
||||
describe("artifact extraction format support", () => {
|
||||
|
|
@ -42,6 +44,33 @@ describe("artifact extraction format support", () => {
|
|||
});
|
||||
await expect(access(output)).rejects.toThrow();
|
||||
});
|
||||
|
||||
it.each(["zip", "hap", "hsp", "app-pack"] as const)(
|
||||
"extracts the ZIP-backed %s package family that inventory already supports",
|
||||
async (format) => {
|
||||
const root = await createTestTempDirectory("rea-extract-zip-family-");
|
||||
const archive = join(
|
||||
root,
|
||||
`Payload.${format === "app-pack" ? "app" : format}`,
|
||||
);
|
||||
await writeOrderedZip(archive, ["payload.txt"]);
|
||||
const result = await new ArtifactProvider(process.env)
|
||||
.createClient(archiveTarget(archive, format))
|
||||
.execute(
|
||||
"extract_artifact",
|
||||
artifactExtractionExecutionSchema.parse({
|
||||
output_root: join(root, "output"),
|
||||
}),
|
||||
);
|
||||
if (!result.ok) throw result.error;
|
||||
const extracted = artifactExtractionResultSchema.parse(
|
||||
result.value.result,
|
||||
);
|
||||
expect(
|
||||
extracted.artifacts.map(({ relative_path }) => relative_path),
|
||||
).toEqual(["payload.txt"]);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
const archiveTarget = (
|
||||
|
|
|
|||
Loading…
Reference in New Issue