@mittwald/cli 1.0.0-alpha.35 → 1.0.0-alpha.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -42,7 +42,7 @@ either the CMD prompt or PowerShell.
42
42
  #### Any OS, using Node.js+NPM
43
43
 
44
44
  Installing the CLI via NPM will work on any OS; however we cannot guarantee
45
- stability, because functionality of the CLI may depend in the Node.js runtime
45
+ stability, because functionality of the CLI may depend on the Node.js runtime
46
46
  already installed on your system. Also, the automatic upgrade will not work when
47
47
  using NPM; remember to run `npm upgrade -g @mittwald/cli` occasionally.
48
48
 
@@ -551,17 +551,18 @@ Download the filesystem of an app within a project to your local machine
551
551
 
552
552
  ```
553
553
  USAGE
554
- $ mw app download [INSTALLATION-ID] --target <value> [-q] [--dry-run] [--delete]
554
+ $ mw app download [INSTALLATION-ID] --target <value> [-q] [--ssh-user <value>] [--dry-run] [--delete]
555
555
 
556
556
  ARGUMENTS
557
557
  INSTALLATION-ID ID or short ID of an app installation; this argument is optional if a default app installation is set
558
558
  in the context
559
559
 
560
560
  FLAGS
561
- -q, --quiet suppress process output and only display a machine-readable summary.
562
- --delete delete local files that are not present on the server
563
- --dry-run do not actually download the app installation
564
- --target=<value> (required) target directory to download the app installation to
561
+ -q, --quiet suppress process output and only display a machine-readable summary.
562
+ --delete delete local files that are not present on the server
563
+ --dry-run do not actually download the app installation
564
+ --ssh-user=<value> override the SSH user to connect with; if omitted, your own user will be used
565
+ --target=<value> (required) target directory to download the app installation to
565
566
 
566
567
  DESCRIPTION
567
568
  Download the filesystem of an app within a project to your local machine
@@ -571,6 +572,13 @@ FLAG DESCRIPTIONS
571
572
 
572
573
  This flag controls if you want to see the process output or only a summary. When using mw non-interactively (e.g. in
573
574
  scripts), you can use this flag to easily get the IDs of created resources for further processing.
575
+
576
+ --ssh-user=<value> override the SSH user to connect with; if omitted, your own user will be used
577
+
578
+ This flag can be used to override the SSH user that is used for a connection; be default, your own personal user
579
+ will be used for this.
580
+
581
+ You can also set this value by setting the MITTWALD_SSH_USER environment variable.
574
582
  ```
575
583
 
576
584
  ## `mw app get [INSTALLATION-ID]`
@@ -1479,19 +1487,28 @@ Connect to an app via SSH
1479
1487
 
1480
1488
  ```
1481
1489
  USAGE
1482
- $ mw app ssh [INSTALLATION-ID] [--cd] [--info] [--test]
1490
+ $ mw app ssh [INSTALLATION-ID] [--ssh-user <value>] [--cd] [--info] [--test]
1483
1491
 
1484
1492
  ARGUMENTS
1485
1493
  INSTALLATION-ID ID or short ID of an app installation; this argument is optional if a default app installation is set
1486
1494
  in the context
1487
1495
 
1488
1496
  FLAGS
1489
- --[no-]cd change to installation path after connecting
1490
- --info only print connection information, without actually connecting
1491
- --test test connection and exit
1497
+ --[no-]cd change to installation path after connecting
1498
+ --info only print connection information, without actually connecting
1499
+ --ssh-user=<value> override the SSH user to connect with; if omitted, your own user will be used
1500
+ --test test connection and exit
1492
1501
 
1493
1502
  DESCRIPTION
1494
1503
  Connect to an app via SSH
1504
+
1505
+ FLAG DESCRIPTIONS
1506
+ --ssh-user=<value> override the SSH user to connect with; if omitted, your own user will be used
1507
+
1508
+ This flag can be used to override the SSH user that is used for a connection; be default, your own personal user
1509
+ will be used for this.
1510
+
1511
+ You can also set this value by setting the MITTWALD_SSH_USER environment variable.
1495
1512
  ```
1496
1513
 
1497
1514
  ## `mw app uninstall [INSTALLATION-ID]`
@@ -2269,7 +2286,7 @@ Create a dump of a MySQL database
2269
2286
 
2270
2287
  ```
2271
2288
  USAGE
2272
- $ mw database mysql dump DATABASE-ID -o <value> [-q] [-p <value>] [--temporary-user]
2289
+ $ mw database mysql dump DATABASE-ID -o <value> [-q] [-p <value>] [--ssh-user <value>] [--temporary-user] [--gzip]
2273
2290
 
2274
2291
  ARGUMENTS
2275
2292
  DATABASE-ID The ID of the database (when a project context is set, you can also use the name)
@@ -2278,6 +2295,8 @@ FLAGS
2278
2295
  -o, --output=<value> (required) the output file to write the dump to ("-" for stdout)
2279
2296
  -p, --mysql-password=<value> the password to use for the MySQL user (env: MYSQL_PWD)
2280
2297
  -q, --quiet suppress process output and only display a machine-readable summary.
2298
+ --gzip compress the dump with gzip
2299
+ --ssh-user=<value> override the SSH user to connect with; if omitted, your own user will be used
2281
2300
  --[no-]temporary-user create a temporary user for the dump
2282
2301
 
2283
2302
  FLAG DESCRIPTIONS
@@ -2300,6 +2319,18 @@ FLAG DESCRIPTIONS
2300
2319
  This flag controls if you want to see the process output or only a summary. When using mw non-interactively (e.g. in
2301
2320
  scripts), you can use this flag to easily get the IDs of created resources for further processing.
2302
2321
 
2322
+ --gzip compress the dump with gzip
2323
+
2324
+ Compress the dump with gzip. This is useful for large databases, as it can significantly reduce the size of the
2325
+ dump.
2326
+
2327
+ --ssh-user=<value> override the SSH user to connect with; if omitted, your own user will be used
2328
+
2329
+ This flag can be used to override the SSH user that is used for a connection; be default, your own personal user
2330
+ will be used for this.
2331
+
2332
+ You can also set this value by setting the MITTWALD_SSH_USER environment variable.
2333
+
2303
2334
  --[no-]temporary-user create a temporary user for the dump
2304
2335
 
2305
2336
  Create a temporary user for the dump. This user will be deleted after the dump has been created. This is useful if
@@ -2376,20 +2407,28 @@ Forward the TCP port of a MySQL database to a local port
2376
2407
 
2377
2408
  ```
2378
2409
  USAGE
2379
- $ mw database mysql port-forward DATABASE-ID [-q] [--port <value>]
2410
+ $ mw database mysql port-forward DATABASE-ID [-q] [--ssh-user <value>] [--port <value>]
2380
2411
 
2381
2412
  ARGUMENTS
2382
2413
  DATABASE-ID The ID of the database (when a project context is set, you can also use the name)
2383
2414
 
2384
2415
  FLAGS
2385
- -q, --quiet suppress process output and only display a machine-readable summary.
2386
- --port=<value> [default: 3306] The local TCP port to forward to
2416
+ -q, --quiet suppress process output and only display a machine-readable summary.
2417
+ --port=<value> [default: 3306] The local TCP port to forward to
2418
+ --ssh-user=<value> override the SSH user to connect with; if omitted, your own user will be used
2387
2419
 
2388
2420
  FLAG DESCRIPTIONS
2389
2421
  -q, --quiet suppress process output and only display a machine-readable summary.
2390
2422
 
2391
2423
  This flag controls if you want to see the process output or only a summary. When using mw non-interactively (e.g. in
2392
2424
  scripts), you can use this flag to easily get the IDs of created resources for further processing.
2425
+
2426
+ --ssh-user=<value> override the SSH user to connect with; if omitted, your own user will be used
2427
+
2428
+ This flag can be used to override the SSH user that is used for a connection; be default, your own personal user
2429
+ will be used for this.
2430
+
2431
+ You can also set this value by setting the MITTWALD_SSH_USER environment variable.
2393
2432
  ```
2394
2433
 
2395
2434
  ## `mw database mysql shell DATABASE-ID`
@@ -9,6 +9,7 @@ export declare class Download extends ExecRenderBaseCommand<typeof Download, voi
9
9
  "dry-run": import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
10
10
  delete: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
11
11
  target: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
12
+ "ssh-user": import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
12
13
  quiet: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
13
14
  };
14
15
  protected exec(): Promise<void>;
@@ -7,6 +7,7 @@ import { Success } from "../../rendering/react/components/Success.js";
7
7
  import { spawn } from "child_process";
8
8
  import { hasBinary } from "../../lib/hasbin.js";
9
9
  import { getSSHConnectionForAppInstallation } from "../../lib/ssh/appinstall.js";
10
+ import { sshConnectionFlags } from "../../lib/ssh/flags.js";
10
11
  export class Download extends ExecRenderBaseCommand {
11
12
  static description = "Download the filesystem of an app within a project to your local machine";
12
13
  static args = {
@@ -14,6 +15,7 @@ export class Download extends ExecRenderBaseCommand {
14
15
  };
15
16
  static flags = {
16
17
  ...processFlags,
18
+ ...sshConnectionFlags,
17
19
  "dry-run": Flags.boolean({
18
20
  description: "do not actually download the app installation",
19
21
  default: false,
@@ -30,10 +32,10 @@ export class Download extends ExecRenderBaseCommand {
30
32
  };
31
33
  async exec() {
32
34
  const appInstallationId = await this.withAppInstallationId(Download);
33
- const { "dry-run": dryRun, target, delete: deleteLocal } = this.flags;
35
+ const { "dry-run": dryRun, target, delete: deleteLocal, "ssh-user": sshUser, } = this.flags;
34
36
  const p = makeProcessRenderer(this.flags, "Downloading app installation");
35
37
  const { host, user, directory } = await p.runStep("getting connection data", async () => {
36
- return getSSHConnectionForAppInstallation(this.apiClient, appInstallationId);
38
+ return getSSHConnectionForAppInstallation(this.apiClient, appInstallationId, sshUser);
37
39
  });
38
40
  await p.runStep("check if rsync is installed", async () => {
39
41
  if (!(await hasBinary("rsync"))) {
@@ -8,6 +8,7 @@ export default class Ssh extends ExtendedBaseCommand<typeof Ssh> {
8
8
  cd: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
9
9
  info: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
10
10
  test: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
11
+ "ssh-user": import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
11
12
  };
12
13
  run(): Promise<void>;
13
14
  }
@@ -3,10 +3,12 @@ import { appInstallationArgs } from "../../lib/app/flags.js";
3
3
  import { Flags } from "@oclif/core";
4
4
  import { ExtendedBaseCommand } from "../../ExtendedBaseCommand.js";
5
5
  import { getSSHConnectionForAppInstallation } from "../../lib/ssh/appinstall.js";
6
+ import { sshConnectionFlags } from "../../lib/ssh/flags.js";
6
7
  export default class Ssh extends ExtendedBaseCommand {
7
8
  static description = "Connect to an app via SSH";
8
9
  static args = { ...appInstallationArgs };
9
10
  static flags = {
11
+ ...sshConnectionFlags,
10
12
  cd: Flags.boolean({
11
13
  summary: "change to installation path after connecting",
12
14
  default: true,
@@ -22,7 +24,7 @@ export default class Ssh extends ExtendedBaseCommand {
22
24
  async run() {
23
25
  const { flags } = await this.parse(Ssh);
24
26
  const appInstallationId = await this.withAppInstallationId(Ssh);
25
- const { host, user, directory } = await getSSHConnectionForAppInstallation(this.apiClient, appInstallationId);
27
+ const { host, user, directory } = await getSSHConnectionForAppInstallation(this.apiClient, appInstallationId, flags["ssh-user"]);
26
28
  if (flags.info) {
27
29
  this.log("hostname: %o", host);
28
30
  this.log("username: %o", user);
@@ -5,6 +5,8 @@ export declare class Dump extends ExecRenderBaseCommand<typeof Dump, Record<stri
5
5
  static flags: {
6
6
  "temporary-user": import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
7
7
  output: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
8
+ gzip: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
9
+ "ssh-user": import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
8
10
  "mysql-password": import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
9
11
  quiet: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
10
12
  };
@@ -12,11 +12,14 @@ import { assertStatus } from "@mittwald/api-client";
12
12
  import { randomBytes } from "crypto";
13
13
  import { executeViaSSH } from "../../../lib/ssh/exec.js";
14
14
  import assertSuccess from "../../../lib/assert_success.js";
15
+ import shellEscape from "shell-escape";
16
+ import { sshConnectionFlags } from "../../../lib/ssh/flags.js";
15
17
  export class Dump extends ExecRenderBaseCommand {
16
18
  static summary = "Create a dump of a MySQL database";
17
19
  static flags = {
18
20
  ...processFlags,
19
21
  ...mysqlConnectionFlags,
22
+ ...sshConnectionFlags,
20
23
  "temporary-user": Flags.boolean({
21
24
  summary: "create a temporary user for the dump",
22
25
  description: "Create a temporary user for the dump. This user will be deleted after the dump has been created. This is useful if you want to dump a database that is not accessible from the outside.\n\nIf this flag is disabled, you will need to specify the password of the default user; either via the --mysql-password flag or via the MYSQL_PWD environment variable.",
@@ -30,6 +33,13 @@ export class Dump extends ExecRenderBaseCommand {
30
33
  description: 'The output file to write the dump to. You can specify "-" or "/dev/stdout" to write the dump directly to STDOUT; in this case, you might want to use the --quiet/-q flag to supress all other output, so that you can pipe the mysqldump for further processing.',
31
34
  required: true,
32
35
  }),
36
+ gzip: Flags.boolean({
37
+ summary: "compress the dump with gzip",
38
+ aliases: ["gz"],
39
+ description: "Compress the dump with gzip. This is useful for large databases, as it can significantly reduce the size of the dump.",
40
+ default: false,
41
+ required: false,
42
+ }),
33
43
  };
34
44
  static args = { ...mysqlArgs };
35
45
  async exec() {
@@ -49,7 +59,14 @@ export class Dump extends ExecRenderBaseCommand {
49
59
  }
50
60
  const { project } = connectionDetails;
51
61
  const mysqldumpArgs = buildMySqlDumpArgs(connectionDetails);
52
- await p.runStep(_jsxs(Text, { children: ["starting mysqldump via SSH on project ", _jsx(Value, { children: project.shortId })] }), () => executeViaSSH(this.apiClient, { projectId: connectionDetails.project.id }, "mysqldump", mysqldumpArgs, this.getOutputStream()));
62
+ let cmd = { command: "mysqldump", args: mysqldumpArgs };
63
+ if (this.flags.gzip) {
64
+ const escapedArgs = shellEscape(mysqldumpArgs);
65
+ cmd = {
66
+ shell: `set -e -o pipefail > /dev/null ; mysqldump ${escapedArgs} | gzip`,
67
+ };
68
+ }
69
+ await p.runStep(_jsxs(Text, { children: ["starting mysqldump via SSH on project ", _jsx(Value, { children: project.shortId })] }), () => executeViaSSH(this.apiClient, this.flags["ssh-user"], { projectId: connectionDetails.project.id }, cmd, this.getOutputStream()));
53
70
  await p.complete(_jsx(DumpSuccess, { database: connectionDetails.database, output: this.flags.output }));
54
71
  return {};
55
72
  }
@@ -4,6 +4,7 @@ export declare class PortForward extends ExecRenderBaseCommand<typeof PortForwar
4
4
  static summary: string;
5
5
  static flags: {
6
6
  port: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<number, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
7
+ "ssh-user": import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
7
8
  quiet: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
8
9
  };
9
10
  static args: {
@@ -7,10 +7,12 @@ import { mysqlArgs, withMySQLId } from "../../../lib/database/mysql/flags.js";
7
7
  import { getConnectionDetails } from "../../../lib/database/mysql/connect.js";
8
8
  import { Value } from "../../../rendering/react/components/Value.js";
9
9
  import { Flags } from "@oclif/core";
10
+ import { sshConnectionFlags } from "../../../lib/ssh/flags.js";
10
11
  export class PortForward extends ExecRenderBaseCommand {
11
12
  static summary = "Forward the TCP port of a MySQL database to a local port";
12
13
  static flags = {
13
14
  ...processFlags,
15
+ ...sshConnectionFlags,
14
16
  port: Flags.integer({
15
17
  summary: "The local TCP port to forward to",
16
18
  default: 3306,
@@ -20,7 +22,7 @@ export class PortForward extends ExecRenderBaseCommand {
20
22
  async exec() {
21
23
  const databaseId = await withMySQLId(this.apiClient, this.flags, this.args, this.config);
22
24
  const p = makeProcessRenderer(this.flags, "Port-forwarding a MySQL database");
23
- const { sshUser, sshHost, hostname, database } = await getConnectionDetails(this.apiClient, databaseId, p);
25
+ const { sshUser, sshHost, hostname, database } = await getConnectionDetails(this.apiClient, databaseId, this.flags["ssh-user"], p);
24
26
  const { port } = this.flags;
25
27
  p.complete(_jsxs(Text, { children: ["Forwarding MySQL database ", _jsx(Value, { children: database }), " to local port", " ", _jsx(Value, { children: port }), ". Use CTRL+C to cancel."] }));
26
28
  const sshArgs = [
@@ -3,6 +3,7 @@ import { MittwaldAPIV2, MittwaldAPIV2Client } from "@mittwald/api-client";
3
3
  export declare function getConnectionDetailsWithPassword(apiClient: MittwaldAPIV2Client, databaseId: string, p: ProcessRenderer, flags: {
4
4
  "mysql-password": string | undefined;
5
5
  "temporary-user"?: boolean;
6
+ "ssh-user"?: string;
6
7
  }): Promise<{
7
8
  password: string;
8
9
  hostname: string;
@@ -12,7 +13,7 @@ export declare function getConnectionDetailsWithPassword(apiClient: MittwaldAPIV
12
13
  sshUser: string;
13
14
  project: MittwaldAPIV2.Components.Schemas.ProjectProject;
14
15
  }>;
15
- export declare function getConnectionDetails(apiClient: MittwaldAPIV2Client, databaseId: string, p: ProcessRenderer): Promise<{
16
+ export declare function getConnectionDetails(apiClient: MittwaldAPIV2Client, databaseId: string, sshUser: string | undefined, p: ProcessRenderer): Promise<{
16
17
  hostname: string;
17
18
  database: string;
18
19
  user: string;
@@ -1,23 +1,25 @@
1
1
  import { assertStatus } from "@mittwald/api-client-commons";
2
- import { getProject, getUser } from "../common.js";
2
+ import { getProject } from "../common.js";
3
+ import { getSSHConnectionForProject } from "../../ssh/project.js";
3
4
  export async function getConnectionDetailsWithPassword(apiClient, databaseId, p, flags) {
4
5
  const password = flags["temporary-user"] ? "" : await getPassword(p, flags);
6
+ const sshUser = flags["ssh-user"];
5
7
  return {
6
- ...(await getConnectionDetails(apiClient, databaseId, p)),
8
+ ...(await getConnectionDetails(apiClient, databaseId, sshUser, p)),
7
9
  password,
8
10
  };
9
11
  }
10
- export async function getConnectionDetails(apiClient, databaseId, p) {
12
+ export async function getConnectionDetails(apiClient, databaseId, sshUser, p) {
11
13
  const database = await getDatabase(apiClient, p, databaseId);
12
14
  const databaseUser = await getDatabaseUser(apiClient, p, databaseId);
13
15
  const project = await getProject(apiClient, p, database);
14
- const user = await getUser(apiClient, p);
16
+ const sshConnectionData = await getSSHConnectionForProject(apiClient, database.projectId, sshUser);
15
17
  return {
16
18
  hostname: database.hostname,
17
19
  database: database.name,
18
20
  user: databaseUser.name,
19
- sshHost: `ssh.${project.clusterID}.${project.clusterDomain}`,
20
- sshUser: `${user.email}@${project.shortId}`,
21
+ sshHost: sshConnectionData.host,
22
+ sshUser: sshConnectionData.user,
21
23
  project,
22
24
  };
23
25
  }
@@ -1,3 +1,3 @@
1
1
  import { MittwaldAPIV2Client } from "@mittwald/api-client";
2
2
  import { SSHConnectionData } from "./types.js";
3
- export declare function getSSHConnectionForAppInstallation(client: MittwaldAPIV2Client, appInstallationId: string): Promise<SSHConnectionData>;
3
+ export declare function getSSHConnectionForAppInstallation(client: MittwaldAPIV2Client, appInstallationId: string, sshUser: string | undefined): Promise<SSHConnectionData>;
@@ -1,6 +1,6 @@
1
1
  import { assertStatus } from "@mittwald/api-client";
2
2
  import path from "path";
3
- export async function getSSHConnectionForAppInstallation(client, appInstallationId) {
3
+ export async function getSSHConnectionForAppInstallation(client, appInstallationId, sshUser) {
4
4
  const appInstallationResponse = await client.app.getAppinstallation({
5
5
  appInstallationId,
6
6
  });
@@ -12,10 +12,13 @@ export async function getSSHConnectionForAppInstallation(client, appInstallation
12
12
  projectId: appInstallationResponse.data.projectId,
13
13
  });
14
14
  assertStatus(projectResponse, 200);
15
- const userResponse = await client.user.getOwnAccount();
16
- assertStatus(userResponse, 200);
15
+ if (sshUser === undefined) {
16
+ const userResponse = await client.user.getOwnAccount();
17
+ assertStatus(userResponse, 200);
18
+ sshUser = userResponse.data.email;
19
+ }
17
20
  const host = `ssh.${projectResponse.data.clusterID}.${projectResponse.data.clusterDomain}`;
18
- const user = `${userResponse.data.email}@${appInstallationResponse.data.shortId}`;
21
+ const user = `${sshUser}@${appInstallationResponse.data.shortId}`;
19
22
  const directory = path.join(projectResponse.data.directories["Web"], appInstallationResponse.data.installationPath);
20
23
  return {
21
24
  host,
@@ -5,4 +5,10 @@ export type RunTarget = {
5
5
  } | {
6
6
  projectId: string;
7
7
  };
8
- export declare function executeViaSSH(client: MittwaldAPIV2Client, target: RunTarget, command: string, args: string[], output: NodeJS.WritableStream): Promise<void>;
8
+ export type RunCommand = {
9
+ command: string;
10
+ args: string[];
11
+ } | {
12
+ shell: string;
13
+ };
14
+ export declare function executeViaSSH(client: MittwaldAPIV2Client, sshUser: string | undefined, target: RunTarget, command: RunCommand, output: NodeJS.WritableStream): Promise<void>;
@@ -1,9 +1,12 @@
1
1
  import cp from "child_process";
2
2
  import { getSSHConnectionForAppInstallation } from "./appinstall.js";
3
3
  import { getSSHConnectionForProject } from "./project.js";
4
- export async function executeViaSSH(client, target, command, args, output) {
5
- const { user, host } = await connectionDataForTarget(client, target);
6
- const sshArgs = ["-l", user, "-T", host, command, ...args];
4
+ export async function executeViaSSH(client, sshUser, target, command, output) {
5
+ const { user, host } = await connectionDataForTarget(client, target, sshUser);
6
+ const sshCommandArgs = "shell" in command
7
+ ? ["bash", "-c", command.shell]
8
+ : [command.command, ...command.args];
9
+ const sshArgs = ["-l", user, "-T", host, ...sshCommandArgs];
7
10
  const ssh = cp.spawn("ssh", sshArgs, {
8
11
  stdio: ["ignore", "pipe", "pipe"],
9
12
  });
@@ -31,11 +34,11 @@ export async function executeViaSSH(client, target, command, args, output) {
31
34
  });
32
35
  });
33
36
  }
34
- async function connectionDataForTarget(client, target) {
37
+ async function connectionDataForTarget(client, target, sshUser) {
35
38
  if ("appInstallationId" in target) {
36
- return getSSHConnectionForAppInstallation(client, target.appInstallationId);
39
+ return getSSHConnectionForAppInstallation(client, target.appInstallationId, sshUser);
37
40
  }
38
41
  else {
39
- return getSSHConnectionForProject(client, target.projectId);
42
+ return getSSHConnectionForProject(client, target.projectId, sshUser);
40
43
  }
41
44
  }
@@ -0,0 +1,3 @@
1
+ export declare const sshConnectionFlags: {
2
+ "ssh-user": import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
3
+ };
@@ -0,0 +1,13 @@
1
+ import { Flags } from "@oclif/core";
2
+ export const sshConnectionFlags = {
3
+ "ssh-user": Flags.string({
4
+ summary: "override the SSH user to connect with; if omitted, your own user will be used",
5
+ description: "This flag can be used to override the SSH user that is used for a " +
6
+ "connection; be default, your own personal user will be used for this." +
7
+ "\n\n" +
8
+ "You can also set this value by setting the MITTWALD_SSH_USER environment variable.",
9
+ required: false,
10
+ default: undefined,
11
+ env: "MITTWALD_SSH_USER",
12
+ }),
13
+ };
@@ -1,3 +1,3 @@
1
1
  import { MittwaldAPIV2Client } from "@mittwald/api-client";
2
2
  import { SSHConnectionData } from "./types.js";
3
- export declare function getSSHConnectionForProject(client: MittwaldAPIV2Client, projectId: string): Promise<SSHConnectionData>;
3
+ export declare function getSSHConnectionForProject(client: MittwaldAPIV2Client, projectId: string, sshUser: string | undefined): Promise<SSHConnectionData>;
@@ -1,11 +1,14 @@
1
1
  import { assertStatus } from "@mittwald/api-client";
2
- export async function getSSHConnectionForProject(client, projectId) {
2
+ export async function getSSHConnectionForProject(client, projectId, sshUser) {
3
3
  const projectResponse = await client.project.getProject({ projectId });
4
4
  assertStatus(projectResponse, 200);
5
- const userResponse = await client.user.getOwnAccount();
6
- assertStatus(userResponse, 200);
5
+ if (sshUser === undefined) {
6
+ const userResponse = await client.user.getOwnAccount();
7
+ assertStatus(userResponse, 200);
8
+ sshUser = userResponse.data.email;
9
+ }
7
10
  const host = `ssh.${projectResponse.data.clusterID}.${projectResponse.data.clusterDomain}`;
8
- const user = `${userResponse.data.email}@${projectResponse.data.shortId}`;
11
+ const user = `${sshUser}@${projectResponse.data.shortId}`;
9
12
  const directory = projectResponse.data.directories["Web"];
10
13
  return {
11
14
  host,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mittwald/cli",
3
- "version": "1.0.0-alpha.35",
3
+ "version": "1.0.0-alpha.36",
4
4
  "description": "Hand-crafted CLI for the mittwald API",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -71,6 +71,7 @@
71
71
  "pretty-bytes": "^6.1.0",
72
72
  "react": "^18.2.0",
73
73
  "semver": "^7.5.4",
74
+ "shell-escape": "^0.2.0",
74
75
  "tempfile": "^5.0.0"
75
76
  },
76
77
  "devDependencies": {
@@ -84,6 +85,7 @@
84
85
  "@types/pretty-bytes": "^5.2.0",
85
86
  "@types/react": "^18",
86
87
  "@types/semver": "^7.5.0",
88
+ "@types/shell-escape": "^0.2.3",
87
89
  "@typescript-eslint/eslint-plugin": "^6.9.1",
88
90
  "@typescript-eslint/parser": "^6.10.0",
89
91
  "@yarnpkg/pnpify": "^4.0.0-rc.48",