mirror of
https://github.com/astral-sh/setup-uv.git
synced 2026-11-05 01:35:32 +00:00
Use Astral mirror for version manifests with GitHub fallback (#1119)
Amp-Thread-ID: https://ampcode.com/threads/T-01a1822a-68da-7125-9e7d-16e038ab0840 Closes: #1118 Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
@@ -242,7 +242,7 @@ describe("download-version", () => {
|
||||
"token",
|
||||
),
|
||||
).rejects.toThrow(
|
||||
"Could not find artifact for version 0.9.26, arch x86_64, platform unknown-linux-gnu in https://raw.githubusercontent.com/astral-sh/versions/main/v1/uv.ndjson .",
|
||||
"Could not find artifact for version 0.9.26, arch x86_64, platform unknown-linux-gnu in https://releases.astral.sh/github/versions/main/v1/uv.ndjson .",
|
||||
);
|
||||
|
||||
expect(mockDownloadTool).not.toHaveBeenCalled();
|
||||
|
||||
@@ -96,6 +96,78 @@ describe("manifest", () => {
|
||||
expect(versions).toHaveLength(2);
|
||||
expect(versions[0]?.version).toBe("0.9.26");
|
||||
expect(versions[1]?.version).toBe("0.9.25");
|
||||
expect(mockFetch.mock.calls.map(([url]) => url)).toEqual([
|
||||
"https://releases.astral.sh/github/versions/main/v1/uv.ndjson",
|
||||
]);
|
||||
});
|
||||
|
||||
it("falls back to GitHub after mirror network retries are exhausted", async () => {
|
||||
jest.useFakeTimers();
|
||||
mockFetch
|
||||
.mockRejectedValueOnce(new Error("mirror unavailable"))
|
||||
.mockRejectedValueOnce(new Error("mirror unavailable"))
|
||||
.mockRejectedValueOnce(new Error("mirror unavailable"))
|
||||
.mockResolvedValueOnce(
|
||||
createMockResponse(true, 200, "OK", sampleManifestResponse),
|
||||
);
|
||||
|
||||
const result = fetchManifest();
|
||||
await jest.runAllTimersAsync();
|
||||
|
||||
await expect(result).resolves.toEqual([
|
||||
expect.objectContaining({ version: "0.9.26" }),
|
||||
expect.objectContaining({ version: "0.9.25" }),
|
||||
]);
|
||||
expect(mockFetch.mock.calls.map(([url]) => url)).toEqual([
|
||||
"https://releases.astral.sh/github/versions/main/v1/uv.ndjson",
|
||||
"https://releases.astral.sh/github/versions/main/v1/uv.ndjson",
|
||||
"https://releases.astral.sh/github/versions/main/v1/uv.ndjson",
|
||||
"https://raw.githubusercontent.com/astral-sh/versions/main/v1/uv.ndjson",
|
||||
]);
|
||||
|
||||
await expect(getLatestVersion()).resolves.toBe("0.9.26");
|
||||
expect(mockFetch).toHaveBeenCalledTimes(4);
|
||||
});
|
||||
|
||||
it.each([404, 429, 503])(
|
||||
"falls back to a streaming GitHub manifest on mirror HTTP %s",
|
||||
async (status) => {
|
||||
mockFetch
|
||||
.mockResolvedValueOnce(createMockResponse(false, status, "Error", ""))
|
||||
.mockResolvedValueOnce(
|
||||
createStreamingMockResponse([`${sampleManifestResponse}\n`]),
|
||||
);
|
||||
|
||||
await expect(getLatestVersion()).resolves.toBe("0.9.26");
|
||||
await expect(
|
||||
getArtifact("0.9.26", "aarch64", "apple-darwin"),
|
||||
).resolves.toEqual({
|
||||
archiveFormat: "tar.gz",
|
||||
checksum:
|
||||
"fcf0a9ea6599c6ae28a4c854ac6da76f2c889354d7c36ce136ef071f7ab9721f",
|
||||
downloadUrl:
|
||||
"https://github.com/astral-sh/uv/releases/download/0.9.26/uv-aarch64-apple-darwin.tar.gz",
|
||||
});
|
||||
expect(mockFetch.mock.calls.map(([url]) => url)).toEqual([
|
||||
"https://releases.astral.sh/github/versions/main/v1/uv.ndjson",
|
||||
"https://raw.githubusercontent.com/astral-sh/versions/main/v1/uv.ndjson",
|
||||
]);
|
||||
},
|
||||
);
|
||||
|
||||
it("does not fall back from a custom manifest", async () => {
|
||||
mockFetch.mockResolvedValue(
|
||||
createMockResponse(false, 503, "Service Unavailable", ""),
|
||||
);
|
||||
|
||||
await expect(
|
||||
fetchManifest("https://example.com/custom.ndjson"),
|
||||
).rejects.toThrow(
|
||||
"Failed to fetch manifest data: 503 Service Unavailable",
|
||||
);
|
||||
expect(mockFetch.mock.calls.map(([url]) => url)).toEqual([
|
||||
"https://example.com/custom.ndjson",
|
||||
]);
|
||||
});
|
||||
|
||||
it("retries network failures", async () => {
|
||||
@@ -123,7 +195,7 @@ describe("manifest", () => {
|
||||
await jest.runAllTimersAsync();
|
||||
|
||||
await result;
|
||||
expect(mockFetch).toHaveBeenCalledTimes(MANIFEST_FETCH_ATTEMPTS);
|
||||
expect(mockFetch).toHaveBeenCalledTimes(MANIFEST_FETCH_ATTEMPTS * 2);
|
||||
});
|
||||
|
||||
it("throws on a failed fetch", async () => {
|
||||
|
||||
+15
-1
@@ -94499,7 +94499,8 @@ function _getGlobal(key, defaultValue) {
|
||||
var TOOL_CACHE_NAME = "uv";
|
||||
var STATE_UV_PATH = "uv-path";
|
||||
var STATE_UV_VERSION = "uv-version";
|
||||
var VERSIONS_MANIFEST_URL = "https://raw.githubusercontent.com/astral-sh/versions/main/v1/uv.ndjson";
|
||||
var VERSIONS_MANIFEST_URL = "https://releases.astral.sh/github/versions/main/v1/uv.ndjson";
|
||||
var GITHUB_VERSIONS_MANIFEST_URL = "https://raw.githubusercontent.com/astral-sh/versions/main/v1/uv.ndjson";
|
||||
var GITHUB_RELEASES_PREFIX = "https://github.com/astral-sh/uv/releases/download/";
|
||||
var ASTRAL_MIRROR_PREFIX = "https://releases.astral.sh/github/uv/releases/download/";
|
||||
|
||||
@@ -100305,6 +100306,19 @@ async function getArtifact(version3, arch3, platform2, manifestUrl = VERSIONS_MA
|
||||
};
|
||||
}
|
||||
async function fetchManifestResponse(manifestUrl) {
|
||||
try {
|
||||
return await fetchManifestResponseFromUrl(manifestUrl);
|
||||
} catch (error2) {
|
||||
if (manifestUrl !== VERSIONS_MANIFEST_URL) {
|
||||
throw error2;
|
||||
}
|
||||
info2(
|
||||
`Failed to fetch manifest from Astral mirror, falling back to GitHub: ${error2.message}`
|
||||
);
|
||||
return await fetchManifestResponseFromUrl(GITHUB_VERSIONS_MANIFEST_URL);
|
||||
}
|
||||
}
|
||||
async function fetchManifestResponseFromUrl(manifestUrl) {
|
||||
let response;
|
||||
for (let attempt = 1; attempt <= MANIFEST_FETCH_ATTEMPTS; attempt++) {
|
||||
info2(`Fetching manifest data from ${manifestUrl} ...`);
|
||||
|
||||
+15
-1
@@ -52854,7 +52854,8 @@ var import_node_readline = require("node:readline");
|
||||
var import_node_stream = require("node:stream");
|
||||
|
||||
// src/utils/constants.ts
|
||||
var VERSIONS_MANIFEST_URL = "https://raw.githubusercontent.com/astral-sh/versions/main/v1/uv.ndjson";
|
||||
var VERSIONS_MANIFEST_URL = "https://releases.astral.sh/github/versions/main/v1/uv.ndjson";
|
||||
var GITHUB_VERSIONS_MANIFEST_URL = "https://raw.githubusercontent.com/astral-sh/versions/main/v1/uv.ndjson";
|
||||
|
||||
// src/utils/fetch.ts
|
||||
var import_undici2 = __toESM(require_undici2(), 1);
|
||||
@@ -52926,6 +52927,19 @@ async function getLatestVersion(manifestUrl = VERSIONS_MANIFEST_URL) {
|
||||
return latestVersion;
|
||||
}
|
||||
async function fetchManifestResponse(manifestUrl) {
|
||||
try {
|
||||
return await fetchManifestResponseFromUrl(manifestUrl);
|
||||
} catch (error2) {
|
||||
if (manifestUrl !== VERSIONS_MANIFEST_URL) {
|
||||
throw error2;
|
||||
}
|
||||
info2(
|
||||
`Failed to fetch manifest from Astral mirror, falling back to GitHub: ${error2.message}`
|
||||
);
|
||||
return await fetchManifestResponseFromUrl(GITHUB_VERSIONS_MANIFEST_URL);
|
||||
}
|
||||
}
|
||||
async function fetchManifestResponseFromUrl(manifestUrl) {
|
||||
let response;
|
||||
for (let attempt = 1; attempt <= MANIFEST_FETCH_ATTEMPTS; attempt++) {
|
||||
info2(`Fetching manifest data from ${manifestUrl} ...`);
|
||||
|
||||
@@ -20,10 +20,12 @@ You can specify a checksum to override those values. The sha256 hashes can also
|
||||
## Manifest file
|
||||
|
||||
By default, setup-uv reads version metadata from
|
||||
[`astral-sh/versions`](https://github.com/astral-sh/versions).
|
||||
the [Astral mirror](https://releases.astral.sh/github/versions/main/v1/uv.ndjson).
|
||||
If the mirror request fails, it falls back to [`astral-sh/versions`](https://github.com/astral-sh/versions).
|
||||
|
||||
The `manifest-file` input lets you override that source with your own URL, for example to test
|
||||
custom uv builds or alternate download locations.
|
||||
custom uv builds or alternate download locations. Custom manifest URLs do not fall back to the
|
||||
default sources.
|
||||
|
||||
### Format
|
||||
|
||||
|
||||
@@ -50,12 +50,13 @@ You can customize the venv location with `venv-path`, for example to place it in
|
||||
|
||||
## GitHub authentication token
|
||||
|
||||
By default, this action resolves available uv versions from
|
||||
[`astral-sh/versions`](https://github.com/astral-sh/versions), then downloads uv artifacts from
|
||||
GitHub Releases.
|
||||
By default, this action fetches version metadata and uv archives from the Astral mirror.
|
||||
If a manifest request fails, it falls back to `raw.githubusercontent.com`. If an archive download
|
||||
fails, it falls back to GitHub Releases.
|
||||
|
||||
You can provide a token via `github-token` to authenticate those downloads. By default, the
|
||||
`GITHUB_TOKEN` secret is used, which is automatically provided by GitHub Actions.
|
||||
You can provide a token via `github-token` to authenticate GitHub Releases downloads. The token
|
||||
is not sent to the Astral mirror or used for manifest requests. By default, the `GITHUB_TOKEN`
|
||||
secret is used, which is automatically provided by GitHub Actions.
|
||||
|
||||
If the default
|
||||
[permissions for the GitHub token](https://docs.github.com/en/actions/security-for-github-actions/security-guides/automatic-token-authentication#permissions-for-the-github_token)
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { createInterface } from "node:readline";
|
||||
import { Readable } from "node:stream";
|
||||
import * as core from "@actions/core";
|
||||
import { VERSIONS_MANIFEST_URL } from "../utils/constants";
|
||||
import {
|
||||
GITHUB_VERSIONS_MANIFEST_URL,
|
||||
VERSIONS_MANIFEST_URL,
|
||||
} from "../utils/constants";
|
||||
import { fetch } from "../utils/fetch";
|
||||
import * as log from "../utils/logging";
|
||||
import { selectDefaultVariant } from "./variant-selection";
|
||||
@@ -167,6 +170,20 @@ export function clearManifestCache(manifestUrl?: string): void {
|
||||
}
|
||||
|
||||
async function fetchManifestResponse(manifestUrl: string) {
|
||||
try {
|
||||
return await fetchManifestResponseFromUrl(manifestUrl);
|
||||
} catch (error) {
|
||||
if (manifestUrl !== VERSIONS_MANIFEST_URL) {
|
||||
throw error;
|
||||
}
|
||||
log.info(
|
||||
`Failed to fetch manifest from Astral mirror, falling back to GitHub: ${(error as Error).message}`,
|
||||
);
|
||||
return await fetchManifestResponseFromUrl(GITHUB_VERSIONS_MANIFEST_URL);
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchManifestResponseFromUrl(manifestUrl: string) {
|
||||
let response: Awaited<ReturnType<typeof fetch>> | undefined;
|
||||
for (let attempt = 1; attempt <= MANIFEST_FETCH_ATTEMPTS; attempt++) {
|
||||
log.info(`Fetching manifest data from ${manifestUrl} ...`);
|
||||
|
||||
@@ -2,6 +2,8 @@ export const TOOL_CACHE_NAME = "uv";
|
||||
export const STATE_UV_PATH = "uv-path";
|
||||
export const STATE_UV_VERSION = "uv-version";
|
||||
export const VERSIONS_MANIFEST_URL =
|
||||
"https://releases.astral.sh/github/versions/main/v1/uv.ndjson";
|
||||
export const GITHUB_VERSIONS_MANIFEST_URL =
|
||||
"https://raw.githubusercontent.com/astral-sh/versions/main/v1/uv.ndjson";
|
||||
|
||||
/** GitHub Releases URL prefix for uv artifacts. */
|
||||
|
||||
Reference in New Issue
Block a user