@rulebricks/cli 2.1.7 → 2.3.1

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.
Files changed (117) hide show
  1. package/README.md +51 -16
  2. package/cluster-setup/aws/README.md +96 -47
  3. package/cluster-setup/aws/check-aws-access.sh +216 -52
  4. package/cluster-setup/aws/parameters.json +13 -0
  5. package/cluster-setup/aws/rulebricks-cluster.cfn.yaml +355 -0
  6. package/cluster-setup/azure/README.md +103 -55
  7. package/cluster-setup/azure/check-aks-prereqs.sh +236 -56
  8. package/cluster-setup/azure/parameters.json +30 -0
  9. package/cluster-setup/azure/rulebricks-cluster.bicep +546 -0
  10. package/cluster-setup/gcp/README.md +51 -34
  11. package/cluster-setup/gcp/check-gke-prereqs.sh +222 -60
  12. package/dist/commands/backup.d.ts +5 -0
  13. package/dist/commands/backup.js +104 -0
  14. package/dist/commands/deploy.d.ts +3 -1
  15. package/dist/commands/deploy.js +226 -326
  16. package/dist/commands/destroy.d.ts +1 -1
  17. package/dist/commands/destroy.js +73 -123
  18. package/dist/commands/init.d.ts +5 -1
  19. package/dist/commands/init.js +78 -54
  20. package/dist/commands/list.d.ts +1 -0
  21. package/dist/commands/list.js +74 -0
  22. package/dist/commands/open.d.ts +1 -1
  23. package/dist/commands/open.js +4 -12
  24. package/dist/commands/redeploy.d.ts +6 -0
  25. package/dist/commands/redeploy.js +310 -0
  26. package/dist/commands/restore.d.ts +5 -0
  27. package/dist/commands/restore.js +338 -0
  28. package/dist/commands/status.js +62 -49
  29. package/dist/commands/upgrade.js +74 -51
  30. package/dist/components/DNSWaitScreen.d.ts +5 -1
  31. package/dist/components/DNSWaitScreen.js +47 -41
  32. package/dist/components/Wizard/WizardContext.d.ts +157 -36
  33. package/dist/components/Wizard/WizardContext.js +872 -160
  34. package/dist/components/Wizard/steps/CloudProviderStep.js +192 -107
  35. package/dist/components/Wizard/steps/DomainStep.js +5 -24
  36. package/dist/components/Wizard/steps/ExternalServicesStep.d.ts +6 -0
  37. package/dist/components/Wizard/steps/ExternalServicesStep.js +645 -0
  38. package/dist/components/Wizard/steps/FeatureConfigStep.d.ts +2 -1
  39. package/dist/components/Wizard/steps/FeatureConfigStep.js +739 -425
  40. package/dist/components/Wizard/steps/FeaturesStep.js +31 -35
  41. package/dist/components/Wizard/steps/ObservabilityStep.d.ts +6 -0
  42. package/dist/components/Wizard/steps/ObservabilityStep.js +137 -0
  43. package/dist/components/Wizard/steps/ReviewStep.d.ts +2 -1
  44. package/dist/components/Wizard/steps/ReviewStep.js +56 -12
  45. package/dist/components/Wizard/steps/StorageStep.d.ts +9 -0
  46. package/dist/components/Wizard/steps/StorageStep.js +592 -0
  47. package/dist/components/Wizard/steps/SupabaseCredentialsStep.js +20 -21
  48. package/dist/components/Wizard/steps/VersionStep.js +45 -23
  49. package/dist/components/Wizard/steps/index.d.ts +3 -3
  50. package/dist/components/Wizard/steps/index.js +3 -3
  51. package/dist/components/common/CommandApproval.d.ts +12 -0
  52. package/dist/components/common/CommandApproval.js +91 -0
  53. package/dist/components/common/DeploymentPicker.d.ts +14 -0
  54. package/dist/components/common/DeploymentPicker.js +16 -0
  55. package/dist/components/common/index.d.ts +2 -0
  56. package/dist/components/common/index.js +2 -0
  57. package/dist/index.js +94 -62
  58. package/dist/lib/cloudCli.d.ts +134 -63
  59. package/dist/lib/cloudCli.js +512 -220
  60. package/dist/lib/clusterSetupDefaults.d.ts +30 -0
  61. package/dist/lib/clusterSetupDefaults.js +64 -0
  62. package/dist/lib/commandApproval.d.ts +26 -0
  63. package/dist/lib/commandApproval.js +114 -0
  64. package/dist/lib/config.d.ts +12 -10
  65. package/dist/lib/config.js +91 -33
  66. package/dist/lib/configFixtures.d.ts +5 -0
  67. package/dist/lib/configFixtures.js +513 -0
  68. package/dist/lib/deploymentHealth.d.ts +32 -0
  69. package/dist/lib/deploymentHealth.js +157 -0
  70. package/dist/lib/dns.d.ts +1 -1
  71. package/dist/lib/dns.js +19 -1
  72. package/dist/lib/dns.test.d.ts +1 -0
  73. package/dist/lib/dns.test.js +27 -0
  74. package/dist/lib/dockerHub.d.ts +12 -1
  75. package/dist/lib/dockerHub.js +18 -8
  76. package/dist/lib/helm.d.ts +4 -0
  77. package/dist/lib/helm.js +16 -0
  78. package/dist/lib/helmValues.d.ts +25 -0
  79. package/dist/lib/helmValues.js +1762 -289
  80. package/dist/lib/helmValues.test.d.ts +1 -0
  81. package/dist/lib/helmValues.test.js +966 -0
  82. package/dist/lib/htpasswd.d.ts +1 -0
  83. package/dist/lib/htpasswd.js +15 -0
  84. package/dist/lib/kubernetes.d.ts +124 -17
  85. package/dist/lib/kubernetes.js +576 -145
  86. package/dist/lib/secrets.d.ts +23 -0
  87. package/dist/lib/secrets.js +158 -0
  88. package/dist/lib/validateValues.d.ts +31 -0
  89. package/dist/lib/validateValues.js +253 -0
  90. package/dist/lib/versions.d.ts +82 -11
  91. package/dist/lib/versions.js +131 -31
  92. package/dist/lib/versions.test.d.ts +1 -0
  93. package/dist/lib/versions.test.js +81 -0
  94. package/dist/lib/wizardSteps.d.ts +14 -0
  95. package/dist/lib/wizardSteps.js +23 -0
  96. package/dist/lib/workloadIdentity.d.ts +26 -0
  97. package/dist/lib/workloadIdentity.js +323 -0
  98. package/dist/lib/workloadIdentity.test.d.ts +1 -0
  99. package/dist/lib/workloadIdentity.test.js +57 -0
  100. package/dist/types/index.d.ts +1860 -164
  101. package/dist/types/index.js +518 -295
  102. package/package.json +9 -4
  103. package/schema/values.schema.json +1934 -0
  104. package/cluster-setup/aws/cluster.yaml +0 -33
  105. package/cluster-setup/azure/main.bicep +0 -282
  106. package/cluster-setup/azure/main.parameters.json +0 -21
  107. package/dist/components/Wizard/steps/CredentialsStep.d.ts +0 -6
  108. package/dist/components/Wizard/steps/CredentialsStep.js +0 -22
  109. package/dist/components/Wizard/steps/DeploymentModeStep.d.ts +0 -5
  110. package/dist/components/Wizard/steps/DeploymentModeStep.js +0 -26
  111. package/dist/components/Wizard/steps/TierStep.d.ts +0 -6
  112. package/dist/components/Wizard/steps/TierStep.js +0 -29
  113. package/dist/lib/terraform.d.ts +0 -66
  114. package/dist/lib/terraform.js +0 -754
  115. package/terraform/aws/main.tf +0 -355
  116. package/terraform/azure/main.tf +0 -371
  117. package/terraform/gcp/main.tf +0 -407
@@ -0,0 +1,74 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useState } from "react";
3
+ import { Box, Text, useApp } from "ink";
4
+ import { BorderBox, Logo, Spinner, ThemeProvider, useTheme, } from "../components/common/index.js";
5
+ import { listDeployments } from "../lib/config.js";
6
+ import { loadDeploymentHealth, } from "../lib/deploymentHealth.js";
7
+ function statusText(health) {
8
+ switch (health.kind) {
9
+ case "online":
10
+ return { icon: "●", label: "online", color: "green" };
11
+ case "installed-unreachable":
12
+ return { icon: "◐", label: "installed, URL unreachable", color: "yellow" };
13
+ case "installed-degraded":
14
+ return { icon: "◐", label: "installed, pods not ready", color: "yellow" };
15
+ case "cluster-unreachable":
16
+ return { icon: "?", label: "cluster unreachable", color: "yellow" };
17
+ case "destroyed":
18
+ return { icon: "○", label: "destroyed", color: "gray" };
19
+ case "not-installed":
20
+ return { icon: "○", label: "not installed", color: "gray" };
21
+ case "config-error":
22
+ return { icon: "✗", label: "config error", color: "red" };
23
+ }
24
+ }
25
+ function ListCommandInner() {
26
+ const { exit } = useApp();
27
+ const { colors } = useTheme();
28
+ const [step, setStep] = useState("loading");
29
+ const [loadingLabel, setLoadingLabel] = useState("Loading deployments...");
30
+ const [deployments, setDeployments] = useState([]);
31
+ const [error, setError] = useState(null);
32
+ useEffect(() => {
33
+ (async () => {
34
+ try {
35
+ const names = await listDeployments();
36
+ if (names.length === 0) {
37
+ setStep("empty");
38
+ setTimeout(() => exit(), 250);
39
+ return;
40
+ }
41
+ setLoadingLabel(`Checking health for ${names.length} deployment(s)...`);
42
+ const results = [];
43
+ for (const name of names) {
44
+ setLoadingLabel(`Checking ${name}...`);
45
+ results.push(await loadDeploymentHealth(name));
46
+ }
47
+ setDeployments(results);
48
+ setStep("complete");
49
+ setTimeout(() => exit(), 250);
50
+ }
51
+ catch (err) {
52
+ setError(err instanceof Error ? err.message : "Failed to list deployments");
53
+ setStep("error");
54
+ setTimeout(() => exit(), 1000);
55
+ }
56
+ })();
57
+ }, [exit]);
58
+ if (step === "loading") {
59
+ return (_jsx(BorderBox, { title: "Deployments", children: _jsx(Box, { marginY: 1, children: _jsx(Spinner, { label: loadingLabel }) }) }));
60
+ }
61
+ if (step === "empty") {
62
+ return (_jsx(BorderBox, { title: "Deployments", children: _jsx(Box, { marginY: 1, children: _jsx(Text, { color: colors.warning, children: "No deployments found. Run \"rulebricks init\" to create one." }) }) }));
63
+ }
64
+ if (step === "error") {
65
+ return (_jsx(BorderBox, { title: "List Failed", children: _jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { color: colors.error, bold: true, children: "\u2717 Error" }), _jsx(Text, { color: colors.error, children: error })] }) }));
66
+ }
67
+ return (_jsx(BorderBox, { title: "Deployments", children: _jsx(Box, { flexDirection: "column", marginY: 1, children: deployments.map((deployment) => {
68
+ const status = statusText(deployment);
69
+ return (_jsxs(Box, { children: [_jsx(Text, { color: status.color, children: status.icon }), _jsxs(Text, { children: [" ", deployment.name, " "] }), _jsx(Text, { color: status.color, children: status.label }), deployment.url && (_jsxs(Text, { color: colors.muted, children: [" ", deployment.url] }))] }, deployment.name));
70
+ }) }) }));
71
+ }
72
+ export function ListCommand() {
73
+ return (_jsxs(ThemeProvider, { theme: "status", children: [_jsx(Logo, {}), _jsx(ListCommandInner, {})] }));
74
+ }
@@ -1,4 +1,4 @@
1
- type OpenTarget = "all" | "config" | "values" | "terraform";
1
+ type OpenTarget = "all" | "config" | "values";
2
2
  interface OpenCommandProps {
3
3
  name: string;
4
4
  target: OpenTarget;
@@ -5,7 +5,7 @@ import { spawn } from "child_process";
5
5
  import path from "path";
6
6
  import { promises as fs } from "fs";
7
7
  import { BorderBox, Spinner, ThemeProvider, useTheme, Logo, } from "../components/common/index.js";
8
- import { deploymentExists, getDeploymentDir, getTerraformDir, getHelmValuesPath, } from "../lib/config.js";
8
+ import { deploymentExists, getDeploymentDir, getHelmValuesPath, } from "../lib/config.js";
9
9
  /**
10
10
  * Resolves the appropriate command to open files/directories based on OS and environment
11
11
  */
@@ -72,9 +72,6 @@ function OpenCommandInner({ name, target }) {
72
72
  case "values":
73
73
  targetPath = getHelmValuesPath(name);
74
74
  break;
75
- case "terraform":
76
- targetPath = getTerraformDir(name);
77
- break;
78
75
  case "all":
79
76
  default:
80
77
  targetPath = deployDir;
@@ -85,10 +82,7 @@ function OpenCommandInner({ name, target }) {
85
82
  await fs.access(targetPath);
86
83
  }
87
84
  catch {
88
- if (target === "terraform") {
89
- setError(`Terraform directory not found. Run "rulebricks deploy ${name}" first to create infrastructure files.`);
90
- }
91
- else if (target === "values") {
85
+ if (target === "values") {
92
86
  setError(`values.yaml not found. Run "rulebricks init" or "rulebricks deploy ${name}" first.`);
93
87
  }
94
88
  else {
@@ -129,10 +123,8 @@ function OpenCommandInner({ name, target }) {
129
123
  ? "deployment directory"
130
124
  : target === "config"
131
125
  ? "config.yaml"
132
- : target === "values"
133
- ? "values.yaml"
134
- : "terraform directory";
135
- return (_jsx(BorderBox, { title: "Opened", children: _jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsxs(Text, { color: colors.success, bold: true, children: ["\u2713 Opened ", targetLabel] }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: colors.muted, dimColor: true, children: ["Path: ", openedPath] }) }), target === "all" && (_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: colors.muted, children: "Available files:" }), _jsxs(Text, { color: colors.muted, children: [" ", "\u2022 config.yaml - Deployment configuration"] }), _jsx(Text, { color: colors.muted, children: " \u2022 values.yaml - Helm chart values" }), _jsxs(Text, { color: colors.muted, children: [" ", "\u2022 terraform/ - Infrastructure files"] })] })), (target === "values" || target === "config") && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.warning, dimColor: true, children: "Note: Manual edits may desync from wizard-managed settings" }) }))] }) }));
126
+ : "values.yaml";
127
+ return (_jsx(BorderBox, { title: "Opened", children: _jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsxs(Text, { color: colors.success, bold: true, children: ["\u2713 Opened ", targetLabel] }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: colors.muted, dimColor: true, children: ["Path: ", openedPath] }) }), target === "all" && (_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: colors.muted, children: "Available files:" }), _jsxs(Text, { color: colors.muted, children: [" ", "\u2022 config.yaml - Deployment configuration"] }), _jsx(Text, { color: colors.muted, children: " \u2022 values.yaml - Helm chart values" })] })), (target === "values" || target === "config") && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.warning, dimColor: true, children: "Note: Manual edits may desync from wizard-managed settings" }) }))] }) }));
136
128
  }
137
129
  export function OpenCommand(props) {
138
130
  return (_jsxs(ThemeProvider, { theme: "status", children: [_jsx(Logo, {}), _jsx(OpenCommandInner, { ...props })] }));
@@ -0,0 +1,6 @@
1
+ interface RedeployCommandProps {
2
+ name: string;
3
+ chartVersion?: string;
4
+ }
5
+ export declare function RedeployCommand(props: RedeployCommandProps): import("react/jsx-runtime").JSX.Element;
6
+ export {};
@@ -0,0 +1,310 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useState } from "react";
3
+ import { Box, Text } from "ink";
4
+ import { BorderBox, Logo, Spinner, ThemeProvider, useTheme, } from "../components/common/index.js";
5
+ import { InitWizard } from "./init.js";
6
+ import { DeployCommand } from "./deploy.js";
7
+ import { configToWizardState, } from "../components/Wizard/WizardContext.js";
8
+ import { loadHelmValues, loadProfile, } from "../lib/config.js";
9
+ import { loadDeploymentHealth } from "../lib/deploymentHealth.js";
10
+ import { getInstalledChartVersion } from "../lib/helm.js";
11
+ function isRecord(value) {
12
+ return !!value && typeof value === "object" && !Array.isArray(value);
13
+ }
14
+ function stringValue(value) {
15
+ return typeof value === "string" && value ? value : undefined;
16
+ }
17
+ function numberValue(value) {
18
+ return typeof value === "number" ? value : undefined;
19
+ }
20
+ function booleanValue(value) {
21
+ return typeof value === "boolean" ? value : undefined;
22
+ }
23
+ function secretRefValue(value) {
24
+ if (!isRecord(value))
25
+ return undefined;
26
+ const name = stringValue(value.name);
27
+ const key = stringValue(value.key);
28
+ return name && key ? { name, key } : undefined;
29
+ }
30
+ function applyHelmValuesToConfig(config, values) {
31
+ const next = JSON.parse(JSON.stringify(config));
32
+ if (!values)
33
+ return next;
34
+ const global = isRecord(values.global) ? values.global : null;
35
+ if (global) {
36
+ next.domain = stringValue(global.domain) ?? next.domain;
37
+ next.adminEmail = stringValue(global.email) ?? next.adminEmail;
38
+ next.licenseKey = stringValue(global.licenseKey) ?? next.licenseKey;
39
+ next.version = stringValue(global.version) ?? next.version;
40
+ if (isRecord(global.smtp)) {
41
+ next.smtp = {
42
+ ...next.smtp,
43
+ host: stringValue(global.smtp.host) ?? next.smtp.host,
44
+ port: numberValue(global.smtp.port) ?? next.smtp.port,
45
+ user: stringValue(global.smtp.user) ?? next.smtp.user,
46
+ pass: stringValue(global.smtp.pass) ?? next.smtp.pass,
47
+ from: stringValue(global.smtp.from) ?? next.smtp.from,
48
+ fromName: stringValue(global.smtp.fromName) ?? next.smtp.fromName,
49
+ };
50
+ }
51
+ if (isRecord(global.supabase)) {
52
+ if (next.database.type === "supabase-cloud") {
53
+ next.database.supabaseUrl =
54
+ stringValue(global.supabase.url) ?? next.database.supabaseUrl;
55
+ next.database.supabaseAnonKey =
56
+ stringValue(global.supabase.anonKey) ??
57
+ next.database.supabaseAnonKey;
58
+ next.database.supabaseServiceKey =
59
+ stringValue(global.supabase.serviceKey) ??
60
+ next.database.supabaseServiceKey;
61
+ next.database.supabaseAccessToken =
62
+ stringValue(global.supabase.accessToken) ??
63
+ next.database.supabaseAccessToken;
64
+ next.database.supabaseProjectRef =
65
+ stringValue(global.supabase.projectRef) ??
66
+ next.database.supabaseProjectRef;
67
+ }
68
+ else {
69
+ next.database.supabaseJwtSecret =
70
+ stringValue(global.supabase.jwtSecret) ??
71
+ next.database.supabaseJwtSecret;
72
+ }
73
+ }
74
+ if (isRecord(global.ai)) {
75
+ next.features.ai.enabled =
76
+ booleanValue(global.ai.enabled) ?? next.features.ai.enabled;
77
+ next.features.ai.openaiApiKey =
78
+ stringValue(global.ai.openaiApiKey) ??
79
+ next.features.ai.openaiApiKey;
80
+ }
81
+ if (isRecord(global.sso)) {
82
+ next.features.sso.enabled =
83
+ booleanValue(global.sso.enabled) ?? next.features.sso.enabled;
84
+ next.features.sso.provider =
85
+ stringValue(global.sso.provider) ??
86
+ next.features.sso.provider;
87
+ next.features.sso.url =
88
+ stringValue(global.sso.url) ?? next.features.sso.url;
89
+ next.features.sso.clientId =
90
+ stringValue(global.sso.clientId) ?? next.features.sso.clientId;
91
+ next.features.sso.clientSecret =
92
+ stringValue(global.sso.clientSecret) ??
93
+ next.features.sso.clientSecret;
94
+ }
95
+ if (isRecord(global.storage) && next.storage) {
96
+ const storage = global.storage;
97
+ next.storage.provider =
98
+ stringValue(storage.provider) ??
99
+ next.storage.provider;
100
+ next.storage.bucket =
101
+ stringValue(storage.bucket) ?? next.storage.bucket;
102
+ next.storage.region =
103
+ stringValue(storage.region) ?? next.storage.region;
104
+ if (isRecord(storage.s3)) {
105
+ next.storage.awsIamRoleArn =
106
+ stringValue(storage.s3.iamRoleArn) ?? next.storage.awsIamRoleArn;
107
+ }
108
+ if (isRecord(storage.azure)) {
109
+ next.storage.cloudAuthMode =
110
+ storage.azure.authMode === "connection-string"
111
+ ? "secret"
112
+ : next.storage.cloudAuthMode;
113
+ next.storage.azureBlobClientId =
114
+ stringValue(storage.azure.clientId) ??
115
+ next.storage.azureBlobClientId;
116
+ next.storage.azureBlobTenantId =
117
+ stringValue(storage.azure.tenantId) ??
118
+ next.storage.azureBlobTenantId;
119
+ next.storage.azureBlobContainer =
120
+ stringValue(storage.azure.container) ??
121
+ next.storage.azureBlobContainer;
122
+ next.storage.azureBlobConnectionStringSecretRef =
123
+ secretRefValue(storage.azure.connectionStringSecretRef) ??
124
+ next.storage.azureBlobConnectionStringSecretRef;
125
+ }
126
+ if (isRecord(storage.gcp)) {
127
+ next.storage.gcpServiceAccountEmail =
128
+ stringValue(storage.gcp.serviceAccountEmail) ??
129
+ next.storage.gcpServiceAccountEmail;
130
+ }
131
+ if (isRecord(storage.paths)) {
132
+ next.storage.paths = {
133
+ decisionLogs: stringValue(storage.paths.decisionLogs) ??
134
+ next.storage.paths?.decisionLogs,
135
+ dbBackups: stringValue(storage.paths.dbBackups) ??
136
+ next.storage.paths?.dbBackups,
137
+ };
138
+ }
139
+ }
140
+ }
141
+ if (isRecord(values.clusterIssuer)) {
142
+ next.tlsEmail = stringValue(values.clusterIssuer.email) ?? next.tlsEmail;
143
+ }
144
+ if (isRecord(values.backup)) {
145
+ next.backup = {
146
+ enabled: booleanValue(values.backup.enabled) ?? next.backup?.enabled ?? false,
147
+ schedule: stringValue(values.backup.schedule) ?? next.backup?.schedule ?? "0 2 * * *",
148
+ retentionDays: numberValue(values.backup.retentionDays) ??
149
+ next.backup?.retentionDays ??
150
+ 7,
151
+ };
152
+ }
153
+ // External services (managed Redis/Kafka). The saved config is authoritative;
154
+ // here we reconcile mode + connection details from the live chart values so a
155
+ // redeploy reflects any drift. Preset/identity fall back to the saved config.
156
+ const rb = isRecord(values.rulebricks) ? values.rulebricks : null;
157
+ const redisLive = rb && isRecord(rb.redis) ? rb.redis : null;
158
+ if (redisLive) {
159
+ const redisEnabled = booleanValue(redisLive.enabled);
160
+ const savedRedis = next.externalServices?.redis;
161
+ if (redisEnabled === false) {
162
+ const ext = isRecord(redisLive.external) ? redisLive.external : {};
163
+ const tls = isRecord(ext.tls) ? booleanValue(ext.tls.enabled) : undefined;
164
+ const httpApiLive = isRecord(ext.httpApi) ? ext.httpApi : null;
165
+ next.externalServices = {
166
+ ...next.externalServices,
167
+ redis: {
168
+ mode: "external",
169
+ external: {
170
+ ...(savedRedis?.external ?? {}),
171
+ host: stringValue(ext.host) ?? savedRedis?.external?.host,
172
+ port: numberValue(ext.port) ?? savedRedis?.external?.port,
173
+ password: stringValue(ext.password) ?? savedRedis?.external?.password,
174
+ existingSecret: stringValue(ext.existingSecret) ??
175
+ savedRedis?.external?.existingSecret,
176
+ existingSecretKey: stringValue(ext.existingSecretKey) ??
177
+ savedRedis?.external?.existingSecretKey,
178
+ tls: tls ?? savedRedis?.external?.tls,
179
+ httpApi: httpApiLive
180
+ ? {
181
+ enabled: booleanValue(httpApiLive.enabled) ?? false,
182
+ url: stringValue(httpApiLive.url),
183
+ token: stringValue(httpApiLive.token),
184
+ }
185
+ : savedRedis?.external?.httpApi,
186
+ },
187
+ },
188
+ };
189
+ }
190
+ else if (redisEnabled === true) {
191
+ next.externalServices = {
192
+ ...next.externalServices,
193
+ redis: { mode: "embedded" },
194
+ };
195
+ }
196
+ }
197
+ const kafkaEnabled = isRecord(values.kafka)
198
+ ? booleanValue(values.kafka.enabled)
199
+ : undefined;
200
+ const loggingLive = rb && isRecord(rb.app) && isRecord(rb.app.logging) ? rb.app.logging : null;
201
+ if (kafkaEnabled === false && loggingLive) {
202
+ const savedKafka = next.externalServices?.kafka;
203
+ const saslLive = isRecord(loggingLive.kafkaSasl)
204
+ ? loggingLive.kafkaSasl
205
+ : null;
206
+ const bridge = isRecord(values.kafkaBridge) ? values.kafkaBridge : null;
207
+ const mechanism = stringValue(saslLive?.mechanism) ?? savedKafka?.external?.sasl?.mechanism;
208
+ next.externalServices = {
209
+ ...next.externalServices,
210
+ kafka: {
211
+ mode: "external",
212
+ external: {
213
+ ...(savedKafka?.external ?? {}),
214
+ preset: savedKafka?.external?.preset ?? "custom",
215
+ brokers: stringValue(loggingLive.kafkaBrokers) ??
216
+ savedKafka?.external?.brokers,
217
+ topic: stringValue(loggingLive.kafkaTopic) ??
218
+ savedKafka?.external?.topic,
219
+ topicPrefix: stringValue(loggingLive.kafkaTopicPrefix) ??
220
+ savedKafka?.external?.topicPrefix,
221
+ ssl: booleanValue(loggingLive.kafkaSsl) ?? savedKafka?.external?.ssl,
222
+ sasl: mechanism
223
+ ? {
224
+ mechanism,
225
+ region: stringValue(saslLive?.region) ??
226
+ savedKafka?.external?.sasl?.region,
227
+ username: stringValue(saslLive?.username) ??
228
+ savedKafka?.external?.sasl?.username,
229
+ password: stringValue(saslLive?.password) ??
230
+ savedKafka?.external?.sasl?.password,
231
+ existingSecret: stringValue(saslLive?.existingSecret) ??
232
+ savedKafka?.external?.sasl?.existingSecret,
233
+ }
234
+ : savedKafka?.external?.sasl,
235
+ identity: {
236
+ ...(savedKafka?.external?.identity ?? {}),
237
+ awsRoleArn: stringValue(bridge?.awsRoleArn) ||
238
+ savedKafka?.external?.identity?.awsRoleArn,
239
+ },
240
+ },
241
+ },
242
+ };
243
+ }
244
+ else if (kafkaEnabled === true) {
245
+ next.externalServices = {
246
+ ...next.externalServices,
247
+ kafka: { mode: "embedded" },
248
+ };
249
+ }
250
+ return next;
251
+ }
252
+ function RedeployCommandInner({ name, chartVersion }) {
253
+ const { colors } = useTheme();
254
+ const [step, setStep] = useState("loading");
255
+ const [error, setError] = useState(null);
256
+ const [wizardState, setWizardState] = useState(null);
257
+ const [profile, setProfile] = useState(null);
258
+ const [resolvedChartVersion, setResolvedChartVersion] = useState(chartVersion);
259
+ useEffect(() => {
260
+ (async () => {
261
+ try {
262
+ const health = await loadDeploymentHealth(name, {
263
+ refreshKubeconfig: true,
264
+ });
265
+ if (!health.config) {
266
+ setError(health.configError || `Deployment "${name}" is invalid.`);
267
+ setStep("error");
268
+ return;
269
+ }
270
+ if (health.kind !== "online") {
271
+ setError(`Deployment "${name}" must be installed and reachable before redeploy. Current status: ${health.kind}.`);
272
+ setStep("error");
273
+ return;
274
+ }
275
+ const [loadedProfile, values] = await Promise.all([
276
+ loadProfile(),
277
+ loadHelmValues(name),
278
+ ]);
279
+ const hydratedConfig = applyHelmValuesToConfig(health.config, values);
280
+ const installedChartVersion = chartVersion ||
281
+ (health.state?.application?.chartVersion !== "latest"
282
+ ? health.state?.application?.chartVersion
283
+ : undefined) ||
284
+ (await getInstalledChartVersion(health.releaseName, health.namespace)) ||
285
+ undefined;
286
+ setProfile(loadedProfile);
287
+ setResolvedChartVersion(installedChartVersion);
288
+ setWizardState(configToWizardState(hydratedConfig, loadedProfile));
289
+ setStep("wizard");
290
+ }
291
+ catch (err) {
292
+ setError(err instanceof Error ? err.message : "Failed to load redeploy");
293
+ setStep("error");
294
+ }
295
+ })();
296
+ }, [name, chartVersion]);
297
+ if (step === "deploy") {
298
+ return (_jsx(DeployCommand, { name: name, version: resolvedChartVersion, regenerateValues: false, assumeDnsConfigured: true }));
299
+ }
300
+ if (step === "wizard" && wizardState) {
301
+ return (_jsx(InitWizard, { initialState: wizardState, mode: "redeploy", profile: profile, onSaveComplete: () => setStep("deploy") }));
302
+ }
303
+ if (step === "error") {
304
+ return (_jsxs(ThemeProvider, { theme: "deploy", children: [_jsx(Logo, {}), _jsx(BorderBox, { title: "Redeploy Failed", children: _jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { color: colors.error, bold: true, children: "\u2717 Error" }), _jsx(Text, { color: colors.error, children: error })] }) })] }));
305
+ }
306
+ return (_jsxs(ThemeProvider, { theme: "deploy", children: [_jsx(Logo, {}), _jsx(BorderBox, { title: "Redeploy", children: _jsx(Box, { marginY: 1, children: _jsx(Spinner, { label: "Checking deployment health..." }) }) })] }));
307
+ }
308
+ export function RedeployCommand(props) {
309
+ return _jsx(RedeployCommandInner, { ...props });
310
+ }
@@ -0,0 +1,5 @@
1
+ interface RestoreCommandProps {
2
+ name: string;
3
+ }
4
+ export declare function RestoreCommand(props: RestoreCommandProps): import("react/jsx-runtime").JSX.Element;
5
+ export {};