Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

defineCommand util #6526

Open
wants to merge 31 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
81a0f03
defineCommand interface
RamIdeas Aug 19, 2024
2e904ca
implement registerAllCommands util
RamIdeas Aug 21, 2024
4df126b
log deprecation/status message before running command handler
RamIdeas Aug 22, 2024
85ddfab
add tests
RamIdeas Aug 22, 2024
120306c
chore
RamIdeas Aug 23, 2024
c200178
cleanup
RamIdeas Aug 23, 2024
6853900
implement registerNamespace
RamIdeas Sep 4, 2024
80ddba2
add contrib guide
RamIdeas Sep 4, 2024
9155288
add validateArgs
RamIdeas Sep 5, 2024
50196fd
fix: don't register commands more than once per run
RamIdeas Sep 6, 2024
b5ed6a3
refactor KV command registration
RamIdeas Sep 6, 2024
7ac47ad
update snapshots
RamIdeas Sep 6, 2024
23af233
prettier
RamIdeas Sep 6, 2024
021182a
implement behaviour.printBanner option
RamIdeas Sep 9, 2024
b29e451
disable printBanner for some kv commands to match existing behaviour
RamIdeas Sep 9, 2024
e1ba44f
add custom deprecation messages to match existing behaviour
RamIdeas Sep 9, 2024
b570b2f
chore
RamIdeas Sep 9, 2024
56e274f
update docs
RamIdeas Sep 10, 2024
d00593c
fix: arg types
RamIdeas Sep 11, 2024
034fa4b
chore: rename
RamIdeas Sep 11, 2024
4d8e709
chore: no need to return yargs
RamIdeas Sep 11, 2024
f411ef1
chore: rename
RamIdeas Sep 11, 2024
dfbaad7
update snapshot
RamIdeas Sep 11, 2024
0c7738b
chore: DefineCommandResult
RamIdeas Sep 11, 2024
81f7cb7
remove behaviour.sendMetrics
RamIdeas Sep 11, 2024
d39580b
Update define-command.ts
RamIdeas Sep 12, 2024
09a65e5
refactor
RamIdeas Sep 19, 2024
3d20c43
lints
RamIdeas Sep 23, 2024
21e9315
fix lint
RamIdeas Oct 1, 2024
2bf7afb
Update packages/wrangler/CONTRIBUTING.md
RamIdeas Oct 16, 2024
b03a10f
update snapshot
RamIdeas Oct 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
186 changes: 186 additions & 0 deletions packages/wrangler/CONTRIBUTING.md
RamIdeas marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
# Wrangler Contributing Guidelines for Internal Teams

## What you will learn

- How to add a new command to Wrangler
- How to specify arguments for a command
- How to use experimental flags
- How to read from the config
- How to implement a command
- How to test a command

## Defining your new command in Wrangler

1. Define the command structure with the utils `defineNamespace()` & `defineCommand()`

```ts
import { defineCommand, defineNamespace } from "./util";

// Namespaces are the prefix before the subcommand
// eg "wrangler kv" in "wrangler kv put"
// eg "wrangler kv key" in "wrangler kv key put"
defineNamespace({
command: "wrangler kv",
metadata: {
description: "Commands for interacting with Workers KV",
status: "stable",
},
});
// Every level of namespaces must be defined
// eg "wrangler kv key" in "wrangler kv key put"
defineNamespace({
command: "wrangler kv key",
metadata: {
description: "Commands for interacting with Workers KV data",
status: "stable",
},
});

// Define the command args, implementation and metadata
const command = defineCommand({
command: "wrangler kv key put", // the full command including the namespace
metadata: {
description: "Put a key-value pair into a Workers KV namespace",
status: "stable",
},
args: {
key: {
type: "string",
description: "The key to put into the KV namespace",
demandOption: true,
},
value: {
type: "string",
description: "The value to put into the KV namespace",
demandOption: true,
},
"namespace-id": {
type: "string",
description: "The namespace to put the key-value pair into",
},
},
// the positionalArgs defines which of the args are positional and in what order
positionalArgs: ["key", "value"],
handler(args, ctx) {
// implementation here
},
});
```

2. Command-specific (named + positional) args vs shared args vs global args

- Command-specific args are defined in the `args` field of the command definition. Command handlers receive these as a typed object automatically. To make any of these positional, add the key to the `positionalArgs` array.
- You can share args between commands by declaring a separate object and spreading it into the `args` field. Feel free to import from another file.
- Global args are shared across all commands and defined in `src/commands/global-args.ts` (same schema as command-specific args). They are available in every command handler.

3. Optionally, get a type for the args

You may want to pass your args to other functions. These functions will need to be typed. To get a type of your args, you can use `typeof command.args`.

4. Implement the command handler

A command handler is just a function that receives the `args` as the first param and `ctx` as the second param. This is where you will want to do API calls, I/O, logging, etc.

- API calls

Define API response type. Use `fetchResult` to make authenticated API calls. Import it from `src/cfetch` or use `ctx.fetchResult`. `fetchResult` will throw an error if the response is not 2xx.

```ts
type UploadResponse = {
jwt?: string;
};

const res = await fetchResult<UploadResponse>(
`/accounts/${accountId}/workers/assets/upload`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: payload,
}
);
```

- Logging

Do not use `console.*` methods to log. You must the `logger` singleton (imported from `src/logger`) or use `ctx.logger`.

- Error handling - UserError vs Error

These classes can be imported from `src/errors` or found on `ctx`, eg. `ctx.errors.UserError`.

Throw `UserError` for errors _caused_ by the user -- these are not sent to Sentry whereas regular `Error`s are and should be used for unexpected exceptions.

For example, if an exception was encountered because the user provided an invalid SQL statement in a D1 command, a `UserError` should be thrown. Whereas, if the D1 local DB crashed for another reason or there was a network error, a regular `Error` would be thrown.

Errors are caught at the top-level and formatted for the console.
penalosa marked this conversation as resolved.
Show resolved Hide resolved

## Best Practices

### Status / Deprecation

Status can be alpha, private-beta, open-beta, or stable. Breaking changes can freely be made in alpha or private-beta. Try avoid breaking changes in open-beta but are acceptable and should be called out in [a changeset](../../CONTRIBUTING.md#Changesets).

Stable commands should never have breaking changes.

### Changesets

Run `npx changesets` from the top of the repo. New commands warrant a "minor" bump. Please explain the functionality with examples.
RamIdeas marked this conversation as resolved.
Show resolved Hide resolved

For example:

```md
feat: implement the `wrangler versions deploy` command

This command allows users to deploy a multiple versions of their Worker.

Note: while in open-beta, the `--experimental-versions` flag is required.

For interactive use (to be prompted for all options), run:

- `wrangler versions deploy --x-versions`

For non-interactive use, run with CLI args (and `--yes` to accept defaults):

- `wrangler versions deploy --version-id $v1 --percentage 90 --version-id $v2 --percentage 10 --yes`
```

### Experimental Flags

If you have a stable command, new features should be added behind an experimental flag. By convention, these are named `--experimental-<feature-name>` and have an alias `--x-<feature-name>`. These should be boolean, defaulting to false (off by default).

To stabilise a feature, flip the default to true while keeping the flag to allow users to disable the feature with `--no-x-<feature-name>`.

After a validation period with no issues reported, you can mark the flag as deprecated and hidden, and remove all code paths using the flag.

### Documentation

Add documentation for the command in the [`cloudflare-docs`](https://github.com/cloudflare/cloudflare-docs) repo.

### PR Best Practices

- link to a ticket or issue
- add a description of what the PR does _and why_
- add a description of how to test the PR manually
- test manually with prelease (automatically published by PR bot)
- lint/check before push
- add "e2e" label if you need e2e tests to run

## Testing

### Unit/Integration Tests

These tests are in the `workers-sdk/packages/wrangler/src/__tests__/` directory.

Write these tests when you need to mock out the API or any module.

### Fixture Tests

These tests are in the `workers-sdk/fixtures/` directory.

Write these when you want to test your feature on a real Workers project.

### E2E Tests

Write these when you want to test your feature against the production API. Use describe.each to write the same test against multiple combinations of flags for your command.
Loading
Loading