Add option to skip Astral mirror downloads (#387)

## Summary
- add `download-from-astral-mirror` input defaulting to `true`
- skip mirror URL rewriting and download directly from GitHub Releases
when disabled
- document the input and add unit coverage

Fixes #384

Refs: pi-session 019f31bc-bd1c-7276-9b4e-9734fa0aa367

## Testing
- npm run build
- npm run check
- npm run test:unit
This commit is contained in:
Kevin Stillhammer
2026-07-05 10:17:44 +00:00
committed by GitHub
parent c35f46c92b
commit 278981a28c
7 changed files with 103 additions and 12 deletions
+6 -3
View File
@@ -32,6 +32,7 @@ anything `ruff` can (ex, fix).
| `version` | The version of Ruff to install. See [Install specific versions](#install-specific-versions) | discovered from `pyproject.toml`, else `latest` | | `version` | The version of Ruff to install. See [Install specific versions](#install-specific-versions) | discovered from `pyproject.toml`, else `latest` |
| `version-file` | The file to read the version from. See [Install a version from a specified version file](#install-a-version-from-a-specified-version-file) | None | | `version-file` | The file to read the version from. See [Install a version from a specified version file](#install-a-version-from-a-specified-version-file) | None |
| `manifest-file` | URL to a custom Ruff manifest in the `astral-sh/versions` format. | None | | `manifest-file` | URL to a custom Ruff manifest in the `astral-sh/versions` format. | None |
| `download-from-astral-mirror` | Download Ruff from the Astral mirror instead of directly from GitHub Releases. | `true` |
| `args` | The arguments to pass to the `ruff` command. See [Configuring Ruff] | `check` | | `args` | The arguments to pass to the `ruff` command. See [Configuring Ruff] | `check` |
| `src` | Source path(s) to run `ruff` on. Supports glob patterns. | [github.workspace] | | `src` | Source path(s) to run `ruff` on. Supports glob patterns. | [github.workspace] |
| `checksum` | The sha256 checksum of the downloaded artifact. | None | | `checksum` | The sha256 checksum of the downloaded artifact. | None |
@@ -224,10 +225,12 @@ are automatically verified by this action. The sha256 hashes can be found on the
### GitHub authentication token ### GitHub authentication token
By default, this action resolves available uv versions from By default, this action resolves available Ruff versions from
[`astral-sh/versions`](https://github.com/astral-sh/versions) and downloads release artifacts from `https://releases.astral.sh`. If this fails this action falls back to downloading from the GitHub releases page of the ruff repository. [`astral-sh/versions`](https://github.com/astral-sh/versions) and downloads release artifacts from `https://releases.astral.sh`. If this fails this action falls back to downloading from the GitHub releases page of the Ruff repository.
You can provide a token via `github-token` to authenticate those downloads. By default, the Set `download-from-astral-mirror` to `false` to skip the Astral mirror and download directly from GitHub Releases.
You can provide a token via `github-token` to authenticate GitHub Releases downloads. By default, the
`GITHUB_TOKEN` secret is used, which is automatically provided by GitHub Actions. `GITHUB_TOKEN` secret is used, which is automatically provided by GitHub Actions.
If the default If the default
@@ -357,6 +357,32 @@ describe("download-version", () => {
expect(mockDownloadTool).toHaveBeenCalledTimes(1); expect(mockDownloadTool).toHaveBeenCalledTimes(1);
}); });
it("skips the Astral mirror when downloadFromAstralMirror is false", async () => {
mockGetArtifact.mockResolvedValue({
archiveFormat: "tar.gz",
checksum: "abc123",
downloadUrl:
"https://github.com/astral-sh/ruff/releases/download/0.15.8/ruff-x86_64-unknown-linux-gnu.tar.gz",
});
await downloadVersion(
"unknown-linux-gnu",
"x86_64",
"0.15.8",
undefined,
"token",
undefined,
false,
);
expect(mockDownloadTool).toHaveBeenCalledWith(
"https://github.com/astral-sh/ruff/releases/download/0.15.8/ruff-x86_64-unknown-linux-gnu.tar.gz",
undefined,
"token",
);
expect(mockDownloadTool).toHaveBeenCalledTimes(1);
});
it("uses manifest-file checksum metadata when checksum input is unset", async () => { it("uses manifest-file checksum metadata when checksum input is unset", async () => {
mockGetArtifact.mockResolvedValue({ mockGetArtifact.mockResolvedValue({
archiveFormat: "tar.gz", archiveFormat: "tar.gz",
+4
View File
@@ -23,6 +23,10 @@ inputs:
manifest-file: manifest-file:
description: "URL to a custom manifest file in the astral-sh/versions format." description: "URL to a custom manifest file in the astral-sh/versions format."
required: false required: false
download-from-astral-mirror:
description: "Download Ruff from the Astral mirror instead of directly from GitHub Releases."
required: false
default: "true"
github-token: github-token:
description: description:
"Used for authenticated downloads of Ruff release artifacts from GitHub." "Used for authenticated downloads of Ruff release artifacts from GitHub."
Generated Vendored
+36 -7
View File
@@ -24275,6 +24275,17 @@ function getInput(name, options) {
} }
return val.trim(); return val.trim();
} }
function getBooleanInput(name, options) {
const trueValue = ["true", "True", "TRUE"];
const falseValue = ["false", "False", "FALSE"];
const val = getInput(name, options);
if (trueValue.includes(val))
return true;
if (falseValue.includes(val))
return false;
throw new TypeError(`Input does not meet YAML 1.2 "Core Schema" specification: ${name}
Support boolean input list: \`true | True | TRUE | false | False | FALSE\``);
}
function setOutput(name, value) { function setOutput(name, value) {
const filePath = process.env["GITHUB_OUTPUT"] || ""; const filePath = process.env["GITHUB_OUTPUT"] || "";
if (filePath) { if (filePath) {
@@ -28183,7 +28194,7 @@ function tryGetFromToolCache(arch3, version2) {
const installedPath = find(TOOL_CACHE_NAME, resolvedVersion, arch3); const installedPath = find(TOOL_CACHE_NAME, resolvedVersion, arch3);
return { installedPath, version: resolvedVersion }; return { installedPath, version: resolvedVersion };
} }
async function downloadVersion(platform2, arch3, version2, checksum, githubToken2, manifestUrl) { async function downloadVersion(platform2, arch3, version2, checksum, githubToken2, manifestUrl, downloadFromAstralMirror2 = true) {
const artifact = await getArtifact(version2, arch3, platform2, manifestUrl); const artifact = await getArtifact(version2, arch3, platform2, manifestUrl);
if (!artifact) { if (!artifact) {
throw new Error( throw new Error(
@@ -28196,7 +28207,8 @@ async function downloadVersion(platform2, arch3, version2, checksum, githubToken
platform2, platform2,
arch3, arch3,
version2, version2,
getDownloadToken(artifact.downloadUrl, githubToken2) getDownloadToken(artifact.downloadUrl, githubToken2),
downloadFromAstralMirror2
); );
await validateChecksum( await validateChecksum(
resolvedChecksum, resolvedChecksum,
@@ -28226,8 +28238,8 @@ function rewriteToMirror(url) {
} }
return ASTRAL_MIRROR_PREFIX + url.slice(GITHUB_RELEASES_PREFIX.length); return ASTRAL_MIRROR_PREFIX + url.slice(GITHUB_RELEASES_PREFIX.length);
} }
async function downloadArtifact(downloadUrl, platform2, arch3, version2, githubToken2) { async function downloadArtifact(downloadUrl, platform2, arch3, version2, githubToken2, downloadFromAstralMirror2) {
const mirrorUrl = rewriteToMirror(downloadUrl); const mirrorUrl = downloadFromAstralMirror2 ? rewriteToMirror(downloadUrl) : void 0;
const resolvedDownloadUrl = mirrorUrl ?? downloadUrl; const resolvedDownloadUrl = mirrorUrl ?? downloadUrl;
try { try {
return await downloadFile( return await downloadFile(
@@ -28307,6 +28319,16 @@ var args = getInput("args");
var src = getInput("src"); var src = getInput("src");
var versionFile = getInput("version-file"); var versionFile = getInput("version-file");
var manifestFile = getInput("manifest-file"); var manifestFile = getInput("manifest-file");
var downloadFromAstralMirror = getBooleanInput2(
"download-from-astral-mirror",
true
);
function getBooleanInput2(name, defaultValue) {
if (getInput(name) === "") {
return defaultValue;
}
return getBooleanInput(name);
}
// src/utils/platforms.ts // src/utils/platforms.ts
function getArch() { function getArch() {
@@ -32030,7 +32052,13 @@ async function run() {
if (arch3 === void 0) { if (arch3 === void 0) {
throw new Error(`Unsupported architecture: ${process.arch}`); throw new Error(`Unsupported architecture: ${process.arch}`);
} }
const setupResult = await setupRuff(platform2, arch3, checkSum, githubToken); const setupResult = await setupRuff(
platform2,
arch3,
checkSum,
githubToken,
downloadFromAstralMirror
);
addRuffToPath(setupResult.ruffDir); addRuffToPath(setupResult.ruffDir);
setOutputFormat(); setOutputFormat();
addMatchers(); addMatchers();
@@ -32042,7 +32070,7 @@ async function run() {
setFailed(err.message); setFailed(err.message);
} }
} }
async function setupRuff(platform2, arch3, checkSum2, githubToken2) { async function setupRuff(platform2, arch3, checkSum2, githubToken2, downloadFromAstralMirror2) {
const resolvedVersion = await determineVersion(); const resolvedVersion = await determineVersion();
const manifestUrl = manifestFile || void 0; const manifestUrl = manifestFile || void 0;
if (semver4.lt(resolvedVersion, "v0.0.247")) { if (semver4.lt(resolvedVersion, "v0.0.247")) {
@@ -32064,7 +32092,8 @@ async function setupRuff(platform2, arch3, checkSum2, githubToken2) {
resolvedVersion, resolvedVersion,
checkSum2, checkSum2,
githubToken2, githubToken2,
manifestUrl manifestUrl,
downloadFromAstralMirror2
); );
return { return {
ruffDir: downloadVersionResult.cachedToolDir, ruffDir: downloadVersionResult.cachedToolDir,
+10 -1
View File
@@ -35,6 +35,7 @@ export async function downloadVersion(
checksum: string | undefined, checksum: string | undefined,
githubToken: string, githubToken: string,
manifestUrl?: string, manifestUrl?: string,
downloadFromAstralMirror = true,
): Promise<{ version: string; cachedToolDir: string }> { ): Promise<{ version: string; cachedToolDir: string }> {
const artifact = await getArtifact(version, arch, platform, manifestUrl); const artifact = await getArtifact(version, arch, platform, manifestUrl);
@@ -57,6 +58,7 @@ export async function downloadVersion(
arch, arch,
version, version,
getDownloadToken(artifact.downloadUrl, githubToken), getDownloadToken(artifact.downloadUrl, githubToken),
downloadFromAstralMirror,
); );
await validateChecksum( await validateChecksum(
resolvedChecksum, resolvedChecksum,
@@ -83,6 +85,10 @@ export async function downloadVersion(
return { cachedToolDir, version }; return { cachedToolDir, version };
} }
/**
* Rewrite a GitHub Releases URL to the Astral mirror.
* Returns `undefined` if the URL does not match the expected GitHub prefix.
*/
export function rewriteToMirror(url: string): string | undefined { export function rewriteToMirror(url: string): string | undefined {
if (!url.startsWith(GITHUB_RELEASES_PREFIX)) { if (!url.startsWith(GITHUB_RELEASES_PREFIX)) {
return undefined; return undefined;
@@ -97,8 +103,11 @@ async function downloadArtifact(
arch: Architecture, arch: Architecture,
version: string, version: string,
githubToken: string | undefined, githubToken: string | undefined,
downloadFromAstralMirror: boolean,
): Promise<string> { ): Promise<string> {
const mirrorUrl = rewriteToMirror(downloadUrl); const mirrorUrl = downloadFromAstralMirror
? rewriteToMirror(downloadUrl)
: undefined;
const resolvedDownloadUrl = mirrorUrl ?? downloadUrl; const resolvedDownloadUrl = mirrorUrl ?? downloadUrl;
try { try {
+10 -1
View File
@@ -9,6 +9,7 @@ import {
import { import {
args, args,
checkSum, checkSum,
downloadFromAstralMirror,
githubToken, githubToken,
manifestFile, manifestFile,
src, src,
@@ -39,7 +40,13 @@ async function run(): Promise<void> {
if (arch === undefined) { if (arch === undefined) {
throw new Error(`Unsupported architecture: ${process.arch}`); throw new Error(`Unsupported architecture: ${process.arch}`);
} }
const setupResult = await setupRuff(platform, arch, checkSum, githubToken); const setupResult = await setupRuff(
platform,
arch,
checkSum,
githubToken,
downloadFromAstralMirror,
);
addRuffToPath(setupResult.ruffDir); addRuffToPath(setupResult.ruffDir);
setOutputFormat(); setOutputFormat();
@@ -60,6 +67,7 @@ async function setupRuff(
arch: Architecture, arch: Architecture,
checkSum: string | undefined, checkSum: string | undefined,
githubToken: string, githubToken: string,
downloadFromAstralMirror: boolean,
): Promise<{ ruffDir: string; version: string }> { ): Promise<{ ruffDir: string; version: string }> {
const resolvedVersion = await determineVersion(); const resolvedVersion = await determineVersion();
const manifestUrl = manifestFile || undefined; const manifestUrl = manifestFile || undefined;
@@ -84,6 +92,7 @@ async function setupRuff(
checkSum, checkSum,
githubToken, githubToken,
manifestUrl, manifestUrl,
downloadFromAstralMirror,
); );
return { return {
+11
View File
@@ -7,3 +7,14 @@ export const args = core.getInput("args");
export const src = core.getInput("src"); export const src = core.getInput("src");
export const versionFile = core.getInput("version-file"); export const versionFile = core.getInput("version-file");
export const manifestFile = core.getInput("manifest-file"); export const manifestFile = core.getInput("manifest-file");
export const downloadFromAstralMirror = getBooleanInput(
"download-from-astral-mirror",
true,
);
function getBooleanInput(name: string, defaultValue: boolean): boolean {
if (core.getInput(name) === "") {
return defaultValue;
}
return core.getBooleanInput(name);
}