mirror of
https://github.com/aphrody-labs/vu.git
synced 2026-10-14 12:19:18 +00:00
fix: bytecode caches are derived data (not in the manifest, not checked); the smoke keeps the artifact pristine
This commit is contained in:
1 parent
f0743b5266
commit
7c3db18909
5 files changed
+161
-4
No files matched your search
@@ -0,0 +1,117 @@
|
||||
{
|
||||
"schema": "aphrody.vu-forge/1",
|
||||
"startedAt": "2026-10-05T21:10:38.090Z",
|
||||
"finishedAt": "2026-10-05T21:10:42.756Z",
|
||||
"ok": true,
|
||||
"revision": "f0743b526600068f0181d579b53e72a87fd2731d",
|
||||
"head": "f0743b526600068f0181d579b53e72a87fd2731d",
|
||||
"dirty": false,
|
||||
"host": "vps",
|
||||
"toolchain": "rustc 1.98.1 (48a229cea 2026-09-01)",
|
||||
"pins": {
|
||||
"uv": {
|
||||
"tag": "0.12.23",
|
||||
"ref": "46b84fd0bfec23b72f29e8e2185ba68a65052f48"
|
||||
},
|
||||
"ruff": {
|
||||
"tag": "0.16.10",
|
||||
"ref": "3265ed1f944c98bb4c04d632fbefb1257cdb583d"
|
||||
},
|
||||
"pyo3": {
|
||||
"tag": "v0.29.3",
|
||||
"ref": "451d99fdcdcddf159e8e0a1186960b332cdb5d7c"
|
||||
}
|
||||
},
|
||||
"steps": [
|
||||
{
|
||||
"name": "tunnel",
|
||||
"ok": true,
|
||||
"ms": 13
|
||||
},
|
||||
{
|
||||
"name": "sync",
|
||||
"ok": true,
|
||||
"ms": 350,
|
||||
"detail": "f0743b526600"
|
||||
},
|
||||
{
|
||||
"name": "fetch",
|
||||
"ok": true,
|
||||
"ms": 904,
|
||||
"detail": "python 3.12.15 731af898886c5f821890dc901eca3c651cca8e51fa7308c159d12a1194aeac91 -> /srv/aphrody-build/workspaces/vu-wsl/build/python"
|
||||
},
|
||||
{
|
||||
"name": "assemble",
|
||||
"ok": true,
|
||||
"ms": 1404,
|
||||
"detail": "/srv/aphrody-build/artifacts/vu/0.1.0-f0743b52"
|
||||
},
|
||||
{
|
||||
"name": "smoke",
|
||||
"ok": true,
|
||||
"ms": 1862
|
||||
}
|
||||
],
|
||||
"artifact": {
|
||||
"artifact": "/srv/aphrody-build/artifacts/vu/0.1.0-f0743b52",
|
||||
"files": 3494,
|
||||
"links": 1049
|
||||
},
|
||||
"smoke": {
|
||||
"ok": true,
|
||||
"checks": [
|
||||
{
|
||||
"name": "manifest",
|
||||
"ok": true,
|
||||
"detail": "every file matches",
|
||||
"ms": 1097
|
||||
},
|
||||
{
|
||||
"name": "version",
|
||||
"ok": true,
|
||||
"detail": "{\"schema\":\"aphrody.vu/1\",\"vu\":\"0.1.0\",\"uv\":\"uv 0.12.23 (46b84fd0b 2026-10-03 x86_64-unknown-linux-gnu)\",\"ruff\":\"ruff 0.16.10\",\"python\":\"3.12.15\",\"pyo3\":\"0.29.3\",\"target\":\"x86_64-unknown-linux-gnu\"}",
|
||||
"ms": 29
|
||||
},
|
||||
{
|
||||
"name": "uv sidecar",
|
||||
"ok": true,
|
||||
"detail": "uv 0.12.23 (46b84fd0b 2026-10-03 x86_64-unknown-linux-gnu)",
|
||||
"ms": 18
|
||||
},
|
||||
{
|
||||
"name": "ruff sidecar",
|
||||
"ok": true,
|
||||
"detail": "ruff 0.16.10; check exit 1",
|
||||
"ms": 19
|
||||
},
|
||||
{
|
||||
"name": "python parity",
|
||||
"ok": true,
|
||||
"detail": "identical sys.version, sys.prefix, sys.executable, sys.path and extension modules",
|
||||
"ms": 240
|
||||
},
|
||||
{
|
||||
"name": "python exit status and stdin",
|
||||
"ok": true,
|
||||
"detail": "exit 7, stdin result 42",
|
||||
"ms": 31
|
||||
},
|
||||
{
|
||||
"name": "sdk wheel through the sidecar uv and the embedded interpreter",
|
||||
"ok": true,
|
||||
"detail": "aphrody.aphrody_rust.cosine_similarity returned 1.0 inside vu python",
|
||||
"ms": 298
|
||||
},
|
||||
{
|
||||
"name": "glibc audit",
|
||||
"ok": true,
|
||||
"detail": "vu: 2.39, uv: 2.39, ruff: 2.39, libpython3.12.so.1.0: 2.17; highest 2.39; within 2.39",
|
||||
"ms": 46
|
||||
}
|
||||
],
|
||||
"wheel": {
|
||||
"path": "/srv/aphrody-build/artifacts/wsl/ir-wheel/dist/aphrody-0.1.0-cp311-abi3-linux_x86_64.whl",
|
||||
"sha256": "f90db352507324e5c55ea3c9e0d646e1ba2704f3102d946cce3ea791baf44eee"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -118,6 +118,8 @@ export async function mapLimit<T, R>(
|
||||
let next = 0;
|
||||
const worker = async (): Promise<void> => {
|
||||
for (let index = next++; index < items.length; index = next++) {
|
||||
// The workers are the concurrency: each one runs its items in sequence.
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
results[index] = await fn(items[index] as T);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -11,7 +11,13 @@ import {
|
||||
} from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { buildManifest, MANIFEST_PATH, type ManifestMeta, verifyManifest } from "./manifest.ts";
|
||||
import {
|
||||
buildManifest,
|
||||
isDerivedPath,
|
||||
MANIFEST_PATH,
|
||||
type ManifestMeta,
|
||||
verifyManifest,
|
||||
} from "./manifest.ts";
|
||||
|
||||
const meta: ManifestMeta = {
|
||||
schema: 1,
|
||||
@@ -77,6 +83,22 @@ describe("manifest", () => {
|
||||
expect(problems).toContain("link target: lib/libpython3.12.so");
|
||||
});
|
||||
|
||||
test("bytecode caches are derived data: never listed, never required, never flagged", async () => {
|
||||
expect(isDerivedPath("lib/python3.12/__pycache__/os.cpython-312.pyc")).toBe(true);
|
||||
expect(isDerivedPath("__pycache__/x.pyc")).toBe(true);
|
||||
expect(isDerivedPath("lib/python3.12/os.py")).toBe(false);
|
||||
expect(isDerivedPath("lib/my__pycache__/x")).toBe(false);
|
||||
const dir = prefix();
|
||||
mkdirSync(join(dir, "lib", "__pycache__"), { recursive: true });
|
||||
writeFileSync(join(dir, "lib", "__pycache__", "a.cpython-312.pyc"), "before");
|
||||
const manifest = await buildManifest(dir, meta);
|
||||
expect(Object.keys(manifest.files).some((path) => path.includes("__pycache__"))).toBe(false);
|
||||
// CPython writes and rewrites caches while the runtime is used.
|
||||
writeFileSync(join(dir, "lib", "__pycache__", "a.cpython-312.pyc"), "after, longer");
|
||||
writeFileSync(join(dir, "lib", "__pycache__", "b.cpython-312.pyc"), "new");
|
||||
expect(await verifyManifest(dir, manifest)).toEqual([]);
|
||||
});
|
||||
|
||||
test("another schema or name is refused", async () => {
|
||||
const dir = prefix();
|
||||
const manifest = await buildManifest(dir, meta);
|
||||
|
||||
+12
-2
@@ -9,6 +9,14 @@ import { mapLimit, sha256File } from "./lib.ts";
|
||||
|
||||
export const MANIFEST_PATH = "share/vu/manifest.json";
|
||||
|
||||
/**
|
||||
* Bytecode caches are derived data: CPython writes them into the runtime when it runs, so they are neither listed
|
||||
* in the manifest nor checked by a consumer (the sources and the binaries they derive from are).
|
||||
*/
|
||||
export function isDerivedPath(path: string): boolean {
|
||||
return /(^|\/)__pycache__\//.test(path);
|
||||
}
|
||||
|
||||
export interface FileEntry {
|
||||
sha256: string;
|
||||
bytes: number;
|
||||
@@ -48,7 +56,7 @@ export async function buildManifest(prefix: string, meta: ManifestMeta): Promise
|
||||
walk(prefix, prefix, listing);
|
||||
const files: Record<string, FileEntry> = {};
|
||||
const entries = await mapLimit(
|
||||
listing.files.filter((path) => path !== MANIFEST_PATH),
|
||||
listing.files.filter((path) => path !== MANIFEST_PATH && !isDerivedPath(path)),
|
||||
32,
|
||||
async (path) => {
|
||||
const stat = lstatSync(join(prefix, path));
|
||||
@@ -76,7 +84,9 @@ export async function verifyManifest(prefix: string, manifest?: Manifest): Promi
|
||||
return ["unsupported manifest schema or name"];
|
||||
const listing = { files: [] as string[], links: [] as string[] };
|
||||
walk(prefix, prefix, listing);
|
||||
const present = new Set(listing.files.filter((path) => path !== MANIFEST_PATH));
|
||||
const present = new Set(
|
||||
listing.files.filter((path) => path !== MANIFEST_PATH && !isDerivedPath(path)),
|
||||
);
|
||||
for (const [path, entry] of Object.entries(declared.files)) {
|
||||
if (!present.has(path)) {
|
||||
problems.push(`missing: ${path}`);
|
||||
|
||||
+7
-1
@@ -44,7 +44,13 @@ export function smokePlan(
|
||||
options: SmokeOptions,
|
||||
): { name: string; run: () => Promise<{ ok: boolean; detail: string }> }[] {
|
||||
const bin = (name: string): string => join(prefix, "bin", name);
|
||||
const environment = { VU_RUNTIME: prefix, UV_PYTHON_DOWNLOADS: "never", UV_NO_CONFIG: "1" };
|
||||
// The artifact must stay pristine while it is checked: no bytecode caches are written into it.
|
||||
const environment = {
|
||||
VU_RUNTIME: prefix,
|
||||
UV_PYTHON_DOWNLOADS: "never",
|
||||
UV_NO_CONFIG: "1",
|
||||
PYTHONDONTWRITEBYTECODE: "1",
|
||||
};
|
||||
return [
|
||||
{
|
||||
name: "manifest",
|
||||
|
||||
Reference in new issue
Block a user