@build-astron-co/nimbus 0.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.
- package/LICENSE +21 -0
- package/README.md +628 -0
- package/bin/nimbus +38 -0
- package/package.json +80 -0
- package/src/__tests__/app.test.ts +76 -0
- package/src/__tests__/audit.test.ts +877 -0
- package/src/__tests__/circuit-breaker.test.ts +116 -0
- package/src/__tests__/cli-run.test.ts +115 -0
- package/src/__tests__/context-manager.test.ts +502 -0
- package/src/__tests__/context.test.ts +242 -0
- package/src/__tests__/enterprise.test.ts +401 -0
- package/src/__tests__/generator.test.ts +433 -0
- package/src/__tests__/hooks.test.ts +582 -0
- package/src/__tests__/init.test.ts +436 -0
- package/src/__tests__/intent-parser.test.ts +229 -0
- package/src/__tests__/llm-router.test.ts +209 -0
- package/src/__tests__/lsp.test.ts +293 -0
- package/src/__tests__/modes.test.ts +336 -0
- package/src/__tests__/permissions.test.ts +338 -0
- package/src/__tests__/serve.test.ts +275 -0
- package/src/__tests__/sessions.test.ts +227 -0
- package/src/__tests__/sharing.test.ts +288 -0
- package/src/__tests__/snapshots.test.ts +581 -0
- package/src/__tests__/state-db.test.ts +334 -0
- package/src/__tests__/stream-with-tools.test.ts +732 -0
- package/src/__tests__/subagents.test.ts +176 -0
- package/src/__tests__/system-prompt.test.ts +169 -0
- package/src/__tests__/tool-converter.test.ts +256 -0
- package/src/__tests__/tool-schemas.test.ts +397 -0
- package/src/__tests__/tools.test.ts +143 -0
- package/src/__tests__/version.test.ts +49 -0
- package/src/agent/compaction-agent.ts +227 -0
- package/src/agent/context-manager.ts +435 -0
- package/src/agent/context.ts +427 -0
- package/src/agent/deploy-preview.ts +426 -0
- package/src/agent/index.ts +68 -0
- package/src/agent/loop.ts +717 -0
- package/src/agent/modes.ts +429 -0
- package/src/agent/permissions.ts +466 -0
- package/src/agent/subagents/base.ts +116 -0
- package/src/agent/subagents/cost.ts +51 -0
- package/src/agent/subagents/explore.ts +42 -0
- package/src/agent/subagents/general.ts +54 -0
- package/src/agent/subagents/index.ts +102 -0
- package/src/agent/subagents/infra.ts +59 -0
- package/src/agent/subagents/security.ts +69 -0
- package/src/agent/system-prompt.ts +436 -0
- package/src/app.ts +122 -0
- package/src/audit/activity-log.ts +290 -0
- package/src/audit/compliance-checker.ts +540 -0
- package/src/audit/cost-tracker.ts +318 -0
- package/src/audit/index.ts +23 -0
- package/src/audit/security-scanner.ts +596 -0
- package/src/auth/guard.ts +75 -0
- package/src/auth/index.ts +56 -0
- package/src/auth/oauth.ts +455 -0
- package/src/auth/providers.ts +470 -0
- package/src/auth/sso.ts +113 -0
- package/src/auth/store.ts +505 -0
- package/src/auth/types.ts +187 -0
- package/src/build.ts +141 -0
- package/src/cli/index.ts +16 -0
- package/src/cli/init.ts +854 -0
- package/src/cli/openapi-spec.ts +356 -0
- package/src/cli/run.ts +237 -0
- package/src/cli/serve-auth.ts +80 -0
- package/src/cli/serve.ts +462 -0
- package/src/cli/web.ts +67 -0
- package/src/cli.ts +1417 -0
- package/src/clients/core-engine-client.ts +227 -0
- package/src/clients/enterprise-client.ts +334 -0
- package/src/clients/generator-client.ts +351 -0
- package/src/clients/git-client.ts +627 -0
- package/src/clients/github-client.ts +410 -0
- package/src/clients/helm-client.ts +504 -0
- package/src/clients/index.ts +80 -0
- package/src/clients/k8s-client.ts +497 -0
- package/src/clients/llm-client.ts +161 -0
- package/src/clients/rest-client.ts +130 -0
- package/src/clients/service-discovery.ts +33 -0
- package/src/clients/terraform-client.ts +482 -0
- package/src/clients/tools-client.ts +1843 -0
- package/src/clients/ws-client.ts +115 -0
- package/src/commands/analyze/index.ts +352 -0
- package/src/commands/apply/helm.ts +473 -0
- package/src/commands/apply/index.ts +213 -0
- package/src/commands/apply/k8s.ts +454 -0
- package/src/commands/apply/terraform.ts +582 -0
- package/src/commands/ask.ts +167 -0
- package/src/commands/audit/index.ts +238 -0
- package/src/commands/auth-cloud.ts +294 -0
- package/src/commands/auth-list.ts +134 -0
- package/src/commands/auth-profile.ts +121 -0
- package/src/commands/auth-status.ts +141 -0
- package/src/commands/aws/ec2.ts +501 -0
- package/src/commands/aws/iam.ts +397 -0
- package/src/commands/aws/index.ts +133 -0
- package/src/commands/aws/lambda.ts +396 -0
- package/src/commands/aws/rds.ts +439 -0
- package/src/commands/aws/s3.ts +439 -0
- package/src/commands/aws/vpc.ts +393 -0
- package/src/commands/aws-discover.ts +649 -0
- package/src/commands/aws-terraform.ts +805 -0
- package/src/commands/azure/aks.ts +376 -0
- package/src/commands/azure/functions.ts +253 -0
- package/src/commands/azure/index.ts +116 -0
- package/src/commands/azure/storage.ts +478 -0
- package/src/commands/azure/vm.ts +355 -0
- package/src/commands/billing/index.ts +256 -0
- package/src/commands/chat.ts +314 -0
- package/src/commands/config.ts +346 -0
- package/src/commands/cost/cloud-cost-estimator.ts +266 -0
- package/src/commands/cost/estimator.ts +79 -0
- package/src/commands/cost/index.ts +594 -0
- package/src/commands/cost/parsers/terraform.ts +273 -0
- package/src/commands/cost/parsers/types.ts +25 -0
- package/src/commands/cost/pricing/aws.ts +544 -0
- package/src/commands/cost/pricing/azure.ts +499 -0
- package/src/commands/cost/pricing/gcp.ts +396 -0
- package/src/commands/cost/pricing/index.ts +40 -0
- package/src/commands/demo.ts +250 -0
- package/src/commands/doctor.ts +794 -0
- package/src/commands/drift/index.ts +439 -0
- package/src/commands/explain.ts +277 -0
- package/src/commands/feedback.ts +389 -0
- package/src/commands/fix.ts +324 -0
- package/src/commands/fs/index.ts +402 -0
- package/src/commands/gcp/compute.ts +325 -0
- package/src/commands/gcp/functions.ts +271 -0
- package/src/commands/gcp/gke.ts +438 -0
- package/src/commands/gcp/iam.ts +344 -0
- package/src/commands/gcp/index.ts +129 -0
- package/src/commands/gcp/storage.ts +284 -0
- package/src/commands/generate-helm.ts +1249 -0
- package/src/commands/generate-k8s.ts +1560 -0
- package/src/commands/generate-terraform.ts +1460 -0
- package/src/commands/gh/index.ts +863 -0
- package/src/commands/git/index.ts +1343 -0
- package/src/commands/helm/index.ts +1126 -0
- package/src/commands/help.ts +539 -0
- package/src/commands/history.ts +142 -0
- package/src/commands/import.ts +868 -0
- package/src/commands/index.ts +367 -0
- package/src/commands/init.ts +1046 -0
- package/src/commands/k8s/index.ts +1137 -0
- package/src/commands/login.ts +631 -0
- package/src/commands/logout.ts +83 -0
- package/src/commands/onboarding.ts +228 -0
- package/src/commands/plan/display.ts +279 -0
- package/src/commands/plan/index.ts +599 -0
- package/src/commands/preview.ts +452 -0
- package/src/commands/questionnaire.ts +1270 -0
- package/src/commands/resume.ts +55 -0
- package/src/commands/team/index.ts +346 -0
- package/src/commands/template.ts +232 -0
- package/src/commands/tf/index.ts +1034 -0
- package/src/commands/upgrade.ts +550 -0
- package/src/commands/usage/index.ts +134 -0
- package/src/commands/version.ts +170 -0
- package/src/compat/index.ts +2 -0
- package/src/compat/runtime.ts +12 -0
- package/src/compat/sqlite.ts +107 -0
- package/src/config/index.ts +17 -0
- package/src/config/manager.ts +530 -0
- package/src/config/safety-policy.ts +358 -0
- package/src/config/schema.ts +125 -0
- package/src/config/types.ts +527 -0
- package/src/context/context-db.ts +199 -0
- package/src/demo/index.ts +349 -0
- package/src/demo/scenarios/full-journey.ts +229 -0
- package/src/demo/scenarios/getting-started.ts +127 -0
- package/src/demo/scenarios/helm-release.ts +341 -0
- package/src/demo/scenarios/k8s-deployment.ts +194 -0
- package/src/demo/scenarios/terraform-vpc.ts +170 -0
- package/src/demo/types.ts +92 -0
- package/src/engine/cost-estimator.ts +438 -0
- package/src/engine/diagram-generator.ts +256 -0
- package/src/engine/drift-detector.ts +902 -0
- package/src/engine/executor.ts +1035 -0
- package/src/engine/index.ts +76 -0
- package/src/engine/orchestrator.ts +636 -0
- package/src/engine/planner.ts +720 -0
- package/src/engine/safety.ts +743 -0
- package/src/engine/verifier.ts +770 -0
- package/src/enterprise/audit.ts +348 -0
- package/src/enterprise/auth.ts +270 -0
- package/src/enterprise/billing.ts +822 -0
- package/src/enterprise/index.ts +17 -0
- package/src/enterprise/teams.ts +443 -0
- package/src/generator/best-practices.ts +1608 -0
- package/src/generator/helm.ts +630 -0
- package/src/generator/index.ts +37 -0
- package/src/generator/intent-parser.ts +514 -0
- package/src/generator/kubernetes.ts +976 -0
- package/src/generator/terraform.ts +1867 -0
- package/src/history/index.ts +8 -0
- package/src/history/manager.ts +322 -0
- package/src/history/types.ts +34 -0
- package/src/hooks/config.ts +432 -0
- package/src/hooks/engine.ts +391 -0
- package/src/hooks/index.ts +4 -0
- package/src/llm/auth-bridge.ts +198 -0
- package/src/llm/circuit-breaker.ts +140 -0
- package/src/llm/config-loader.ts +201 -0
- package/src/llm/cost-calculator.ts +171 -0
- package/src/llm/index.ts +8 -0
- package/src/llm/model-aliases.ts +115 -0
- package/src/llm/provider-registry.ts +63 -0
- package/src/llm/providers/anthropic.ts +433 -0
- package/src/llm/providers/bedrock.ts +477 -0
- package/src/llm/providers/google.ts +405 -0
- package/src/llm/providers/ollama.ts +767 -0
- package/src/llm/providers/openai-compatible.ts +340 -0
- package/src/llm/providers/openai.ts +328 -0
- package/src/llm/providers/openrouter.ts +338 -0
- package/src/llm/router.ts +1035 -0
- package/src/llm/types.ts +232 -0
- package/src/lsp/client.ts +298 -0
- package/src/lsp/languages.ts +116 -0
- package/src/lsp/manager.ts +278 -0
- package/src/mcp/client.ts +402 -0
- package/src/mcp/index.ts +5 -0
- package/src/mcp/manager.ts +133 -0
- package/src/nimbus.ts +214 -0
- package/src/plugins/index.ts +27 -0
- package/src/plugins/loader.ts +334 -0
- package/src/plugins/manager.ts +376 -0
- package/src/plugins/types.ts +284 -0
- package/src/scanners/cicd-scanner.ts +258 -0
- package/src/scanners/cloud-scanner.ts +466 -0
- package/src/scanners/framework-scanner.ts +469 -0
- package/src/scanners/iac-scanner.ts +388 -0
- package/src/scanners/index.ts +539 -0
- package/src/scanners/language-scanner.ts +276 -0
- package/src/scanners/package-manager-scanner.ts +277 -0
- package/src/scanners/types.ts +172 -0
- package/src/sessions/manager.ts +365 -0
- package/src/sessions/types.ts +44 -0
- package/src/sharing/sync.ts +296 -0
- package/src/sharing/viewer.ts +97 -0
- package/src/snapshots/index.ts +2 -0
- package/src/snapshots/manager.ts +530 -0
- package/src/state/artifacts.ts +147 -0
- package/src/state/audit.ts +137 -0
- package/src/state/billing.ts +240 -0
- package/src/state/checkpoints.ts +117 -0
- package/src/state/config.ts +67 -0
- package/src/state/conversations.ts +14 -0
- package/src/state/credentials.ts +154 -0
- package/src/state/db.ts +58 -0
- package/src/state/index.ts +26 -0
- package/src/state/messages.ts +115 -0
- package/src/state/projects.ts +123 -0
- package/src/state/schema.ts +236 -0
- package/src/state/sessions.ts +147 -0
- package/src/state/teams.ts +200 -0
- package/src/telemetry.ts +108 -0
- package/src/tools/aws-ops.ts +952 -0
- package/src/tools/azure-ops.ts +579 -0
- package/src/tools/file-ops.ts +593 -0
- package/src/tools/gcp-ops.ts +625 -0
- package/src/tools/git-ops.ts +773 -0
- package/src/tools/github-ops.ts +799 -0
- package/src/tools/helm-ops.ts +943 -0
- package/src/tools/index.ts +17 -0
- package/src/tools/k8s-ops.ts +819 -0
- package/src/tools/schemas/converter.ts +184 -0
- package/src/tools/schemas/devops.ts +612 -0
- package/src/tools/schemas/index.ts +73 -0
- package/src/tools/schemas/standard.ts +1144 -0
- package/src/tools/schemas/types.ts +705 -0
- package/src/tools/terraform-ops.ts +862 -0
- package/src/types/ambient.d.ts +193 -0
- package/src/types/config.ts +83 -0
- package/src/types/drift.ts +116 -0
- package/src/types/enterprise.ts +335 -0
- package/src/types/index.ts +20 -0
- package/src/types/plan.ts +44 -0
- package/src/types/request.ts +65 -0
- package/src/types/response.ts +54 -0
- package/src/types/service.ts +51 -0
- package/src/ui/App.tsx +997 -0
- package/src/ui/DeployPreview.tsx +169 -0
- package/src/ui/Header.tsx +68 -0
- package/src/ui/InputBox.tsx +350 -0
- package/src/ui/MessageList.tsx +585 -0
- package/src/ui/PermissionPrompt.tsx +151 -0
- package/src/ui/StatusBar.tsx +158 -0
- package/src/ui/ToolCallDisplay.tsx +409 -0
- package/src/ui/chat-ui.ts +853 -0
- package/src/ui/index.ts +33 -0
- package/src/ui/ink/index.ts +711 -0
- package/src/ui/streaming.ts +176 -0
- package/src/ui/types.ts +57 -0
- package/src/utils/analytics.ts +72 -0
- package/src/utils/cost-warning.ts +27 -0
- package/src/utils/env.ts +46 -0
- package/src/utils/errors.ts +69 -0
- package/src/utils/event-bus.ts +38 -0
- package/src/utils/index.ts +24 -0
- package/src/utils/logger.ts +171 -0
- package/src/utils/rate-limiter.ts +121 -0
- package/src/utils/service-auth.ts +49 -0
- package/src/utils/validation.ts +53 -0
- package/src/version.ts +4 -0
- package/src/watcher/index.ts +163 -0
- package/src/wizard/approval.ts +383 -0
- package/src/wizard/index.ts +25 -0
- package/src/wizard/prompts.ts +338 -0
- package/src/wizard/types.ts +171 -0
- package/src/wizard/ui.ts +556 -0
- package/src/wizard/wizard.ts +304 -0
- package/tsconfig.json +24 -0
|
@@ -0,0 +1,1249 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate Helm Values Command
|
|
3
|
+
*
|
|
4
|
+
* Interactive wizard for generating Helm values files
|
|
5
|
+
*
|
|
6
|
+
* Usage: nimbus generate helm [options]
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { logger } from '../utils';
|
|
10
|
+
import {
|
|
11
|
+
createWizard,
|
|
12
|
+
ui,
|
|
13
|
+
select,
|
|
14
|
+
confirm,
|
|
15
|
+
input,
|
|
16
|
+
pathInput,
|
|
17
|
+
type WizardStep,
|
|
18
|
+
type StepResult,
|
|
19
|
+
} from '../wizard';
|
|
20
|
+
import { helmClient } from '../clients';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Environment types
|
|
24
|
+
*/
|
|
25
|
+
export type HelmEnvironment = 'dev' | 'staging' | 'production';
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Command options from CLI arguments
|
|
29
|
+
*/
|
|
30
|
+
export interface GenerateHelmOptions {
|
|
31
|
+
chart?: string;
|
|
32
|
+
releaseName?: string;
|
|
33
|
+
namespace?: string;
|
|
34
|
+
values?: Record<string, unknown>;
|
|
35
|
+
valuesFile?: string;
|
|
36
|
+
output?: string;
|
|
37
|
+
nonInteractive?: boolean;
|
|
38
|
+
includeSecrets?: boolean;
|
|
39
|
+
environment?: HelmEnvironment;
|
|
40
|
+
version?: string;
|
|
41
|
+
repo?: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Wizard context for Helm generation
|
|
46
|
+
*/
|
|
47
|
+
export interface HelmWizardContext {
|
|
48
|
+
// Chart selection
|
|
49
|
+
chartSource?: 'repo' | 'local';
|
|
50
|
+
chart?: string;
|
|
51
|
+
chartVersion?: string;
|
|
52
|
+
repoName?: string;
|
|
53
|
+
repoUrl?: string;
|
|
54
|
+
localPath?: string;
|
|
55
|
+
|
|
56
|
+
// Release configuration
|
|
57
|
+
releaseName?: string;
|
|
58
|
+
namespace?: string;
|
|
59
|
+
|
|
60
|
+
// Environment
|
|
61
|
+
environment?: HelmEnvironment;
|
|
62
|
+
|
|
63
|
+
// Values customization
|
|
64
|
+
customValues?: Record<string, unknown>;
|
|
65
|
+
imageRepository?: string;
|
|
66
|
+
imageTag?: string;
|
|
67
|
+
replicas?: number;
|
|
68
|
+
cpuRequest?: string;
|
|
69
|
+
cpuLimit?: string;
|
|
70
|
+
memoryRequest?: string;
|
|
71
|
+
memoryLimit?: string;
|
|
72
|
+
|
|
73
|
+
// Service configuration
|
|
74
|
+
serviceType?: 'ClusterIP' | 'NodePort' | 'LoadBalancer';
|
|
75
|
+
servicePort?: number;
|
|
76
|
+
|
|
77
|
+
// Ingress configuration
|
|
78
|
+
ingressEnabled?: boolean;
|
|
79
|
+
ingressHost?: string;
|
|
80
|
+
ingressTls?: boolean;
|
|
81
|
+
|
|
82
|
+
// Secret management
|
|
83
|
+
includeSecrets?: boolean;
|
|
84
|
+
secretValues?: Record<string, string>;
|
|
85
|
+
|
|
86
|
+
// Chart default values (fetched from chart)
|
|
87
|
+
defaultValues?: string;
|
|
88
|
+
|
|
89
|
+
// Output
|
|
90
|
+
outputPath?: string;
|
|
91
|
+
generatedFiles?: string[];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Popular Helm charts with their repos
|
|
96
|
+
*/
|
|
97
|
+
const POPULAR_CHARTS = [
|
|
98
|
+
{
|
|
99
|
+
name: 'nginx',
|
|
100
|
+
repo: 'bitnami',
|
|
101
|
+
url: 'https://charts.bitnami.com/bitnami',
|
|
102
|
+
description: 'NGINX web server',
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
name: 'postgresql',
|
|
106
|
+
repo: 'bitnami',
|
|
107
|
+
url: 'https://charts.bitnami.com/bitnami',
|
|
108
|
+
description: 'PostgreSQL database',
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
name: 'redis',
|
|
112
|
+
repo: 'bitnami',
|
|
113
|
+
url: 'https://charts.bitnami.com/bitnami',
|
|
114
|
+
description: 'Redis cache',
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
name: 'mysql',
|
|
118
|
+
repo: 'bitnami',
|
|
119
|
+
url: 'https://charts.bitnami.com/bitnami',
|
|
120
|
+
description: 'MySQL database',
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
name: 'mongodb',
|
|
124
|
+
repo: 'bitnami',
|
|
125
|
+
url: 'https://charts.bitnami.com/bitnami',
|
|
126
|
+
description: 'MongoDB database',
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
name: 'kafka',
|
|
130
|
+
repo: 'bitnami',
|
|
131
|
+
url: 'https://charts.bitnami.com/bitnami',
|
|
132
|
+
description: 'Apache Kafka',
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
name: 'rabbitmq',
|
|
136
|
+
repo: 'bitnami',
|
|
137
|
+
url: 'https://charts.bitnami.com/bitnami',
|
|
138
|
+
description: 'RabbitMQ message broker',
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
name: 'elasticsearch',
|
|
142
|
+
repo: 'elastic',
|
|
143
|
+
url: 'https://helm.elastic.co',
|
|
144
|
+
description: 'Elasticsearch search engine',
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
name: 'prometheus',
|
|
148
|
+
repo: 'prometheus-community',
|
|
149
|
+
url: 'https://prometheus-community.github.io/helm-charts',
|
|
150
|
+
description: 'Prometheus monitoring',
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
name: 'grafana',
|
|
154
|
+
repo: 'grafana',
|
|
155
|
+
url: 'https://grafana.github.io/helm-charts',
|
|
156
|
+
description: 'Grafana dashboards',
|
|
157
|
+
},
|
|
158
|
+
];
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Run the generate helm command
|
|
162
|
+
*/
|
|
163
|
+
export async function generateHelmCommand(options: GenerateHelmOptions = {}): Promise<void> {
|
|
164
|
+
logger.info('Starting Helm values generation wizard');
|
|
165
|
+
|
|
166
|
+
// Non-interactive mode
|
|
167
|
+
if (options.nonInteractive) {
|
|
168
|
+
await runNonInteractive(options);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Interactive wizard mode
|
|
173
|
+
const wizard = createWizard<HelmWizardContext>({
|
|
174
|
+
title: 'nimbus generate helm',
|
|
175
|
+
description: 'Generate Helm values files for your deployment',
|
|
176
|
+
initialContext: {
|
|
177
|
+
chart: options.chart,
|
|
178
|
+
releaseName: options.releaseName,
|
|
179
|
+
namespace: options.namespace,
|
|
180
|
+
environment: options.environment,
|
|
181
|
+
includeSecrets: options.includeSecrets,
|
|
182
|
+
outputPath: options.output,
|
|
183
|
+
chartVersion: options.version,
|
|
184
|
+
},
|
|
185
|
+
steps: createWizardSteps(),
|
|
186
|
+
onEvent: event => {
|
|
187
|
+
logger.debug('Wizard event', { type: event.type });
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
const result = await wizard.run();
|
|
192
|
+
|
|
193
|
+
if (result.success) {
|
|
194
|
+
ui.newLine();
|
|
195
|
+
ui.box({
|
|
196
|
+
title: 'Complete!',
|
|
197
|
+
content: [
|
|
198
|
+
'Your Helm values have been generated.',
|
|
199
|
+
'',
|
|
200
|
+
'Generated files:',
|
|
201
|
+
...(result.context.generatedFiles?.map(f => ` - ${f}`) || [' - values.yaml']),
|
|
202
|
+
'',
|
|
203
|
+
'Next steps:',
|
|
204
|
+
` 1. Review the generated files in ${result.context.outputPath}`,
|
|
205
|
+
' 2. Customize values as needed for your environment',
|
|
206
|
+
` 3. Run "helm install ${result.context.releaseName} ${result.context.chart} -f <values-file>"`,
|
|
207
|
+
' or use "nimbus apply helm"',
|
|
208
|
+
],
|
|
209
|
+
style: 'rounded',
|
|
210
|
+
borderColor: 'green',
|
|
211
|
+
padding: 1,
|
|
212
|
+
});
|
|
213
|
+
} else {
|
|
214
|
+
ui.error(`Wizard failed: ${result.error?.message || 'Unknown error'}`);
|
|
215
|
+
process.exit(1);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Create wizard steps
|
|
221
|
+
*/
|
|
222
|
+
function createWizardSteps(): WizardStep<HelmWizardContext>[] {
|
|
223
|
+
return [
|
|
224
|
+
// Step 1: Chart Selection
|
|
225
|
+
{
|
|
226
|
+
id: 'chart-selection',
|
|
227
|
+
title: 'Chart Selection',
|
|
228
|
+
description: 'Select a Helm chart to configure',
|
|
229
|
+
execute: chartSelectionStep,
|
|
230
|
+
},
|
|
231
|
+
|
|
232
|
+
// Step 2: Release Configuration
|
|
233
|
+
{
|
|
234
|
+
id: 'release-config',
|
|
235
|
+
title: 'Release Configuration',
|
|
236
|
+
description: 'Configure release name and namespace',
|
|
237
|
+
execute: releaseConfigStep,
|
|
238
|
+
},
|
|
239
|
+
|
|
240
|
+
// Step 3: Environment Selection
|
|
241
|
+
{
|
|
242
|
+
id: 'environment',
|
|
243
|
+
title: 'Environment Selection',
|
|
244
|
+
description: 'Select target environment',
|
|
245
|
+
execute: environmentStep,
|
|
246
|
+
},
|
|
247
|
+
|
|
248
|
+
// Step 4: Image Configuration
|
|
249
|
+
{
|
|
250
|
+
id: 'image-config',
|
|
251
|
+
title: 'Image Configuration',
|
|
252
|
+
description: 'Configure container image settings',
|
|
253
|
+
execute: imageConfigStep,
|
|
254
|
+
},
|
|
255
|
+
|
|
256
|
+
// Step 5: Resource Configuration
|
|
257
|
+
{
|
|
258
|
+
id: 'resources',
|
|
259
|
+
title: 'Resource Configuration',
|
|
260
|
+
description: 'Configure replicas and resource limits',
|
|
261
|
+
execute: resourceConfigStep,
|
|
262
|
+
},
|
|
263
|
+
|
|
264
|
+
// Step 6: Service Configuration
|
|
265
|
+
{
|
|
266
|
+
id: 'service-config',
|
|
267
|
+
title: 'Service Configuration',
|
|
268
|
+
description: 'Configure service exposure',
|
|
269
|
+
execute: serviceConfigStep,
|
|
270
|
+
},
|
|
271
|
+
|
|
272
|
+
// Step 7: Ingress Configuration
|
|
273
|
+
{
|
|
274
|
+
id: 'ingress-config',
|
|
275
|
+
title: 'Ingress Configuration',
|
|
276
|
+
description: 'Configure ingress settings',
|
|
277
|
+
execute: ingressConfigStep,
|
|
278
|
+
},
|
|
279
|
+
|
|
280
|
+
// Step 8: Secrets Configuration
|
|
281
|
+
{
|
|
282
|
+
id: 'secrets-config',
|
|
283
|
+
title: 'Secrets Configuration',
|
|
284
|
+
description: 'Configure secret values',
|
|
285
|
+
execute: secretsConfigStep,
|
|
286
|
+
},
|
|
287
|
+
|
|
288
|
+
// Step 9: Output Configuration
|
|
289
|
+
{
|
|
290
|
+
id: 'output',
|
|
291
|
+
title: 'Output Configuration',
|
|
292
|
+
description: 'Configure where to save the values files',
|
|
293
|
+
execute: outputConfigStep,
|
|
294
|
+
},
|
|
295
|
+
|
|
296
|
+
// Step 10: Generate
|
|
297
|
+
{
|
|
298
|
+
id: 'generate',
|
|
299
|
+
title: 'Generate Values',
|
|
300
|
+
description: 'Generating your Helm values files...',
|
|
301
|
+
execute: generateStep,
|
|
302
|
+
},
|
|
303
|
+
];
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Step 1: Chart Selection
|
|
308
|
+
*/
|
|
309
|
+
async function chartSelectionStep(ctx: HelmWizardContext): Promise<StepResult> {
|
|
310
|
+
// Chart source selection
|
|
311
|
+
const chartSource = await select<'repo' | 'local' | 'popular'>({
|
|
312
|
+
message: 'How would you like to select a chart?',
|
|
313
|
+
options: [
|
|
314
|
+
{
|
|
315
|
+
value: 'popular',
|
|
316
|
+
label: 'Popular charts',
|
|
317
|
+
description: 'Choose from commonly used Helm charts',
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
value: 'repo',
|
|
321
|
+
label: 'From repository',
|
|
322
|
+
description: 'Specify a chart from a Helm repository',
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
value: 'local',
|
|
326
|
+
label: 'Local chart',
|
|
327
|
+
description: 'Use a local chart directory',
|
|
328
|
+
},
|
|
329
|
+
],
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
if (!chartSource) {
|
|
333
|
+
return { success: false, error: 'No chart source selected' };
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
let chart: string | undefined;
|
|
337
|
+
let repoName: string | undefined;
|
|
338
|
+
let repoUrl: string | undefined;
|
|
339
|
+
let localPath: string | undefined;
|
|
340
|
+
let chartVersion: string | undefined;
|
|
341
|
+
|
|
342
|
+
if (chartSource === 'popular') {
|
|
343
|
+
// Show popular charts
|
|
344
|
+
ui.newLine();
|
|
345
|
+
const selectedChart = await select({
|
|
346
|
+
message: 'Select a popular chart:',
|
|
347
|
+
options: POPULAR_CHARTS.map(c => ({
|
|
348
|
+
value: c.name,
|
|
349
|
+
label: `${c.repo}/${c.name}`,
|
|
350
|
+
description: c.description,
|
|
351
|
+
})),
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
if (!selectedChart) {
|
|
355
|
+
return { success: false, error: 'No chart selected' };
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
const chartInfo = POPULAR_CHARTS.find(c => c.name === selectedChart);
|
|
359
|
+
chart = `${chartInfo!.repo}/${chartInfo!.name}`;
|
|
360
|
+
repoName = chartInfo!.repo;
|
|
361
|
+
repoUrl = chartInfo!.url;
|
|
362
|
+
} else if (chartSource === 'repo') {
|
|
363
|
+
// Manual chart specification
|
|
364
|
+
ui.newLine();
|
|
365
|
+
repoName = await input({
|
|
366
|
+
message: 'Repository name (e.g., bitnami):',
|
|
367
|
+
defaultValue: ctx.repoName,
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
repoUrl = await input({
|
|
371
|
+
message: 'Repository URL:',
|
|
372
|
+
defaultValue: ctx.repoUrl,
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
const chartName = await input({
|
|
376
|
+
message: 'Chart name:',
|
|
377
|
+
defaultValue: ctx.chart?.split('/')[1],
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
if (!chartName) {
|
|
381
|
+
return { success: false, error: 'Chart name is required' };
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
chart = repoName ? `${repoName}/${chartName}` : chartName;
|
|
385
|
+
} else {
|
|
386
|
+
// Local chart
|
|
387
|
+
ui.newLine();
|
|
388
|
+
localPath = await pathInput('Path to local chart:', ctx.localPath || './chart');
|
|
389
|
+
|
|
390
|
+
if (!localPath) {
|
|
391
|
+
return { success: false, error: 'Chart path is required' };
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
chart = localPath;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// Version selection
|
|
398
|
+
if (chartSource !== 'local') {
|
|
399
|
+
ui.newLine();
|
|
400
|
+
const specifyVersion = await confirm({
|
|
401
|
+
message: 'Specify a chart version?',
|
|
402
|
+
defaultValue: false,
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
if (specifyVersion) {
|
|
406
|
+
chartVersion = await input({
|
|
407
|
+
message: 'Chart version:',
|
|
408
|
+
defaultValue: ctx.chartVersion,
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
return {
|
|
414
|
+
success: true,
|
|
415
|
+
data: {
|
|
416
|
+
chartSource: chartSource === 'popular' ? 'repo' : chartSource,
|
|
417
|
+
chart,
|
|
418
|
+
repoName,
|
|
419
|
+
repoUrl,
|
|
420
|
+
localPath,
|
|
421
|
+
chartVersion,
|
|
422
|
+
},
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Step 2: Release Configuration
|
|
428
|
+
*/
|
|
429
|
+
async function releaseConfigStep(ctx: HelmWizardContext): Promise<StepResult> {
|
|
430
|
+
// Extract chart name for default release name
|
|
431
|
+
const chartName = ctx.chart?.split('/').pop() || 'release';
|
|
432
|
+
|
|
433
|
+
const releaseName = await input({
|
|
434
|
+
message: 'Release name:',
|
|
435
|
+
defaultValue: ctx.releaseName || chartName,
|
|
436
|
+
validate: value => {
|
|
437
|
+
if (!value) {
|
|
438
|
+
return 'Release name is required';
|
|
439
|
+
}
|
|
440
|
+
if (!/^[a-z0-9]([-a-z0-9]*[a-z0-9])?$/.test(value)) {
|
|
441
|
+
return 'Release name must be lowercase alphanumeric with dashes only';
|
|
442
|
+
}
|
|
443
|
+
return true;
|
|
444
|
+
},
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
if (!releaseName) {
|
|
448
|
+
return { success: false, error: 'Release name is required' };
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
ui.newLine();
|
|
452
|
+
const namespace = await input({
|
|
453
|
+
message: 'Namespace:',
|
|
454
|
+
defaultValue: ctx.namespace || 'default',
|
|
455
|
+
validate: value => {
|
|
456
|
+
if (!value) {
|
|
457
|
+
return 'Namespace is required';
|
|
458
|
+
}
|
|
459
|
+
if (!/^[a-z0-9]([-a-z0-9]*[a-z0-9])?$/.test(value)) {
|
|
460
|
+
return 'Namespace must be lowercase alphanumeric with dashes only';
|
|
461
|
+
}
|
|
462
|
+
return true;
|
|
463
|
+
},
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
if (!namespace) {
|
|
467
|
+
return { success: false, error: 'Namespace is required' };
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
return {
|
|
471
|
+
success: true,
|
|
472
|
+
data: { releaseName, namespace },
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* Step 3: Environment Selection
|
|
478
|
+
*/
|
|
479
|
+
async function environmentStep(ctx: HelmWizardContext): Promise<StepResult> {
|
|
480
|
+
const environment = await select<HelmEnvironment>({
|
|
481
|
+
message: 'Target environment:',
|
|
482
|
+
options: [
|
|
483
|
+
{
|
|
484
|
+
value: 'dev',
|
|
485
|
+
label: 'Development',
|
|
486
|
+
description: 'Local development settings (minimal resources)',
|
|
487
|
+
},
|
|
488
|
+
{
|
|
489
|
+
value: 'staging',
|
|
490
|
+
label: 'Staging',
|
|
491
|
+
description: 'Pre-production testing environment',
|
|
492
|
+
},
|
|
493
|
+
{
|
|
494
|
+
value: 'production',
|
|
495
|
+
label: 'Production',
|
|
496
|
+
description: 'Production-ready settings (HA, security hardening)',
|
|
497
|
+
},
|
|
498
|
+
],
|
|
499
|
+
defaultValue: ctx.environment || 'dev',
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
if (!environment) {
|
|
503
|
+
return { success: false, error: 'No environment selected' };
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
return {
|
|
507
|
+
success: true,
|
|
508
|
+
data: { environment },
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* Step 4: Image Configuration
|
|
514
|
+
*/
|
|
515
|
+
async function imageConfigStep(ctx: HelmWizardContext): Promise<StepResult> {
|
|
516
|
+
const customizeImage = await confirm({
|
|
517
|
+
message: 'Customize container image settings?',
|
|
518
|
+
defaultValue: false,
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
if (!customizeImage) {
|
|
522
|
+
return { success: true, data: {} };
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
ui.newLine();
|
|
526
|
+
const imageRepository = await input({
|
|
527
|
+
message: 'Image repository (leave empty for chart default):',
|
|
528
|
+
defaultValue: ctx.imageRepository,
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
const imageTag = await input({
|
|
532
|
+
message: 'Image tag (leave empty for chart default):',
|
|
533
|
+
defaultValue: ctx.imageTag,
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
return {
|
|
537
|
+
success: true,
|
|
538
|
+
data: {
|
|
539
|
+
imageRepository: imageRepository || undefined,
|
|
540
|
+
imageTag: imageTag || undefined,
|
|
541
|
+
},
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* Step 5: Resource Configuration
|
|
547
|
+
*/
|
|
548
|
+
async function resourceConfigStep(ctx: HelmWizardContext): Promise<StepResult> {
|
|
549
|
+
// Replicas
|
|
550
|
+
const replicasInput = await input({
|
|
551
|
+
message: 'Number of replicas:',
|
|
552
|
+
defaultValue: String(ctx.replicas || getDefaultReplicas(ctx.environment)),
|
|
553
|
+
validate: value => {
|
|
554
|
+
const num = parseInt(value, 10);
|
|
555
|
+
if (isNaN(num) || num < 1) {
|
|
556
|
+
return 'Must be a positive number';
|
|
557
|
+
}
|
|
558
|
+
return true;
|
|
559
|
+
},
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
const replicas = parseInt(replicasInput || '1', 10);
|
|
563
|
+
|
|
564
|
+
// Resource limits
|
|
565
|
+
ui.newLine();
|
|
566
|
+
const setResources = await confirm({
|
|
567
|
+
message: 'Configure resource requests and limits?',
|
|
568
|
+
defaultValue: ctx.environment === 'production',
|
|
569
|
+
});
|
|
570
|
+
|
|
571
|
+
let cpuRequest: string | undefined;
|
|
572
|
+
let cpuLimit: string | undefined;
|
|
573
|
+
let memoryRequest: string | undefined;
|
|
574
|
+
let memoryLimit: string | undefined;
|
|
575
|
+
|
|
576
|
+
if (setResources) {
|
|
577
|
+
const defaults = getDefaultResources(ctx.environment);
|
|
578
|
+
|
|
579
|
+
ui.newLine();
|
|
580
|
+
ui.info('Resource requests (guaranteed resources):');
|
|
581
|
+
|
|
582
|
+
cpuRequest = await input({
|
|
583
|
+
message: 'CPU request:',
|
|
584
|
+
defaultValue: ctx.cpuRequest || defaults.cpuRequest,
|
|
585
|
+
});
|
|
586
|
+
|
|
587
|
+
memoryRequest = await input({
|
|
588
|
+
message: 'Memory request:',
|
|
589
|
+
defaultValue: ctx.memoryRequest || defaults.memoryRequest,
|
|
590
|
+
});
|
|
591
|
+
|
|
592
|
+
ui.newLine();
|
|
593
|
+
ui.info('Resource limits (maximum allowed):');
|
|
594
|
+
|
|
595
|
+
cpuLimit = await input({
|
|
596
|
+
message: 'CPU limit:',
|
|
597
|
+
defaultValue: ctx.cpuLimit || defaults.cpuLimit,
|
|
598
|
+
});
|
|
599
|
+
|
|
600
|
+
memoryLimit = await input({
|
|
601
|
+
message: 'Memory limit:',
|
|
602
|
+
defaultValue: ctx.memoryLimit || defaults.memoryLimit,
|
|
603
|
+
});
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
return {
|
|
607
|
+
success: true,
|
|
608
|
+
data: {
|
|
609
|
+
replicas,
|
|
610
|
+
cpuRequest,
|
|
611
|
+
cpuLimit,
|
|
612
|
+
memoryRequest,
|
|
613
|
+
memoryLimit,
|
|
614
|
+
},
|
|
615
|
+
};
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
/**
|
|
619
|
+
* Step 6: Service Configuration
|
|
620
|
+
*/
|
|
621
|
+
async function serviceConfigStep(ctx: HelmWizardContext): Promise<StepResult> {
|
|
622
|
+
const serviceType = await select<'ClusterIP' | 'NodePort' | 'LoadBalancer'>({
|
|
623
|
+
message: 'Service type:',
|
|
624
|
+
options: [
|
|
625
|
+
{
|
|
626
|
+
value: 'ClusterIP',
|
|
627
|
+
label: 'ClusterIP',
|
|
628
|
+
description: 'Internal cluster access only (default)',
|
|
629
|
+
},
|
|
630
|
+
{
|
|
631
|
+
value: 'NodePort',
|
|
632
|
+
label: 'NodePort',
|
|
633
|
+
description: "Expose on each node's IP at a static port",
|
|
634
|
+
},
|
|
635
|
+
{
|
|
636
|
+
value: 'LoadBalancer',
|
|
637
|
+
label: 'LoadBalancer',
|
|
638
|
+
description: 'External load balancer (cloud provider)',
|
|
639
|
+
},
|
|
640
|
+
],
|
|
641
|
+
defaultValue: ctx.serviceType || 'ClusterIP',
|
|
642
|
+
});
|
|
643
|
+
|
|
644
|
+
const servicePortInput = await input({
|
|
645
|
+
message: 'Service port:',
|
|
646
|
+
defaultValue: String(ctx.servicePort || 80),
|
|
647
|
+
validate: value => {
|
|
648
|
+
const num = parseInt(value, 10);
|
|
649
|
+
if (isNaN(num) || num < 1 || num > 65535) {
|
|
650
|
+
return 'Must be between 1 and 65535';
|
|
651
|
+
}
|
|
652
|
+
return true;
|
|
653
|
+
},
|
|
654
|
+
});
|
|
655
|
+
|
|
656
|
+
const servicePort = parseInt(servicePortInput || '80', 10);
|
|
657
|
+
|
|
658
|
+
return {
|
|
659
|
+
success: true,
|
|
660
|
+
data: { serviceType, servicePort },
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
/**
|
|
665
|
+
* Step 7: Ingress Configuration
|
|
666
|
+
*/
|
|
667
|
+
async function ingressConfigStep(ctx: HelmWizardContext): Promise<StepResult> {
|
|
668
|
+
const ingressEnabled = await confirm({
|
|
669
|
+
message: 'Enable Ingress?',
|
|
670
|
+
defaultValue: ctx.ingressEnabled ?? ctx.environment === 'production',
|
|
671
|
+
});
|
|
672
|
+
|
|
673
|
+
if (!ingressEnabled) {
|
|
674
|
+
return { success: true, data: { ingressEnabled: false } };
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
ui.newLine();
|
|
678
|
+
const ingressHost = await input({
|
|
679
|
+
message: 'Hostname (e.g., app.example.com):',
|
|
680
|
+
defaultValue: ctx.ingressHost || `${ctx.releaseName}.example.com`,
|
|
681
|
+
validate: value => {
|
|
682
|
+
if (!value) {
|
|
683
|
+
return 'Hostname is required for Ingress';
|
|
684
|
+
}
|
|
685
|
+
return true;
|
|
686
|
+
},
|
|
687
|
+
});
|
|
688
|
+
|
|
689
|
+
const ingressTls = await confirm({
|
|
690
|
+
message: 'Enable TLS?',
|
|
691
|
+
defaultValue: ctx.ingressTls ?? ctx.environment === 'production',
|
|
692
|
+
});
|
|
693
|
+
|
|
694
|
+
return {
|
|
695
|
+
success: true,
|
|
696
|
+
data: { ingressEnabled, ingressHost, ingressTls },
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
/**
|
|
701
|
+
* Step 8: Secrets Configuration
|
|
702
|
+
*/
|
|
703
|
+
async function secretsConfigStep(ctx: HelmWizardContext): Promise<StepResult> {
|
|
704
|
+
const includeSecrets =
|
|
705
|
+
ctx.includeSecrets ??
|
|
706
|
+
(await confirm({
|
|
707
|
+
message: 'Generate a separate secrets values file?',
|
|
708
|
+
defaultValue: true,
|
|
709
|
+
}));
|
|
710
|
+
|
|
711
|
+
if (!includeSecrets) {
|
|
712
|
+
return { success: true, data: { includeSecrets: false } };
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
ui.newLine();
|
|
716
|
+
ui.info('You can add secret values below. Press Enter to skip.');
|
|
717
|
+
ui.info('(Actual secret values should be added later or managed by a secrets tool)');
|
|
718
|
+
|
|
719
|
+
const secretValues: Record<string, string> = {};
|
|
720
|
+
|
|
721
|
+
// Common secret keys based on chart
|
|
722
|
+
const chartName = ctx.chart?.split('/').pop() || '';
|
|
723
|
+
const suggestedKeys = getSuggestedSecretKeys(chartName);
|
|
724
|
+
|
|
725
|
+
for (const key of suggestedKeys) {
|
|
726
|
+
const value = await input({
|
|
727
|
+
message: `${key}:`,
|
|
728
|
+
defaultValue: '',
|
|
729
|
+
});
|
|
730
|
+
if (value) {
|
|
731
|
+
secretValues[key] = value;
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
// Allow adding custom secret keys
|
|
736
|
+
let addMore =
|
|
737
|
+
Object.keys(secretValues).length === 0 ||
|
|
738
|
+
(await confirm({
|
|
739
|
+
message: 'Add custom secret keys?',
|
|
740
|
+
defaultValue: false,
|
|
741
|
+
}));
|
|
742
|
+
|
|
743
|
+
while (addMore) {
|
|
744
|
+
const key = await input({
|
|
745
|
+
message: 'Secret key (or press Enter to finish):',
|
|
746
|
+
});
|
|
747
|
+
|
|
748
|
+
if (!key) {
|
|
749
|
+
break;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
const value = await input({
|
|
753
|
+
message: `Value for ${key}:`,
|
|
754
|
+
});
|
|
755
|
+
|
|
756
|
+
if (value) {
|
|
757
|
+
secretValues[key] = value;
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
addMore = await confirm({
|
|
761
|
+
message: 'Add another secret?',
|
|
762
|
+
defaultValue: false,
|
|
763
|
+
});
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
return {
|
|
767
|
+
success: true,
|
|
768
|
+
data: {
|
|
769
|
+
includeSecrets,
|
|
770
|
+
secretValues: Object.keys(secretValues).length > 0 ? secretValues : undefined,
|
|
771
|
+
},
|
|
772
|
+
};
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
/**
|
|
776
|
+
* Step 9: Output Configuration
|
|
777
|
+
*/
|
|
778
|
+
async function outputConfigStep(ctx: HelmWizardContext): Promise<StepResult> {
|
|
779
|
+
const outputPath = await pathInput(
|
|
780
|
+
'Output directory:',
|
|
781
|
+
ctx.outputPath || `./${ctx.releaseName}-helm`
|
|
782
|
+
);
|
|
783
|
+
|
|
784
|
+
if (!outputPath) {
|
|
785
|
+
return { success: false, error: 'Output path is required' };
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
return {
|
|
789
|
+
success: true,
|
|
790
|
+
data: { outputPath },
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
/**
|
|
795
|
+
* Step 10: Generate Values Files
|
|
796
|
+
*/
|
|
797
|
+
async function generateStep(ctx: HelmWizardContext): Promise<StepResult> {
|
|
798
|
+
ui.startSpinner({ message: 'Generating Helm values files...' });
|
|
799
|
+
|
|
800
|
+
try {
|
|
801
|
+
// Fetch default values from chart if available
|
|
802
|
+
if (ctx.chartSource === 'repo' && ctx.chart) {
|
|
803
|
+
try {
|
|
804
|
+
const valuesResult = await helmClient.showValues(ctx.chart, {
|
|
805
|
+
version: ctx.chartVersion,
|
|
806
|
+
});
|
|
807
|
+
if (valuesResult.success) {
|
|
808
|
+
ctx.defaultValues = valuesResult.values;
|
|
809
|
+
}
|
|
810
|
+
} catch {
|
|
811
|
+
// Continue without default values
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
// Generate values files
|
|
816
|
+
const files = generateValuesFiles(ctx);
|
|
817
|
+
await writeFilesToDisk(files, ctx.outputPath!);
|
|
818
|
+
|
|
819
|
+
ui.stopSpinnerSuccess(`Generated ${files.length} values file(s)`);
|
|
820
|
+
|
|
821
|
+
return {
|
|
822
|
+
success: true,
|
|
823
|
+
data: {
|
|
824
|
+
generatedFiles: files.map(f => f.path),
|
|
825
|
+
},
|
|
826
|
+
};
|
|
827
|
+
} catch (error: any) {
|
|
828
|
+
ui.stopSpinnerFail('Generation failed');
|
|
829
|
+
return {
|
|
830
|
+
success: false,
|
|
831
|
+
error: error.message,
|
|
832
|
+
};
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
/**
|
|
837
|
+
* Generate values files
|
|
838
|
+
*/
|
|
839
|
+
function generateValuesFiles(
|
|
840
|
+
ctx: HelmWizardContext
|
|
841
|
+
): Array<{ name: string; content: string; path: string }> {
|
|
842
|
+
const files: Array<{ name: string; content: string; path: string }> = [];
|
|
843
|
+
|
|
844
|
+
// Main values file
|
|
845
|
+
const mainValues = generateMainValues(ctx);
|
|
846
|
+
const envSuffix = ctx.environment !== 'dev' ? `-${ctx.environment}` : '';
|
|
847
|
+
|
|
848
|
+
files.push({
|
|
849
|
+
name: `values${envSuffix}.yaml`,
|
|
850
|
+
content: mainValues,
|
|
851
|
+
path: `${ctx.outputPath}/values${envSuffix}.yaml`,
|
|
852
|
+
});
|
|
853
|
+
|
|
854
|
+
// Secrets values file
|
|
855
|
+
if (ctx.includeSecrets) {
|
|
856
|
+
const secretsValues = generateSecretsValues(ctx);
|
|
857
|
+
files.push({
|
|
858
|
+
name: `secrets${envSuffix}.yaml`,
|
|
859
|
+
content: secretsValues,
|
|
860
|
+
path: `${ctx.outputPath}/secrets${envSuffix}.yaml`,
|
|
861
|
+
});
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
// README
|
|
865
|
+
files.push({
|
|
866
|
+
name: 'README.md',
|
|
867
|
+
content: generateReadme(ctx),
|
|
868
|
+
path: `${ctx.outputPath}/README.md`,
|
|
869
|
+
});
|
|
870
|
+
|
|
871
|
+
return files;
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
/**
|
|
875
|
+
* Generate main values content
|
|
876
|
+
*/
|
|
877
|
+
function generateMainValues(ctx: HelmWizardContext): string {
|
|
878
|
+
const lines: string[] = [
|
|
879
|
+
`# Helm values for ${ctx.releaseName}`,
|
|
880
|
+
`# Environment: ${ctx.environment}`,
|
|
881
|
+
`# Generated by Nimbus CLI`,
|
|
882
|
+
`# Chart: ${ctx.chart}`,
|
|
883
|
+
'',
|
|
884
|
+
];
|
|
885
|
+
|
|
886
|
+
// Replicas
|
|
887
|
+
lines.push(`replicaCount: ${ctx.replicas}`);
|
|
888
|
+
lines.push('');
|
|
889
|
+
|
|
890
|
+
// Image configuration
|
|
891
|
+
if (ctx.imageRepository || ctx.imageTag) {
|
|
892
|
+
lines.push('image:');
|
|
893
|
+
if (ctx.imageRepository) {
|
|
894
|
+
lines.push(` repository: ${ctx.imageRepository}`);
|
|
895
|
+
}
|
|
896
|
+
if (ctx.imageTag) {
|
|
897
|
+
lines.push(` tag: "${ctx.imageTag}"`);
|
|
898
|
+
}
|
|
899
|
+
lines.push('');
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
// Service configuration
|
|
903
|
+
lines.push('service:');
|
|
904
|
+
lines.push(` type: ${ctx.serviceType}`);
|
|
905
|
+
lines.push(` port: ${ctx.servicePort}`);
|
|
906
|
+
lines.push('');
|
|
907
|
+
|
|
908
|
+
// Ingress configuration
|
|
909
|
+
lines.push('ingress:');
|
|
910
|
+
lines.push(` enabled: ${ctx.ingressEnabled}`);
|
|
911
|
+
if (ctx.ingressEnabled) {
|
|
912
|
+
lines.push(' annotations:');
|
|
913
|
+
lines.push(' kubernetes.io/ingress.class: nginx');
|
|
914
|
+
lines.push(' hosts:');
|
|
915
|
+
lines.push(` - host: ${ctx.ingressHost}`);
|
|
916
|
+
lines.push(' paths:');
|
|
917
|
+
lines.push(' - path: /');
|
|
918
|
+
lines.push(' pathType: Prefix');
|
|
919
|
+
if (ctx.ingressTls) {
|
|
920
|
+
lines.push(' tls:');
|
|
921
|
+
lines.push(` - secretName: ${ctx.releaseName}-tls`);
|
|
922
|
+
lines.push(' hosts:');
|
|
923
|
+
lines.push(` - ${ctx.ingressHost}`);
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
lines.push('');
|
|
927
|
+
|
|
928
|
+
// Resource configuration
|
|
929
|
+
if (ctx.cpuRequest || ctx.memoryRequest || ctx.cpuLimit || ctx.memoryLimit) {
|
|
930
|
+
lines.push('resources:');
|
|
931
|
+
if (ctx.cpuRequest || ctx.memoryRequest) {
|
|
932
|
+
lines.push(' requests:');
|
|
933
|
+
if (ctx.cpuRequest) {
|
|
934
|
+
lines.push(` cpu: ${ctx.cpuRequest}`);
|
|
935
|
+
}
|
|
936
|
+
if (ctx.memoryRequest) {
|
|
937
|
+
lines.push(` memory: ${ctx.memoryRequest}`);
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
if (ctx.cpuLimit || ctx.memoryLimit) {
|
|
941
|
+
lines.push(' limits:');
|
|
942
|
+
if (ctx.cpuLimit) {
|
|
943
|
+
lines.push(` cpu: ${ctx.cpuLimit}`);
|
|
944
|
+
}
|
|
945
|
+
if (ctx.memoryLimit) {
|
|
946
|
+
lines.push(` memory: ${ctx.memoryLimit}`);
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
lines.push('');
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
// Environment-specific settings
|
|
953
|
+
if (ctx.environment === 'production') {
|
|
954
|
+
lines.push('# Production settings');
|
|
955
|
+
lines.push('podDisruptionBudget:');
|
|
956
|
+
lines.push(' enabled: true');
|
|
957
|
+
lines.push(' minAvailable: 1');
|
|
958
|
+
lines.push('');
|
|
959
|
+
lines.push('autoscaling:');
|
|
960
|
+
lines.push(' enabled: true');
|
|
961
|
+
lines.push(` minReplicas: ${ctx.replicas}`);
|
|
962
|
+
lines.push(` maxReplicas: ${(ctx.replicas || 1) * 3}`);
|
|
963
|
+
lines.push(' targetCPUUtilizationPercentage: 70');
|
|
964
|
+
lines.push('');
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
// Node selector / tolerations placeholder
|
|
968
|
+
lines.push('nodeSelector: {}');
|
|
969
|
+
lines.push('');
|
|
970
|
+
lines.push('tolerations: []');
|
|
971
|
+
lines.push('');
|
|
972
|
+
lines.push('affinity: {}');
|
|
973
|
+
|
|
974
|
+
return lines.join('\n');
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
/**
|
|
978
|
+
* Generate secrets values content
|
|
979
|
+
*/
|
|
980
|
+
function generateSecretsValues(ctx: HelmWizardContext): string {
|
|
981
|
+
const lines: string[] = [
|
|
982
|
+
`# Helm secrets for ${ctx.releaseName}`,
|
|
983
|
+
`# Environment: ${ctx.environment}`,
|
|
984
|
+
'# IMPORTANT: Do not commit this file to version control!',
|
|
985
|
+
'# Consider using tools like SOPS, sealed-secrets, or external-secrets',
|
|
986
|
+
'',
|
|
987
|
+
];
|
|
988
|
+
|
|
989
|
+
if (ctx.secretValues && Object.keys(ctx.secretValues).length > 0) {
|
|
990
|
+
lines.push('secrets:');
|
|
991
|
+
for (const [key, value] of Object.entries(ctx.secretValues)) {
|
|
992
|
+
// Mask actual values in generated file with placeholders
|
|
993
|
+
const placeholder = value || Buffer.from('REPLACE_ME').toString('base64');
|
|
994
|
+
lines.push(
|
|
995
|
+
` ${key}: "${placeholder}" # base64-encoded value — replace with: echo -n 'your-value' | base64`
|
|
996
|
+
);
|
|
997
|
+
}
|
|
998
|
+
} else {
|
|
999
|
+
lines.push('# Add your secret values here');
|
|
1000
|
+
lines.push('# Example:');
|
|
1001
|
+
lines.push('# secrets:');
|
|
1002
|
+
lines.push('# databasePassword: "<YOUR_PASSWORD>"');
|
|
1003
|
+
lines.push('# apiKey: "<YOUR_API_KEY>"');
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
return lines.join('\n');
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
/**
|
|
1010
|
+
* Generate README content
|
|
1011
|
+
*/
|
|
1012
|
+
function generateReadme(ctx: HelmWizardContext): string {
|
|
1013
|
+
const envSuffix = ctx.environment !== 'dev' ? `-${ctx.environment}` : '';
|
|
1014
|
+
|
|
1015
|
+
return `# ${ctx.releaseName} Helm Values
|
|
1016
|
+
|
|
1017
|
+
Generated by Nimbus CLI
|
|
1018
|
+
|
|
1019
|
+
## Chart Information
|
|
1020
|
+
|
|
1021
|
+
- **Chart:** ${ctx.chart}
|
|
1022
|
+
- **Release Name:** ${ctx.releaseName}
|
|
1023
|
+
- **Namespace:** ${ctx.namespace}
|
|
1024
|
+
- **Environment:** ${ctx.environment}
|
|
1025
|
+
|
|
1026
|
+
## Installation
|
|
1027
|
+
|
|
1028
|
+
\`\`\`bash
|
|
1029
|
+
# Add repository (if using a repo chart)
|
|
1030
|
+
${ctx.repoName && ctx.repoUrl ? `helm repo add ${ctx.repoName} ${ctx.repoUrl}` : '# helm repo add <repo-name> <repo-url>'}
|
|
1031
|
+
helm repo update
|
|
1032
|
+
|
|
1033
|
+
# Install the chart
|
|
1034
|
+
helm install ${ctx.releaseName} ${ctx.chart} \\
|
|
1035
|
+
--namespace ${ctx.namespace} \\
|
|
1036
|
+
--create-namespace \\
|
|
1037
|
+
-f values${envSuffix}.yaml${
|
|
1038
|
+
ctx.includeSecrets
|
|
1039
|
+
? ` \\
|
|
1040
|
+
-f secrets${envSuffix}.yaml`
|
|
1041
|
+
: ''
|
|
1042
|
+
}
|
|
1043
|
+
\`\`\`
|
|
1044
|
+
|
|
1045
|
+
## Upgrade
|
|
1046
|
+
|
|
1047
|
+
\`\`\`bash
|
|
1048
|
+
helm upgrade ${ctx.releaseName} ${ctx.chart} \\
|
|
1049
|
+
--namespace ${ctx.namespace} \\
|
|
1050
|
+
-f values${envSuffix}.yaml${
|
|
1051
|
+
ctx.includeSecrets
|
|
1052
|
+
? ` \\
|
|
1053
|
+
-f secrets${envSuffix}.yaml`
|
|
1054
|
+
: ''
|
|
1055
|
+
}
|
|
1056
|
+
\`\`\`
|
|
1057
|
+
|
|
1058
|
+
## Uninstall
|
|
1059
|
+
|
|
1060
|
+
\`\`\`bash
|
|
1061
|
+
helm uninstall ${ctx.releaseName} --namespace ${ctx.namespace}
|
|
1062
|
+
\`\`\`
|
|
1063
|
+
|
|
1064
|
+
## Files
|
|
1065
|
+
|
|
1066
|
+
- \`values${envSuffix}.yaml\` - Main configuration values
|
|
1067
|
+
${ctx.includeSecrets ? `- \`secrets${envSuffix}.yaml\` - Secret values (DO NOT commit to git!)` : ''}
|
|
1068
|
+
|
|
1069
|
+
## Security Notes
|
|
1070
|
+
|
|
1071
|
+
${
|
|
1072
|
+
ctx.includeSecrets
|
|
1073
|
+
? `- The \`secrets${envSuffix}.yaml\` file contains sensitive data
|
|
1074
|
+
- Add it to \`.gitignore\` to prevent accidental commits
|
|
1075
|
+
- Consider using:
|
|
1076
|
+
- [SOPS](https://github.com/mozilla/sops) for encrypted secrets in git
|
|
1077
|
+
- [Sealed Secrets](https://github.com/bitnami-labs/sealed-secrets) for Kubernetes-native encryption
|
|
1078
|
+
- [External Secrets](https://external-secrets.io/) for external secret managers`
|
|
1079
|
+
: '- No secrets file generated'
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
## Customization
|
|
1083
|
+
|
|
1084
|
+
Edit the values files to customize your deployment. Common modifications:
|
|
1085
|
+
|
|
1086
|
+
- Increase/decrease replicas
|
|
1087
|
+
- Adjust resource limits
|
|
1088
|
+
- Configure ingress hosts
|
|
1089
|
+
- Add environment variables
|
|
1090
|
+
- Configure persistence
|
|
1091
|
+
`;
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
/**
|
|
1095
|
+
* Get default replicas based on environment
|
|
1096
|
+
*/
|
|
1097
|
+
function getDefaultReplicas(environment?: HelmEnvironment): number {
|
|
1098
|
+
switch (environment) {
|
|
1099
|
+
case 'production':
|
|
1100
|
+
return 3;
|
|
1101
|
+
case 'staging':
|
|
1102
|
+
return 2;
|
|
1103
|
+
default:
|
|
1104
|
+
return 1;
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
/**
|
|
1109
|
+
* Get default resources based on environment
|
|
1110
|
+
*/
|
|
1111
|
+
function getDefaultResources(environment?: HelmEnvironment): {
|
|
1112
|
+
cpuRequest: string;
|
|
1113
|
+
cpuLimit: string;
|
|
1114
|
+
memoryRequest: string;
|
|
1115
|
+
memoryLimit: string;
|
|
1116
|
+
} {
|
|
1117
|
+
switch (environment) {
|
|
1118
|
+
case 'production':
|
|
1119
|
+
return {
|
|
1120
|
+
cpuRequest: '250m',
|
|
1121
|
+
cpuLimit: '1000m',
|
|
1122
|
+
memoryRequest: '256Mi',
|
|
1123
|
+
memoryLimit: '512Mi',
|
|
1124
|
+
};
|
|
1125
|
+
case 'staging':
|
|
1126
|
+
return {
|
|
1127
|
+
cpuRequest: '100m',
|
|
1128
|
+
cpuLimit: '500m',
|
|
1129
|
+
memoryRequest: '128Mi',
|
|
1130
|
+
memoryLimit: '256Mi',
|
|
1131
|
+
};
|
|
1132
|
+
default:
|
|
1133
|
+
return {
|
|
1134
|
+
cpuRequest: '50m',
|
|
1135
|
+
cpuLimit: '200m',
|
|
1136
|
+
memoryRequest: '64Mi',
|
|
1137
|
+
memoryLimit: '128Mi',
|
|
1138
|
+
};
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
/**
|
|
1143
|
+
* Get suggested secret keys based on chart name
|
|
1144
|
+
*/
|
|
1145
|
+
function getSuggestedSecretKeys(chartName: string): string[] {
|
|
1146
|
+
const chartSecrets: Record<string, string[]> = {
|
|
1147
|
+
postgresql: ['postgresql-password', 'postgresql-postgres-password', 'replication-password'],
|
|
1148
|
+
mysql: ['mysql-root-password', 'mysql-password'],
|
|
1149
|
+
mongodb: ['mongodb-root-password', 'mongodb-password'],
|
|
1150
|
+
redis: ['redis-password'],
|
|
1151
|
+
rabbitmq: ['rabbitmq-password', 'rabbitmq-erlang-cookie'],
|
|
1152
|
+
kafka: ['kafka-password'],
|
|
1153
|
+
elasticsearch: ['elasticsearch-password'],
|
|
1154
|
+
};
|
|
1155
|
+
|
|
1156
|
+
return chartSecrets[chartName.toLowerCase()] || ['secret-key'];
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
/**
|
|
1160
|
+
* Write files to disk
|
|
1161
|
+
*/
|
|
1162
|
+
async function writeFilesToDisk(
|
|
1163
|
+
files: Array<{ name: string; content: string; path: string }>,
|
|
1164
|
+
outputPath: string
|
|
1165
|
+
): Promise<void> {
|
|
1166
|
+
const fs = await import('fs/promises');
|
|
1167
|
+
const path = await import('path');
|
|
1168
|
+
|
|
1169
|
+
// Create output directory
|
|
1170
|
+
await fs.mkdir(outputPath, { recursive: true });
|
|
1171
|
+
|
|
1172
|
+
// Write each file
|
|
1173
|
+
for (const file of files) {
|
|
1174
|
+
const filePath = path.join(outputPath, file.name);
|
|
1175
|
+
await fs.writeFile(filePath, file.content, 'utf-8');
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
// Create .gitignore for secrets
|
|
1179
|
+
const gitignorePath = path.join(outputPath, '.gitignore');
|
|
1180
|
+
await fs.writeFile(gitignorePath, 'secrets*.yaml\n', 'utf-8');
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
/**
|
|
1184
|
+
* Run in non-interactive mode
|
|
1185
|
+
*/
|
|
1186
|
+
async function runNonInteractive(options: GenerateHelmOptions): Promise<void> {
|
|
1187
|
+
ui.header('nimbus generate helm', 'Non-interactive mode');
|
|
1188
|
+
|
|
1189
|
+
// Validate required options
|
|
1190
|
+
if (!options.chart) {
|
|
1191
|
+
ui.error('Chart is required in non-interactive mode (--chart)');
|
|
1192
|
+
process.exit(1);
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
if (!options.releaseName) {
|
|
1196
|
+
ui.error('Release name is required in non-interactive mode (--release)');
|
|
1197
|
+
process.exit(1);
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
ui.info(`Chart: ${options.chart}`);
|
|
1201
|
+
ui.info(`Release: ${options.releaseName}`);
|
|
1202
|
+
ui.info(`Namespace: ${options.namespace || 'default'}`);
|
|
1203
|
+
ui.info(`Environment: ${options.environment || 'dev'}`);
|
|
1204
|
+
ui.info(`Output: ${options.output || `./${options.releaseName}-helm`}`);
|
|
1205
|
+
|
|
1206
|
+
// Build context from options
|
|
1207
|
+
const ctx: HelmWizardContext = {
|
|
1208
|
+
chartSource: 'repo',
|
|
1209
|
+
chart: options.chart,
|
|
1210
|
+
releaseName: options.releaseName,
|
|
1211
|
+
namespace: options.namespace || 'default',
|
|
1212
|
+
environment: options.environment || 'dev',
|
|
1213
|
+
replicas: getDefaultReplicas(options.environment),
|
|
1214
|
+
serviceType: 'ClusterIP',
|
|
1215
|
+
servicePort: 80,
|
|
1216
|
+
ingressEnabled: false,
|
|
1217
|
+
includeSecrets: options.includeSecrets ?? true,
|
|
1218
|
+
outputPath: options.output || `./${options.releaseName}-helm`,
|
|
1219
|
+
};
|
|
1220
|
+
|
|
1221
|
+
ui.newLine();
|
|
1222
|
+
ui.startSpinner({ message: 'Generating values files...' });
|
|
1223
|
+
|
|
1224
|
+
try {
|
|
1225
|
+
const files = generateValuesFiles(ctx);
|
|
1226
|
+
await writeFilesToDisk(files, ctx.outputPath!);
|
|
1227
|
+
|
|
1228
|
+
ui.stopSpinnerSuccess(`Generated ${files.length} file(s)`);
|
|
1229
|
+
|
|
1230
|
+
ui.newLine();
|
|
1231
|
+
ui.box({
|
|
1232
|
+
title: 'Complete!',
|
|
1233
|
+
content: [
|
|
1234
|
+
`Generated ${files.length} file(s) in ${ctx.outputPath}:`,
|
|
1235
|
+
...files.map(f => ` - ${f.name}`),
|
|
1236
|
+
],
|
|
1237
|
+
style: 'rounded',
|
|
1238
|
+
borderColor: 'green',
|
|
1239
|
+
padding: 1,
|
|
1240
|
+
});
|
|
1241
|
+
} catch (error: any) {
|
|
1242
|
+
ui.stopSpinnerFail('Generation failed');
|
|
1243
|
+
ui.error(error.message);
|
|
1244
|
+
process.exit(1);
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
// Export as default command
|
|
1249
|
+
export default generateHelmCommand;
|