Skip to content

Commit

Permalink
Add taskCommand to ConfigEditor
Browse files Browse the repository at this point in the history
  • Loading branch information
SchoofsKelvin committed Mar 19, 2021
1 parent 7d94bb8 commit 262ec9c
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 3 deletions.
15 changes: 12 additions & 3 deletions webview/src/ConfigEditor/fields.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export function merge(config: FileSystemConfig, onChange: FSCChanged<'merge'>):
}

export function label(config: FileSystemConfig, onChange: FSCChanged<'label'>): React.ReactElement {
const callback = (value?: string) => onChange('label', value);
const callback = (newValue?: string) => onChange('label', newValue);
const description = 'Label to display in some UI places (e.g. popups)';
return <FieldString key="label" label="Label" value={config.label} onChange={callback} optional description={description} />
}
Expand Down Expand Up @@ -97,7 +97,7 @@ export function password(config: FileSystemConfig, onChange: FSCChanged<'passwor
}

export function privateKeyPath(config: FileSystemConfig, onChange: FSCChanged<'privateKeyPath'>): React.ReactElement {
const callback = (value?: string) => onChange('privateKeyPath', value);
const callback = (newValue?: string) => onChange('privateKeyPath', newValue);
const description = 'A path to a private key. Supports environment variables, e.g. `$USERPROFILE/.ssh/myKey.ppk` or `$HOME/.ssh/myKey`';
return <FieldPath key="privateKeyPath" label="Private key" value={config.privateKeyPath} onChange={callback} optional description={description} />
}
Expand Down Expand Up @@ -133,9 +133,18 @@ export function terminalCommand(config: FileSystemConfig, onChange: FSCChanged<'
return <FieldDropdownWithInput key="terminalCommand" label="Terminal command" {...{ value, values, description }} onChange={callback} optional />
}

export function taskCommand(config: FileSystemConfig, onChange: FSCChanged<'taskCommand'>): React.ReactElement {
const callback = (newValue?: string) => onChange('taskCommand', newValue);
const description = 'The command(s) to run when a `ssh-shell` gets run. Defaults to the placeholder `$COMMAND`. Internally the command `cd ...` is run first';
const values = ['$COMMAND'];
let value = config.taskCommand;
if (Array.isArray(value)) value = value.join('; ');
return <FieldDropdownWithInput key="taskCommand" label="Terminal command" {...{ value, values, description }} onChange={callback} optional />
}

export type FieldFactory = (config: FileSystemConfig, onChange: FSCChanged, onChangeMultiple: FSCChangedMultiple) => React.ReactElement | null;
export const FIELDS: FieldFactory[] = [
name, merge, label, group, putty, host, port,
root, agent, username, password, privateKeyPath, passphrase,
sftpCommand, sftpSudo, terminalCommand,
sftpCommand, sftpSudo, terminalCommand, taskCommand,
PROXY_FIELD];
36 changes: 36 additions & 0 deletions webview/src/types/fileSystemConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,12 @@ export interface FileSystemConfig extends ConnectConfig {
sftpSudo?: string | boolean;
/** The command(s) to run when a new SSH terminal gets created. Defaults to `$SHELL`. Internally the command `cd ...` is run first */
terminalCommand?: string | string[];
/** The command(s) to run when a `ssh-shell` gets run. Defaults to the placeholder `$COMMAND`. Internally the command `cd ...` is run first */
taskCommand?: string | string[];
/** The filemode to assign to created files */
newFileMode?: number | string;
/** Whether this config was created from an instant connection string. Enables fuzzy matching for e.g. PuTTY, config-by-host, ... */
instantConnection?: boolean;
/** Internal property saying where this config comes from. Undefined if this config is merged or something */
_location?: ConfigLocation;
/** Internal property keeping track of where this config comes from (including merges) */
Expand All @@ -113,3 +117,35 @@ export function invalidConfigName(name: string) {
if (name.match(/^[\w_\\/.@\-+]+$/)) return null;
return `A SSH FS name can only exists of lowercase alphanumeric characters, slashes and any of these: _.+-@`;
}

/**
* https://regexr.com/5m3gl (mostly based on https://tools.ietf.org/html/draft-ietf-secsh-scp-sftp-ssh-uri-04)
* Supports several formats, the first one being the "full" format, with others being partial:
* - `user;abc=def,[email protected]:22/some/file.ext`
* - `[email protected]/directory`
* - `server:22/directory`
* - `user@server`
* - `server`
* - `@server/path` - Unlike OpenSSH, we allow a @ (and connection parameters) without a username
*
* The resulting FileSystemConfig will have as name basically the input, but without the path. If there is no
* username given, the name will start with `@`, as to differentiate between connection strings and config names.
*/
const CONNECTION_REGEX = /^((?<user>\w+)?(;[\w-]+=[\w\d-]+(,[\w\d-]+=[\w\d-]+)*)?@)?(?<host>[^@\\/:,=]+)(:(?<port>\d+))?(?<path>\/.*)?$/;

export function parseConnectionString(input: string): [config: FileSystemConfig, path?: string] | string {
input = input.trim();
const match = input.match(CONNECTION_REGEX);
if (!match) return 'Invalid format, expected something like "[email protected]:22/some/path"';
const { user, host, path } = match.groups!;
const portStr = match.groups!.port;
const port = portStr ? Number.parseInt(portStr) : undefined;
if (portStr && (!port || port < 1 || port > 65535)) return `The string '${port}' is not a valid port number`;
const name = `${user || ''}@${host}${port ? `:${port}` : ''}${path || ''}`;
return [{
name, host, port,
instantConnection: true,
username: user || '$USERNAME',
_locations: [],
}, path];
}

0 comments on commit 262ec9c

Please sign in to comment.