fix(bun-release): support windows in npm package (#9873)

* fix npm install on windows

* try again

* again

* copy less file

* revert changes

* remove package.json from git

* okay

* now?
This commit is contained in:
dave caruso
2024-04-03 23:16:48 -07:00
committed by GitHub
parent 14c23cc429
commit 0cdad4bebb
18 changed files with 244 additions and 187 deletions

44
.github/workflows/bun-release-test.yml vendored Normal file
View File

@@ -0,0 +1,44 @@
# This workflow tests bun-release's code and the packages to ensure that npm,
# yarn, and pnpm can install bun on all platforms. This does not test that bun
# itself works as it hardcodes 1.1.0 as the version to package.
name: bun-release-test
concurrency: release-test
on:
pull_request:
paths:
- "packages/bun-release/**"
- ".github/workflows/bun-release-test.yml"
jobs:
test-release-script:
name: Test Release Script
strategy:
matrix:
machine: [namespace-profile-bun-linux-x64, linux-arm64, macos-arm64, macos-12-large, windows-latest]
fail-fast: false
runs-on: ${{ matrix.machine }}
permissions:
contents: read
defaults:
run:
working-directory: packages/bun-release
timeout-minutes: 5
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Bun
uses: oven-sh/setup-bun@v1
with:
bun-version: "1.1.0"
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
- name: Install Dependencies
run: bun install && npm i -g pnpm yarn npm
- name: Release
run: bun upload-npm -- 1.1.0 test
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

2
.gitignore vendored
View File

@@ -140,4 +140,4 @@ txt.js
x64
yarn.lock
zig-cache
zig-out
zig-out

View File

@@ -1,6 +1,8 @@
.DS_Store
.env
node_modules
/npm/**/bin
/npm/**/*.js
/npm/**/.npmrc
.DS_Store
.env
node_modules
/npm/**/bin
/npm/**/*.js
/npm/**/package.json
/npm/**/.npmrc
*.tgz

Binary file not shown.

View File

@@ -1,16 +0,0 @@
{
"name": "@oven/bun-darwin-aarch64",
"version": "0.5.3",
"description": "This is the macOS arm64 binary for Bun, a fast all-in-one JavaScript runtime.",
"homepage": "https://bun.sh",
"bugs": "https://github.com/oven-sh/issues",
"license": "MIT",
"repository": "https://github.com/oven-sh/bun",
"preferUnplugged": true,
"os": [
"darwin"
],
"cpu": [
"arm64"
]
}

View File

@@ -1,16 +0,0 @@
{
"name": "@oven/bun-darwin-x64-baseline",
"version": "0.5.3",
"description": "This is the macOS x64 binary for Bun, a fast all-in-one JavaScript runtime.",
"homepage": "https://bun.sh",
"bugs": "https://github.com/oven-sh/issues",
"license": "MIT",
"repository": "https://github.com/oven-sh/bun",
"preferUnplugged": true,
"os": [
"darwin"
],
"cpu": [
"x64"
]
}

View File

@@ -1,16 +0,0 @@
{
"name": "@oven/bun-darwin-x64",
"version": "0.5.3",
"description": "This is the macOS x64 binary for Bun, a fast all-in-one JavaScript runtime.",
"homepage": "https://bun.sh",
"bugs": "https://github.com/oven-sh/issues",
"license": "MIT",
"repository": "https://github.com/oven-sh/bun",
"preferUnplugged": true,
"os": [
"darwin"
],
"cpu": [
"x64"
]
}

View File

@@ -1,16 +0,0 @@
{
"name": "@oven/bun-linux-aarch64",
"version": "0.5.3",
"description": "This is the Linux arm64 binary for Bun, a fast all-in-one JavaScript runtime.",
"homepage": "https://bun.sh",
"bugs": "https://github.com/oven-sh/issues",
"license": "MIT",
"repository": "https://github.com/oven-sh/bun",
"preferUnplugged": true,
"os": [
"linux"
],
"cpu": [
"arm64"
]
}

View File

@@ -1,16 +0,0 @@
{
"name": "@oven/bun-linux-x64-baseline",
"version": "0.5.3",
"description": "This is the Linux x64 binary for Bun, a fast all-in-one JavaScript runtime.",
"homepage": "https://bun.sh",
"bugs": "https://github.com/oven-sh/issues",
"license": "MIT",
"repository": "https://github.com/oven-sh/bun",
"preferUnplugged": true,
"os": [
"linux"
],
"cpu": [
"x64"
]
}

View File

@@ -1,16 +0,0 @@
{
"name": "@oven/bun-linux-x64",
"version": "0.5.3",
"description": "This is the Linux x64 binary for Bun, a fast all-in-one JavaScript runtime.",
"homepage": "https://bun.sh",
"bugs": "https://github.com/oven-sh/issues",
"license": "MIT",
"repository": "https://github.com/oven-sh/bun",
"preferUnplugged": true,
"os": [
"linux"
],
"cpu": [
"x64"
]
}

View File

@@ -0,0 +1,5 @@
# Bun
This is the Windows x64 binary for Bun, a fast all-in-one JavaScript runtime. https://bun.sh
_Note: "Baseline" builds are for machines that do not support [AVX2](https://en.wikipedia.org/wiki/Advanced_Vector_Extensions) instructions._

View File

@@ -0,0 +1,3 @@
# Bun
This is the Windows x64 binary for Bun, a fast all-in-one JavaScript runtime. https://bun.sh

View File

@@ -1,42 +0,0 @@
{
"name": "bun",
"version": "0.5.3",
"description": "Bun is a fast all-in-one JavaScript runtime.",
"keywords": [
"bun",
"bun.js",
"node",
"node.js",
"runtime",
"bundler",
"transpiler",
"typescript"
],
"homepage": "https://bun.sh",
"bugs": "https://github.com/oven-sh/issues",
"license": "MIT",
"bin": {
"bun": "bin/bun",
"bunx": "bin/bun"
},
"repository": "https://github.com/oven-sh/bun",
"scripts": {
"postinstall": "node install.js"
},
"optionalDependencies": {
"@oven/bun-darwin-aarch64": "0.5.3",
"@oven/bun-darwin-x64": "0.5.3",
"@oven/bun-darwin-x64-baseline": "0.5.3",
"@oven/bun-linux-aarch64": "0.5.3",
"@oven/bun-linux-x64": "0.5.3",
"@oven/bun-linux-x64-baseline": "0.5.3"
},
"os": [
"darwin",
"linux"
],
"cpu": [
"arm64",
"x64"
]
}

View File

@@ -9,7 +9,7 @@
},
"devDependencies": {
"@octokit/types": "^8.1.1",
"bun-types": "^0.4.0",
"bun-types": "^1.1.0",
"prettier": "^2.8.2"
},
"scripts": {

View File

@@ -1,4 +1,4 @@
import { importBun } from "../src/npm/install";
import { importBun, optimizeBun } from "../src/npm/install";
import { execFileSync } from "child_process";
importBun()

View File

@@ -1,4 +1,8 @@
import { join, copy, exists, chmod, write, writeJson } from "../src/fs";
import { mkdtemp } from "fs/promises";
import { rmSync, mkdirSync } from "fs";
import { tmpdir } from "os";
import { dirname } from "path";
import { fetch } from "../src/fetch";
import { spawn } from "../src/spawn";
import type { Platform } from "../src/platform";
@@ -10,41 +14,51 @@ import { buildSync, formatMessagesSync } from "esbuild";
import type { JSZipObject } from "jszip";
import { loadAsync } from "jszip";
import { debug, log, error } from "../src/console";
import { expect } from "bun:test";
const module = "bun";
const owner = "@oven";
let version: string;
const [tag, action] = process.argv.slice(2);
await build(tag);
const release = await getRelease(tag);
const version = await getSemver(release.tag_name);
if (action !== "test-only") await build();
if (action === "publish") {
await publish();
} else if (action === "dry-run") {
await publish(true);
} else if (action === "test") {
await publish(true);
await test();
} else if (action === "test-only") {
await test();
} else if (action) {
throw new Error(`Unknown action: ${action}`);
}
process.exit(0); // HACK
async function build(tag?: string): Promise<void> {
const release = await getRelease(tag);
version = await getSemver(release.tag_name);
async function build(): Promise<void> {
await buildRootModule();
for (const platform of platforms) {
if (action !== "publish" && (platform.os !== process.platform || platform.arch !== process.arch)) continue;
await buildModule(release, platform);
}
}
async function publish(dryRun?: boolean): Promise<void> {
const modules = platforms.map(({ bin }) => `${owner}/${bin}`);
const modules = platforms
.filter(({ os, arch }) => action === "publish" || (os === process.platform && arch === process.arch))
.map(({ bin }) => `${owner}/${bin}`);
modules.push(module);
for (const module of modules) {
publishModule(module, dryRun);
}
}
async function buildRootModule() {
async function buildRootModule(dryRun?: boolean) {
log("Building:", `${module}@${version}`);
const cwd = join("npm", module);
const define = {
@@ -54,28 +68,53 @@ async function buildRootModule() {
};
bundle(join("scripts", "npm-postinstall.ts"), join(cwd, "install.js"), {
define,
});
bundle(join("scripts", "npm-exec.ts"), join(cwd, "bin", "bun"), {
define,
banner: {
js: "#!/usr/bin/env node",
js: "// Source code: https://github.com/oven-sh/bun/blob/main/packages/bun-release/scripts/npm-postinstall.ts",
},
});
write(join(cwd, "bin", "bun.exe"), "");
write(
join(cwd, "bin", "README.txt"),
`The 'bun.exe' file is a placeholder for the binary file, which
is replaced by Bun's 'postinstall' script. For this to work, make
sure that you do not use --ignore-scripts while installing.
The postinstall script is responsible for linking the binary file
directly into 'node_modules/.bin' and avoiding a Node.js wrapper
script being called on every invocation of 'bun'. If this wasn't
done, Bun would seem to be slower than Node.js, because it would
be executing a copy of Node.js every time!
Unfortunately, it is not possible to fix all cases on all platforms
without *requiring* a postinstall script.
`,
);
const os = [...new Set(platforms.map(({ os }) => os))];
const cpu = [...new Set(platforms.map(({ arch }) => arch))];
writeJson(join(cwd, "package.json"), {
name: module,
description: "Bun is a fast all-in-one JavaScript runtime.",
version: version,
scripts: {
postinstall: "node install.js",
},
optionalDependencies: Object.fromEntries(platforms.map(({ bin }) => [`${owner}/${bin}`, version])),
optionalDependencies: Object.fromEntries(
platforms.map(({ bin }) => [
`${owner}/${bin}`,
dryRun ? `file:./oven-${bin.replaceAll("/", "-") + "-" + version + ".tgz"}` : version,
]),
),
bin: {
bun: "bin/bun",
bunx: "bin/bun",
bun: "bin/bun.exe",
bunx: "bin/bun.exe",
},
os,
cpu,
keywords: ["bun", "bun.js", "node", "node.js", "runtime", "bundler", "transpiler", "typescript"],
homepage: "https://bun.sh",
bugs: "https://github.com/oven-sh/issues",
license: "MIT",
repository: "https://github.com/oven-sh/bun",
});
if (exists(".npmrc")) {
copy(".npmrc", join(cwd, ".npmrc"));
@@ -95,11 +134,17 @@ async function buildModule(
}
const bun = await extractFromZip(asset.browser_download_url, `${bin}/bun`);
const cwd = join("npm", module);
mkdirSync(dirname(join(cwd, exe)), { recursive: true });
write(join(cwd, exe), await bun.async("arraybuffer"));
chmod(join(cwd, exe), 0o755);
writeJson(join(cwd, "package.json"), {
name: module,
version: version,
description: "This is the macOS arm64 binary for Bun, a fast all-in-one JavaScript runtime.",
homepage: "https://bun.sh",
bugs: "https://github.com/oven-sh/issues",
license: "MIT",
repository: "https://github.com/oven-sh/bun",
preferUnplugged: true,
os: [os],
cpu: [arch],
@@ -111,22 +156,33 @@ async function buildModule(
function publishModule(name: string, dryRun?: boolean): void {
log(dryRun ? "Dry-run Publishing:" : "Publishing:", `${name}@${version}`);
const { exitCode, stdout, stderr } = spawn(
"npm",
[
"publish",
"--access",
"public",
"--tag",
version.includes("canary") ? "canary" : "latest",
...(dryRun ? ["--dry-run"] : []),
],
{
cwd: join("npm", name),
},
);
if (exitCode === 0) {
if (!dryRun) {
const { exitCode, stdout, stderr } = spawn(
"npm",
[
"publish",
"--access",
"public",
"--tag",
version.includes("canary") ? "canary" : "latest",
...(dryRun ? ["--dry-run"] : []),
],
{
cwd: join("npm", name),
},
);
error(stderr || stdout);
if (exitCode !== 0) {
throw new Error("npm publish failed with code " + exitCode);
}
} else {
const { exitCode, stdout, stderr } = spawn("npm", ["pack"], {
cwd: join("npm", name),
});
error(stderr || stdout);
if (exitCode !== 0) {
throw new Error("npm pack failed with code " + exitCode);
}
}
}
@@ -162,3 +218,86 @@ function bundle(src: string, dst: string, options: BuildOptions = {}): void {
throw new Error(messages.join("\n"));
}
}
async function test() {
const root = await mkdtemp(join(tmpdir(), "bun-release-test-"));
const $ = new Bun.$.Shell().cwd(root);
for (const platform of platforms) {
if (platform.os !== process.platform) continue;
if (platform.arch !== process.arch) continue;
copy(
join(
import.meta.dir,
"../npm/@oven/",
platform.bin,
"oven-" + platform.bin.replaceAll("/", "-") + `-${version}.tgz`,
),
join(root, `${platform.bin}-${version}.tgz`),
);
}
copy(join(import.meta.dir, "../npm", "bun", "bun-" + version + ".tgz"), join(root, "bun-" + version + ".tgz"));
console.log(root);
for (const [install, exec] of [
["npm i", "npm exec"],
["yarn set version berry; yarn add", "yarn"],
["yarn set version latest; yarn add", "yarn"],
["pnpm i", "pnpm"],
["bun i", "bun run"],
]) {
rmSync(join(root, "node_modules"), { recursive: true, force: true });
rmSync(join(root, "package-lock.json"), { recursive: true, force: true });
rmSync(join(root, "package.json"), { recursive: true, force: true });
rmSync(join(root, "pnpm-lock.yaml"), { recursive: true, force: true });
rmSync(join(root, "yarn.lock"), { recursive: true, force: true });
writeJson(join(root, "package.json"), {
name: "bun-release-test",
});
console.log("Testing", install + " bun");
await $`${{ raw: install }} ./bun-${version}.tgz`;
console.log("Running " + exec + " bun");
// let output = await $`${{
// raw: exec,
// }} bun -- -e "console.log(JSON.stringify([Bun.version, process.platform, process.arch, process.execPath]))"`.text();
const split = exec.split(" ");
let {
stdout: output,
stderr,
exitCode,
} = spawn(
split[0],
[
...split.slice(1),
"--",
"bun",
"-e",
"console.log(JSON.stringify([Bun.version, process.platform, process.arch, process.execPath]))",
],
{
cwd: root,
},
);
if (exitCode !== 0) {
console.error(stderr);
throw new Error("Failed to run " + exec + " bun, exit code: " + exitCode);
}
try {
output = JSON.parse(output);
} catch (e) {
console.log({ output });
throw e;
}
expect(output[0]).toBe(version);
expect(output[1]).toBe(process.platform);
expect(output[2]).toBe(process.arch);
expect(output[3]).toStartWith(root);
expect(output[3]).toInclude("bun");
}
}

View File

@@ -121,16 +121,9 @@ async function downloadBun(platform: Platform, dst: string): Promise<void> {
}
export function optimizeBun(path: string): void {
const installScript =
os === "win32" ? 'powershell -c "irm bun.sh/install.ps1 | iex"' : "curl -fsSL https://bun.sh/install | bash";
const { npm_config_user_agent } = process.env;
if (npm_config_user_agent && /\byarn\//.test(npm_config_user_agent)) {
throw new Error(
`Yarn does not support bun, because it does not allow linking to binaries. To use bun, install using the following command: ${installScript}`,
);
}
const installScript = os === "win32" ? 'powershell -c "irm bun.sh/install.ps1 | iex"' : "curl -fsSL https://bun.sh/install | bash";
try {
rename(path, join(__dirname, "bin", "bun"));
rename(path, join(__dirname, "bin", "bun.exe"));
return;
} catch (error) {
debug("optimizeBun failed", error);

View File

@@ -106,6 +106,15 @@ function isRosetta2(): boolean {
}
function isWindowsAVX2(): boolean {
// TODO: Implement AVX2 detection on Windows
return false;
try {
return (
spawn("powershell", [
"-c",
`(Add-Type -MemberDefinition '[DllImport("kernel32.dll")] public static extern bool IsProcessorFeaturePresent(int ProcessorFeature);' -Name 'Kernel32' -Namespace 'Win32' -PassThru)::IsProcessorFeaturePresent(40);`,
]).stdout == "True"
);
} catch (error) {
debug("isWindowsAVX2 failed", error);
return false;
}
}