From c26f6b2cb71f6a98101b0cde446169c23e2b5c96 Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Sun, 22 Sep 2024 01:50:25 -0700 Subject: [PATCH] Implement `--registry` CLI flag in bun install --- src/install/install.zig | 21 ++++--- test/cli/install/bun-install.test.ts | 93 ++++++++++++++++++++++++---- 2 files changed, 94 insertions(+), 20 deletions(-) diff --git a/src/install/install.zig b/src/install/install.zig index 3b12363ff8d2e..861df28fd86a1 100644 --- a/src/install/install.zig +++ b/src/install/install.zig @@ -7165,9 +7165,7 @@ pub const PackageManager = struct { } if (cli_) |cli| { - if (cli.registry.len > 0 and strings.startsWith(cli.registry, "https://") or - strings.startsWith(cli.registry, "http://")) - { + if (cli.registry.len > 0) { this.scope.url = URL.parse(cli.registry); } @@ -9100,7 +9098,7 @@ pub const PackageManager = struct { clap.parseParam("-g, --global Install globally") catch unreachable, clap.parseParam("--cwd Set a specific cwd") catch unreachable, clap.parseParam("--backend Platform-specific optimizations for installing dependencies. " ++ platform_specific_backend_label) catch unreachable, - clap.parseParam("--link-native-bins ... Link \"bin\" from a matching platform-specific \"optionalDependencies\" instead. Default: esbuild, turbo") catch unreachable, + clap.parseParam("--registry Use a specific registry by default, overriding .npmrc, bunfig.toml and environment variables") catch unreachable, clap.parseParam("--concurrent-scripts Maximum number of concurrent jobs for lifecycle scripts (default 5)") catch unreachable, clap.parseParam("-h, --help Print this help menu") catch unreachable, }; @@ -9171,7 +9169,6 @@ pub const PackageManager = struct { }); pub const CommandLineArguments = struct { - registry: string = "", cache_dir: string = "", lockfile: string = "", token: string = "", @@ -9203,8 +9200,6 @@ pub const PackageManager = struct { pack_destination: string = "", pack_gzip_level: ?string = null, - link_native_bins: []const string = &[_]string{}, - development: bool = false, optional: bool = false, @@ -9217,6 +9212,8 @@ pub const PackageManager = struct { patch: PatchOpts = .{ .nothing = .{} }, + registry: string = "", + const PatchOpts = union(enum) { nothing: struct {}, patch: struct {}, @@ -9586,8 +9583,6 @@ pub const PackageManager = struct { cli.config = opt; } - cli.link_native_bins = args.options("--link-native-bins"); - if (comptime subcommand == .add or subcommand == .install) { cli.development = args.flag("--development") or args.flag("--dev"); cli.optional = args.flag("--optional"); @@ -9636,6 +9631,14 @@ pub const PackageManager = struct { } } + if (args.option("--registry")) |registry| { + if (!strings.hasPrefixComptime(registry, "https://") and !strings.hasPrefixComptime(registry, "http://")) { + Output.errGeneric("Registry URL must start with 'https://' or 'http://': {}\n", .{bun.fmt.quote(registry)}); + Global.crash(); + } + cli.registry = registry; + } + cli.positionals = args.positionals(); if (subcommand == .patch and cli.positionals.len < 2) { diff --git a/test/cli/install/bun-install.test.ts b/test/cli/install/bun-install.test.ts index 072bcb22eea71..ef179f327ac17 100644 --- a/test/cli/install/bun-install.test.ts +++ b/test/cli/install/bun-install.test.ts @@ -1,5 +1,16 @@ import { file, listen, Socket, spawn } from "bun"; -import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, setDefaultTimeout, test } from "bun:test"; +import { + jest, + afterAll, + afterEach, + beforeAll, + beforeEach, + describe, + expect, + it, + setDefaultTimeout, + test, +} from "bun:test"; import { access, mkdir, readlink, rm, writeFile } from "fs/promises"; import { bunEnv, bunExe, bunEnv as env, tempDirWithFiles, toBeValidBin, toBeWorkspaceLink, toHaveBins } from "harness"; import { join, sep } from "path"; @@ -81,7 +92,7 @@ it("should not error when package.json has comments and trailing commas", async expect(requested).toBe(1); try { await access(join(package_dir, "bun.lockb")); - expect(() => {}).toThrow(); + expect.unreachable(); } catch (err: any) { expect(err.code).toBe("ENOENT"); } @@ -226,10 +237,70 @@ registry = "http://${server.hostname}:${server.port}/" expect(await exited).toBe(1); try { await access(join(package_dir, "bun.lockb")); - expect(() => {}).toThrow(); + expect.unreachable(); + } catch (err: any) { + expect(err.code).toBe("ENOENT"); + } +}); + +it("should support --registry CLI flag", async () => { + const connected = jest.fn(); + function end(socket: Socket) { + connected(); + socket.end(); + } + const server = listen({ + socket: { + data: function data(socket) { + end(socket); + }, + drain: function drain(socket) { + end(socket); + }, + open: function open(socket) { + end(socket); + }, + }, + hostname: "localhost", + port: 0, + }); + await writeFile( + join(package_dir, "bunfig.toml"), + ` +[install] +cache = false +registry = "https://badssl.com:bad" +`, + ); + await writeFile( + join(package_dir, "package.json"), + JSON.stringify({ + name: "foo", + version: "0.0.1", + dependencies: { + bar: "0.0.2", + }, + }), + ); + const { stdout, stderr, exited } = spawn({ + cmd: [bunExe(), "install", "--registry", `http://${server.hostname}:${server.port}/`], + cwd: package_dir, + stdout: "pipe", + stdin: "pipe", + stderr: "pipe", + env, + }); + const err = await new Response(stderr).text(); + expect(err).toMatch(/error: (ConnectionRefused|ConnectionClosed) downloading package manifest bar/gm); + expect(await new Response(stdout).text()).toBeEmpty(); + expect(await exited).toBe(1); + try { + await access(join(package_dir, "bun.lockb")); + expect.unreachable(); } catch (err: any) { expect(err.code).toBe("ENOENT"); } + expect(connected).toHaveBeenCalled(); }); it("should work when moving workspace packages", async () => { @@ -410,7 +481,7 @@ it("should handle missing package", async () => { expect(requested).toBe(1); try { await access(join(package_dir, "bun.lockb")); - expect(() => {}).toThrow(); + expect.unreachable(); } catch (err: any) { expect(err.code).toBe("ENOENT"); } @@ -461,7 +532,7 @@ foo = { token = "bar" } expect(requested).toBe(1); try { await access(join(package_dir, "bun.lockb")); - expect(() => {}).toThrow(); + expect.unreachable(); } catch (err: any) { expect(err.code).toBe("ENOENT"); } @@ -1551,7 +1622,7 @@ it("should handle ^1 in dependencies", async () => { expect(requested).toBe(1); try { await access(join(package_dir, "bun.lockb")); - expect(() => {}).toThrow(); + expect.unreachable(); } catch (err: any) { expect(err.code).toBe("ENOENT"); } @@ -1628,7 +1699,7 @@ it("should handle ^0.1 in dependencies", async () => { expect(requested).toBe(1); try { await access(join(package_dir, "bun.lockb")); - expect(() => {}).toThrow(); + expect.unreachable(); } catch (err: any) { expect(err.code).toBe("ENOENT"); } @@ -1663,7 +1734,7 @@ it("should handle ^0.0.0 in dependencies", async () => { expect(requested).toBe(1); try { await access(join(package_dir, "bun.lockb")); - expect(() => {}).toThrow(); + expect.unreachable(); } catch (err: any) { expect(err.code).toBe("ENOENT"); } @@ -4512,7 +4583,7 @@ it("should fail on invalid Git URL", async () => { expect(requested).toBe(0); try { await access(join(package_dir, "bun.lockb")); - expect(() => {}).toThrow(); + expect.unreachable(); } catch (err: any) { expect(err.code).toBe("ENOENT"); } @@ -4548,7 +4619,7 @@ it("should fail on ssh Git URL if invalid credentials", async () => { expect(requested).toBe(0); try { await access(join(package_dir, "bun.lockb")); - expect(() => {}).toThrow(); + expect.unreachable(); } catch (err: any) { expect(err.code).toBe("ENOENT"); } @@ -4586,7 +4657,7 @@ it("should fail on Git URL with invalid committish", async () => { expect(requested).toBe(0); try { await access(join(package_dir, "bun.lockb")); - expect(() => {}).toThrow(); + expect.unreachable(); } catch (err: any) { expect(err.code).toBe("ENOENT"); }