From 278981a28ce3188b1e39527901f38254bf3aac89 Mon Sep 17 00:00:00 2001 From: Kevin Stillhammer Date: Sun, 5 Jul 2026 12:17:44 +0200 Subject: [PATCH] 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 --- README.md | 9 +++-- __tests__/download/download-version.test.ts | 26 +++++++++++++ action.yml | 4 ++ dist/ruff-action/index.cjs | 43 +++++++++++++++++---- src/download/download-version.ts | 11 +++++- src/ruff-action.ts | 11 +++++- src/utils/inputs.ts | 11 ++++++ 7 files changed, 103 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 4edcd66..50d31b5 100644 --- a/README.md +++ b/README.md @@ -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-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 | +| `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` | | `src` | Source path(s) to run `ruff` on. Supports glob patterns. | [github.workspace] | | `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 -By default, this action resolves available uv 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. +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. -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. If the default diff --git a/__tests__/download/download-version.test.ts b/__tests__/download/download-version.test.ts index a99dfd9..50c49c4 100644 --- a/__tests__/download/download-version.test.ts +++ b/__tests__/download/download-version.test.ts @@ -357,6 +357,32 @@ describe("download-version", () => { 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 () => { mockGetArtifact.mockResolvedValue({ archiveFormat: "tar.gz", diff --git a/action.yml b/action.yml index 42f29b6..325c67c 100644 --- a/action.yml +++ b/action.yml @@ -23,6 +23,10 @@ inputs: manifest-file: description: "URL to a custom manifest file in the astral-sh/versions format." 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: description: "Used for authenticated downloads of Ruff release artifacts from GitHub." diff --git a/dist/ruff-action/index.cjs b/dist/ruff-action/index.cjs index 60359f9..1ac5481 100644 --- a/dist/ruff-action/index.cjs +++ b/dist/ruff-action/index.cjs @@ -24275,6 +24275,17 @@ function getInput(name, options) { } 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) { const filePath = process.env["GITHUB_OUTPUT"] || ""; if (filePath) { @@ -28183,7 +28194,7 @@ function tryGetFromToolCache(arch3, version2) { const installedPath = find(TOOL_CACHE_NAME, resolvedVersion, arch3); 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); if (!artifact) { throw new Error( @@ -28196,7 +28207,8 @@ async function downloadVersion(platform2, arch3, version2, checksum, githubToken platform2, arch3, version2, - getDownloadToken(artifact.downloadUrl, githubToken2) + getDownloadToken(artifact.downloadUrl, githubToken2), + downloadFromAstralMirror2 ); await validateChecksum( resolvedChecksum, @@ -28226,8 +28238,8 @@ function rewriteToMirror(url) { } return ASTRAL_MIRROR_PREFIX + url.slice(GITHUB_RELEASES_PREFIX.length); } -async function downloadArtifact(downloadUrl, platform2, arch3, version2, githubToken2) { - const mirrorUrl = rewriteToMirror(downloadUrl); +async function downloadArtifact(downloadUrl, platform2, arch3, version2, githubToken2, downloadFromAstralMirror2) { + const mirrorUrl = downloadFromAstralMirror2 ? rewriteToMirror(downloadUrl) : void 0; const resolvedDownloadUrl = mirrorUrl ?? downloadUrl; try { return await downloadFile( @@ -28307,6 +28319,16 @@ var args = getInput("args"); var src = getInput("src"); var versionFile = getInput("version-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 function getArch() { @@ -32030,7 +32052,13 @@ async function run() { if (arch3 === void 0) { 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); setOutputFormat(); addMatchers(); @@ -32042,7 +32070,7 @@ async function run() { setFailed(err.message); } } -async function setupRuff(platform2, arch3, checkSum2, githubToken2) { +async function setupRuff(platform2, arch3, checkSum2, githubToken2, downloadFromAstralMirror2) { const resolvedVersion = await determineVersion(); const manifestUrl = manifestFile || void 0; if (semver4.lt(resolvedVersion, "v0.0.247")) { @@ -32064,7 +32092,8 @@ async function setupRuff(platform2, arch3, checkSum2, githubToken2) { resolvedVersion, checkSum2, githubToken2, - manifestUrl + manifestUrl, + downloadFromAstralMirror2 ); return { ruffDir: downloadVersionResult.cachedToolDir, diff --git a/src/download/download-version.ts b/src/download/download-version.ts index c5f68aa..c9fd3a4 100644 --- a/src/download/download-version.ts +++ b/src/download/download-version.ts @@ -35,6 +35,7 @@ export async function downloadVersion( checksum: string | undefined, githubToken: string, manifestUrl?: string, + downloadFromAstralMirror = true, ): Promise<{ version: string; cachedToolDir: string }> { const artifact = await getArtifact(version, arch, platform, manifestUrl); @@ -57,6 +58,7 @@ export async function downloadVersion( arch, version, getDownloadToken(artifact.downloadUrl, githubToken), + downloadFromAstralMirror, ); await validateChecksum( resolvedChecksum, @@ -83,6 +85,10 @@ export async function downloadVersion( 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 { if (!url.startsWith(GITHUB_RELEASES_PREFIX)) { return undefined; @@ -97,8 +103,11 @@ async function downloadArtifact( arch: Architecture, version: string, githubToken: string | undefined, + downloadFromAstralMirror: boolean, ): Promise { - const mirrorUrl = rewriteToMirror(downloadUrl); + const mirrorUrl = downloadFromAstralMirror + ? rewriteToMirror(downloadUrl) + : undefined; const resolvedDownloadUrl = mirrorUrl ?? downloadUrl; try { diff --git a/src/ruff-action.ts b/src/ruff-action.ts index 5c62d3d..163d079 100644 --- a/src/ruff-action.ts +++ b/src/ruff-action.ts @@ -9,6 +9,7 @@ import { import { args, checkSum, + downloadFromAstralMirror, githubToken, manifestFile, src, @@ -39,7 +40,13 @@ async function run(): Promise { if (arch === undefined) { 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); setOutputFormat(); @@ -60,6 +67,7 @@ async function setupRuff( arch: Architecture, checkSum: string | undefined, githubToken: string, + downloadFromAstralMirror: boolean, ): Promise<{ ruffDir: string; version: string }> { const resolvedVersion = await determineVersion(); const manifestUrl = manifestFile || undefined; @@ -84,6 +92,7 @@ async function setupRuff( checkSum, githubToken, manifestUrl, + downloadFromAstralMirror, ); return { diff --git a/src/utils/inputs.ts b/src/utils/inputs.ts index ec4c1fa..9c84947 100644 --- a/src/utils/inputs.ts +++ b/src/utils/inputs.ts @@ -7,3 +7,14 @@ export const args = core.getInput("args"); export const src = core.getInput("src"); export const versionFile = core.getInput("version-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); +}