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:
744
.buildkite/ci.mjs
Normal file → Executable file
744
.buildkite/ci.mjs
Normal file → Executable file
@@ -8,13 +8,14 @@
|
|||||||
import { writeFileSync } from "node:fs";
|
import { writeFileSync } from "node:fs";
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import {
|
import {
|
||||||
|
getBootstrapVersion,
|
||||||
|
getBuildNumber,
|
||||||
getCanaryRevision,
|
getCanaryRevision,
|
||||||
getChangedFiles,
|
getChangedFiles,
|
||||||
getCommit,
|
getCommit,
|
||||||
getCommitMessage,
|
getCommitMessage,
|
||||||
getLastSuccessfulBuild,
|
getLastSuccessfulBuild,
|
||||||
getMainBranch,
|
getMainBranch,
|
||||||
getRepositoryOwner,
|
|
||||||
getTargetBranch,
|
getTargetBranch,
|
||||||
isBuildkite,
|
isBuildkite,
|
||||||
isFork,
|
isFork,
|
||||||
@@ -22,102 +23,162 @@ import {
|
|||||||
isMergeQueue,
|
isMergeQueue,
|
||||||
printEnvironment,
|
printEnvironment,
|
||||||
spawnSafe,
|
spawnSafe,
|
||||||
|
toYaml,
|
||||||
|
uploadArtifact,
|
||||||
} from "../scripts/utils.mjs";
|
} from "../scripts/utils.mjs";
|
||||||
|
|
||||||
function toYaml(obj, indent = 0) {
|
/**
|
||||||
const spaces = " ".repeat(indent);
|
* @typedef PipelineOptions
|
||||||
let result = "";
|
* @property {string} [buildId]
|
||||||
|
* @property {boolean} [buildImages]
|
||||||
|
* @property {boolean} [publishImages]
|
||||||
|
* @property {boolean} [skipTests]
|
||||||
|
*/
|
||||||
|
|
||||||
for (const [key, value] of Object.entries(obj)) {
|
/**
|
||||||
if (value === undefined) {
|
* @param {PipelineOptions} options
|
||||||
continue;
|
*/
|
||||||
}
|
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
|
* Helpers
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const getKey = platform => {
|
/**
|
||||||
const { os, arch, abi, baseline } = platform;
|
* @param {string} text
|
||||||
|
* @returns {string}
|
||||||
if (abi) {
|
* @link https://github.com/buildkite/emojis#emoji-reference
|
||||||
if (baseline) {
|
*/
|
||||||
return `${os}-${arch}-${abi}-baseline`;
|
const getEmoji = string => {
|
||||||
}
|
if (string === "amazonlinux") {
|
||||||
return `${os}-${arch}-${abi}`;
|
return ":aws:";
|
||||||
}
|
}
|
||||||
if (baseline) {
|
return `:${string}:`;
|
||||||
return `${os}-${arch}-baseline`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return `${os}-${arch}`;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const getLabel = platform => {
|
/**
|
||||||
const { os, arch, abi, baseline, release } = platform;
|
* @typedef {"linux" | "darwin" | "windows"} Os
|
||||||
let label = release ? `:${os}: ${release} ${arch}` : `:${os}: ${arch}`;
|
* @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) {
|
if (abi) {
|
||||||
label += `-${abi}`;
|
label += `-${abi}`;
|
||||||
}
|
}
|
||||||
if (baseline) {
|
if (baseline) {
|
||||||
label += `-baseline`;
|
label += "-baseline";
|
||||||
}
|
}
|
||||||
return label;
|
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 {
|
return {
|
||||||
automatic: [
|
automatic: [
|
||||||
{ exit_status: 1, limit: 1 },
|
{ exit_status: 1, limit },
|
||||||
{ exit_status: -1, limit },
|
{ exit_status: -1, limit: 3 },
|
||||||
{ exit_status: 255, limit },
|
{ exit_status: 255, limit: 3 },
|
||||||
{ signal_reason: "agent_stop", limit },
|
{ 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 = () => {
|
const getPriority = () => {
|
||||||
if (isFork()) {
|
if (isFork()) {
|
||||||
return -1;
|
return -1;
|
||||||
@@ -131,132 +192,286 @@ function getPipeline(buildId) {
|
|||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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 {
|
||||||
|
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,
|
||||||
|
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
|
* Steps
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const getBuildVendorStep = platform => {
|
/**
|
||||||
const { os, arch, abi, baseline } = platform;
|
* @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 {
|
return {
|
||||||
key: `${getKey(platform)}-build-vendor`,
|
key: `${getImageKey(platform)}-build-image`,
|
||||||
label: `build-vendor`,
|
label: `${getImageLabel(platform)} - build-image`,
|
||||||
agents: {
|
agents: {
|
||||||
os,
|
queue: "build-image",
|
||||||
arch,
|
|
||||||
abi,
|
|
||||||
queue: abi ? `build-${os}-${abi}` : `build-${os}`,
|
|
||||||
},
|
},
|
||||||
|
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(),
|
retry: getRetry(),
|
||||||
cancel_on_build_failing: isMergeQueue(),
|
cancel_on_build_failing: isMergeQueue(),
|
||||||
env: {
|
env: getBuildEnv(target),
|
||||||
ENABLE_BASELINE: baseline ? "ON" : "OFF",
|
|
||||||
},
|
|
||||||
command: "bun run build:ci --target dependencies",
|
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 {
|
return {
|
||||||
key: `${getKey(platform)}-build-cpp`,
|
key: `${getTargetKey(target)}-build-cpp`,
|
||||||
label: `build-cpp`,
|
label: `${getTargetLabel(target)} - build-cpp`,
|
||||||
agents: {
|
agents: getBuildAgent(target),
|
||||||
os,
|
|
||||||
arch,
|
|
||||||
abi,
|
|
||||||
queue: abi ? `build-${os}-${abi}` : `build-${os}`,
|
|
||||||
},
|
|
||||||
retry: getRetry(),
|
retry: getRetry(),
|
||||||
cancel_on_build_failing: isMergeQueue(),
|
cancel_on_build_failing: isMergeQueue(),
|
||||||
env: {
|
env: {
|
||||||
BUN_CPP_ONLY: "ON",
|
BUN_CPP_ONLY: "ON",
|
||||||
ENABLE_BASELINE: baseline ? "ON" : "OFF",
|
...getBuildEnv(target),
|
||||||
},
|
},
|
||||||
command: "bun run build:ci --target bun",
|
command: "bun run build:ci --target bun",
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const getBuildZigStep = platform => {
|
/**
|
||||||
const { os, arch, abi, baseline } = platform;
|
* @param {Target} target
|
||||||
const toolchain = getKey(platform);
|
* @returns {Step}
|
||||||
|
*/
|
||||||
|
const getBuildZigStep = target => {
|
||||||
|
const toolchain = getBuildToolchain(target);
|
||||||
return {
|
return {
|
||||||
key: `${getKey(platform)}-build-zig`,
|
key: `${getTargetKey(target)}-build-zig`,
|
||||||
label: `build-zig`,
|
label: `${getTargetLabel(target)} - build-zig`,
|
||||||
agents: {
|
agents: getZigAgent(target),
|
||||||
queue: "build-zig",
|
retry: getRetry(1), // FIXME: Sometimes zig build hangs, so we need to retry once
|
||||||
},
|
|
||||||
retry: getRetry(),
|
|
||||||
cancel_on_build_failing: isMergeQueue(),
|
cancel_on_build_failing: isMergeQueue(),
|
||||||
env: {
|
env: getBuildEnv(target),
|
||||||
ENABLE_BASELINE: baseline ? "ON" : "OFF",
|
|
||||||
},
|
|
||||||
command: `bun run build:ci --target bun-zig --toolchain ${toolchain}`,
|
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 {
|
return {
|
||||||
key: `${getKey(platform)}-build-bun`,
|
key: `${getTargetKey(target)}-build-bun`,
|
||||||
label: `build-bun`,
|
label: `${getTargetLabel(target)} - build-bun`,
|
||||||
depends_on: [
|
depends_on: [
|
||||||
`${getKey(platform)}-build-vendor`,
|
`${getTargetKey(target)}-build-vendor`,
|
||||||
`${getKey(platform)}-build-cpp`,
|
`${getTargetKey(target)}-build-cpp`,
|
||||||
`${getKey(platform)}-build-zig`,
|
`${getTargetKey(target)}-build-zig`,
|
||||||
],
|
],
|
||||||
agents: {
|
agents: getBuildAgent(target),
|
||||||
os,
|
|
||||||
arch,
|
|
||||||
abi,
|
|
||||||
queue: `build-${os}`,
|
|
||||||
},
|
|
||||||
retry: getRetry(),
|
retry: getRetry(),
|
||||||
cancel_on_build_failing: isMergeQueue(),
|
cancel_on_build_failing: isMergeQueue(),
|
||||||
env: {
|
env: {
|
||||||
BUN_LINK_ONLY: "ON",
|
BUN_LINK_ONLY: "ON",
|
||||||
ENABLE_BASELINE: baseline ? "ON" : "OFF",
|
...getBuildEnv(target),
|
||||||
},
|
},
|
||||||
command: "bun run build:ci --target bun",
|
command: "bun run build:ci --target bun",
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Platform} platform
|
||||||
|
* @returns {Step}
|
||||||
|
*/
|
||||||
const getTestBunStep = platform => {
|
const getTestBunStep = platform => {
|
||||||
const { os, arch, abi, distro, release } = platform;
|
const { os } = 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 };
|
|
||||||
}
|
|
||||||
|
|
||||||
let command;
|
let command;
|
||||||
if (os === "windows") {
|
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 {
|
} else {
|
||||||
command = `./scripts/runner.node.mjs --step ${getKey(platform)}-build-bun`;
|
command = `./scripts/runner.node.mjs --step ${getTargetKey(platform)}-build-bun`;
|
||||||
}
|
}
|
||||||
|
|
||||||
let parallelism;
|
let parallelism;
|
||||||
if (os === "darwin") {
|
if (os === "darwin") {
|
||||||
parallelism = 2;
|
parallelism = 2;
|
||||||
} else {
|
} else {
|
||||||
parallelism = 10;
|
parallelism = 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
let depends;
|
let depends;
|
||||||
let env;
|
let env;
|
||||||
if (buildId) {
|
if (buildId) {
|
||||||
@@ -264,21 +479,19 @@ function getPipeline(buildId) {
|
|||||||
BUILDKITE_ARTIFACT_BUILD_ID: buildId,
|
BUILDKITE_ARTIFACT_BUILD_ID: buildId,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
depends = [`${getKey(platform)}-build-bun`];
|
depends = [`${getTargetKey(platform)}-build-bun`];
|
||||||
}
|
}
|
||||||
|
|
||||||
let retry;
|
let retry;
|
||||||
if (os !== "windows") {
|
if (os !== "windows") {
|
||||||
// When the runner fails on Windows, Buildkite only detects an exit code of 1.
|
// 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.
|
// Because of this, we don't know if the run was fatal, or soft-failed.
|
||||||
retry = getRetry();
|
retry = getRetry(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
key: `${getKey(platform)}-${distro}-${release.replace(/\./g, "")}-test-bun`,
|
key: `${getPlatformKey(platform)}-test-bun`,
|
||||||
label: `${name} - test-bun`,
|
label: `${getPlatformLabel(platform)} - test-bun`,
|
||||||
depends_on: depends,
|
depends_on: depends,
|
||||||
agents,
|
agents: getTestAgent(platform),
|
||||||
retry,
|
retry,
|
||||||
cancel_on_build_failing: isMergeQueue(),
|
cancel_on_build_failing: isMergeQueue(),
|
||||||
soft_fail: isMainBranch(),
|
soft_fail: isMainBranch(),
|
||||||
@@ -292,66 +505,145 @@ function getPipeline(buildId) {
|
|||||||
* Config
|
* Config
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Platform[]}
|
||||||
|
*/
|
||||||
const buildPlatforms = [
|
const buildPlatforms = [
|
||||||
{ os: "darwin", arch: "aarch64" },
|
{ os: "darwin", arch: "aarch64", release: "14" },
|
||||||
{ os: "darwin", arch: "x64" },
|
{ os: "darwin", arch: "x64", release: "14" },
|
||||||
{ os: "linux", arch: "aarch64" },
|
{ os: "linux", arch: "aarch64", distro: "debian", release: "11" },
|
||||||
// { os: "linux", arch: "aarch64", abi: "musl" }, // TODO:
|
{ os: "linux", arch: "x64", distro: "debian", release: "11" },
|
||||||
{ os: "linux", arch: "x64" },
|
{ os: "linux", arch: "x64", baseline: true, distro: "debian", release: "11" },
|
||||||
{ os: "linux", arch: "x64", baseline: true },
|
{ os: "linux", arch: "aarch64", abi: "musl", distro: "alpine", release: "3.20" },
|
||||||
// { os: "linux", arch: "x64", abi: "musl" }, // TODO:
|
{ os: "linux", arch: "x64", abi: "musl", distro: "alpine", release: "3.20" },
|
||||||
{ os: "windows", arch: "x64" },
|
{ os: "linux", arch: "x64", abi: "musl", baseline: true, distro: "alpine", release: "3.20" },
|
||||||
{ os: "windows", arch: "x64", baseline: true },
|
{ os: "windows", arch: "x64", release: "2019" },
|
||||||
|
{ os: "windows", arch: "x64", baseline: true, release: "2019" },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Platform[]}
|
||||||
|
*/
|
||||||
const testPlatforms = [
|
const testPlatforms = [
|
||||||
{ os: "darwin", arch: "aarch64", distro: "sonoma", release: "14" },
|
{ os: "darwin", arch: "aarch64", release: "14" },
|
||||||
{ os: "darwin", arch: "aarch64", distro: "ventura", release: "13" },
|
{ os: "darwin", arch: "aarch64", release: "13" },
|
||||||
{ os: "darwin", arch: "x64", distro: "sonoma", release: "14" },
|
{ os: "darwin", arch: "x64", release: "14" },
|
||||||
{ os: "darwin", arch: "x64", distro: "ventura", release: "13" },
|
{ os: "darwin", arch: "x64", release: "13" },
|
||||||
{ os: "linux", arch: "aarch64", distro: "debian", release: "12" },
|
{ 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: "22.04" },
|
||||||
{ os: "linux", arch: "aarch64", distro: "ubuntu", release: "20.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: "ubuntu", release: "24.04" },
|
||||||
{ os: "linux", arch: "x64", distro: "debian", release: "12" },
|
|
||||||
{ os: "linux", arch: "x64", distro: "ubuntu", release: "22.04" },
|
{ os: "linux", arch: "x64", distro: "ubuntu", release: "22.04" },
|
||||||
{ os: "linux", arch: "x64", distro: "ubuntu", release: "20.04" },
|
{ os: "linux", arch: "x64", distro: "ubuntu", release: "20.04" },
|
||||||
{ os: "linux", arch: "x64", distro: "debian", release: "12", baseline: true },
|
// { os: "linux", arch: "x64", baseline: true, distro: "ubuntu", release: "24.04" },
|
||||||
{ os: "linux", arch: "x64", distro: "ubuntu", release: "22.04", baseline: true },
|
{ os: "linux", arch: "x64", baseline: true, distro: "ubuntu", release: "22.04" },
|
||||||
{ os: "linux", arch: "x64", distro: "ubuntu", release: "20.04", baseline: true },
|
{ os: "linux", arch: "x64", baseline: true, distro: "ubuntu", release: "20.04" },
|
||||||
// { os: "linux", arch: "x64", abi: "musl", distro: "alpine", release: "edge" }, // TODO:
|
// { os: "linux", arch: "aarch64", distro: "amazonlinux", release: "2023" },
|
||||||
{ os: "windows", arch: "x64", distro: "server", release: "2019" },
|
// { os: "linux", arch: "aarch64", distro: "amazonlinux", release: "2" },
|
||||||
{ os: "windows", arch: "x64", distro: "server", release: "2019", baseline: true },
|
// { 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" },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const imagePlatforms = new Map(
|
||||||
|
[...buildPlatforms, ...testPlatforms]
|
||||||
|
.filter(platform => buildImages && isUsingNewAgent(platform))
|
||||||
|
.map(platform => [getImageKey(platform), platform]),
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Step[]}
|
||||||
|
*/
|
||||||
|
const steps = [];
|
||||||
|
|
||||||
|
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),
|
||||||
|
getBuildBunStep(platform),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
return {
|
||||||
priority: getPriority(),
|
priority: getPriority(),
|
||||||
steps: [
|
steps,
|
||||||
...buildPlatforms.map(platform => {
|
|
||||||
const { os, arch, baseline } = platform;
|
|
||||||
|
|
||||||
let steps = [
|
|
||||||
...testPlatforms
|
|
||||||
.filter(platform => platform.os === os && platform.arch === arch && baseline === platform.baseline)
|
|
||||||
.map(platform => getTestBunStep(platform)),
|
|
||||||
];
|
|
||||||
|
|
||||||
if (!buildId) {
|
|
||||||
steps.unshift(
|
|
||||||
getBuildVendorStep(platform),
|
|
||||||
getBuildCppStep(platform),
|
|
||||||
getBuildZigStep(platform),
|
|
||||||
getBuildBunStep(platform),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
key: getKey(platform),
|
|
||||||
group: getLabel(platform),
|
|
||||||
steps,
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -369,7 +661,6 @@ async function main() {
|
|||||||
console.log(" - No build found");
|
console.log(" - No build found");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let changedFiles;
|
let changedFiles;
|
||||||
if (!isFork()) {
|
if (!isFork()) {
|
||||||
console.log("Checking changed files...");
|
console.log("Checking changed files...");
|
||||||
@@ -377,7 +668,7 @@ async function main() {
|
|||||||
console.log(" - Base Ref:", baseRef);
|
console.log(" - Base Ref:", baseRef);
|
||||||
const headRef = lastBuild?.commit_id || getTargetBranch() || getMainBranch();
|
const headRef = lastBuild?.commit_id || getTargetBranch() || getMainBranch();
|
||||||
console.log(" - Head Ref:", headRef);
|
console.log(" - Head Ref:", headRef);
|
||||||
|
|
||||||
changedFiles = await getChangedFiles(undefined, baseRef, headRef);
|
changedFiles = await getChangedFiles(undefined, baseRef, headRef);
|
||||||
if (changedFiles) {
|
if (changedFiles) {
|
||||||
if (changedFiles.length) {
|
if (changedFiles.length) {
|
||||||
@@ -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...");
|
console.log("Checking if build should be skipped...");
|
||||||
let skipBuild;
|
let skipBuild;
|
||||||
if (!forceBuild) {
|
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...");
|
console.log("Checking if build is a named release...");
|
||||||
let buildRelease;
|
let buildRelease;
|
||||||
{
|
{
|
||||||
@@ -447,7 +774,13 @@ async function main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
console.log("Generating pipeline...");
|
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 content = toYaml(pipeline);
|
||||||
const contentPath = join(process.cwd(), ".buildkite", "ci.yml");
|
const contentPath = join(process.cwd(), ".buildkite", "ci.yml");
|
||||||
writeFileSync(contentPath, content);
|
writeFileSync(contentPath, content);
|
||||||
@@ -455,14 +788,17 @@ async function main() {
|
|||||||
console.log("Generated pipeline:");
|
console.log("Generated pipeline:");
|
||||||
console.log(" - Path:", contentPath);
|
console.log(" - Path:", contentPath);
|
||||||
console.log(" - Size:", (content.length / 1024).toFixed(), "KB");
|
console.log(" - Size:", (content.length / 1024).toFixed(), "KB");
|
||||||
|
if (isBuildkite) {
|
||||||
|
await uploadArtifact(contentPath);
|
||||||
|
}
|
||||||
|
|
||||||
if (isBuildkite) {
|
if (isBuildkite) {
|
||||||
console.log("Setting canary revision...");
|
console.log("Setting canary revision...");
|
||||||
const canaryRevision = buildRelease ? 0 : await getCanaryRevision();
|
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...");
|
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-profile.zip
|
||||||
bun-linux-x64-baseline.zip
|
bun-linux-x64-baseline.zip
|
||||||
bun-linux-x64-baseline-profile.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.zip
|
||||||
bun-windows-x64-profile.zip
|
bun-windows-x64-profile.zip
|
||||||
bun-windows-x64-baseline.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,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"bootstrap": "brew install gh jq cirruslabs/cli/tart cirruslabs/cli/sshpass hashicorp/tap/packer && packer init darwin",
|
"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:image-name": "echo ghcr.io/oven-sh/bun-vm",
|
||||||
"fetch:darwin-version": "echo 1",
|
"fetch:darwin-version": "echo 1",
|
||||||
"fetch:macos-version": "sw_vers -productVersion | cut -d. -f1",
|
"fetch:macos-version": "sw_vers -productVersion | cut -d. -f1",
|
||||||
|
|||||||
@@ -105,14 +105,6 @@ else()
|
|||||||
unsupported(CMAKE_HOST_SYSTEM_NAME)
|
unsupported(CMAKE_HOST_SYSTEM_NAME)
|
||||||
endif()
|
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")
|
if(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "arm64|ARM64|aarch64|AARCH64")
|
||||||
set(HOST_OS "aarch64")
|
set(HOST_OS "aarch64")
|
||||||
elseif(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "x86_64|X86_64|x64|X64|amd64|AMD64")
|
elseif(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "x86_64|X86_64|x64|X64|amd64|AMD64")
|
||||||
@@ -144,6 +136,16 @@ else()
|
|||||||
set(WARNING WARNING)
|
set(WARNING WARNING)
|
||||||
endif()
|
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.
|
# TODO: This causes flaky zig builds in CI, so temporarily disable it.
|
||||||
# if(CI)
|
# if(CI)
|
||||||
# set(DEFAULT_VENDOR_PATH ${CACHE_PATH}/vendor)
|
# 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")
|
if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm|ARM|arm64|ARM64|aarch64|AARCH64")
|
||||||
set(IS_ARM64 ON)
|
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
set(ZIG_CPU "apple_m1")
|
set(ZIG_CPU "apple_m1")
|
||||||
else()
|
else()
|
||||||
set(ZIG_CPU "native")
|
set(ZIG_CPU "native")
|
||||||
endif()
|
endif()
|
||||||
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|X86_64|x64|X64|amd64|AMD64")
|
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|X86_64|x64|X64|amd64|AMD64")
|
||||||
set(IS_X86_64 ON)
|
|
||||||
if(ENABLE_BASELINE)
|
if(ENABLE_BASELINE)
|
||||||
set(ZIG_CPU "nehalem")
|
set(ZIG_CPU "nehalem")
|
||||||
else()
|
else()
|
||||||
@@ -761,8 +759,8 @@ if(NOT WIN32)
|
|||||||
)
|
)
|
||||||
if(DEBUG)
|
if(DEBUG)
|
||||||
# TODO: this shouldn't be necessary long term
|
# TODO: this shouldn't be necessary long term
|
||||||
if (NOT IS_MUSL)
|
if (NOT ABI STREQUAL "musl")
|
||||||
set(ABI_PUBLIC_FLAGS
|
target_compile_options(${bun} PUBLIC
|
||||||
-fsanitize=null
|
-fsanitize=null
|
||||||
-fsanitize-recover=all
|
-fsanitize-recover=all
|
||||||
-fsanitize=bounds
|
-fsanitize=bounds
|
||||||
@@ -773,14 +771,9 @@ if(NOT WIN32)
|
|||||||
-fsanitize=returns-nonnull-attribute
|
-fsanitize=returns-nonnull-attribute
|
||||||
-fsanitize=unreachable
|
-fsanitize=unreachable
|
||||||
)
|
)
|
||||||
set(ABI_PRIVATE_FLAGS
|
target_link_libraries(${bun} PRIVATE
|
||||||
-fsanitize=null
|
-fsanitize=null
|
||||||
)
|
)
|
||||||
else()
|
|
||||||
set(ABI_PUBLIC_FLAGS
|
|
||||||
)
|
|
||||||
set(ABI_PRIVATE_FLAGS
|
|
||||||
)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_compile_options(${bun} PUBLIC
|
target_compile_options(${bun} PUBLIC
|
||||||
@@ -798,10 +791,6 @@ if(NOT WIN32)
|
|||||||
-Wno-unused-function
|
-Wno-unused-function
|
||||||
-Wno-nullability-completeness
|
-Wno-nullability-completeness
|
||||||
-Werror
|
-Werror
|
||||||
${ABI_PUBLIC_FLAGS}
|
|
||||||
)
|
|
||||||
target_link_libraries(${bun} PRIVATE
|
|
||||||
${ABI_PRIVATE_FLAGS}
|
|
||||||
)
|
)
|
||||||
else()
|
else()
|
||||||
# Leave -Werror=unused off in release builds so we avoid errors from being used in ASSERT
|
# 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
|
/delayload:IPHLPAPI.dll
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
elseif(APPLE)
|
endif()
|
||||||
|
|
||||||
|
if(APPLE)
|
||||||
target_link_options(${bun} PUBLIC
|
target_link_options(${bun} PUBLIC
|
||||||
-dead_strip
|
-dead_strip
|
||||||
-dead_strip_dylibs
|
-dead_strip_dylibs
|
||||||
@@ -856,63 +847,36 @@ elseif(APPLE)
|
|||||||
-fno-keep-static-consts
|
-fno-keep-static-consts
|
||||||
-Wl,-map,${bun}.linker-map
|
-Wl,-map,${bun}.linker-map
|
||||||
)
|
)
|
||||||
else()
|
endif()
|
||||||
# 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(LINUX)
|
||||||
if(CI)
|
if(NOT ABI STREQUAL "musl")
|
||||||
# Ensure we don't use a differing version of lld in CI vs clang
|
if(ARCH STREQUAL "aarch64")
|
||||||
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}")
|
target_link_options(${bun} PUBLIC
|
||||||
endif()
|
-Wl,--wrap=fcntl64
|
||||||
|
-Wl,--wrap=statx
|
||||||
# To make it easier for contributors, allow differing versions of lld vs clang/cmake
|
)
|
||||||
find_program(LLD_EXECUTABLE_NAME lld)
|
endif()
|
||||||
|
|
||||||
|
if(ARCH STREQUAL "x64")
|
||||||
|
target_link_options(${bun} PUBLIC
|
||||||
|
-Wl,--wrap=fcntl
|
||||||
|
-Wl,--wrap=fcntl64
|
||||||
|
-Wl,--wrap=fstat
|
||||||
|
-Wl,--wrap=fstat64
|
||||||
|
-Wl,--wrap=fstatat
|
||||||
|
-Wl,--wrap=fstatat64
|
||||||
|
-Wl,--wrap=lstat
|
||||||
|
-Wl,--wrap=lstat64
|
||||||
|
-Wl,--wrap=mknod
|
||||||
|
-Wl,--wrap=mknodat
|
||||||
|
-Wl,--wrap=stat
|
||||||
|
-Wl,--wrap=stat64
|
||||||
|
-Wl,--wrap=statx
|
||||||
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT LLD_EXECUTABLE_NAME)
|
target_link_options(${bun} PUBLIC
|
||||||
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
|
|
||||||
-Wl,--wrap=fcntl64
|
|
||||||
-Wl,--wrap=statx
|
|
||||||
)
|
|
||||||
elseif(IS_X86_64)
|
|
||||||
set(ARCH_WRAP_FLAGS
|
|
||||||
-Wl,--wrap=fcntl
|
|
||||||
-Wl,--wrap=fcntl64
|
|
||||||
-Wl,--wrap=fstat
|
|
||||||
-Wl,--wrap=fstat64
|
|
||||||
-Wl,--wrap=fstatat
|
|
||||||
-Wl,--wrap=fstatat64
|
|
||||||
-Wl,--wrap=lstat
|
|
||||||
-Wl,--wrap=lstat64
|
|
||||||
-Wl,--wrap=mknod
|
|
||||||
-Wl,--wrap=mknodat
|
|
||||||
-Wl,--wrap=stat
|
|
||||||
-Wl,--wrap=stat64
|
|
||||||
-Wl,--wrap=statx
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
set(ARCH_WRAP_FLAGS
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (NOT IS_MUSL)
|
|
||||||
set(ABI_WRAP_FLAGS
|
|
||||||
-Wl,--wrap=cosf
|
-Wl,--wrap=cosf
|
||||||
-Wl,--wrap=exp
|
-Wl,--wrap=exp
|
||||||
-Wl,--wrap=expf
|
-Wl,--wrap=expf
|
||||||
@@ -929,13 +893,10 @@ else()
|
|||||||
-Wl,--wrap=sinf
|
-Wl,--wrap=sinf
|
||||||
-Wl,--wrap=tanf
|
-Wl,--wrap=tanf
|
||||||
)
|
)
|
||||||
else()
|
|
||||||
set(ABI_WRAP_FLAGS
|
|
||||||
)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_link_options(${bun} PUBLIC
|
target_link_options(${bun} PUBLIC
|
||||||
-fuse-ld=${LLD_NAME}
|
--ld-path=${LLD_PROGRAM}
|
||||||
-fno-pic
|
-fno-pic
|
||||||
-static-libstdc++
|
-static-libstdc++
|
||||||
-static-libgcc
|
-static-libgcc
|
||||||
@@ -944,8 +905,6 @@ else()
|
|||||||
-Wl,--as-needed
|
-Wl,--as-needed
|
||||||
-Wl,--gc-sections
|
-Wl,--gc-sections
|
||||||
-Wl,-z,stack-size=12800000
|
-Wl,-z,stack-size=12800000
|
||||||
${ARCH_WRAP_FLAGS}
|
|
||||||
${ABI_WRAP_FLAGS}
|
|
||||||
-Wl,--compress-debug-sections=zlib
|
-Wl,--compress-debug-sections=zlib
|
||||||
-Wl,-z,lazy
|
-Wl,-z,lazy
|
||||||
-Wl,-z,norelro
|
-Wl,-z,norelro
|
||||||
@@ -1095,12 +1054,12 @@ endif()
|
|||||||
|
|
||||||
if(NOT BUN_CPP_ONLY)
|
if(NOT BUN_CPP_ONLY)
|
||||||
set(CMAKE_STRIP_FLAGS "")
|
set(CMAKE_STRIP_FLAGS "")
|
||||||
if (APPLE)
|
if(APPLE)
|
||||||
# We do not build with exceptions enabled. These are generated by lolhtml
|
# We do not build with exceptions enabled. These are generated by lolhtml
|
||||||
# and other dependencies. We build lolhtml with abort on panic, so it
|
# and other dependencies. We build lolhtml with abort on panic, so it
|
||||||
# shouldn't be including these in the first place.
|
# 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)
|
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]]
|
# 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.
|
# So, we must use GNU strip to do this.
|
||||||
set(CMAKE_STRIP_FLAGS -R .eh_frame -R .gcc_except_table)
|
set(CMAKE_STRIP_FLAGS -R .eh_frame -R .gcc_except_table)
|
||||||
@@ -1193,10 +1152,12 @@ if(NOT BUN_CPP_ONLY)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(CI)
|
if(CI)
|
||||||
|
set(bunTriplet bun-${OS}-${ARCH})
|
||||||
|
if(ABI STREQUAL "musl")
|
||||||
|
set(bunTriplet ${bunTriplet}-musl)
|
||||||
|
endif()
|
||||||
if(ENABLE_BASELINE)
|
if(ENABLE_BASELINE)
|
||||||
set(bunTriplet bun-${OS}-${ARCH}-baseline)
|
set(bunTriplet ${bunTriplet}-baseline)
|
||||||
else()
|
|
||||||
set(bunTriplet bun-${OS}-${ARCH})
|
|
||||||
endif()
|
endif()
|
||||||
string(REPLACE bun ${bunTriplet} bunPath ${bun})
|
string(REPLACE bun ${bunTriplet} bunPath ${bun})
|
||||||
set(bunFiles ${bunExe} features.json)
|
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_NAME Linux)
|
||||||
set(CMAKE_SYSTEM_PROCESSOR aarch64)
|
set(CMAKE_SYSTEM_PROCESSOR aarch64)
|
||||||
|
set(ABI gnu)
|
||||||
|
|
||||||
set(CMAKE_C_COMPILER_WORKS ON)
|
set(CMAKE_C_COMPILER_WORKS ON)
|
||||||
set(CMAKE_CXX_COMPILER_WORKS ON)
|
set(CMAKE_CXX_COMPILER_WORKS ON)
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
set(CMAKE_SYSTEM_NAME Linux)
|
set(CMAKE_SYSTEM_NAME Linux)
|
||||||
set(CMAKE_SYSTEM_PROCESSOR x64)
|
set(CMAKE_SYSTEM_PROCESSOR x64)
|
||||||
set(ENABLE_BASELINE ON)
|
set(ENABLE_BASELINE ON)
|
||||||
|
set(ABI gnu)
|
||||||
|
|
||||||
set(CMAKE_C_COMPILER_WORKS ON)
|
set(CMAKE_C_COMPILER_WORKS ON)
|
||||||
set(CMAKE_CXX_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_NAME Linux)
|
||||||
set(CMAKE_SYSTEM_PROCESSOR x64)
|
set(CMAKE_SYSTEM_PROCESSOR x64)
|
||||||
|
set(ABI gnu)
|
||||||
|
|
||||||
set(CMAKE_C_COMPILER_WORKS ON)
|
set(CMAKE_C_COMPILER_WORKS ON)
|
||||||
set(CMAKE_CXX_COMPILER_WORKS ON)
|
set(CMAKE_CXX_COMPILER_WORKS ON)
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ execute_process(
|
|||||||
)
|
)
|
||||||
|
|
||||||
if(NOT GIT_DIFF_RESULT EQUAL 0)
|
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()
|
return()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ if(NOT ENABLE_LLVM)
|
|||||||
return()
|
return()
|
||||||
endif()
|
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")
|
set(DEFAULT_LLVM_VERSION "18.1.8")
|
||||||
else()
|
else()
|
||||||
set(DEFAULT_LLVM_VERSION "16.0.6")
|
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}.${LLVM_VERSION_PATCH}/bin
|
||||||
/usr/lib/llvm-${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}/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
|
||||||
|
/usr/lib/llvm${LLVM_VERSION_MAJOR}/bin
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
@@ -122,6 +123,9 @@ else()
|
|||||||
find_llvm_command(CMAKE_STRIP llvm-strip)
|
find_llvm_command(CMAKE_STRIP llvm-strip)
|
||||||
endif()
|
endif()
|
||||||
find_llvm_command(CMAKE_RANLIB llvm-ranlib)
|
find_llvm_command(CMAKE_RANLIB llvm-ranlib)
|
||||||
|
if(LINUX)
|
||||||
|
find_llvm_command(LLD_PROGRAM ld.lld)
|
||||||
|
endif()
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
find_llvm_command(CMAKE_DSYMUTIL dsymutil)
|
find_llvm_command(CMAKE_DSYMUTIL dsymutil)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ else()
|
|||||||
message(FATAL_ERROR "Unsupported architecture: ${CMAKE_SYSTEM_PROCESSOR}")
|
message(FATAL_ERROR "Unsupported architecture: ${CMAKE_SYSTEM_PROCESSOR}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(IS_MUSL)
|
if(ABI STREQUAL "musl")
|
||||||
set(WEBKIT_SUFFIX "-musl")
|
set(WEBKIT_SUFFIX "-musl")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ if(APPLE)
|
|||||||
elseif(WIN32)
|
elseif(WIN32)
|
||||||
set(DEFAULT_ZIG_TARGET ${DEFAULT_ZIG_ARCH}-windows-msvc)
|
set(DEFAULT_ZIG_TARGET ${DEFAULT_ZIG_ARCH}-windows-msvc)
|
||||||
elseif(LINUX)
|
elseif(LINUX)
|
||||||
if(IS_MUSL)
|
if(ABI STREQUAL "musl")
|
||||||
set(DEFAULT_ZIG_TARGET ${DEFAULT_ZIG_ARCH}-linux-musl)
|
set(DEFAULT_ZIG_TARGET ${DEFAULT_ZIG_ARCH}-linux-musl)
|
||||||
else()
|
else()
|
||||||
set(DEFAULT_ZIG_TARGET ${DEFAULT_ZIG_ARCH}-linux-gnu)
|
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";
|
} from "./utils.mjs";
|
||||||
import { userInfo } from "node:os";
|
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 testsPath = join(cwd, "test");
|
||||||
|
|
||||||
const spawnTimeout = 5_000;
|
const spawnTimeout = 5_000;
|
||||||
@@ -232,7 +232,7 @@ async function runTests() {
|
|||||||
if (testRunner === "bun") {
|
if (testRunner === "bun") {
|
||||||
await runTest(title, () => spawnBunTest(execPath, testPath, { cwd: vendorPath }));
|
await runTest(title, () => spawnBunTest(execPath, testPath, { cwd: vendorPath }));
|
||||||
} else {
|
} else {
|
||||||
const testRunnerPath = join(import.meta.dirname, "..", "test", "runners", `${testRunner}.ts`);
|
const testRunnerPath = join(cwd, "test", "runners", `${testRunner}.ts`);
|
||||||
if (!existsSync(testRunnerPath)) {
|
if (!existsSync(testRunnerPath)) {
|
||||||
throw new Error(`Unsupported test runner: ${testRunner}`);
|
throw new Error(`Unsupported test runner: ${testRunner}`);
|
||||||
}
|
}
|
||||||
@@ -632,7 +632,7 @@ function parseTestStdout(stdout, testPath) {
|
|||||||
const removeStart = lines.length - skipCount;
|
const removeStart = lines.length - skipCount;
|
||||||
const removeCount = skipCount - 2;
|
const removeCount = skipCount - 2;
|
||||||
const omitLine = `${getAnsi("gray")}... omitted ${removeCount} tests ...${getAnsi("reset")}`;
|
const omitLine = `${getAnsi("gray")}... omitted ${removeCount} tests ...${getAnsi("reset")}`;
|
||||||
lines = lines.toSpliced(removeStart, removeCount, omitLine);
|
lines.splice(removeStart, removeCount, omitLine);
|
||||||
}
|
}
|
||||||
skipCount = 0;
|
skipCount = 0;
|
||||||
}
|
}
|
||||||
@@ -1133,6 +1133,13 @@ function addPath(...paths) {
|
|||||||
return paths.join(":");
|
return paths.join(":");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {string | undefined}
|
||||||
|
*/
|
||||||
|
function getTestLabel() {
|
||||||
|
return getBuildLabel()?.replace(" - test-bun", "");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {TestResult | TestResult[]} result
|
* @param {TestResult | TestResult[]} result
|
||||||
* @param {boolean} concise
|
* @param {boolean} concise
|
||||||
@@ -1140,7 +1147,7 @@ function addPath(...paths) {
|
|||||||
*/
|
*/
|
||||||
function formatTestToMarkdown(result, concise) {
|
function formatTestToMarkdown(result, concise) {
|
||||||
const results = Array.isArray(result) ? result : [result];
|
const results = Array.isArray(result) ? result : [result];
|
||||||
const buildLabel = getBuildLabel();
|
const buildLabel = getTestLabel();
|
||||||
const buildUrl = getBuildUrl();
|
const buildUrl = getBuildUrl();
|
||||||
const platform = buildUrl ? `<a href="${buildUrl}">${buildLabel}</a>` : buildLabel;
|
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}`;
|
const cause = error ?? signal ?? `code ${status}`;
|
||||||
throw new Error(`Failed to create annotation: ${label}`, { cause });
|
throw new Error(`Failed to create annotation: ${label}`, { cause });
|
||||||
}
|
}
|
||||||
const buildLabel = getBuildLabel();
|
const buildLabel = getTestLabel();
|
||||||
const buildUrl = getBuildUrl();
|
const buildUrl = getBuildUrl();
|
||||||
const platform = buildUrl ? `<a href="${buildUrl}">${buildLabel}</a>` : buildLabel;
|
const platform = buildUrl ? `<a href="${buildUrl}">${buildLabel}</a>` : buildLabel;
|
||||||
let errorMessage = `<details><summary><a><code>${label}</code></a> - annotation error on ${platform}</summary>`;
|
let errorMessage = `<details><summary><a><code>${label}</code></a> - annotation error on ${platform}</summary>`;
|
||||||
|
|||||||
622
scripts/utils.mjs
Normal file → Executable file
622
scripts/utils.mjs
Normal file → Executable file
@@ -3,9 +3,18 @@
|
|||||||
|
|
||||||
import { spawn as nodeSpawn, spawnSync as nodeSpawnSync } from "node:child_process";
|
import { spawn as nodeSpawn, spawnSync as nodeSpawnSync } from "node:child_process";
|
||||||
import { createHash } from "node:crypto";
|
import { createHash } from "node:crypto";
|
||||||
import { appendFileSync, existsSync, mkdtempSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
|
import {
|
||||||
import { writeFile } from "node:fs/promises";
|
appendFileSync,
|
||||||
import { hostname, tmpdir as nodeTmpdir, userInfo } from "node:os";
|
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 { dirname, join, relative, resolve } from "node:path";
|
||||||
import { normalize as normalizeWindows } from "node:path/win32";
|
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");
|
command.push("--skip-redaction");
|
||||||
}
|
}
|
||||||
|
|
||||||
const { error, stdout: secret } = spawnSync(command);
|
const { error, stdout } = spawnSync(command);
|
||||||
if (error || !secret.trim()) {
|
const secret = stdout.trim();
|
||||||
|
if (error || !secret) {
|
||||||
const orgId = getEnv("BUILDKITE_ORGANIZATION_SLUG", false);
|
const orgId = getEnv("BUILDKITE_ORGANIZATION_SLUG", false);
|
||||||
const clusterId = getEnv("BUILDKITE_CLUSTER_ID", false);
|
const clusterId = getEnv("BUILDKITE_CLUSTER_ID", false);
|
||||||
|
|
||||||
@@ -106,8 +116,8 @@ export function setEnv(name, value) {
|
|||||||
* @property {string} [cwd]
|
* @property {string} [cwd]
|
||||||
* @property {number} [timeout]
|
* @property {number} [timeout]
|
||||||
* @property {Record<string, string | undefined>} [env]
|
* @property {Record<string, string | undefined>} [env]
|
||||||
* @property {string} [stdout]
|
* @property {string} [stdin]
|
||||||
* @property {string} [stderr]
|
* @property {boolean} [privileged]
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -119,20 +129,93 @@ export function setEnv(name, value) {
|
|||||||
* @property {Error} [error]
|
* @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 {string[]} command
|
||||||
* @param {SpawnOptions} options
|
* @param {SpawnOptions} options
|
||||||
* @returns {Promise<SpawnResult>}
|
* @returns {Promise<SpawnResult>}
|
||||||
*/
|
*/
|
||||||
export async function spawn(command, options = {}) {
|
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 = {
|
const spawnOptions = {
|
||||||
cwd: options["cwd"] ?? process.cwd(),
|
cwd: options["cwd"] ?? process.cwd(),
|
||||||
timeout: options["timeout"] ?? undefined,
|
timeout: options["timeout"] ?? undefined,
|
||||||
env: options["env"] ?? undefined,
|
env: options["env"] ?? undefined,
|
||||||
stdio: ["ignore", "pipe", "pipe"],
|
stdio: [stdin ? "pipe" : "ignore", "pipe", "pipe"],
|
||||||
...options,
|
...options,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -145,6 +228,16 @@ export async function spawn(command, options = {}) {
|
|||||||
const result = new Promise((resolve, reject) => {
|
const result = new Promise((resolve, reject) => {
|
||||||
const subprocess = nodeSpawn(cmd, args, spawnOptions);
|
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 => {
|
subprocess.stdout?.on("data", chunk => {
|
||||||
stdout += chunk;
|
stdout += chunk;
|
||||||
});
|
});
|
||||||
@@ -215,9 +308,9 @@ export async function spawnSafe(command, options) {
|
|||||||
* @returns {SpawnResult}
|
* @returns {SpawnResult}
|
||||||
*/
|
*/
|
||||||
export function spawnSync(command, options = {}) {
|
export function spawnSync(command, options = {}) {
|
||||||
debugLog("$", ...command);
|
const [cmd, ...args] = parseCommand(command, options);
|
||||||
|
debugLog("$", cmd, ...args);
|
||||||
|
|
||||||
const [cmd, ...args] = command;
|
|
||||||
const spawnOptions = {
|
const spawnOptions = {
|
||||||
cwd: options["cwd"] ?? process.cwd(),
|
cwd: options["cwd"] ?? process.cwd(),
|
||||||
timeout: options["timeout"] ?? undefined,
|
timeout: options["timeout"] ?? undefined,
|
||||||
@@ -245,8 +338,8 @@ export function spawnSync(command, options = {}) {
|
|||||||
} else {
|
} else {
|
||||||
exitCode = status ?? 1;
|
exitCode = status ?? 1;
|
||||||
signalCode = signal || undefined;
|
signalCode = signal || undefined;
|
||||||
stdout = stdoutBuffer.toString();
|
stdout = stdoutBuffer?.toString();
|
||||||
stderr = stderrBuffer.toString();
|
stderr = stderrBuffer?.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (exitCode !== 0 && isWindows) {
|
if (exitCode !== 0 && isWindows) {
|
||||||
@@ -258,7 +351,7 @@ export function spawnSync(command, options = {}) {
|
|||||||
|
|
||||||
if (error || signalCode || exitCode !== 0) {
|
if (error || signalCode || exitCode !== 0) {
|
||||||
const description = command.map(arg => (arg.includes(" ") ? `"${arg.replace(/"/g, '\\"')}"` : arg)).join(" ");
|
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) {
|
if (signalCode) {
|
||||||
error = new Error(`Command killed with ${signalCode}: ${description}`, { cause });
|
error = new Error(`Command killed with ${signalCode}: ${description}`, { cause });
|
||||||
@@ -670,7 +763,7 @@ export async function curl(url, options = {}) {
|
|||||||
try {
|
try {
|
||||||
if (filename && ok) {
|
if (filename && ok) {
|
||||||
const buffer = await response.arrayBuffer();
|
const buffer = await response.arrayBuffer();
|
||||||
await writeFile(filename, new Uint8Array(buffer));
|
writeFile(filename, new Uint8Array(buffer));
|
||||||
} else if (arrayBuffer && ok) {
|
} else if (arrayBuffer && ok) {
|
||||||
body = await response.arrayBuffer();
|
body = await response.arrayBuffer();
|
||||||
} else if (json && ok) {
|
} else if (json && ok) {
|
||||||
@@ -735,7 +828,7 @@ export function readFile(filename, options = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const relativePath = relative(process.cwd(), absolutePath);
|
const relativePath = relative(process.cwd(), absolutePath);
|
||||||
debugLog("cat", relativePath);
|
debugLog("$", "cat", relativePath);
|
||||||
|
|
||||||
let content;
|
let content;
|
||||||
try {
|
try {
|
||||||
@@ -752,6 +845,51 @@ export function readFile(filename, options = {}) {
|
|||||||
return content;
|
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} [cwd]
|
||||||
* @param {string} [base]
|
* @param {string} [base]
|
||||||
@@ -840,7 +978,7 @@ export function getBuildUrl() {
|
|||||||
*/
|
*/
|
||||||
export function getBuildLabel() {
|
export function getBuildLabel() {
|
||||||
if (isBuildkite) {
|
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) {
|
if (label) {
|
||||||
return 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
|
* @typedef {object} BuildArtifact
|
||||||
* @property {string} [job]
|
* @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
|
* @param {string} string
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
@@ -1035,6 +1200,17 @@ export function stripAnsi(string) {
|
|||||||
return string.replace(/\u001b\[\d+m/g, "");
|
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
|
* @param {string} string
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
@@ -1173,24 +1349,79 @@ export function getArch() {
|
|||||||
return parseArch(process.arch);
|
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}
|
* @returns {"musl" | "gnu" | undefined}
|
||||||
*/
|
*/
|
||||||
export function getAbi() {
|
export function getAbi() {
|
||||||
if (isLinux) {
|
if (!isLinux) {
|
||||||
const arch = getArch() === "x64" ? "x86_64" : "aarch64";
|
return;
|
||||||
const muslLibPath = `/lib/ld-musl-${arch}.so.1`;
|
}
|
||||||
if (existsSync(muslLibPath)) {
|
|
||||||
|
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)) {
|
||||||
|
return "musl";
|
||||||
|
}
|
||||||
|
|
||||||
|
const gnuLibPath = `/lib/ld-linux-${arch}.so.2`;
|
||||||
|
if (existsSync(gnuLibPath)) {
|
||||||
|
return "gnu";
|
||||||
|
}
|
||||||
|
|
||||||
|
const { error, stdout } = spawnSync(["ldd", "--version"]);
|
||||||
|
if (!error) {
|
||||||
|
if (/musl/i.test(stdout)) {
|
||||||
return "musl";
|
return "musl";
|
||||||
}
|
}
|
||||||
|
if (/gnu|glibc/i.test(stdout)) {
|
||||||
const gnuLibPath = `/lib/ld-linux-${arch}.so.2`;
|
|
||||||
if (existsSync(gnuLibPath)) {
|
|
||||||
return "gnu";
|
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}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {object} Target
|
* @typedef {object} Target
|
||||||
* @property {"darwin" | "linux" | "windows"} os
|
* @property {"darwin" | "linux" | "windows"} os
|
||||||
@@ -1360,17 +1591,24 @@ export async function downloadTarget(target, release) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns {string | undefined}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
export function getTailscaleIp() {
|
export function getTailscale() {
|
||||||
let tailscale = "tailscale";
|
|
||||||
if (isMacOS) {
|
if (isMacOS) {
|
||||||
const tailscaleApp = "/Applications/Tailscale.app/Contents/MacOS/tailscale";
|
const tailscaleApp = "/Applications/Tailscale.app/Contents/MacOS/tailscale";
|
||||||
if (existsSync(tailscaleApp)) {
|
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"]);
|
const { error, stdout } = spawnSync([tailscale, "ip", "--1"]);
|
||||||
if (!error) {
|
if (!error) {
|
||||||
return stdout.trim();
|
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() {
|
export function getDistro() {
|
||||||
if (isMacOS) {
|
if (isMacOS) {
|
||||||
@@ -1427,6 +1689,11 @@ export function getDistro() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isLinux) {
|
if (isLinux) {
|
||||||
|
const alpinePath = "/etc/alpine-release";
|
||||||
|
if (existsSync(alpinePath)) {
|
||||||
|
return "alpine";
|
||||||
|
}
|
||||||
|
|
||||||
const releasePath = "/etc/os-release";
|
const releasePath = "/etc/os-release";
|
||||||
if (existsSync(releasePath)) {
|
if (existsSync(releasePath)) {
|
||||||
const releaseFile = readFile(releasePath, { cache: true });
|
const releaseFile = readFile(releasePath, { cache: true });
|
||||||
@@ -1438,10 +1705,8 @@ export function getDistro() {
|
|||||||
|
|
||||||
const { error, stdout } = spawnSync(["lsb_release", "-is"]);
|
const { error, stdout } = spawnSync(["lsb_release", "-is"]);
|
||||||
if (!error) {
|
if (!error) {
|
||||||
return stdout.trim();
|
return stdout.trim().toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
return "Linux";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isWindows) {
|
if (isWindows) {
|
||||||
@@ -1449,17 +1714,13 @@ export function getDistro() {
|
|||||||
if (!error) {
|
if (!error) {
|
||||||
return stdout.trim();
|
return stdout.trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
return "Windows";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return `${process.platform} ${process.arch}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns {string | undefined}
|
* @returns {string | undefined}
|
||||||
*/
|
*/
|
||||||
export function getDistroRelease() {
|
export function getDistroVersion() {
|
||||||
if (isMacOS) {
|
if (isMacOS) {
|
||||||
const { error, stdout } = spawnSync(["sw_vers", "-productVersion"]);
|
const { error, stdout } = spawnSync(["sw_vers", "-productVersion"]);
|
||||||
if (!error) {
|
if (!error) {
|
||||||
@@ -1468,6 +1729,16 @@ export function getDistroRelease() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isLinux) {
|
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";
|
const releasePath = "/etc/os-release";
|
||||||
if (existsSync(releasePath)) {
|
if (existsSync(releasePath)) {
|
||||||
const releaseFile = readFile(releasePath, { cache: true });
|
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>}
|
* @returns {Promise<number | undefined>}
|
||||||
*/
|
*/
|
||||||
@@ -1536,6 +2032,52 @@ export function getGithubUrl() {
|
|||||||
return new URL(getEnv("GITHUB_SERVER_URL", false) || "https://github.com");
|
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 {string} title
|
||||||
* @param {function} [fn]
|
* @param {function} [fn]
|
||||||
@@ -1575,11 +2117,13 @@ export function printEnvironment() {
|
|||||||
startGroup("Machine", () => {
|
startGroup("Machine", () => {
|
||||||
console.log("Operating System:", getOs());
|
console.log("Operating System:", getOs());
|
||||||
console.log("Architecture:", getArch());
|
console.log("Architecture:", getArch());
|
||||||
|
console.log("Kernel:", getKernel());
|
||||||
if (isLinux) {
|
if (isLinux) {
|
||||||
console.log("ABI:", getAbi());
|
console.log("ABI:", getAbi());
|
||||||
|
console.log("ABI Version:", getAbiVersion());
|
||||||
}
|
}
|
||||||
console.log("Distro:", getDistro());
|
console.log("Distro:", getDistro());
|
||||||
console.log("Release:", getDistroRelease());
|
console.log("Distro Version:", getDistroVersion());
|
||||||
console.log("Hostname:", getHostname());
|
console.log("Hostname:", getHostname());
|
||||||
if (isCI) {
|
if (isCI) {
|
||||||
console.log("Tailscale IP:", getTailscaleIp());
|
console.log("Tailscale IP:", getTailscaleIp());
|
||||||
|
|||||||
@@ -78,6 +78,14 @@ case $platform in
|
|||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
case "$target" in
|
||||||
|
'linux'*)
|
||||||
|
if [ -f /etc/alpine-release ]; then
|
||||||
|
target="$target-musl"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
if [[ $target = darwin-x64 ]]; then
|
if [[ $target = darwin-x64 ]]; then
|
||||||
# Is this process running in Rosetta?
|
# Is this process running in Rosetta?
|
||||||
# redirect stderr to devnull to avoid error message when not 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"
|
github_repo="$GITHUB/oven-sh/bun"
|
||||||
|
|
||||||
if [[ $target = darwin-x64 ]]; then
|
# If AVX2 isn't supported, use the -baseline build
|
||||||
# If AVX2 isn't supported, use the -baseline build
|
case "$target" in
|
||||||
|
'darwin-x64'*)
|
||||||
if [[ $(sysctl -a | grep machdep.cpu | grep AVX2) == '' ]]; then
|
if [[ $(sysctl -a | grep machdep.cpu | grep AVX2) == '' ]]; then
|
||||||
target=darwin-x64-baseline
|
target="$target-baseline"
|
||||||
fi
|
fi
|
||||||
fi
|
;;
|
||||||
|
'linux-x64'*)
|
||||||
if [[ $target = linux-x64 ]]; then
|
|
||||||
# If AVX2 isn't supported, use the -baseline build
|
# If AVX2 isn't supported, use the -baseline build
|
||||||
if [[ $(cat /proc/cpuinfo | grep avx2) = '' ]]; then
|
if [[ $(cat /proc/cpuinfo | grep avx2) = '' ]]; then
|
||||||
target=linux-x64-baseline
|
target="$target-baseline"
|
||||||
fi
|
fi
|
||||||
fi
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
exe_name=bun
|
exe_name=bun
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user