Skip to content

Commit

Permalink
add bun pm whoami (#14387)
Browse files Browse the repository at this point in the history
  • Loading branch information
dylan-conway authored Oct 8, 2024
1 parent fc85a2d commit c071415
Show file tree
Hide file tree
Showing 7 changed files with 392 additions and 71 deletions.
8 changes: 8 additions & 0 deletions docs/cli/pm.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,14 @@ $ bun pm ls --all
├── ...
```

## whoami

Print your npm username. Requires you to be logged in (`bunx npm login`) with credentials in either `bunfig.toml` or `.npmrc`:

```bash
$ bun pm whoami
```

## hash

To generate and print the hash of the current lockfile:
Expand Down
19 changes: 19 additions & 0 deletions src/cli/package_manager_command.zig
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const TrustCommand = @import("./pm_trusted_command.zig").TrustCommand;
const DefaultTrustedCommand = @import("./pm_trusted_command.zig").DefaultTrustedCommand;
const Environment = bun.Environment;
pub const PackCommand = @import("./pack_command.zig").PackCommand;
const Npm = Install.Npm;

const ByName = struct {
dependencies: []const Dependency,
Expand Down Expand Up @@ -109,6 +110,7 @@ pub const PackageManagerCommand = struct {
\\ <d>└<r> <cyan>-g<r> print the <b>global<r> path to bin folder
\\ bun pm <b>ls<r> list the dependency tree according to the current lockfile
\\ <d>└<r> <cyan>--all<r> list the entire dependency tree according to the current lockfile
\\ bun pm <b>whoami<r> print the current npm username
\\ bun pm <b>hash<r> generate & print the hash of the current lockfile
\\ bun pm <b>hash-string<r> print the string used to hash the lockfile
\\ bun pm <b>hash-print<r> print the hash stored in the current lockfile
Expand Down Expand Up @@ -152,6 +154,23 @@ pub const PackageManagerCommand = struct {
if (strings.eqlComptime(subcommand, "pack")) {
try PackCommand.execWithManager(ctx, pm);
Global.exit(0);
} else if (strings.eqlComptime(subcommand, "whoami")) {
const username = Npm.whoami(ctx.allocator, pm) catch |err| {
switch (err) {
error.OutOfMemory => bun.outOfMemory(),
error.NeedAuth => {
Output.errGeneric("missing authentication (run <cyan>`bunx npm login`<r>)", .{});
},
error.ProbablyInvalidAuth => {
Output.errGeneric("failed to authenticate with registry '{}'", .{
bun.fmt.redactedNpmUrl(pm.options.scope.url.href),
});
},
}
Global.crash();
};
Output.println("{s}", .{username});
Global.exit(0);
} else if (strings.eqlComptime(subcommand, "bin")) {
const output_path = Path.joinAbs(Fs.FileSystem.instance.top_level_dir, .auto, bun.asByteSlice(pm.options.bin_path));
Output.prettyln("{s}", .{output_path});
Expand Down
81 changes: 24 additions & 57 deletions src/cli/publish_command.zig
Original file line number Diff line number Diff line change
Expand Up @@ -586,7 +586,14 @@ pub const PublishCommand = struct {
if (!prompt_for_otp) {
// general error
const otp_response = false;
return handleResponseErrors(directory_publish, ctx, &req, &res, &response_buf, otp_response);
try Npm.responseError(
ctx.allocator,
&req,
&res,
.{ ctx.package_name, ctx.package_version },
&response_buf,
otp_response,
);
}

// https://github.com/npm/cli/blob/534ad7789e5c61f579f44d782bdd18ea3ff1ee20/node_modules/npm-registry-fetch/lib/check-response.js#L14
Expand Down Expand Up @@ -637,7 +644,14 @@ pub const PublishCommand = struct {
switch (otp_res.status_code) {
400...std.math.maxInt(@TypeOf(otp_res.status_code)) => {
const otp_response = true;
return handleResponseErrors(directory_publish, ctx, &otp_req, &otp_res, &response_buf, otp_response);
try Npm.responseError(
ctx.allocator,
&otp_req,
&otp_res,
.{ ctx.package_name, ctx.package_version },
&response_buf,
otp_response,
);
},
else => {
// https://github.com/npm/cli/blob/534ad7789e5c61f579f44d782bdd18ea3ff1ee20/node_modules/npm-registry-fetch/lib/check-response.js#L14
Expand All @@ -654,60 +668,6 @@ pub const PublishCommand = struct {
}
}

fn handleResponseErrors(
comptime directory_publish: bool,
ctx: *const Context(directory_publish),
req: *const http.AsyncHTTP,
res: *const bun.picohttp.Response,
response_body: *MutableString,
comptime otp_response: bool,
) OOM!void {
const message = message: {
const source = logger.Source.initPathString("???", response_body.list.items);
const json = JSON.parseUTF8(&source, ctx.manager.log, ctx.allocator) catch |err| {
switch (err) {
error.OutOfMemory => |oom| return oom,
else => break :message null,
}
};

// I don't think we should make this check, I cannot find code in npm
// that does this
// if (comptime otp_response) {
// if (json.get("success")) |success_expr| {
// if (success_expr.asBool()) |successful| {
// if (successful) {
// // possible to hit this with otp responses
// return;
// }
// }
// }
// }

const @"error", _ = try json.getString(ctx.allocator, "error") orelse break :message null;
break :message @"error";
};

Output.prettyErrorln("\n<red>{d}<r>{s}{s}: {s}\n", .{
res.status_code,
if (res.status.len > 0) " " else "",
res.status,
bun.fmt.redactedNpmUrl(req.url.href),
});

if (message) |msg| {
if (comptime otp_response) {
if (res.status_code == 401 and strings.containsComptime(msg, "You must provide a one-time pass. Upgrade your client to npm@latest in order to use 2FA.")) {
Output.prettyErrorln("\n - Received invalid OTP", .{});
Global.crash();
}
}
Output.prettyErrorln("\n - {s}", .{msg});
}

Global.crash();
}

const GetOTPError = OOM || error{};

fn pressEnterToOpenInBrowser(auth_url: stringZ) void {
Expand Down Expand Up @@ -874,7 +834,14 @@ pub const PublishCommand = struct {
},
else => {
const otp_response = false;
try handleResponseErrors(directory_publish, ctx, &req, &res, response_buf, otp_response);
try Npm.responseError(
ctx.allocator,
&req,
&res,
.{ ctx.package_name, ctx.package_version },
response_buf,
otp_response,
);
},
}
}
Expand Down
7 changes: 2 additions & 5 deletions src/ini.zig
Original file line number Diff line number Diff line change
Expand Up @@ -439,18 +439,15 @@ pub const Parser = struct {
}

const env_var = val[i + 2 .. j];
const expanded = this.expandEnvVar(env_var);
// https://github.com/npm/cli/blob/534ad7789e5c61f579f44d782bdd18ea3ff1ee20/workspaces/config/lib/env-replace.js#L6
const expanded = this.env.get(env_var) orelse return null;
unesc.appendSlice(expanded) catch bun.outOfMemory();

return j;
}
return null;
}

fn expandEnvVar(this: *Parser, name: []const u8) []const u8 {
return this.env.get(name) orelse "";
}

fn singleStrRope(ropealloc: Allocator, str: []const u8) *Rope {
const rope = ropealloc.create(Rope) catch bun.outOfMemory();
rope.* = .{
Expand Down
Loading

0 comments on commit c071415

Please sign in to comment.