Skip to content

Add speedtest command#864

Draft
yazan-abu-obaideh wants to merge 3 commits intocoder:mainfrom
yazan-abu-obaideh:feat-750
Draft

Add speedtest command#864
yazan-abu-obaideh wants to merge 3 commits intocoder:mainfrom
yazan-abu-obaideh:feat-750

Conversation

@yazan-abu-obaideh
Copy link
Copy Markdown

@yazan-abu-obaideh yazan-abu-obaideh commented Mar 27, 2026

Closes #750

Copy link
Copy Markdown
Collaborator

@EhabY EhabY left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for contributing to this repo, appreciated ♥️

Also make sure to run format/lint:fix (I think the pipeline would fail now)

/**
* Build CLI auth flags for execFile (no shell escaping).
*/
function authArgs(auth: CliAuth): string[] {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pretty sure that this function exists elsewhere already, getGlobalFlags in the cli settings

src/commands.ts Outdated
const configDir = this.pathResolver.getGlobalConfigDir(safeHost);
const configs = vscode.workspace.getConfiguration();
const auth = resolveCliAuth(configs, featureSet, baseUrl, configDir);
const workspaceName = createWorkspaceIdentifier(this.workspace!);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should avoid these kinds of null assertions (I thought we had a rule for that actually 🤔). Also might make sense to capture this.workspace in a variable in case it changes during one of the await operations (and would make the null assertion unnecessary as well)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, and I'd like to contribute the eslint rule too 👀 (is it null assertions in general or something more specific?)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

null assertions (https://typescript-eslint.io/rules/no-non-null-assertion/) but in this case TS detect something was wrong (we could reset the variable in the meantime)

src/commands.ts Outdated
Comment on lines +181 to +186
const doc = await vscode.workspace.openTextDocument({
content: stdout,
language: "json",
});
await vscode.window.showTextDocument(doc);
} catch (error) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO I think we should show a save dialog instead of opening an untitled window.

We can show the save dialog then an info dialog that confirms it's been saved with a button to open the file in the editor

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed, but I'm not sure if I prefer this UX 🤔

Isn't it better to just show the results immediately, and only save if the user explicitly chooses to? Sometimes the user just want to see the results.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm basing this off this comment from Asher #750 (comment)

I suppose we can let the user copy it vs. save it, or opening the file and they can choose to copy it vs. save it 🤔

I don't like having to make the user choose since it adds friction, like we want results fast and easy, so perhaps opening them in an untitled file and letting the user save it might work, but in that case, can we suggest a good filename based on the timestamp so it's still easy to save?

@code-asher Thoughts?

Current flow is this BTW:

  1. Execute command
  2. Input the time (by default it's populted with 5s) and press Enter
  3. Speed test runs and then shows a save dialog
  4. User has to choose where to save it
  5. A notification appears that offers to open the file

This does seem a lot now yeah...

Copy link
Copy Markdown
Member

@code-asher code-asher Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My completely biased guess is that the most common thing will be for people to look at the results and discard them, or copy/paste them to Slack or GitHub or somewhere like that for debugging something, so saving will be the least used workflow. But I could be completely wrong about that.

On a personal level I would vote to open it in a new, unsaved file (a default title would indeed be nice) because it feels like how Emacs would do it. 😆

Copy link
Copy Markdown
Member

@code-asher code-asher Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah except the JSON output is not particularly human readable is it? So viewing it may not be very useful if you always have to pipe it into some graphing tool anyway...

Edit: I tried the JSON, I suppose it is not that bad to read.

src/commands.ts Outdated
Comment on lines +159 to +164
await withProgress(
{
location: vscode.ProgressLocation.Notification,
title: "Running speed test...",
},
async () => {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we pass the cancellation token to the CLI here? Also possibly use the -t argument and mention that in the title: https://coder.com/docs/reference/cli/speedtest#-t---time

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, it's cancellable now, and the user chooses the time through the command dialog (5s default), but I'm not sure what you mean by 'mention that in the title'.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Running speed test... could be something like Running ${duration} speed test... (or something along those lines)

@yazan-abu-obaideh
Copy link
Copy Markdown
Author

@EhabY happy to contribute 😉

(should pass now 🙏)

Copy link
Copy Markdown
Collaborator

@EhabY EhabY left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love the tests! I think we'll have to await Asher's response for the UX but the rest are smaller issues!

function buildGlobalFlags(
configs: Pick<WorkspaceConfiguration, "get">,
auth: CliAuth,
escapeAuth: boolean,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer we pass the escape method itself here instead of a boolean

* Returns global configuration flags for Coder CLI commands with raw auth
* values suitable for `execFile` (no shell escaping).
*/
export function getGlobalFlagsForExec(
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about we rename those to be clearer to something like this:

  • getGlobalFlags → raw args array (unescaped), the default, used by execFile
  • getGlobalShellFlags → shell-escaped args, used by spawn({ shell: true }), terminal.sendText, and SSH ProxyCommand

Comment on lines +170 to +172
return /^\d+[sm]$/.test(v.trim())
? null
: "Enter a duration like 5s, 10s, or 1m";
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this only take seconds and minutes 🤔 ?

Comment on lines +229 to +233
const action = await vscode.window.showInformationMessage(
"Speed test results saved.",
"Open File",
);
if (action === "Open File") {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might be removed but we should never await a thenable that is not blocking. We had a bug a while back that caused the notification to show, then hide after 10s and this promise was left hanging (VS Code API uses a thenable so it does not adhere to promises and as such might never throw)

binPath: string,
globalFlags: string[],
workspaceName: string,
options?: { signal?: AbortSignal; duration?: string },
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Options are always passed no?

Comment on lines +152 to +158
if (isWindows()) {
await fs.writeFile(echoArgsBin, `@node "${scriptPath}" %*\r\n`);
} else {
const content = await fs.readFile(scriptPath, "utf8");
await fs.writeFile(echoArgsBin, `#!/usr/bin/env node\n${content}`);
await fs.chmod(echoArgsBin, "755");
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this? Can't we execute the JS file the same way?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add command to run a coder speedtest against a workspace

3 participants