@mittwald/cli 1.10.0 → 1.11.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.
- package/bin/run.js +4 -1
- package/dist/commands/app/create/node.d.ts +1 -3
- package/dist/commands/app/create/php-worker.d.ts +1 -3
- package/dist/commands/app/create/php.d.ts +1 -3
- package/dist/commands/app/create/python.d.ts +1 -3
- package/dist/commands/app/create/static.d.ts +1 -3
- package/dist/commands/app/dependency/update.js +2 -4
- package/dist/commands/app/install/contao.d.ts +1 -3
- package/dist/commands/app/install/joomla.d.ts +1 -3
- package/dist/commands/app/install/matomo.d.ts +1 -3
- package/dist/commands/app/install/nextcloud.d.ts +1 -3
- package/dist/commands/app/install/shopware5.d.ts +1 -3
- package/dist/commands/app/install/shopware6.d.ts +1 -3
- package/dist/commands/app/install/typo3.d.ts +1 -3
- package/dist/commands/app/install/wordpress.d.ts +1 -3
- package/dist/commands/app/ssh.d.ts +1 -0
- package/dist/commands/app/ssh.js +15 -1
- package/dist/commands/app/update.js +4 -8
- package/dist/commands/app/upgrade.js +9 -9
- package/dist/commands/backup/create.js +1 -2
- package/dist/commands/backup/download.js +4 -5
- package/dist/commands/container/run.d.ts +4 -2
- package/dist/commands/container/run.js +7 -6
- package/dist/commands/database/mysql/create.js +1 -2
- package/dist/commands/database/mysql/dump.js +1 -2
- package/dist/commands/database/mysql/import.js +1 -2
- package/dist/commands/ddev/init.js +2 -6
- package/dist/commands/extension/install.js +1 -3
- package/dist/commands/login/reset.js +1 -2
- package/dist/commands/mail/address/create.js +1 -4
- package/dist/commands/mail/deliverybox/create.js +1 -4
- package/dist/commands/project/create.js +4 -6
- package/dist/commands/registry/create.js +1 -2
- package/dist/commands/registry/update.js +1 -2
- package/dist/commands/user/api-token/create.js +1 -1
- package/dist/commands/user/ssh-key/create.js +3 -8
- package/dist/commands/user/ssh-key/import.js +2 -3
- package/dist/lib/basecommands/DeleteBaseCommand.js +4 -4
- package/dist/lib/ddev/init_assert.js +1 -6
- package/dist/lib/ddev/init_database.js +1 -7
- package/dist/lib/ddev/init_projecttype.js +2 -11
- package/dist/lib/intellij/config.d.ts +5 -0
- package/dist/lib/intellij/config.js +295 -0
- package/dist/lib/intellij/config.test.d.ts +1 -0
- package/dist/lib/intellij/config.test.js +262 -0
- package/dist/lib/intellij/config_xml_types.d.ts +72 -0
- package/dist/lib/intellij/config_xml_types.js +2 -0
- package/dist/lib/resources/app/Installer.d.ts +6 -2
- package/dist/lib/resources/app/Installer.js +13 -6
- package/dist/lib/resources/app/flags.js +9 -12
- package/dist/lib/resources/app/install.d.ts +2 -1
- package/dist/lib/resources/app/install.js +3 -1
- package/dist/lib/resources/app/versions.js +1 -4
- package/dist/lib/resources/app/wait.js +1 -3
- package/dist/lib/resources/mail/commons.js +1 -4
- package/dist/lib/resources/ssh/appinstall.d.ts +3 -1
- package/dist/lib/resources/ssh/appinstall.js +1 -0
- package/dist/rendering/process/components/ProcessStateIcon.js +1 -1
- package/dist/rendering/process/process.d.ts +16 -16
- package/dist/rendering/process/process_exec.d.ts +1 -2
- package/dist/rendering/process/process_fancy.d.ts +9 -9
- package/dist/rendering/process/process_flags.js +4 -0
- package/dist/rendering/process/process_quiet.d.ts +3 -4
- package/dist/rendering/process/process_simple.d.ts +26 -0
- package/dist/rendering/process/process_simple.js +143 -0
- package/dist/rendering/process/process_simple.test.d.ts +1 -0
- package/dist/rendering/process/process_simple.test.js +149 -0
- package/dist/rendering/react/components/AppInstallation/AppBackendAccessHints.d.ts +15 -0
- package/dist/rendering/react/components/AppInstallation/AppBackendAccessHints.js +13 -0
- package/dist/rendering/react/components/AppInstallation/AppDomainConnectionHints.d.ts +12 -0
- package/dist/rendering/react/components/AppInstallation/AppDomainConnectionHints.js +21 -0
- package/dist/rendering/react/components/AppInstallation/AppManagementCommands.d.ts +12 -0
- package/dist/rendering/react/components/AppInstallation/AppManagementCommands.js +17 -0
- package/dist/rendering/react/components/AppInstallation/AppUsageHints.d.ts +17 -0
- package/dist/rendering/react/components/AppInstallation/AppUsageHints.js +23 -0
- package/dist/rendering/react/components/Container/CommandHint.d.ts +14 -0
- package/dist/rendering/react/components/Container/CommandHint.js +13 -0
- package/dist/rendering/react/components/Container/ContainerManagementCommands.d.ts +22 -0
- package/dist/rendering/react/components/Container/ContainerManagementCommands.js +23 -0
- package/dist/rendering/react/components/Container/ContainerUsageHints.d.ts +12 -0
- package/dist/rendering/react/components/Container/ContainerUsageHints.js +35 -0
- package/dist/rendering/react/components/Container/DomainConnectionHints.d.ts +12 -0
- package/dist/rendering/react/components/Container/DomainConnectionHints.js +21 -0
- package/dist/rendering/react/components/Container/InternalConnectionHints.d.ts +12 -0
- package/dist/rendering/react/components/Container/InternalConnectionHints.js +12 -0
- package/dist/rendering/react/components/Container/NoPortsUsageHints.d.ts +12 -0
- package/dist/rendering/react/components/Container/NoPortsUsageHints.js +12 -0
- package/dist/rendering/react/components/Container/PortConnectionHints.d.ts +13 -0
- package/dist/rendering/react/components/Container/PortConnectionHints.js +11 -0
- package/dist/rendering/react/components/Container/PortForwardingHints.d.ts +12 -0
- package/dist/rendering/react/components/Container/PortForwardingHints.js +18 -0
- package/dist/rendering/react/components/Container/types.d.ts +4 -0
- package/dist/rendering/react/components/Container/types.js +1 -0
- package/dist/rendering/react/components/Error/ErrorBox.d.ts +1 -1
- package/dist/rendering/react/components/Error/ErrorBox.js +3 -4
- package/dist/rendering/react/components/Error/GenericError.js +1 -1
- package/dist/rendering/react/components/ErrorBoundary.d.ts +1 -1
- package/dist/rendering/react/components/Success.d.ts +0 -1
- package/dist/rendering/react/components/Success.js +4 -2
- package/dist/rendering/react/styles/useDefaultBoxStyles.d.ts +11 -0
- package/dist/rendering/react/styles/useDefaultBoxStyles.js +23 -0
- package/package.json +6 -5
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
import { jsx as _jsx
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { Flags } from "@oclif/core";
|
|
3
3
|
import { assertStatus } from "@mittwald/api-client-commons";
|
|
4
4
|
import { serverFlags } from "../../lib/resources/server/flags.js";
|
|
5
5
|
import { ExecRenderBaseCommand } from "../../lib/basecommands/ExecRenderBaseCommand.js";
|
|
6
|
-
import { Text } from "ink";
|
|
7
6
|
import { Success } from "../../rendering/react/components/Success.js";
|
|
8
|
-
import { Value } from "../../rendering/react/components/Value.js";
|
|
9
7
|
import { makeProcessRenderer, processFlags, } from "../../rendering/process/process_flags.js";
|
|
10
8
|
import { waitFlags, waitUntil } from "../../lib/wait.js";
|
|
11
9
|
import Context from "../../lib/context/Context.js";
|
|
@@ -29,16 +27,16 @@ export default class Create extends ExecRenderBaseCommand {
|
|
|
29
27
|
const { description } = flags;
|
|
30
28
|
const process = makeProcessRenderer(flags, "Creating project");
|
|
31
29
|
const serverId = await this.withServerId(Create);
|
|
32
|
-
const stepCreating = process.addStep(
|
|
30
|
+
const stepCreating = process.addStep("creating a new project");
|
|
33
31
|
const result = await this.apiClient.project.createProject({
|
|
34
32
|
serverId,
|
|
35
33
|
data: { description },
|
|
36
34
|
});
|
|
37
35
|
assertStatus(result, 201);
|
|
38
36
|
stepCreating.complete();
|
|
39
|
-
process.addInfo(
|
|
37
|
+
process.addInfo(`project ID: ${result.data.id}`);
|
|
40
38
|
if (flags.wait) {
|
|
41
|
-
const stepWaiting = process.addStep(
|
|
39
|
+
const stepWaiting = process.addStep("waiting for project to be ready");
|
|
42
40
|
await waitUntil(async () => {
|
|
43
41
|
const projectResponse = await this.apiClient.project.getProject({
|
|
44
42
|
projectId: result.data.id,
|
|
@@ -6,7 +6,6 @@ import { Success } from "../../rendering/react/components/Success.js";
|
|
|
6
6
|
import { Value } from "../../rendering/react/components/Value.js";
|
|
7
7
|
import { projectFlags } from "../../lib/resources/project/flags.js";
|
|
8
8
|
import { Flags } from "@oclif/core";
|
|
9
|
-
import { Text } from "ink";
|
|
10
9
|
export class Create extends ExecRenderBaseCommand {
|
|
11
10
|
static summary = "Create a new container registry";
|
|
12
11
|
static flags = {
|
|
@@ -72,6 +71,6 @@ export class Create extends ExecRenderBaseCommand {
|
|
|
72
71
|
if (this.flags.password) {
|
|
73
72
|
return this.flags.password;
|
|
74
73
|
}
|
|
75
|
-
return await process.addInput(
|
|
74
|
+
return await process.addInput("enter registry password", true);
|
|
76
75
|
}
|
|
77
76
|
}
|
|
@@ -4,7 +4,6 @@ import { Args, Flags } from "@oclif/core";
|
|
|
4
4
|
import { makeProcessRenderer, processFlags, } from "../../rendering/process/process_flags.js";
|
|
5
5
|
import { Success } from "../../rendering/react/components/Success.js";
|
|
6
6
|
import assertSuccess from "../../lib/apiutil/assert_success.js";
|
|
7
|
-
import { Text } from "ink";
|
|
8
7
|
export default class Update extends ExecRenderBaseCommand {
|
|
9
8
|
static description = "Update an existing container registry";
|
|
10
9
|
static args = {
|
|
@@ -68,6 +67,6 @@ export default class Update extends ExecRenderBaseCommand {
|
|
|
68
67
|
if (this.flags.password) {
|
|
69
68
|
return this.flags.password;
|
|
70
69
|
}
|
|
71
|
-
return await process.addInput(
|
|
70
|
+
return await process.addInput("enter registry password", true);
|
|
72
71
|
}
|
|
73
72
|
}
|
|
@@ -39,7 +39,7 @@ export default class Create extends ExecRenderBaseCommand {
|
|
|
39
39
|
});
|
|
40
40
|
step.complete();
|
|
41
41
|
assertStatus(response, 201);
|
|
42
|
-
process.complete(_jsx(Success, {
|
|
42
|
+
process.complete(_jsx(Success, { children: _jsxs(Text, { children: ["API token successfully created. Have fun. \uD83E\uDD73", _jsx(Newline, { count: 2 }), "This is your API token; make sure to store it somewhere safe:", _jsx(Newline, { count: 1 }), _jsx(Value, { children: response.data.token })] }) }));
|
|
43
43
|
return { token: response.data.token };
|
|
44
44
|
}
|
|
45
45
|
catch (e) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx as _jsx
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { Flags } from "@oclif/core";
|
|
3
3
|
import { assertStatus } from "@mittwald/api-client-commons";
|
|
4
4
|
import * as path from "path";
|
|
@@ -7,8 +7,6 @@ import * as fs from "fs/promises";
|
|
|
7
7
|
import { ExecRenderBaseCommand } from "../../../lib/basecommands/ExecRenderBaseCommand.js";
|
|
8
8
|
import { makeProcessRenderer, processFlags, } from "../../../rendering/process/process_flags.js";
|
|
9
9
|
import { Success } from "../../../rendering/react/components/Success.js";
|
|
10
|
-
import { Filename } from "../../../rendering/react/components/Filename.js";
|
|
11
|
-
import { Text } from "ink";
|
|
12
10
|
import { spawnInProcess } from "../../../rendering/process/process_exec.js";
|
|
13
11
|
import { expireFlags } from "../../../lib/flags/expireFlags.js";
|
|
14
12
|
export default class Create extends ExecRenderBaseCommand {
|
|
@@ -39,7 +37,7 @@ export default class Create extends ExecRenderBaseCommand {
|
|
|
39
37
|
}
|
|
40
38
|
await spawnInProcess(r, "generating SSH key using ssh-keygen", cmd, args);
|
|
41
39
|
const publicKey = await fs.readFile(outputFile + ".pub", "utf-8");
|
|
42
|
-
r.addInfo(
|
|
40
|
+
r.addInfo(`ssh key saved to ${outputFile}.`);
|
|
43
41
|
await r.runStep("importing SSH key", async () => {
|
|
44
42
|
const response = await this.apiClient.user.createSshKey({
|
|
45
43
|
data: {
|
|
@@ -58,12 +56,9 @@ export default class Create extends ExecRenderBaseCommand {
|
|
|
58
56
|
if (this.flags["no-passphrase"]) {
|
|
59
57
|
return "";
|
|
60
58
|
}
|
|
61
|
-
return await r.addInput(
|
|
59
|
+
return await r.addInput("enter passphrase for SSH key", true);
|
|
62
60
|
}
|
|
63
61
|
}
|
|
64
62
|
function SSHKeySuccess() {
|
|
65
63
|
return (_jsx(Success, { children: "Your SSH key was successfully created and imported to your user profile." }));
|
|
66
64
|
}
|
|
67
|
-
function InfoSSHKeySaved({ filename }) {
|
|
68
|
-
return (_jsxs(Text, { children: ["ssh key saved to ", _jsx(Filename, { filename: filename }), "."] }));
|
|
69
|
-
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx as _jsx
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { Flags } from "@oclif/core";
|
|
3
3
|
import { assertStatus } from "@mittwald/api-client-commons";
|
|
4
4
|
import * as path from "path";
|
|
@@ -7,7 +7,6 @@ import * as fs from "fs/promises";
|
|
|
7
7
|
import { ExecRenderBaseCommand } from "../../../lib/basecommands/ExecRenderBaseCommand.js";
|
|
8
8
|
import { makeProcessRenderer, processFlags, } from "../../../rendering/process/process_flags.js";
|
|
9
9
|
import { Success } from "../../../rendering/react/components/Success.js";
|
|
10
|
-
import { Filename } from "../../../rendering/react/components/Filename.js";
|
|
11
10
|
import { expireFlags } from "../../../lib/flags/expireFlags.js";
|
|
12
11
|
export default class Import extends ExecRenderBaseCommand {
|
|
13
12
|
static description = "Import an existing (local) SSH key";
|
|
@@ -32,7 +31,7 @@ export default class Import extends ExecRenderBaseCommand {
|
|
|
32
31
|
});
|
|
33
32
|
const keyAlreadyExists = (keys.sshKeys ?? []).some(({ key }) => publicKeyParts.includes(key));
|
|
34
33
|
if (keyAlreadyExists) {
|
|
35
|
-
r.addInfo(
|
|
34
|
+
r.addInfo(`the SSH key ${inputFile} is already imported.`);
|
|
36
35
|
await r.complete(_jsx(SSHKeySuccess, {}));
|
|
37
36
|
return;
|
|
38
37
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Flags, ux } from "@oclif/core";
|
|
3
3
|
import { ExecRenderBaseCommand } from "./ExecRenderBaseCommand.js";
|
|
4
4
|
import { makeProcessRenderer, processFlags, } from "../../rendering/process/process_flags.js";
|
|
@@ -18,15 +18,15 @@ export class DeleteBaseCommand extends ExecRenderBaseCommand {
|
|
|
18
18
|
const resourceName = this.ctor.resourceName;
|
|
19
19
|
const process = makeProcessRenderer(this.flags, `Deleting ${resourceName}`);
|
|
20
20
|
if (!this.flags.force) {
|
|
21
|
-
const confirmed = await process.addConfirmation(
|
|
21
|
+
const confirmed = await process.addConfirmation(`confirm deletion of ${resourceName}`);
|
|
22
22
|
if (!confirmed) {
|
|
23
|
-
process.addInfo(
|
|
23
|
+
process.addInfo(`deletion of ${resourceName} was cancelled`);
|
|
24
24
|
process.complete(_jsx(_Fragment, {}));
|
|
25
25
|
ux.exit(1);
|
|
26
26
|
return;
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
|
-
const deletingStep = process.addStep(
|
|
29
|
+
const deletingStep = process.addStep(`deleting ${resourceName}`);
|
|
30
30
|
try {
|
|
31
31
|
await this.deleteResource();
|
|
32
32
|
deletingStep.complete();
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
1
|
import { promisify } from "util";
|
|
3
2
|
import { exec } from "child_process";
|
|
4
|
-
import { Value } from "../../rendering/react/components/Value.js";
|
|
5
3
|
import { hasBinaryInPath } from "../util/fs/hasBinaryInPath.js";
|
|
6
4
|
const execAsync = promisify(exec);
|
|
7
5
|
export async function assertDDEVIsInstalled(r) {
|
|
@@ -14,9 +12,6 @@ export async function assertDDEVIsInstalled(r) {
|
|
|
14
12
|
export async function determineDDEVVersion(r) {
|
|
15
13
|
const { stdout } = await execAsync("ddev --version");
|
|
16
14
|
const version = stdout.trim().replace(/^ddev version +/, "");
|
|
17
|
-
r.addInfo(
|
|
15
|
+
r.addInfo(`detected DDEV version: ${version}`);
|
|
18
16
|
return version;
|
|
19
17
|
}
|
|
20
|
-
function InfoDDEVVersion({ version }) {
|
|
21
|
-
return (_jsxs(_Fragment, { children: ["detected DDEV version: ", _jsx(Value, { children: version })] }));
|
|
22
|
-
}
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
1
|
import { assertStatus, } from "@mittwald/api-client";
|
|
3
|
-
import { Value } from "../../rendering/react/components/Value.js";
|
|
4
|
-
import { Text } from "ink";
|
|
5
2
|
/**
|
|
6
3
|
* Determines the database ID to use for the DDEV project.
|
|
7
4
|
*
|
|
@@ -29,7 +26,7 @@ export async function determineDDEVDatabaseId(r, apiClient, flags, appInstallati
|
|
|
29
26
|
mysqlDatabaseId: databaseId,
|
|
30
27
|
});
|
|
31
28
|
assertStatus(mysqlDatabaseResponse, 200);
|
|
32
|
-
r.addInfo(
|
|
29
|
+
r.addInfo(`using database: ${mysqlDatabaseResponse.data.name}`);
|
|
33
30
|
return mysqlDatabaseResponse.data.name;
|
|
34
31
|
}
|
|
35
32
|
return await promptDatabaseFromUser(r, apiClient, appInstallation);
|
|
@@ -54,6 +51,3 @@ async function promptDatabaseFromUser(r, apiClient, appInstallation) {
|
|
|
54
51
|
},
|
|
55
52
|
]);
|
|
56
53
|
}
|
|
57
|
-
function InfoDatabase({ name }) {
|
|
58
|
-
return (_jsxs(Text, { children: ["using database: ", _jsx(Value, { children: name })] }));
|
|
59
|
-
}
|
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
1
|
import { typo3Installer } from "../../commands/app/install/typo3.js";
|
|
3
2
|
import { wordpressInstaller } from "../../commands/app/install/wordpress.js";
|
|
4
3
|
import { shopware6Installer } from "../../commands/app/install/shopware6.js";
|
|
5
|
-
import { Value } from "../../rendering/react/components/Value.js";
|
|
6
|
-
import { Text } from "ink";
|
|
7
4
|
/**
|
|
8
5
|
* A list of all known DDEV project types. Shamelessly stolen from
|
|
9
6
|
* https://ddev.readthedocs.io/en/latest/users/configuration/config/#type
|
|
@@ -38,12 +35,12 @@ export const knownDDEVProjectTypes = [
|
|
|
38
35
|
*/
|
|
39
36
|
export async function determineProjectType(r, client, inst, typeOverride) {
|
|
40
37
|
if (typeOverride !== "auto") {
|
|
41
|
-
r.addInfo(
|
|
38
|
+
r.addInfo(`using DDEV project type: ${typeOverride} (explicitly specified)`);
|
|
42
39
|
return typeOverride;
|
|
43
40
|
}
|
|
44
41
|
const determinedProjectType = await determineProjectTypeFromAppInstallation(client, inst);
|
|
45
42
|
if (determinedProjectType !== null) {
|
|
46
|
-
r.addInfo(
|
|
43
|
+
r.addInfo(`using DDEV project type: ${determinedProjectType} (derived from app installation)`);
|
|
47
44
|
return determinedProjectType;
|
|
48
45
|
}
|
|
49
46
|
return await promptProjectTypeFromUser(r);
|
|
@@ -76,9 +73,3 @@ export async function determineProjectTypeFromAppInstallation(client, inst) {
|
|
|
76
73
|
return null;
|
|
77
74
|
}
|
|
78
75
|
}
|
|
79
|
-
function ProjectTypeInfoOverride({ type }) {
|
|
80
|
-
return (_jsxs(Text, { children: ["using DDEV project type: ", _jsx(Value, { children: type }), " (explicitly specified)"] }));
|
|
81
|
-
}
|
|
82
|
-
function ProjectTypeInfoAuto({ type }) {
|
|
83
|
-
return (_jsxs(Text, { children: ["using DDEV project type: ", _jsx(Value, { children: type }), " (derived from app installation)"] }));
|
|
84
|
-
}
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
import { randomUUID } from "crypto";
|
|
4
|
+
import { XMLBuilder, XMLParser } from "fast-xml-parser";
|
|
5
|
+
// Common XML configuration
|
|
6
|
+
const XML_PARSER_CONFIG = {
|
|
7
|
+
ignoreAttributes: false,
|
|
8
|
+
attributeNamePrefix: "@_",
|
|
9
|
+
parseAttributeValue: false,
|
|
10
|
+
};
|
|
11
|
+
const XML_BUILDER_CONFIG = {
|
|
12
|
+
ignoreAttributes: false,
|
|
13
|
+
attributeNamePrefix: "@_",
|
|
14
|
+
format: true,
|
|
15
|
+
suppressBooleanAttributes: false,
|
|
16
|
+
};
|
|
17
|
+
// Common XML document structure
|
|
18
|
+
const createXmlDocumentBase = () => ({
|
|
19
|
+
"?xml": { "@_version": "1.0", "@_encoding": "UTF-8" },
|
|
20
|
+
project: { "@_version": "4" },
|
|
21
|
+
});
|
|
22
|
+
export function generateIntellijConfigs(data, projectDir = ".") {
|
|
23
|
+
const ideaDir = path.join(projectDir, ".idea");
|
|
24
|
+
try {
|
|
25
|
+
if (!fs.existsSync(ideaDir)) {
|
|
26
|
+
fs.mkdirSync(ideaDir, { recursive: true });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
throw new Error(`Cannot create .idea directory: ${error instanceof Error ? error.message : error}`);
|
|
31
|
+
}
|
|
32
|
+
const webServerId = randomUUID();
|
|
33
|
+
// First, ensure SSH config exists and get its ID (existing or newly created)
|
|
34
|
+
const sshConfigId = ensureSshConfigExists(data, ideaDir);
|
|
35
|
+
// Then create web server and deployment configs using the correct SSH config ID
|
|
36
|
+
generateWebServersXml(data, webServerId, sshConfigId, ideaDir);
|
|
37
|
+
generateDeploymentXml(data, ideaDir);
|
|
38
|
+
}
|
|
39
|
+
// Common XML manipulation utility
|
|
40
|
+
function manipulateXmlFile(ideaDir, config, newItem) {
|
|
41
|
+
const configPath = path.join(ideaDir, config.filename);
|
|
42
|
+
const parser = new XMLParser(XML_PARSER_CONFIG);
|
|
43
|
+
const builder = new XMLBuilder(XML_BUILDER_CONFIG);
|
|
44
|
+
let xmlDoc = loadExistingXmlDocument(configPath, parser);
|
|
45
|
+
if (xmlDoc) {
|
|
46
|
+
const wasAdded = tryAddToExistingDocument(xmlDoc, newItem, config);
|
|
47
|
+
if (!wasAdded) {
|
|
48
|
+
return; // Item already exists
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
xmlDoc = config.createNewDocumentFn(newItem);
|
|
53
|
+
}
|
|
54
|
+
const xmlContent = builder.build(xmlDoc);
|
|
55
|
+
fs.writeFileSync(configPath, xmlContent);
|
|
56
|
+
}
|
|
57
|
+
function loadExistingXmlDocument(configPath, parser) {
|
|
58
|
+
if (!fs.existsSync(configPath)) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
const content = fs.readFileSync(configPath, "utf8");
|
|
62
|
+
const xmlDoc = parser.parse(content);
|
|
63
|
+
if (!xmlDoc.project?.component) {
|
|
64
|
+
throw new Error("Invalid XML structure");
|
|
65
|
+
}
|
|
66
|
+
return xmlDoc;
|
|
67
|
+
}
|
|
68
|
+
function tryAddToExistingDocument(xmlDoc, newItem, config) {
|
|
69
|
+
// This function delegates to the specific addItemFn
|
|
70
|
+
// Return false if item already exists, true if added
|
|
71
|
+
return config.addItemFn(xmlDoc, newItem);
|
|
72
|
+
}
|
|
73
|
+
// Helper function to handle array/single element patterns
|
|
74
|
+
function ensureArray(item) {
|
|
75
|
+
return Array.isArray(item) ? item : [item];
|
|
76
|
+
}
|
|
77
|
+
// Specific type-safe helper functions for each XML structure
|
|
78
|
+
function addSshConfig(configs, newConfig) {
|
|
79
|
+
if (Array.isArray(configs.sshConfig)) {
|
|
80
|
+
configs.sshConfig.push(newConfig);
|
|
81
|
+
}
|
|
82
|
+
else if (configs.sshConfig) {
|
|
83
|
+
configs.sshConfig = [configs.sshConfig, newConfig];
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
configs.sshConfig = newConfig;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function addWebServer(option, newServer) {
|
|
90
|
+
if (Array.isArray(option.webServer)) {
|
|
91
|
+
option.webServer.push(newServer);
|
|
92
|
+
}
|
|
93
|
+
else if (option.webServer) {
|
|
94
|
+
option.webServer = [option.webServer, newServer];
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
option.webServer = newServer;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function addDeploymentPath(serverData, newPath) {
|
|
101
|
+
if (Array.isArray(serverData.paths)) {
|
|
102
|
+
serverData.paths.push(newPath);
|
|
103
|
+
}
|
|
104
|
+
else if (serverData.paths) {
|
|
105
|
+
serverData.paths = [serverData.paths, newPath];
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
serverData.paths = newPath;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// Function to ensure SSH config exists and return its ID
|
|
112
|
+
function ensureSshConfigExists(data, ideaDir) {
|
|
113
|
+
const configPath = path.join(ideaDir, "sshConfigs.xml");
|
|
114
|
+
const parser = new XMLParser(XML_PARSER_CONFIG);
|
|
115
|
+
const existingXmlDoc = loadExistingXmlDocument(configPath, parser);
|
|
116
|
+
if (existingXmlDoc) {
|
|
117
|
+
// Check if SSH config for this host already exists
|
|
118
|
+
const configs = existingXmlDoc.project.component.configs;
|
|
119
|
+
if (configs?.sshConfig) {
|
|
120
|
+
const existingConfigs = ensureArray(configs.sshConfig);
|
|
121
|
+
const existingConfig = existingConfigs.find((config) => config["@_host"] === data.host);
|
|
122
|
+
if (existingConfig) {
|
|
123
|
+
// Return existing SSH config ID
|
|
124
|
+
return existingConfig["@_id"];
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
// SSH config doesn't exist, create it with a new ID
|
|
129
|
+
const newSshConfigId = randomUUID();
|
|
130
|
+
generateSshConfigsXml(data, newSshConfigId, ideaDir);
|
|
131
|
+
return newSshConfigId;
|
|
132
|
+
}
|
|
133
|
+
// SSH Config specific functions
|
|
134
|
+
function createSshConfigItem(host, username, configId) {
|
|
135
|
+
return {
|
|
136
|
+
"@_authType": "OPEN_SSH",
|
|
137
|
+
"@_host": host,
|
|
138
|
+
"@_id": configId,
|
|
139
|
+
"@_port": "22",
|
|
140
|
+
"@_nameFormat": "DESCRIPTIVE",
|
|
141
|
+
"@_username": username,
|
|
142
|
+
"@_useOpenSSHConfig": "true",
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
function generateSshConfigsXml(data, configId, ideaDir) {
|
|
146
|
+
const sshConfigItem = createSshConfigItem(data.host, data.user, configId);
|
|
147
|
+
const config = {
|
|
148
|
+
filename: "sshConfigs.xml",
|
|
149
|
+
componentName: "SshConfigs",
|
|
150
|
+
checkDuplicateFn: (existing, newItem) => existing.some((config) => config["@_host"] === newItem["@_host"]),
|
|
151
|
+
addItemFn: (xmlDoc, newItem) => {
|
|
152
|
+
const configs = xmlDoc.project.component.configs;
|
|
153
|
+
if (configs?.sshConfig) {
|
|
154
|
+
const existingConfigs = ensureArray(configs.sshConfig);
|
|
155
|
+
if (existingConfigs.some((config) => config["@_host"] === newItem["@_host"])) {
|
|
156
|
+
return false; // Already exists
|
|
157
|
+
}
|
|
158
|
+
addSshConfig(configs, newItem);
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
if (!xmlDoc.project.component.configs) {
|
|
162
|
+
xmlDoc.project.component.configs = {};
|
|
163
|
+
}
|
|
164
|
+
xmlDoc.project.component.configs.sshConfig = newItem;
|
|
165
|
+
}
|
|
166
|
+
return true;
|
|
167
|
+
},
|
|
168
|
+
createNewDocumentFn: (newItem) => ({
|
|
169
|
+
...createXmlDocumentBase(),
|
|
170
|
+
project: {
|
|
171
|
+
"@_version": "4",
|
|
172
|
+
component: {
|
|
173
|
+
"@_name": "SshConfigs",
|
|
174
|
+
configs: { sshConfig: newItem },
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
}),
|
|
178
|
+
};
|
|
179
|
+
manipulateXmlFile(ideaDir, config, sshConfigItem);
|
|
180
|
+
}
|
|
181
|
+
// Web Server specific functions
|
|
182
|
+
function createWebServerItem(serverId, appShortId, host, username, sshConfigId) {
|
|
183
|
+
return {
|
|
184
|
+
"@_id": serverId,
|
|
185
|
+
"@_name": appShortId,
|
|
186
|
+
fileTransfer: {
|
|
187
|
+
"@_accessType": "SFTP",
|
|
188
|
+
"@_host": host,
|
|
189
|
+
"@_port": "22",
|
|
190
|
+
"@_sshConfigId": sshConfigId,
|
|
191
|
+
"@_sshConfig": `${username}@${host}:22 agent`,
|
|
192
|
+
"@_authAgent": "true",
|
|
193
|
+
advancedOptions: {
|
|
194
|
+
advancedOptions: {
|
|
195
|
+
"@_dataProtectionLevel": "Private",
|
|
196
|
+
"@_keepAliveTimeout": "0",
|
|
197
|
+
"@_passiveMode": "true",
|
|
198
|
+
"@_shareSSLContext": "true",
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
function generateWebServersXml(data, serverId, sshConfigId, ideaDir) {
|
|
205
|
+
const webServerItem = createWebServerItem(serverId, data.appShortId, data.host, data.user, sshConfigId);
|
|
206
|
+
const config = {
|
|
207
|
+
filename: "webServers.xml",
|
|
208
|
+
componentName: "WebServers",
|
|
209
|
+
checkDuplicateFn: (existing, newItem) => existing.some((server) => server["@_name"] === newItem["@_name"]),
|
|
210
|
+
addItemFn: (xmlDoc, newItem) => {
|
|
211
|
+
const servers = xmlDoc.project.component.option;
|
|
212
|
+
if (servers?.webServer) {
|
|
213
|
+
const existingServers = ensureArray(servers.webServer);
|
|
214
|
+
if (existingServers.some((server) => server["@_name"] === newItem["@_name"])) {
|
|
215
|
+
return false; // Already exists
|
|
216
|
+
}
|
|
217
|
+
addWebServer(servers, newItem);
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
if (!xmlDoc.project.component.option) {
|
|
221
|
+
xmlDoc.project.component.option = { "@_name": "servers" };
|
|
222
|
+
}
|
|
223
|
+
xmlDoc.project.component.option.webServer = newItem;
|
|
224
|
+
}
|
|
225
|
+
return true;
|
|
226
|
+
},
|
|
227
|
+
createNewDocumentFn: (newItem) => ({
|
|
228
|
+
...createXmlDocumentBase(),
|
|
229
|
+
project: {
|
|
230
|
+
"@_version": "4",
|
|
231
|
+
component: {
|
|
232
|
+
"@_name": "WebServers",
|
|
233
|
+
option: {
|
|
234
|
+
"@_name": "servers",
|
|
235
|
+
webServer: newItem,
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
},
|
|
239
|
+
}),
|
|
240
|
+
};
|
|
241
|
+
manipulateXmlFile(ideaDir, config, webServerItem);
|
|
242
|
+
}
|
|
243
|
+
// Deployment specific functions
|
|
244
|
+
function createDeploymentPathItem(appShortId, directory) {
|
|
245
|
+
return {
|
|
246
|
+
"@_name": appShortId,
|
|
247
|
+
serverdata: {
|
|
248
|
+
mappings: {
|
|
249
|
+
mapping: {
|
|
250
|
+
"@_deploy": directory,
|
|
251
|
+
"@_local": "$PROJECT_DIR$",
|
|
252
|
+
"@_web": "/",
|
|
253
|
+
},
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
function generateDeploymentXml(data, ideaDir) {
|
|
259
|
+
const deploymentPathItem = createDeploymentPathItem(data.appShortId, data.directory);
|
|
260
|
+
const config = {
|
|
261
|
+
filename: "deployment.xml",
|
|
262
|
+
componentName: "PublishConfigData",
|
|
263
|
+
checkDuplicateFn: (existing, newItem) => existing.some((path) => path["@_name"] === newItem["@_name"]),
|
|
264
|
+
addItemFn: (xmlDoc, newItem) => {
|
|
265
|
+
const serverData = xmlDoc.project.component.serverData;
|
|
266
|
+
if (serverData?.paths) {
|
|
267
|
+
const existingPaths = ensureArray(serverData.paths);
|
|
268
|
+
if (existingPaths.some((path) => path["@_name"] === newItem["@_name"])) {
|
|
269
|
+
return false; // Already exists
|
|
270
|
+
}
|
|
271
|
+
addDeploymentPath(serverData, newItem);
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
if (!xmlDoc.project.component.serverData) {
|
|
275
|
+
xmlDoc.project.component.serverData = {};
|
|
276
|
+
}
|
|
277
|
+
xmlDoc.project.component.serverData.paths = newItem;
|
|
278
|
+
}
|
|
279
|
+
return true;
|
|
280
|
+
},
|
|
281
|
+
createNewDocumentFn: (newItem) => ({
|
|
282
|
+
...createXmlDocumentBase(),
|
|
283
|
+
project: {
|
|
284
|
+
"@_version": "4",
|
|
285
|
+
component: {
|
|
286
|
+
"@_name": "PublishConfigData",
|
|
287
|
+
"@_serverName": data.appShortId,
|
|
288
|
+
"@_remoteFilesAllowedToDisappearOnAutoupload": "false",
|
|
289
|
+
serverData: { paths: newItem },
|
|
290
|
+
},
|
|
291
|
+
},
|
|
292
|
+
}),
|
|
293
|
+
};
|
|
294
|
+
manipulateXmlFile(ideaDir, config, deploymentPathItem);
|
|
295
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|