Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ This extension uses two different methods to get information from the Kubernetes
# Features
- Configure, visualize and manage Flux resources
- Tree views for Clusters, Sources, and Workloads
- **HelmRelease IntelliSense** - Auto-completion, hover documentation, and validation for HelmRelease `values` fields
- Observe Flux resource updates in the cluster in real-time
- Select clusters and examine installed [GitOps Toolkit components](https://fluxcd.io/docs/components/)
- Enable and Disable GitOps (install/uninstall Flux) on clusters
Expand All @@ -56,6 +57,18 @@ This extension uses two different methods to get information from the Kubernetes
- Watch Flux controller logs and `flux` CLI commands for diagnostics
- Documentation links for [Flux](https://fluxcd.io/docs) and [Weave GitOps](https://docs.gitops.weave.works/docs/intro/) embedded in the extension

## HelmRelease Values IntelliSense

When editing HelmRelease resources, the extension provides IntelliSense support for the `values` field:

- **Auto-completion**: Get suggestions for valid configuration properties based on the Helm chart's `values.schema.json`
- **Hover documentation**: See property descriptions, types, and default values when hovering over keys
- **Validation**: Get warnings for unknown properties and type mismatches

This feature works with Helm charts that include a JSON schema (like Traefik, Bitnami charts, etc.). The schema is automatically fetched from the HelmRepository and cached for offline use.

![HelmRelease IntelliSense](docs/images/helmrelease-intellisense.png)

# Video / Demo

[![GitOps Deployments from VS Code with little to no Kubernetes Knowledge](https://img.youtube.com/vi/G7fdFWW1I-0/0.jpg)](https://www.youtube.com/watch?v=G7fdFWW1I-0 "VSCode Live Stream, Mar/23/2023")
Expand Down
75 changes: 59 additions & 16 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,7 @@
"@types/node": "14.x",
"@types/semver": "^7.3.9",
"@types/shelljs": "^0.8.11",
"@types/tar": "^6.1.4",
"@types/uuid": "^9.0.1",
"@types/vscode": "^1.59.0",
"@types/vscode-webview": "^1.57.0",
Expand Down Expand Up @@ -587,6 +588,7 @@
"semver": "^7.3.5",
"shell-escape-tag": "^2.0.2",
"shelljs": "^0.8.5",
"tar": "^6.1.11",
"tinytim": "^0.1.1",
"uuid": "^9.0.0",
"vscode-kubernetes-tools-api": "^1.3.0"
Expand Down
47 changes: 46 additions & 1 deletion src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { commands, ExtensionContext, ExtensionMode, window, workspace } from 'vscode';
import { commands, ExtensionContext, ExtensionMode, languages, window, workspace } from 'vscode';
import { CommandId, registerCommands } from './commands';
import { getExtensionVersion } from './commands/showInstalledVersions';
import { showNewUserGuide } from './commands/showNewUserGuide';
Expand All @@ -12,6 +12,10 @@ import { Telemetry, TelemetryEventNames } from './telemetry';
import { createTreeViews, clusterTreeViewProvider, sourceTreeViewProvider, workloadTreeViewProvider } from './views/treeViews';
import { shell } from './shell';
import { kubeProxyKeepAlive, setExtensionActive, disposeKubeProxy } from './k8s/kubectlProxy';
import { helmReleaseValuesCompletionProvider } from './language/helmReleaseValuesCompletionProvider';
import { helmReleaseValuesHoverProvider } from './language/helmReleaseValuesHoverProvider';
import { helmReleaseValuesDiagnosticProvider } from './language/helmReleaseValuesDiagnosticProvider';
import { schemaCache } from './language/schemaCache';

/** Disable interactive modal dialogs, useful for testing */
export let disableConfirmations = false;
Expand Down Expand Up @@ -40,12 +44,53 @@ export async function activate(context: ExtensionContext) {

telemetry = new Telemetry(context, getExtensionVersion(), GitOpsExtensionConstants.ExtensionId);

// Initialize schema cache with extension context for disk storage
schemaCache.initialize(context);

// create gitops tree views
createTreeViews();

// register gitops commands
registerCommands(context);

// Register HelmRelease values completion provider
context.subscriptions.push(
languages.registerCompletionItemProvider(
{ language: 'yaml', scheme: 'file' },
helmReleaseValuesCompletionProvider,
'.', ':', ' ' // Trigger on property access, colon, and space
)
);

// Register HelmRelease values hover provider
context.subscriptions.push(
languages.registerHoverProvider(
{ language: 'yaml', scheme: 'file' },
helmReleaseValuesHoverProvider
)
);

// Register HelmRelease values diagnostic provider
context.subscriptions.push(helmReleaseValuesDiagnosticProvider.getDiagnosticCollection());

// Validate on document open and change
context.subscriptions.push(
workspace.onDidOpenTextDocument(doc => {
helmReleaseValuesDiagnosticProvider.validateDocument(doc);
}),
workspace.onDidChangeTextDocument(event => {
helmReleaseValuesDiagnosticProvider.validateDocument(event.document);
}),
workspace.onDidCloseTextDocument(doc => {
helmReleaseValuesDiagnosticProvider.clearDiagnostics(doc);
})
);

// Validate already open documents
workspace.textDocuments.forEach(doc => {
helmReleaseValuesDiagnosticProvider.validateDocument(doc);
});

telemetry.send(TelemetryEventNames.Startup);

if (globalState.get(GlobalStateKey.FirstEverActivationStorageKey) === undefined) {
Expand Down
85 changes: 85 additions & 0 deletions src/language/chartReferenceParser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { HelmReleaseInfo } from './helmReleaseDetector';

/**
* Parsed chart reference with all necessary info to fetch schema
*/
export interface ChartReference {
/** Chart name (e.g., 'traefik', 'postgresql') */
chartName: string;
/** Chart version (e.g., '10.0.0', '>=1.0.0') - may be undefined for latest */
chartVersion?: string;
/** Source kind (HelmRepository, GitRepository, OCIRepository) */
sourceKind: string;
/** Source name */
sourceName: string;
/** Source namespace (defaults to release namespace or 'flux-system') */
sourceNamespace: string;
/** The HelmRelease name for reference */
releaseName: string;
/** The HelmRelease namespace */
releaseNamespace: string;
}

/**
* Parses chart references from HelmRelease information
*/
export class ChartReferenceParser {

/**
* Parse a ChartReference from HelmReleaseInfo
* Returns undefined if required fields are missing
*/
public parseChartReference(info: HelmReleaseInfo): ChartReference | undefined {
// Chart name is required
if (!info.chartName) {
return undefined;
}

// Source reference is required
if (!info.sourceRefKind || !info.sourceRefName) {
return undefined;
}

return {
chartName: info.chartName,
chartVersion: info.chartVersion,
sourceKind: info.sourceRefKind,
sourceName: info.sourceRefName,
sourceNamespace: info.sourceRefNamespace || info.namespace || 'flux-system',
releaseName: info.name,
releaseNamespace: info.namespace || 'default'
};
}

/**
* Generate a unique cache key for a chart reference
*/
public getCacheKey(ref: ChartReference): string {
const version = ref.chartVersion || 'latest';
return `${ref.sourceKind}/${ref.sourceNamespace}/${ref.sourceName}/${ref.chartName}@${version}`;
}

/**
* Check if this is a HelmRepository source (most common, easiest to fetch)
*/
public isHelmRepositorySource(ref: ChartReference): boolean {
return ref.sourceKind === 'HelmRepository';
}

/**
* Check if this is an OCI repository source
*/
public isOCIRepositorySource(ref: ChartReference): boolean {
return ref.sourceKind === 'OCIRepository';
}

/**
* Check if this is a Git repository source
*/
public isGitRepositorySource(ref: ChartReference): boolean {
return ref.sourceKind === 'GitRepository';
}
}

// Singleton instance
export const chartReferenceParser = new ChartReferenceParser();
Loading