@mittwald/cli 1.1.0 → 1.2.0
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.
|
@@ -11,10 +11,15 @@ export declare class Download extends ExecRenderBaseCommand<typeof Download, voi
|
|
|
11
11
|
exclude: import("@oclif/core/interfaces").OptionFlag<string[], import("@oclif/core/interfaces").CustomOptions>;
|
|
12
12
|
"dry-run": import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
13
13
|
delete: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
14
|
+
"remote-sub-directory": import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
15
|
"ssh-user": import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
16
|
"ssh-identity-file": import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
16
17
|
quiet: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
17
18
|
};
|
|
19
|
+
static examples: {
|
|
20
|
+
description: string;
|
|
21
|
+
command: string;
|
|
22
|
+
}[];
|
|
18
23
|
protected exec(): Promise<void>;
|
|
19
24
|
protected render(): ReactNode;
|
|
20
25
|
}
|
|
@@ -8,7 +8,7 @@ import { getSSHConnectionForAppInstallation } from "../../lib/resources/ssh/appi
|
|
|
8
8
|
import { spawnInProcess } from "../../rendering/process/process_exec.js";
|
|
9
9
|
import { sshConnectionFlags } from "../../lib/resources/ssh/flags.js";
|
|
10
10
|
import { sshUsageDocumentation } from "../../lib/resources/ssh/doc.js";
|
|
11
|
-
import { appInstallationSyncFlags, appInstallationSyncFlagsToRsyncFlags, filterFileDocumentation, filterFileToRsyncFlagsIfPresent, } from "../../lib/resources/app/sync.js";
|
|
11
|
+
import { appInstallationSyncFlags, appInstallationSyncFlagsToRsyncFlags, buildRsyncConnectionString, filterFileDocumentation, filterFileToRsyncFlagsIfPresent, } from "../../lib/resources/app/sync.js";
|
|
12
12
|
import { hasBinaryInPath } from "../../lib/util/fs/hasBinaryInPath.js";
|
|
13
13
|
export class Download extends ExecRenderBaseCommand {
|
|
14
14
|
static summary = "Download the filesystem of an app within a project to your local machine";
|
|
@@ -30,11 +30,21 @@ export class Download extends ExecRenderBaseCommand {
|
|
|
30
30
|
exists: false,
|
|
31
31
|
}),
|
|
32
32
|
};
|
|
33
|
+
static examples = [
|
|
34
|
+
{
|
|
35
|
+
description: "Download entire app to current working directory",
|
|
36
|
+
command: "$ <%= config.bin %> <%= command.id %> .",
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
description: "Download only shared dir from a deployer-managed app",
|
|
40
|
+
command: "<%= config.bin %> <%= command.id %> --remote-sub-directory=shared .",
|
|
41
|
+
},
|
|
42
|
+
];
|
|
33
43
|
async exec() {
|
|
34
44
|
const appInstallationId = await this.withAppInstallationId(Download);
|
|
35
45
|
const { "dry-run": dryRun, target, "ssh-user": sshUser } = this.flags;
|
|
36
46
|
const p = makeProcessRenderer(this.flags, "Downloading app installation");
|
|
37
|
-
const
|
|
47
|
+
const connectionData = await p.runStep("getting connection data", async () => {
|
|
38
48
|
return getSSHConnectionForAppInstallation(this.apiClient, appInstallationId, sshUser);
|
|
39
49
|
});
|
|
40
50
|
await p.runStep("check if rsync is installed", async () => {
|
|
@@ -42,11 +52,12 @@ export class Download extends ExecRenderBaseCommand {
|
|
|
42
52
|
throw new Error("this command requires rsync to be installed");
|
|
43
53
|
}
|
|
44
54
|
});
|
|
55
|
+
const rsyncHost = buildRsyncConnectionString(connectionData, this.flags);
|
|
45
56
|
const rsyncOpts = [
|
|
46
57
|
...appInstallationSyncFlagsToRsyncFlags(this.flags),
|
|
47
58
|
...(await filterFileToRsyncFlagsIfPresent(target)),
|
|
48
59
|
];
|
|
49
|
-
await spawnInProcess(p, "downloading app installation" + (dryRun ? " (dry-run)" : ""), "rsync", [...rsyncOpts,
|
|
60
|
+
await spawnInProcess(p, "downloading app installation" + (dryRun ? " (dry-run)" : ""), "rsync", [...rsyncOpts, rsyncHost, target]);
|
|
50
61
|
if (dryRun) {
|
|
51
62
|
await p.complete(_jsx(Success, { children: "App would (probably) have successfully been downloaded. \uD83D\uDE42" }));
|
|
52
63
|
}
|
|
@@ -11,6 +11,7 @@ export declare class Upload extends ExecRenderBaseCommand<typeof Upload, void> {
|
|
|
11
11
|
exclude: import("@oclif/core/interfaces").OptionFlag<string[], import("@oclif/core/interfaces").CustomOptions>;
|
|
12
12
|
"dry-run": import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
13
13
|
delete: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
14
|
+
"remote-sub-directory": import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
15
|
"ssh-user": import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
16
|
"ssh-identity-file": import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
16
17
|
quiet: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
@@ -8,7 +8,7 @@ import { getSSHConnectionForAppInstallation } from "../../lib/resources/ssh/appi
|
|
|
8
8
|
import { spawnInProcess } from "../../rendering/process/process_exec.js";
|
|
9
9
|
import { sshConnectionFlags } from "../../lib/resources/ssh/flags.js";
|
|
10
10
|
import { sshUsageDocumentation } from "../../lib/resources/ssh/doc.js";
|
|
11
|
-
import { appInstallationSyncFlags, appInstallationSyncFlagsToRsyncFlags, filterFileDocumentation, filterFileToRsyncFlagsIfPresent, } from "../../lib/resources/app/sync.js";
|
|
11
|
+
import { appInstallationSyncFlags, appInstallationSyncFlagsToRsyncFlags, buildRsyncConnectionString, filterFileDocumentation, filterFileToRsyncFlagsIfPresent, } from "../../lib/resources/app/sync.js";
|
|
12
12
|
import { hasBinaryInPath } from "../../lib/util/fs/hasBinaryInPath.js";
|
|
13
13
|
export class Upload extends ExecRenderBaseCommand {
|
|
14
14
|
static summary = "Upload the filesystem of an app to a project";
|
|
@@ -36,7 +36,7 @@ export class Upload extends ExecRenderBaseCommand {
|
|
|
36
36
|
const appInstallationId = await this.withAppInstallationId(Upload);
|
|
37
37
|
const { "dry-run": dryRun, source, "ssh-user": sshUser } = this.flags;
|
|
38
38
|
const p = makeProcessRenderer(this.flags, "Uploading app installation");
|
|
39
|
-
const
|
|
39
|
+
const connectionData = await p.runStep("getting connection data", async () => {
|
|
40
40
|
return getSSHConnectionForAppInstallation(this.apiClient, appInstallationId, sshUser);
|
|
41
41
|
});
|
|
42
42
|
await p.runStep("check if rsync is installed", async () => {
|
|
@@ -44,11 +44,12 @@ export class Upload extends ExecRenderBaseCommand {
|
|
|
44
44
|
throw new Error("this command requires rsync to be installed");
|
|
45
45
|
}
|
|
46
46
|
});
|
|
47
|
+
const rsyncHost = buildRsyncConnectionString(connectionData, this.flags);
|
|
47
48
|
const rsyncOpts = [
|
|
48
49
|
...appInstallationSyncFlagsToRsyncFlags(this.flags),
|
|
49
50
|
...(await filterFileToRsyncFlagsIfPresent(source)),
|
|
50
51
|
];
|
|
51
|
-
await spawnInProcess(p, "uploading app installation" + (dryRun ? " (dry-run)" : ""), "rsync", [...rsyncOpts, source,
|
|
52
|
+
await spawnInProcess(p, "uploading app installation" + (dryRun ? " (dry-run)" : ""), "rsync", [...rsyncOpts, source, rsyncHost]);
|
|
52
53
|
await p.complete(_jsx(UploadSuccess, { dryRun: dryRun }));
|
|
53
54
|
}
|
|
54
55
|
render() {
|
|
@@ -1,15 +1,29 @@
|
|
|
1
1
|
import { SSHConnectionFlags } from "../ssh/flags.js";
|
|
2
|
+
import { SSHConnectionData } from "../ssh/types.js";
|
|
2
3
|
export declare const defaultRsyncFilterFile = ".mw-rsync-filter";
|
|
3
4
|
export interface AppInstallationSyncFlags {
|
|
4
5
|
exclude: string[];
|
|
5
6
|
"dry-run": boolean;
|
|
6
7
|
delete: boolean;
|
|
8
|
+
"sub-directory"?: string;
|
|
7
9
|
}
|
|
8
10
|
export declare const appInstallationSyncFlags: (direction: "upload" | "download") => {
|
|
9
11
|
exclude: import("@oclif/core/interfaces").OptionFlag<string[], import("@oclif/core/interfaces").CustomOptions>;
|
|
10
12
|
"dry-run": import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
13
|
delete: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
14
|
+
"remote-sub-directory": import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
15
|
};
|
|
13
16
|
export declare const filterFileDocumentation: string;
|
|
14
17
|
export declare function filterFileToRsyncFlagsIfPresent(targetDir: string, filterFile?: string): Promise<string[]>;
|
|
18
|
+
/**
|
|
19
|
+
* Build the rsync connection string for the given SSH connection data and flag
|
|
20
|
+
* inputs.
|
|
21
|
+
*
|
|
22
|
+
* @param host Remote SSH hostname
|
|
23
|
+
* @param directory Remove base directory of the app installation
|
|
24
|
+
* @param user Remote SSH user
|
|
25
|
+
* @param subDirectory Optional sub-directory within the app installation to
|
|
26
|
+
* sync
|
|
27
|
+
*/
|
|
28
|
+
export declare function buildRsyncConnectionString({ host, directory, user }: SSHConnectionData, { "sub-directory": subDirectory }: AppInstallationSyncFlags): string;
|
|
15
29
|
export declare function appInstallationSyncFlagsToRsyncFlags(f: AppInstallationSyncFlags & SSHConnectionFlags): string[];
|
|
@@ -18,6 +18,13 @@ export const appInstallationSyncFlags = (direction) => ({
|
|
|
18
18
|
: "delete remote files that are not present locally",
|
|
19
19
|
default: false,
|
|
20
20
|
}),
|
|
21
|
+
"remote-sub-directory": Flags.string({
|
|
22
|
+
summary: `specify a sub-directory within the app installation to ${direction}`,
|
|
23
|
+
description: `This is particularly useful when you only want to ${direction} a specific sub-directory of the app installation, ` +
|
|
24
|
+
"for example when you are using a deployment tool that manages the app installation directory itself, " +
|
|
25
|
+
`and you only want to ${direction} exempt files, like environment specific configuration files or user data. ` +
|
|
26
|
+
`For example, if you want to ${direction} ${direction === "upload" ? "to" : "from"} "/html/my-app-XXXXX/config", set "--remote-sub-directory=config".`,
|
|
27
|
+
}),
|
|
21
28
|
});
|
|
22
29
|
export const filterFileDocumentation = `This command will also look for a file named ${defaultRsyncFilterFile} in the current ` +
|
|
23
30
|
"directory and use it as a filter file for rsync. Have a look at " +
|
|
@@ -30,6 +37,22 @@ export async function filterFileToRsyncFlagsIfPresent(targetDir, filterFile = de
|
|
|
30
37
|
}
|
|
31
38
|
return [];
|
|
32
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* Build the rsync connection string for the given SSH connection data and flag
|
|
42
|
+
* inputs.
|
|
43
|
+
*
|
|
44
|
+
* @param host Remote SSH hostname
|
|
45
|
+
* @param directory Remove base directory of the app installation
|
|
46
|
+
* @param user Remote SSH user
|
|
47
|
+
* @param subDirectory Optional sub-directory within the app installation to
|
|
48
|
+
* sync
|
|
49
|
+
*/
|
|
50
|
+
export function buildRsyncConnectionString({ host, directory, user }, { "sub-directory": subDirectory }) {
|
|
51
|
+
if (subDirectory) {
|
|
52
|
+
directory = path.join(directory, subDirectory).replace(/\/$/, "");
|
|
53
|
+
}
|
|
54
|
+
return `${user}@${host}:${directory}/`;
|
|
55
|
+
}
|
|
33
56
|
export function appInstallationSyncFlagsToRsyncFlags(f) {
|
|
34
57
|
const { "dry-run": dryRun, "ssh-identity-file": sshIdentityFile, exclude, } = f;
|
|
35
58
|
const rsyncOpts = [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mittwald/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Hand-crafted CLI for the mittwald API",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"@oclif/plugin-warn-if-update-available": "^3.0.2",
|
|
52
52
|
"axios-retry": "^4.0.0",
|
|
53
53
|
"chalk": "^5.3.0",
|
|
54
|
-
"date-fns": "^
|
|
54
|
+
"date-fns": "^4.0.0",
|
|
55
55
|
"ink": "^5.0.1",
|
|
56
56
|
"ink-link": "^4.0.0",
|
|
57
57
|
"ink-text-input": "^6.0.0",
|