mirror of
https://gitea.com/actions/setup-deno.git
synced 2025-12-11 02:46:43 +00:00
171 lines
4.1 KiB
JavaScript
171 lines
4.1 KiB
JavaScript
const semver = require("semver");
|
|
const { fetch } = require("undici");
|
|
const fs = require("fs");
|
|
|
|
const GIT_HASH_RE = /^[0-9a-fA-F]{40}$/;
|
|
|
|
/**
|
|
* @typedef VersionRange
|
|
* @property {string} range
|
|
* @property {boolean} isCanary
|
|
*/
|
|
|
|
/**
|
|
* @typedef Version
|
|
* @property {string} version
|
|
* @property {boolean} isCanary
|
|
*/
|
|
|
|
/**
|
|
* Parses the version from the user into a structure.
|
|
*
|
|
* @param {string | undefined} version
|
|
* @returns {VersionRange | null}
|
|
*/
|
|
function parseVersionRange(version) {
|
|
version = String(version) || "1.x";
|
|
version = version.trim();
|
|
|
|
if (version == "canary") {
|
|
return { range: "latest", isCanary: true };
|
|
}
|
|
|
|
if (version == "latest") {
|
|
return { range: "latest", isCanary: false };
|
|
}
|
|
|
|
if (GIT_HASH_RE.test(version)) {
|
|
return { range: version, isCanary: true };
|
|
}
|
|
|
|
const range = semver.validRange(version);
|
|
if (range !== null) {
|
|
return { range, isCanary: false };
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Parses the version from the version file
|
|
*
|
|
* @param {string} versionFilePath
|
|
* @returns {string | undefined}
|
|
*/
|
|
function getDenoVersionFromFile(versionFilePath) {
|
|
if (!fs.existsSync(versionFilePath)) {
|
|
throw new Error(
|
|
`The specified node version file at: ${versionFilePath} does not exist`,
|
|
);
|
|
}
|
|
|
|
const contents = fs.readFileSync(versionFilePath, "utf8");
|
|
|
|
// .tool-versions typically looks like
|
|
// ```
|
|
// ruby 2.6.5
|
|
// deno 1.43.1
|
|
// node 20.0.0
|
|
// ```
|
|
// This parses the version of Deno from the file
|
|
const denoVersionInToolVersions = contents.match(
|
|
/^deno\s+v?(?<version>[^\s]+)$/m,
|
|
);
|
|
|
|
return denoVersionInToolVersions?.groups?.version || contents.trim();
|
|
}
|
|
|
|
/**
|
|
* @param {VersionRange} range
|
|
* @returns {Promise<Version | null>}
|
|
*/
|
|
async function resolveVersion({ range, isCanary }) {
|
|
if (isCanary) {
|
|
if (range === "latest") {
|
|
const res = await fetchWithRetries(
|
|
"https://dl.deno.land/canary-latest.txt",
|
|
);
|
|
if (res.status !== 200) {
|
|
throw new Error(
|
|
"Failed to fetch canary version info from dl.deno.land. Please try again later.",
|
|
);
|
|
}
|
|
const version = (await res.text()).trim();
|
|
return { version, isCanary: true };
|
|
}
|
|
return { version: range, isCanary: true };
|
|
}
|
|
|
|
if (range === "latest") {
|
|
const res = await fetchWithRetries(
|
|
"https://dl.deno.land/release-latest.txt",
|
|
);
|
|
if (res.status !== 200) {
|
|
throw new Error(
|
|
"Failed to fetch release version info from dl.deno.land. Please try again later.",
|
|
);
|
|
}
|
|
let version = (await res.text()).trim();
|
|
version = semver.clean(version);
|
|
if (version === null) {
|
|
return null;
|
|
}
|
|
return { version, isCanary: false };
|
|
}
|
|
|
|
const res = await fetchWithRetries("https://deno.com/versions.json");
|
|
if (res.status !== 200) {
|
|
throw new Error(
|
|
"Failed to fetch stable version info from deno.com/versions.json. Please try again later.",
|
|
);
|
|
}
|
|
const versionJson = await res.json();
|
|
if (!("cli" in versionJson)) {
|
|
throw new Error("Fetched stable version info is invalid.");
|
|
}
|
|
/** @type {string[]} */
|
|
const versions = versionJson.cli;
|
|
if (!Array.isArray(versions)) {
|
|
throw new Error("Fetched stable version info is invalid.");
|
|
}
|
|
|
|
let version = semver.maxSatisfying(versions, range);
|
|
if (version === null) {
|
|
return null;
|
|
}
|
|
version = semver.clean(version);
|
|
if (version === null) {
|
|
return null;
|
|
}
|
|
|
|
return { version, isCanary: false };
|
|
}
|
|
|
|
/** @param {string} url */
|
|
async function fetchWithRetries(url, maxRetries = 5) {
|
|
let sleepMs = 250;
|
|
let iterationCount = 0;
|
|
while (true) {
|
|
iterationCount++;
|
|
try {
|
|
const res = await fetch(url);
|
|
if (res.status === 200 || iterationCount > maxRetries) {
|
|
return res;
|
|
}
|
|
} catch (err) {
|
|
if (iterationCount > maxRetries) {
|
|
throw err;
|
|
}
|
|
}
|
|
console.warn(`Failed fetching. Retrying in ${sleepMs}ms...`);
|
|
await new Promise((resolve) => setTimeout(resolve, sleepMs));
|
|
sleepMs = Math.min(sleepMs * 2, 10_000);
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
parseVersionRange,
|
|
resolveVersion,
|
|
getDenoVersionFromFile,
|
|
};
|