Skip to content

Commit 6380d86

Browse files
emily-shenedmundhungpenalosa
authored
chore: refactor wrangler login/logout/whoami to use defineCommand (#7150)
* move whoami.ts into user/ * move wranger `login/logout/whoami` and use defineCommand * Update packages/wrangler/src/index.ts Co-authored-by: Somhairle MacLeòid <[email protected]> * make args optional * disable warning for invalid config * add changeset * allow disabling readConfig warning with defineCommand --------- Co-authored-by: Edmund Hung <[email protected]> Co-authored-by: Somhairle MacLeòid <[email protected]>
1 parent edef523 commit 6380d86

File tree

8 files changed

+158
-119
lines changed

8 files changed

+158
-119
lines changed

.changeset/good-eggs-lay.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"wrangler": patch
3+
---
4+
5+
refactor: improve login/logout/whoami setup with the new internal registration utils

packages/wrangler/src/__tests__/sentry.test.ts

+16-5
Original file line numberDiff line numberDiff line change
@@ -259,18 +259,29 @@ describe("sentry", () => {
259259
Object {
260260
"colno": 0,
261261
"context_line": "",
262-
"filename": "/wrangler/packages/wrangler/src/index.ts",
262+
"filename": "/wrangler/packages/wrangler/src/core/register-commands.ts",
263263
"function": "",
264264
"in_app": false,
265265
"lineno": 0,
266-
"module": "index.ts",
266+
"module": "register-commands.ts",
267+
"post_context": Array [],
268+
"pre_context": Array [],
269+
},
270+
Object {
271+
"colno": 0,
272+
"context_line": "",
273+
"filename": "/wrangler/packages/wrangler/src/user/commands.ts",
274+
"function": "",
275+
"in_app": false,
276+
"lineno": 0,
277+
"module": "commands.ts",
267278
"post_context": Array [],
268279
"pre_context": Array [],
269280
},
270281
Object {
271282
"colno": 0,
272283
"context_line": "",
273-
"filename": "/wrangler/packages/wrangler/src/whoami.ts",
284+
"filename": "/wrangler/packages/wrangler/src/user/whoami.ts",
274285
"function": "",
275286
"in_app": false,
276287
"lineno": 0,
@@ -281,7 +292,7 @@ describe("sentry", () => {
281292
Object {
282293
"colno": 0,
283294
"context_line": "",
284-
"filename": "/wrangler/packages/wrangler/src/whoami.ts",
295+
"filename": "/wrangler/packages/wrangler/src/user/whoami.ts",
285296
"function": "",
286297
"in_app": false,
287298
"lineno": 0,
@@ -292,7 +303,7 @@ describe("sentry", () => {
292303
Object {
293304
"colno": 0,
294305
"context_line": "",
295-
"filename": "/wrangler/packages/wrangler/src/whoami.ts",
306+
"filename": "/wrangler/packages/wrangler/src/user/whoami.ts",
296307
"function": "",
297308
"in_app": false,
298309
"lineno": 0,

packages/wrangler/src/__tests__/whoami.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { http, HttpResponse } from "msw";
22
import { writeAuthConfigFile } from "../user";
3-
import { getUserInfo } from "../whoami";
3+
import { getUserInfo } from "../user/whoami";
44
import { mockConsoleMethods } from "./helpers/mock-console";
55
import { useMockIsTTY } from "./helpers/mock-istty";
66
import {

packages/wrangler/src/core/define-command.ts

+8-1
Original file line numberDiff line numberDiff line change
@@ -101,13 +101,20 @@ export type CommandDefinition<
101101
* @default true
102102
*/
103103
printBanner?: boolean;
104+
105+
/**
106+
* By default, wrangler will print warnings about wrangler.toml configuration.
107+
* Set this value to `false` to skip printing these warnings.
108+
*/
109+
printConfigWarnings?: boolean;
104110
};
105111

106112
/**
107113
* A plain key-value object describing the CLI args for this command.
108114
* Shared args can be defined as another plain object and spread into this.
109115
*/
110-
args: NamedArgDefs;
116+
args?: NamedArgDefs;
117+
111118
/**
112119
* Optionally declare some of the named args as positional args.
113120
* The order of this array is the order they are expected in the command.

packages/wrangler/src/core/register-commands.ts

+12-5
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ function walkTreeAndRegister(
106106
// inference from positionalArgs
107107
const commandPositionalArgsSuffix = def.positionalArgs
108108
?.map((key) => {
109-
const { demandOption, array } = def.args[key];
109+
const { demandOption, array } = def.args?.[key] ?? {};
110110
return demandOption
111111
? `<${key}${array ? ".." : ""}>` // <key> or <key..>
112112
: `[${key}${array ? ".." : ""}]`; // [key] or [key..]
@@ -124,21 +124,23 @@ function walkTreeAndRegister(
124124
(def.metadata.hidden ? false : def.metadata.description) as string, // cast to satisfy typescript overload selection
125125
function builder(subYargs) {
126126
if (def.type === "command") {
127-
yargs.options(def.args);
127+
const args = def.args ?? {};
128+
129+
yargs.options(args);
128130

129131
// Yargs offers an `array: true` option that will always coerces the value to an array
130132
// e.g. `--name foo` becomes `{ name: ["foo"] }` instead of `{ name: "foo" }`
131133
// However, non-array arguments can still receives multiple values
132134
// e.g. `--name foo --name bar` becomes `{ name: ["foo", "bar"] }` regardless of the `array` setting
133135
// @see https://github.com/yargs/yargs/issues/1318
134-
for (const [key, opt] of Object.entries(def.args)) {
136+
for (const [key, opt] of Object.entries(args)) {
135137
if (!opt.array) {
136138
yargs.check(demandSingleValue(key));
137139
}
138140
}
139141

140142
for (const key of def.positionalArgs ?? []) {
141-
yargs.positional(key, def.args[key]);
143+
yargs.positional(key, args[key]);
142144
}
143145
} else if (def.type === "namespace") {
144146
// this is our hacky way of printing --help text for incomplete commands
@@ -180,7 +182,12 @@ function createHandler(def: CommandDefinition) {
180182
await def.validateArgs?.(args);
181183

182184
await def.handler(args, {
183-
config: readConfig(args.config, args),
185+
config: readConfig(
186+
args.config,
187+
args,
188+
undefined,
189+
!(def.behaviour?.printConfigWarnings ?? true)
190+
),
184191
errors: { UserError, FatalError },
185192
logger,
186193
fetchResult,

packages/wrangler/src/index.ts

+7-102
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import makeCLI from "yargs";
77
import { version as wranglerVersion } from "../package.json";
88
import { ai } from "./ai";
99
import { cloudchamber } from "./cloudchamber";
10-
import { loadDotEnv, readConfig } from "./config";
10+
import { loadDotEnv } from "./config";
1111
import { createCommandRegister } from "./core/register-commands";
1212
import { d1 } from "./d1";
1313
import { deleteHandler, deleteOptions } from "./delete";
@@ -48,9 +48,9 @@ import { hyperdrive } from "./hyperdrive/index";
4848
import { initHandler, initOptions } from "./init";
4949
import "./kv";
5050
import "./workflows";
51+
import "./user/commands";
5152
import { demandSingleValue } from "./core";
5253
import { logBuildFailure, logger, LOGGER_LEVELS } from "./logger";
53-
import * as metrics from "./metrics";
5454
import { mTlsCertificateCommands } from "./mtls-certificate/cli";
5555
import { writeOutput } from "./output";
5656
import { pages } from "./pages";
@@ -70,19 +70,13 @@ import { tailHandler, tailOptions } from "./tail";
7070
import registerTriggersSubcommands from "./triggers";
7171
import { typesHandler, typesOptions } from "./type-generation";
7272
import { printWranglerBanner, updateCheck } from "./update-check";
73-
import {
74-
getAuthFromEnv,
75-
listScopes,
76-
login,
77-
logout,
78-
validateScopeKeys,
79-
} from "./user";
73+
import { getAuthFromEnv } from "./user";
74+
import { whoami } from "./user/whoami";
8075
import { debugLogFilepath } from "./utils/log-file";
8176
import { vectorize } from "./vectorize/index";
8277
import registerVersionsSubcommands from "./versions";
8378
import registerVersionsDeploymentsSubcommands from "./versions/deployments";
8479
import registerVersionsRollbackCommand from "./versions/rollback";
85-
import { whoami } from "./whoami";
8680
import { asJson } from "./yargs-types";
8781
import type { Config } from "./config";
8882
import type { LoggerLevel } from "./logger";
@@ -596,99 +590,10 @@ export function createCLIParser(argv: string[]) {
596590
});
597591

598592
/******************** CMD GROUP ***********************/
599-
// login
600-
wrangler.command(
601-
// this needs scopes as an option?
602-
"login",
603-
"🔓 Login to Cloudflare",
604-
(yargs) => {
605-
// TODO: This needs some copy editing
606-
// I mean, this entire app does, but this too.
607-
return yargs
608-
.option("scopes-list", {
609-
describe: "List all the available OAuth scopes with descriptions",
610-
})
611-
.option("browser", {
612-
default: true,
613-
type: "boolean",
614-
describe: "Automatically open the OAuth link in a browser",
615-
})
616-
.option("scopes", {
617-
describe: "Pick the set of applicable OAuth scopes when logging in",
618-
array: true,
619-
type: "string",
620-
requiresArg: true,
621-
});
622-
// TODO: scopes
623-
},
624-
async (args) => {
625-
await printWranglerBanner();
626-
if (args["scopes-list"]) {
627-
listScopes();
628-
return;
629-
}
630-
if (args.scopes) {
631-
if (args.scopes.length === 0) {
632-
// don't allow no scopes to be passed, that would be weird
633-
listScopes();
634-
return;
635-
}
636-
if (!validateScopeKeys(args.scopes)) {
637-
throw new CommandLineArgsError(
638-
`One of ${args.scopes} is not a valid authentication scope. Run "wrangler login --scopes-list" to see the valid scopes.`
639-
);
640-
}
641-
await login({ scopes: args.scopes, browser: args.browser });
642-
return;
643-
}
644-
await login({ browser: args.browser });
645-
const config = readConfig(args.config, args, undefined, true);
646-
await metrics.sendMetricsEvent("login user", {
647-
sendMetrics: config.send_metrics,
648-
});
649-
650-
// TODO: would be nice if it optionally saved login
651-
// credentials inside node_modules/.cache or something
652-
// this way you could have multiple users on a single machine
653-
}
654-
);
655593

656-
// logout
657-
wrangler.command(
658-
// this needs scopes as an option?
659-
"logout",
660-
"🚪 Logout from Cloudflare",
661-
() => {},
662-
async (args) => {
663-
await printWranglerBanner();
664-
await logout();
665-
const config = readConfig(undefined, args, undefined, true);
666-
await metrics.sendMetricsEvent("logout user", {
667-
sendMetrics: config.send_metrics,
668-
});
669-
}
670-
);
671-
672-
// whoami
673-
wrangler.command(
674-
"whoami",
675-
"🕵️ Retrieve your user information",
676-
(yargs) => {
677-
return yargs.option("account", {
678-
type: "string",
679-
describe:
680-
"Show membership information for the given account (id or name).",
681-
});
682-
},
683-
async (args) => {
684-
await printWranglerBanner();
685-
await whoami(args.account);
686-
const config = readConfig(undefined, args);
687-
await metrics.sendMetricsEvent("view accounts", {
688-
sendMetrics: config.send_metrics,
689-
});
690-
}
691-
);
594+
register.registerNamespace("login");
595+
register.registerNamespace("logout");
596+
register.registerNamespace("whoami");
692597

693598
/******************************************************/
694599
/* DEPRECATED COMMANDS */
+104
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import { defineCommand } from "../core/define-command";
2+
import { CommandLineArgsError } from "../errors";
3+
import * as metrics from "../metrics";
4+
import { listScopes, login, logout, validateScopeKeys } from "./user";
5+
import { whoami } from "./whoami";
6+
7+
defineCommand({
8+
command: "wrangler login",
9+
metadata: {
10+
description: "🔓 Login to Cloudflare",
11+
owner: "Workers: Authoring and Testing",
12+
status: "stable",
13+
},
14+
behaviour: {
15+
printConfigWarnings: false,
16+
},
17+
args: {
18+
"scopes-list": {
19+
describe: "List all the available OAuth scopes with descriptions",
20+
},
21+
browser: {
22+
default: true,
23+
type: "boolean",
24+
describe: "Automatically open the OAuth link in a browser",
25+
},
26+
scopes: {
27+
describe: "Pick the set of applicable OAuth scopes when logging in",
28+
array: true,
29+
type: "string",
30+
requiresArg: true,
31+
},
32+
},
33+
async handler(args, { config }) {
34+
if (args.scopesList) {
35+
listScopes();
36+
return;
37+
}
38+
if (args.scopes) {
39+
if (args.scopes.length === 0) {
40+
// don't allow no scopes to be passed, that would be weird
41+
listScopes();
42+
return;
43+
}
44+
if (!validateScopeKeys(args.scopes)) {
45+
throw new CommandLineArgsError(
46+
`One of ${args.scopes} is not a valid authentication scope. Run "wrangler login --scopes-list" to see the valid scopes.`
47+
);
48+
}
49+
await login({ scopes: args.scopes, browser: args.browser });
50+
return;
51+
}
52+
await login({ browser: args.browser });
53+
await metrics.sendMetricsEvent("login user", {
54+
sendMetrics: config.send_metrics,
55+
});
56+
57+
// TODO: would be nice if it optionally saved login
58+
// credentials inside node_modules/.cache or something
59+
// this way you could have multiple users on a single machine
60+
},
61+
});
62+
63+
defineCommand({
64+
command: "wrangler logout",
65+
metadata: {
66+
description: "🚪 Logout from Cloudflare",
67+
owner: "Workers: Authoring and Testing",
68+
status: "stable",
69+
},
70+
behaviour: {
71+
printConfigWarnings: false,
72+
},
73+
async handler(_, { config }) {
74+
await logout();
75+
await metrics.sendMetricsEvent("logout user", {
76+
sendMetrics: config.send_metrics,
77+
});
78+
},
79+
});
80+
81+
defineCommand({
82+
command: "wrangler whoami",
83+
metadata: {
84+
description: "🕵️ Retrieve your user information",
85+
owner: "Workers: Authoring and Testing",
86+
status: "stable",
87+
},
88+
behaviour: {
89+
printConfigWarnings: false,
90+
},
91+
args: {
92+
account: {
93+
type: "string",
94+
describe:
95+
"Show membership information for the given account (id or name).",
96+
},
97+
},
98+
async handler(args, { config }) {
99+
await whoami(args.account);
100+
await metrics.sendMetricsEvent("view accounts", {
101+
sendMetrics: config.send_metrics,
102+
});
103+
},
104+
});

0 commit comments

Comments
 (0)