-
Notifications
You must be signed in to change notification settings - Fork 47
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
166 additions
and
166 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
{ | ||
"semi": false, | ||
"semi": true, | ||
"quoteProps": "preserve" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,210 +1,210 @@ | ||
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, | ||
revision, | ||
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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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", | ||
}) | ||
}); | ||
} |
Oops, something went wrong.