mirror of
https://github.com/oven-sh/bun
synced 2026-02-15 05:12:29 +00:00
add docs for bun publish (#14327)
This commit is contained in:
@@ -543,47 +543,72 @@ async function authBunfig(user: string) {
|
||||
|
||||
describe("publish", async () => {
|
||||
describe("otp", async () => {
|
||||
const mockRegistryFetch = function (opts: {
|
||||
token: string;
|
||||
setAuthHeader?: boolean;
|
||||
otpFail?: boolean;
|
||||
npmNotice?: boolean;
|
||||
}) {
|
||||
return async function (req: Request) {
|
||||
const { token, setAuthHeader = true, otpFail = false, npmNotice = false } = opts;
|
||||
if (req.url.includes("otp-pkg")) {
|
||||
if (req.headers.get("npm-otp") === token) {
|
||||
if (otpFail) {
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
error: "You must provide a one-time pass. Upgrade your client to npm@latest in order to use 2FA.",
|
||||
}),
|
||||
{ status: 401 },
|
||||
);
|
||||
} else {
|
||||
return new Response("OK", { status: 200 });
|
||||
}
|
||||
} else {
|
||||
const headers = new Headers();
|
||||
if (setAuthHeader) headers.set("www-authenticate", "OTP");
|
||||
|
||||
// `bun publish` won't request a url from a message in the npm-notice header, but we
|
||||
// can test that it's displayed
|
||||
if (npmNotice) headers.set("npm-notice", `visit http://localhost:${this.port}/auth to login`);
|
||||
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
// this isn't accurate, but we just want to check that finding this string works
|
||||
mock: setAuthHeader ? "" : "one-time password",
|
||||
|
||||
authUrl: `http://localhost:${this.port}/auth`,
|
||||
doneUrl: `http://localhost:${this.port}/done`,
|
||||
}),
|
||||
{
|
||||
status: 401,
|
||||
headers,
|
||||
},
|
||||
);
|
||||
}
|
||||
} else if (req.url.endsWith("auth")) {
|
||||
expect.unreachable("url given to user, bun publish should not request");
|
||||
} else if (req.url.endsWith("done")) {
|
||||
// send a fake response saying the user has authenticated successfully with the auth url
|
||||
return new Response(JSON.stringify({ token: token }), { status: 200 });
|
||||
}
|
||||
|
||||
expect.unreachable("unexpected url");
|
||||
};
|
||||
};
|
||||
|
||||
for (const setAuthHeader of [true, false]) {
|
||||
test("mock web login" + (setAuthHeader ? "" : " (without auth header)"), async () => {
|
||||
const token = await generateRegistryUser("otp" + (setAuthHeader ? "" : "noheader"), "otp");
|
||||
|
||||
using mockRegistry = Bun.serve({
|
||||
port: 0,
|
||||
async fetch(req) {
|
||||
if (req.url.endsWith("otp-pkg-1")) {
|
||||
if (req.headers.get("npm-otp") === authToken) {
|
||||
return new Response("OK", { status: 200 });
|
||||
} else {
|
||||
const headers = new Headers();
|
||||
if (setAuthHeader) headers.set("www-authenticate", "OTP");
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
// this isn't accurate, but we just want to check that finding this string works
|
||||
mock: setAuthHeader ? "" : "one-time password",
|
||||
|
||||
authUrl: `http://localhost:${this.port}/auth`,
|
||||
doneUrl: `http://localhost:${this.port}/done`,
|
||||
}),
|
||||
{
|
||||
status: 401,
|
||||
headers,
|
||||
},
|
||||
);
|
||||
}
|
||||
} else if (req.url.endsWith("auth")) {
|
||||
expect.unreachable("url given to user, bun publish should not request");
|
||||
} else if (req.url.endsWith("done")) {
|
||||
// send a fake response saying the user has authenticated successfully with the auth url
|
||||
return new Response(JSON.stringify({ token: authToken }), { status: 200 });
|
||||
}
|
||||
|
||||
expect.unreachable("unexpected url");
|
||||
},
|
||||
fetch: mockRegistryFetch({ token }),
|
||||
});
|
||||
|
||||
const authToken = await generateRegistryUser("otp" + (setAuthHeader ? "" : "noheader"), "otp");
|
||||
const bunfig = `
|
||||
[install]
|
||||
cache = false
|
||||
registry = { url = "http://localhost:${mockRegistry.port}", token = "${authToken}" }`;
|
||||
registry = { url = "http://localhost:${mockRegistry.port}", token = "${token}" }`;
|
||||
await Promise.all([
|
||||
rm(join(import.meta.dir, "packages", "otp-pkg-1"), { recursive: true, force: true }),
|
||||
write(join(packageDir, "bunfig.toml"), bunfig),
|
||||
@@ -603,7 +628,78 @@ describe("publish", async () => {
|
||||
expect(exitCode).toBe(0);
|
||||
});
|
||||
}
|
||||
|
||||
test("otp failure", async () => {
|
||||
const token = await generateRegistryUser("otp-fail", "otp");
|
||||
using mockRegistry = Bun.serve({
|
||||
port: 0,
|
||||
fetch: mockRegistryFetch({ token, otpFail: true }),
|
||||
});
|
||||
|
||||
const bunfig = `
|
||||
[install]
|
||||
cache = false
|
||||
registry = { url = "http://localhost:${mockRegistry.port}", token = "${token}" }`;
|
||||
|
||||
await Promise.all([
|
||||
rm(join(import.meta.dir, "packages", "otp-pkg-2"), { recursive: true, force: true }),
|
||||
write(join(packageDir, "bunfig.toml"), bunfig),
|
||||
write(
|
||||
join(packageDir, "package.json"),
|
||||
JSON.stringify({
|
||||
name: "otp-pkg-2",
|
||||
version: "1.1.1",
|
||||
dependencies: {
|
||||
"otp-pkg-2": "1.1.1",
|
||||
},
|
||||
}),
|
||||
),
|
||||
]);
|
||||
|
||||
const { out, err, exitCode } = await publish(env, packageDir);
|
||||
expect(exitCode).toBe(1);
|
||||
expect(err).toContain(" - Received invalid OTP");
|
||||
});
|
||||
|
||||
test("npm-notice with login url", async () => {
|
||||
// Situation: user has 2FA enabled account with faceid sign-in.
|
||||
// They run `bun publish` with --auth-type=legacy, prompting them
|
||||
// to enter their OTP. Because they have faceid sign-in, they don't
|
||||
// have a code to enter, so npm sends a message in the npm-notice
|
||||
// header with a url for logging in.
|
||||
|
||||
const token = await generateRegistryUser("otp-notice", "otp");
|
||||
using mockRegistry = Bun.serve({
|
||||
port: 0,
|
||||
fetch: mockRegistryFetch({ token, npmNotice: true }),
|
||||
});
|
||||
|
||||
const bunfig = `
|
||||
[install]
|
||||
cache = false
|
||||
registry = { url = "http://localhost:${mockRegistry.port}", token = "${token}" }`;
|
||||
|
||||
await Promise.all([
|
||||
rm(join(import.meta.dir, "packages", "otp-pkg-3"), { recursive: true, force: true }),
|
||||
write(join(packageDir, "bunfig.toml"), bunfig),
|
||||
write(
|
||||
join(packageDir, "package.json"),
|
||||
JSON.stringify({
|
||||
name: "otp-pkg-3",
|
||||
version: "3.3.3",
|
||||
dependencies: {
|
||||
"otp-pkg-3": "3.3.3",
|
||||
},
|
||||
}),
|
||||
),
|
||||
]);
|
||||
|
||||
const { out, err, exitCode } = await publish(env, packageDir);
|
||||
expect(exitCode).toBe(0);
|
||||
expect(err).toContain(`note: visit http://localhost:${mockRegistry.port}/auth to login`);
|
||||
});
|
||||
});
|
||||
|
||||
test("can publish a package then install it", async () => {
|
||||
const bunfig = await authBunfig("basic");
|
||||
await Promise.all([
|
||||
|
||||
Reference in New Issue
Block a user