mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-22 13:18:09 +00:00
fix docker store seed target packages (#91547)
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
// Lists production store packages from lockfile data.
|
||||
// Lists current-target production packages for Docker's offline prune store seed.
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { parse } from "yaml";
|
||||
@@ -6,6 +6,11 @@ import { parse } from "yaml";
|
||||
const parsed = JSON.parse(fs.readFileSync(0, "utf8"));
|
||||
const roots = Array.isArray(parsed) ? parsed : [parsed];
|
||||
const specs = new Set();
|
||||
const target = {
|
||||
cpu: process.arch,
|
||||
libc: detectLibc(),
|
||||
os: process.platform,
|
||||
};
|
||||
|
||||
function packageSpec(name, version) {
|
||||
if (!name || !version || typeof version !== "string") {
|
||||
@@ -22,26 +27,72 @@ function packageSpec(name, version) {
|
||||
return `${name}@${normalizedVersion}`;
|
||||
}
|
||||
|
||||
function packageSpecFromLockfileKey(key) {
|
||||
function detectLibc() {
|
||||
if (process.platform !== "linux") {
|
||||
return undefined;
|
||||
}
|
||||
const report = process.report?.getReport?.();
|
||||
return report?.header?.glibcVersionRuntime ? "glibc" : "musl";
|
||||
}
|
||||
|
||||
function matchesTargetSelector(selector, value) {
|
||||
if (!Array.isArray(selector) || !value) {
|
||||
return true;
|
||||
}
|
||||
const blocked = selector.some((entry) => entry === `!${value}`);
|
||||
if (blocked) {
|
||||
return false;
|
||||
}
|
||||
const allowed = selector.filter((entry) => typeof entry === "string" && !entry.startsWith("!"));
|
||||
return allowed.length === 0 || allowed.includes(value);
|
||||
}
|
||||
|
||||
function packageEntryForSpec(lockfile, spec) {
|
||||
return lockfile?.packages?.[spec] ?? lockfile?.packages?.[`/${spec}`];
|
||||
}
|
||||
|
||||
function normalizeLockfilePackageKey(key) {
|
||||
if (typeof key !== "string") {
|
||||
return undefined;
|
||||
}
|
||||
const normalizedKey = (key.startsWith("/") ? key.slice(1) : key).replace(/\(.+\)$/, "");
|
||||
const separator = normalizedKey.lastIndexOf("@");
|
||||
if (separator <= 0) {
|
||||
return undefined;
|
||||
}
|
||||
return packageSpec(normalizedKey.slice(0, separator), normalizedKey.slice(separator + 1));
|
||||
return (key.startsWith("/") ? key.slice(1) : key).replace(/\(.+\)$/, "");
|
||||
}
|
||||
|
||||
function visitListNode(node) {
|
||||
function snapshotForSpec(lockfile, spec) {
|
||||
const snapshots = lockfile?.snapshots;
|
||||
if (!snapshots) {
|
||||
return undefined;
|
||||
}
|
||||
return (
|
||||
snapshots[spec] ??
|
||||
snapshots[`/${spec}`] ??
|
||||
Object.entries(snapshots).find(([key]) => normalizeLockfilePackageKey(key) === spec)?.[1]
|
||||
);
|
||||
}
|
||||
|
||||
function packageSupportsTarget(lockfile, spec) {
|
||||
const entry = packageEntryForSpec(lockfile, spec);
|
||||
return (
|
||||
matchesTargetSelector(entry?.os, target.os) &&
|
||||
matchesTargetSelector(entry?.cpu, target.cpu) &&
|
||||
matchesTargetSelector(entry?.libc, target.libc)
|
||||
);
|
||||
}
|
||||
|
||||
function addSpec(lockfile, spec) {
|
||||
if (spec && packageSupportsTarget(lockfile, spec)) {
|
||||
specs.add(spec);
|
||||
}
|
||||
}
|
||||
|
||||
function visitListNode(lockfile, node) {
|
||||
for (const dep of Object.values(node.dependencies ?? {})) {
|
||||
const name = dep.from || dep.name;
|
||||
const spec = packageSpec(name, dep.version);
|
||||
if (spec && dep.resolved?.startsWith("https://registry.npmjs.org/")) {
|
||||
specs.add(spec);
|
||||
addSpec(lockfile, spec);
|
||||
}
|
||||
visitListNode(dep);
|
||||
visitListNode(lockfile, dep);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,15 +104,6 @@ function readLockfile() {
|
||||
return parse(fs.readFileSync(lockfilePath, "utf8"));
|
||||
}
|
||||
|
||||
function addLockfilePackages(lockfile) {
|
||||
for (const key of Object.keys(lockfile?.packages ?? {})) {
|
||||
const spec = packageSpecFromLockfileKey(key);
|
||||
if (spec) {
|
||||
specs.add(spec);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addSnapshotClosure(lockfile) {
|
||||
const snapshots = lockfile?.snapshots;
|
||||
const packages = lockfile?.packages;
|
||||
@@ -76,26 +118,36 @@ function addSnapshotClosure(lockfile) {
|
||||
continue;
|
||||
}
|
||||
visited.add(spec);
|
||||
const snapshot = snapshots[spec];
|
||||
const snapshot = snapshotForSpec(lockfile, spec);
|
||||
if (!snapshot) {
|
||||
continue;
|
||||
}
|
||||
for (const [name, version] of Object.entries(snapshot.dependencies ?? {})) {
|
||||
const addDependencySpec = (name, version) => {
|
||||
const depSpec = packageSpec(name, typeof version === "string" ? version : version?.version);
|
||||
if (!depSpec || !packages[depSpec] || specs.has(depSpec)) {
|
||||
continue;
|
||||
if (
|
||||
!depSpec ||
|
||||
!packages[depSpec] ||
|
||||
specs.has(depSpec) ||
|
||||
!packageSupportsTarget(lockfile, depSpec)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
specs.add(depSpec);
|
||||
pending.push(depSpec);
|
||||
};
|
||||
for (const [name, version] of Object.entries(snapshot.dependencies ?? {})) {
|
||||
addDependencySpec(name, version);
|
||||
}
|
||||
for (const [name, version] of Object.entries(snapshot.optionalDependencies ?? {})) {
|
||||
addDependencySpec(name, version);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const root of roots) {
|
||||
visitListNode(root);
|
||||
}
|
||||
const lockfile = readLockfile();
|
||||
for (const root of roots) {
|
||||
visitListNode(lockfile, root);
|
||||
}
|
||||
addSnapshotClosure(lockfile);
|
||||
addLockfilePackages(lockfile);
|
||||
|
||||
process.stdout.write([...specs].toSorted((a, b) => a.localeCompare(b)).join("\n"));
|
||||
|
||||
@@ -101,7 +101,66 @@ describe("list-prod-store-packages", () => {
|
||||
expect(result.stdout).toBe("source-map-support@0.5.21\nsource-map@0.6.1");
|
||||
});
|
||||
|
||||
it("adds lockfile packages missing from pnpm list output", () => {
|
||||
it("adds target optional dependencies from peer-resolved lockfile snapshots", () => {
|
||||
const cwd = makeTempRepoRoot(tempDirs, "openclaw-prod-store-packages-");
|
||||
const platformPackages = [
|
||||
["darwin", "arm64"],
|
||||
["darwin", "x64"],
|
||||
["linux", "arm64"],
|
||||
["linux", "x64"],
|
||||
["win32", "arm64"],
|
||||
["win32", "x64"],
|
||||
] as const;
|
||||
writeFileSync(
|
||||
join(cwd, "pnpm-lock.yaml"),
|
||||
[
|
||||
"lockfileVersion: '10.0'",
|
||||
"",
|
||||
"packages:",
|
||||
" native-wrapper@1.0.0:",
|
||||
" resolution: {integrity: sha512-test}",
|
||||
...platformPackages.flatMap(([os, cpu]) => [
|
||||
` native-wrapper-${os}-${cpu}@1.0.0:`,
|
||||
" resolution: {integrity: sha512-test}",
|
||||
` cpu: [${cpu}]`,
|
||||
` os: [${os}]`,
|
||||
]),
|
||||
"",
|
||||
"snapshots:",
|
||||
" native-wrapper@1.0.0(peer@1.0.0):",
|
||||
" optionalDependencies:",
|
||||
...platformPackages.map(([os, cpu]) => ` native-wrapper-${os}-${cpu}: 1.0.0`),
|
||||
...platformPackages.flatMap(([os, cpu]) => [
|
||||
` native-wrapper-${os}-${cpu}@1.0.0:`,
|
||||
" optional: true",
|
||||
]),
|
||||
"",
|
||||
].join("\n"),
|
||||
);
|
||||
const result = runListProdStorePackages(
|
||||
{
|
||||
dependencies: {
|
||||
nativeWrapper: {
|
||||
from: "native-wrapper",
|
||||
resolved: "https://registry.npmjs.org/native-wrapper/-/native-wrapper-1.0.0.tgz",
|
||||
version: "1.0.0(peer@1.0.0)",
|
||||
},
|
||||
},
|
||||
},
|
||||
cwd,
|
||||
);
|
||||
|
||||
expect(result.status).toBe(0);
|
||||
const expectedPlatformPackage = [`native-wrapper-${process.platform}-${process.arch}@1.0.0`];
|
||||
const supportedPlatformPackage = ["linux", "darwin", "win32"].includes(process.platform)
|
||||
? expectedPlatformPackage
|
||||
: [];
|
||||
expect(result.stdout.split("\n").filter(Boolean)).toEqual(
|
||||
["native-wrapper@1.0.0", ...supportedPlatformPackage].toSorted((a, b) => a.localeCompare(b)),
|
||||
);
|
||||
});
|
||||
|
||||
it("does not add unrelated lockfile packages missing from pnpm list output", () => {
|
||||
const cwd = makeTempRepoRoot(tempDirs, "openclaw-prod-store-packages-");
|
||||
writeFileSync(
|
||||
join(cwd, "pnpm-lock.yaml"),
|
||||
@@ -119,6 +178,68 @@ describe("list-prod-store-packages", () => {
|
||||
const result = runListProdStorePackages({ dependencies: {} }, cwd);
|
||||
|
||||
expect(result.status).toBe(0);
|
||||
expect(result.stdout).toBe("recma-jsx@1.0.1");
|
||||
expect(result.stdout).toBe("");
|
||||
});
|
||||
|
||||
it("only adds optional platform packages matching the current target", () => {
|
||||
const cwd = makeTempRepoRoot(tempDirs, "openclaw-prod-store-packages-");
|
||||
const platformPackages = [
|
||||
["darwin", "arm64"],
|
||||
["darwin", "x64"],
|
||||
["linux", "arm64"],
|
||||
["linux", "x64"],
|
||||
["win32", "arm64"],
|
||||
["win32", "x64"],
|
||||
] as const;
|
||||
const expectedPlatformPackage = platformPackages
|
||||
.map(([os, cpu]) => `@zed-industries/codex-acp-${os}-${cpu}@0.15.0`)
|
||||
.find(
|
||||
(spec) => spec === `@zed-industries/codex-acp-${process.platform}-${process.arch}@0.15.0`,
|
||||
);
|
||||
writeFileSync(
|
||||
join(cwd, "pnpm-lock.yaml"),
|
||||
[
|
||||
"lockfileVersion: '10.0'",
|
||||
"",
|
||||
"packages:",
|
||||
" '@zed-industries/codex-acp@0.15.0':",
|
||||
" resolution: {integrity: sha512-test}",
|
||||
...platformPackages.flatMap(([os, cpu]) => [
|
||||
` '@zed-industries/codex-acp-${os}-${cpu}@0.15.0':`,
|
||||
" resolution: {integrity: sha512-test}",
|
||||
` cpu: [${cpu}]`,
|
||||
` os: [${os}]`,
|
||||
]),
|
||||
"",
|
||||
"snapshots:",
|
||||
" '@zed-industries/codex-acp@0.15.0':",
|
||||
" optionalDependencies:",
|
||||
...platformPackages.map(
|
||||
([os, cpu]) => ` '@zed-industries/codex-acp-${os}-${cpu}': 0.15.0`,
|
||||
),
|
||||
...platformPackages.flatMap(([os, cpu]) => [
|
||||
` '@zed-industries/codex-acp-${os}-${cpu}@0.15.0':`,
|
||||
" optional: true",
|
||||
]),
|
||||
"",
|
||||
].join("\n"),
|
||||
);
|
||||
const result = runListProdStorePackages(
|
||||
{
|
||||
dependencies: {
|
||||
codexAcp: {
|
||||
from: "@zed-industries/codex-acp",
|
||||
resolved: "https://registry.npmjs.org/@zed-industries/codex-acp/-/codex-acp-0.15.0.tgz",
|
||||
version: "0.15.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
cwd,
|
||||
);
|
||||
|
||||
expect(result.status).toBe(0);
|
||||
expect(result.stdout.split("\n").filter(Boolean)).toEqual(
|
||||
[expectedPlatformPackage, "@zed-industries/codex-acp@0.15.0"].filter(Boolean),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user