release orchestration strengthening

This commit is contained in:
master
2026-01-17 21:32:03 +02:00
parent 195dff2457
commit da27b9faa9
256 changed files with 94634 additions and 2269 deletions

View 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"
}
}

View 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>
`;
}