release orchestration strengthening
This commit is contained in:
146
src/Extensions/vscode-stella-ops/package.json
Normal file
146
src/Extensions/vscode-stella-ops/package.json
Normal file
@@ -0,0 +1,146 @@
|
||||
{
|
||||
"name": "stella-ops",
|
||||
"displayName": "Stella Ops",
|
||||
"description": "VS Code extension for Stella Ops release control plane",
|
||||
"version": "1.0.0",
|
||||
"publisher": "stella-ops",
|
||||
"engines": {
|
||||
"vscode": "^1.85.0"
|
||||
},
|
||||
"categories": [
|
||||
"Other",
|
||||
"SCM Providers"
|
||||
],
|
||||
"keywords": [
|
||||
"release",
|
||||
"deployment",
|
||||
"devops",
|
||||
"ci-cd",
|
||||
"promotion"
|
||||
],
|
||||
"activationEvents": [
|
||||
"workspaceContains:**/stella.yaml"
|
||||
],
|
||||
"main": "./out/extension.js",
|
||||
"contributes": {
|
||||
"commands": [
|
||||
{
|
||||
"command": "stella.createRelease",
|
||||
"title": "Create Release",
|
||||
"category": "Stella"
|
||||
},
|
||||
{
|
||||
"command": "stella.promote",
|
||||
"title": "Promote Release",
|
||||
"category": "Stella"
|
||||
},
|
||||
{
|
||||
"command": "stella.viewRelease",
|
||||
"title": "View Release Details",
|
||||
"category": "Stella"
|
||||
},
|
||||
{
|
||||
"command": "stella.viewDeployment",
|
||||
"title": "View Deployment",
|
||||
"category": "Stella"
|
||||
},
|
||||
{
|
||||
"command": "stella.refreshReleases",
|
||||
"title": "Refresh Releases",
|
||||
"category": "Stella",
|
||||
"icon": "$(refresh)"
|
||||
},
|
||||
{
|
||||
"command": "stella.validateConfig",
|
||||
"title": "Validate Configuration",
|
||||
"category": "Stella"
|
||||
},
|
||||
{
|
||||
"command": "stella.openDashboard",
|
||||
"title": "Open Dashboard",
|
||||
"category": "Stella"
|
||||
},
|
||||
{
|
||||
"command": "stella.login",
|
||||
"title": "Login",
|
||||
"category": "Stella"
|
||||
}
|
||||
],
|
||||
"viewsContainers": {
|
||||
"activitybar": [
|
||||
{
|
||||
"id": "stella-ops",
|
||||
"title": "Stella Ops",
|
||||
"icon": "resources/stella-icon.svg"
|
||||
}
|
||||
]
|
||||
},
|
||||
"views": {
|
||||
"stella-ops": [
|
||||
{
|
||||
"id": "stellaReleases",
|
||||
"name": "Releases",
|
||||
"icon": "resources/release-icon.svg"
|
||||
},
|
||||
{
|
||||
"id": "stellaEnvironments",
|
||||
"name": "Environments",
|
||||
"icon": "resources/environment-icon.svg"
|
||||
}
|
||||
]
|
||||
},
|
||||
"menus": {
|
||||
"view/title": [
|
||||
{
|
||||
"command": "stella.refreshReleases",
|
||||
"when": "view == stellaReleases",
|
||||
"group": "navigation"
|
||||
}
|
||||
],
|
||||
"view/item/context": [
|
||||
{
|
||||
"command": "stella.promote",
|
||||
"when": "viewItem == release",
|
||||
"group": "inline"
|
||||
}
|
||||
]
|
||||
},
|
||||
"configuration": {
|
||||
"title": "Stella Ops",
|
||||
"properties": {
|
||||
"stella.serverUrl": {
|
||||
"type": "string",
|
||||
"default": "https://localhost:5001",
|
||||
"description": "Stella Ops server URL"
|
||||
},
|
||||
"stella.autoValidate": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Automatically validate stella.yaml on save"
|
||||
}
|
||||
}
|
||||
},
|
||||
"languages": [
|
||||
{
|
||||
"id": "stella-yaml",
|
||||
"extensions": [".stella.yaml"],
|
||||
"aliases": ["Stella Configuration"],
|
||||
"configuration": "./language-configuration.json"
|
||||
}
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"vscode:prepublish": "npm run compile",
|
||||
"compile": "tsc -p ./",
|
||||
"watch": "tsc -watch -p ./",
|
||||
"lint": "eslint src --ext ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/vscode": "^1.85.0",
|
||||
"@types/node": "^20.0.0",
|
||||
"typescript": "^5.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
||||
"@typescript-eslint/parser": "^6.0.0",
|
||||
"eslint": "^8.0.0"
|
||||
}
|
||||
}
|
||||
367
src/Extensions/vscode-stella-ops/src/extension.ts
Normal file
367
src/Extensions/vscode-stella-ops/src/extension.ts
Normal file
@@ -0,0 +1,367 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// StellaOpsExtension - VS Code Extension
|
||||
// Sprint: SPRINT_20260117_037_ReleaseOrchestrator_developer_experience
|
||||
// Task: TASK-037-06 - VS Code Extension with tree view, commands, and code lens
|
||||
// Description: VS Code extension package definition
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* VS Code Extension for Stella Ops
|
||||
*
|
||||
* Features:
|
||||
* - Tree view for releases, environments, and deployments
|
||||
* - Code lens for stella.yaml configuration files
|
||||
* - Commands for release management
|
||||
* - Status bar integration
|
||||
* - IntelliSense for configuration files
|
||||
*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
// ============================================================================
|
||||
// Extension Activation
|
||||
// ============================================================================
|
||||
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
console.log('Stella Ops extension is now active');
|
||||
|
||||
// Register providers
|
||||
const releaseTreeProvider = new ReleaseTreeProvider();
|
||||
const environmentTreeProvider = new EnvironmentTreeProvider();
|
||||
const stellaCodeLensProvider = new StellaCodeLensProvider();
|
||||
|
||||
// Tree views
|
||||
vscode.window.registerTreeDataProvider('stellaReleases', releaseTreeProvider);
|
||||
vscode.window.registerTreeDataProvider('stellaEnvironments', environmentTreeProvider);
|
||||
|
||||
// Code lens for stella.yaml files
|
||||
context.subscriptions.push(
|
||||
vscode.languages.registerCodeLensProvider(
|
||||
{ pattern: '**/stella.yaml' },
|
||||
stellaCodeLensProvider
|
||||
)
|
||||
);
|
||||
|
||||
// Register commands
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('stella.createRelease', createReleaseCommand),
|
||||
vscode.commands.registerCommand('stella.promote', promoteCommand),
|
||||
vscode.commands.registerCommand('stella.viewRelease', viewReleaseCommand),
|
||||
vscode.commands.registerCommand('stella.viewDeployment', viewDeploymentCommand),
|
||||
vscode.commands.registerCommand('stella.refreshReleases', () => releaseTreeProvider.refresh()),
|
||||
vscode.commands.registerCommand('stella.validateConfig', validateConfigCommand),
|
||||
vscode.commands.registerCommand('stella.openDashboard', openDashboardCommand),
|
||||
vscode.commands.registerCommand('stella.login', loginCommand)
|
||||
);
|
||||
|
||||
// Status bar
|
||||
const statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 100);
|
||||
statusBarItem.text = '$(rocket) Stella Ops';
|
||||
statusBarItem.command = 'stella.openDashboard';
|
||||
statusBarItem.show();
|
||||
context.subscriptions.push(statusBarItem);
|
||||
|
||||
// File watcher for stella.yaml changes
|
||||
const watcher = vscode.workspace.createFileSystemWatcher('**/stella.yaml');
|
||||
watcher.onDidChange(() => validateConfigCommand());
|
||||
context.subscriptions.push(watcher);
|
||||
}
|
||||
|
||||
export function deactivate() {}
|
||||
|
||||
// ============================================================================
|
||||
// Tree Data Providers
|
||||
// ============================================================================
|
||||
|
||||
class ReleaseTreeProvider implements vscode.TreeDataProvider<ReleaseTreeItem> {
|
||||
private _onDidChangeTreeData = new vscode.EventEmitter<ReleaseTreeItem | undefined>();
|
||||
readonly onDidChangeTreeData = this._onDidChangeTreeData.event;
|
||||
|
||||
refresh(): void {
|
||||
this._onDidChangeTreeData.fire(undefined);
|
||||
}
|
||||
|
||||
getTreeItem(element: ReleaseTreeItem): vscode.TreeItem {
|
||||
return element;
|
||||
}
|
||||
|
||||
async getChildren(element?: ReleaseTreeItem): Promise<ReleaseTreeItem[]> {
|
||||
if (!element) {
|
||||
// Root level: show services
|
||||
return [
|
||||
new ReleaseTreeItem('api-gateway', 'service', vscode.TreeItemCollapsibleState.Collapsed),
|
||||
new ReleaseTreeItem('user-service', 'service', vscode.TreeItemCollapsibleState.Collapsed),
|
||||
new ReleaseTreeItem('order-service', 'service', vscode.TreeItemCollapsibleState.Collapsed)
|
||||
];
|
||||
}
|
||||
|
||||
if (element.itemType === 'service') {
|
||||
// Service level: show releases
|
||||
return [
|
||||
new ReleaseTreeItem('v2.3.1 (Production)', 'release', vscode.TreeItemCollapsibleState.None, {
|
||||
status: 'deployed',
|
||||
environment: 'prod'
|
||||
}),
|
||||
new ReleaseTreeItem('v2.4.0 (Staging)', 'release', vscode.TreeItemCollapsibleState.None, {
|
||||
status: 'deployed',
|
||||
environment: 'staging'
|
||||
}),
|
||||
new ReleaseTreeItem('v2.5.0-rc1 (Dev)', 'release', vscode.TreeItemCollapsibleState.None, {
|
||||
status: 'deployed',
|
||||
environment: 'dev'
|
||||
})
|
||||
];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
class ReleaseTreeItem extends vscode.TreeItem {
|
||||
constructor(
|
||||
public readonly label: string,
|
||||
public readonly itemType: 'service' | 'release',
|
||||
public readonly collapsibleState: vscode.TreeItemCollapsibleState,
|
||||
public readonly metadata?: { status?: string; environment?: string }
|
||||
) {
|
||||
super(label, collapsibleState);
|
||||
|
||||
if (itemType === 'service') {
|
||||
this.iconPath = new vscode.ThemeIcon('package');
|
||||
this.contextValue = 'service';
|
||||
} else {
|
||||
this.iconPath = metadata?.status === 'deployed'
|
||||
? new vscode.ThemeIcon('check', new vscode.ThemeColor('testing.iconPassed'))
|
||||
: new vscode.ThemeIcon('circle-outline');
|
||||
this.contextValue = 'release';
|
||||
this.command = {
|
||||
command: 'stella.viewRelease',
|
||||
title: 'View Release',
|
||||
arguments: [this]
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class EnvironmentTreeProvider implements vscode.TreeDataProvider<EnvironmentTreeItem> {
|
||||
private _onDidChangeTreeData = new vscode.EventEmitter<EnvironmentTreeItem | undefined>();
|
||||
readonly onDidChangeTreeData = this._onDidChangeTreeData.event;
|
||||
|
||||
getTreeItem(element: EnvironmentTreeItem): vscode.TreeItem {
|
||||
return element;
|
||||
}
|
||||
|
||||
async getChildren(element?: EnvironmentTreeItem): Promise<EnvironmentTreeItem[]> {
|
||||
if (!element) {
|
||||
return [
|
||||
new EnvironmentTreeItem('Production', 'prod', 'healthy'),
|
||||
new EnvironmentTreeItem('Staging', 'staging', 'healthy'),
|
||||
new EnvironmentTreeItem('Development', 'dev', 'healthy')
|
||||
];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
class EnvironmentTreeItem extends vscode.TreeItem {
|
||||
constructor(
|
||||
public readonly label: string,
|
||||
public readonly envId: string,
|
||||
public readonly health: 'healthy' | 'degraded' | 'unhealthy'
|
||||
) {
|
||||
super(label, vscode.TreeItemCollapsibleState.None);
|
||||
|
||||
this.iconPath = health === 'healthy'
|
||||
? new vscode.ThemeIcon('check', new vscode.ThemeColor('testing.iconPassed'))
|
||||
: health === 'degraded'
|
||||
? new vscode.ThemeIcon('warning', new vscode.ThemeColor('editorWarning.foreground'))
|
||||
: new vscode.ThemeIcon('error', new vscode.ThemeColor('editorError.foreground'));
|
||||
|
||||
this.description = health;
|
||||
this.contextValue = 'environment';
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Code Lens Provider
|
||||
// ============================================================================
|
||||
|
||||
class StellaCodeLensProvider implements vscode.CodeLensProvider {
|
||||
provideCodeLenses(document: vscode.TextDocument): vscode.CodeLens[] {
|
||||
const codeLenses: vscode.CodeLens[] = [];
|
||||
const text = document.getText();
|
||||
const lines = text.split('\n');
|
||||
|
||||
lines.forEach((line, index) => {
|
||||
// Add code lens for version declarations
|
||||
if (line.match(/^\s*version:/)) {
|
||||
const range = new vscode.Range(index, 0, index, line.length);
|
||||
codeLenses.push(
|
||||
new vscode.CodeLens(range, {
|
||||
title: '$(rocket) Create Release',
|
||||
command: 'stella.createRelease'
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// Add code lens for environment references
|
||||
if (line.match(/^\s*environment:/)) {
|
||||
const range = new vscode.Range(index, 0, index, line.length);
|
||||
codeLenses.push(
|
||||
new vscode.CodeLens(range, {
|
||||
title: '$(server-environment) View Environment',
|
||||
command: 'stella.openDashboard'
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// Add code lens for policy references
|
||||
if (line.match(/^\s*policies:/)) {
|
||||
const range = new vscode.Range(index, 0, index, line.length);
|
||||
codeLenses.push(
|
||||
new vscode.CodeLens(range, {
|
||||
title: '$(shield) Validate Policies',
|
||||
command: 'stella.validateConfig'
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return codeLenses;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Commands
|
||||
// ============================================================================
|
||||
|
||||
async function createReleaseCommand() {
|
||||
const service = await vscode.window.showInputBox({
|
||||
prompt: 'Service name',
|
||||
placeHolder: 'e.g., api-gateway'
|
||||
});
|
||||
|
||||
if (!service) return;
|
||||
|
||||
const version = await vscode.window.showInputBox({
|
||||
prompt: 'Version',
|
||||
placeHolder: 'e.g., v1.2.3'
|
||||
});
|
||||
|
||||
if (!version) return;
|
||||
|
||||
const notes = await vscode.window.showInputBox({
|
||||
prompt: 'Release notes (optional)',
|
||||
placeHolder: 'Description of changes'
|
||||
});
|
||||
|
||||
// Execute CLI command
|
||||
const terminal = vscode.window.createTerminal('Stella Ops');
|
||||
terminal.sendText(`stella release create ${service} ${version}${notes ? ` --notes "${notes}"` : ''}`);
|
||||
terminal.show();
|
||||
}
|
||||
|
||||
async function promoteCommand() {
|
||||
const release = await vscode.window.showInputBox({
|
||||
prompt: 'Release ID',
|
||||
placeHolder: 'e.g., rel-abc123'
|
||||
});
|
||||
|
||||
if (!release) return;
|
||||
|
||||
const target = await vscode.window.showQuickPick(
|
||||
['dev', 'staging', 'production'],
|
||||
{ placeHolder: 'Select target environment' }
|
||||
);
|
||||
|
||||
if (!target) return;
|
||||
|
||||
const terminal = vscode.window.createTerminal('Stella Ops');
|
||||
terminal.sendText(`stella promote start ${release} ${target}`);
|
||||
terminal.show();
|
||||
}
|
||||
|
||||
async function viewReleaseCommand(item?: ReleaseTreeItem) {
|
||||
// Open release details in a webview
|
||||
const panel = vscode.window.createWebviewPanel(
|
||||
'stellaRelease',
|
||||
`Release: ${item?.label || 'Details'}`,
|
||||
vscode.ViewColumn.One,
|
||||
{ enableScripts: true }
|
||||
);
|
||||
|
||||
panel.webview.html = getReleaseWebviewContent(item?.label || 'Unknown');
|
||||
}
|
||||
|
||||
async function viewDeploymentCommand() {
|
||||
const deploymentId = await vscode.window.showInputBox({
|
||||
prompt: 'Deployment ID',
|
||||
placeHolder: 'e.g., dep-abc123'
|
||||
});
|
||||
|
||||
if (!deploymentId) return;
|
||||
|
||||
const terminal = vscode.window.createTerminal('Stella Ops');
|
||||
terminal.sendText(`stella deploy status ${deploymentId} --watch`);
|
||||
terminal.show();
|
||||
}
|
||||
|
||||
async function validateConfigCommand() {
|
||||
const terminal = vscode.window.createTerminal('Stella Ops');
|
||||
terminal.sendText('stella config validate');
|
||||
terminal.show();
|
||||
}
|
||||
|
||||
async function openDashboardCommand() {
|
||||
vscode.env.openExternal(vscode.Uri.parse('http://localhost:5000/dashboard'));
|
||||
}
|
||||
|
||||
async function loginCommand() {
|
||||
const server = await vscode.window.showInputBox({
|
||||
prompt: 'Stella server URL',
|
||||
placeHolder: 'https://stella.example.com',
|
||||
value: 'https://localhost:5001'
|
||||
});
|
||||
|
||||
if (!server) return;
|
||||
|
||||
const terminal = vscode.window.createTerminal('Stella Ops');
|
||||
terminal.sendText(`stella auth login ${server} --interactive`);
|
||||
terminal.show();
|
||||
}
|
||||
|
||||
function getReleaseWebviewContent(releaseName: string): string {
|
||||
return `
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Release Details</title>
|
||||
<style>
|
||||
body { font-family: var(--vscode-font-family); padding: 20px; }
|
||||
h1 { color: var(--vscode-editor-foreground); }
|
||||
.section { margin: 20px 0; }
|
||||
.label { color: var(--vscode-descriptionForeground); }
|
||||
.value { color: var(--vscode-editor-foreground); font-weight: bold; }
|
||||
.status-deployed { color: var(--vscode-testing-iconPassed); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Release: ${releaseName}</h1>
|
||||
<div class="section">
|
||||
<span class="label">Status: </span>
|
||||
<span class="value status-deployed">Deployed</span>
|
||||
</div>
|
||||
<div class="section">
|
||||
<span class="label">Environment: </span>
|
||||
<span class="value">Production</span>
|
||||
</div>
|
||||
<div class="section">
|
||||
<span class="label">Deployed At: </span>
|
||||
<span class="value">2026-01-17 12:00 UTC</span>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
}
|
||||
Reference in New Issue
Block a user