mirror of
https://github.com/oven-sh/bun
synced 2026-02-02 15:08:46 +00:00
ci: musl builds (#15154)
Co-authored-by: Electroid <Electroid@users.noreply.github.com> Co-authored-by: Meghan Denny <meghan@bun.sh>
This commit is contained in:
722
.buildkite/ci.mjs
Normal file → Executable file
722
.buildkite/ci.mjs
Normal file → Executable file
@@ -8,13 +8,14 @@
|
||||
import { writeFileSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import {
|
||||
getBootstrapVersion,
|
||||
getBuildNumber,
|
||||
getCanaryRevision,
|
||||
getChangedFiles,
|
||||
getCommit,
|
||||
getCommitMessage,
|
||||
getLastSuccessfulBuild,
|
||||
getMainBranch,
|
||||
getRepositoryOwner,
|
||||
getTargetBranch,
|
||||
isBuildkite,
|
||||
isFork,
|
||||
@@ -22,102 +23,162 @@ import {
|
||||
isMergeQueue,
|
||||
printEnvironment,
|
||||
spawnSafe,
|
||||
toYaml,
|
||||
uploadArtifact,
|
||||
} from "../scripts/utils.mjs";
|
||||
|
||||
function toYaml(obj, indent = 0) {
|
||||
const spaces = " ".repeat(indent);
|
||||
let result = "";
|
||||
/**
|
||||
* @typedef PipelineOptions
|
||||
* @property {string} [buildId]
|
||||
* @property {boolean} [buildImages]
|
||||
* @property {boolean} [publishImages]
|
||||
* @property {boolean} [skipTests]
|
||||
*/
|
||||
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
if (value === undefined) {
|
||||
continue;
|
||||
}
|
||||
/**
|
||||
* @param {PipelineOptions} options
|
||||
*/
|
||||
function getPipeline(options) {
|
||||
const { buildId, buildImages, publishImages, skipTests } = options;
|
||||
|
||||
if (value === null) {
|
||||
result += `${spaces}${key}: null\n`;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
result += `${spaces}${key}:\n`;
|
||||
value.forEach(item => {
|
||||
if (typeof item === "object" && item !== null) {
|
||||
result += `${spaces}- \n${toYaml(item, indent + 2)
|
||||
.split("\n")
|
||||
.map(line => `${spaces} ${line}`)
|
||||
.join("\n")}\n`;
|
||||
} else {
|
||||
result += `${spaces}- ${item}\n`;
|
||||
}
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
if (typeof value === "object") {
|
||||
result += `${spaces}${key}:\n${toYaml(value, indent + 2)}`;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
typeof value === "string" &&
|
||||
(value.includes(":") || value.includes("#") || value.includes("'") || value.includes('"') || value.includes("\n"))
|
||||
) {
|
||||
result += `${spaces}${key}: "${value.replace(/"/g, '\\"')}"\n`;
|
||||
continue;
|
||||
}
|
||||
|
||||
result += `${spaces}${key}: ${value}\n`;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function getPipeline(buildId) {
|
||||
/**
|
||||
* Helpers
|
||||
*/
|
||||
|
||||
const getKey = platform => {
|
||||
const { os, arch, abi, baseline } = platform;
|
||||
|
||||
if (abi) {
|
||||
if (baseline) {
|
||||
return `${os}-${arch}-${abi}-baseline`;
|
||||
/**
|
||||
* @param {string} text
|
||||
* @returns {string}
|
||||
* @link https://github.com/buildkite/emojis#emoji-reference
|
||||
*/
|
||||
const getEmoji = string => {
|
||||
if (string === "amazonlinux") {
|
||||
return ":aws:";
|
||||
}
|
||||
return `${os}-${arch}-${abi}`;
|
||||
}
|
||||
if (baseline) {
|
||||
return `${os}-${arch}-baseline`;
|
||||
}
|
||||
|
||||
return `${os}-${arch}`;
|
||||
return `:${string}:`;
|
||||
};
|
||||
|
||||
const getLabel = platform => {
|
||||
const { os, arch, abi, baseline, release } = platform;
|
||||
let label = release ? `:${os}: ${release} ${arch}` : `:${os}: ${arch}`;
|
||||
/**
|
||||
* @typedef {"linux" | "darwin" | "windows"} Os
|
||||
* @typedef {"aarch64" | "x64"} Arch
|
||||
* @typedef {"musl"} Abi
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef Target
|
||||
* @property {Os} os
|
||||
* @property {Arch} arch
|
||||
* @property {Abi} [abi]
|
||||
* @property {boolean} [baseline]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {Target} target
|
||||
* @returns {string}
|
||||
*/
|
||||
const getTargetKey = target => {
|
||||
const { os, arch, abi, baseline } = target;
|
||||
let key = `${os}-${arch}`;
|
||||
if (abi) {
|
||||
key += `-${abi}`;
|
||||
}
|
||||
if (baseline) {
|
||||
key += "-baseline";
|
||||
}
|
||||
return key;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Target} target
|
||||
* @returns {string}
|
||||
*/
|
||||
const getTargetLabel = target => {
|
||||
const { os, arch, abi, baseline } = target;
|
||||
let label = `${getEmoji(os)} ${arch}`;
|
||||
if (abi) {
|
||||
label += `-${abi}`;
|
||||
}
|
||||
if (baseline) {
|
||||
label += `-baseline`;
|
||||
label += "-baseline";
|
||||
}
|
||||
return label;
|
||||
};
|
||||
|
||||
// https://buildkite.com/docs/pipelines/command-step#retry-attributes
|
||||
const getRetry = (limit = 3) => {
|
||||
/**
|
||||
* @typedef Platform
|
||||
* @property {Os} os
|
||||
* @property {Arch} arch
|
||||
* @property {Abi} [abi]
|
||||
* @property {boolean} [baseline]
|
||||
* @property {string} [distro]
|
||||
* @property {string} release
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {Platform} platform
|
||||
* @returns {string}
|
||||
*/
|
||||
const getPlatformKey = platform => {
|
||||
const { os, arch, abi, baseline, distro, release } = platform;
|
||||
const target = getTargetKey({ os, arch, abi, baseline });
|
||||
if (distro) {
|
||||
return `${target}-${distro}-${release.replace(/\./g, "")}`;
|
||||
}
|
||||
return `${target}-${release.replace(/\./g, "")}`;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Platform} platform
|
||||
* @returns {string}
|
||||
*/
|
||||
const getPlatformLabel = platform => {
|
||||
const { os, arch, baseline, distro, release } = platform;
|
||||
let label = `${getEmoji(distro || os)} ${release} ${arch}`;
|
||||
if (baseline) {
|
||||
label += "-baseline";
|
||||
}
|
||||
return label;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Platform} platform
|
||||
* @returns {string}
|
||||
*/
|
||||
const getImageKey = platform => {
|
||||
const { os, arch, distro, release } = platform;
|
||||
if (distro) {
|
||||
return `${os}-${arch}-${distro}-${release.replace(/\./g, "")}`;
|
||||
}
|
||||
return `${os}-${arch}-${release.replace(/\./g, "")}`;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Platform} platform
|
||||
* @returns {string}
|
||||
*/
|
||||
const getImageLabel = platform => {
|
||||
const { os, arch, distro, release } = platform;
|
||||
return `${getEmoji(distro || os)} ${release} ${arch}`;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {number} [limit]
|
||||
* @link https://buildkite.com/docs/pipelines/command-step#retry-attributes
|
||||
*/
|
||||
const getRetry = (limit = 0) => {
|
||||
return {
|
||||
automatic: [
|
||||
{ exit_status: 1, limit: 1 },
|
||||
{ exit_status: -1, limit },
|
||||
{ exit_status: 255, limit },
|
||||
{ signal_reason: "agent_stop", limit },
|
||||
{ exit_status: 1, limit },
|
||||
{ exit_status: -1, limit: 3 },
|
||||
{ exit_status: 255, limit: 3 },
|
||||
{ signal_reason: "agent_stop", limit: 3 },
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
// https://buildkite.com/docs/pipelines/managing-priorities
|
||||
/**
|
||||
* @returns {number}
|
||||
* @link https://buildkite.com/docs/pipelines/managing-priorities
|
||||
*/
|
||||
const getPriority = () => {
|
||||
if (isFork()) {
|
||||
return -1;
|
||||
@@ -132,131 +193,285 @@ function getPipeline(buildId) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Steps
|
||||
* @param {Target} target
|
||||
* @returns {Record<string, string | undefined>}
|
||||
*/
|
||||
const getBuildEnv = target => {
|
||||
const { baseline, abi } = target;
|
||||
return {
|
||||
ENABLE_BASELINE: baseline ? "ON" : "OFF",
|
||||
ABI: abi === "musl" ? "musl" : undefined,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Target} target
|
||||
* @returns {string}
|
||||
*/
|
||||
const getBuildToolchain = target => {
|
||||
const { os, arch, abi, baseline } = target;
|
||||
let key = `${os}-${arch}`;
|
||||
if (abi) {
|
||||
key += `-${abi}`;
|
||||
}
|
||||
if (baseline) {
|
||||
key += "-baseline";
|
||||
}
|
||||
return key;
|
||||
};
|
||||
|
||||
/**
|
||||
* Agents
|
||||
*/
|
||||
|
||||
const getBuildVendorStep = platform => {
|
||||
const { os, arch, abi, baseline } = platform;
|
||||
/**
|
||||
* @typedef {Record<string, string | undefined>} Agent
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {Platform} platform
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const isUsingNewAgent = platform => {
|
||||
const { os, distro } = platform;
|
||||
if (os === "linux" && distro === "alpine") {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {"v1" | "v2"} version
|
||||
* @param {Platform} platform
|
||||
* @param {string} [instanceType]
|
||||
* @returns {Agent}
|
||||
*/
|
||||
const getEmphemeralAgent = (version, platform, instanceType) => {
|
||||
const { os, arch, abi, distro, release } = platform;
|
||||
if (version === "v1") {
|
||||
return {
|
||||
key: `${getKey(platform)}-build-vendor`,
|
||||
label: `build-vendor`,
|
||||
agents: {
|
||||
robobun: true,
|
||||
os,
|
||||
arch,
|
||||
distro,
|
||||
release,
|
||||
};
|
||||
}
|
||||
let image;
|
||||
if (distro) {
|
||||
image = `${os}-${arch}-${distro}-${release}`;
|
||||
} else {
|
||||
image = `${os}-${arch}-${release}`;
|
||||
}
|
||||
if (buildImages && !publishImages) {
|
||||
image += `-build-${getBuildNumber()}`;
|
||||
} else {
|
||||
image += `-v${getBootstrapVersion()}`;
|
||||
}
|
||||
return {
|
||||
robobun: true,
|
||||
robobun2: true,
|
||||
os,
|
||||
arch,
|
||||
abi,
|
||||
queue: abi ? `build-${os}-${abi}` : `build-${os}`,
|
||||
distro,
|
||||
release,
|
||||
"image-name": image,
|
||||
"instance-type": instanceType,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Target} target
|
||||
* @returns {Agent}
|
||||
*/
|
||||
const getBuildAgent = target => {
|
||||
const { os, arch, abi } = target;
|
||||
if (isUsingNewAgent(target)) {
|
||||
const instanceType = arch === "aarch64" ? "c8g.8xlarge" : "c7i.8xlarge";
|
||||
return getEmphemeralAgent("v2", target, instanceType);
|
||||
}
|
||||
return {
|
||||
queue: `build-${os}`,
|
||||
os,
|
||||
arch,
|
||||
abi,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Target} target
|
||||
* @returns {Agent}
|
||||
*/
|
||||
const getZigAgent = target => {
|
||||
const { abi, arch } = target;
|
||||
// if (abi === "musl") {
|
||||
// const instanceType = arch === "aarch64" ? "c8g.large" : "c7i.large";
|
||||
// return getEmphemeralAgent("v2", target, instanceType);
|
||||
// }
|
||||
return {
|
||||
queue: "build-zig",
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Platform} platform
|
||||
* @returns {Agent}
|
||||
*/
|
||||
const getTestAgent = platform => {
|
||||
const { os, arch, release } = platform;
|
||||
if (isUsingNewAgent(platform)) {
|
||||
const instanceType = arch === "aarch64" ? "t4g.large" : "t3.large";
|
||||
return getEmphemeralAgent("v2", platform, instanceType);
|
||||
}
|
||||
if (os === "darwin") {
|
||||
return {
|
||||
os,
|
||||
arch,
|
||||
release,
|
||||
queue: "test-darwin",
|
||||
};
|
||||
}
|
||||
return getEmphemeralAgent("v1", platform);
|
||||
};
|
||||
|
||||
/**
|
||||
* Steps
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef Step
|
||||
* @property {string} key
|
||||
* @property {string} [label]
|
||||
* @property {Record<string, string | undefined>} [agents]
|
||||
* @property {Record<string, string | undefined>} [env]
|
||||
* @property {string} command
|
||||
* @property {string[]} [depends_on]
|
||||
* @property {Record<string, string | undefined>} [retry]
|
||||
* @property {boolean} [cancel_on_build_failing]
|
||||
* @property {boolean} [soft_fail]
|
||||
* @property {number} [parallelism]
|
||||
* @property {number} [concurrency]
|
||||
* @property {string} [concurrency_group]
|
||||
* @property {number} [priority]
|
||||
* @property {number} [timeout_in_minutes]
|
||||
* @link https://buildkite.com/docs/pipelines/command-step
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {Platform} platform
|
||||
* @returns {Step}
|
||||
*/
|
||||
const getBuildImageStep = platform => {
|
||||
const { os, arch, distro, release } = platform;
|
||||
const action = publishImages ? "publish-image" : "create-image";
|
||||
return {
|
||||
key: `${getImageKey(platform)}-build-image`,
|
||||
label: `${getImageLabel(platform)} - build-image`,
|
||||
agents: {
|
||||
queue: "build-image",
|
||||
},
|
||||
env: {
|
||||
DEBUG: "1",
|
||||
},
|
||||
command: `node ./scripts/machine.mjs ${action} --ci --cloud=aws --os=${os} --arch=${arch} --distro=${distro} --distro-version=${release}`,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Target} target
|
||||
* @returns {Step}
|
||||
*/
|
||||
const getBuildVendorStep = target => {
|
||||
return {
|
||||
key: `${getTargetKey(target)}-build-vendor`,
|
||||
label: `${getTargetLabel(target)} - build-vendor`,
|
||||
agents: getBuildAgent(target),
|
||||
retry: getRetry(),
|
||||
cancel_on_build_failing: isMergeQueue(),
|
||||
env: {
|
||||
ENABLE_BASELINE: baseline ? "ON" : "OFF",
|
||||
},
|
||||
env: getBuildEnv(target),
|
||||
command: "bun run build:ci --target dependencies",
|
||||
};
|
||||
};
|
||||
|
||||
const getBuildCppStep = platform => {
|
||||
const { os, arch, abi, baseline } = platform;
|
||||
|
||||
/**
|
||||
* @param {Target} target
|
||||
* @returns {Step}
|
||||
*/
|
||||
const getBuildCppStep = target => {
|
||||
return {
|
||||
key: `${getKey(platform)}-build-cpp`,
|
||||
label: `build-cpp`,
|
||||
agents: {
|
||||
os,
|
||||
arch,
|
||||
abi,
|
||||
queue: abi ? `build-${os}-${abi}` : `build-${os}`,
|
||||
},
|
||||
key: `${getTargetKey(target)}-build-cpp`,
|
||||
label: `${getTargetLabel(target)} - build-cpp`,
|
||||
agents: getBuildAgent(target),
|
||||
retry: getRetry(),
|
||||
cancel_on_build_failing: isMergeQueue(),
|
||||
env: {
|
||||
BUN_CPP_ONLY: "ON",
|
||||
ENABLE_BASELINE: baseline ? "ON" : "OFF",
|
||||
...getBuildEnv(target),
|
||||
},
|
||||
command: "bun run build:ci --target bun",
|
||||
};
|
||||
};
|
||||
|
||||
const getBuildZigStep = platform => {
|
||||
const { os, arch, abi, baseline } = platform;
|
||||
const toolchain = getKey(platform);
|
||||
|
||||
/**
|
||||
* @param {Target} target
|
||||
* @returns {Step}
|
||||
*/
|
||||
const getBuildZigStep = target => {
|
||||
const toolchain = getBuildToolchain(target);
|
||||
return {
|
||||
key: `${getKey(platform)}-build-zig`,
|
||||
label: `build-zig`,
|
||||
agents: {
|
||||
queue: "build-zig",
|
||||
},
|
||||
retry: getRetry(),
|
||||
key: `${getTargetKey(target)}-build-zig`,
|
||||
label: `${getTargetLabel(target)} - build-zig`,
|
||||
agents: getZigAgent(target),
|
||||
retry: getRetry(1), // FIXME: Sometimes zig build hangs, so we need to retry once
|
||||
cancel_on_build_failing: isMergeQueue(),
|
||||
env: {
|
||||
ENABLE_BASELINE: baseline ? "ON" : "OFF",
|
||||
},
|
||||
env: getBuildEnv(target),
|
||||
command: `bun run build:ci --target bun-zig --toolchain ${toolchain}`,
|
||||
};
|
||||
};
|
||||
|
||||
const getBuildBunStep = platform => {
|
||||
const { os, arch, abi, baseline } = platform;
|
||||
|
||||
/**
|
||||
* @param {Target} target
|
||||
* @returns {Step}
|
||||
*/
|
||||
const getBuildBunStep = target => {
|
||||
return {
|
||||
key: `${getKey(platform)}-build-bun`,
|
||||
label: `build-bun`,
|
||||
key: `${getTargetKey(target)}-build-bun`,
|
||||
label: `${getTargetLabel(target)} - build-bun`,
|
||||
depends_on: [
|
||||
`${getKey(platform)}-build-vendor`,
|
||||
`${getKey(platform)}-build-cpp`,
|
||||
`${getKey(platform)}-build-zig`,
|
||||
`${getTargetKey(target)}-build-vendor`,
|
||||
`${getTargetKey(target)}-build-cpp`,
|
||||
`${getTargetKey(target)}-build-zig`,
|
||||
],
|
||||
agents: {
|
||||
os,
|
||||
arch,
|
||||
abi,
|
||||
queue: `build-${os}`,
|
||||
},
|
||||
agents: getBuildAgent(target),
|
||||
retry: getRetry(),
|
||||
cancel_on_build_failing: isMergeQueue(),
|
||||
env: {
|
||||
BUN_LINK_ONLY: "ON",
|
||||
ENABLE_BASELINE: baseline ? "ON" : "OFF",
|
||||
...getBuildEnv(target),
|
||||
},
|
||||
command: "bun run build:ci --target bun",
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Platform} platform
|
||||
* @returns {Step}
|
||||
*/
|
||||
const getTestBunStep = platform => {
|
||||
const { os, arch, abi, distro, release } = platform;
|
||||
|
||||
let name;
|
||||
if (os === "darwin" || os === "windows") {
|
||||
name = getLabel({ ...platform, release });
|
||||
} else {
|
||||
name = getLabel({ ...platform, os: distro, release });
|
||||
}
|
||||
|
||||
let agents;
|
||||
if (os === "darwin") {
|
||||
agents = { os, arch, abi, queue: `test-darwin` };
|
||||
} else if (os === "windows") {
|
||||
agents = { os, arch, abi, robobun: true };
|
||||
} else {
|
||||
agents = { os, arch, abi, distro, release, robobun: true };
|
||||
}
|
||||
|
||||
const { os } = platform;
|
||||
let command;
|
||||
if (os === "windows") {
|
||||
command = `node .\\scripts\\runner.node.mjs --step ${getKey(platform)}-build-bun`;
|
||||
command = `node .\\scripts\\runner.node.mjs --step ${getTargetKey(platform)}-build-bun`;
|
||||
} else {
|
||||
command = `./scripts/runner.node.mjs --step ${getKey(platform)}-build-bun`;
|
||||
command = `./scripts/runner.node.mjs --step ${getTargetKey(platform)}-build-bun`;
|
||||
}
|
||||
|
||||
let parallelism;
|
||||
if (os === "darwin") {
|
||||
parallelism = 2;
|
||||
} else {
|
||||
parallelism = 10;
|
||||
}
|
||||
|
||||
let depends;
|
||||
let env;
|
||||
if (buildId) {
|
||||
@@ -264,21 +479,19 @@ function getPipeline(buildId) {
|
||||
BUILDKITE_ARTIFACT_BUILD_ID: buildId,
|
||||
};
|
||||
} else {
|
||||
depends = [`${getKey(platform)}-build-bun`];
|
||||
depends = [`${getTargetKey(platform)}-build-bun`];
|
||||
}
|
||||
|
||||
let retry;
|
||||
if (os !== "windows") {
|
||||
// When the runner fails on Windows, Buildkite only detects an exit code of 1.
|
||||
// Because of this, we don't know if the run was fatal, or soft-failed.
|
||||
retry = getRetry();
|
||||
retry = getRetry(1);
|
||||
}
|
||||
|
||||
return {
|
||||
key: `${getKey(platform)}-${distro}-${release.replace(/\./g, "")}-test-bun`,
|
||||
label: `${name} - test-bun`,
|
||||
key: `${getPlatformKey(platform)}-test-bun`,
|
||||
label: `${getPlatformLabel(platform)} - test-bun`,
|
||||
depends_on: depends,
|
||||
agents,
|
||||
agents: getTestAgent(platform),
|
||||
retry,
|
||||
cancel_on_build_failing: isMergeQueue(),
|
||||
soft_fail: isMainBranch(),
|
||||
@@ -292,52 +505,90 @@ function getPipeline(buildId) {
|
||||
* Config
|
||||
*/
|
||||
|
||||
/**
|
||||
* @type {Platform[]}
|
||||
*/
|
||||
const buildPlatforms = [
|
||||
{ os: "darwin", arch: "aarch64" },
|
||||
{ os: "darwin", arch: "x64" },
|
||||
{ os: "linux", arch: "aarch64" },
|
||||
// { os: "linux", arch: "aarch64", abi: "musl" }, // TODO:
|
||||
{ os: "linux", arch: "x64" },
|
||||
{ os: "linux", arch: "x64", baseline: true },
|
||||
// { os: "linux", arch: "x64", abi: "musl" }, // TODO:
|
||||
{ os: "windows", arch: "x64" },
|
||||
{ os: "windows", arch: "x64", baseline: true },
|
||||
{ os: "darwin", arch: "aarch64", release: "14" },
|
||||
{ os: "darwin", arch: "x64", release: "14" },
|
||||
{ os: "linux", arch: "aarch64", distro: "debian", release: "11" },
|
||||
{ os: "linux", arch: "x64", distro: "debian", release: "11" },
|
||||
{ os: "linux", arch: "x64", baseline: true, distro: "debian", release: "11" },
|
||||
{ os: "linux", arch: "aarch64", abi: "musl", distro: "alpine", release: "3.20" },
|
||||
{ os: "linux", arch: "x64", abi: "musl", distro: "alpine", release: "3.20" },
|
||||
{ os: "linux", arch: "x64", abi: "musl", baseline: true, distro: "alpine", release: "3.20" },
|
||||
{ os: "windows", arch: "x64", release: "2019" },
|
||||
{ os: "windows", arch: "x64", baseline: true, release: "2019" },
|
||||
];
|
||||
|
||||
/**
|
||||
* @type {Platform[]}
|
||||
*/
|
||||
const testPlatforms = [
|
||||
{ os: "darwin", arch: "aarch64", distro: "sonoma", release: "14" },
|
||||
{ os: "darwin", arch: "aarch64", distro: "ventura", release: "13" },
|
||||
{ os: "darwin", arch: "x64", distro: "sonoma", release: "14" },
|
||||
{ os: "darwin", arch: "x64", distro: "ventura", release: "13" },
|
||||
{ os: "darwin", arch: "aarch64", release: "14" },
|
||||
{ os: "darwin", arch: "aarch64", release: "13" },
|
||||
{ os: "darwin", arch: "x64", release: "14" },
|
||||
{ os: "darwin", arch: "x64", release: "13" },
|
||||
{ os: "linux", arch: "aarch64", distro: "debian", release: "12" },
|
||||
// { os: "linux", arch: "aarch64", distro: "debian", release: "11" },
|
||||
// { os: "linux", arch: "aarch64", distro: "debian", release: "10" },
|
||||
{ os: "linux", arch: "x64", distro: "debian", release: "12" },
|
||||
// { os: "linux", arch: "x64", distro: "debian", release: "11" },
|
||||
// { os: "linux", arch: "x64", distro: "debian", release: "10" },
|
||||
{ os: "linux", arch: "x64", baseline: true, distro: "debian", release: "12" },
|
||||
// { os: "linux", arch: "x64", baseline: true, distro: "debian", release: "11" },
|
||||
// { os: "linux", arch: "x64", baseline: true, distro: "debian", release: "10" },
|
||||
// { os: "linux", arch: "aarch64", distro: "ubuntu", release: "24.04" },
|
||||
{ os: "linux", arch: "aarch64", distro: "ubuntu", release: "22.04" },
|
||||
{ os: "linux", arch: "aarch64", distro: "ubuntu", release: "20.04" },
|
||||
// { os: "linux", arch: "aarch64", abi: "musl", distro: "alpine", release: "edge" }, // TODO:
|
||||
{ os: "linux", arch: "x64", distro: "debian", release: "12" },
|
||||
// { os: "linux", arch: "x64", distro: "ubuntu", release: "24.04" },
|
||||
{ os: "linux", arch: "x64", distro: "ubuntu", release: "22.04" },
|
||||
{ os: "linux", arch: "x64", distro: "ubuntu", release: "20.04" },
|
||||
{ os: "linux", arch: "x64", distro: "debian", release: "12", baseline: true },
|
||||
{ os: "linux", arch: "x64", distro: "ubuntu", release: "22.04", baseline: true },
|
||||
{ os: "linux", arch: "x64", distro: "ubuntu", release: "20.04", baseline: true },
|
||||
// { os: "linux", arch: "x64", abi: "musl", distro: "alpine", release: "edge" }, // TODO:
|
||||
{ os: "windows", arch: "x64", distro: "server", release: "2019" },
|
||||
{ os: "windows", arch: "x64", distro: "server", release: "2019", baseline: true },
|
||||
// { os: "linux", arch: "x64", baseline: true, distro: "ubuntu", release: "24.04" },
|
||||
{ os: "linux", arch: "x64", baseline: true, distro: "ubuntu", release: "22.04" },
|
||||
{ os: "linux", arch: "x64", baseline: true, distro: "ubuntu", release: "20.04" },
|
||||
// { os: "linux", arch: "aarch64", distro: "amazonlinux", release: "2023" },
|
||||
// { os: "linux", arch: "aarch64", distro: "amazonlinux", release: "2" },
|
||||
// { os: "linux", arch: "x64", distro: "amazonlinux", release: "2023" },
|
||||
// { os: "linux", arch: "x64", distro: "amazonlinux", release: "2" },
|
||||
// { os: "linux", arch: "x64", baseline: true, distro: "amazonlinux", release: "2023" },
|
||||
// { os: "linux", arch: "x64", baseline: true, distro: "amazonlinux", release: "2" },
|
||||
{ os: "linux", arch: "aarch64", abi: "musl", distro: "alpine", release: "3.20" },
|
||||
// { os: "linux", arch: "aarch64", abi: "musl", distro: "alpine", release: "3.17" },
|
||||
{ os: "linux", arch: "x64", abi: "musl", distro: "alpine", release: "3.20" },
|
||||
// { os: "linux", arch: "x64", abi: "musl", distro: "alpine", release: "3.17" },
|
||||
{ os: "linux", arch: "x64", abi: "musl", baseline: true, distro: "alpine", release: "3.20" },
|
||||
// { os: "linux", arch: "x64", abi: "musl", baseline: true, distro: "alpine", release: "3.17" },
|
||||
{ os: "windows", arch: "x64", release: "2019" },
|
||||
{ os: "windows", arch: "x64", baseline: true, release: "2019" },
|
||||
];
|
||||
|
||||
return {
|
||||
priority: getPriority(),
|
||||
steps: [
|
||||
...buildPlatforms.map(platform => {
|
||||
const { os, arch, baseline } = platform;
|
||||
const imagePlatforms = new Map(
|
||||
[...buildPlatforms, ...testPlatforms]
|
||||
.filter(platform => buildImages && isUsingNewAgent(platform))
|
||||
.map(platform => [getImageKey(platform), platform]),
|
||||
);
|
||||
|
||||
let steps = [
|
||||
...testPlatforms
|
||||
.filter(platform => platform.os === os && platform.arch === arch && baseline === platform.baseline)
|
||||
.map(platform => getTestBunStep(platform)),
|
||||
];
|
||||
/**
|
||||
* @type {Step[]}
|
||||
*/
|
||||
const steps = [];
|
||||
|
||||
if (!buildId) {
|
||||
steps.unshift(
|
||||
if (imagePlatforms.size) {
|
||||
steps.push({
|
||||
group: ":docker:",
|
||||
steps: [...imagePlatforms.values()].map(platform => getBuildImageStep(platform)),
|
||||
});
|
||||
}
|
||||
|
||||
for (const platform of buildPlatforms) {
|
||||
const { os, arch, abi, baseline } = platform;
|
||||
|
||||
/** @type {Step[]} */
|
||||
const platformSteps = [];
|
||||
|
||||
if (buildImages || !buildId) {
|
||||
platformSteps.push(
|
||||
getBuildVendorStep(platform),
|
||||
getBuildCppStep(platform),
|
||||
getBuildZigStep(platform),
|
||||
@@ -345,14 +596,55 @@ function getPipeline(buildId) {
|
||||
);
|
||||
}
|
||||
|
||||
if (!skipTests) {
|
||||
platformSteps.push(
|
||||
...testPlatforms
|
||||
.filter(
|
||||
testPlatform =>
|
||||
testPlatform.os === os &&
|
||||
testPlatform.arch === arch &&
|
||||
testPlatform.abi === abi &&
|
||||
testPlatform.baseline === baseline,
|
||||
)
|
||||
.map(testPlatform => getTestBunStep(testPlatform)),
|
||||
);
|
||||
}
|
||||
|
||||
if (!platformSteps.length) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (imagePlatforms.has(getImageKey(platform))) {
|
||||
for (const step of platformSteps) {
|
||||
if (step.agents?.["image-name"]) {
|
||||
step.depends_on ??= [];
|
||||
step.depends_on.push(`${getImageKey(platform)}-build-image`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
steps.push({
|
||||
key: getTargetKey(platform),
|
||||
group: getTargetLabel(platform),
|
||||
steps: platformSteps,
|
||||
});
|
||||
}
|
||||
|
||||
if (isMainBranch() && !isFork()) {
|
||||
steps.push({
|
||||
label: ":github:",
|
||||
agents: {
|
||||
queue: "test-darwin",
|
||||
},
|
||||
depends_on: buildPlatforms.map(platform => `${getTargetKey(platform)}-build-bun`),
|
||||
command: ".buildkite/scripts/upload-release.sh",
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
key: getKey(platform),
|
||||
group: getLabel(platform),
|
||||
priority: getPriority(),
|
||||
steps,
|
||||
};
|
||||
}),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
async function main() {
|
||||
@@ -369,7 +661,6 @@ async function main() {
|
||||
console.log(" - No build found");
|
||||
}
|
||||
|
||||
|
||||
let changedFiles;
|
||||
if (!isFork()) {
|
||||
console.log("Checking changed files...");
|
||||
@@ -418,6 +709,31 @@ async function main() {
|
||||
}
|
||||
}
|
||||
|
||||
console.log("Checking if CI should re-build images...");
|
||||
let buildImages;
|
||||
{
|
||||
const message = getCommitMessage();
|
||||
const match = /\[(build images?|images? build)\]/i.exec(message);
|
||||
if (match) {
|
||||
const [, reason] = match;
|
||||
console.log(" - Yes, because commit message contains:", reason);
|
||||
buildImages = true;
|
||||
}
|
||||
}
|
||||
|
||||
console.log("Checking if CI should publish images...");
|
||||
let publishImages;
|
||||
{
|
||||
const message = getCommitMessage();
|
||||
const match = /\[(publish images?|images? publish)\]/i.exec(message);
|
||||
if (match) {
|
||||
const [, reason] = match;
|
||||
console.log(" - Yes, because commit message contains:", reason);
|
||||
publishImages = true;
|
||||
buildImages = true;
|
||||
}
|
||||
}
|
||||
|
||||
console.log("Checking if build should be skipped...");
|
||||
let skipBuild;
|
||||
if (!forceBuild) {
|
||||
@@ -434,6 +750,17 @@ async function main() {
|
||||
}
|
||||
}
|
||||
|
||||
console.log("Checking if tests should be skipped...");
|
||||
let skipTests;
|
||||
{
|
||||
const message = getCommitMessage();
|
||||
const match = /\[(skip tests?|tests? skip|no tests?|tests? no)\]/i.exec(message);
|
||||
if (match) {
|
||||
console.log(" - Yes, because commit message contains:", match[1]);
|
||||
skipTests = true;
|
||||
}
|
||||
}
|
||||
|
||||
console.log("Checking if build is a named release...");
|
||||
let buildRelease;
|
||||
{
|
||||
@@ -447,7 +774,13 @@ async function main() {
|
||||
}
|
||||
|
||||
console.log("Generating pipeline...");
|
||||
const pipeline = getPipeline(lastBuild && skipBuild && !forceBuild ? lastBuild.id : undefined);
|
||||
const pipeline = getPipeline({
|
||||
buildId: lastBuild && skipBuild && !forceBuild ? lastBuild.id : undefined,
|
||||
buildImages,
|
||||
publishImages,
|
||||
skipTests,
|
||||
});
|
||||
|
||||
const content = toYaml(pipeline);
|
||||
const contentPath = join(process.cwd(), ".buildkite", "ci.yml");
|
||||
writeFileSync(contentPath, content);
|
||||
@@ -455,14 +788,17 @@ async function main() {
|
||||
console.log("Generated pipeline:");
|
||||
console.log(" - Path:", contentPath);
|
||||
console.log(" - Size:", (content.length / 1024).toFixed(), "KB");
|
||||
if (isBuildkite) {
|
||||
await uploadArtifact(contentPath);
|
||||
}
|
||||
|
||||
if (isBuildkite) {
|
||||
console.log("Setting canary revision...");
|
||||
const canaryRevision = buildRelease ? 0 : await getCanaryRevision();
|
||||
await spawnSafe(["buildkite-agent", "meta-data", "set", "canary", `${canaryRevision}`]);
|
||||
await spawnSafe(["buildkite-agent", "meta-data", "set", "canary", `${canaryRevision}`], { stdio: "inherit" });
|
||||
|
||||
console.log("Uploading pipeline...");
|
||||
await spawnSafe(["buildkite-agent", "pipeline", "upload", contentPath]);
|
||||
await spawnSafe(["buildkite-agent", "pipeline", "upload", contentPath], { stdio: "inherit" });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -202,6 +202,12 @@ function create_release() {
|
||||
bun-linux-x64-profile.zip
|
||||
bun-linux-x64-baseline.zip
|
||||
bun-linux-x64-baseline-profile.zip
|
||||
bun-linux-aarch64-musl.zip
|
||||
bun-linux-aarch64-musl-profile.zip
|
||||
bun-linux-x64-musl.zip
|
||||
bun-linux-x64-musl-profile.zip
|
||||
bun-linux-x64-musl-baseline.zip
|
||||
bun-linux-x64-musl-baseline-profile.zip
|
||||
bun-windows-x64.zip
|
||||
bun-windows-x64-profile.zip
|
||||
bun-windows-x64-baseline.zip
|
||||
|
||||
18
ci/linux/Dockerfile
Normal file
18
ci/linux/Dockerfile
Normal file
@@ -0,0 +1,18 @@
|
||||
ARG IMAGE=debian:11
|
||||
FROM $IMAGE
|
||||
COPY ./scripts/bootstrap.sh /tmp/bootstrap.sh
|
||||
ENV CI=true
|
||||
RUN sh /tmp/bootstrap.sh && rm -rf /tmp/*
|
||||
WORKDIR /workspace/bun
|
||||
COPY bunfig.toml bunfig.toml
|
||||
COPY package.json package.json
|
||||
COPY CMakeLists.txt CMakeLists.txt
|
||||
COPY cmake/ cmake/
|
||||
COPY scripts/ scripts/
|
||||
COPY patches/ patches/
|
||||
COPY *.zig ./
|
||||
COPY src/ src/
|
||||
COPY packages/ packages/
|
||||
COPY test/ test/
|
||||
RUN bun i
|
||||
RUN bun run build:ci
|
||||
27
ci/linux/scripts/set-hostname.sh
Normal file
27
ci/linux/scripts/set-hostname.sh
Normal file
@@ -0,0 +1,27 @@
|
||||
#!/bin/sh
|
||||
|
||||
# This script sets the hostname of the current machine.
|
||||
|
||||
execute() {
|
||||
echo "$ $@" >&2
|
||||
if ! "$@"; then
|
||||
echo "Command failed: $@" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
main() {
|
||||
if [ "$#" -ne 1 ]; then
|
||||
echo "Usage: $0 <hostname>" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -f "$(which hostnamectl)" ]; then
|
||||
execute hostnamectl set-hostname "$1"
|
||||
else
|
||||
echo "Error: hostnamectl is not installed." >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
main "$@"
|
||||
22
ci/linux/scripts/start-tailscale.sh
Normal file
22
ci/linux/scripts/start-tailscale.sh
Normal file
@@ -0,0 +1,22 @@
|
||||
#!/bin/sh
|
||||
|
||||
# This script starts tailscale on the current machine.
|
||||
|
||||
execute() {
|
||||
echo "$ $@" >&2
|
||||
if ! "$@"; then
|
||||
echo "Command failed: $@" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
main() {
|
||||
if [ "$#" -ne 1 ]; then
|
||||
echo "Usage: $0 <auth-key>" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
execute tailscale up --reset --ssh --accept-risk=lose-ssh --auth-key="$1"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
@@ -2,7 +2,7 @@
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"bootstrap": "brew install gh jq cirruslabs/cli/tart cirruslabs/cli/sshpass hashicorp/tap/packer && packer init darwin",
|
||||
"login": "gh auth token | tart login ghcr.io --username $(gh api user --jq .login) --password-stdin",
|
||||
"login": "token=$(gh auth token); username=$(gh api user --jq .login); echo \"Login as $username...\"; echo \"$token\" | tart login ghcr.io --username \"$username\" --password-stdin; echo \"$token\" | docker login ghcr.io --username \"$username\" --password-stdin",
|
||||
"fetch:image-name": "echo ghcr.io/oven-sh/bun-vm",
|
||||
"fetch:darwin-version": "echo 1",
|
||||
"fetch:macos-version": "sw_vers -productVersion | cut -d. -f1",
|
||||
|
||||
@@ -105,14 +105,6 @@ else()
|
||||
unsupported(CMAKE_HOST_SYSTEM_NAME)
|
||||
endif()
|
||||
|
||||
if(EXISTS "/lib/ld-musl-aarch64.so.1")
|
||||
set(IS_MUSL ON)
|
||||
elseif(EXISTS "/lib/ld-musl-x86_64.so.1")
|
||||
set(IS_MUSL ON)
|
||||
else()
|
||||
set(IS_MUSL OFF)
|
||||
endif()
|
||||
|
||||
if(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "arm64|ARM64|aarch64|AARCH64")
|
||||
set(HOST_OS "aarch64")
|
||||
elseif(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "x86_64|X86_64|x64|X64|amd64|AMD64")
|
||||
@@ -144,6 +136,16 @@ else()
|
||||
set(WARNING WARNING)
|
||||
endif()
|
||||
|
||||
if(LINUX)
|
||||
if(EXISTS "/etc/alpine-release")
|
||||
set(DEFAULT_ABI "musl")
|
||||
else()
|
||||
set(DEFAULT_ABI "gnu")
|
||||
endif()
|
||||
|
||||
optionx(ABI "musl|gnu" "The ABI to use (e.g. musl, gnu)" DEFAULT ${DEFAULT_ABI})
|
||||
endif()
|
||||
|
||||
# TODO: This causes flaky zig builds in CI, so temporarily disable it.
|
||||
# if(CI)
|
||||
# set(DEFAULT_VENDOR_PATH ${CACHE_PATH}/vendor)
|
||||
|
||||
@@ -484,14 +484,12 @@ set(BUN_ZIG_OUTPUT ${BUILD_PATH}/bun-zig.o)
|
||||
|
||||
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm|ARM|arm64|ARM64|aarch64|AARCH64")
|
||||
set(IS_ARM64 ON)
|
||||
if(APPLE)
|
||||
set(ZIG_CPU "apple_m1")
|
||||
else()
|
||||
set(ZIG_CPU "native")
|
||||
endif()
|
||||
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|X86_64|x64|X64|amd64|AMD64")
|
||||
set(IS_X86_64 ON)
|
||||
if(ENABLE_BASELINE)
|
||||
set(ZIG_CPU "nehalem")
|
||||
else()
|
||||
@@ -761,8 +759,8 @@ if(NOT WIN32)
|
||||
)
|
||||
if(DEBUG)
|
||||
# TODO: this shouldn't be necessary long term
|
||||
if (NOT IS_MUSL)
|
||||
set(ABI_PUBLIC_FLAGS
|
||||
if (NOT ABI STREQUAL "musl")
|
||||
target_compile_options(${bun} PUBLIC
|
||||
-fsanitize=null
|
||||
-fsanitize-recover=all
|
||||
-fsanitize=bounds
|
||||
@@ -773,14 +771,9 @@ if(NOT WIN32)
|
||||
-fsanitize=returns-nonnull-attribute
|
||||
-fsanitize=unreachable
|
||||
)
|
||||
set(ABI_PRIVATE_FLAGS
|
||||
target_link_libraries(${bun} PRIVATE
|
||||
-fsanitize=null
|
||||
)
|
||||
else()
|
||||
set(ABI_PUBLIC_FLAGS
|
||||
)
|
||||
set(ABI_PRIVATE_FLAGS
|
||||
)
|
||||
endif()
|
||||
|
||||
target_compile_options(${bun} PUBLIC
|
||||
@@ -798,10 +791,6 @@ if(NOT WIN32)
|
||||
-Wno-unused-function
|
||||
-Wno-nullability-completeness
|
||||
-Werror
|
||||
${ABI_PUBLIC_FLAGS}
|
||||
)
|
||||
target_link_libraries(${bun} PRIVATE
|
||||
${ABI_PRIVATE_FLAGS}
|
||||
)
|
||||
else()
|
||||
# Leave -Werror=unused off in release builds so we avoid errors from being used in ASSERT
|
||||
@@ -846,7 +835,9 @@ if(WIN32)
|
||||
/delayload:IPHLPAPI.dll
|
||||
)
|
||||
endif()
|
||||
elseif(APPLE)
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
target_link_options(${bun} PUBLIC
|
||||
-dead_strip
|
||||
-dead_strip_dylibs
|
||||
@@ -856,41 +847,19 @@ elseif(APPLE)
|
||||
-fno-keep-static-consts
|
||||
-Wl,-map,${bun}.linker-map
|
||||
)
|
||||
else()
|
||||
# Try to use lld-16 if available, otherwise fallback to lld
|
||||
# Cache it so we don't have to re-run CMake to pick it up
|
||||
if((NOT DEFINED LLD_NAME) AND (NOT CI OR BUN_LINK_ONLY))
|
||||
find_program(LLD_EXECUTABLE_NAME lld-${LLVM_VERSION_MAJOR})
|
||||
|
||||
if(NOT LLD_EXECUTABLE_NAME)
|
||||
if(CI)
|
||||
# Ensure we don't use a differing version of lld in CI vs clang
|
||||
message(FATAL_ERROR "lld-${LLVM_VERSION_MAJOR} not found. Please make sure you have LLVM ${LLVM_VERSION_MAJOR}.x installed and set to lld-${LLVM_VERSION_MAJOR}")
|
||||
endif()
|
||||
|
||||
# To make it easier for contributors, allow differing versions of lld vs clang/cmake
|
||||
find_program(LLD_EXECUTABLE_NAME lld)
|
||||
endif()
|
||||
|
||||
if(NOT LLD_EXECUTABLE_NAME)
|
||||
message(FATAL_ERROR "LLD not found. Please make sure you have LLVM ${LLVM_VERSION_MAJOR}.x installed and lld is available in your PATH as lld-${LLVM_VERSION_MAJOR}")
|
||||
endif()
|
||||
|
||||
# normalize to basename so it can be used with -fuse-ld
|
||||
get_filename_component(LLD_NAME ${LLD_EXECUTABLE_NAME} NAME CACHE)
|
||||
message(STATUS "Using linker: ${LLD_NAME} (${LLD_EXECUTABLE_NAME})")
|
||||
elseif(NOT DEFINED LLD_NAME)
|
||||
set(LLD_NAME lld-${LLVM_VERSION_MAJOR})
|
||||
endif()
|
||||
|
||||
if (NOT IS_MUSL)
|
||||
if (IS_ARM64)
|
||||
set(ARCH_WRAP_FLAGS
|
||||
if(LINUX)
|
||||
if(NOT ABI STREQUAL "musl")
|
||||
if(ARCH STREQUAL "aarch64")
|
||||
target_link_options(${bun} PUBLIC
|
||||
-Wl,--wrap=fcntl64
|
||||
-Wl,--wrap=statx
|
||||
)
|
||||
elseif(IS_X86_64)
|
||||
set(ARCH_WRAP_FLAGS
|
||||
endif()
|
||||
|
||||
if(ARCH STREQUAL "x64")
|
||||
target_link_options(${bun} PUBLIC
|
||||
-Wl,--wrap=fcntl
|
||||
-Wl,--wrap=fcntl64
|
||||
-Wl,--wrap=fstat
|
||||
@@ -906,13 +875,8 @@ else()
|
||||
-Wl,--wrap=statx
|
||||
)
|
||||
endif()
|
||||
else()
|
||||
set(ARCH_WRAP_FLAGS
|
||||
)
|
||||
endif()
|
||||
|
||||
if (NOT IS_MUSL)
|
||||
set(ABI_WRAP_FLAGS
|
||||
target_link_options(${bun} PUBLIC
|
||||
-Wl,--wrap=cosf
|
||||
-Wl,--wrap=exp
|
||||
-Wl,--wrap=expf
|
||||
@@ -929,13 +893,10 @@ else()
|
||||
-Wl,--wrap=sinf
|
||||
-Wl,--wrap=tanf
|
||||
)
|
||||
else()
|
||||
set(ABI_WRAP_FLAGS
|
||||
)
|
||||
endif()
|
||||
|
||||
target_link_options(${bun} PUBLIC
|
||||
-fuse-ld=${LLD_NAME}
|
||||
--ld-path=${LLD_PROGRAM}
|
||||
-fno-pic
|
||||
-static-libstdc++
|
||||
-static-libgcc
|
||||
@@ -944,8 +905,6 @@ else()
|
||||
-Wl,--as-needed
|
||||
-Wl,--gc-sections
|
||||
-Wl,-z,stack-size=12800000
|
||||
${ARCH_WRAP_FLAGS}
|
||||
${ABI_WRAP_FLAGS}
|
||||
-Wl,--compress-debug-sections=zlib
|
||||
-Wl,-z,lazy
|
||||
-Wl,-z,norelro
|
||||
@@ -1100,7 +1059,7 @@ if(NOT BUN_CPP_ONLY)
|
||||
# and other dependencies. We build lolhtml with abort on panic, so it
|
||||
# shouldn't be including these in the first place.
|
||||
set(CMAKE_STRIP_FLAGS --remove-section=__TEXT,__eh_frame --remove-section=__TEXT,__unwind_info --remove-section=__TEXT,__gcc_except_tab)
|
||||
elseif(LINUX)
|
||||
elseif(LINUX AND NOT ABI STREQUAL "musl")
|
||||
# When you use llvm-strip to do this, it doesn't delete it from the binary and instead keeps it as [LOAD #2 [R]]
|
||||
# So, we must use GNU strip to do this.
|
||||
set(CMAKE_STRIP_FLAGS -R .eh_frame -R .gcc_except_table)
|
||||
@@ -1193,10 +1152,12 @@ if(NOT BUN_CPP_ONLY)
|
||||
endif()
|
||||
|
||||
if(CI)
|
||||
if(ENABLE_BASELINE)
|
||||
set(bunTriplet bun-${OS}-${ARCH}-baseline)
|
||||
else()
|
||||
set(bunTriplet bun-${OS}-${ARCH})
|
||||
if(ABI STREQUAL "musl")
|
||||
set(bunTriplet ${bunTriplet}-musl)
|
||||
endif()
|
||||
if(ENABLE_BASELINE)
|
||||
set(bunTriplet ${bunTriplet}-baseline)
|
||||
endif()
|
||||
string(REPLACE bun ${bunTriplet} bunPath ${bun})
|
||||
set(bunFiles ${bunExe} features.json)
|
||||
|
||||
6
cmake/toolchains/linux-aarch64-musl.cmake
Normal file
6
cmake/toolchains/linux-aarch64-musl.cmake
Normal file
@@ -0,0 +1,6 @@
|
||||
set(CMAKE_SYSTEM_NAME Linux)
|
||||
set(CMAKE_SYSTEM_PROCESSOR aarch64)
|
||||
set(ABI musl)
|
||||
|
||||
set(CMAKE_C_COMPILER_WORKS ON)
|
||||
set(CMAKE_CXX_COMPILER_WORKS ON)
|
||||
@@ -1,5 +1,6 @@
|
||||
set(CMAKE_SYSTEM_NAME Linux)
|
||||
set(CMAKE_SYSTEM_PROCESSOR aarch64)
|
||||
set(ABI gnu)
|
||||
|
||||
set(CMAKE_C_COMPILER_WORKS ON)
|
||||
set(CMAKE_CXX_COMPILER_WORKS ON)
|
||||
@@ -1,6 +1,7 @@
|
||||
set(CMAKE_SYSTEM_NAME Linux)
|
||||
set(CMAKE_SYSTEM_PROCESSOR x64)
|
||||
set(ENABLE_BASELINE ON)
|
||||
set(ABI gnu)
|
||||
|
||||
set(CMAKE_C_COMPILER_WORKS ON)
|
||||
set(CMAKE_CXX_COMPILER_WORKS ON)
|
||||
7
cmake/toolchains/linux-x64-musl-baseline.cmake
Normal file
7
cmake/toolchains/linux-x64-musl-baseline.cmake
Normal file
@@ -0,0 +1,7 @@
|
||||
set(CMAKE_SYSTEM_NAME Linux)
|
||||
set(CMAKE_SYSTEM_PROCESSOR x64)
|
||||
set(ENABLE_BASELINE ON)
|
||||
set(ABI musl)
|
||||
|
||||
set(CMAKE_C_COMPILER_WORKS ON)
|
||||
set(CMAKE_CXX_COMPILER_WORKS ON)
|
||||
6
cmake/toolchains/linux-x64-musl.cmake
Normal file
6
cmake/toolchains/linux-x64-musl.cmake
Normal file
@@ -0,0 +1,6 @@
|
||||
set(CMAKE_SYSTEM_NAME Linux)
|
||||
set(CMAKE_SYSTEM_PROCESSOR x64)
|
||||
set(ABI musl)
|
||||
|
||||
set(CMAKE_C_COMPILER_WORKS ON)
|
||||
set(CMAKE_CXX_COMPILER_WORKS ON)
|
||||
@@ -1,5 +1,6 @@
|
||||
set(CMAKE_SYSTEM_NAME Linux)
|
||||
set(CMAKE_SYSTEM_PROCESSOR x64)
|
||||
set(ABI gnu)
|
||||
|
||||
set(CMAKE_C_COMPILER_WORKS ON)
|
||||
set(CMAKE_CXX_COMPILER_WORKS ON)
|
||||
|
||||
@@ -29,7 +29,7 @@ execute_process(
|
||||
)
|
||||
|
||||
if(NOT GIT_DIFF_RESULT EQUAL 0)
|
||||
message(${WARNING} "Command failed: ${GIT_DIFF_COMMAND} ${GIT_DIFF_ERROR}")
|
||||
message(WARNING "Command failed: ${GIT_DIFF_COMMAND} ${GIT_DIFF_ERROR}")
|
||||
return()
|
||||
endif()
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ if(NOT ENABLE_LLVM)
|
||||
return()
|
||||
endif()
|
||||
|
||||
if(CMAKE_HOST_WIN32 OR CMAKE_HOST_APPLE OR IS_MUSL)
|
||||
if(CMAKE_HOST_WIN32 OR CMAKE_HOST_APPLE OR ABI STREQUAL "musl")
|
||||
set(DEFAULT_LLVM_VERSION "18.1.8")
|
||||
else()
|
||||
set(DEFAULT_LLVM_VERSION "16.0.6")
|
||||
@@ -52,6 +52,7 @@ if(UNIX)
|
||||
/usr/lib/llvm-${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}.${LLVM_VERSION_PATCH}/bin
|
||||
/usr/lib/llvm-${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}/bin
|
||||
/usr/lib/llvm-${LLVM_VERSION_MAJOR}/bin
|
||||
/usr/lib/llvm${LLVM_VERSION_MAJOR}/bin
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
@@ -122,6 +123,9 @@ else()
|
||||
find_llvm_command(CMAKE_STRIP llvm-strip)
|
||||
endif()
|
||||
find_llvm_command(CMAKE_RANLIB llvm-ranlib)
|
||||
if(LINUX)
|
||||
find_llvm_command(LLD_PROGRAM ld.lld)
|
||||
endif()
|
||||
if(APPLE)
|
||||
find_llvm_command(CMAKE_DSYMUTIL dsymutil)
|
||||
endif()
|
||||
|
||||
@@ -63,7 +63,7 @@ else()
|
||||
message(FATAL_ERROR "Unsupported architecture: ${CMAKE_SYSTEM_PROCESSOR}")
|
||||
endif()
|
||||
|
||||
if(IS_MUSL)
|
||||
if(ABI STREQUAL "musl")
|
||||
set(WEBKIT_SUFFIX "-musl")
|
||||
endif()
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ if(APPLE)
|
||||
elseif(WIN32)
|
||||
set(DEFAULT_ZIG_TARGET ${DEFAULT_ZIG_ARCH}-windows-msvc)
|
||||
elseif(LINUX)
|
||||
if(IS_MUSL)
|
||||
if(ABI STREQUAL "musl")
|
||||
set(DEFAULT_ZIG_TARGET ${DEFAULT_ZIG_ARCH}-linux-musl)
|
||||
else()
|
||||
set(DEFAULT_ZIG_TARGET ${DEFAULT_ZIG_ARCH}-linux-gnu)
|
||||
|
||||
230
scripts/agent.mjs
Executable file
230
scripts/agent.mjs
Executable file
@@ -0,0 +1,230 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
// An agent that starts buildkite-agent and runs others services.
|
||||
|
||||
import { join } from "node:path";
|
||||
import { realpathSync } from "node:fs";
|
||||
import {
|
||||
isWindows,
|
||||
getOs,
|
||||
getArch,
|
||||
getKernel,
|
||||
getAbi,
|
||||
getAbiVersion,
|
||||
getDistro,
|
||||
getDistroVersion,
|
||||
getHostname,
|
||||
getCloud,
|
||||
getCloudMetadataTag,
|
||||
which,
|
||||
getEnv,
|
||||
writeFile,
|
||||
spawnSafe,
|
||||
} from "./utils.mjs";
|
||||
import { parseArgs } from "node:util";
|
||||
|
||||
/**
|
||||
* @param {"install" | "start"} action
|
||||
*/
|
||||
async function doBuildkiteAgent(action) {
|
||||
const username = "buildkite-agent";
|
||||
const command = which("buildkite-agent", { required: true });
|
||||
|
||||
let homePath, cachePath, logsPath, agentLogPath, pidPath;
|
||||
if (isWindows) {
|
||||
throw new Error("TODO: Windows");
|
||||
} else {
|
||||
homePath = "/var/lib/buildkite-agent";
|
||||
cachePath = "/var/cache/buildkite-agent";
|
||||
logsPath = "/var/log/buildkite-agent";
|
||||
agentLogPath = join(logsPath, "buildkite-agent.log");
|
||||
pidPath = join(logsPath, "buildkite-agent.pid");
|
||||
}
|
||||
|
||||
async function install() {
|
||||
const command = process.execPath;
|
||||
const args = [realpathSync(process.argv[1]), "start"];
|
||||
|
||||
if (isOpenRc()) {
|
||||
const servicePath = "/etc/init.d/buildkite-agent";
|
||||
const service = `#!/sbin/openrc-run
|
||||
name="buildkite-agent"
|
||||
description="Buildkite Agent"
|
||||
command=${escape(command)}
|
||||
command_args=${escape(args.map(escape).join(" "))}
|
||||
command_user=${escape(username)}
|
||||
|
||||
pidfile=${escape(pidPath)}
|
||||
start_stop_daemon_args=" \
|
||||
--background \
|
||||
--make-pidfile \
|
||||
--stdout ${escape(agentLogPath)} \
|
||||
--stderr ${escape(agentLogPath)}"
|
||||
|
||||
depend() {
|
||||
need net
|
||||
use dns logger
|
||||
}
|
||||
`;
|
||||
writeFile(servicePath, service, { mode: 0o755 });
|
||||
await spawnSafe(["rc-update", "add", "buildkite-agent", "default"], { stdio: "inherit", privileged: true });
|
||||
}
|
||||
|
||||
if (isSystemd()) {
|
||||
const servicePath = "/etc/systemd/system/buildkite-agent.service";
|
||||
const service = `
|
||||
[Unit]
|
||||
Description=Buildkite Agent
|
||||
After=syslog.target
|
||||
After=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=${username}
|
||||
ExecStart=${escape(command)} ${escape(args.map(escape).join(" "))}
|
||||
RestartSec=5
|
||||
Restart=on-failure
|
||||
KillMode=process
|
||||
|
||||
[Journal]
|
||||
Storage=persistent
|
||||
StateDirectory=${escape(agentLogPath)}
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
`;
|
||||
writeFile(servicePath, service);
|
||||
await spawnSafe(["systemctl", "daemon-reload"], { stdio: "inherit", privileged: true });
|
||||
await spawnSafe(["systemctl", "enable", "buildkite-agent"], { stdio: "inherit", privileged: true });
|
||||
}
|
||||
}
|
||||
|
||||
async function start() {
|
||||
const cloud = await getCloud();
|
||||
|
||||
let token = getEnv("BUILDKITE_AGENT_TOKEN", false);
|
||||
if (!token && cloud) {
|
||||
token = await getCloudMetadataTag("buildkite:token");
|
||||
}
|
||||
|
||||
let shell;
|
||||
if (isWindows) {
|
||||
const pwsh = which(["pwsh", "powershell"], { required: true });
|
||||
shell = `${pwsh} -Command`;
|
||||
} else {
|
||||
const sh = which(["bash", "sh"], { required: true });
|
||||
shell = `${sh} -c`;
|
||||
}
|
||||
|
||||
const flags = ["enable-job-log-tmpfile", "no-feature-reporting"];
|
||||
const options = {
|
||||
"name": getHostname(),
|
||||
"token": token || "xxx",
|
||||
"shell": shell,
|
||||
"job-log-path": logsPath,
|
||||
"build-path": join(homePath, "builds"),
|
||||
"hooks-path": join(homePath, "hooks"),
|
||||
"plugins-path": join(homePath, "plugins"),
|
||||
"experiment": "normalised-upload-paths,resolve-commit-after-checkout,agent-api",
|
||||
};
|
||||
|
||||
let ephemeral;
|
||||
if (cloud) {
|
||||
const jobId = await getCloudMetadataTag("buildkite:job-uuid");
|
||||
if (jobId) {
|
||||
options["acquire-job"] = jobId;
|
||||
flags.push("disconnect-after-job");
|
||||
ephemeral = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (ephemeral) {
|
||||
options["git-clone-flags"] = "-v --depth=1";
|
||||
options["git-fetch-flags"] = "-v --prune --depth=1";
|
||||
} else {
|
||||
options["git-mirrors-path"] = join(cachePath, "git");
|
||||
}
|
||||
|
||||
const tags = {
|
||||
"os": getOs(),
|
||||
"arch": getArch(),
|
||||
"kernel": getKernel(),
|
||||
"abi": getAbi(),
|
||||
"abi-version": getAbiVersion(),
|
||||
"distro": getDistro(),
|
||||
"distro-version": getDistroVersion(),
|
||||
"cloud": cloud,
|
||||
};
|
||||
|
||||
if (cloud) {
|
||||
const requiredTags = ["robobun", "robobun2"];
|
||||
for (const tag of requiredTags) {
|
||||
const value = await getCloudMetadataTag(tag);
|
||||
if (typeof value === "string") {
|
||||
tags[tag] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
options["tags"] = Object.entries(tags)
|
||||
.filter(([, value]) => value)
|
||||
.map(([key, value]) => `${key}=${value}`)
|
||||
.join(",");
|
||||
|
||||
await spawnSafe(
|
||||
[
|
||||
command,
|
||||
"start",
|
||||
...flags.map(flag => `--${flag}`),
|
||||
...Object.entries(options).map(([key, value]) => `--${key}=${value}`),
|
||||
],
|
||||
{
|
||||
stdio: "inherit",
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if (action === "install") {
|
||||
await install();
|
||||
} else if (action === "start") {
|
||||
await start();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function isSystemd() {
|
||||
return !!which("systemctl");
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function isOpenRc() {
|
||||
return !!which("rc-service");
|
||||
}
|
||||
|
||||
function escape(string) {
|
||||
return JSON.stringify(string);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const { positionals: args } = parseArgs({
|
||||
allowPositionals: true,
|
||||
});
|
||||
|
||||
if (!args.length || args.includes("install")) {
|
||||
console.log("Installing agent...");
|
||||
await doBuildkiteAgent("install");
|
||||
console.log("Agent installed.");
|
||||
}
|
||||
|
||||
if (args.includes("start")) {
|
||||
console.log("Starting agent...");
|
||||
await doBuildkiteAgent("start");
|
||||
console.log("Agent started.");
|
||||
}
|
||||
}
|
||||
|
||||
await main();
|
||||
File diff suppressed because it is too large
Load Diff
0
scripts/build.mjs
Normal file → Executable file
0
scripts/build.mjs
Normal file → Executable file
0
scripts/features.mjs
Normal file → Executable file
0
scripts/features.mjs
Normal file → Executable file
1201
scripts/machine.mjs
Executable file
1201
scripts/machine.mjs
Executable file
File diff suppressed because it is too large
Load Diff
@@ -39,7 +39,7 @@ import {
|
||||
} from "./utils.mjs";
|
||||
import { userInfo } from "node:os";
|
||||
|
||||
const cwd = dirname(import.meta.dirname);
|
||||
const cwd = import.meta.dirname ? dirname(import.meta.dirname) : process.cwd();
|
||||
const testsPath = join(cwd, "test");
|
||||
|
||||
const spawnTimeout = 5_000;
|
||||
@@ -232,7 +232,7 @@ async function runTests() {
|
||||
if (testRunner === "bun") {
|
||||
await runTest(title, () => spawnBunTest(execPath, testPath, { cwd: vendorPath }));
|
||||
} else {
|
||||
const testRunnerPath = join(import.meta.dirname, "..", "test", "runners", `${testRunner}.ts`);
|
||||
const testRunnerPath = join(cwd, "test", "runners", `${testRunner}.ts`);
|
||||
if (!existsSync(testRunnerPath)) {
|
||||
throw new Error(`Unsupported test runner: ${testRunner}`);
|
||||
}
|
||||
@@ -632,7 +632,7 @@ function parseTestStdout(stdout, testPath) {
|
||||
const removeStart = lines.length - skipCount;
|
||||
const removeCount = skipCount - 2;
|
||||
const omitLine = `${getAnsi("gray")}... omitted ${removeCount} tests ...${getAnsi("reset")}`;
|
||||
lines = lines.toSpliced(removeStart, removeCount, omitLine);
|
||||
lines.splice(removeStart, removeCount, omitLine);
|
||||
}
|
||||
skipCount = 0;
|
||||
}
|
||||
@@ -1133,6 +1133,13 @@ function addPath(...paths) {
|
||||
return paths.join(":");
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string | undefined}
|
||||
*/
|
||||
function getTestLabel() {
|
||||
return getBuildLabel()?.replace(" - test-bun", "");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {TestResult | TestResult[]} result
|
||||
* @param {boolean} concise
|
||||
@@ -1140,7 +1147,7 @@ function addPath(...paths) {
|
||||
*/
|
||||
function formatTestToMarkdown(result, concise) {
|
||||
const results = Array.isArray(result) ? result : [result];
|
||||
const buildLabel = getBuildLabel();
|
||||
const buildLabel = getTestLabel();
|
||||
const buildUrl = getBuildUrl();
|
||||
const platform = buildUrl ? `<a href="${buildUrl}">${buildLabel}</a>` : buildLabel;
|
||||
|
||||
@@ -1273,7 +1280,7 @@ function reportAnnotationToBuildKite({ label, content, style = "error", priority
|
||||
const cause = error ?? signal ?? `code ${status}`;
|
||||
throw new Error(`Failed to create annotation: ${label}`, { cause });
|
||||
}
|
||||
const buildLabel = getBuildLabel();
|
||||
const buildLabel = getTestLabel();
|
||||
const buildUrl = getBuildUrl();
|
||||
const platform = buildUrl ? `<a href="${buildUrl}">${buildLabel}</a>` : buildLabel;
|
||||
let errorMessage = `<details><summary><a><code>${label}</code></a> - annotation error on ${platform}</summary>`;
|
||||
|
||||
610
scripts/utils.mjs
Normal file → Executable file
610
scripts/utils.mjs
Normal file → Executable file
@@ -3,9 +3,18 @@
|
||||
|
||||
import { spawn as nodeSpawn, spawnSync as nodeSpawnSync } from "node:child_process";
|
||||
import { createHash } from "node:crypto";
|
||||
import { appendFileSync, existsSync, mkdtempSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
|
||||
import { writeFile } from "node:fs/promises";
|
||||
import { hostname, tmpdir as nodeTmpdir, userInfo } from "node:os";
|
||||
import {
|
||||
appendFileSync,
|
||||
chmodSync,
|
||||
existsSync,
|
||||
mkdirSync,
|
||||
mkdtempSync,
|
||||
readdirSync,
|
||||
readFileSync,
|
||||
writeFileSync,
|
||||
} from "node:fs";
|
||||
import { connect } from "node:net";
|
||||
import { hostname, tmpdir as nodeTmpdir, userInfo, release } from "node:os";
|
||||
import { dirname, join, relative, resolve } from "node:path";
|
||||
import { normalize as normalizeWindows } from "node:path/win32";
|
||||
|
||||
@@ -53,8 +62,9 @@ export function getSecret(name, options = { required: true, redact: true }) {
|
||||
command.push("--skip-redaction");
|
||||
}
|
||||
|
||||
const { error, stdout: secret } = spawnSync(command);
|
||||
if (error || !secret.trim()) {
|
||||
const { error, stdout } = spawnSync(command);
|
||||
const secret = stdout.trim();
|
||||
if (error || !secret) {
|
||||
const orgId = getEnv("BUILDKITE_ORGANIZATION_SLUG", false);
|
||||
const clusterId = getEnv("BUILDKITE_CLUSTER_ID", false);
|
||||
|
||||
@@ -106,8 +116,8 @@ export function setEnv(name, value) {
|
||||
* @property {string} [cwd]
|
||||
* @property {number} [timeout]
|
||||
* @property {Record<string, string | undefined>} [env]
|
||||
* @property {string} [stdout]
|
||||
* @property {string} [stderr]
|
||||
* @property {string} [stdin]
|
||||
* @property {boolean} [privileged]
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -119,20 +129,93 @@ export function setEnv(name, value) {
|
||||
* @property {Error} [error]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {TemplateStringsArray} strings
|
||||
* @param {...any} values
|
||||
* @returns {string[]}
|
||||
*/
|
||||
export function $(strings, ...values) {
|
||||
const result = [];
|
||||
for (let i = 0; i < strings.length; i++) {
|
||||
result.push(...strings[i].trim().split(/\s+/).filter(Boolean));
|
||||
if (i < values.length) {
|
||||
const value = values[i];
|
||||
if (Array.isArray(value)) {
|
||||
result.push(...value);
|
||||
} else if (typeof value === "string") {
|
||||
if (result.at(-1)?.endsWith("=")) {
|
||||
result[result.length - 1] += value;
|
||||
} else {
|
||||
result.push(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/** @type {string[] | undefined} */
|
||||
let priviledgedCommand;
|
||||
|
||||
/**
|
||||
* @param {string[]} command
|
||||
* @param {SpawnOptions} options
|
||||
*/
|
||||
function parseCommand(command, options) {
|
||||
if (options?.privileged) {
|
||||
return [...getPrivilegedCommand(), ...command];
|
||||
}
|
||||
return command;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string[]}
|
||||
*/
|
||||
function getPrivilegedCommand() {
|
||||
if (typeof priviledgedCommand !== "undefined") {
|
||||
return priviledgedCommand;
|
||||
}
|
||||
|
||||
if (isWindows) {
|
||||
return (priviledgedCommand = []);
|
||||
}
|
||||
|
||||
const sudo = ["sudo", "-n"];
|
||||
const { error: sudoError } = spawnSync([...sudo, "true"]);
|
||||
if (!sudoError) {
|
||||
return (priviledgedCommand = sudo);
|
||||
}
|
||||
|
||||
const su = ["su", "-s", "sh", "root", "-c"];
|
||||
const { error: suError } = spawnSync([...su, "true"]);
|
||||
if (!suError) {
|
||||
return (priviledgedCommand = su);
|
||||
}
|
||||
|
||||
const doas = ["doas", "-u", "root"];
|
||||
const { error: doasError } = spawnSync([...doas, "true"]);
|
||||
if (!doasError) {
|
||||
return (priviledgedCommand = doas);
|
||||
}
|
||||
|
||||
return (priviledgedCommand = []);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string[]} command
|
||||
* @param {SpawnOptions} options
|
||||
* @returns {Promise<SpawnResult>}
|
||||
*/
|
||||
export async function spawn(command, options = {}) {
|
||||
debugLog("$", ...command);
|
||||
const [cmd, ...args] = parseCommand(command, options);
|
||||
debugLog("$", cmd, ...args);
|
||||
|
||||
const [cmd, ...args] = command;
|
||||
const stdin = options["stdin"];
|
||||
const spawnOptions = {
|
||||
cwd: options["cwd"] ?? process.cwd(),
|
||||
timeout: options["timeout"] ?? undefined,
|
||||
env: options["env"] ?? undefined,
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
stdio: [stdin ? "pipe" : "ignore", "pipe", "pipe"],
|
||||
...options,
|
||||
};
|
||||
|
||||
@@ -145,6 +228,16 @@ export async function spawn(command, options = {}) {
|
||||
const result = new Promise((resolve, reject) => {
|
||||
const subprocess = nodeSpawn(cmd, args, spawnOptions);
|
||||
|
||||
if (typeof stdin !== "undefined") {
|
||||
subprocess.stdin?.on("error", error => {
|
||||
if (error.code !== "EPIPE") {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
subprocess.stdin?.write(stdin);
|
||||
subprocess.stdin?.end();
|
||||
}
|
||||
|
||||
subprocess.stdout?.on("data", chunk => {
|
||||
stdout += chunk;
|
||||
});
|
||||
@@ -215,9 +308,9 @@ export async function spawnSafe(command, options) {
|
||||
* @returns {SpawnResult}
|
||||
*/
|
||||
export function spawnSync(command, options = {}) {
|
||||
debugLog("$", ...command);
|
||||
const [cmd, ...args] = parseCommand(command, options);
|
||||
debugLog("$", cmd, ...args);
|
||||
|
||||
const [cmd, ...args] = command;
|
||||
const spawnOptions = {
|
||||
cwd: options["cwd"] ?? process.cwd(),
|
||||
timeout: options["timeout"] ?? undefined,
|
||||
@@ -245,8 +338,8 @@ export function spawnSync(command, options = {}) {
|
||||
} else {
|
||||
exitCode = status ?? 1;
|
||||
signalCode = signal || undefined;
|
||||
stdout = stdoutBuffer.toString();
|
||||
stderr = stderrBuffer.toString();
|
||||
stdout = stdoutBuffer?.toString();
|
||||
stderr = stderrBuffer?.toString();
|
||||
}
|
||||
|
||||
if (exitCode !== 0 && isWindows) {
|
||||
@@ -258,7 +351,7 @@ export function spawnSync(command, options = {}) {
|
||||
|
||||
if (error || signalCode || exitCode !== 0) {
|
||||
const description = command.map(arg => (arg.includes(" ") ? `"${arg.replace(/"/g, '\\"')}"` : arg)).join(" ");
|
||||
const cause = error || stderr.trim() || stdout.trim() || undefined;
|
||||
const cause = error || stderr?.trim() || stdout?.trim() || undefined;
|
||||
|
||||
if (signalCode) {
|
||||
error = new Error(`Command killed with ${signalCode}: ${description}`, { cause });
|
||||
@@ -670,7 +763,7 @@ export async function curl(url, options = {}) {
|
||||
try {
|
||||
if (filename && ok) {
|
||||
const buffer = await response.arrayBuffer();
|
||||
await writeFile(filename, new Uint8Array(buffer));
|
||||
writeFile(filename, new Uint8Array(buffer));
|
||||
} else if (arrayBuffer && ok) {
|
||||
body = await response.arrayBuffer();
|
||||
} else if (json && ok) {
|
||||
@@ -735,7 +828,7 @@ export function readFile(filename, options = {}) {
|
||||
}
|
||||
|
||||
const relativePath = relative(process.cwd(), absolutePath);
|
||||
debugLog("cat", relativePath);
|
||||
debugLog("$", "cat", relativePath);
|
||||
|
||||
let content;
|
||||
try {
|
||||
@@ -752,6 +845,51 @@ export function readFile(filename, options = {}) {
|
||||
return content;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} filename
|
||||
* @param {string | Buffer} content
|
||||
* @param {object} [options]
|
||||
* @param {number} [options.mode]
|
||||
*/
|
||||
export function writeFile(filename, content, options = {}) {
|
||||
const parent = dirname(filename);
|
||||
if (!existsSync(parent)) {
|
||||
mkdirSync(parent, { recursive: true });
|
||||
}
|
||||
|
||||
writeFileSync(filename, content);
|
||||
|
||||
if (options["mode"]) {
|
||||
chmodSync(filename, options["mode"]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string | string[]} command
|
||||
* @param {object} [options]
|
||||
* @param {boolean} [options.required]
|
||||
* @returns {string | undefined}
|
||||
*/
|
||||
export function which(command, options = {}) {
|
||||
const commands = Array.isArray(command) ? command : [command];
|
||||
const path = getEnv("PATH", false) || "";
|
||||
const binPaths = path.split(isWindows ? ";" : ":");
|
||||
|
||||
for (const binPath of binPaths) {
|
||||
for (const command of commands) {
|
||||
const commandPath = join(binPath, command);
|
||||
if (existsSync(commandPath)) {
|
||||
return commandPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (options["required"]) {
|
||||
const description = commands.join(" or ");
|
||||
throw new Error(`Command not found: ${description}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} [cwd]
|
||||
* @param {string} [base]
|
||||
@@ -840,7 +978,7 @@ export function getBuildUrl() {
|
||||
*/
|
||||
export function getBuildLabel() {
|
||||
if (isBuildkite) {
|
||||
const label = getEnv("BUILDKITE_GROUP_LABEL", false) || getEnv("BUILDKITE_LABEL", false);
|
||||
const label = getEnv("BUILDKITE_LABEL", false) || getEnv("BUILDKITE_GROUP_LABEL", false);
|
||||
if (label) {
|
||||
return label;
|
||||
}
|
||||
@@ -854,6 +992,22 @@ export function getBuildLabel() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {number}
|
||||
*/
|
||||
export function getBootstrapVersion() {
|
||||
if (isWindows) {
|
||||
return 0; // TODO
|
||||
}
|
||||
const scriptPath = join(import.meta.dirname, "bootstrap.sh");
|
||||
const scriptContent = readFile(scriptPath, { cache: true });
|
||||
const match = /# Version: (\d+)/.exec(scriptContent);
|
||||
if (match) {
|
||||
return parseInt(match[1]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {object} BuildArtifact
|
||||
* @property {string} [job]
|
||||
@@ -1027,6 +1181,17 @@ export async function getLastSuccessfulBuild() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} filename
|
||||
* @param {string} [cwd]
|
||||
*/
|
||||
export async function uploadArtifact(filename, cwd) {
|
||||
if (isBuildkite) {
|
||||
const relativePath = relative(cwd ?? process.cwd(), filename);
|
||||
await spawnSafe(["buildkite-agent", "artifact", "upload", relativePath], { cwd, stdio: "inherit" });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} string
|
||||
* @returns {string}
|
||||
@@ -1035,6 +1200,17 @@ export function stripAnsi(string) {
|
||||
return string.replace(/\u001b\[\d+m/g, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} string
|
||||
* @returns {string}
|
||||
*/
|
||||
export function escapeYaml(string) {
|
||||
if (/[:"{}[\],&*#?|\-<>=!%@`]/.test(string)) {
|
||||
return `"${string.replace(/"/g, '\\"')}"`;
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} string
|
||||
* @returns {string}
|
||||
@@ -1173,11 +1349,36 @@ export function getArch() {
|
||||
return parseArch(process.arch);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string}
|
||||
*/
|
||||
export function getKernel() {
|
||||
const kernel = release();
|
||||
const match = /(\d+)\.(\d+)(?:\.(\d+))?/.exec(kernel);
|
||||
|
||||
if (match) {
|
||||
const [, major, minor, patch] = match;
|
||||
if (patch) {
|
||||
return `${major}.${minor}.${patch}`;
|
||||
}
|
||||
return `${major}.${minor}`;
|
||||
}
|
||||
|
||||
return kernel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {"musl" | "gnu" | undefined}
|
||||
*/
|
||||
export function getAbi() {
|
||||
if (isLinux) {
|
||||
if (!isLinux) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (existsSync("/etc/alpine-release")) {
|
||||
return "musl";
|
||||
}
|
||||
|
||||
const arch = getArch() === "x64" ? "x86_64" : "aarch64";
|
||||
const muslLibPath = `/lib/ld-musl-${arch}.so.1`;
|
||||
if (existsSync(muslLibPath)) {
|
||||
@@ -1188,6 +1389,36 @@ export function getAbi() {
|
||||
if (existsSync(gnuLibPath)) {
|
||||
return "gnu";
|
||||
}
|
||||
|
||||
const { error, stdout } = spawnSync(["ldd", "--version"]);
|
||||
if (!error) {
|
||||
if (/musl/i.test(stdout)) {
|
||||
return "musl";
|
||||
}
|
||||
if (/gnu|glibc/i.test(stdout)) {
|
||||
return "gnu";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string | undefined}
|
||||
*/
|
||||
export function getAbiVersion() {
|
||||
if (!isLinux) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { error, stdout } = spawnSync(["ldd", "--version"]);
|
||||
if (!error) {
|
||||
const match = /(\d+)\.(\d+)(?:\.(\d+))?/.exec(stdout);
|
||||
if (match) {
|
||||
const [, major, minor, patch] = match;
|
||||
if (patch) {
|
||||
return `${major}.${minor}.${patch}`;
|
||||
}
|
||||
return `${major}.${minor}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1360,17 +1591,24 @@ export async function downloadTarget(target, release) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string | undefined}
|
||||
* @returns {string}
|
||||
*/
|
||||
export function getTailscaleIp() {
|
||||
let tailscale = "tailscale";
|
||||
export function getTailscale() {
|
||||
if (isMacOS) {
|
||||
const tailscaleApp = "/Applications/Tailscale.app/Contents/MacOS/tailscale";
|
||||
if (existsSync(tailscaleApp)) {
|
||||
tailscale = tailscaleApp;
|
||||
return tailscaleApp;
|
||||
}
|
||||
}
|
||||
|
||||
return "tailscale";
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string | undefined}
|
||||
*/
|
||||
export function getTailscaleIp() {
|
||||
const tailscale = getTailscale();
|
||||
const { error, stdout } = spawnSync([tailscale, "ip", "--1"]);
|
||||
if (!error) {
|
||||
return stdout.trim();
|
||||
@@ -1419,7 +1657,31 @@ export function getUsername() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string}
|
||||
* @typedef {object} User
|
||||
* @property {string} username
|
||||
* @property {number} uid
|
||||
* @property {number} gid
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {string} username
|
||||
* @returns {Promise<User>}
|
||||
*/
|
||||
export async function getUser(username) {
|
||||
if (isWindows) {
|
||||
throw new Error("TODO: Windows");
|
||||
}
|
||||
|
||||
const [uid, gid] = await Promise.all([
|
||||
spawnSafe(["id", "-u", username]).then(({ stdout }) => parseInt(stdout.trim())),
|
||||
spawnSafe(["id", "-g", username]).then(({ stdout }) => parseInt(stdout.trim())),
|
||||
]);
|
||||
|
||||
return { username, uid, gid };
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string | undefined}
|
||||
*/
|
||||
export function getDistro() {
|
||||
if (isMacOS) {
|
||||
@@ -1427,6 +1689,11 @@ export function getDistro() {
|
||||
}
|
||||
|
||||
if (isLinux) {
|
||||
const alpinePath = "/etc/alpine-release";
|
||||
if (existsSync(alpinePath)) {
|
||||
return "alpine";
|
||||
}
|
||||
|
||||
const releasePath = "/etc/os-release";
|
||||
if (existsSync(releasePath)) {
|
||||
const releaseFile = readFile(releasePath, { cache: true });
|
||||
@@ -1438,10 +1705,8 @@ export function getDistro() {
|
||||
|
||||
const { error, stdout } = spawnSync(["lsb_release", "-is"]);
|
||||
if (!error) {
|
||||
return stdout.trim();
|
||||
return stdout.trim().toLowerCase();
|
||||
}
|
||||
|
||||
return "Linux";
|
||||
}
|
||||
|
||||
if (isWindows) {
|
||||
@@ -1449,17 +1714,13 @@ export function getDistro() {
|
||||
if (!error) {
|
||||
return stdout.trim();
|
||||
}
|
||||
|
||||
return "Windows";
|
||||
}
|
||||
|
||||
return `${process.platform} ${process.arch}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string | undefined}
|
||||
*/
|
||||
export function getDistroRelease() {
|
||||
export function getDistroVersion() {
|
||||
if (isMacOS) {
|
||||
const { error, stdout } = spawnSync(["sw_vers", "-productVersion"]);
|
||||
if (!error) {
|
||||
@@ -1468,6 +1729,16 @@ export function getDistroRelease() {
|
||||
}
|
||||
|
||||
if (isLinux) {
|
||||
const alpinePath = "/etc/alpine-release";
|
||||
if (existsSync(alpinePath)) {
|
||||
const release = readFile(alpinePath, { cache: true }).trim();
|
||||
if (release.includes("_")) {
|
||||
const [version] = release.split("_");
|
||||
return `${version}-edge`;
|
||||
}
|
||||
return release;
|
||||
}
|
||||
|
||||
const releasePath = "/etc/os-release";
|
||||
if (existsSync(releasePath)) {
|
||||
const releaseFile = readFile(releasePath, { cache: true });
|
||||
@@ -1491,6 +1762,231 @@ export function getDistroRelease() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {"aws" | "google"} Cloud
|
||||
*/
|
||||
|
||||
/** @type {Cloud | undefined} */
|
||||
let detectedCloud;
|
||||
|
||||
/**
|
||||
* @returns {Promise<boolean | undefined>}
|
||||
*/
|
||||
export async function isAws() {
|
||||
if (typeof detectedCloud === "string") {
|
||||
return detectedCloud === "aws";
|
||||
}
|
||||
|
||||
async function checkAws() {
|
||||
if (isLinux) {
|
||||
const kernel = release();
|
||||
if (kernel.endsWith("-aws")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const { error: systemdError, stdout } = await spawn(["systemd-detect-virt"]);
|
||||
if (!systemdError) {
|
||||
if (stdout.includes("amazon")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const dmiPath = "/sys/devices/virtual/dmi/id/board_asset_tag";
|
||||
if (existsSync(dmiPath)) {
|
||||
const dmiFile = readFileSync(dmiPath, { encoding: "utf-8" });
|
||||
if (dmiFile.startsWith("i-")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isWindows) {
|
||||
const executionEnv = getEnv("AWS_EXECUTION_ENV", false);
|
||||
if (executionEnv === "EC2") {
|
||||
return true;
|
||||
}
|
||||
|
||||
const { error: powershellError, stdout } = await spawn([
|
||||
"powershell",
|
||||
"-Command",
|
||||
"Get-CimInstance -ClassName Win32_ComputerSystem | Select-Object Manufacturer",
|
||||
]);
|
||||
if (!powershellError) {
|
||||
return stdout.includes("Amazon");
|
||||
}
|
||||
}
|
||||
|
||||
const instanceId = await getCloudMetadata("instance-id", "google");
|
||||
if (instanceId) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (await checkAws()) {
|
||||
detectedCloud = "aws";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Promise<boolean | undefined>}
|
||||
*/
|
||||
export async function isGoogleCloud() {
|
||||
if (typeof detectedCloud === "string") {
|
||||
return detectedCloud === "google";
|
||||
}
|
||||
|
||||
async function detectGoogleCloud() {
|
||||
if (isLinux) {
|
||||
const vendorPaths = [
|
||||
"/sys/class/dmi/id/sys_vendor",
|
||||
"/sys/class/dmi/id/bios_vendor",
|
||||
"/sys/class/dmi/id/product_name",
|
||||
];
|
||||
|
||||
for (const vendorPath of vendorPaths) {
|
||||
if (existsSync(vendorPath)) {
|
||||
const vendorFile = readFileSync(vendorPath, { encoding: "utf-8" });
|
||||
if (vendorFile.includes("Google")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const instanceId = await getCloudMetadata("id", "google");
|
||||
if (instanceId) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (await detectGoogleCloud()) {
|
||||
detectedCloud = "google";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Promise<Cloud | undefined>}
|
||||
*/
|
||||
export async function getCloud() {
|
||||
if (typeof detectedCloud === "string") {
|
||||
return detectedCloud;
|
||||
}
|
||||
|
||||
if (await isAws()) {
|
||||
return "aws";
|
||||
}
|
||||
|
||||
if (await isGoogleCloud()) {
|
||||
return "google";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string | Record<Cloud, string>} name
|
||||
* @param {Cloud} [cloud]
|
||||
* @returns {Promise<string | undefined>}
|
||||
*/
|
||||
export async function getCloudMetadata(name, cloud) {
|
||||
cloud ??= await getCloud();
|
||||
if (!cloud) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof name === "object") {
|
||||
name = name[cloud];
|
||||
}
|
||||
|
||||
let url;
|
||||
let headers;
|
||||
if (cloud === "aws") {
|
||||
url = new URL(name, "http://169.254.169.254/latest/meta-data/");
|
||||
} else if (cloud === "google") {
|
||||
url = new URL(name, "http://metadata.google.internal/computeMetadata/v1/instance/");
|
||||
headers = { "Metadata-Flavor": "Google" };
|
||||
} else {
|
||||
throw new Error(`Unsupported cloud: ${inspect(cloud)}`);
|
||||
}
|
||||
|
||||
const { error, body } = await curl(url, { headers, retries: 0 });
|
||||
if (error) {
|
||||
return;
|
||||
}
|
||||
|
||||
return body.trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} tag
|
||||
* @param {Cloud} [cloud]
|
||||
* @returns {Promise<string | undefined>}
|
||||
*/
|
||||
export function getCloudMetadataTag(tag, cloud) {
|
||||
const metadata = {
|
||||
"aws": `tags/instance/${tag}`,
|
||||
};
|
||||
|
||||
return getCloudMetadata(metadata, cloud);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @returns {Promise<string | undefined>}
|
||||
*/
|
||||
export async function getBuildMetadata(name) {
|
||||
if (isBuildkite) {
|
||||
const { error, stdout } = await spawn(["buildkite-agent", "meta-data", "get", name]);
|
||||
if (!error) {
|
||||
const value = stdout.trim();
|
||||
if (value) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef ConnectOptions
|
||||
* @property {string} hostname
|
||||
* @property {number} port
|
||||
* @property {number} [retries]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {ConnectOptions} options
|
||||
* @returns {Promise<Error | undefined>}
|
||||
*/
|
||||
export async function waitForPort(options) {
|
||||
const { hostname, port, retries = 10 } = options;
|
||||
|
||||
let cause;
|
||||
for (let i = 0; i < retries; i++) {
|
||||
if (cause) {
|
||||
await new Promise(resolve => setTimeout(resolve, Math.pow(2, i) * 1000));
|
||||
}
|
||||
|
||||
const connected = new Promise((resolve, reject) => {
|
||||
const socket = connect({ host: hostname, port });
|
||||
socket.on("connect", () => {
|
||||
socket.destroy();
|
||||
resolve();
|
||||
});
|
||||
socket.on("error", error => {
|
||||
socket.destroy();
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
|
||||
try {
|
||||
return await connected;
|
||||
} catch (error) {
|
||||
cause = error;
|
||||
}
|
||||
}
|
||||
|
||||
return cause;
|
||||
}
|
||||
/**
|
||||
* @returns {Promise<number | undefined>}
|
||||
*/
|
||||
@@ -1536,6 +2032,52 @@ export function getGithubUrl() {
|
||||
return new URL(getEnv("GITHUB_SERVER_URL", false) || "https://github.com");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} obj
|
||||
* @param {number} indent
|
||||
* @returns {string}
|
||||
*/
|
||||
export function toYaml(obj, indent = 0) {
|
||||
const spaces = " ".repeat(indent);
|
||||
let result = "";
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
if (value === undefined) {
|
||||
continue;
|
||||
}
|
||||
if (value === null) {
|
||||
result += `${spaces}${key}: null\n`;
|
||||
continue;
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
result += `${spaces}${key}:\n`;
|
||||
value.forEach(item => {
|
||||
if (typeof item === "object" && item !== null) {
|
||||
result += `${spaces}- \n${toYaml(item, indent + 2)
|
||||
.split("\n")
|
||||
.map(line => `${spaces} ${line}`)
|
||||
.join("\n")}\n`;
|
||||
} else {
|
||||
result += `${spaces}- ${item}\n`;
|
||||
}
|
||||
});
|
||||
continue;
|
||||
}
|
||||
if (typeof value === "object") {
|
||||
result += `${spaces}${key}:\n${toYaml(value, indent + 2)}`;
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
typeof value === "string" &&
|
||||
(value.includes(":") || value.includes("#") || value.includes("'") || value.includes('"') || value.includes("\n"))
|
||||
) {
|
||||
result += `${spaces}${key}: "${value.replace(/"/g, '\\"')}"\n`;
|
||||
continue;
|
||||
}
|
||||
result += `${spaces}${key}: ${value}\n`;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} title
|
||||
* @param {function} [fn]
|
||||
@@ -1575,11 +2117,13 @@ export function printEnvironment() {
|
||||
startGroup("Machine", () => {
|
||||
console.log("Operating System:", getOs());
|
||||
console.log("Architecture:", getArch());
|
||||
console.log("Kernel:", getKernel());
|
||||
if (isLinux) {
|
||||
console.log("ABI:", getAbi());
|
||||
console.log("ABI Version:", getAbiVersion());
|
||||
}
|
||||
console.log("Distro:", getDistro());
|
||||
console.log("Release:", getDistroRelease());
|
||||
console.log("Distro Version:", getDistroVersion());
|
||||
console.log("Hostname:", getHostname());
|
||||
if (isCI) {
|
||||
console.log("Tailscale IP:", getTailscaleIp());
|
||||
|
||||
@@ -78,6 +78,14 @@ case $platform in
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$target" in
|
||||
'linux'*)
|
||||
if [ -f /etc/alpine-release ]; then
|
||||
target="$target-musl"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ $target = darwin-x64 ]]; then
|
||||
# Is this process running in Rosetta?
|
||||
# redirect stderr to devnull to avoid error message when not running in Rosetta
|
||||
@@ -91,19 +99,20 @@ GITHUB=${GITHUB-"https://github.com"}
|
||||
|
||||
github_repo="$GITHUB/oven-sh/bun"
|
||||
|
||||
if [[ $target = darwin-x64 ]]; then
|
||||
# If AVX2 isn't supported, use the -baseline build
|
||||
case "$target" in
|
||||
'darwin-x64'*)
|
||||
if [[ $(sysctl -a | grep machdep.cpu | grep AVX2) == '' ]]; then
|
||||
target=darwin-x64-baseline
|
||||
target="$target-baseline"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ $target = linux-x64 ]]; then
|
||||
;;
|
||||
'linux-x64'*)
|
||||
# If AVX2 isn't supported, use the -baseline build
|
||||
if [[ $(cat /proc/cpuinfo | grep avx2) = '' ]]; then
|
||||
target=linux-x64-baseline
|
||||
fi
|
||||
target="$target-baseline"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
exe_name=bun
|
||||
|
||||
|
||||
Reference in New Issue
Block a user