mirror of
https://github.com/astral-sh/setup-uv.git
synced 2026-03-15 09:35:17 +00:00
Speed up version client by partial response reads
This commit is contained in:
@@ -37,10 +37,13 @@ const mockGetLatestVersionFromNdjson = jest.fn<any>();
|
|||||||
const mockGetAllVersionsFromNdjson = jest.fn<any>();
|
const mockGetAllVersionsFromNdjson = jest.fn<any>();
|
||||||
// biome-ignore lint/suspicious/noExplicitAny: Mock requires flexible typing in tests.
|
// biome-ignore lint/suspicious/noExplicitAny: Mock requires flexible typing in tests.
|
||||||
const mockGetArtifactFromNdjson = jest.fn<any>();
|
const mockGetArtifactFromNdjson = jest.fn<any>();
|
||||||
|
// biome-ignore lint/suspicious/noExplicitAny: Mock requires flexible typing in tests.
|
||||||
|
const mockGetHighestSatisfyingVersionFromNdjson = jest.fn<any>();
|
||||||
|
|
||||||
jest.unstable_mockModule("../../src/download/versions-client", () => ({
|
jest.unstable_mockModule("../../src/download/versions-client", () => ({
|
||||||
getAllVersions: mockGetAllVersionsFromNdjson,
|
getAllVersions: mockGetAllVersionsFromNdjson,
|
||||||
getArtifact: mockGetArtifactFromNdjson,
|
getArtifact: mockGetArtifactFromNdjson,
|
||||||
|
getHighestSatisfyingVersion: mockGetHighestSatisfyingVersionFromNdjson,
|
||||||
getLatestVersion: mockGetLatestVersionFromNdjson,
|
getLatestVersion: mockGetLatestVersionFromNdjson,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -81,6 +84,7 @@ describe("download-version", () => {
|
|||||||
mockGetLatestVersionFromNdjson.mockReset();
|
mockGetLatestVersionFromNdjson.mockReset();
|
||||||
mockGetAllVersionsFromNdjson.mockReset();
|
mockGetAllVersionsFromNdjson.mockReset();
|
||||||
mockGetArtifactFromNdjson.mockReset();
|
mockGetArtifactFromNdjson.mockReset();
|
||||||
|
mockGetHighestSatisfyingVersionFromNdjson.mockReset();
|
||||||
mockGetAllManifestVersions.mockReset();
|
mockGetAllManifestVersions.mockReset();
|
||||||
mockGetLatestVersionInManifest.mockReset();
|
mockGetLatestVersionInManifest.mockReset();
|
||||||
mockGetManifestArtifact.mockReset();
|
mockGetManifestArtifact.mockReset();
|
||||||
@@ -102,13 +106,26 @@ describe("download-version", () => {
|
|||||||
expect(mockGetLatestVersionFromNdjson).toHaveBeenCalledTimes(1);
|
expect(mockGetLatestVersionFromNdjson).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("uses astral-sh/versions to resolve available versions", async () => {
|
it("streams astral-sh/versions to resolve the highest matching version", async () => {
|
||||||
mockGetAllVersionsFromNdjson.mockResolvedValue(["0.9.26", "0.9.25"]);
|
mockGetHighestSatisfyingVersionFromNdjson.mockResolvedValue("0.9.26");
|
||||||
|
|
||||||
const version = await resolveVersion("^0.9.0", undefined);
|
const version = await resolveVersion("^0.9.0", undefined);
|
||||||
|
|
||||||
expect(version).toBe("0.9.26");
|
expect(version).toBe("0.9.26");
|
||||||
|
expect(mockGetHighestSatisfyingVersionFromNdjson).toHaveBeenCalledWith(
|
||||||
|
"^0.9.0",
|
||||||
|
);
|
||||||
|
expect(mockGetAllVersionsFromNdjson).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("still loads all versions when resolving the lowest matching version", async () => {
|
||||||
|
mockGetAllVersionsFromNdjson.mockResolvedValue(["0.9.26", "0.9.25"]);
|
||||||
|
|
||||||
|
const version = await resolveVersion("^0.9.0", undefined, "lowest");
|
||||||
|
|
||||||
|
expect(version).toBe("0.9.25");
|
||||||
expect(mockGetAllVersionsFromNdjson).toHaveBeenCalledTimes(1);
|
expect(mockGetAllVersionsFromNdjson).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockGetHighestSatisfyingVersionFromNdjson).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("does not fall back when astral-sh/versions fails", async () => {
|
it("does not fall back when astral-sh/versions fails", async () => {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ const {
|
|||||||
fetchVersionData,
|
fetchVersionData,
|
||||||
getAllVersions,
|
getAllVersions,
|
||||||
getArtifact,
|
getArtifact,
|
||||||
|
getHighestSatisfyingVersion,
|
||||||
getLatestVersion,
|
getLatestVersion,
|
||||||
parseVersionData,
|
parseVersionData,
|
||||||
} = await import("../../src/download/versions-client");
|
} = await import("../../src/download/versions-client");
|
||||||
@@ -21,13 +22,28 @@ const sampleNdjsonResponse = `{"version":"0.9.26","artifacts":[{"platform":"aarc
|
|||||||
|
|
||||||
const multiVariantNdjsonResponse = `{"version":"0.9.26","artifacts":[{"platform":"aarch64-apple-darwin","variant":"python-managed","url":"https://github.com/astral-sh/uv/releases/download/0.9.26/uv-aarch64-apple-darwin-managed.tar.gz","archive_format":"tar.gz","sha256":"managed-checksum"},{"platform":"aarch64-apple-darwin","variant":"default","url":"https://github.com/astral-sh/uv/releases/download/0.9.26/uv-aarch64-apple-darwin.zip","archive_format":"zip","sha256":"default-checksum"}]}`;
|
const multiVariantNdjsonResponse = `{"version":"0.9.26","artifacts":[{"platform":"aarch64-apple-darwin","variant":"python-managed","url":"https://github.com/astral-sh/uv/releases/download/0.9.26/uv-aarch64-apple-darwin-managed.tar.gz","archive_format":"tar.gz","sha256":"managed-checksum"},{"platform":"aarch64-apple-darwin","variant":"default","url":"https://github.com/astral-sh/uv/releases/download/0.9.26/uv-aarch64-apple-darwin.zip","archive_format":"zip","sha256":"default-checksum"}]}`;
|
||||||
|
|
||||||
|
function createMockStream(chunks: string[]): ReadableStream<Uint8Array> {
|
||||||
|
const encoder = new TextEncoder();
|
||||||
|
|
||||||
|
return new ReadableStream<Uint8Array>({
|
||||||
|
start(controller) {
|
||||||
|
for (const chunk of chunks) {
|
||||||
|
controller.enqueue(encoder.encode(chunk));
|
||||||
|
}
|
||||||
|
controller.close();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function createMockResponse(
|
function createMockResponse(
|
||||||
ok: boolean,
|
ok: boolean,
|
||||||
status: number,
|
status: number,
|
||||||
statusText: string,
|
statusText: string,
|
||||||
data: string,
|
data: string,
|
||||||
|
chunks: string[] = [data],
|
||||||
) {
|
) {
|
||||||
return {
|
return {
|
||||||
|
body: createMockStream(chunks),
|
||||||
ok,
|
ok,
|
||||||
status,
|
status,
|
||||||
statusText,
|
statusText,
|
||||||
@@ -86,6 +102,22 @@ describe("versions-client", () => {
|
|||||||
|
|
||||||
expect(latest).toBe("0.9.26");
|
expect(latest).toBe("0.9.26");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should stop after the first record when resolving latest", async () => {
|
||||||
|
mockFetch.mockResolvedValue(
|
||||||
|
createMockResponse(
|
||||||
|
true,
|
||||||
|
200,
|
||||||
|
"OK",
|
||||||
|
`${sampleNdjsonResponse}\n{"version":`,
|
||||||
|
[`${sampleNdjsonResponse.split("\n")[0]}\n`, '{"version":'],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
const latest = await getLatestVersion();
|
||||||
|
|
||||||
|
expect(latest).toBe("0.9.26");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("getAllVersions", () => {
|
describe("getAllVersions", () => {
|
||||||
@@ -100,6 +132,24 @@ describe("versions-client", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("getHighestSatisfyingVersion", () => {
|
||||||
|
it("should return the first matching version from the stream", async () => {
|
||||||
|
mockFetch.mockResolvedValue(
|
||||||
|
createMockResponse(
|
||||||
|
true,
|
||||||
|
200,
|
||||||
|
"OK",
|
||||||
|
`${sampleNdjsonResponse}\n{"version":`,
|
||||||
|
[`${sampleNdjsonResponse.split("\n")[0]}\n`, '{"version":'],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
const version = await getHighestSatisfyingVersion("^0.9.0");
|
||||||
|
|
||||||
|
expect(version).toBe("0.9.26");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("getArtifact", () => {
|
describe("getArtifact", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mockFetch.mockResolvedValue(
|
mockFetch.mockResolvedValue(
|
||||||
@@ -118,6 +168,27 @@ describe("versions-client", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should stop once the requested version is found", async () => {
|
||||||
|
mockFetch.mockResolvedValue(
|
||||||
|
createMockResponse(
|
||||||
|
true,
|
||||||
|
200,
|
||||||
|
"OK",
|
||||||
|
`${sampleNdjsonResponse}\n{"version":`,
|
||||||
|
[`${sampleNdjsonResponse.split("\n")[0]}\n`, '{"version":'],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
const artifact = await getArtifact("0.9.26", "aarch64", "apple-darwin");
|
||||||
|
|
||||||
|
expect(artifact).toEqual({
|
||||||
|
archiveFormat: "tar.gz",
|
||||||
|
sha256:
|
||||||
|
"fcf0a9ea6599c6ae28a4c854ac6da76f2c889354d7c36ce136ef071f7ab9721f",
|
||||||
|
url: "https://github.com/astral-sh/uv/releases/download/0.9.26/uv-aarch64-apple-darwin.tar.gz",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("should find windows artifact", async () => {
|
it("should find windows artifact", async () => {
|
||||||
const artifact = await getArtifact("0.9.26", "x86_64", "pc-windows-msvc");
|
const artifact = await getArtifact("0.9.26", "x86_64", "pc-windows-msvc");
|
||||||
|
|
||||||
|
|||||||
286
dist/setup/index.cjs
generated
vendored
286
dist/setup/index.cjs
generated
vendored
@@ -20742,7 +20742,7 @@ var require_satisfies = __commonJS({
|
|||||||
"node_modules/@actions/cache/node_modules/semver/functions/satisfies.js"(exports2, module2) {
|
"node_modules/@actions/cache/node_modules/semver/functions/satisfies.js"(exports2, module2) {
|
||||||
"use strict";
|
"use strict";
|
||||||
var Range = require_range();
|
var Range = require_range();
|
||||||
var satisfies4 = (version4, range2, options) => {
|
var satisfies6 = (version4, range2, options) => {
|
||||||
try {
|
try {
|
||||||
range2 = new Range(range2, options);
|
range2 = new Range(range2, options);
|
||||||
} catch (er) {
|
} catch (er) {
|
||||||
@@ -20750,7 +20750,7 @@ var require_satisfies = __commonJS({
|
|||||||
}
|
}
|
||||||
return range2.test(version4);
|
return range2.test(version4);
|
||||||
};
|
};
|
||||||
module2.exports = satisfies4;
|
module2.exports = satisfies6;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -20905,7 +20905,7 @@ var require_outside = __commonJS({
|
|||||||
var Comparator = require_comparator();
|
var Comparator = require_comparator();
|
||||||
var { ANY } = Comparator;
|
var { ANY } = Comparator;
|
||||||
var Range = require_range();
|
var Range = require_range();
|
||||||
var satisfies4 = require_satisfies();
|
var satisfies6 = require_satisfies();
|
||||||
var gt3 = require_gt();
|
var gt3 = require_gt();
|
||||||
var lt = require_lt();
|
var lt = require_lt();
|
||||||
var lte = require_lte();
|
var lte = require_lte();
|
||||||
@@ -20932,7 +20932,7 @@ var require_outside = __commonJS({
|
|||||||
default:
|
default:
|
||||||
throw new TypeError('Must provide a hilo val of "<" or ">"');
|
throw new TypeError('Must provide a hilo val of "<" or ">"');
|
||||||
}
|
}
|
||||||
if (satisfies4(version4, range2, options)) {
|
if (satisfies6(version4, range2, options)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (let i = 0; i < range2.set.length; ++i) {
|
for (let i = 0; i < range2.set.length; ++i) {
|
||||||
@@ -21004,7 +21004,7 @@ var require_intersects = __commonJS({
|
|||||||
var require_simplify = __commonJS({
|
var require_simplify = __commonJS({
|
||||||
"node_modules/@actions/cache/node_modules/semver/ranges/simplify.js"(exports2, module2) {
|
"node_modules/@actions/cache/node_modules/semver/ranges/simplify.js"(exports2, module2) {
|
||||||
"use strict";
|
"use strict";
|
||||||
var satisfies4 = require_satisfies();
|
var satisfies6 = require_satisfies();
|
||||||
var compare = require_compare();
|
var compare = require_compare();
|
||||||
module2.exports = (versions, range2, options) => {
|
module2.exports = (versions, range2, options) => {
|
||||||
const set = [];
|
const set = [];
|
||||||
@@ -21012,7 +21012,7 @@ var require_simplify = __commonJS({
|
|||||||
let prev = null;
|
let prev = null;
|
||||||
const v = versions.sort((a, b) => compare(a, b, options));
|
const v = versions.sort((a, b) => compare(a, b, options));
|
||||||
for (const version4 of v) {
|
for (const version4 of v) {
|
||||||
const included = satisfies4(version4, range2, options);
|
const included = satisfies6(version4, range2, options);
|
||||||
if (included) {
|
if (included) {
|
||||||
prev = version4;
|
prev = version4;
|
||||||
if (!first) {
|
if (!first) {
|
||||||
@@ -21057,7 +21057,7 @@ var require_subset = __commonJS({
|
|||||||
var Range = require_range();
|
var Range = require_range();
|
||||||
var Comparator = require_comparator();
|
var Comparator = require_comparator();
|
||||||
var { ANY } = Comparator;
|
var { ANY } = Comparator;
|
||||||
var satisfies4 = require_satisfies();
|
var satisfies6 = require_satisfies();
|
||||||
var compare = require_compare();
|
var compare = require_compare();
|
||||||
var subset = (sub, dom, options = {}) => {
|
var subset = (sub, dom, options = {}) => {
|
||||||
if (sub === dom) {
|
if (sub === dom) {
|
||||||
@@ -21126,14 +21126,14 @@ var require_subset = __commonJS({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const eq of eqSet) {
|
for (const eq of eqSet) {
|
||||||
if (gt3 && !satisfies4(eq, String(gt3), options)) {
|
if (gt3 && !satisfies6(eq, String(gt3), options)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (lt && !satisfies4(eq, String(lt), options)) {
|
if (lt && !satisfies6(eq, String(lt), options)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
for (const c of dom) {
|
for (const c of dom) {
|
||||||
if (!satisfies4(eq, String(c), options)) {
|
if (!satisfies6(eq, String(c), options)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -21160,7 +21160,7 @@ var require_subset = __commonJS({
|
|||||||
if (higher === c && higher !== gt3) {
|
if (higher === c && higher !== gt3) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (gt3.operator === ">=" && !satisfies4(gt3.semver, String(c), options)) {
|
} else if (gt3.operator === ">=" && !satisfies6(gt3.semver, String(c), options)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -21175,7 +21175,7 @@ var require_subset = __commonJS({
|
|||||||
if (lower === c && lower !== lt) {
|
if (lower === c && lower !== lt) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (lt.operator === "<=" && !satisfies4(lt.semver, String(c), options)) {
|
} else if (lt.operator === "<=" && !satisfies6(lt.semver, String(c), options)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -21245,7 +21245,7 @@ var require_semver2 = __commonJS({
|
|||||||
var coerce = require_coerce();
|
var coerce = require_coerce();
|
||||||
var Comparator = require_comparator();
|
var Comparator = require_comparator();
|
||||||
var Range = require_range();
|
var Range = require_range();
|
||||||
var satisfies4 = require_satisfies();
|
var satisfies6 = require_satisfies();
|
||||||
var toComparators = require_to_comparators();
|
var toComparators = require_to_comparators();
|
||||||
var maxSatisfying3 = require_max_satisfying();
|
var maxSatisfying3 = require_max_satisfying();
|
||||||
var minSatisfying4 = require_min_satisfying();
|
var minSatisfying4 = require_min_satisfying();
|
||||||
@@ -21283,7 +21283,7 @@ var require_semver2 = __commonJS({
|
|||||||
coerce,
|
coerce,
|
||||||
Comparator,
|
Comparator,
|
||||||
Range,
|
Range,
|
||||||
satisfies: satisfies4,
|
satisfies: satisfies6,
|
||||||
toComparators,
|
toComparators,
|
||||||
maxSatisfying: maxSatisfying3,
|
maxSatisfying: maxSatisfying3,
|
||||||
minSatisfying: minSatisfying4,
|
minSatisfying: minSatisfying4,
|
||||||
@@ -28352,7 +28352,7 @@ var require_satisfies2 = __commonJS({
|
|||||||
"node_modules/@actions/tool-cache/node_modules/semver/functions/satisfies.js"(exports2, module2) {
|
"node_modules/@actions/tool-cache/node_modules/semver/functions/satisfies.js"(exports2, module2) {
|
||||||
"use strict";
|
"use strict";
|
||||||
var Range = require_range2();
|
var Range = require_range2();
|
||||||
var satisfies4 = (version4, range2, options) => {
|
var satisfies6 = (version4, range2, options) => {
|
||||||
try {
|
try {
|
||||||
range2 = new Range(range2, options);
|
range2 = new Range(range2, options);
|
||||||
} catch (er) {
|
} catch (er) {
|
||||||
@@ -28360,7 +28360,7 @@ var require_satisfies2 = __commonJS({
|
|||||||
}
|
}
|
||||||
return range2.test(version4);
|
return range2.test(version4);
|
||||||
};
|
};
|
||||||
module2.exports = satisfies4;
|
module2.exports = satisfies6;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -28515,7 +28515,7 @@ var require_outside2 = __commonJS({
|
|||||||
var Comparator = require_comparator2();
|
var Comparator = require_comparator2();
|
||||||
var { ANY } = Comparator;
|
var { ANY } = Comparator;
|
||||||
var Range = require_range2();
|
var Range = require_range2();
|
||||||
var satisfies4 = require_satisfies2();
|
var satisfies6 = require_satisfies2();
|
||||||
var gt3 = require_gt2();
|
var gt3 = require_gt2();
|
||||||
var lt = require_lt2();
|
var lt = require_lt2();
|
||||||
var lte = require_lte2();
|
var lte = require_lte2();
|
||||||
@@ -28542,7 +28542,7 @@ var require_outside2 = __commonJS({
|
|||||||
default:
|
default:
|
||||||
throw new TypeError('Must provide a hilo val of "<" or ">"');
|
throw new TypeError('Must provide a hilo val of "<" or ">"');
|
||||||
}
|
}
|
||||||
if (satisfies4(version4, range2, options)) {
|
if (satisfies6(version4, range2, options)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (let i = 0; i < range2.set.length; ++i) {
|
for (let i = 0; i < range2.set.length; ++i) {
|
||||||
@@ -28614,7 +28614,7 @@ var require_intersects2 = __commonJS({
|
|||||||
var require_simplify2 = __commonJS({
|
var require_simplify2 = __commonJS({
|
||||||
"node_modules/@actions/tool-cache/node_modules/semver/ranges/simplify.js"(exports2, module2) {
|
"node_modules/@actions/tool-cache/node_modules/semver/ranges/simplify.js"(exports2, module2) {
|
||||||
"use strict";
|
"use strict";
|
||||||
var satisfies4 = require_satisfies2();
|
var satisfies6 = require_satisfies2();
|
||||||
var compare = require_compare2();
|
var compare = require_compare2();
|
||||||
module2.exports = (versions, range2, options) => {
|
module2.exports = (versions, range2, options) => {
|
||||||
const set = [];
|
const set = [];
|
||||||
@@ -28622,7 +28622,7 @@ var require_simplify2 = __commonJS({
|
|||||||
let prev = null;
|
let prev = null;
|
||||||
const v = versions.sort((a, b) => compare(a, b, options));
|
const v = versions.sort((a, b) => compare(a, b, options));
|
||||||
for (const version4 of v) {
|
for (const version4 of v) {
|
||||||
const included = satisfies4(version4, range2, options);
|
const included = satisfies6(version4, range2, options);
|
||||||
if (included) {
|
if (included) {
|
||||||
prev = version4;
|
prev = version4;
|
||||||
if (!first) {
|
if (!first) {
|
||||||
@@ -28667,7 +28667,7 @@ var require_subset2 = __commonJS({
|
|||||||
var Range = require_range2();
|
var Range = require_range2();
|
||||||
var Comparator = require_comparator2();
|
var Comparator = require_comparator2();
|
||||||
var { ANY } = Comparator;
|
var { ANY } = Comparator;
|
||||||
var satisfies4 = require_satisfies2();
|
var satisfies6 = require_satisfies2();
|
||||||
var compare = require_compare2();
|
var compare = require_compare2();
|
||||||
var subset = (sub, dom, options = {}) => {
|
var subset = (sub, dom, options = {}) => {
|
||||||
if (sub === dom) {
|
if (sub === dom) {
|
||||||
@@ -28736,14 +28736,14 @@ var require_subset2 = __commonJS({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const eq of eqSet) {
|
for (const eq of eqSet) {
|
||||||
if (gt3 && !satisfies4(eq, String(gt3), options)) {
|
if (gt3 && !satisfies6(eq, String(gt3), options)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (lt && !satisfies4(eq, String(lt), options)) {
|
if (lt && !satisfies6(eq, String(lt), options)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
for (const c of dom) {
|
for (const c of dom) {
|
||||||
if (!satisfies4(eq, String(c), options)) {
|
if (!satisfies6(eq, String(c), options)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -28770,7 +28770,7 @@ var require_subset2 = __commonJS({
|
|||||||
if (higher === c && higher !== gt3) {
|
if (higher === c && higher !== gt3) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (gt3.operator === ">=" && !satisfies4(gt3.semver, String(c), options)) {
|
} else if (gt3.operator === ">=" && !satisfies6(gt3.semver, String(c), options)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -28785,7 +28785,7 @@ var require_subset2 = __commonJS({
|
|||||||
if (lower === c && lower !== lt) {
|
if (lower === c && lower !== lt) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (lt.operator === "<=" && !satisfies4(lt.semver, String(c), options)) {
|
} else if (lt.operator === "<=" && !satisfies6(lt.semver, String(c), options)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -28855,7 +28855,7 @@ var require_semver4 = __commonJS({
|
|||||||
var coerce = require_coerce2();
|
var coerce = require_coerce2();
|
||||||
var Comparator = require_comparator2();
|
var Comparator = require_comparator2();
|
||||||
var Range = require_range2();
|
var Range = require_range2();
|
||||||
var satisfies4 = require_satisfies2();
|
var satisfies6 = require_satisfies2();
|
||||||
var toComparators = require_to_comparators2();
|
var toComparators = require_to_comparators2();
|
||||||
var maxSatisfying3 = require_max_satisfying2();
|
var maxSatisfying3 = require_max_satisfying2();
|
||||||
var minSatisfying4 = require_min_satisfying2();
|
var minSatisfying4 = require_min_satisfying2();
|
||||||
@@ -28893,7 +28893,7 @@ var require_semver4 = __commonJS({
|
|||||||
coerce,
|
coerce,
|
||||||
Comparator,
|
Comparator,
|
||||||
Range,
|
Range,
|
||||||
satisfies: satisfies4,
|
satisfies: satisfies6,
|
||||||
toComparators,
|
toComparators,
|
||||||
maxSatisfying: maxSatisfying3,
|
maxSatisfying: maxSatisfying3,
|
||||||
minSatisfying: minSatisfying4,
|
minSatisfying: minSatisfying4,
|
||||||
@@ -29229,7 +29229,7 @@ var require_specifier = __commonJS({
|
|||||||
module2.exports = {
|
module2.exports = {
|
||||||
RANGE_PATTERN,
|
RANGE_PATTERN,
|
||||||
parse: parse3,
|
parse: parse3,
|
||||||
satisfies: satisfies4,
|
satisfies: satisfies6,
|
||||||
filter,
|
filter,
|
||||||
validRange,
|
validRange,
|
||||||
maxSatisfying: maxSatisfying3,
|
maxSatisfying: maxSatisfying3,
|
||||||
@@ -29306,7 +29306,7 @@ var require_specifier = __commonJS({
|
|||||||
}, true);
|
}, true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function satisfies4(version4, specifier, options = {}) {
|
function satisfies6(version4, specifier, options = {}) {
|
||||||
const filtered = pick([version4], specifier, options);
|
const filtered = pick([version4], specifier, options);
|
||||||
return filtered.length === 1;
|
return filtered.length === 1;
|
||||||
}
|
}
|
||||||
@@ -29336,7 +29336,7 @@ var require_specifier = __commonJS({
|
|||||||
if (spec.epoch) {
|
if (spec.epoch) {
|
||||||
compatiblePrefix = spec.epoch + "!" + compatiblePrefix;
|
compatiblePrefix = spec.epoch + "!" + compatiblePrefix;
|
||||||
}
|
}
|
||||||
return satisfies4(version4, `>=${spec.version}, ==${compatiblePrefix}`, {
|
return satisfies6(version4, `>=${spec.version}, ==${compatiblePrefix}`, {
|
||||||
prereleases: spec.prereleases
|
prereleases: spec.prereleases
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -29527,7 +29527,7 @@ var require_pep440 = __commonJS({
|
|||||||
maxSatisfying: maxSatisfying3,
|
maxSatisfying: maxSatisfying3,
|
||||||
minSatisfying: minSatisfying4,
|
minSatisfying: minSatisfying4,
|
||||||
RANGE_PATTERN,
|
RANGE_PATTERN,
|
||||||
satisfies: satisfies4,
|
satisfies: satisfies6,
|
||||||
validRange
|
validRange
|
||||||
} = require_specifier();
|
} = require_specifier();
|
||||||
var { major, minor, patch, inc } = require_semantic();
|
var { major, minor, patch, inc } = require_semantic();
|
||||||
@@ -29554,7 +29554,7 @@ var require_pep440 = __commonJS({
|
|||||||
maxSatisfying: maxSatisfying3,
|
maxSatisfying: maxSatisfying3,
|
||||||
minSatisfying: minSatisfying4,
|
minSatisfying: minSatisfying4,
|
||||||
RANGE_PATTERN,
|
RANGE_PATTERN,
|
||||||
satisfies: satisfies4,
|
satisfies: satisfies6,
|
||||||
validRange,
|
validRange,
|
||||||
// semantic
|
// semantic
|
||||||
major,
|
major,
|
||||||
@@ -30183,13 +30183,13 @@ var require_semver5 = __commonJS({
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
rangeTmp = new Range(comp26.value, options);
|
rangeTmp = new Range(comp26.value, options);
|
||||||
return satisfies4(this.value, rangeTmp, options);
|
return satisfies6(this.value, rangeTmp, options);
|
||||||
} else if (comp26.operator === "") {
|
} else if (comp26.operator === "") {
|
||||||
if (comp26.value === "") {
|
if (comp26.value === "") {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
rangeTmp = new Range(this.value, options);
|
rangeTmp = new Range(this.value, options);
|
||||||
return satisfies4(comp26.semver, rangeTmp, options);
|
return satisfies6(comp26.semver, rangeTmp, options);
|
||||||
}
|
}
|
||||||
var sameDirectionIncreasing = (this.operator === ">=" || this.operator === ">") && (comp26.operator === ">=" || comp26.operator === ">");
|
var sameDirectionIncreasing = (this.operator === ">=" || this.operator === ">") && (comp26.operator === ">=" || comp26.operator === ">");
|
||||||
var sameDirectionDecreasing = (this.operator === "<=" || this.operator === "<") && (comp26.operator === "<=" || comp26.operator === "<");
|
var sameDirectionDecreasing = (this.operator === "<=" || this.operator === "<") && (comp26.operator === "<=" || comp26.operator === "<");
|
||||||
@@ -30516,8 +30516,8 @@ var require_semver5 = __commonJS({
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
exports2.satisfies = satisfies4;
|
exports2.satisfies = satisfies6;
|
||||||
function satisfies4(version4, range2, options) {
|
function satisfies6(version4, range2, options) {
|
||||||
try {
|
try {
|
||||||
range2 = new Range(range2, options);
|
range2 = new Range(range2, options);
|
||||||
} catch (er) {
|
} catch (er) {
|
||||||
@@ -30647,7 +30647,7 @@ var require_semver5 = __commonJS({
|
|||||||
default:
|
default:
|
||||||
throw new TypeError('Must provide a hilo val of "<" or ">"');
|
throw new TypeError('Must provide a hilo val of "<" or ">"');
|
||||||
}
|
}
|
||||||
if (satisfies4(version4, range2, options)) {
|
if (satisfies6(version4, range2, options)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (var i2 = 0; i2 < range2.set.length; ++i2) {
|
for (var i2 = 0; i2 < range2.set.length; ++i2) {
|
||||||
@@ -91879,8 +91879,8 @@ function _getGlobal(key, defaultValue) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// src/download/download-version.ts
|
// src/download/download-version.ts
|
||||||
var pep440 = __toESM(require_pep440(), 1);
|
var pep4402 = __toESM(require_pep440(), 1);
|
||||||
var semver5 = __toESM(require_semver5(), 1);
|
var semver6 = __toESM(require_semver5(), 1);
|
||||||
|
|
||||||
// src/utils/constants.ts
|
// src/utils/constants.ts
|
||||||
var TOOL_CACHE_NAME = "uv";
|
var TOOL_CACHE_NAME = "uv";
|
||||||
@@ -96338,7 +96338,7 @@ async function validateFileCheckSum(filePath, expected) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// src/download/version-manifest.ts
|
// src/download/version-manifest.ts
|
||||||
var semver4 = __toESM(require_semver5(), 1);
|
var semver5 = __toESM(require_semver5(), 1);
|
||||||
|
|
||||||
// src/utils/fetch.ts
|
// src/utils/fetch.ts
|
||||||
var import_undici2 = __toESM(require_undici2(), 1);
|
var import_undici2 = __toESM(require_undici2(), 1);
|
||||||
@@ -96424,7 +96424,11 @@ function formatVariants(entries) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// src/download/versions-client.ts
|
// src/download/versions-client.ts
|
||||||
|
var pep440 = __toESM(require_pep440(), 1);
|
||||||
|
var semver4 = __toESM(require_semver5(), 1);
|
||||||
var cachedVersionData = /* @__PURE__ */ new Map();
|
var cachedVersionData = /* @__PURE__ */ new Map();
|
||||||
|
var cachedLatestVersionData = /* @__PURE__ */ new Map();
|
||||||
|
var cachedVersionLookup = /* @__PURE__ */ new Map();
|
||||||
async function fetchVersionData(url2 = VERSIONS_NDJSON_URL) {
|
async function fetchVersionData(url2 = VERSIONS_NDJSON_URL) {
|
||||||
const cachedVersions = cachedVersionData.get(url2);
|
const cachedVersions = cachedVersionData.get(url2);
|
||||||
if (cachedVersions !== void 0) {
|
if (cachedVersions !== void 0) {
|
||||||
@@ -96432,15 +96436,8 @@ async function fetchVersionData(url2 = VERSIONS_NDJSON_URL) {
|
|||||||
return cachedVersions;
|
return cachedVersions;
|
||||||
}
|
}
|
||||||
info(`Fetching version data from ${url2} ...`);
|
info(`Fetching version data from ${url2} ...`);
|
||||||
const response = await fetch(url2, {});
|
const { versions } = await readVersionData(url2);
|
||||||
if (!response.ok) {
|
cacheCompleteVersionData(url2, versions);
|
||||||
throw new Error(
|
|
||||||
`Failed to fetch version data: ${response.status} ${response.statusText}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const body2 = await response.text();
|
|
||||||
const versions = parseVersionData(body2, url2);
|
|
||||||
cachedVersionData.set(url2, versions);
|
|
||||||
return versions;
|
return versions;
|
||||||
}
|
}
|
||||||
function parseVersionData(data, sourceDescription) {
|
function parseVersionData(data, sourceDescription) {
|
||||||
@@ -96450,20 +96447,7 @@ function parseVersionData(data, sourceDescription) {
|
|||||||
if (trimmed === "") {
|
if (trimmed === "") {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let parsed;
|
versions.push(parseVersionLine(trimmed, sourceDescription, index + 1));
|
||||||
try {
|
|
||||||
parsed = JSON.parse(trimmed);
|
|
||||||
} catch (error2) {
|
|
||||||
throw new Error(
|
|
||||||
`Failed to parse version data from ${sourceDescription} at line ${index + 1}: ${error2.message}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (!isNdjsonVersion(parsed)) {
|
|
||||||
throw new Error(
|
|
||||||
`Invalid NDJSON record in ${sourceDescription} at line ${index + 1}.`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
versions.push(parsed);
|
|
||||||
}
|
}
|
||||||
if (versions.length === 0) {
|
if (versions.length === 0) {
|
||||||
throw new Error(`No version data found in ${sourceDescription}.`);
|
throw new Error(`No version data found in ${sourceDescription}.`);
|
||||||
@@ -96471,23 +96455,34 @@ function parseVersionData(data, sourceDescription) {
|
|||||||
return versions;
|
return versions;
|
||||||
}
|
}
|
||||||
async function getLatestVersion() {
|
async function getLatestVersion() {
|
||||||
const versions = await fetchVersionData();
|
const cachedVersions = cachedVersionData.get(VERSIONS_NDJSON_URL);
|
||||||
const latestVersion = versions[0]?.version;
|
const cachedLatestVersion = cachedVersions?.[0] ?? cachedLatestVersionData.get(VERSIONS_NDJSON_URL);
|
||||||
|
if (cachedLatestVersion !== void 0) {
|
||||||
|
debug(
|
||||||
|
`Latest version from NDJSON cache: ${cachedLatestVersion.version}`
|
||||||
|
);
|
||||||
|
return cachedLatestVersion.version;
|
||||||
|
}
|
||||||
|
const latestVersion = await findVersionData(() => true);
|
||||||
if (!latestVersion) {
|
if (!latestVersion) {
|
||||||
throw new Error("No versions found in NDJSON data");
|
throw new Error("No versions found in NDJSON data");
|
||||||
}
|
}
|
||||||
debug(`Latest version from NDJSON: ${latestVersion}`);
|
debug(`Latest version from NDJSON: ${latestVersion.version}`);
|
||||||
return latestVersion;
|
return latestVersion.version;
|
||||||
}
|
}
|
||||||
async function getAllVersions() {
|
async function getAllVersions() {
|
||||||
const versions = await fetchVersionData();
|
const versions = await fetchVersionData();
|
||||||
return versions.map((versionData) => versionData.version);
|
return versions.map((versionData) => versionData.version);
|
||||||
}
|
}
|
||||||
async function getArtifact(version4, arch3, platform2) {
|
async function getHighestSatisfyingVersion(versionSpecifier, url2 = VERSIONS_NDJSON_URL) {
|
||||||
const versions = await fetchVersionData();
|
const matchedVersion = await findVersionData(
|
||||||
const versionData = versions.find(
|
(candidate) => versionSatisfies(candidate.version, versionSpecifier),
|
||||||
(candidate) => candidate.version === version4
|
url2
|
||||||
);
|
);
|
||||||
|
return matchedVersion?.version;
|
||||||
|
}
|
||||||
|
async function getArtifact(version4, arch3, platform2) {
|
||||||
|
const versionData = await getVersionData(version4);
|
||||||
if (!versionData) {
|
if (!versionData) {
|
||||||
debug(`Version ${version4} not found in NDJSON data`);
|
debug(`Version ${version4} not found in NDJSON data`);
|
||||||
return void 0;
|
return void 0;
|
||||||
@@ -96515,6 +96510,135 @@ function selectArtifact(artifacts, version4, targetPlatform) {
|
|||||||
`Multiple artifacts found for ${targetPlatform} in version ${version4}`
|
`Multiple artifacts found for ${targetPlatform} in version ${version4}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
async function getVersionData(version4, url2 = VERSIONS_NDJSON_URL) {
|
||||||
|
const cachedVersions = cachedVersionData.get(url2);
|
||||||
|
if (cachedVersions !== void 0) {
|
||||||
|
return cachedVersions.find((candidate) => candidate.version === version4);
|
||||||
|
}
|
||||||
|
const cachedVersion = cachedVersionLookup.get(url2)?.get(version4);
|
||||||
|
if (cachedVersion !== void 0) {
|
||||||
|
return cachedVersion;
|
||||||
|
}
|
||||||
|
return await findVersionData(
|
||||||
|
(candidate) => candidate.version === version4,
|
||||||
|
url2
|
||||||
|
);
|
||||||
|
}
|
||||||
|
async function findVersionData(predicate, url2 = VERSIONS_NDJSON_URL) {
|
||||||
|
const cachedVersions = cachedVersionData.get(url2);
|
||||||
|
if (cachedVersions !== void 0) {
|
||||||
|
return cachedVersions.find(predicate);
|
||||||
|
}
|
||||||
|
const { matchedVersion, versions, complete } = await readVersionData(
|
||||||
|
url2,
|
||||||
|
predicate
|
||||||
|
);
|
||||||
|
if (complete) {
|
||||||
|
cacheCompleteVersionData(url2, versions);
|
||||||
|
}
|
||||||
|
return matchedVersion;
|
||||||
|
}
|
||||||
|
async function readVersionData(url2, stopWhen) {
|
||||||
|
const response = await fetch(url2, {});
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(
|
||||||
|
`Failed to fetch version data: ${response.status} ${response.statusText}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (response.body === null) {
|
||||||
|
const body2 = await response.text();
|
||||||
|
const versions2 = parseVersionData(body2, url2);
|
||||||
|
const matchedVersion2 = stopWhen ? versions2.find((candidate) => stopWhen(candidate)) : void 0;
|
||||||
|
return { complete: true, matchedVersion: matchedVersion2, versions: versions2 };
|
||||||
|
}
|
||||||
|
const versions = [];
|
||||||
|
let lineNumber = 0;
|
||||||
|
let matchedVersion;
|
||||||
|
let buffer3 = "";
|
||||||
|
const decoder = new TextDecoder();
|
||||||
|
const reader = response.body.getReader();
|
||||||
|
const processLine = (line) => {
|
||||||
|
const trimmed = line.trim();
|
||||||
|
if (trimmed === "") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
lineNumber += 1;
|
||||||
|
const versionData = parseVersionLine(trimmed, url2, lineNumber);
|
||||||
|
if (versions.length === 0) {
|
||||||
|
cachedLatestVersionData.set(url2, versionData);
|
||||||
|
}
|
||||||
|
versions.push(versionData);
|
||||||
|
cacheVersion(url2, versionData);
|
||||||
|
if (stopWhen?.(versionData) === true) {
|
||||||
|
matchedVersion = versionData;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
while (true) {
|
||||||
|
const { done, value } = await reader.read();
|
||||||
|
if (done) {
|
||||||
|
buffer3 += decoder.decode();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
buffer3 += decoder.decode(value, { stream: true });
|
||||||
|
let newlineIndex = buffer3.indexOf("\n");
|
||||||
|
while (newlineIndex !== -1) {
|
||||||
|
const line = buffer3.slice(0, newlineIndex);
|
||||||
|
buffer3 = buffer3.slice(newlineIndex + 1);
|
||||||
|
if (processLine(line)) {
|
||||||
|
await reader.cancel();
|
||||||
|
return { complete: false, matchedVersion, versions };
|
||||||
|
}
|
||||||
|
newlineIndex = buffer3.indexOf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (buffer3.trim() !== "" && processLine(buffer3)) {
|
||||||
|
return { complete: true, matchedVersion, versions };
|
||||||
|
}
|
||||||
|
if (versions.length === 0) {
|
||||||
|
throw new Error(`No version data found in ${url2}.`);
|
||||||
|
}
|
||||||
|
return { complete: true, matchedVersion, versions };
|
||||||
|
}
|
||||||
|
function cacheCompleteVersionData(url2, versions) {
|
||||||
|
cachedVersionData.set(url2, versions);
|
||||||
|
if (versions[0] !== void 0) {
|
||||||
|
cachedLatestVersionData.set(url2, versions[0]);
|
||||||
|
}
|
||||||
|
const versionLookup = /* @__PURE__ */ new Map();
|
||||||
|
for (const versionData of versions) {
|
||||||
|
versionLookup.set(versionData.version, versionData);
|
||||||
|
}
|
||||||
|
cachedVersionLookup.set(url2, versionLookup);
|
||||||
|
}
|
||||||
|
function cacheVersion(url2, versionData) {
|
||||||
|
let versionLookup = cachedVersionLookup.get(url2);
|
||||||
|
if (versionLookup === void 0) {
|
||||||
|
versionLookup = /* @__PURE__ */ new Map();
|
||||||
|
cachedVersionLookup.set(url2, versionLookup);
|
||||||
|
}
|
||||||
|
versionLookup.set(versionData.version, versionData);
|
||||||
|
}
|
||||||
|
function parseVersionLine(line, sourceDescription, lineNumber) {
|
||||||
|
let parsed;
|
||||||
|
try {
|
||||||
|
parsed = JSON.parse(line);
|
||||||
|
} catch (error2) {
|
||||||
|
throw new Error(
|
||||||
|
`Failed to parse version data from ${sourceDescription} at line ${lineNumber}: ${error2.message}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!isNdjsonVersion(parsed)) {
|
||||||
|
throw new Error(
|
||||||
|
`Invalid NDJSON record in ${sourceDescription} at line ${lineNumber}.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
function versionSatisfies(version4, versionSpecifier) {
|
||||||
|
return semver4.satisfies(version4, versionSpecifier) || pep440.satisfies(version4, versionSpecifier);
|
||||||
|
}
|
||||||
function isNdjsonVersion(value) {
|
function isNdjsonVersion(value) {
|
||||||
if (!isRecord2(value)) {
|
if (!isRecord2(value)) {
|
||||||
return false;
|
return false;
|
||||||
@@ -96540,7 +96664,7 @@ var cachedManifestEntries = /* @__PURE__ */ new Map();
|
|||||||
async function getLatestKnownVersion(manifestUrl) {
|
async function getLatestKnownVersion(manifestUrl) {
|
||||||
const versions = await getAllVersions2(manifestUrl);
|
const versions = await getAllVersions2(manifestUrl);
|
||||||
const latestVersion = versions.reduce(
|
const latestVersion = versions.reduce(
|
||||||
(latest, current) => semver4.gt(current, latest) ? current : latest
|
(latest, current) => semver5.gt(current, latest) ? current : latest
|
||||||
);
|
);
|
||||||
return latestVersion;
|
return latestVersion;
|
||||||
}
|
}
|
||||||
@@ -96745,12 +96869,20 @@ async function resolveVersion(versionInput, manifestUrl, resolutionStrategy2 = "
|
|||||||
if (isExplicitVersion(version4)) {
|
if (isExplicitVersion(version4)) {
|
||||||
debug(`Version ${version4} is an explicit version.`);
|
debug(`Version ${version4} is an explicit version.`);
|
||||||
if (resolveVersionSpecifierToLatest) {
|
if (resolveVersionSpecifierToLatest) {
|
||||||
if (!pep440.satisfies(version4, versionInput)) {
|
if (!pep4402.satisfies(version4, versionInput)) {
|
||||||
throw new Error(`No version found for ${versionInput}`);
|
throw new Error(`No version found for ${versionInput}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return version4;
|
return version4;
|
||||||
}
|
}
|
||||||
|
if (manifestUrl === void 0 && resolutionStrategy2 === "highest") {
|
||||||
|
const resolvedVersion2 = await getHighestSatisfyingVersion(version4);
|
||||||
|
if (resolvedVersion2 !== void 0) {
|
||||||
|
debug(`Resolved version from NDJSON stream: ${resolvedVersion2}`);
|
||||||
|
return resolvedVersion2;
|
||||||
|
}
|
||||||
|
throw new Error(`No version found for ${version4}`);
|
||||||
|
}
|
||||||
const availableVersions = await getAvailableVersions(manifestUrl);
|
const availableVersions = await getAvailableVersions(manifestUrl);
|
||||||
debug(`Available versions: ${availableVersions}`);
|
debug(`Available versions: ${availableVersions}`);
|
||||||
const resolvedVersion = resolutionStrategy2 === "lowest" ? minSatisfying3(availableVersions, version4) : maxSatisfying2(availableVersions, version4);
|
const resolvedVersion = resolutionStrategy2 === "lowest" ? minSatisfying3(availableVersions, version4) : maxSatisfying2(availableVersions, version4);
|
||||||
@@ -96775,7 +96907,7 @@ function maxSatisfying2(versions, version4) {
|
|||||||
debug(`Found a version that satisfies the semver range: ${maxSemver}`);
|
debug(`Found a version that satisfies the semver range: ${maxSemver}`);
|
||||||
return maxSemver;
|
return maxSemver;
|
||||||
}
|
}
|
||||||
const maxPep440 = pep440.maxSatisfying(versions, version4);
|
const maxPep440 = pep4402.maxSatisfying(versions, version4);
|
||||||
if (maxPep440 !== null) {
|
if (maxPep440 !== null) {
|
||||||
debug(
|
debug(
|
||||||
`Found a version that satisfies the pep440 specifier: ${maxPep440}`
|
`Found a version that satisfies the pep440 specifier: ${maxPep440}`
|
||||||
@@ -96785,12 +96917,12 @@ function maxSatisfying2(versions, version4) {
|
|||||||
return void 0;
|
return void 0;
|
||||||
}
|
}
|
||||||
function minSatisfying3(versions, version4) {
|
function minSatisfying3(versions, version4) {
|
||||||
const minSemver = semver5.minSatisfying(versions, version4);
|
const minSemver = semver6.minSatisfying(versions, version4);
|
||||||
if (minSemver !== null) {
|
if (minSemver !== null) {
|
||||||
debug(`Found a version that satisfies the semver range: ${minSemver}`);
|
debug(`Found a version that satisfies the semver range: ${minSemver}`);
|
||||||
return minSemver;
|
return minSemver;
|
||||||
}
|
}
|
||||||
const minPep440 = pep440.minSatisfying(versions, version4);
|
const minPep440 = pep4402.minSatisfying(versions, version4);
|
||||||
if (minPep440 !== null) {
|
if (minPep440 !== null) {
|
||||||
debug(
|
debug(
|
||||||
`Found a version that satisfies the pep440 specifier: ${minPep440}`
|
`Found a version that satisfies the pep440 specifier: ${minPep440}`
|
||||||
|
|||||||
823
dist/update-known-checksums/index.cjs
generated
vendored
823
dist/update-known-checksums/index.cjs
generated
vendored
@@ -19201,13 +19201,13 @@ var require_semver = __commonJS({
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
rangeTmp = new Range(comp.value, options);
|
rangeTmp = new Range(comp.value, options);
|
||||||
return satisfies(this.value, rangeTmp, options);
|
return satisfies3(this.value, rangeTmp, options);
|
||||||
} else if (comp.operator === "") {
|
} else if (comp.operator === "") {
|
||||||
if (comp.value === "") {
|
if (comp.value === "") {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
rangeTmp = new Range(this.value, options);
|
rangeTmp = new Range(this.value, options);
|
||||||
return satisfies(comp.semver, rangeTmp, options);
|
return satisfies3(comp.semver, rangeTmp, options);
|
||||||
}
|
}
|
||||||
var sameDirectionIncreasing = (this.operator === ">=" || this.operator === ">") && (comp.operator === ">=" || comp.operator === ">");
|
var sameDirectionIncreasing = (this.operator === ">=" || this.operator === ">") && (comp.operator === ">=" || comp.operator === ">");
|
||||||
var sameDirectionDecreasing = (this.operator === "<=" || this.operator === "<") && (comp.operator === "<=" || comp.operator === "<");
|
var sameDirectionDecreasing = (this.operator === "<=" || this.operator === "<") && (comp.operator === "<=" || comp.operator === "<");
|
||||||
@@ -19534,8 +19534,8 @@ var require_semver = __commonJS({
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
exports2.satisfies = satisfies;
|
exports2.satisfies = satisfies3;
|
||||||
function satisfies(version, range, options) {
|
function satisfies3(version, range, options) {
|
||||||
try {
|
try {
|
||||||
range = new Range(range, options);
|
range = new Range(range, options);
|
||||||
} catch (er) {
|
} catch (er) {
|
||||||
@@ -19665,7 +19665,7 @@ var require_semver = __commonJS({
|
|||||||
default:
|
default:
|
||||||
throw new TypeError('Must provide a hilo val of "<" or ">"');
|
throw new TypeError('Must provide a hilo val of "<" or ">"');
|
||||||
}
|
}
|
||||||
if (satisfies(version, range, options)) {
|
if (satisfies3(version, range, options)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (var i2 = 0; i2 < range.set.length; ++i2) {
|
for (var i2 = 0; i2 < range.set.length; ++i2) {
|
||||||
@@ -19739,6 +19739,654 @@ var require_semver = __commonJS({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// node_modules/@renovatebot/pep440/lib/version.js
|
||||||
|
var require_version = __commonJS({
|
||||||
|
"node_modules/@renovatebot/pep440/lib/version.js"(exports2, module2) {
|
||||||
|
var VERSION_PATTERN = [
|
||||||
|
"v?",
|
||||||
|
"(?:",
|
||||||
|
/* */
|
||||||
|
"(?:(?<epoch>[0-9]+)!)?",
|
||||||
|
// epoch
|
||||||
|
/* */
|
||||||
|
"(?<release>[0-9]+(?:\\.[0-9]+)*)",
|
||||||
|
// release segment
|
||||||
|
/* */
|
||||||
|
"(?<pre>",
|
||||||
|
// pre-release
|
||||||
|
/* */
|
||||||
|
"[-_\\.]?",
|
||||||
|
/* */
|
||||||
|
"(?<pre_l>(a|b|c|rc|alpha|beta|pre|preview))",
|
||||||
|
/* */
|
||||||
|
"[-_\\.]?",
|
||||||
|
/* */
|
||||||
|
"(?<pre_n>[0-9]+)?",
|
||||||
|
/* */
|
||||||
|
")?",
|
||||||
|
/* */
|
||||||
|
"(?<post>",
|
||||||
|
// post release
|
||||||
|
/* */
|
||||||
|
"(?:-(?<post_n1>[0-9]+))",
|
||||||
|
/* */
|
||||||
|
"|",
|
||||||
|
/* */
|
||||||
|
"(?:",
|
||||||
|
/* */
|
||||||
|
"[-_\\.]?",
|
||||||
|
/* */
|
||||||
|
"(?<post_l>post|rev|r)",
|
||||||
|
/* */
|
||||||
|
"[-_\\.]?",
|
||||||
|
/* */
|
||||||
|
"(?<post_n2>[0-9]+)?",
|
||||||
|
/* */
|
||||||
|
")",
|
||||||
|
/* */
|
||||||
|
")?",
|
||||||
|
/* */
|
||||||
|
"(?<dev>",
|
||||||
|
// dev release
|
||||||
|
/* */
|
||||||
|
"[-_\\.]?",
|
||||||
|
/* */
|
||||||
|
"(?<dev_l>dev)",
|
||||||
|
/* */
|
||||||
|
"[-_\\.]?",
|
||||||
|
/* */
|
||||||
|
"(?<dev_n>[0-9]+)?",
|
||||||
|
/* */
|
||||||
|
")?",
|
||||||
|
")",
|
||||||
|
"(?:\\+(?<local>[a-z0-9]+(?:[-_\\.][a-z0-9]+)*))?"
|
||||||
|
// local version
|
||||||
|
].join("");
|
||||||
|
module2.exports = {
|
||||||
|
VERSION_PATTERN,
|
||||||
|
valid,
|
||||||
|
clean,
|
||||||
|
explain,
|
||||||
|
parse,
|
||||||
|
stringify
|
||||||
|
};
|
||||||
|
var validRegex = new RegExp("^" + VERSION_PATTERN + "$", "i");
|
||||||
|
function valid(version) {
|
||||||
|
return validRegex.test(version) ? version : null;
|
||||||
|
}
|
||||||
|
var cleanRegex = new RegExp("^\\s*" + VERSION_PATTERN + "\\s*$", "i");
|
||||||
|
function clean(version) {
|
||||||
|
return stringify(parse(version, cleanRegex));
|
||||||
|
}
|
||||||
|
function parse(version, regex) {
|
||||||
|
const { groups } = (regex || validRegex).exec(version) || {};
|
||||||
|
if (!groups) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const parsed = {
|
||||||
|
epoch: Number(groups.epoch ? groups.epoch : 0),
|
||||||
|
release: groups.release.split(".").map(Number),
|
||||||
|
pre: normalize_letter_version(groups.pre_l, groups.pre_n),
|
||||||
|
post: normalize_letter_version(
|
||||||
|
groups.post_l,
|
||||||
|
groups.post_n1 || groups.post_n2
|
||||||
|
),
|
||||||
|
dev: normalize_letter_version(groups.dev_l, groups.dev_n),
|
||||||
|
local: parse_local_version(groups.local)
|
||||||
|
};
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
function stringify(parsed) {
|
||||||
|
if (!parsed) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const { epoch, release, pre, post, dev, local } = parsed;
|
||||||
|
const parts = [];
|
||||||
|
if (epoch !== 0) {
|
||||||
|
parts.push(`${epoch}!`);
|
||||||
|
}
|
||||||
|
parts.push(release.join("."));
|
||||||
|
if (pre) {
|
||||||
|
parts.push(pre.join(""));
|
||||||
|
}
|
||||||
|
if (post) {
|
||||||
|
parts.push("." + post.join(""));
|
||||||
|
}
|
||||||
|
if (dev) {
|
||||||
|
parts.push("." + dev.join(""));
|
||||||
|
}
|
||||||
|
if (local) {
|
||||||
|
parts.push(`+${local}`);
|
||||||
|
}
|
||||||
|
return parts.join("");
|
||||||
|
}
|
||||||
|
function normalize_letter_version(letterIn, numberIn) {
|
||||||
|
let letter = letterIn;
|
||||||
|
let number = numberIn;
|
||||||
|
if (letter) {
|
||||||
|
if (!number) {
|
||||||
|
number = 0;
|
||||||
|
}
|
||||||
|
letter = letter.toLowerCase();
|
||||||
|
if (letter === "alpha") {
|
||||||
|
letter = "a";
|
||||||
|
} else if (letter === "beta") {
|
||||||
|
letter = "b";
|
||||||
|
} else if (["c", "pre", "preview"].includes(letter)) {
|
||||||
|
letter = "rc";
|
||||||
|
} else if (["rev", "r"].includes(letter)) {
|
||||||
|
letter = "post";
|
||||||
|
}
|
||||||
|
return [letter, Number(number)];
|
||||||
|
}
|
||||||
|
if (!letter && number) {
|
||||||
|
letter = "post";
|
||||||
|
return [letter, Number(number)];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
function parse_local_version(local) {
|
||||||
|
if (local) {
|
||||||
|
return local.split(/[._-]/).map(
|
||||||
|
(part) => Number.isNaN(Number(part)) ? part.toLowerCase() : Number(part)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
function explain(version) {
|
||||||
|
const parsed = parse(version);
|
||||||
|
if (!parsed) {
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
const { epoch, release, pre, post, dev, local } = parsed;
|
||||||
|
let base_version = "";
|
||||||
|
if (epoch !== 0) {
|
||||||
|
base_version += epoch + "!";
|
||||||
|
}
|
||||||
|
base_version += release.join(".");
|
||||||
|
const is_prerelease = Boolean(dev || pre);
|
||||||
|
const is_devrelease = Boolean(dev);
|
||||||
|
const is_postrelease = Boolean(post);
|
||||||
|
return {
|
||||||
|
epoch,
|
||||||
|
release,
|
||||||
|
pre,
|
||||||
|
post: post ? post[1] : post,
|
||||||
|
dev: dev ? dev[1] : dev,
|
||||||
|
local: local ? local.join(".") : local,
|
||||||
|
public: stringify(parsed).split("+", 1)[0],
|
||||||
|
base_version,
|
||||||
|
is_prerelease,
|
||||||
|
is_devrelease,
|
||||||
|
is_postrelease
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// node_modules/@renovatebot/pep440/lib/operator.js
|
||||||
|
var require_operator = __commonJS({
|
||||||
|
"node_modules/@renovatebot/pep440/lib/operator.js"(exports2, module2) {
|
||||||
|
var { parse } = require_version();
|
||||||
|
module2.exports = {
|
||||||
|
compare,
|
||||||
|
rcompare: rcompare2,
|
||||||
|
lt,
|
||||||
|
le,
|
||||||
|
eq,
|
||||||
|
ne,
|
||||||
|
ge,
|
||||||
|
gt,
|
||||||
|
"<": lt,
|
||||||
|
"<=": le,
|
||||||
|
"==": eq,
|
||||||
|
"!=": ne,
|
||||||
|
">=": ge,
|
||||||
|
">": gt,
|
||||||
|
"===": arbitrary
|
||||||
|
};
|
||||||
|
function lt(version, other) {
|
||||||
|
return compare(version, other) < 0;
|
||||||
|
}
|
||||||
|
function le(version, other) {
|
||||||
|
return compare(version, other) <= 0;
|
||||||
|
}
|
||||||
|
function eq(version, other) {
|
||||||
|
return compare(version, other) === 0;
|
||||||
|
}
|
||||||
|
function ne(version, other) {
|
||||||
|
return compare(version, other) !== 0;
|
||||||
|
}
|
||||||
|
function ge(version, other) {
|
||||||
|
return compare(version, other) >= 0;
|
||||||
|
}
|
||||||
|
function gt(version, other) {
|
||||||
|
return compare(version, other) > 0;
|
||||||
|
}
|
||||||
|
function arbitrary(version, other) {
|
||||||
|
return version.toLowerCase() === other.toLowerCase();
|
||||||
|
}
|
||||||
|
function compare(version, other) {
|
||||||
|
const parsedVersion = parse(version);
|
||||||
|
const parsedOther = parse(other);
|
||||||
|
const keyVersion = calculateKey(parsedVersion);
|
||||||
|
const keyOther = calculateKey(parsedOther);
|
||||||
|
return pyCompare(keyVersion, keyOther);
|
||||||
|
}
|
||||||
|
function rcompare2(version, other) {
|
||||||
|
return -compare(version, other);
|
||||||
|
}
|
||||||
|
function pyCompare(elemIn, otherIn) {
|
||||||
|
let elem = elemIn;
|
||||||
|
let other = otherIn;
|
||||||
|
if (elem === other) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (Array.isArray(elem) !== Array.isArray(other)) {
|
||||||
|
elem = Array.isArray(elem) ? elem : [elem];
|
||||||
|
other = Array.isArray(other) ? other : [other];
|
||||||
|
}
|
||||||
|
if (Array.isArray(elem)) {
|
||||||
|
const len = Math.min(elem.length, other.length);
|
||||||
|
for (let i = 0; i < len; i += 1) {
|
||||||
|
const res = pyCompare(elem[i], other[i]);
|
||||||
|
if (res !== 0) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return elem.length - other.length;
|
||||||
|
}
|
||||||
|
if (elem === -Infinity || other === Infinity) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (elem === Infinity || other === -Infinity) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return elem < other ? -1 : 1;
|
||||||
|
}
|
||||||
|
function calculateKey(input) {
|
||||||
|
const { epoch } = input;
|
||||||
|
let { release, pre, post, local, dev } = input;
|
||||||
|
release = release.concat();
|
||||||
|
release.reverse();
|
||||||
|
while (release.length && release[0] === 0) {
|
||||||
|
release.shift();
|
||||||
|
}
|
||||||
|
release.reverse();
|
||||||
|
if (!pre && !post && dev) pre = -Infinity;
|
||||||
|
else if (!pre) pre = Infinity;
|
||||||
|
if (!post) post = -Infinity;
|
||||||
|
if (!dev) dev = Infinity;
|
||||||
|
if (!local) {
|
||||||
|
local = -Infinity;
|
||||||
|
} else {
|
||||||
|
local = local.map(
|
||||||
|
(i) => Number.isNaN(Number(i)) ? [-Infinity, i] : [Number(i), ""]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return [epoch, release, pre, post, dev, local];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// node_modules/@renovatebot/pep440/lib/specifier.js
|
||||||
|
var require_specifier = __commonJS({
|
||||||
|
"node_modules/@renovatebot/pep440/lib/specifier.js"(exports2, module2) {
|
||||||
|
var { VERSION_PATTERN, explain: explainVersion } = require_version();
|
||||||
|
var Operator = require_operator();
|
||||||
|
var RANGE_PATTERN = [
|
||||||
|
"(?<operator>(===|~=|==|!=|<=|>=|<|>))",
|
||||||
|
"\\s*",
|
||||||
|
"(",
|
||||||
|
/* */
|
||||||
|
"(?<version>(?:" + VERSION_PATTERN.replace(/\?<\w+>/g, "?:") + "))",
|
||||||
|
/* */
|
||||||
|
"(?<prefix>\\.\\*)?",
|
||||||
|
/* */
|
||||||
|
"|",
|
||||||
|
/* */
|
||||||
|
"(?<legacy>[^,;\\s)]+)",
|
||||||
|
")"
|
||||||
|
].join("");
|
||||||
|
module2.exports = {
|
||||||
|
RANGE_PATTERN,
|
||||||
|
parse,
|
||||||
|
satisfies: satisfies3,
|
||||||
|
filter,
|
||||||
|
validRange,
|
||||||
|
maxSatisfying,
|
||||||
|
minSatisfying
|
||||||
|
};
|
||||||
|
var isEqualityOperator = (op) => ["==", "!=", "==="].includes(op);
|
||||||
|
var rangeRegex = new RegExp("^" + RANGE_PATTERN + "$", "i");
|
||||||
|
function parse(ranges) {
|
||||||
|
if (!ranges.trim()) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const specifiers = ranges.split(",").map((range) => rangeRegex.exec(range.trim()) || {}).map(({ groups }) => {
|
||||||
|
if (!groups) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
let { ...spec } = groups;
|
||||||
|
const { operator, version, prefix, legacy } = groups;
|
||||||
|
if (version) {
|
||||||
|
spec = { ...spec, ...explainVersion(version) };
|
||||||
|
if (operator === "~=") {
|
||||||
|
if (spec.release.length < 2) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isEqualityOperator(operator) && spec.local) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (prefix) {
|
||||||
|
if (!isEqualityOperator(operator) || spec.dev || spec.local) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (legacy && operator !== "===") {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return spec;
|
||||||
|
});
|
||||||
|
if (specifiers.filter(Boolean).length !== specifiers.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return specifiers;
|
||||||
|
}
|
||||||
|
function filter(versions, specifier, options = {}) {
|
||||||
|
const filtered = pick(versions, specifier, options);
|
||||||
|
if (filtered.length === 0 && options.prereleases === void 0) {
|
||||||
|
return pick(versions, specifier, { prereleases: true });
|
||||||
|
}
|
||||||
|
return filtered;
|
||||||
|
}
|
||||||
|
function maxSatisfying(versions, range, options) {
|
||||||
|
const found = filter(versions, range, options).sort(Operator.compare);
|
||||||
|
return found.length === 0 ? null : found[found.length - 1];
|
||||||
|
}
|
||||||
|
function minSatisfying(versions, range, options) {
|
||||||
|
const found = filter(versions, range, options).sort(Operator.compare);
|
||||||
|
return found.length === 0 ? null : found[0];
|
||||||
|
}
|
||||||
|
function pick(versions, specifier, options) {
|
||||||
|
const parsed = parse(specifier);
|
||||||
|
if (!parsed) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return versions.filter((version) => {
|
||||||
|
const explained = explainVersion(version);
|
||||||
|
if (!parsed.length) {
|
||||||
|
return explained && !(explained.is_prerelease && !options.prereleases);
|
||||||
|
}
|
||||||
|
return parsed.reduce((pass, spec) => {
|
||||||
|
if (!pass) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return contains({ ...spec, ...options }, { version, explained });
|
||||||
|
}, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function satisfies3(version, specifier, options = {}) {
|
||||||
|
const filtered = pick([version], specifier, options);
|
||||||
|
return filtered.length === 1;
|
||||||
|
}
|
||||||
|
function arrayStartsWith(array, prefix) {
|
||||||
|
if (prefix.length > array.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (let i = 0; i < prefix.length; i += 1) {
|
||||||
|
if (prefix[i] !== array[i]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
function contains(specifier, input) {
|
||||||
|
const { explained } = input;
|
||||||
|
let { version } = input;
|
||||||
|
const { ...spec } = specifier;
|
||||||
|
if (spec.prereleases === void 0) {
|
||||||
|
spec.prereleases = spec.is_prerelease;
|
||||||
|
}
|
||||||
|
if (explained && explained.is_prerelease && !spec.prereleases) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (spec.operator === "~=") {
|
||||||
|
let compatiblePrefix = spec.release.slice(0, -1).concat("*").join(".");
|
||||||
|
if (spec.epoch) {
|
||||||
|
compatiblePrefix = spec.epoch + "!" + compatiblePrefix;
|
||||||
|
}
|
||||||
|
return satisfies3(version, `>=${spec.version}, ==${compatiblePrefix}`, {
|
||||||
|
prereleases: spec.prereleases
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (spec.prefix) {
|
||||||
|
const isMatching = explained.epoch === spec.epoch && arrayStartsWith(explained.release, spec.release);
|
||||||
|
const isEquality = spec.operator !== "!=";
|
||||||
|
return isEquality ? isMatching : !isMatching;
|
||||||
|
}
|
||||||
|
if (explained) {
|
||||||
|
if (explained.local && spec.version) {
|
||||||
|
version = explained.public;
|
||||||
|
spec.version = explainVersion(spec.version).public;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (spec.operator === "<" || spec.operator === ">") {
|
||||||
|
if (Operator.eq(spec.release.join("."), explained.release.join("."))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const op = Operator[spec.operator];
|
||||||
|
return op(version, spec.version || spec.legacy);
|
||||||
|
}
|
||||||
|
function validRange(specifier) {
|
||||||
|
return Boolean(parse(specifier));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// node_modules/@renovatebot/pep440/lib/semantic.js
|
||||||
|
var require_semantic = __commonJS({
|
||||||
|
"node_modules/@renovatebot/pep440/lib/semantic.js"(exports2, module2) {
|
||||||
|
var { explain, parse, stringify } = require_version();
|
||||||
|
module2.exports = {
|
||||||
|
major,
|
||||||
|
minor,
|
||||||
|
patch,
|
||||||
|
inc
|
||||||
|
};
|
||||||
|
function major(input) {
|
||||||
|
const version = explain(input);
|
||||||
|
if (!version) {
|
||||||
|
throw new TypeError("Invalid Version: " + input);
|
||||||
|
}
|
||||||
|
return version.release[0];
|
||||||
|
}
|
||||||
|
function minor(input) {
|
||||||
|
const version = explain(input);
|
||||||
|
if (!version) {
|
||||||
|
throw new TypeError("Invalid Version: " + input);
|
||||||
|
}
|
||||||
|
if (version.release.length < 2) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return version.release[1];
|
||||||
|
}
|
||||||
|
function patch(input) {
|
||||||
|
const version = explain(input);
|
||||||
|
if (!version) {
|
||||||
|
throw new TypeError("Invalid Version: " + input);
|
||||||
|
}
|
||||||
|
if (version.release.length < 3) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return version.release[2];
|
||||||
|
}
|
||||||
|
function inc(input, release, preReleaseIdentifier) {
|
||||||
|
let identifier = preReleaseIdentifier || `a`;
|
||||||
|
const version = parse(input);
|
||||||
|
if (!version) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!["a", "b", "c", "rc", "alpha", "beta", "pre", "preview"].includes(
|
||||||
|
identifier
|
||||||
|
)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
switch (release) {
|
||||||
|
case "premajor":
|
||||||
|
{
|
||||||
|
const [majorVersion] = version.release;
|
||||||
|
version.release.fill(0);
|
||||||
|
version.release[0] = majorVersion + 1;
|
||||||
|
}
|
||||||
|
version.pre = [identifier, 0];
|
||||||
|
delete version.post;
|
||||||
|
delete version.dev;
|
||||||
|
delete version.local;
|
||||||
|
break;
|
||||||
|
case "preminor":
|
||||||
|
{
|
||||||
|
const [majorVersion, minorVersion = 0] = version.release;
|
||||||
|
version.release.fill(0);
|
||||||
|
version.release[0] = majorVersion;
|
||||||
|
version.release[1] = minorVersion + 1;
|
||||||
|
}
|
||||||
|
version.pre = [identifier, 0];
|
||||||
|
delete version.post;
|
||||||
|
delete version.dev;
|
||||||
|
delete version.local;
|
||||||
|
break;
|
||||||
|
case "prepatch":
|
||||||
|
{
|
||||||
|
const [majorVersion, minorVersion = 0, patchVersion = 0] = version.release;
|
||||||
|
version.release.fill(0);
|
||||||
|
version.release[0] = majorVersion;
|
||||||
|
version.release[1] = minorVersion;
|
||||||
|
version.release[2] = patchVersion + 1;
|
||||||
|
}
|
||||||
|
version.pre = [identifier, 0];
|
||||||
|
delete version.post;
|
||||||
|
delete version.dev;
|
||||||
|
delete version.local;
|
||||||
|
break;
|
||||||
|
case "prerelease":
|
||||||
|
if (version.pre === null) {
|
||||||
|
const [majorVersion, minorVersion = 0, patchVersion = 0] = version.release;
|
||||||
|
version.release.fill(0);
|
||||||
|
version.release[0] = majorVersion;
|
||||||
|
version.release[1] = minorVersion;
|
||||||
|
version.release[2] = patchVersion + 1;
|
||||||
|
version.pre = [identifier, 0];
|
||||||
|
} else {
|
||||||
|
if (preReleaseIdentifier === void 0 && version.pre !== null) {
|
||||||
|
[identifier] = version.pre;
|
||||||
|
}
|
||||||
|
const [letter, number] = version.pre;
|
||||||
|
if (letter === identifier) {
|
||||||
|
version.pre = [letter, number + 1];
|
||||||
|
} else {
|
||||||
|
version.pre = [identifier, 0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete version.post;
|
||||||
|
delete version.dev;
|
||||||
|
delete version.local;
|
||||||
|
break;
|
||||||
|
case "major":
|
||||||
|
if (version.release.slice(1).some((value) => value !== 0) || version.pre === null) {
|
||||||
|
const [majorVersion] = version.release;
|
||||||
|
version.release.fill(0);
|
||||||
|
version.release[0] = majorVersion + 1;
|
||||||
|
}
|
||||||
|
delete version.pre;
|
||||||
|
delete version.post;
|
||||||
|
delete version.dev;
|
||||||
|
delete version.local;
|
||||||
|
break;
|
||||||
|
case "minor":
|
||||||
|
if (version.release.slice(2).some((value) => value !== 0) || version.pre === null) {
|
||||||
|
const [majorVersion, minorVersion = 0] = version.release;
|
||||||
|
version.release.fill(0);
|
||||||
|
version.release[0] = majorVersion;
|
||||||
|
version.release[1] = minorVersion + 1;
|
||||||
|
}
|
||||||
|
delete version.pre;
|
||||||
|
delete version.post;
|
||||||
|
delete version.dev;
|
||||||
|
delete version.local;
|
||||||
|
break;
|
||||||
|
case "patch":
|
||||||
|
if (version.release.slice(3).some((value) => value !== 0) || version.pre === null) {
|
||||||
|
const [majorVersion, minorVersion = 0, patchVersion = 0] = version.release;
|
||||||
|
version.release.fill(0);
|
||||||
|
version.release[0] = majorVersion;
|
||||||
|
version.release[1] = minorVersion;
|
||||||
|
version.release[2] = patchVersion + 1;
|
||||||
|
}
|
||||||
|
delete version.pre;
|
||||||
|
delete version.post;
|
||||||
|
delete version.dev;
|
||||||
|
delete version.local;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return stringify(version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// node_modules/@renovatebot/pep440/index.js
|
||||||
|
var require_pep440 = __commonJS({
|
||||||
|
"node_modules/@renovatebot/pep440/index.js"(exports2, module2) {
|
||||||
|
var { valid, clean, explain, parse } = require_version();
|
||||||
|
var { lt, le, eq, ne, ge, gt, compare, rcompare: rcompare2 } = require_operator();
|
||||||
|
var {
|
||||||
|
filter,
|
||||||
|
maxSatisfying,
|
||||||
|
minSatisfying,
|
||||||
|
RANGE_PATTERN,
|
||||||
|
satisfies: satisfies3,
|
||||||
|
validRange
|
||||||
|
} = require_specifier();
|
||||||
|
var { major, minor, patch, inc } = require_semantic();
|
||||||
|
module2.exports = {
|
||||||
|
// version
|
||||||
|
valid,
|
||||||
|
clean,
|
||||||
|
explain,
|
||||||
|
parse,
|
||||||
|
// operator
|
||||||
|
lt,
|
||||||
|
le,
|
||||||
|
lte: le,
|
||||||
|
eq,
|
||||||
|
ne,
|
||||||
|
neq: ne,
|
||||||
|
ge,
|
||||||
|
gte: ge,
|
||||||
|
gt,
|
||||||
|
compare,
|
||||||
|
rcompare: rcompare2,
|
||||||
|
// range
|
||||||
|
filter,
|
||||||
|
maxSatisfying,
|
||||||
|
minSatisfying,
|
||||||
|
RANGE_PATTERN,
|
||||||
|
satisfies: satisfies3,
|
||||||
|
validRange,
|
||||||
|
// semantic
|
||||||
|
major,
|
||||||
|
minor,
|
||||||
|
patch,
|
||||||
|
inc
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// node_modules/undici/lib/core/symbols.js
|
// node_modules/undici/lib/core/symbols.js
|
||||||
var require_symbols6 = __commonJS({
|
var require_symbols6 = __commonJS({
|
||||||
"node_modules/undici/lib/core/symbols.js"(exports2, module2) {
|
"node_modules/undici/lib/core/symbols.js"(exports2, module2) {
|
||||||
@@ -44945,7 +45593,7 @@ function info(message) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// src/update-known-checksums.ts
|
// src/update-known-checksums.ts
|
||||||
var semver = __toESM(require_semver(), 1);
|
var semver2 = __toESM(require_semver(), 1);
|
||||||
|
|
||||||
// src/download/checksum/known-checksums.ts
|
// src/download/checksum/known-checksums.ts
|
||||||
var KNOWN_CHECKSUMS = {
|
var KNOWN_CHECKSUMS = {
|
||||||
@@ -49376,6 +50024,10 @@ async function updateChecksums(filePath, checksumEntries) {
|
|||||||
await import_node_fs.promises.writeFile(filePath, content);
|
await import_node_fs.promises.writeFile(filePath, content);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// src/download/versions-client.ts
|
||||||
|
var pep440 = __toESM(require_pep440(), 1);
|
||||||
|
var semver = __toESM(require_semver(), 1);
|
||||||
|
|
||||||
// src/utils/constants.ts
|
// src/utils/constants.ts
|
||||||
var VERSIONS_NDJSON_URL = "https://raw.githubusercontent.com/astral-sh/versions/main/v1/uv.ndjson";
|
var VERSIONS_NDJSON_URL = "https://raw.githubusercontent.com/astral-sh/versions/main/v1/uv.ndjson";
|
||||||
|
|
||||||
@@ -49399,6 +50051,8 @@ var fetch = async (url, opts) => await (0, import_undici2.fetch)(url, {
|
|||||||
|
|
||||||
// src/download/versions-client.ts
|
// src/download/versions-client.ts
|
||||||
var cachedVersionData = /* @__PURE__ */ new Map();
|
var cachedVersionData = /* @__PURE__ */ new Map();
|
||||||
|
var cachedLatestVersionData = /* @__PURE__ */ new Map();
|
||||||
|
var cachedVersionLookup = /* @__PURE__ */ new Map();
|
||||||
async function fetchVersionData(url = VERSIONS_NDJSON_URL) {
|
async function fetchVersionData(url = VERSIONS_NDJSON_URL) {
|
||||||
const cachedVersions = cachedVersionData.get(url);
|
const cachedVersions = cachedVersionData.get(url);
|
||||||
if (cachedVersions !== void 0) {
|
if (cachedVersions !== void 0) {
|
||||||
@@ -49406,15 +50060,8 @@ async function fetchVersionData(url = VERSIONS_NDJSON_URL) {
|
|||||||
return cachedVersions;
|
return cachedVersions;
|
||||||
}
|
}
|
||||||
info(`Fetching version data from ${url} ...`);
|
info(`Fetching version data from ${url} ...`);
|
||||||
const response = await fetch(url, {});
|
const { versions } = await readVersionData(url);
|
||||||
if (!response.ok) {
|
cacheCompleteVersionData(url, versions);
|
||||||
throw new Error(
|
|
||||||
`Failed to fetch version data: ${response.status} ${response.statusText}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const body = await response.text();
|
|
||||||
const versions = parseVersionData(body, url);
|
|
||||||
cachedVersionData.set(url, versions);
|
|
||||||
return versions;
|
return versions;
|
||||||
}
|
}
|
||||||
function parseVersionData(data, sourceDescription) {
|
function parseVersionData(data, sourceDescription) {
|
||||||
@@ -49424,20 +50071,7 @@ function parseVersionData(data, sourceDescription) {
|
|||||||
if (trimmed === "") {
|
if (trimmed === "") {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let parsed;
|
versions.push(parseVersionLine(trimmed, sourceDescription, index + 1));
|
||||||
try {
|
|
||||||
parsed = JSON.parse(trimmed);
|
|
||||||
} catch (error) {
|
|
||||||
throw new Error(
|
|
||||||
`Failed to parse version data from ${sourceDescription} at line ${index + 1}: ${error.message}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (!isNdjsonVersion(parsed)) {
|
|
||||||
throw new Error(
|
|
||||||
`Invalid NDJSON record in ${sourceDescription} at line ${index + 1}.`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
versions.push(parsed);
|
|
||||||
}
|
}
|
||||||
if (versions.length === 0) {
|
if (versions.length === 0) {
|
||||||
throw new Error(`No version data found in ${sourceDescription}.`);
|
throw new Error(`No version data found in ${sourceDescription}.`);
|
||||||
@@ -49445,13 +50079,132 @@ function parseVersionData(data, sourceDescription) {
|
|||||||
return versions;
|
return versions;
|
||||||
}
|
}
|
||||||
async function getLatestVersion() {
|
async function getLatestVersion() {
|
||||||
const versions = await fetchVersionData();
|
const cachedVersions = cachedVersionData.get(VERSIONS_NDJSON_URL);
|
||||||
const latestVersion = versions[0]?.version;
|
const cachedLatestVersion = cachedVersions?.[0] ?? cachedLatestVersionData.get(VERSIONS_NDJSON_URL);
|
||||||
|
if (cachedLatestVersion !== void 0) {
|
||||||
|
debug(
|
||||||
|
`Latest version from NDJSON cache: ${cachedLatestVersion.version}`
|
||||||
|
);
|
||||||
|
return cachedLatestVersion.version;
|
||||||
|
}
|
||||||
|
const latestVersion = await findVersionData(() => true);
|
||||||
if (!latestVersion) {
|
if (!latestVersion) {
|
||||||
throw new Error("No versions found in NDJSON data");
|
throw new Error("No versions found in NDJSON data");
|
||||||
}
|
}
|
||||||
debug(`Latest version from NDJSON: ${latestVersion}`);
|
debug(`Latest version from NDJSON: ${latestVersion.version}`);
|
||||||
return latestVersion;
|
return latestVersion.version;
|
||||||
|
}
|
||||||
|
async function findVersionData(predicate, url = VERSIONS_NDJSON_URL) {
|
||||||
|
const cachedVersions = cachedVersionData.get(url);
|
||||||
|
if (cachedVersions !== void 0) {
|
||||||
|
return cachedVersions.find(predicate);
|
||||||
|
}
|
||||||
|
const { matchedVersion, versions, complete } = await readVersionData(
|
||||||
|
url,
|
||||||
|
predicate
|
||||||
|
);
|
||||||
|
if (complete) {
|
||||||
|
cacheCompleteVersionData(url, versions);
|
||||||
|
}
|
||||||
|
return matchedVersion;
|
||||||
|
}
|
||||||
|
async function readVersionData(url, stopWhen) {
|
||||||
|
const response = await fetch(url, {});
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(
|
||||||
|
`Failed to fetch version data: ${response.status} ${response.statusText}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (response.body === null) {
|
||||||
|
const body = await response.text();
|
||||||
|
const versions2 = parseVersionData(body, url);
|
||||||
|
const matchedVersion2 = stopWhen ? versions2.find((candidate) => stopWhen(candidate)) : void 0;
|
||||||
|
return { complete: true, matchedVersion: matchedVersion2, versions: versions2 };
|
||||||
|
}
|
||||||
|
const versions = [];
|
||||||
|
let lineNumber = 0;
|
||||||
|
let matchedVersion;
|
||||||
|
let buffer = "";
|
||||||
|
const decoder = new TextDecoder();
|
||||||
|
const reader = response.body.getReader();
|
||||||
|
const processLine = (line) => {
|
||||||
|
const trimmed = line.trim();
|
||||||
|
if (trimmed === "") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
lineNumber += 1;
|
||||||
|
const versionData = parseVersionLine(trimmed, url, lineNumber);
|
||||||
|
if (versions.length === 0) {
|
||||||
|
cachedLatestVersionData.set(url, versionData);
|
||||||
|
}
|
||||||
|
versions.push(versionData);
|
||||||
|
cacheVersion(url, versionData);
|
||||||
|
if (stopWhen?.(versionData) === true) {
|
||||||
|
matchedVersion = versionData;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
while (true) {
|
||||||
|
const { done, value } = await reader.read();
|
||||||
|
if (done) {
|
||||||
|
buffer += decoder.decode();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
buffer += decoder.decode(value, { stream: true });
|
||||||
|
let newlineIndex = buffer.indexOf("\n");
|
||||||
|
while (newlineIndex !== -1) {
|
||||||
|
const line = buffer.slice(0, newlineIndex);
|
||||||
|
buffer = buffer.slice(newlineIndex + 1);
|
||||||
|
if (processLine(line)) {
|
||||||
|
await reader.cancel();
|
||||||
|
return { complete: false, matchedVersion, versions };
|
||||||
|
}
|
||||||
|
newlineIndex = buffer.indexOf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (buffer.trim() !== "" && processLine(buffer)) {
|
||||||
|
return { complete: true, matchedVersion, versions };
|
||||||
|
}
|
||||||
|
if (versions.length === 0) {
|
||||||
|
throw new Error(`No version data found in ${url}.`);
|
||||||
|
}
|
||||||
|
return { complete: true, matchedVersion, versions };
|
||||||
|
}
|
||||||
|
function cacheCompleteVersionData(url, versions) {
|
||||||
|
cachedVersionData.set(url, versions);
|
||||||
|
if (versions[0] !== void 0) {
|
||||||
|
cachedLatestVersionData.set(url, versions[0]);
|
||||||
|
}
|
||||||
|
const versionLookup = /* @__PURE__ */ new Map();
|
||||||
|
for (const versionData of versions) {
|
||||||
|
versionLookup.set(versionData.version, versionData);
|
||||||
|
}
|
||||||
|
cachedVersionLookup.set(url, versionLookup);
|
||||||
|
}
|
||||||
|
function cacheVersion(url, versionData) {
|
||||||
|
let versionLookup = cachedVersionLookup.get(url);
|
||||||
|
if (versionLookup === void 0) {
|
||||||
|
versionLookup = /* @__PURE__ */ new Map();
|
||||||
|
cachedVersionLookup.set(url, versionLookup);
|
||||||
|
}
|
||||||
|
versionLookup.set(versionData.version, versionData);
|
||||||
|
}
|
||||||
|
function parseVersionLine(line, sourceDescription, lineNumber) {
|
||||||
|
let parsed;
|
||||||
|
try {
|
||||||
|
parsed = JSON.parse(line);
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(
|
||||||
|
`Failed to parse version data from ${sourceDescription} at line ${lineNumber}: ${error.message}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!isNdjsonVersion(parsed)) {
|
||||||
|
throw new Error(
|
||||||
|
`Invalid NDJSON record in ${sourceDescription} at line ${lineNumber}.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return parsed;
|
||||||
}
|
}
|
||||||
function isNdjsonVersion(value) {
|
function isNdjsonVersion(value) {
|
||||||
if (!isRecord(value)) {
|
if (!isRecord(value)) {
|
||||||
@@ -49484,7 +50237,7 @@ async function run() {
|
|||||||
}
|
}
|
||||||
const latestVersion = await getLatestVersion();
|
const latestVersion = await getLatestVersion();
|
||||||
const latestKnownVersion = getLatestKnownVersionFromChecksums();
|
const latestKnownVersion = getLatestKnownVersionFromChecksums();
|
||||||
if (semver.lte(latestVersion, latestKnownVersion)) {
|
if (semver2.lte(latestVersion, latestKnownVersion)) {
|
||||||
info(
|
info(
|
||||||
`Latest release (${latestVersion}) is not newer than the latest known version (${latestKnownVersion}). Skipping update.`
|
`Latest release (${latestVersion}) is not newer than the latest known version (${latestKnownVersion}). Skipping update.`
|
||||||
);
|
);
|
||||||
@@ -49503,7 +50256,7 @@ function getLatestKnownVersionFromChecksums() {
|
|||||||
versions.add(version);
|
versions.add(version);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const latestVersion = [...versions].sort(semver.rcompare)[0];
|
const latestVersion = [...versions].sort(semver2.rcompare)[0];
|
||||||
if (!latestVersion) {
|
if (!latestVersion) {
|
||||||
throw new Error("Could not determine latest known version from checksums.");
|
throw new Error("Could not determine latest known version from checksums.");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
"build": "tsc --noEmit",
|
"build": "tsc --noEmit",
|
||||||
"check": "biome check --write",
|
"check": "biome check --write",
|
||||||
"package": "node scripts/build-dist.mjs",
|
"package": "node scripts/build-dist.mjs",
|
||||||
|
"bench:versions": "node scripts/bench-versions-client.mjs",
|
||||||
"test:unit": "node --experimental-vm-modules ./node_modules/jest/bin/jest.js",
|
"test:unit": "node --experimental-vm-modules ./node_modules/jest/bin/jest.js",
|
||||||
"test": "npm run build && npm run test:unit",
|
"test": "npm run build && npm run test:unit",
|
||||||
"act": "act pull_request -W .github/workflows/test.yml --container-architecture linux/amd64 -s GITHUB_TOKEN=\"$(gh auth token)\"",
|
"act": "act pull_request -W .github/workflows/test.yml --container-architecture linux/amd64 -s GITHUB_TOKEN=\"$(gh auth token)\"",
|
||||||
|
|||||||
483
scripts/bench-versions-client.mjs
Normal file
483
scripts/bench-versions-client.mjs
Normal file
@@ -0,0 +1,483 @@
|
|||||||
|
import { performance } from "node:perf_hooks";
|
||||||
|
import * as pep440 from "@renovatebot/pep440";
|
||||||
|
import * as semver from "semver";
|
||||||
|
import { ProxyAgent, fetch as undiciFetch } from "undici";
|
||||||
|
|
||||||
|
const DEFAULT_URL =
|
||||||
|
"https://raw.githubusercontent.com/astral-sh/versions/main/v1/uv.ndjson";
|
||||||
|
const DEFAULT_ITERATIONS = 100;
|
||||||
|
const DEFAULT_ARCH = "aarch64";
|
||||||
|
const DEFAULT_PLATFORM = "apple-darwin";
|
||||||
|
|
||||||
|
function getProxyAgent() {
|
||||||
|
const httpProxy = process.env.HTTP_PROXY || process.env.http_proxy;
|
||||||
|
if (httpProxy) {
|
||||||
|
return new ProxyAgent(httpProxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
const httpsProxy = process.env.HTTPS_PROXY || process.env.https_proxy;
|
||||||
|
if (httpsProxy) {
|
||||||
|
return new ProxyAgent(httpsProxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetch(url) {
|
||||||
|
return await undiciFetch(url, {
|
||||||
|
dispatcher: getProxyAgent(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseArgs(argv) {
|
||||||
|
const options = {
|
||||||
|
arch: DEFAULT_ARCH,
|
||||||
|
iterations: DEFAULT_ITERATIONS,
|
||||||
|
platform: DEFAULT_PLATFORM,
|
||||||
|
url: DEFAULT_URL,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (let index = 0; index < argv.length; index += 1) {
|
||||||
|
const arg = argv[index];
|
||||||
|
const next = argv[index + 1];
|
||||||
|
|
||||||
|
if (arg === "--iterations" && next !== undefined) {
|
||||||
|
options.iterations = Number.parseInt(next, 10);
|
||||||
|
index += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg === "--url" && next !== undefined) {
|
||||||
|
options.url = next;
|
||||||
|
index += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg === "--arch" && next !== undefined) {
|
||||||
|
options.arch = next;
|
||||||
|
index += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg === "--platform" && next !== undefined) {
|
||||||
|
options.platform = next;
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Number.isInteger(options.iterations) || options.iterations <= 0) {
|
||||||
|
throw new Error("--iterations must be a positive integer");
|
||||||
|
}
|
||||||
|
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseVersionLine(line, sourceDescription, lineNumber) {
|
||||||
|
let parsed;
|
||||||
|
try {
|
||||||
|
parsed = JSON.parse(line);
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(
|
||||||
|
`Failed to parse version data from ${sourceDescription} at line ${lineNumber}: ${error.message}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
typeof parsed !== "object" ||
|
||||||
|
parsed === null ||
|
||||||
|
typeof parsed.version !== "string" ||
|
||||||
|
!Array.isArray(parsed.artifacts)
|
||||||
|
) {
|
||||||
|
throw new Error(
|
||||||
|
`Invalid NDJSON record in ${sourceDescription} at line ${lineNumber}.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseVersionData(data, sourceDescription) {
|
||||||
|
const versions = [];
|
||||||
|
|
||||||
|
for (const [index, line] of data.split("\n").entries()) {
|
||||||
|
const trimmed = line.trim();
|
||||||
|
if (trimmed === "") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
versions.push(parseVersionLine(trimmed, sourceDescription, index + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (versions.length === 0) {
|
||||||
|
throw new Error(`No version data found in ${sourceDescription}.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return versions;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function readEntireResponse(response) {
|
||||||
|
if (response.body === null) {
|
||||||
|
const text = await response.text();
|
||||||
|
return {
|
||||||
|
bytesRead: Buffer.byteLength(text, "utf8"),
|
||||||
|
text,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const reader = response.body.getReader();
|
||||||
|
const decoder = new TextDecoder();
|
||||||
|
const chunks = [];
|
||||||
|
let bytesRead = 0;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const { done, value } = await reader.read();
|
||||||
|
if (done) {
|
||||||
|
chunks.push(decoder.decode());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytesRead += value.byteLength;
|
||||||
|
chunks.push(decoder.decode(value, { stream: true }));
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
bytesRead,
|
||||||
|
text: chunks.join(""),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchAllVersions(url) {
|
||||||
|
const response = await fetch(url);
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(
|
||||||
|
`Failed to fetch version data: ${response.status} ${response.statusText}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { bytesRead, text } = await readEntireResponse(response);
|
||||||
|
return {
|
||||||
|
bytesRead,
|
||||||
|
versions: parseVersionData(text, url),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function streamUntil(url, predicate) {
|
||||||
|
const response = await fetch(url);
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(
|
||||||
|
`Failed to fetch version data: ${response.status} ${response.statusText}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.body === null) {
|
||||||
|
const { bytesRead, versions } = await fetchAllVersions(url);
|
||||||
|
return {
|
||||||
|
bytesRead,
|
||||||
|
matchedVersion: versions.find(predicate),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const reader = response.body.getReader();
|
||||||
|
const decoder = new TextDecoder();
|
||||||
|
let bytesRead = 0;
|
||||||
|
let buffer = "";
|
||||||
|
let lineNumber = 0;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const { done, value } = await reader.read();
|
||||||
|
if (done) {
|
||||||
|
buffer += decoder.decode();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytesRead += value.byteLength;
|
||||||
|
buffer += decoder.decode(value, { stream: true });
|
||||||
|
|
||||||
|
let newlineIndex = buffer.indexOf("\n");
|
||||||
|
while (newlineIndex !== -1) {
|
||||||
|
const line = buffer.slice(0, newlineIndex);
|
||||||
|
buffer = buffer.slice(newlineIndex + 1);
|
||||||
|
const trimmed = line.trim();
|
||||||
|
|
||||||
|
if (trimmed !== "") {
|
||||||
|
lineNumber += 1;
|
||||||
|
const versionData = parseVersionLine(trimmed, url, lineNumber);
|
||||||
|
if (predicate(versionData)) {
|
||||||
|
await reader.cancel();
|
||||||
|
return { bytesRead, matchedVersion: versionData };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newlineIndex = buffer.indexOf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer.trim() !== "") {
|
||||||
|
lineNumber += 1;
|
||||||
|
const versionData = parseVersionLine(buffer.trim(), url, lineNumber);
|
||||||
|
if (predicate(versionData)) {
|
||||||
|
return { bytesRead, matchedVersion: versionData };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { bytesRead, matchedVersion: undefined };
|
||||||
|
}
|
||||||
|
|
||||||
|
function versionSatisfies(version, versionSpecifier) {
|
||||||
|
return (
|
||||||
|
semver.satisfies(version, versionSpecifier) ||
|
||||||
|
pep440.satisfies(version, versionSpecifier)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function maxSatisfying(versions, versionSpecifier) {
|
||||||
|
const semverMatch = semver.maxSatisfying(versions, versionSpecifier);
|
||||||
|
if (semverMatch !== null) {
|
||||||
|
return semverMatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pep440.maxSatisfying(versions, versionSpecifier) ?? undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectArtifact(artifacts) {
|
||||||
|
if (artifacts.length === 1) {
|
||||||
|
return artifacts[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultVariant = artifacts.find(
|
||||||
|
(candidate) => candidate.variant === "default",
|
||||||
|
);
|
||||||
|
if (defaultVariant !== undefined) {
|
||||||
|
return defaultVariant;
|
||||||
|
}
|
||||||
|
|
||||||
|
return artifacts[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
async function benchmarkCase(name, expected, implementations, iterations) {
|
||||||
|
const results = {
|
||||||
|
name,
|
||||||
|
new: [],
|
||||||
|
old: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
for (let iteration = 0; iteration < iterations; iteration += 1) {
|
||||||
|
const order = iteration % 2 === 0 ? ["old", "new"] : ["new", "old"];
|
||||||
|
|
||||||
|
for (const label of order) {
|
||||||
|
const implementation = implementations[label];
|
||||||
|
const startedAt = performance.now();
|
||||||
|
const outcome = await implementation.run();
|
||||||
|
const durationMs = performance.now() - startedAt;
|
||||||
|
|
||||||
|
if (outcome.value !== expected) {
|
||||||
|
throw new Error(
|
||||||
|
`${name} ${label} produced ${JSON.stringify(outcome.value)}; expected ${JSON.stringify(expected)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
results[label].push({
|
||||||
|
bytesRead: outcome.bytesRead,
|
||||||
|
durationMs,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
function summarize(samples) {
|
||||||
|
const durations = samples
|
||||||
|
.map((sample) => sample.durationMs)
|
||||||
|
.sort((left, right) => left - right);
|
||||||
|
const bytes = samples
|
||||||
|
.map((sample) => sample.bytesRead)
|
||||||
|
.sort((left, right) => left - right);
|
||||||
|
|
||||||
|
const sum = (values) => values.reduce((total, value) => total + value, 0);
|
||||||
|
const percentile = (values, ratio) => {
|
||||||
|
const index = Math.min(
|
||||||
|
values.length - 1,
|
||||||
|
Math.max(0, Math.ceil(values.length * ratio) - 1),
|
||||||
|
);
|
||||||
|
return values[index];
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
avgBytes: sum(bytes) / bytes.length,
|
||||||
|
avgMs: sum(durations) / durations.length,
|
||||||
|
maxMs: durations[durations.length - 1],
|
||||||
|
medianMs: percentile(durations, 0.5),
|
||||||
|
minMs: durations[0],
|
||||||
|
p95Ms: percentile(durations, 0.95),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatNumber(value, digits = 2) {
|
||||||
|
return value.toFixed(digits);
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatSummary(name, oldSummary, newSummary) {
|
||||||
|
const speedup = oldSummary.avgMs / newSummary.avgMs;
|
||||||
|
const timeReduction =
|
||||||
|
((oldSummary.avgMs - newSummary.avgMs) / oldSummary.avgMs) * 100;
|
||||||
|
const byteReduction =
|
||||||
|
((oldSummary.avgBytes - newSummary.avgBytes) / oldSummary.avgBytes) * 100;
|
||||||
|
|
||||||
|
return [
|
||||||
|
`Scenario: ${name}`,
|
||||||
|
` old avg: ${formatNumber(oldSummary.avgMs)} ms | median: ${formatNumber(oldSummary.medianMs)} ms | p95: ${formatNumber(oldSummary.p95Ms)} ms | avg bytes: ${Math.round(oldSummary.avgBytes)}`,
|
||||||
|
` new avg: ${formatNumber(newSummary.avgMs)} ms | median: ${formatNumber(newSummary.medianMs)} ms | p95: ${formatNumber(newSummary.p95Ms)} ms | avg bytes: ${Math.round(newSummary.avgBytes)}`,
|
||||||
|
` delta: ${formatNumber(timeReduction)}% faster | ${formatNumber(speedup)}x speedup | ${formatNumber(byteReduction)}% fewer bytes read`,
|
||||||
|
].join("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const options = parseArgs(process.argv.slice(2));
|
||||||
|
console.log(`Preparing benchmark data from ${options.url}`);
|
||||||
|
const baseline = await fetchAllVersions(options.url);
|
||||||
|
const latestVersion = baseline.versions[0]?.version;
|
||||||
|
if (!latestVersion) {
|
||||||
|
throw new Error("No versions found in NDJSON data");
|
||||||
|
}
|
||||||
|
|
||||||
|
const latestArtifact = selectArtifact(
|
||||||
|
baseline.versions[0].artifacts.filter(
|
||||||
|
(candidate) =>
|
||||||
|
candidate.platform === `${options.arch}-${options.platform}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (!latestArtifact) {
|
||||||
|
throw new Error(
|
||||||
|
`No artifact found for ${options.arch}-${options.platform} in ${latestVersion}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const rangeSpecifier = `^${latestVersion.split(".")[0]}.${latestVersion.split(".")[1]}.0`;
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`Running ${options.iterations} iterations per scenario against ${options.url}`,
|
||||||
|
);
|
||||||
|
console.log(`Latest version: ${latestVersion}`);
|
||||||
|
console.log(`Range benchmark: ${rangeSpecifier}`);
|
||||||
|
console.log(`Artifact benchmark: ${options.arch}-${options.platform}`);
|
||||||
|
console.log("");
|
||||||
|
|
||||||
|
const scenarios = [
|
||||||
|
await benchmarkCase(
|
||||||
|
"latest version",
|
||||||
|
latestVersion,
|
||||||
|
{
|
||||||
|
new: {
|
||||||
|
run: async () => {
|
||||||
|
const { bytesRead, matchedVersion } = await streamUntil(
|
||||||
|
options.url,
|
||||||
|
() => true,
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
bytesRead,
|
||||||
|
value: matchedVersion?.version,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
old: {
|
||||||
|
run: async () => {
|
||||||
|
const { bytesRead, versions } = await fetchAllVersions(options.url);
|
||||||
|
return {
|
||||||
|
bytesRead,
|
||||||
|
value: versions[0]?.version,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options.iterations,
|
||||||
|
),
|
||||||
|
await benchmarkCase(
|
||||||
|
"highest satisfying range",
|
||||||
|
latestVersion,
|
||||||
|
{
|
||||||
|
new: {
|
||||||
|
run: async () => {
|
||||||
|
const { bytesRead, matchedVersion } = await streamUntil(
|
||||||
|
options.url,
|
||||||
|
(candidate) =>
|
||||||
|
versionSatisfies(candidate.version, rangeSpecifier),
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
bytesRead,
|
||||||
|
value: matchedVersion?.version,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
old: {
|
||||||
|
run: async () => {
|
||||||
|
const { bytesRead, versions } = await fetchAllVersions(options.url);
|
||||||
|
return {
|
||||||
|
bytesRead,
|
||||||
|
value: maxSatisfying(
|
||||||
|
versions.map((versionData) => versionData.version),
|
||||||
|
rangeSpecifier,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options.iterations,
|
||||||
|
),
|
||||||
|
await benchmarkCase(
|
||||||
|
"exact version artifact",
|
||||||
|
latestArtifact.url,
|
||||||
|
{
|
||||||
|
new: {
|
||||||
|
run: async () => {
|
||||||
|
const { bytesRead, matchedVersion } = await streamUntil(
|
||||||
|
options.url,
|
||||||
|
(candidate) => candidate.version === latestVersion,
|
||||||
|
);
|
||||||
|
const artifact = matchedVersion
|
||||||
|
? selectArtifact(
|
||||||
|
matchedVersion.artifacts.filter(
|
||||||
|
(candidate) =>
|
||||||
|
candidate.platform ===
|
||||||
|
`${options.arch}-${options.platform}`,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: undefined;
|
||||||
|
return {
|
||||||
|
bytesRead,
|
||||||
|
value: artifact?.url,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
old: {
|
||||||
|
run: async () => {
|
||||||
|
const { bytesRead, versions } = await fetchAllVersions(options.url);
|
||||||
|
const versionData = versions.find(
|
||||||
|
(candidate) => candidate.version === latestVersion,
|
||||||
|
);
|
||||||
|
const artifact = selectArtifact(
|
||||||
|
versionData.artifacts.filter(
|
||||||
|
(candidate) =>
|
||||||
|
candidate.platform === `${options.arch}-${options.platform}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
bytesRead,
|
||||||
|
value: artifact?.url,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options.iterations,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const scenario of scenarios) {
|
||||||
|
const oldSummary = summarize(scenario.old);
|
||||||
|
const newSummary = summarize(scenario.new);
|
||||||
|
console.log(formatSummary(scenario.name, oldSummary, newSummary));
|
||||||
|
console.log("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await main();
|
||||||
@@ -15,6 +15,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
getAllVersions as getAllVersionsFromNdjson,
|
getAllVersions as getAllVersionsFromNdjson,
|
||||||
getArtifact as getArtifactFromNdjson,
|
getArtifact as getArtifactFromNdjson,
|
||||||
|
getHighestSatisfyingVersion as getHighestSatisfyingVersionFromNdjson,
|
||||||
getLatestVersion as getLatestVersionFromNdjson,
|
getLatestVersion as getLatestVersionFromNdjson,
|
||||||
} from "./versions-client";
|
} from "./versions-client";
|
||||||
|
|
||||||
@@ -187,6 +188,17 @@ export async function resolveVersion(
|
|||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (manifestUrl === undefined && resolutionStrategy === "highest") {
|
||||||
|
const resolvedVersion =
|
||||||
|
await getHighestSatisfyingVersionFromNdjson(version);
|
||||||
|
if (resolvedVersion !== undefined) {
|
||||||
|
core.debug(`Resolved version from NDJSON stream: ${resolvedVersion}`);
|
||||||
|
return resolvedVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`No version found for ${version}`);
|
||||||
|
}
|
||||||
|
|
||||||
const availableVersions = await getAvailableVersions(manifestUrl);
|
const availableVersions = await getAvailableVersions(manifestUrl);
|
||||||
core.debug(`Available versions: ${availableVersions}`);
|
core.debug(`Available versions: ${availableVersions}`);
|
||||||
const resolvedVersion =
|
const resolvedVersion =
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import * as core from "@actions/core";
|
import * as core from "@actions/core";
|
||||||
|
import * as pep440 from "@renovatebot/pep440";
|
||||||
|
import * as semver from "semver";
|
||||||
import { VERSIONS_NDJSON_URL } from "../utils/constants";
|
import { VERSIONS_NDJSON_URL } from "../utils/constants";
|
||||||
import { fetch } from "../utils/fetch";
|
import { fetch } from "../utils/fetch";
|
||||||
import { selectDefaultVariant } from "./variant-selection";
|
import { selectDefaultVariant } from "./variant-selection";
|
||||||
@@ -23,6 +25,8 @@ export interface ArtifactResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const cachedVersionData = new Map<string, NdjsonVersion[]>();
|
const cachedVersionData = new Map<string, NdjsonVersion[]>();
|
||||||
|
const cachedLatestVersionData = new Map<string, NdjsonVersion>();
|
||||||
|
const cachedVersionLookup = new Map<string, Map<string, NdjsonVersion>>();
|
||||||
|
|
||||||
export async function fetchVersionData(
|
export async function fetchVersionData(
|
||||||
url: string = VERSIONS_NDJSON_URL,
|
url: string = VERSIONS_NDJSON_URL,
|
||||||
@@ -34,16 +38,8 @@ export async function fetchVersionData(
|
|||||||
}
|
}
|
||||||
|
|
||||||
core.info(`Fetching version data from ${url} ...`);
|
core.info(`Fetching version data from ${url} ...`);
|
||||||
const response = await fetch(url, {});
|
const { versions } = await readVersionData(url);
|
||||||
if (!response.ok) {
|
cacheCompleteVersionData(url, versions);
|
||||||
throw new Error(
|
|
||||||
`Failed to fetch version data: ${response.status} ${response.statusText}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const body = await response.text();
|
|
||||||
const versions = parseVersionData(body, url);
|
|
||||||
cachedVersionData.set(url, versions);
|
|
||||||
return versions;
|
return versions;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,22 +55,7 @@ export function parseVersionData(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let parsed: unknown;
|
versions.push(parseVersionLine(trimmed, sourceDescription, index + 1));
|
||||||
try {
|
|
||||||
parsed = JSON.parse(trimmed);
|
|
||||||
} catch (error) {
|
|
||||||
throw new Error(
|
|
||||||
`Failed to parse version data from ${sourceDescription} at line ${index + 1}: ${(error as Error).message}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isNdjsonVersion(parsed)) {
|
|
||||||
throw new Error(
|
|
||||||
`Invalid NDJSON record in ${sourceDescription} at line ${index + 1}.`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
versions.push(parsed);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (versions.length === 0) {
|
if (versions.length === 0) {
|
||||||
@@ -85,14 +66,23 @@ export function parseVersionData(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getLatestVersion(): Promise<string> {
|
export async function getLatestVersion(): Promise<string> {
|
||||||
const versions = await fetchVersionData();
|
const cachedVersions = cachedVersionData.get(VERSIONS_NDJSON_URL);
|
||||||
const latestVersion = versions[0]?.version;
|
const cachedLatestVersion =
|
||||||
|
cachedVersions?.[0] ?? cachedLatestVersionData.get(VERSIONS_NDJSON_URL);
|
||||||
|
if (cachedLatestVersion !== undefined) {
|
||||||
|
core.debug(
|
||||||
|
`Latest version from NDJSON cache: ${cachedLatestVersion.version}`,
|
||||||
|
);
|
||||||
|
return cachedLatestVersion.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
const latestVersion = await findVersionData(() => true);
|
||||||
if (!latestVersion) {
|
if (!latestVersion) {
|
||||||
throw new Error("No versions found in NDJSON data");
|
throw new Error("No versions found in NDJSON data");
|
||||||
}
|
}
|
||||||
|
|
||||||
core.debug(`Latest version from NDJSON: ${latestVersion}`);
|
core.debug(`Latest version from NDJSON: ${latestVersion.version}`);
|
||||||
return latestVersion;
|
return latestVersion.version;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getAllVersions(): Promise<string[]> {
|
export async function getAllVersions(): Promise<string[]> {
|
||||||
@@ -100,15 +90,24 @@ export async function getAllVersions(): Promise<string[]> {
|
|||||||
return versions.map((versionData) => versionData.version);
|
return versions.map((versionData) => versionData.version);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getHighestSatisfyingVersion(
|
||||||
|
versionSpecifier: string,
|
||||||
|
url: string = VERSIONS_NDJSON_URL,
|
||||||
|
): Promise<string | undefined> {
|
||||||
|
const matchedVersion = await findVersionData(
|
||||||
|
(candidate) => versionSatisfies(candidate.version, versionSpecifier),
|
||||||
|
url,
|
||||||
|
);
|
||||||
|
|
||||||
|
return matchedVersion?.version;
|
||||||
|
}
|
||||||
|
|
||||||
export async function getArtifact(
|
export async function getArtifact(
|
||||||
version: string,
|
version: string,
|
||||||
arch: string,
|
arch: string,
|
||||||
platform: string,
|
platform: string,
|
||||||
): Promise<ArtifactResult | undefined> {
|
): Promise<ArtifactResult | undefined> {
|
||||||
const versions = await fetchVersionData();
|
const versionData = await getVersionData(version);
|
||||||
const versionData = versions.find(
|
|
||||||
(candidate) => candidate.version === version,
|
|
||||||
);
|
|
||||||
if (!versionData) {
|
if (!versionData) {
|
||||||
core.debug(`Version ${version} not found in NDJSON data`);
|
core.debug(`Version ${version} not found in NDJSON data`);
|
||||||
return undefined;
|
return undefined;
|
||||||
@@ -140,10 +139,14 @@ export async function getArtifact(
|
|||||||
export function clearCache(url?: string): void {
|
export function clearCache(url?: string): void {
|
||||||
if (url === undefined) {
|
if (url === undefined) {
|
||||||
cachedVersionData.clear();
|
cachedVersionData.clear();
|
||||||
|
cachedLatestVersionData.clear();
|
||||||
|
cachedVersionLookup.clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
cachedVersionData.delete(url);
|
cachedVersionData.delete(url);
|
||||||
|
cachedLatestVersionData.delete(url);
|
||||||
|
cachedVersionLookup.delete(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectArtifact(
|
function selectArtifact(
|
||||||
@@ -157,6 +160,192 @@ function selectArtifact(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getVersionData(
|
||||||
|
version: string,
|
||||||
|
url: string = VERSIONS_NDJSON_URL,
|
||||||
|
): Promise<NdjsonVersion | undefined> {
|
||||||
|
const cachedVersions = cachedVersionData.get(url);
|
||||||
|
if (cachedVersions !== undefined) {
|
||||||
|
return cachedVersions.find((candidate) => candidate.version === version);
|
||||||
|
}
|
||||||
|
|
||||||
|
const cachedVersion = cachedVersionLookup.get(url)?.get(version);
|
||||||
|
if (cachedVersion !== undefined) {
|
||||||
|
return cachedVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
return await findVersionData(
|
||||||
|
(candidate) => candidate.version === version,
|
||||||
|
url,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function findVersionData(
|
||||||
|
predicate: (versionData: NdjsonVersion) => boolean,
|
||||||
|
url: string = VERSIONS_NDJSON_URL,
|
||||||
|
): Promise<NdjsonVersion | undefined> {
|
||||||
|
const cachedVersions = cachedVersionData.get(url);
|
||||||
|
if (cachedVersions !== undefined) {
|
||||||
|
return cachedVersions.find(predicate);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { matchedVersion, versions, complete } = await readVersionData(
|
||||||
|
url,
|
||||||
|
predicate,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (complete) {
|
||||||
|
cacheCompleteVersionData(url, versions);
|
||||||
|
}
|
||||||
|
|
||||||
|
return matchedVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function readVersionData(
|
||||||
|
url: string,
|
||||||
|
stopWhen?: (versionData: NdjsonVersion) => boolean,
|
||||||
|
): Promise<{
|
||||||
|
complete: boolean;
|
||||||
|
matchedVersion: NdjsonVersion | undefined;
|
||||||
|
versions: NdjsonVersion[];
|
||||||
|
}> {
|
||||||
|
const response = await fetch(url, {});
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(
|
||||||
|
`Failed to fetch version data: ${response.status} ${response.statusText}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.body === null) {
|
||||||
|
const body = await response.text();
|
||||||
|
const versions = parseVersionData(body, url);
|
||||||
|
const matchedVersion = stopWhen
|
||||||
|
? versions.find((candidate) => stopWhen(candidate))
|
||||||
|
: undefined;
|
||||||
|
return { complete: true, matchedVersion, versions };
|
||||||
|
}
|
||||||
|
|
||||||
|
const versions: NdjsonVersion[] = [];
|
||||||
|
let lineNumber = 0;
|
||||||
|
let matchedVersion: NdjsonVersion | undefined;
|
||||||
|
let buffer = "";
|
||||||
|
const decoder = new TextDecoder();
|
||||||
|
const reader = response.body.getReader();
|
||||||
|
|
||||||
|
const processLine = (line: string): boolean => {
|
||||||
|
const trimmed = line.trim();
|
||||||
|
if (trimmed === "") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
lineNumber += 1;
|
||||||
|
const versionData = parseVersionLine(trimmed, url, lineNumber);
|
||||||
|
if (versions.length === 0) {
|
||||||
|
cachedLatestVersionData.set(url, versionData);
|
||||||
|
}
|
||||||
|
|
||||||
|
versions.push(versionData);
|
||||||
|
cacheVersion(url, versionData);
|
||||||
|
|
||||||
|
if (stopWhen?.(versionData) === true) {
|
||||||
|
matchedVersion = versionData;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const { done, value } = await reader.read();
|
||||||
|
if (done) {
|
||||||
|
buffer += decoder.decode();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer += decoder.decode(value, { stream: true });
|
||||||
|
let newlineIndex = buffer.indexOf("\n");
|
||||||
|
while (newlineIndex !== -1) {
|
||||||
|
const line = buffer.slice(0, newlineIndex);
|
||||||
|
buffer = buffer.slice(newlineIndex + 1);
|
||||||
|
|
||||||
|
if (processLine(line)) {
|
||||||
|
await reader.cancel();
|
||||||
|
return { complete: false, matchedVersion, versions };
|
||||||
|
}
|
||||||
|
|
||||||
|
newlineIndex = buffer.indexOf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer.trim() !== "" && processLine(buffer)) {
|
||||||
|
return { complete: true, matchedVersion, versions };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (versions.length === 0) {
|
||||||
|
throw new Error(`No version data found in ${url}.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { complete: true, matchedVersion, versions };
|
||||||
|
}
|
||||||
|
|
||||||
|
function cacheCompleteVersionData(
|
||||||
|
url: string,
|
||||||
|
versions: NdjsonVersion[],
|
||||||
|
): void {
|
||||||
|
cachedVersionData.set(url, versions);
|
||||||
|
|
||||||
|
if (versions[0] !== undefined) {
|
||||||
|
cachedLatestVersionData.set(url, versions[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const versionLookup = new Map<string, NdjsonVersion>();
|
||||||
|
for (const versionData of versions) {
|
||||||
|
versionLookup.set(versionData.version, versionData);
|
||||||
|
}
|
||||||
|
|
||||||
|
cachedVersionLookup.set(url, versionLookup);
|
||||||
|
}
|
||||||
|
|
||||||
|
function cacheVersion(url: string, versionData: NdjsonVersion): void {
|
||||||
|
let versionLookup = cachedVersionLookup.get(url);
|
||||||
|
if (versionLookup === undefined) {
|
||||||
|
versionLookup = new Map<string, NdjsonVersion>();
|
||||||
|
cachedVersionLookup.set(url, versionLookup);
|
||||||
|
}
|
||||||
|
|
||||||
|
versionLookup.set(versionData.version, versionData);
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseVersionLine(
|
||||||
|
line: string,
|
||||||
|
sourceDescription: string,
|
||||||
|
lineNumber: number,
|
||||||
|
): NdjsonVersion {
|
||||||
|
let parsed: unknown;
|
||||||
|
try {
|
||||||
|
parsed = JSON.parse(line);
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(
|
||||||
|
`Failed to parse version data from ${sourceDescription} at line ${lineNumber}: ${(error as Error).message}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isNdjsonVersion(parsed)) {
|
||||||
|
throw new Error(
|
||||||
|
`Invalid NDJSON record in ${sourceDescription} at line ${lineNumber}.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
function versionSatisfies(version: string, versionSpecifier: string): boolean {
|
||||||
|
return (
|
||||||
|
semver.satisfies(version, versionSpecifier) ||
|
||||||
|
pep440.satisfies(version, versionSpecifier)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function isNdjsonVersion(value: unknown): value is NdjsonVersion {
|
function isNdjsonVersion(value: unknown): value is NdjsonVersion {
|
||||||
if (!isRecord(value)) {
|
if (!isRecord(value)) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
Reference in New Issue
Block a user