add docs for bun publish (#14327)

This commit is contained in:
Dylan Conway
2024-10-03 03:41:11 -07:00
committed by GitHub
parent eda608d629
commit 39b1c0111e
12 changed files with 341 additions and 91 deletions

View File

@@ -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([