rollback semi

This commit is contained in:
Okinea Dev 2025-02-04 14:00:04 +02:00
parent f6221cd95f
commit 2a182f9b07
No known key found for this signature in database
GPG Key ID: 07944BC5E01E7B43
6 changed files with 166 additions and 166 deletions

View File

@ -1,4 +1,4 @@
{
"semi": false,
"semi": true,
"quoteProps": "preserve"
}

View File

@ -1,117 +1,117 @@
import { createHash } from "node:crypto"
import { homedir } from "node:os"
import { join } from "node:path"
import { createHash } from "node:crypto";
import { homedir } from "node:os";
import { join } from "node:path";
import {
mkdirSync,
readdirSync,
symlinkSync,
renameSync,
copyFileSync,
} from "node:fs"
import { addPath, info, warning } from "@actions/core"
import { isFeatureAvailable, restoreCache } from "@actions/cache"
import { downloadTool, extractZip } from "@actions/tool-cache"
import { getExecOutput } from "@actions/exec"
import { writeBunfig } from "./bunfig"
import { saveState } from "@actions/core"
import { addExtension, retry } from "./utils"
} from "node:fs";
import { addPath, info, warning } from "@actions/core";
import { isFeatureAvailable, restoreCache } from "@actions/cache";
import { downloadTool, extractZip } from "@actions/tool-cache";
import { getExecOutput } from "@actions/exec";
import { writeBunfig } from "./bunfig";
import { saveState } from "@actions/core";
import { addExtension, retry } from "./utils";
export type Input = {
customUrl?: string
version?: string
os?: string
arch?: string
avx2?: boolean
profile?: boolean
scope?: string
registryUrl?: string
noCache?: boolean
}
customUrl?: string;
version?: string;
os?: string;
arch?: string;
avx2?: boolean;
profile?: boolean;
scope?: string;
registryUrl?: string;
noCache?: boolean;
};
export type Output = {
version: string
revision: string
bunPath: string
url: string
cacheHit: boolean
}
version: string;
revision: string;
bunPath: string;
url: string;
cacheHit: boolean;
};
export type CacheState = {
cacheEnabled: boolean
cacheHit: boolean
bunPath: string
url: string
}
cacheEnabled: boolean;
cacheHit: boolean;
bunPath: string;
url: string;
};
export default async (options: Input): Promise<Output> => {
const bunfigPath = join(process.cwd(), "bunfig.toml")
writeBunfig(bunfigPath, options)
const bunfigPath = join(process.cwd(), "bunfig.toml");
writeBunfig(bunfigPath, options);
const url = getDownloadUrl(options)
const cacheEnabled = isCacheEnabled(options)
const url = getDownloadUrl(options);
const cacheEnabled = isCacheEnabled(options);
const binPath = join(homedir(), ".bun", "bin")
const binPath = join(homedir(), ".bun", "bin");
try {
mkdirSync(binPath, { recursive: true })
mkdirSync(binPath, { recursive: true });
} catch (error) {
if (error.code !== "EEXIST") {
throw error
throw error;
}
}
addPath(binPath)
addPath(binPath);
const exe = (name: string) =>
process.platform === "win32" ? `${name}.exe` : name
const bunPath = join(binPath, exe("bun"))
process.platform === "win32" ? `${name}.exe` : name;
const bunPath = join(binPath, exe("bun"));
try {
symlinkSync(bunPath, join(binPath, exe("bunx")))
symlinkSync(bunPath, join(binPath, exe("bunx")));
} catch (error) {
if (error.code !== "EEXIST") {
throw error
throw error;
}
}
let revision: string | undefined
let cacheHit = false
let revision: string | undefined;
let cacheHit = false;
if (cacheEnabled) {
const cacheKey = createHash("sha1").update(url).digest("base64")
const cacheKey = createHash("sha1").update(url).digest("base64");
const cacheRestored = await restoreCache([bunPath], cacheKey)
const cacheRestored = await restoreCache([bunPath], cacheKey);
if (cacheRestored) {
revision = await getRevision(bunPath)
revision = await getRevision(bunPath);
if (revision) {
cacheHit = true
info(`Using a cached version of Bun: ${revision}`)
cacheHit = true;
info(`Using a cached version of Bun: ${revision}`);
} else {
warning(
`Found a cached version of Bun: ${revision} (but it appears to be corrupted?)`,
)
);
}
}
}
if (!cacheHit) {
info(`Downloading a new version of Bun: ${url}`)
info(`Downloading a new version of Bun: ${url}`);
// TODO: remove this, temporary fix for https://github.com/oven-sh/setup-bun/issues/73
revision = await retry(async () => await downloadBun(url, bunPath), 3)
revision = await retry(async () => await downloadBun(url, bunPath), 3);
}
if (!revision) {
throw new Error(
"Downloaded a new version of Bun, but failed to check its version? Try again.",
)
);
}
const [version] = revision.split("+")
const [version] = revision.split("+");
const cacheState: CacheState = {
cacheEnabled,
cacheHit,
bunPath,
url,
}
};
saveState("cache", JSON.stringify(cacheState))
saveState("cache", JSON.stringify(cacheState));
return {
version,
@ -119,92 +119,92 @@ export default async (options: Input): Promise<Output> => {
bunPath,
url,
cacheHit,
}
}
};
};
async function downloadBun(
url: string,
bunPath: string,
): Promise<string | undefined> {
// Workaround for https://github.com/oven-sh/setup-bun/issues/79 and https://github.com/actions/toolkit/issues/1179
const zipPath = addExtension(await downloadTool(url), ".zip")
const extractedZipPath = await extractZip(zipPath)
const extractedBunPath = await extractBun(extractedZipPath)
const zipPath = addExtension(await downloadTool(url), ".zip");
const extractedZipPath = await extractZip(zipPath);
const extractedBunPath = await extractBun(extractedZipPath);
try {
renameSync(extractedBunPath, bunPath)
renameSync(extractedBunPath, bunPath);
} catch {
// If mv does not work, try to copy the file instead.
// For example: EXDEV: cross-device link not permitted
copyFileSync(extractedBunPath, bunPath)
copyFileSync(extractedBunPath, bunPath);
}
return await getRevision(bunPath)
return await getRevision(bunPath);
}
function isCacheEnabled(options: Input): boolean {
const { customUrl, version, noCache } = options
const { customUrl, version, noCache } = options;
if (noCache) {
return false
return false;
}
if (customUrl) {
return false
return false;
}
if (!version || /latest|canary|action/i.test(version)) {
return false
return false;
}
return isFeatureAvailable()
return isFeatureAvailable();
}
function getDownloadUrl(options: Input): string {
const { customUrl } = options
const { customUrl } = options;
if (customUrl) {
return customUrl
return customUrl;
}
const { version, os, arch, avx2, profile } = options
const eversion = encodeURIComponent(version ?? "latest")
const eos = encodeURIComponent(os ?? process.platform)
const earch = encodeURIComponent(arch ?? process.arch)
const eavx2 = encodeURIComponent(avx2 ?? true)
const eprofile = encodeURIComponent(profile ?? false)
const { version, os, arch, avx2, profile } = options;
const eversion = encodeURIComponent(version ?? "latest");
const eos = encodeURIComponent(os ?? process.platform);
const earch = encodeURIComponent(arch ?? process.arch);
const eavx2 = encodeURIComponent(avx2 ?? true);
const eprofile = encodeURIComponent(profile ?? false);
const { href } = new URL(
`${eversion}/${eos}/${earch}?avx2=${eavx2}&profile=${eprofile}`,
"https://bun.sh/download/",
)
return href
);
return href;
}
async function extractBun(path: string): Promise<string> {
for (const entry of readdirSync(path, { withFileTypes: true })) {
const { name } = entry
const entryPath = join(path, name)
const { name } = entry;
const entryPath = join(path, name);
if (entry.isFile()) {
if (name === "bun" || name === "bun.exe") {
return entryPath
return entryPath;
}
if (/^bun.*\.zip/.test(name)) {
const extractedPath = await extractZip(entryPath)
return extractBun(extractedPath)
const extractedPath = await extractZip(entryPath);
return extractBun(extractedPath);
}
}
if (/^bun/.test(name) && entry.isDirectory()) {
return extractBun(entryPath)
return extractBun(entryPath);
}
}
throw new Error("Could not find executable: bun")
throw new Error("Could not find executable: bun");
}
async function getRevision(exe: string): Promise<string | undefined> {
const revision = await getExecOutput(exe, ["--revision"], {
ignoreReturnCode: true,
})
});
if (revision.exitCode === 0 && /^\d+\.\d+\.\d+/.test(revision.stdout)) {
return revision.stdout.trim()
return revision.stdout.trim();
}
const version = await getExecOutput(exe, ["--version"], {
ignoreReturnCode: true,
})
});
if (version.exitCode === 0 && /^\d+\.\d+\.\d+/.test(version.stdout)) {
return version.stdout.trim()
return version.stdout.trim();
}
return undefined
return undefined;
}

View File

@ -1,50 +1,50 @@
import { EOL } from "node:os"
import { appendFileSync } from "node:fs"
import { info } from "@actions/core"
import { EOL } from "node:os";
import { appendFileSync } from "node:fs";
import { info } from "@actions/core";
type BunfigOptions = {
registryUrl?: string
scope?: string
}
registryUrl?: string;
scope?: string;
};
export function createBunfig(options: BunfigOptions): string | null {
const { registryUrl, scope } = options
const { registryUrl, scope } = options;
let url: URL | undefined
let url: URL | undefined;
if (registryUrl) {
try {
url = new URL(registryUrl)
url = new URL(registryUrl);
} catch {
throw new Error(`Invalid registry-url: ${registryUrl}`)
throw new Error(`Invalid registry-url: ${registryUrl}`);
}
}
let owner: string | undefined
let owner: string | undefined;
if (scope) {
owner = scope.startsWith("@")
? scope.toLocaleLowerCase()
: `@${scope.toLocaleLowerCase()}`
: `@${scope.toLocaleLowerCase()}`;
}
if (url && owner) {
return `[install.scopes]${EOL}'${owner}' = { token = "$BUN_AUTH_TOKEN", url = "${url}"}${EOL}`
return `[install.scopes]${EOL}'${owner}' = { token = "$BUN_AUTH_TOKEN", url = "${url}"}${EOL}`;
}
if (url && !owner) {
return `[install]${EOL}registry = "${url}"${EOL}`
return `[install]${EOL}registry = "${url}"${EOL}`;
}
return null
return null;
}
export function writeBunfig(path: string, options: BunfigOptions): void {
const bunfig = createBunfig(options)
const bunfig = createBunfig(options);
if (!bunfig) {
return
return;
}
info(`Writing bunfig.toml to '${path}'.`)
info(`Writing bunfig.toml to '${path}'.`);
appendFileSync(path, bunfig, {
encoding: "utf8",
})
});
}

View File

@ -1,17 +1,17 @@
import { saveCache } from "@actions/cache"
import { getState, warning } from "@actions/core"
import { CacheState } from "./action"
import { createHash } from "node:crypto"
;(async () => {
const state: CacheState = JSON.parse(getState("cache"))
import { saveCache } from "@actions/cache";
import { getState, warning } from "@actions/core";
import { CacheState } from "./action";
import { createHash } from "node:crypto";
(async () => {
const state: CacheState = JSON.parse(getState("cache"));
if (state.cacheEnabled && !state.cacheHit) {
const cacheKey = createHash("sha1").update(state.url).digest("base64")
const cacheKey = createHash("sha1").update(state.url).digest("base64");
try {
await saveCache([state.bunPath], cacheKey)
process.exit(0)
await saveCache([state.bunPath], cacheKey);
process.exit(0);
} catch (error) {
warning("Failed to save Bun to cache.")
warning("Failed to save Bun to cache.");
}
}
})()
})();

View File

@ -1,10 +1,10 @@
import { tmpdir } from "node:os"
import { getInput, setOutput, setFailed, getBooleanInput } from "@actions/core"
import runAction from "./action.js"
import { readVersionFromFile } from "./utils.js"
import { tmpdir } from "node:os";
import { getInput, setOutput, setFailed, getBooleanInput } from "@actions/core";
import runAction from "./action.js";
import { readVersionFromFile } from "./utils.js";
if (!process.env.RUNNER_TEMP) {
process.env.RUNNER_TEMP = tmpdir()
process.env.RUNNER_TEMP = tmpdir();
}
runAction({
@ -18,14 +18,14 @@ runAction({
noCache: getBooleanInput("no-cache") || false,
})
.then(({ version, revision, bunPath, url, cacheHit }) => {
setOutput("bun-version", version)
setOutput("bun-revision", revision)
setOutput("bun-path", bunPath)
setOutput("bun-download-url", url)
setOutput("cache-hit", cacheHit)
process.exit(0)
setOutput("bun-version", version);
setOutput("bun-revision", revision);
setOutput("bun-path", bunPath);
setOutput("bun-download-url", url);
setOutput("cache-hit", cacheHit);
process.exit(0);
})
.catch((error) => {
setFailed(error)
process.exit(1)
})
setFailed(error);
process.exit(1);
});

View File

@ -1,7 +1,7 @@
import { debug, warning } from "@actions/core"
import { info } from "node:console"
import { existsSync, readFileSync, renameSync } from "node:fs"
import { resolve, basename } from "node:path"
import { debug, warning } from "@actions/core";
import { info } from "node:console";
import { existsSync, readFileSync, renameSync } from "node:fs";
import { resolve, basename } from "node:path";
export function retry<T>(
fn: () => Promise<T>,
@ -10,21 +10,21 @@ export function retry<T>(
): Promise<T> {
return fn().catch((err) => {
if (retries <= 0) {
throw err
throw err;
}
return new Promise((resolve) => setTimeout(resolve, timeout)).then(() =>
retry(fn, retries - 1, timeout),
)
})
);
});
}
export function addExtension(path: string, ext: string): string {
if (!path.endsWith(ext)) {
renameSync(path, path + ext)
return path + ext
renameSync(path, path + ext);
return path + ext;
}
return path
return path;
}
const FILE_VERSION_READERS = {
@ -34,45 +34,45 @@ const FILE_VERSION_READERS = {
content.match(/^bun\s*(?<version>.*?)$/m)?.groups?.version,
".bumrc": (content: string) => content, // https://github.com/owenizedd/bum
".bun-version": (content: string) => content,
}
};
export function readVersionFromFile(file: string): string | undefined {
const cwd = process.env.GITHUB_WORKSPACE
const cwd = process.env.GITHUB_WORKSPACE;
if (!cwd) {
return
return;
}
if (!file) {
return
return;
}
debug(`Reading version from ${file}`)
debug(`Reading version from ${file}`);
const path = resolve(cwd, file)
const base = basename(file)
const path = resolve(cwd, file);
const base = basename(file);
if (!existsSync(path)) {
warning(`File ${path} not found`)
return
warning(`File ${path} not found`);
return;
}
const reader = FILE_VERSION_READERS[base] ?? (() => undefined)
const reader = FILE_VERSION_READERS[base] ?? (() => undefined);
let output: string | undefined
let output: string | undefined;
try {
output = reader(readFileSync(path, "utf8"))?.trim()
output = reader(readFileSync(path, "utf8"))?.trim();
if (!output) {
warning(`Failed to read version from ${file}`)
return
warning(`Failed to read version from ${file}`);
return;
}
} catch (error) {
const { message } = error as Error
warning(`Failed to read ${file}: ${message}`)
const { message } = error as Error;
warning(`Failed to read ${file}: ${message}`);
} finally {
if (output) {
info(`Obtained version ${output} from ${file}`)
return output
info(`Obtained version ${output} from ${file}`);
return output;
}
}
}