@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,1126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helm Commands
|
|
3
|
+
*
|
|
4
|
+
* CLI commands for Helm operations
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { helmClient } from '../../clients';
|
|
8
|
+
import { ui } from '../../wizard/ui';
|
|
9
|
+
import { confirmWithResourceName } from '../../wizard/approval';
|
|
10
|
+
import { showDestructionCostWarning } from '../../utils/cost-warning';
|
|
11
|
+
import { historyManager } from '../../history';
|
|
12
|
+
|
|
13
|
+
export interface HelmCommandOptions {
|
|
14
|
+
namespace?: string;
|
|
15
|
+
allNamespaces?: boolean;
|
|
16
|
+
values?: Record<string, any>;
|
|
17
|
+
valuesFile?: string;
|
|
18
|
+
version?: string;
|
|
19
|
+
wait?: boolean;
|
|
20
|
+
timeout?: string;
|
|
21
|
+
createNamespace?: boolean;
|
|
22
|
+
dryRun?: boolean;
|
|
23
|
+
keepHistory?: boolean;
|
|
24
|
+
install?: boolean;
|
|
25
|
+
repo?: string;
|
|
26
|
+
yes?: boolean;
|
|
27
|
+
// package-specific
|
|
28
|
+
destination?: string;
|
|
29
|
+
appVersion?: string;
|
|
30
|
+
dependencyUpdate?: boolean;
|
|
31
|
+
// status-specific
|
|
32
|
+
revision?: number;
|
|
33
|
+
// template-specific
|
|
34
|
+
set?: Record<string, string>;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* List Helm releases
|
|
39
|
+
*/
|
|
40
|
+
export async function helmListCommand(options: HelmCommandOptions = {}): Promise<void> {
|
|
41
|
+
ui.header('Helm Releases');
|
|
42
|
+
|
|
43
|
+
if (options.allNamespaces) {
|
|
44
|
+
ui.info('Showing releases across all namespaces');
|
|
45
|
+
} else if (options.namespace) {
|
|
46
|
+
ui.info(`Namespace: ${options.namespace}`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
ui.startSpinner({ message: 'Fetching Helm releases...' });
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
const available = await helmClient.isAvailable();
|
|
53
|
+
if (!available) {
|
|
54
|
+
ui.stopSpinnerFail('Helm Tools Service not available');
|
|
55
|
+
ui.error('Please ensure the Helm Tools Service is running.');
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const result = await helmClient.list({
|
|
60
|
+
namespace: options.namespace,
|
|
61
|
+
allNamespaces: options.allNamespaces,
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
if (result.success) {
|
|
65
|
+
ui.stopSpinnerSuccess(`Found ${result.releases.length} releases`);
|
|
66
|
+
|
|
67
|
+
if (result.releases.length > 0) {
|
|
68
|
+
ui.table({
|
|
69
|
+
columns: [
|
|
70
|
+
{ key: 'name', header: 'Name' },
|
|
71
|
+
{ key: 'namespace', header: 'Namespace' },
|
|
72
|
+
{ key: 'revision', header: 'Revision' },
|
|
73
|
+
{ key: 'status', header: 'Status' },
|
|
74
|
+
{ key: 'chart', header: 'Chart' },
|
|
75
|
+
{ key: 'appVersion', header: 'App Version' },
|
|
76
|
+
{ key: 'updated', header: 'Updated' },
|
|
77
|
+
],
|
|
78
|
+
data: result.releases.map(release => ({
|
|
79
|
+
name: release.name,
|
|
80
|
+
namespace: release.namespace,
|
|
81
|
+
revision: release.revision,
|
|
82
|
+
status: release.status,
|
|
83
|
+
chart: release.chart,
|
|
84
|
+
appVersion: release.appVersion,
|
|
85
|
+
updated: release.updated,
|
|
86
|
+
})),
|
|
87
|
+
});
|
|
88
|
+
} else {
|
|
89
|
+
ui.info('No releases found');
|
|
90
|
+
}
|
|
91
|
+
} else {
|
|
92
|
+
ui.stopSpinnerFail('Failed to list releases');
|
|
93
|
+
if (result.error) {
|
|
94
|
+
ui.error(result.error);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
} catch (error: any) {
|
|
98
|
+
ui.stopSpinnerFail('Error listing Helm releases');
|
|
99
|
+
ui.error(error.message);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Install a Helm chart
|
|
105
|
+
*/
|
|
106
|
+
export async function helmInstallCommand(
|
|
107
|
+
releaseName: string,
|
|
108
|
+
chart: string,
|
|
109
|
+
options: HelmCommandOptions = {}
|
|
110
|
+
): Promise<void> {
|
|
111
|
+
ui.header('Helm Install');
|
|
112
|
+
ui.info(`Release: ${releaseName}`);
|
|
113
|
+
ui.info(`Chart: ${chart}`);
|
|
114
|
+
|
|
115
|
+
if (options.namespace) {
|
|
116
|
+
ui.info(`Namespace: ${options.namespace}`);
|
|
117
|
+
}
|
|
118
|
+
if (options.version) {
|
|
119
|
+
ui.info(`Version: ${options.version}`);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
ui.startSpinner({ message: `Installing ${chart}...` });
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
const available = await helmClient.isAvailable();
|
|
126
|
+
if (!available) {
|
|
127
|
+
ui.stopSpinnerFail('Helm Tools Service not available');
|
|
128
|
+
ui.error('Please ensure the Helm Tools Service is running.');
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const result = await helmClient.install(releaseName, chart, {
|
|
133
|
+
namespace: options.namespace,
|
|
134
|
+
values: options.values,
|
|
135
|
+
valuesFile: options.valuesFile,
|
|
136
|
+
version: options.version,
|
|
137
|
+
wait: options.wait,
|
|
138
|
+
timeout: options.timeout,
|
|
139
|
+
createNamespace: options.createNamespace,
|
|
140
|
+
dryRun: options.dryRun,
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
if (result.success) {
|
|
144
|
+
ui.stopSpinnerSuccess(`Installed ${releaseName}`);
|
|
145
|
+
|
|
146
|
+
ui.info(`Status: ${result.release.status}`);
|
|
147
|
+
ui.info(`Revision: ${result.release.revision}`);
|
|
148
|
+
|
|
149
|
+
if (result.output) {
|
|
150
|
+
ui.box({ title: 'Output', content: result.output });
|
|
151
|
+
}
|
|
152
|
+
} else {
|
|
153
|
+
ui.stopSpinnerFail('Failed to install chart');
|
|
154
|
+
if (result.error) {
|
|
155
|
+
ui.error(result.error);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
} catch (error: any) {
|
|
159
|
+
ui.stopSpinnerFail('Error installing Helm chart');
|
|
160
|
+
ui.error(error.message);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Upgrade a Helm release
|
|
166
|
+
*/
|
|
167
|
+
export async function helmUpgradeCommand(
|
|
168
|
+
releaseName: string,
|
|
169
|
+
chart: string,
|
|
170
|
+
options: HelmCommandOptions = {}
|
|
171
|
+
): Promise<void> {
|
|
172
|
+
ui.header('Helm Upgrade');
|
|
173
|
+
ui.info(`Release: ${releaseName}`);
|
|
174
|
+
ui.info(`Chart: ${chart}`);
|
|
175
|
+
|
|
176
|
+
if (options.namespace) {
|
|
177
|
+
ui.info(`Namespace: ${options.namespace}`);
|
|
178
|
+
}
|
|
179
|
+
if (options.version) {
|
|
180
|
+
ui.info(`Version: ${options.version}`);
|
|
181
|
+
}
|
|
182
|
+
if (options.install) {
|
|
183
|
+
ui.info('Install if not exists: yes');
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
ui.startSpinner({ message: `Upgrading ${releaseName}...` });
|
|
187
|
+
|
|
188
|
+
try {
|
|
189
|
+
const available = await helmClient.isAvailable();
|
|
190
|
+
if (!available) {
|
|
191
|
+
ui.stopSpinnerFail('Helm Tools Service not available');
|
|
192
|
+
ui.error('Please ensure the Helm Tools Service is running.');
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const result = await helmClient.upgrade(releaseName, chart, {
|
|
197
|
+
namespace: options.namespace,
|
|
198
|
+
values: options.values,
|
|
199
|
+
valuesFile: options.valuesFile,
|
|
200
|
+
version: options.version,
|
|
201
|
+
wait: options.wait,
|
|
202
|
+
timeout: options.timeout,
|
|
203
|
+
install: options.install,
|
|
204
|
+
dryRun: options.dryRun,
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
if (result.success) {
|
|
208
|
+
ui.stopSpinnerSuccess(`Upgraded ${releaseName}`);
|
|
209
|
+
|
|
210
|
+
ui.info(`Status: ${result.release.status}`);
|
|
211
|
+
ui.info(`Revision: ${result.release.revision}`);
|
|
212
|
+
|
|
213
|
+
if (result.output) {
|
|
214
|
+
ui.box({ title: 'Output', content: result.output });
|
|
215
|
+
}
|
|
216
|
+
} else {
|
|
217
|
+
ui.stopSpinnerFail('Failed to upgrade release');
|
|
218
|
+
if (result.error) {
|
|
219
|
+
ui.error(result.error);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
} catch (error: any) {
|
|
223
|
+
ui.stopSpinnerFail('Error upgrading Helm release');
|
|
224
|
+
ui.error(error.message);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Uninstall a Helm release
|
|
230
|
+
*/
|
|
231
|
+
export async function helmUninstallCommand(
|
|
232
|
+
releaseName: string,
|
|
233
|
+
options: HelmCommandOptions = {}
|
|
234
|
+
): Promise<void> {
|
|
235
|
+
ui.header('Helm Uninstall');
|
|
236
|
+
ui.info(`Release: ${releaseName}`);
|
|
237
|
+
|
|
238
|
+
if (options.namespace) {
|
|
239
|
+
ui.info(`Namespace: ${options.namespace}`);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Show release info warning before destructive operation
|
|
243
|
+
ui.newLine();
|
|
244
|
+
ui.warning(`Destructive operation: uninstalling helm release "${releaseName}"`);
|
|
245
|
+
ui.print(` ${ui.color('Release:', 'yellow')} ${releaseName}`);
|
|
246
|
+
ui.print(` ${ui.color('Namespace:', 'yellow')} ${options.namespace || 'default'}`);
|
|
247
|
+
ui.print(` ${ui.color('Keep history:', 'yellow')} ${options.keepHistory ? 'yes' : 'no'}`);
|
|
248
|
+
ui.print(
|
|
249
|
+
` ${ui.color('Impact:', 'red')} All resources managed by this release will be deleted.`
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
// Attempt to show release details if service is available
|
|
253
|
+
try {
|
|
254
|
+
const available = await helmClient.isAvailable();
|
|
255
|
+
if (available) {
|
|
256
|
+
const statusResult = await helmClient.status(releaseName, {
|
|
257
|
+
namespace: options.namespace,
|
|
258
|
+
});
|
|
259
|
+
if (statusResult?.release) {
|
|
260
|
+
ui.print(` ${ui.color('Chart:', 'yellow')} ${statusResult.release.chart || 'unknown'}`);
|
|
261
|
+
ui.print(
|
|
262
|
+
` ${ui.color('Revision:', 'yellow')} ${statusResult.release.revision || 'unknown'}`
|
|
263
|
+
);
|
|
264
|
+
ui.print(` ${ui.color('Status:', 'yellow')} ${statusResult.release.status || 'unknown'}`);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
} catch {
|
|
268
|
+
// Best-effort release info display -- silently skip on failure
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Show cost warning before destructive operation
|
|
272
|
+
await showDestructionCostWarning(process.cwd());
|
|
273
|
+
|
|
274
|
+
// Require confirmation for destructive operation
|
|
275
|
+
if (!options.dryRun && !options.yes) {
|
|
276
|
+
const confirmed = await confirmWithResourceName(releaseName, 'helm release');
|
|
277
|
+
if (!confirmed) {
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
ui.startSpinner({ message: `Uninstalling ${releaseName}...` });
|
|
283
|
+
|
|
284
|
+
try {
|
|
285
|
+
const available = await helmClient.isAvailable();
|
|
286
|
+
if (!available) {
|
|
287
|
+
ui.stopSpinnerFail('Helm Tools Service not available');
|
|
288
|
+
ui.error('Please ensure the Helm Tools Service is running.');
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const result = await helmClient.uninstall(releaseName, {
|
|
293
|
+
namespace: options.namespace,
|
|
294
|
+
keepHistory: options.keepHistory,
|
|
295
|
+
dryRun: options.dryRun,
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
if (result.success) {
|
|
299
|
+
ui.stopSpinnerSuccess(`Uninstalled ${releaseName}`);
|
|
300
|
+
|
|
301
|
+
if (result.output) {
|
|
302
|
+
ui.box({ title: 'Output', content: result.output });
|
|
303
|
+
}
|
|
304
|
+
} else {
|
|
305
|
+
ui.stopSpinnerFail('Failed to uninstall release');
|
|
306
|
+
if (result.error) {
|
|
307
|
+
ui.error(result.error);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
} catch (error: any) {
|
|
311
|
+
ui.stopSpinnerFail('Error uninstalling Helm release');
|
|
312
|
+
ui.error(error.message);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Rollback a Helm release
|
|
318
|
+
*/
|
|
319
|
+
export async function helmRollbackCommand(
|
|
320
|
+
releaseName: string,
|
|
321
|
+
revision: number,
|
|
322
|
+
options: HelmCommandOptions = {}
|
|
323
|
+
): Promise<void> {
|
|
324
|
+
ui.header('Helm Rollback');
|
|
325
|
+
ui.info(`Release: ${releaseName}`);
|
|
326
|
+
ui.info(`Revision: ${revision}`);
|
|
327
|
+
|
|
328
|
+
if (options.namespace) {
|
|
329
|
+
ui.info(`Namespace: ${options.namespace}`);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
ui.startSpinner({ message: `Rolling back ${releaseName} to revision ${revision}...` });
|
|
333
|
+
|
|
334
|
+
try {
|
|
335
|
+
const available = await helmClient.isAvailable();
|
|
336
|
+
if (!available) {
|
|
337
|
+
ui.stopSpinnerFail('Helm Tools Service not available');
|
|
338
|
+
ui.error('Please ensure the Helm Tools Service is running.');
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const result = await helmClient.rollback(releaseName, revision, {
|
|
343
|
+
namespace: options.namespace,
|
|
344
|
+
wait: options.wait,
|
|
345
|
+
dryRun: options.dryRun,
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
if (result.success) {
|
|
349
|
+
ui.stopSpinnerSuccess(`Rolled back ${releaseName} to revision ${revision}`);
|
|
350
|
+
|
|
351
|
+
if (result.output) {
|
|
352
|
+
ui.box({ title: 'Output', content: result.output });
|
|
353
|
+
}
|
|
354
|
+
} else {
|
|
355
|
+
ui.stopSpinnerFail('Failed to rollback release');
|
|
356
|
+
if (result.error) {
|
|
357
|
+
ui.error(result.error);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
} catch (error: any) {
|
|
361
|
+
ui.stopSpinnerFail('Error rolling back Helm release');
|
|
362
|
+
ui.error(error.message);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Show release history
|
|
368
|
+
*/
|
|
369
|
+
export async function helmHistoryCommand(
|
|
370
|
+
releaseName: string,
|
|
371
|
+
options: HelmCommandOptions = {}
|
|
372
|
+
): Promise<void> {
|
|
373
|
+
ui.header(`Helm History - ${releaseName}`);
|
|
374
|
+
|
|
375
|
+
if (options.namespace) {
|
|
376
|
+
ui.info(`Namespace: ${options.namespace}`);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
ui.startSpinner({ message: 'Fetching release history...' });
|
|
380
|
+
|
|
381
|
+
try {
|
|
382
|
+
const available = await helmClient.isAvailable();
|
|
383
|
+
if (!available) {
|
|
384
|
+
ui.stopSpinnerFail('Helm Tools Service not available');
|
|
385
|
+
ui.error('Please ensure the Helm Tools Service is running.');
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
const result = await helmClient.history(releaseName, {
|
|
390
|
+
namespace: options.namespace,
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
if (result.success) {
|
|
394
|
+
ui.stopSpinnerSuccess(`Found ${result.history.length} revisions`);
|
|
395
|
+
|
|
396
|
+
if (result.history.length > 0) {
|
|
397
|
+
// Display history - the history format can vary, so we just show it raw
|
|
398
|
+
for (const entry of result.history) {
|
|
399
|
+
ui.info(JSON.stringify(entry));
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
} else {
|
|
403
|
+
ui.stopSpinnerFail('Failed to get release history');
|
|
404
|
+
if (result.error) {
|
|
405
|
+
ui.error(result.error);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
} catch (error: any) {
|
|
409
|
+
ui.stopSpinnerFail('Error getting Helm history');
|
|
410
|
+
ui.error(error.message);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Search for Helm charts
|
|
416
|
+
*/
|
|
417
|
+
export async function helmSearchCommand(
|
|
418
|
+
keyword: string,
|
|
419
|
+
options: HelmCommandOptions = {}
|
|
420
|
+
): Promise<void> {
|
|
421
|
+
ui.header(`Helm Search - "${keyword}"`);
|
|
422
|
+
|
|
423
|
+
if (options.repo) {
|
|
424
|
+
ui.info(`Repository: ${options.repo}`);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
ui.startSpinner({ message: 'Searching for charts...' });
|
|
428
|
+
|
|
429
|
+
try {
|
|
430
|
+
const available = await helmClient.isAvailable();
|
|
431
|
+
if (!available) {
|
|
432
|
+
ui.stopSpinnerFail('Helm Tools Service not available');
|
|
433
|
+
ui.error('Please ensure the Helm Tools Service is running.');
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
const result = await helmClient.search(keyword, {
|
|
438
|
+
repo: options.repo,
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
if (result.success) {
|
|
442
|
+
ui.stopSpinnerSuccess(`Found ${result.charts.length} charts`);
|
|
443
|
+
|
|
444
|
+
if (result.charts.length > 0) {
|
|
445
|
+
ui.table({
|
|
446
|
+
columns: [
|
|
447
|
+
{ key: 'name', header: 'Name' },
|
|
448
|
+
{ key: 'version', header: 'Version' },
|
|
449
|
+
{ key: 'appVersion', header: 'App Version' },
|
|
450
|
+
{ key: 'description', header: 'Description' },
|
|
451
|
+
],
|
|
452
|
+
data: result.charts.map(chart => ({
|
|
453
|
+
name: chart.name,
|
|
454
|
+
version: chart.version,
|
|
455
|
+
appVersion: chart.appVersion,
|
|
456
|
+
description: chart.description,
|
|
457
|
+
})),
|
|
458
|
+
});
|
|
459
|
+
} else {
|
|
460
|
+
ui.info('No charts found');
|
|
461
|
+
}
|
|
462
|
+
} else {
|
|
463
|
+
ui.stopSpinnerFail('Failed to search charts');
|
|
464
|
+
if (result.error) {
|
|
465
|
+
ui.error(result.error);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
} catch (error: any) {
|
|
469
|
+
ui.stopSpinnerFail('Error searching Helm charts');
|
|
470
|
+
ui.error(error.message);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* Add a Helm repository
|
|
476
|
+
*/
|
|
477
|
+
export async function helmRepoAddCommand(name: string, url: string): Promise<void> {
|
|
478
|
+
ui.header('Helm Repo Add');
|
|
479
|
+
ui.info(`Name: ${name}`);
|
|
480
|
+
ui.info(`URL: ${url}`);
|
|
481
|
+
|
|
482
|
+
ui.startSpinner({ message: `Adding repository ${name}...` });
|
|
483
|
+
|
|
484
|
+
try {
|
|
485
|
+
const available = await helmClient.isAvailable();
|
|
486
|
+
if (!available) {
|
|
487
|
+
ui.stopSpinnerFail('Helm Tools Service not available');
|
|
488
|
+
ui.error('Please ensure the Helm Tools Service is running.');
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
const result = await helmClient.repoAdd(name, url);
|
|
493
|
+
|
|
494
|
+
if (result.success) {
|
|
495
|
+
ui.stopSpinnerSuccess(`Added repository ${name}`);
|
|
496
|
+
|
|
497
|
+
if (result.output) {
|
|
498
|
+
ui.info(result.output);
|
|
499
|
+
}
|
|
500
|
+
} else {
|
|
501
|
+
ui.stopSpinnerFail('Failed to add repository');
|
|
502
|
+
if (result.error) {
|
|
503
|
+
ui.error(result.error);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
} catch (error: any) {
|
|
507
|
+
ui.stopSpinnerFail('Error adding Helm repository');
|
|
508
|
+
ui.error(error.message);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* Update Helm repositories
|
|
514
|
+
*/
|
|
515
|
+
export async function helmRepoUpdateCommand(): Promise<void> {
|
|
516
|
+
ui.header('Helm Repo Update');
|
|
517
|
+
|
|
518
|
+
ui.startSpinner({ message: 'Updating repositories...' });
|
|
519
|
+
|
|
520
|
+
try {
|
|
521
|
+
const available = await helmClient.isAvailable();
|
|
522
|
+
if (!available) {
|
|
523
|
+
ui.stopSpinnerFail('Helm Tools Service not available');
|
|
524
|
+
ui.error('Please ensure the Helm Tools Service is running.');
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
const result = await helmClient.repoUpdate();
|
|
529
|
+
|
|
530
|
+
if (result.success) {
|
|
531
|
+
ui.stopSpinnerSuccess('Repositories updated');
|
|
532
|
+
|
|
533
|
+
if (result.output) {
|
|
534
|
+
ui.info(result.output);
|
|
535
|
+
}
|
|
536
|
+
} else {
|
|
537
|
+
ui.stopSpinnerFail('Failed to update repositories');
|
|
538
|
+
if (result.error) {
|
|
539
|
+
ui.error(result.error);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
} catch (error: any) {
|
|
543
|
+
ui.stopSpinnerFail('Error updating Helm repositories');
|
|
544
|
+
ui.error(error.message);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* Show chart information
|
|
550
|
+
*/
|
|
551
|
+
export async function helmShowCommand(
|
|
552
|
+
chart: string,
|
|
553
|
+
options: HelmCommandOptions & { subcommand?: 'all' | 'chart' | 'readme' | 'values' | 'crds' } = {}
|
|
554
|
+
): Promise<void> {
|
|
555
|
+
const sub = options.subcommand || 'all';
|
|
556
|
+
ui.header(`Helm Show ${sub} - ${chart}`);
|
|
557
|
+
|
|
558
|
+
if (options.version) {
|
|
559
|
+
ui.info(`Version: ${options.version}`);
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
ui.startSpinner({ message: `Fetching chart info for ${chart}...` });
|
|
563
|
+
|
|
564
|
+
try {
|
|
565
|
+
const available = await helmClient.isAvailable();
|
|
566
|
+
if (!available) {
|
|
567
|
+
ui.stopSpinnerFail('Helm Tools Service not available');
|
|
568
|
+
ui.error('Please ensure the Helm Tools Service is running.');
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
const result = await helmClient.show(chart, {
|
|
573
|
+
subcommand: sub,
|
|
574
|
+
version: options.version,
|
|
575
|
+
});
|
|
576
|
+
|
|
577
|
+
if (result.success) {
|
|
578
|
+
ui.stopSpinnerSuccess(`Chart info retrieved`);
|
|
579
|
+
if (result.output) {
|
|
580
|
+
console.log(result.output);
|
|
581
|
+
}
|
|
582
|
+
} else {
|
|
583
|
+
ui.stopSpinnerFail('Failed to show chart info');
|
|
584
|
+
if (result.error) {
|
|
585
|
+
ui.error(result.error);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
} catch (error: any) {
|
|
589
|
+
ui.stopSpinnerFail('Error showing chart info');
|
|
590
|
+
ui.error(error.message);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
/**
|
|
595
|
+
* Lint a Helm chart
|
|
596
|
+
*/
|
|
597
|
+
export async function helmLintCommand(
|
|
598
|
+
chartPath: string,
|
|
599
|
+
options: HelmCommandOptions & { strict?: boolean; valuesFiles?: string[] } = {}
|
|
600
|
+
): Promise<void> {
|
|
601
|
+
ui.header('Helm Lint');
|
|
602
|
+
ui.info(`Chart: ${chartPath}`);
|
|
603
|
+
|
|
604
|
+
if (options.strict) {
|
|
605
|
+
ui.info('Mode: strict');
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
ui.startSpinner({ message: `Linting ${chartPath}...` });
|
|
609
|
+
|
|
610
|
+
try {
|
|
611
|
+
const available = await helmClient.isAvailable();
|
|
612
|
+
if (!available) {
|
|
613
|
+
ui.stopSpinnerFail('Helm Tools Service not available');
|
|
614
|
+
ui.error('Please ensure the Helm Tools Service is running.');
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
const result = await helmClient.lint(chartPath, {
|
|
619
|
+
strict: options.strict,
|
|
620
|
+
valuesFiles: options.valuesFiles,
|
|
621
|
+
namespace: options.namespace,
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
if (result.success) {
|
|
625
|
+
const lintData = result.data;
|
|
626
|
+
const messages: any[] = lintData?.messages || [];
|
|
627
|
+
const errors = messages.filter((m: any) => m.severity === 'error');
|
|
628
|
+
const warnings = messages.filter((m: any) => m.severity === 'warning');
|
|
629
|
+
|
|
630
|
+
if (errors.length === 0) {
|
|
631
|
+
ui.stopSpinnerSuccess(
|
|
632
|
+
`Chart linted successfully${warnings.length > 0 ? ` (${warnings.length} warnings)` : ''}`
|
|
633
|
+
);
|
|
634
|
+
} else {
|
|
635
|
+
ui.stopSpinnerFail(`Lint found ${errors.length} error(s)`);
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
if (messages.length > 0) {
|
|
639
|
+
ui.newLine();
|
|
640
|
+
for (const msg of messages) {
|
|
641
|
+
if (msg.severity === 'error') {
|
|
642
|
+
ui.error(` ${msg.message || msg}`);
|
|
643
|
+
} else {
|
|
644
|
+
ui.warning(` ${msg.message || msg}`);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
if (lintData?.output) {
|
|
650
|
+
ui.newLine();
|
|
651
|
+
ui.box({ title: 'Lint Output', content: lintData.output });
|
|
652
|
+
}
|
|
653
|
+
} else {
|
|
654
|
+
ui.stopSpinnerFail('Lint failed');
|
|
655
|
+
if (result.error) {
|
|
656
|
+
ui.error(typeof result.error === 'string' ? result.error : 'Unknown error');
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
} catch (error: any) {
|
|
660
|
+
ui.stopSpinnerFail('Error linting Helm chart');
|
|
661
|
+
ui.error(error.message);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
/**
|
|
666
|
+
* Render chart templates locally without installing
|
|
667
|
+
*/
|
|
668
|
+
export async function helmTemplateCommand(
|
|
669
|
+
releaseName: string,
|
|
670
|
+
chart: string,
|
|
671
|
+
options: HelmCommandOptions & { set?: Record<string, string> } = {}
|
|
672
|
+
): Promise<void> {
|
|
673
|
+
ui.header('Helm Template');
|
|
674
|
+
ui.info(`Release name: ${releaseName}`);
|
|
675
|
+
ui.info(`Chart: ${chart}`);
|
|
676
|
+
|
|
677
|
+
if (options.namespace) {
|
|
678
|
+
ui.info(`Namespace: ${options.namespace}`);
|
|
679
|
+
}
|
|
680
|
+
if (options.version) {
|
|
681
|
+
ui.info(`Version: ${options.version}`);
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
ui.startSpinner({ message: `Rendering templates for ${chart}...` });
|
|
685
|
+
|
|
686
|
+
try {
|
|
687
|
+
const available = await helmClient.isAvailable();
|
|
688
|
+
if (!available) {
|
|
689
|
+
ui.stopSpinnerFail('Helm Tools Service not available');
|
|
690
|
+
ui.error('Please ensure the Helm Tools Service is running.');
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
const result = await helmClient.template(releaseName, chart, {
|
|
695
|
+
namespace: options.namespace,
|
|
696
|
+
values: options.values,
|
|
697
|
+
valuesFile: options.valuesFile,
|
|
698
|
+
set: options.set,
|
|
699
|
+
version: options.version,
|
|
700
|
+
});
|
|
701
|
+
|
|
702
|
+
if (result.success) {
|
|
703
|
+
ui.stopSpinnerSuccess('Templates rendered successfully');
|
|
704
|
+
|
|
705
|
+
if (result.manifests) {
|
|
706
|
+
ui.newLine();
|
|
707
|
+
console.log(result.manifests);
|
|
708
|
+
}
|
|
709
|
+
} else {
|
|
710
|
+
ui.stopSpinnerFail('Failed to render templates');
|
|
711
|
+
if (result.error) {
|
|
712
|
+
ui.error(result.error);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
} catch (error: any) {
|
|
716
|
+
ui.stopSpinnerFail('Error rendering Helm templates');
|
|
717
|
+
ui.error(error.message);
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
/**
|
|
722
|
+
* Package a chart directory into a chart archive
|
|
723
|
+
*/
|
|
724
|
+
export async function helmPackageCommand(
|
|
725
|
+
chartPath: string,
|
|
726
|
+
options: HelmCommandOptions & {
|
|
727
|
+
destination?: string;
|
|
728
|
+
appVersion?: string;
|
|
729
|
+
dependencyUpdate?: boolean;
|
|
730
|
+
} = {}
|
|
731
|
+
): Promise<void> {
|
|
732
|
+
ui.header('Helm Package');
|
|
733
|
+
ui.info(`Chart path: ${chartPath}`);
|
|
734
|
+
|
|
735
|
+
if (options.destination) {
|
|
736
|
+
ui.info(`Destination: ${options.destination}`);
|
|
737
|
+
}
|
|
738
|
+
if (options.version) {
|
|
739
|
+
ui.info(`Version: ${options.version}`);
|
|
740
|
+
}
|
|
741
|
+
if (options.appVersion) {
|
|
742
|
+
ui.info(`App version: ${options.appVersion}`);
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
ui.startSpinner({ message: `Packaging chart ${chartPath}...` });
|
|
746
|
+
|
|
747
|
+
try {
|
|
748
|
+
const available = await helmClient.isAvailable();
|
|
749
|
+
if (!available) {
|
|
750
|
+
ui.stopSpinnerFail('Helm Tools Service not available');
|
|
751
|
+
ui.error('Please ensure the Helm Tools Service is running.');
|
|
752
|
+
return;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
const result = await helmClient.package(chartPath, {
|
|
756
|
+
destination: options.destination,
|
|
757
|
+
version: options.version,
|
|
758
|
+
appVersion: options.appVersion,
|
|
759
|
+
dependencyUpdate: options.dependencyUpdate,
|
|
760
|
+
});
|
|
761
|
+
|
|
762
|
+
if (result.success) {
|
|
763
|
+
ui.stopSpinnerSuccess('Chart packaged successfully');
|
|
764
|
+
|
|
765
|
+
if (result.output) {
|
|
766
|
+
ui.box({ title: 'Output', content: result.output });
|
|
767
|
+
}
|
|
768
|
+
} else {
|
|
769
|
+
ui.stopSpinnerFail('Failed to package chart');
|
|
770
|
+
if (result.error) {
|
|
771
|
+
ui.error(result.error);
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
} catch (error: any) {
|
|
775
|
+
ui.stopSpinnerFail('Error packaging Helm chart');
|
|
776
|
+
ui.error(error.message);
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
/**
|
|
781
|
+
* Manage chart dependencies
|
|
782
|
+
*/
|
|
783
|
+
export async function helmDependencyCommand(
|
|
784
|
+
depSubcommand: string,
|
|
785
|
+
chartPath: string
|
|
786
|
+
): Promise<void> {
|
|
787
|
+
const action = depSubcommand === 'build' ? 'build' : 'update';
|
|
788
|
+
ui.header(`Helm Dependency ${action.charAt(0).toUpperCase() + action.slice(1)}`);
|
|
789
|
+
ui.info(`Chart path: ${chartPath}`);
|
|
790
|
+
|
|
791
|
+
ui.startSpinner({ message: `Running dependency ${action} for ${chartPath}...` });
|
|
792
|
+
|
|
793
|
+
try {
|
|
794
|
+
const available = await helmClient.isAvailable();
|
|
795
|
+
if (!available) {
|
|
796
|
+
ui.stopSpinnerFail('Helm Tools Service not available');
|
|
797
|
+
ui.error('Please ensure the Helm Tools Service is running.');
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
const result =
|
|
802
|
+
action === 'build'
|
|
803
|
+
? await helmClient.dependencyBuild(chartPath)
|
|
804
|
+
: await helmClient.dependencyUpdate(chartPath);
|
|
805
|
+
|
|
806
|
+
if (result.success) {
|
|
807
|
+
ui.stopSpinnerSuccess(`Dependency ${action} completed`);
|
|
808
|
+
|
|
809
|
+
if (result.output) {
|
|
810
|
+
ui.box({ title: 'Output', content: result.output });
|
|
811
|
+
}
|
|
812
|
+
} else {
|
|
813
|
+
ui.stopSpinnerFail(`Failed to ${action} dependencies`);
|
|
814
|
+
if (result.error) {
|
|
815
|
+
ui.error(result.error);
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
} catch (error: any) {
|
|
819
|
+
ui.stopSpinnerFail(`Error running dependency ${action}`);
|
|
820
|
+
ui.error(error.message);
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
/**
|
|
825
|
+
* Show the status of a named release
|
|
826
|
+
*/
|
|
827
|
+
export async function helmStatusCommand(
|
|
828
|
+
releaseName: string,
|
|
829
|
+
options: HelmCommandOptions & { revision?: number } = {}
|
|
830
|
+
): Promise<void> {
|
|
831
|
+
ui.header(`Helm Status - ${releaseName}`);
|
|
832
|
+
|
|
833
|
+
if (options.namespace) {
|
|
834
|
+
ui.info(`Namespace: ${options.namespace}`);
|
|
835
|
+
}
|
|
836
|
+
if (options.revision !== undefined) {
|
|
837
|
+
ui.info(`Revision: ${options.revision}`);
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
ui.startSpinner({ message: `Fetching status for ${releaseName}...` });
|
|
841
|
+
|
|
842
|
+
try {
|
|
843
|
+
const available = await helmClient.isAvailable();
|
|
844
|
+
if (!available) {
|
|
845
|
+
ui.stopSpinnerFail('Helm Tools Service not available');
|
|
846
|
+
ui.error('Please ensure the Helm Tools Service is running.');
|
|
847
|
+
return;
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
const result = await helmClient.statusDetailed(releaseName, {
|
|
851
|
+
namespace: options.namespace,
|
|
852
|
+
revision: options.revision,
|
|
853
|
+
});
|
|
854
|
+
|
|
855
|
+
if (result.success) {
|
|
856
|
+
ui.stopSpinnerSuccess(`Status retrieved for ${releaseName}`);
|
|
857
|
+
|
|
858
|
+
if (result.status) {
|
|
859
|
+
const statusData = result.status;
|
|
860
|
+
if (typeof statusData === 'object') {
|
|
861
|
+
ui.newLine();
|
|
862
|
+
if (statusData.name) {
|
|
863
|
+
ui.info(`Name: ${statusData.name}`);
|
|
864
|
+
}
|
|
865
|
+
if (statusData.namespace) {
|
|
866
|
+
ui.info(`Namespace: ${statusData.namespace}`);
|
|
867
|
+
}
|
|
868
|
+
if (statusData.status) {
|
|
869
|
+
ui.info(`Status: ${statusData.status}`);
|
|
870
|
+
}
|
|
871
|
+
if (statusData.revision) {
|
|
872
|
+
ui.info(`Revision: ${statusData.revision}`);
|
|
873
|
+
}
|
|
874
|
+
if (statusData.chart) {
|
|
875
|
+
ui.info(`Chart: ${statusData.chart}`);
|
|
876
|
+
}
|
|
877
|
+
if (statusData.appVersion) {
|
|
878
|
+
ui.info(`App Ver: ${statusData.appVersion}`);
|
|
879
|
+
}
|
|
880
|
+
if (statusData.updated) {
|
|
881
|
+
ui.info(`Updated: ${statusData.updated}`);
|
|
882
|
+
}
|
|
883
|
+
if (statusData.description) {
|
|
884
|
+
ui.newLine();
|
|
885
|
+
ui.info(`Description: ${statusData.description}`);
|
|
886
|
+
}
|
|
887
|
+
if (statusData.notes) {
|
|
888
|
+
ui.newLine();
|
|
889
|
+
ui.box({ title: 'Notes', content: statusData.notes });
|
|
890
|
+
}
|
|
891
|
+
} else if (typeof statusData === 'string') {
|
|
892
|
+
ui.newLine();
|
|
893
|
+
console.log(statusData);
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
} else {
|
|
897
|
+
ui.stopSpinnerFail(`Failed to get status for ${releaseName}`);
|
|
898
|
+
if (result.error) {
|
|
899
|
+
ui.error(result.error);
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
} catch (error: any) {
|
|
903
|
+
ui.stopSpinnerFail('Error fetching Helm release status');
|
|
904
|
+
ui.error(error.message);
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
/**
|
|
909
|
+
* Main helm command router
|
|
910
|
+
*/
|
|
911
|
+
export async function helmCommand(subcommand: string, args: string[]): Promise<void> {
|
|
912
|
+
const options: HelmCommandOptions = {};
|
|
913
|
+
|
|
914
|
+
// Extract positional args and options
|
|
915
|
+
const positionalArgs: string[] = [];
|
|
916
|
+
|
|
917
|
+
for (let i = 0; i < args.length; i++) {
|
|
918
|
+
const arg = args[i];
|
|
919
|
+
if (arg === '-n' || arg === '--namespace') {
|
|
920
|
+
options.namespace = args[++i];
|
|
921
|
+
} else if (arg === '-A' || arg === '--all-namespaces') {
|
|
922
|
+
options.allNamespaces = true;
|
|
923
|
+
} else if (arg === '-f' || arg === '--values') {
|
|
924
|
+
options.valuesFile = args[++i];
|
|
925
|
+
} else if (arg === '--version') {
|
|
926
|
+
options.version = args[++i];
|
|
927
|
+
} else if (arg === '--wait') {
|
|
928
|
+
options.wait = true;
|
|
929
|
+
} else if (arg === '--timeout') {
|
|
930
|
+
options.timeout = args[++i];
|
|
931
|
+
} else if (arg === '--create-namespace') {
|
|
932
|
+
options.createNamespace = true;
|
|
933
|
+
} else if (arg === '--dry-run') {
|
|
934
|
+
options.dryRun = true;
|
|
935
|
+
} else if (arg === '--keep-history') {
|
|
936
|
+
options.keepHistory = true;
|
|
937
|
+
} else if (arg === '-i' || arg === '--install') {
|
|
938
|
+
options.install = true;
|
|
939
|
+
} else if (arg === '--yes' || arg === '-y') {
|
|
940
|
+
options.yes = true;
|
|
941
|
+
} else if (arg === '--repo') {
|
|
942
|
+
options.repo = args[++i];
|
|
943
|
+
} else if (arg === '--destination' || arg === '-d') {
|
|
944
|
+
options.destination = args[++i];
|
|
945
|
+
} else if (arg === '--app-version') {
|
|
946
|
+
options.appVersion = args[++i];
|
|
947
|
+
} else if (arg === '--dependency-update') {
|
|
948
|
+
options.dependencyUpdate = true;
|
|
949
|
+
} else if (arg === '--revision') {
|
|
950
|
+
options.revision = parseInt(args[++i], 10);
|
|
951
|
+
} else if (arg.startsWith('--set=')) {
|
|
952
|
+
const setExpr = arg.slice(6);
|
|
953
|
+
const eqIdx = setExpr.indexOf('=');
|
|
954
|
+
if (eqIdx !== -1) {
|
|
955
|
+
const key = setExpr.slice(0, eqIdx);
|
|
956
|
+
const value = setExpr.slice(eqIdx + 1);
|
|
957
|
+
options.set = options.set || {};
|
|
958
|
+
options.set[key] = value;
|
|
959
|
+
options.values = options.values || {};
|
|
960
|
+
options.values[key] = value;
|
|
961
|
+
}
|
|
962
|
+
} else if (arg === '--set') {
|
|
963
|
+
const setExpr = args[++i] || '';
|
|
964
|
+
const eqIdx = setExpr.indexOf('=');
|
|
965
|
+
if (eqIdx !== -1) {
|
|
966
|
+
const key = setExpr.slice(0, eqIdx);
|
|
967
|
+
const value = setExpr.slice(eqIdx + 1);
|
|
968
|
+
options.set = options.set || {};
|
|
969
|
+
options.set[key] = value;
|
|
970
|
+
options.values = options.values || {};
|
|
971
|
+
options.values[key] = value;
|
|
972
|
+
}
|
|
973
|
+
} else if (!arg.startsWith('-')) {
|
|
974
|
+
positionalArgs.push(arg);
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
const startTime = Date.now();
|
|
979
|
+
const entry = historyManager.addEntry('helm', [subcommand, ...args]);
|
|
980
|
+
|
|
981
|
+
try {
|
|
982
|
+
switch (subcommand) {
|
|
983
|
+
case 'list':
|
|
984
|
+
case 'ls':
|
|
985
|
+
await helmListCommand(options);
|
|
986
|
+
break;
|
|
987
|
+
case 'install':
|
|
988
|
+
if (positionalArgs.length < 2) {
|
|
989
|
+
ui.error('Usage: nimbus helm install <release-name> <chart>');
|
|
990
|
+
return;
|
|
991
|
+
}
|
|
992
|
+
await helmInstallCommand(positionalArgs[0], positionalArgs[1], options);
|
|
993
|
+
break;
|
|
994
|
+
case 'upgrade':
|
|
995
|
+
if (positionalArgs.length < 2) {
|
|
996
|
+
ui.error('Usage: nimbus helm upgrade <release-name> <chart>');
|
|
997
|
+
return;
|
|
998
|
+
}
|
|
999
|
+
await helmUpgradeCommand(positionalArgs[0], positionalArgs[1], options);
|
|
1000
|
+
break;
|
|
1001
|
+
case 'uninstall':
|
|
1002
|
+
case 'delete':
|
|
1003
|
+
if (positionalArgs.length < 1) {
|
|
1004
|
+
ui.error('Usage: nimbus helm uninstall <release-name>');
|
|
1005
|
+
return;
|
|
1006
|
+
}
|
|
1007
|
+
await helmUninstallCommand(positionalArgs[0], options);
|
|
1008
|
+
break;
|
|
1009
|
+
case 'rollback':
|
|
1010
|
+
if (positionalArgs.length < 2) {
|
|
1011
|
+
ui.error('Usage: nimbus helm rollback <release-name> <revision>');
|
|
1012
|
+
return;
|
|
1013
|
+
}
|
|
1014
|
+
await helmRollbackCommand(positionalArgs[0], parseInt(positionalArgs[1], 10), options);
|
|
1015
|
+
break;
|
|
1016
|
+
case 'history':
|
|
1017
|
+
if (positionalArgs.length < 1) {
|
|
1018
|
+
ui.error('Usage: nimbus helm history <release-name>');
|
|
1019
|
+
return;
|
|
1020
|
+
}
|
|
1021
|
+
await helmHistoryCommand(positionalArgs[0], options);
|
|
1022
|
+
break;
|
|
1023
|
+
case 'search':
|
|
1024
|
+
if (positionalArgs.length < 1) {
|
|
1025
|
+
ui.error('Usage: nimbus helm search <keyword>');
|
|
1026
|
+
return;
|
|
1027
|
+
}
|
|
1028
|
+
await helmSearchCommand(positionalArgs[0], options);
|
|
1029
|
+
break;
|
|
1030
|
+
case 'show':
|
|
1031
|
+
if (positionalArgs.length < 1) {
|
|
1032
|
+
ui.error('Usage: nimbus helm show <chart> [--subcommand all|chart|readme|values|crds]');
|
|
1033
|
+
return;
|
|
1034
|
+
}
|
|
1035
|
+
{
|
|
1036
|
+
// First positional arg might be the subcommand (all, chart, readme, values, crds)
|
|
1037
|
+
const validSubs = ['all', 'chart', 'readme', 'values', 'crds'];
|
|
1038
|
+
let showSub: 'all' | 'chart' | 'readme' | 'values' | 'crds' = 'all';
|
|
1039
|
+
let chartName = positionalArgs[0];
|
|
1040
|
+
if (validSubs.includes(positionalArgs[0]) && positionalArgs[1]) {
|
|
1041
|
+
showSub = positionalArgs[0] as typeof showSub;
|
|
1042
|
+
chartName = positionalArgs[1];
|
|
1043
|
+
}
|
|
1044
|
+
await helmShowCommand(chartName, { ...options, subcommand: showSub });
|
|
1045
|
+
}
|
|
1046
|
+
break;
|
|
1047
|
+
case 'repo':
|
|
1048
|
+
if (positionalArgs[0] === 'add' && positionalArgs.length >= 3) {
|
|
1049
|
+
await helmRepoAddCommand(positionalArgs[1], positionalArgs[2]);
|
|
1050
|
+
} else if (positionalArgs[0] === 'update') {
|
|
1051
|
+
await helmRepoUpdateCommand();
|
|
1052
|
+
} else {
|
|
1053
|
+
ui.error('Usage: nimbus helm repo add <name> <url> | nimbus helm repo update');
|
|
1054
|
+
}
|
|
1055
|
+
break;
|
|
1056
|
+
case 'lint':
|
|
1057
|
+
if (positionalArgs.length < 1) {
|
|
1058
|
+
ui.error('Usage: nimbus helm lint <chart-path> [--strict]');
|
|
1059
|
+
return;
|
|
1060
|
+
}
|
|
1061
|
+
{
|
|
1062
|
+
const strict = args.includes('--strict');
|
|
1063
|
+
const valuesFiles: string[] = [];
|
|
1064
|
+
for (let i = 0; i < args.length; i++) {
|
|
1065
|
+
if ((args[i] === '-f' || args[i] === '--values') && args[i + 1]) {
|
|
1066
|
+
valuesFiles.push(args[i + 1]);
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
await helmLintCommand(positionalArgs[0], { ...options, strict, valuesFiles });
|
|
1070
|
+
}
|
|
1071
|
+
break;
|
|
1072
|
+
case 'template':
|
|
1073
|
+
if (positionalArgs.length < 2) {
|
|
1074
|
+
ui.error(
|
|
1075
|
+
'Usage: nimbus helm template <release-name> <chart> [--namespace <ns>] [--values <file>] [--set key=val] [--version <ver>]'
|
|
1076
|
+
);
|
|
1077
|
+
return;
|
|
1078
|
+
}
|
|
1079
|
+
await helmTemplateCommand(positionalArgs[0], positionalArgs[1], options);
|
|
1080
|
+
break;
|
|
1081
|
+
case 'package':
|
|
1082
|
+
if (positionalArgs.length < 1) {
|
|
1083
|
+
ui.error(
|
|
1084
|
+
'Usage: nimbus helm package <chart-path> [--destination <dir>] [--version <ver>] [--app-version <ver>]'
|
|
1085
|
+
);
|
|
1086
|
+
return;
|
|
1087
|
+
}
|
|
1088
|
+
await helmPackageCommand(positionalArgs[0], options);
|
|
1089
|
+
break;
|
|
1090
|
+
case 'dependency':
|
|
1091
|
+
case 'dep': {
|
|
1092
|
+
// Supports: nimbus helm dependency update <chart-path>
|
|
1093
|
+
// nimbus helm dependency build <chart-path>
|
|
1094
|
+
const depSubcommand = positionalArgs[0] || 'update';
|
|
1095
|
+
const depChartPath = positionalArgs[1];
|
|
1096
|
+
if (!depChartPath) {
|
|
1097
|
+
ui.error('Usage: nimbus helm dependency <update|build> <chart-path>');
|
|
1098
|
+
return;
|
|
1099
|
+
}
|
|
1100
|
+
await helmDependencyCommand(depSubcommand, depChartPath);
|
|
1101
|
+
break;
|
|
1102
|
+
}
|
|
1103
|
+
case 'status':
|
|
1104
|
+
if (positionalArgs.length < 1) {
|
|
1105
|
+
ui.error(
|
|
1106
|
+
'Usage: nimbus helm status <release-name> [--namespace <ns>] [--revision <rev>]'
|
|
1107
|
+
);
|
|
1108
|
+
return;
|
|
1109
|
+
}
|
|
1110
|
+
await helmStatusCommand(positionalArgs[0], options);
|
|
1111
|
+
break;
|
|
1112
|
+
default:
|
|
1113
|
+
ui.error(`Unknown helm subcommand: ${subcommand}`);
|
|
1114
|
+
ui.info(
|
|
1115
|
+
'Available commands: list, install, upgrade, uninstall, rollback, history, search, show, repo, lint, template, package, dependency, status'
|
|
1116
|
+
);
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
historyManager.completeEntry(entry.id, 'success', Date.now() - startTime);
|
|
1120
|
+
} catch (error: any) {
|
|
1121
|
+
historyManager.completeEntry(entry.id, 'failure', Date.now() - startTime, {
|
|
1122
|
+
error: error.message,
|
|
1123
|
+
});
|
|
1124
|
+
throw error;
|
|
1125
|
+
}
|
|
1126
|
+
}
|