Compare commits

...

9 Commits

Author SHA1 Message Date
Dylan Conway
62e73d6699 Merge branch 'main' into dylan/scripts-in-foreground 2024-03-29 21:00:02 -07:00
Dylan Conway
7ec2427dbf Merge branch 'main' into dylan/scripts-in-foreground 2024-03-29 15:25:44 -07:00
Dylan Conway
b8882454f1 boolean variable, comptime, 1_1_0 2024-03-29 15:19:37 -07:00
autofix-ci[bot]
9a93a1f3b8 [autofix.ci] apply automated fixes 2024-03-29 22:04:25 +00:00
Dylan Conway
a0cc23047a Merge branch 'main' into dylan/scripts-in-foreground 2024-03-29 15:01:45 -07:00
Dylan Conway
f182f3d74a move back to installPackages 2024-03-25 14:23:46 -07:00
Dylan Conway
a44784615b test for breaking changes 2024-03-25 13:38:58 -07:00
Dylan Conway
383f4bfa3e ignore if silent 2024-03-24 15:42:59 -07:00
Dylan Conway
3b4f9ae958 root scripts in foreground 2024-03-24 15:42:59 -07:00
5 changed files with 365 additions and 201 deletions

View File

@@ -348,8 +348,9 @@ pub const TrustCommand = struct {
pm.sleep();
}
const output_in_foreground = false;
switch (pm.options.log_level) {
inline else => |log_level| try pm.spawnPackageLifecycleScripts(ctx, info.scripts_list, log_level),
inline else => |log_level| try pm.spawnPackageLifecycleScripts(ctx, info.scripts_list, log_level, output_in_foreground),
}
if (pm.options.log_level.showProgress()) {

View File

@@ -6435,10 +6435,15 @@ pub const PackageManager = struct {
.npm => brk: {
if (comptime FeatureFlags.breaking_changes_1_1_0) {
if (request.version.tag == .dist_tag) {
const fmt = if (options.exact_versions) "{}" else "^{}";
break :brk try std.fmt.allocPrint(allocator, fmt, .{
request.resolution.value.npm.version.fmt(request.version_buf),
});
if (options.exact_versions) {
break :brk try std.fmt.allocPrint(allocator, "{}", .{
request.resolution.value.npm.version.fmt(request.version_buf),
});
} else {
break :brk try std.fmt.allocPrint(allocator, "^{}", .{
request.resolution.value.npm.version.fmt(request.version_buf),
});
}
}
break :brk null;
} else {
@@ -8269,7 +8274,8 @@ pub const PackageManager = struct {
const tree_id = entry.tree_id;
if (this.canRunScripts(tree_id)) {
_ = this.pending_lifecycle_scripts.swapRemove(i);
this.manager.spawnPackageLifecycleScripts(this.command_ctx, entry.list, log_level) catch |err| {
const output_in_foreground = false;
this.manager.spawnPackageLifecycleScripts(this.command_ctx, entry.list, log_level, output_in_foreground) catch |err| {
if (comptime log_level != .silent) {
const fmt = "\n<r><red>error:<r> failed to spawn life-cycle scripts for <b>{s}<r>: {s}\n";
const args = .{ name, @errorName(err) };
@@ -8307,7 +8313,8 @@ pub const PackageManager = struct {
PackageManager.instance.sleep();
}
this.manager.spawnPackageLifecycleScripts(this.command_ctx, entry.list, log_level) catch |err| {
const output_in_foreground = false;
this.manager.spawnPackageLifecycleScripts(this.command_ctx, entry.list, log_level, output_in_foreground) catch |err| {
if (comptime log_level != .silent) {
const fmt = "\n<r><red>error:<r> failed to spawn life-cycle scripts for <b>{s}<r>: {s}\n";
const args = .{ package_name, @errorName(err) };
@@ -9190,23 +9197,6 @@ pub const PackageManager = struct {
);
}
const root_lifecycle_scripts_count = brk: {
if (this.options.do.run_scripts and
this.options.do.install_packages and
this.root_lifecycle_scripts != null)
{
var counter: usize = 0;
for (this.root_lifecycle_scripts.?.items) |item| {
if (item != null) counter += 1;
}
this.total_scripts += counter;
break :brk counter;
}
break :brk 0;
};
var root_node: *Progress.Node = undefined;
var download_node: Progress.Node = undefined;
var install_node: Progress.Node = undefined;
@@ -9220,7 +9210,7 @@ pub const PackageManager = struct {
download_node = root_node.start(ProgressStrings.download(), 0);
install_node = root_node.start(ProgressStrings.install(), this.lockfile.packages.len);
scripts_node = root_node.start(ProgressStrings.script(), root_lifecycle_scripts_count);
scripts_node = root_node.start(ProgressStrings.script(), 0);
this.downloads_node = &download_node;
this.scripts_node = &scripts_node;
}
@@ -9515,10 +9505,14 @@ pub const PackageManager = struct {
installer.completeRemainingScripts(log_level);
if (root_lifecycle_scripts_count > 0) {
// root lifecycle scripts can run now that all dependencies are installed
// and their lifecycle script have finished
try this.spawnPackageLifecycleScripts(ctx, this.root_lifecycle_scripts.?, log_level);
if (comptime !FeatureFlags.breaking_changes_1_1_0) {
if (this.root_lifecycle_scripts) |scripts| {
if (comptime log_level.showProgress()) {
scripts_node.setEstimatedTotalItems(scripts_node.unprotected_completed_items + scripts.total);
}
const output_in_foreground = false;
try this.spawnPackageLifecycleScripts(ctx, scripts, log_level, output_in_foreground);
}
}
while (this.pending_lifecycle_script_tasks.load(.Monotonic) > 0) {
@@ -9526,7 +9520,7 @@ pub const PackageManager = struct {
if (PackageManager.hasEnoughTimePassedBetweenWaitingMessages()) Output.prettyErrorln("<d>[PackageManager]<r> waiting for {d} scripts\n", .{this.pending_lifecycle_script_tasks.load(.Monotonic)});
}
PackageManager.instance.sleep();
this.sleep();
}
if (comptime log_level.showProgress()) {
@@ -10166,6 +10160,33 @@ pub const PackageManager = struct {
}
}
if (comptime FeatureFlags.breaking_changes_1_1_0) {
if (manager.options.do.run_scripts) {
if (manager.root_lifecycle_scripts) |scripts| {
if (comptime Environment.allow_assert) {
std.debug.assert(scripts.total > 0);
}
if (comptime log_level != .silent) {
Output.printError("\n", .{});
Output.flush();
}
// root lifecycle scripts can run now that all dependencies are installed, dependency scripts
// have finished, and lockfiles have been saved
const output_in_foreground = true;
try manager.spawnPackageLifecycleScripts(ctx, scripts, log_level, output_in_foreground);
while (manager.pending_lifecycle_script_tasks.load(.Monotonic) > 0) {
if (PackageManager.verbose_install) {
if (PackageManager.hasEnoughTimePassedBetweenWaitingMessages()) Output.prettyErrorln("<d>[PackageManager]<r> waiting for {d} scripts\n", .{manager.pending_lifecycle_script_tasks.load(.Monotonic)});
}
manager.sleep();
}
}
}
}
var printed_timestamp = false;
if (comptime log_level != .silent) {
if (manager.options.do.summary) {
@@ -10286,6 +10307,7 @@ pub const PackageManager = struct {
ctx: Command.Context,
list: Lockfile.Package.Scripts.List,
comptime log_level: PackageManager.Options.LogLevel,
comptime foreground: bool,
) !void {
var any_scripts = false;
for (list.items) |maybe_item| {
@@ -10333,7 +10355,7 @@ pub const PackageManager = struct {
try this_bundler.env.map.put("PATH", original_path);
PATH.deinit();
try LifecycleScriptSubprocess.spawnPackageScripts(this, list, envp, log_level);
try LifecycleScriptSubprocess.spawnPackageScripts(this, list, envp, log_level, foreground);
}
};

View File

@@ -31,6 +31,8 @@ pub const LifecycleScriptSubprocess = struct {
has_incremented_alive_count: bool = false,
foreground: bool = false,
pub usingnamespace bun.New(@This());
pub const min_milliseconds_to_log = 500;
@@ -109,19 +111,6 @@ pub const LifecycleScriptSubprocess = struct {
this.stdout.setParent(this);
this.stderr.setParent(this);
if (manager.scripts_node) |scripts_node| {
manager.setNodeName(
scripts_node,
this.package_name,
PackageManager.ProgressStrings.script_emoji,
true,
);
if (manager.finished_installing.load(.Monotonic)) {
scripts_node.activate();
manager.progress.refresh();
}
}
this.current_script_index = next_script_index;
this.has_called_process_exit = false;
@@ -134,6 +123,22 @@ pub const LifecycleScriptSubprocess = struct {
const combined_script: [:0]u8 = copy_script.items[0 .. copy_script.items.len - 1 :0];
if (this.foreground and this.manager.options.log_level != .silent) {
Output.prettyError("<r><d><magenta>$<r> <d><b>{s}<r>\n", .{combined_script});
Output.flush();
} else if (manager.scripts_node) |scripts_node| {
manager.setNodeName(
scripts_node,
this.package_name,
PackageManager.ProgressStrings.script_emoji,
true,
);
if (manager.finished_installing.load(.Monotonic)) {
scripts_node.activate();
manager.progress.refresh();
}
}
log("{s} - {s} $ {s}", .{ this.package_name, this.scriptName(), combined_script });
var argv = [_]?[*:0]const u8{
@@ -148,7 +153,9 @@ pub const LifecycleScriptSubprocess = struct {
}
const spawn_options = bun.spawn.SpawnOptions{
.stdin = .ignore,
.stdout = if (this.manager.options.log_level.isVerbose())
.stdout = if (this.manager.options.log_level == .silent)
.ignore
else if (this.manager.options.log_level.isVerbose() or this.foreground)
.inherit
else if (Environment.isPosix)
.buffer
@@ -156,7 +163,9 @@ pub const LifecycleScriptSubprocess = struct {
.{
.buffer = this.stdout.source.?.pipe,
},
.stderr = if (this.manager.options.log_level.isVerbose())
.stderr = if (this.manager.options.log_level == .silent)
.ignore
else if (this.manager.options.log_level.isVerbose() or this.foreground)
.inherit
else if (Environment.isPosix)
.buffer
@@ -286,11 +295,11 @@ pub const LifecycleScriptSubprocess = struct {
Global.exit(exit.code);
}
if (this.manager.scripts_node) |scripts_node| {
if (!this.foreground and this.manager.scripts_node != null) {
if (this.manager.finished_installing.load(.Monotonic)) {
scripts_node.completeOne();
this.manager.scripts_node.?.completeOne();
} else {
_ = @atomicRmw(usize, &scripts_node.unprotected_completed_items, .Add, 1, .Monotonic);
_ = @atomicRmw(usize, &this.manager.scripts_node.?.unprotected_completed_items, .Add, 1, .Monotonic);
}
}
@@ -401,12 +410,14 @@ pub const LifecycleScriptSubprocess = struct {
list: Lockfile.Package.Scripts.List,
envp: [:null]?[*:0]u8,
comptime log_level: PackageManager.Options.LogLevel,
comptime foreground: bool,
) !void {
var lifecycle_subprocess = LifecycleScriptSubprocess.new(.{
.manager = manager,
.envp = envp,
.scripts = list,
.package_name = list.package_name,
.foreground = foreground,
});
if (comptime log_level.isVerbose()) {

View File

@@ -1,5 +1,13 @@
import { file, spawn } from "bun";
import { bunEnv, bunExe, bunEnv as env, isWindows, mergeWindowEnvs, toBeValidBin, toHaveBins } from "harness";
import {
bunExe,
bunEnv as env,
isWindows,
mergeWindowEnvs,
toBeValidBin,
toHaveBins,
breakingChanges_1_1_0,
} from "harness";
import { join } from "path";
import { mkdtempSync, realpathSync, copyFileSync, mkdirSync } from "fs";
import { rm, writeFile, mkdir, exists, cp } from "fs/promises";
@@ -7,7 +15,6 @@ import { readdirSorted } from "../dummy.registry";
import { tmpdir } from "os";
import { fork, ChildProcess } from "child_process";
import { beforeAll, afterAll, beforeEach, afterEach, test, expect, describe } from "bun:test";
import { f } from "js/bun/http/js-sink-sourmap-fixture/index.mjs";
expect.extend({
toBeValidBin,
@@ -3782,94 +3789,103 @@ for (const forceWaiterThread of [false, true]) {
expect(await exited).toBe(0);
});
test.todo("default trusted dependencies should not be used of trustedDependencies is populated", async () => {
await writeFile(
join(packageDir, "package.json"),
JSON.stringify({
name: "foo",
version: "1.2.3",
dependencies: {
"uses-what-bin": "1.0.0",
// fake electron package because it's in the default trustedDependencies list
"electron": "1.0.0",
},
}),
);
test.if(breakingChanges_1_1_0)(
"default trusted dependencies should not be used of trustedDependencies is populated",
async () => {
await writeFile(
join(packageDir, "package.json"),
JSON.stringify({
name: "foo",
version: "1.2.3",
dependencies: {
"uses-what-bin": "1.0.0",
// fake electron package because it's in the default trustedDependencies list
"electron": "1.0.0",
},
}),
);
var { stdout, stderr, exited } = spawn({
cmd: [bunExe(), "install"],
cwd: packageDir,
stdout: "pipe",
stdin: "pipe",
stderr: "pipe",
env: testEnv,
});
var { stdout, stderr, exited } = spawn({
cmd: [bunExe(), "install"],
cwd: packageDir,
stdout: "pipe",
stdin: "pipe",
stderr: "pipe",
env: testEnv,
});
// electron lifecycle scripts should run, uses-what-bin scripts should not run
var err = await new Response(stderr).text();
expect(err).toContain("Saved lockfile");
expect(err).not.toContain("not found");
expect(err).not.toContain("error:");
expect(err).not.toContain("panic:");
var out = await new Response(stdout).text();
expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([
"",
" + electron@1.0.0",
" + uses-what-bin@1.0.0",
"",
expect.stringContaining("3 packages installed"),
]);
expect(await exited).toBe(0);
expect(await exists(join(packageDir, "node_modules", "uses-what-bin", "what-bin.txt"))).toBeFalse();
expect(await exists(join(packageDir, "node_modules", "electron", "preinstall.txt"))).toBeTrue();
// electron lifecycle scripts should run, uses-what-bin scripts should not run
var err = await new Response(stderr).text();
expect(err).toContain("Saved lockfile");
expect(err).not.toContain("not found");
expect(err).not.toContain("error:");
expect(err).not.toContain("panic:");
var out = await new Response(stdout).text();
expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([
"",
" + electron@1.0.0",
" + uses-what-bin@1.0.0",
"",
expect.stringContaining("3 packages installed"),
"",
" Blocked 1 postinstall. Run `bun pm untrusted` for details.",
"",
]);
expect(await exited).toBe(0);
expect(await exists(join(packageDir, "node_modules", "uses-what-bin", "what-bin.txt"))).toBeFalse();
expect(await exists(join(packageDir, "node_modules", "electron", "preinstall.txt"))).toBeTrue();
await rm(join(packageDir, "node_modules"), { recursive: true, force: true });
await rm(join(packageDir, "bun.lockb"));
await rm(join(packageDir, "node_modules"), { recursive: true, force: true });
await rm(join(packageDir, "bun.lockb"));
await writeFile(
join(packageDir, "package.json"),
JSON.stringify({
name: "foo",
version: "1.2.3",
dependencies: {
"uses-what-bin": "1.0.0",
"electron": "1.0.0",
},
trustedDependencies: ["uses-what-bin"],
}),
);
await writeFile(
join(packageDir, "package.json"),
JSON.stringify({
name: "foo",
version: "1.2.3",
dependencies: {
"uses-what-bin": "1.0.0",
"electron": "1.0.0",
},
trustedDependencies: ["uses-what-bin"],
}),
);
// now uses-what-bin scripts should run and electron scripts should not run.
// now uses-what-bin scripts should run and electron scripts should not run.
({ stdout, stderr, exited } = spawn({
cmd: [bunExe(), "install"],
cwd: packageDir,
stdout: "pipe",
stdin: "pipe",
stderr: "pipe",
env: testEnv,
}));
({ stdout, stderr, exited } = spawn({
cmd: [bunExe(), "install"],
cwd: packageDir,
stdout: "pipe",
stdin: "pipe",
stderr: "pipe",
env: testEnv,
}));
err = await Bun.readableStreamToText(stderr);
expect(err).toContain("Saved lockfile");
expect(err).not.toContain("not found");
expect(err).not.toContain("error:");
expect(err).not.toContain("panic:");
out = await Bun.readableStreamToText(stdout);
expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([
"",
" + electron@1.0.0",
" + uses-what-bin@1.0.0",
"",
expect.stringContaining("3 packages installed"),
]);
expect(await exited).toBe(0);
err = await Bun.readableStreamToText(stderr);
expect(err).toContain("Saved lockfile");
expect(err).not.toContain("not found");
expect(err).not.toContain("error:");
expect(err).not.toContain("panic:");
out = await Bun.readableStreamToText(stdout);
expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([
"",
" + electron@1.0.0",
" + uses-what-bin@1.0.0",
"",
expect.stringContaining("3 packages installed"),
"",
" Blocked 1 postinstall. Run `bun pm untrusted` for details.",
"",
]);
expect(await exited).toBe(0);
expect(await exists(join(packageDir, "node_modules", "uses-what-bin", "what-bin.txt"))).toBeTrue();
expect(await exists(join(packageDir, "node_modules", "electron", "preinstall.txt"))).toBeFalse();
});
expect(await exists(join(packageDir, "node_modules", "uses-what-bin", "what-bin.txt"))).toBeTrue();
expect(await exists(join(packageDir, "node_modules", "electron", "preinstall.txt"))).toBeFalse();
},
);
test.todo("does not run any scripts if trustedDependencies is an empty list", async () => {
test.if(breakingChanges_1_1_0)("does not run any scripts if trustedDependencies is an empty list", async () => {
await writeFile(
join(packageDir, "package.json"),
JSON.stringify({
@@ -3904,86 +3920,95 @@ for (const forceWaiterThread of [false, true]) {
" + uses-what-bin@1.0.0",
"",
expect.stringContaining("3 packages installed"),
"",
" Blocked 2 postinstalls. Run `bun pm untrusted` for details.",
"",
]);
expect(await exited).toBe(0);
expect(await exists(join(packageDir, "node_modules", "uses-what-bin", "what-bin.txt"))).toBeFalse();
expect(await exists(join(packageDir, "node_modules", "electron", "preinstall.txt"))).toBeFalse();
});
test.todo("will run default trustedDependencies after install that didn't include them", async () => {
await writeFile(
join(packageDir, "package.json"),
JSON.stringify({
name: "foo",
version: "1.2.3",
dependencies: {
electron: "1.0.0",
},
trustedDependencies: ["blah"],
}),
);
test.if(breakingChanges_1_1_0)(
"will run default trustedDependencies after install that didn't include them",
async () => {
await writeFile(
join(packageDir, "package.json"),
JSON.stringify({
name: "foo",
version: "1.2.3",
dependencies: {
electron: "1.0.0",
},
trustedDependencies: ["blah"],
}),
);
// first install does not run electron scripts
// first install does not run electron scripts
var { stdout, stderr, exited } = spawn({
cmd: [bunExe(), "install"],
cwd: packageDir,
stdout: "pipe",
stdin: "pipe",
stderr: "pipe",
env: testEnv,
});
var { stdout, stderr, exited } = spawn({
cmd: [bunExe(), "install"],
cwd: packageDir,
stdout: "pipe",
stdin: "pipe",
stderr: "pipe",
env: testEnv,
});
var err = await Bun.readableStreamToText(stderr);
expect(err).toContain("Saved lockfile");
expect(err).not.toContain("not found");
expect(err).not.toContain("error:");
expect(err).not.toContain("panic:");
var out = await Bun.readableStreamToText(stdout);
expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([
"",
" + electron@1.0.0",
"",
expect.stringContaining("1 package installed"),
]);
expect(await exited).toBe(0);
expect(await exists(join(packageDir, "node_modules", "electron", "preinstall.txt"))).toBeFalse();
var err = await Bun.readableStreamToText(stderr);
expect(err).toContain("Saved lockfile");
expect(err).not.toContain("not found");
expect(err).not.toContain("error:");
expect(err).not.toContain("panic:");
var out = await Bun.readableStreamToText(stdout);
expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([
"",
" + electron@1.0.0",
"",
expect.stringContaining("1 package installed"),
"",
" Blocked 1 postinstall. Run `bun pm untrusted` for details.",
"",
]);
expect(await exited).toBe(0);
expect(await exists(join(packageDir, "node_modules", "electron", "preinstall.txt"))).toBeFalse();
await writeFile(
join(packageDir, "package.json"),
JSON.stringify({
name: "foo",
version: "1.2.3",
dependencies: {
electron: "1.0.0",
},
}),
);
await writeFile(
join(packageDir, "package.json"),
JSON.stringify({
name: "foo",
version: "1.2.3",
dependencies: {
electron: "1.0.0",
},
}),
);
// The electron scripts should run now because it's in default trusted dependencies.
// The electron scripts should run now because it's in default trusted dependencies.
({ stdout, stderr, exited } = spawn({
cmd: [bunExe(), "install"],
cwd: packageDir,
stdout: "pipe",
stdin: "pipe",
stderr: "pipe",
env: testEnv,
}));
({ stdout, stderr, exited } = spawn({
cmd: [bunExe(), "install"],
cwd: packageDir,
stdout: "pipe",
stdin: "pipe",
stderr: "pipe",
env: testEnv,
}));
err = await Bun.readableStreamToText(stderr);
expect(err).toContain("Saved lockfile");
expect(err).not.toContain("not found");
expect(err).not.toContain("error:");
expect(err).not.toContain("panic:");
out = await Bun.readableStreamToText(stdout);
expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([
"",
"Checked 1 install across 2 packages (no changes)",
]);
expect(await exited).toBe(0);
expect(await exists(join(packageDir, "node_modules", "electron", "preinstall.txt"))).toBeTrue();
});
err = await Bun.readableStreamToText(stderr);
expect(err).toContain("Saved lockfile");
expect(err).not.toContain("not found");
expect(err).not.toContain("error:");
expect(err).not.toContain("panic:");
out = await Bun.readableStreamToText(stdout);
expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([
"",
"Checked 1 install across 2 packages (no changes)",
]);
expect(await exited).toBe(0);
expect(await exists(join(packageDir, "node_modules", "electron", "preinstall.txt"))).toBeTrue();
},
);
describe("--trust", async () => {
const trustTests = [
@@ -4814,6 +4839,109 @@ for (const forceWaiterThread of [false, true]) {
});
});
});
describe.if(breakingChanges_1_1_0)("stdout/stderr is inherited from root scripts during install", async () => {
test("without packages", async () => {
const exe = bunExe().replace(/\\/g, "\\\\");
await writeFile(
join(packageDir, "package.json"),
JSON.stringify({
name: "foo",
version: "1.2.3",
scripts: {
"preinstall": `${exe} -e 'process.stderr.write("preinstall stderr 🍦\\n")'`,
"install": `${exe} -e 'process.stdout.write("install stdout 🚀\\n")'`,
"prepare": `${exe} -e 'Bun.sleepSync(200); process.stdout.write("prepare stdout done ✅\\n")'`,
},
}),
);
const { stdout, stderr, exited } = spawn({
cmd: [bunExe(), "install"],
cwd: packageDir,
stdout: "pipe",
stderr: "pipe",
env: testEnv,
});
const err = await Bun.readableStreamToText(stderr);
expect(err).not.toContain("error:");
expect(err).not.toContain("warn:");
expect(err).not.toContain("panic:");
expect(err.split(/\r?\n/)).toEqual([
expect.stringContaining("bun install"),
"No packages! Deleted empty lockfile",
"",
`$ ${exe} -e 'process.stderr.write("preinstall stderr 🍦\\n")'`,
"preinstall stderr 🍦",
`$ ${exe} -e 'process.stdout.write("install stdout 🚀\\n")'`,
`$ ${exe} -e 'Bun.sleepSync(200); process.stdout.write("prepare stdout done ✅\\n")'`,
"",
]);
const out = await Bun.readableStreamToText(stdout);
expect(out.split(/\r?\n/)).toEqual([
"install stdout 🚀",
"prepare stdout done ✅",
"",
expect.stringContaining("done"),
"",
]);
expect(await exited).toBe(0);
});
test("with a package", async () => {
const exe = bunExe().replace(/\\/g, "\\\\");
await writeFile(
join(packageDir, "package.json"),
JSON.stringify({
name: "foo",
version: "1.2.3",
scripts: {
"preinstall": `${exe} -e 'process.stderr.write("preinstall stderr 🍦\\n")'`,
"install": `${exe} -e 'process.stdout.write("install stdout 🚀\\n")'`,
"prepare": `${exe} -e 'Bun.sleepSync(200); process.stdout.write("prepare stdout done ✅\\n")'`,
},
dependencies: {
"no-deps": "1.0.0",
},
}),
);
const { stdout, stderr, exited } = spawn({
cmd: [bunExe(), "install"],
cwd: packageDir,
stdout: "pipe",
stderr: "pipe",
env: testEnv,
});
const err = await Bun.readableStreamToText(stderr);
expect(err).not.toContain("error:");
expect(err).not.toContain("warn:");
expect(err).not.toContain("panic:");
expect(err.split(/\r?\n/)).toEqual([
expect.stringContaining("bun install"),
" Resolving dependencies",
expect.stringContaining(" Resolved, downloaded and extracted "),
" Saved lockfile",
"",
`$ ${exe} -e 'process.stderr.write("preinstall stderr 🍦\\n")'`,
"preinstall stderr 🍦",
`$ ${exe} -e 'process.stdout.write("install stdout 🚀\\n")'`,
`$ ${exe} -e 'Bun.sleepSync(200); process.stdout.write("prepare stdout done ✅\\n")'`,
"",
]);
const out = await Bun.readableStreamToText(stdout);
expect(out.split(/\r?\n/)).toEqual([
"install stdout 🚀",
"prepare stdout done ✅",
"",
" + no-deps@1.0.0",
"",
expect.stringContaining(" 1 package installed"),
"",
]);
expect(await exited).toBe(0);
});
});
}
describe("pm trust", async () => {
@@ -6736,7 +6864,7 @@ test.if(isWindows)(
stdout: "pipe",
stdin: "pipe",
stderr: "pipe",
env: mergeWindowEnvs([bunEnv, { PATH: PATH }]),
env: mergeWindowEnvs([env, { PATH: PATH }]),
});
expect(stderr).toBeDefined();
const err = await new Response(stderr).text();
@@ -6754,7 +6882,7 @@ test.if(isWindows)(
stdout: "pipe",
stdin: "pipe",
stderr: "pipe",
env: mergeWindowEnvs([bunEnv, { PATH: PATH }]),
env: mergeWindowEnvs([env, { PATH: PATH }]),
});
expect(stderr).toBeDefined();
const err = await new Response(stderr).text();
@@ -6772,7 +6900,7 @@ test.if(isWindows)(
stdout: "pipe",
stdin: "pipe",
stderr: "pipe",
env: mergeWindowEnvs([bunEnv, { PATH: PATH }]),
env: mergeWindowEnvs([env, { PATH: PATH }]),
});
expect(stderr).toBeDefined();
const err = await new Response(stderr).text();
@@ -6790,7 +6918,7 @@ test.if(isWindows)(
stdout: "pipe",
stdin: "pipe",
stderr: "pipe",
env: mergeWindowEnvs([bunEnv, { PATH: PATH }]),
env: mergeWindowEnvs([env, { PATH: PATH }]),
});
expect(stderr).toBeDefined();
const err = await new Response(stderr).text();

View File

@@ -10,6 +10,8 @@ export const isPosix = isMacOS || isLinux;
export const isWindows = process.platform === "win32";
export const isIntelMacOS = isMacOS && process.arch === "x64";
export const breakingChanges_1_1_0 = false;
export const bunEnv: NodeJS.ProcessEnv = {
...process.env,
GITHUB_ACTIONS: "false",