Skip to content

Commit 581035c

Browse files
committed
changes for deploy
1 parent 5fff97f commit 581035c

File tree

9 files changed

+1646
-140
lines changed

9 files changed

+1646
-140
lines changed

package-lock.json

Lines changed: 1274 additions & 54 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@
2727
"multi-root ready"
2828
],
2929
"preview": true,
30-
"activationEvents": [],
30+
"activationEvents": [
31+
"onCommand:azureProjectCreation.openDeployPlan"
32+
],
3133
"main": "./main.js",
3234
"contributes": {
3335
"mcpServerDefinitionProviders": [
@@ -122,7 +124,7 @@
122124
},
123125
{
124126
"command": "containerApps.showProjectCreation",
125-
"title": "Show Azure Project Creation View",
127+
"title": "Open Deploy Plan View",
126128
"category": "Azure Container Apps"
127129
},
128130
{
@@ -893,6 +895,7 @@
893895
"deep-eql": "^4.1.3",
894896
"dotenv": "^16.0.0",
895897
"fs-extra": "^8.1.0",
898+
"mermaid": "^11.14.0",
896899
"monaco-editor": "~0.51.0",
897900
"p-retry": "^4.6.2",
898901
"react": "^18.3.1",

src/commands/copilot/openDeploymentPlanView.ts

Lines changed: 54 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -3,73 +3,72 @@
33
* Licensed under the MIT License. See License.md in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6+
import * as vscode from "vscode";
67
import { type DeploymentPlanData } from "../../webviews/DeploymentPlanView";
78
import { DeploymentPlanViewController } from "../../webviews/DeploymentPlanViewController";
8-
import * as vscode from "vscode";
9+
import { parseDeploymentPlanMarkdown } from "../../utils/parseDeploymentPlanMarkdown";
910

1011
let currentDeploymentPlanViewController: DeploymentPlanViewController | undefined;
1112

12-
const sampleDeploymentPlanData: DeploymentPlanData = {
13-
status: "Awaiting Approval",
14-
mode: "MODERNIZE — deploy existing full-stack app to Azure",
15-
subscription: "meganmott dev",
16-
location: "East US",
17-
locationCode: "eastus",
18-
mermaidDiagram: `graph TD
19-
Browser -->|HTTPS| SWA[Azure Static Web Apps<br/>React / Vite frontend]
20-
SWA -->|/api/* proxy| FN[Azure Functions App<br/>Node.js 20 · TypeScript]
21-
FN -->|Managed Identity| KV[Azure Key Vault<br/>Cosmos connection string]
22-
FN -->|Cosmos SDK| DB[(Azure Cosmos DB<br/>NoSQL · Serverless)]
13+
export function openDeploymentPlanView(uri: vscode.Uri): void {
14+
void openDeploymentPlanViewAsync(uri);
15+
}
16+
17+
export function openDeploymentPlanViewWithContent(content: string): void {
18+
const planData = parseDeploymentPlanMarkdown(content);
19+
showOrUpdateView(planData);
20+
}
21+
22+
export function refreshDeploymentPlanView(uri: vscode.Uri): void {
23+
if (currentDeploymentPlanViewController) {
24+
void refreshDeploymentPlanViewAsync(uri);
25+
}
26+
}
2327

24-
style SWA fill:#0078d4,color:#fff
25-
style FN fill:#0078d4,color:#fff
26-
style KV fill:#0078d4,color:#fff
27-
style DB fill:#0078d4,color:#fff`,
28-
workspaceScan: {
29-
headers: ["Component", "Technology", "Azure Target"],
30-
rows: [
31-
["src/web", "React 18, Vite, TypeScript", "Azure Static Web Apps"],
32-
["src/functions", "Azure Functions v4, Node.js, TypeScript", "Azure Functions (linked to SWA)"],
33-
["src/shared", "TypeScript library", "Built + bundled (no separate service)"],
34-
["Cosmos DB", "@azure/cosmos SDK, connection string env var", "Azure Cosmos DB for NoSQL (serverless)"],
35-
["Web → API routing", "Vite /api proxy in dev", "SWA native /api proxy at runtime"],
36-
],
37-
},
38-
decisions: {
39-
headers: ["Decision", "Choice", "Rationale"],
40-
rows: [
41-
["Recipe", "AZD + Bicep", "Both tools installed; AZD is the recommended default for end-to-end deploy"],
42-
["Functions plan", "Consumption", "Cost-efficient for low-to-medium workloads"],
43-
["Cosmos DB capacity", "Serverless", "No sustained throughput; ideal for dev/compliance calendar usage"],
44-
["Frontend hosting", "Azure Static Web Apps (SWA)", "Native Vite/React support; built-in /api proxy to linked Functions app"],
45-
["Secrets", "Key Vault + SWA/Functions app settings", "Cosmos connection string stored in Key Vault; referenced via @Microsoft.KeyVault(...)"],
46-
["Identity", "System-assigned Managed Identity on Functions app", "Grants Functions access to Key Vault without storing credentials in config"],
47-
],
48-
},
49-
resources: {
50-
headers: ["Resource", "Name pattern", "SKU / Tier"],
51-
rows: [
52-
["Resource Group", "rg-compliance-<env>", "—"],
53-
["Static Web Apps", "swa-compliance-<env>", "Free"],
54-
["Functions App", "func-compliance-<env>", "Consumption (Y1)"],
55-
["App Service Plan", "asp-compliance-<env>", "[Consumption — auto-created by Functions]"],
56-
["Storage Account", "stcompliance<env>", "Standard LRS (required by Functions)"],
57-
["Cosmos DB account", "cosmos-compliance-<env>", "Serverless, NoSQL"],
58-
["Key Vault", "kv-compliance-<env>", "Standard"],
59-
["Log Analytics Workspace", "log-compliance-<env>", "PerGB2018"],
60-
["Application Insights", "appi-compliance-<env>", "—"],
61-
],
62-
},
63-
};
28+
export async function openDeploymentPlanViewFromWorkspace(): Promise<void> {
29+
const files = await vscode.workspace.findFiles('**/.azure/plan.md', '**/node_modules/**', 10);
30+
if (files.length === 0) {
31+
void vscode.window.showInformationMessage('No deployment plan markdown files found in the workspace.');
32+
return;
33+
}
34+
35+
let selected: vscode.Uri;
36+
if (files.length === 1) {
37+
selected = files[0];
38+
} else {
39+
const picked = await vscode.window.showQuickPick(
40+
files.map((f) => ({ label: vscode.workspace.asRelativePath(f), uri: f })),
41+
{ placeHolder: 'Select a deployment plan file to open' },
42+
);
43+
if (!picked) {
44+
return;
45+
}
46+
selected = picked.uri;
47+
}
48+
49+
await openDeploymentPlanViewAsync(selected);
50+
}
51+
52+
async function openDeploymentPlanViewAsync(uri: vscode.Uri): Promise<void> {
53+
const content = Buffer.from(await vscode.workspace.fs.readFile(uri)).toString('utf-8');
54+
const planData = parseDeploymentPlanMarkdown(content);
55+
showOrUpdateView(planData);
56+
}
57+
58+
async function refreshDeploymentPlanViewAsync(uri: vscode.Uri): Promise<void> {
59+
const content = Buffer.from(await vscode.workspace.fs.readFile(uri)).toString('utf-8');
60+
const planData = parseDeploymentPlanMarkdown(content);
61+
currentDeploymentPlanViewController?.updateDeploymentPlanData(planData);
62+
}
6463

65-
export function openDeploymentPlanView(): void {
64+
function showOrUpdateView(planData: DeploymentPlanData): void {
6665
if (currentDeploymentPlanViewController) {
67-
currentDeploymentPlanViewController.updateDeploymentPlanData(sampleDeploymentPlanData);
66+
currentDeploymentPlanViewController.updateDeploymentPlanData(planData);
6867
currentDeploymentPlanViewController.revealToForeground(vscode.ViewColumn.Active);
6968
return;
7069
}
7170

72-
currentDeploymentPlanViewController = new DeploymentPlanViewController(sampleDeploymentPlanData);
71+
currentDeploymentPlanViewController = new DeploymentPlanViewController(planData);
7372
currentDeploymentPlanViewController.revealToForeground(vscode.ViewColumn.Active);
7473

7574
currentDeploymentPlanViewController.panel.onDidDispose(() => {

src/commands/registerCommands.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { deployWithCopilot } from './copilot/deployWithCopilot';
1212
import { createProjectWithCopilot } from './copilot/createProjectWithCopilot';
1313
import { openPlanView, openPlanViewFromWorkspace } from './copilot/openPlanView';
1414
import { openLocalPlanView, openLocalPlanViewFromWorkspace } from './copilot/openLocalPlanView';
15+
import { openDeploymentPlanView, openDeploymentPlanViewFromWorkspace } from './copilot/openDeploymentPlanView';
1516
import { createContainerApp } from './createContainerApp/createContainerApp';
1617
import { createManagedEnvironment } from './createManagedEnvironment/createManagedEnvironment';
1718
import { deleteContainerApp } from './deleteContainerApp/deleteContainerApp';
@@ -151,9 +152,14 @@ export function registerCommands(): void {
151152
registerCommand('containerApps.openPlanViewFromWorkspace', () => openPlanViewFromWorkspace());
152153
registerCommand('containerApps.openLocalPlanView', (_context: IActionContext, uri: vscode.Uri) => openLocalPlanView(uri));
153154
registerCommand('containerApps.openLocalPlanViewFromWorkspace', () => openLocalPlanViewFromWorkspace());
154-
registerCommand('containerApps.showProjectCreation', async () => {
155-
await vscode.commands.executeCommand('azureProjectCreation.show');
155+
registerCommand('azureProjectCreation.openDeployPlan', (_context: IActionContext, uri?: vscode.Uri) => {
156+
if (uri) {
157+
openDeploymentPlanView(uri);
158+
} else {
159+
void openDeploymentPlanViewFromWorkspace();
160+
}
156161
});
162+
registerCommand('containerApps.showProjectCreation', () => openDeploymentPlanViewFromWorkspace());
157163
registerCommand('containerApps.startLocalDev', async () => {
158164
await vscode.commands.executeCommand('workbench.action.chat.open', {
159165
mode: 'agent',

src/extension.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import * as path from 'path';
1313
import { v4 as uuid } from 'uuid';
1414
import * as vscode from 'vscode';
1515
import { createContainerAppsApiProvider } from './commands/api/createContainerAppsApiProvider';
16+
import { openDeploymentPlanView, refreshDeploymentPlanView } from './commands/copilot/openDeploymentPlanView';
1617
import { openLocalPlanView, refreshLocalPlanView } from './commands/copilot/openLocalPlanView';
1718
import { openPlanView, refreshPlanView } from './commands/copilot/openPlanView';
1819
import { registerCommands } from './commands/registerCommands';
@@ -92,6 +93,26 @@ export async function activate(context: vscode.ExtensionContext, perfStats: { lo
9293
});
9394
context.subscriptions.push(localPlanWatcher);
9495

96+
// Watch for .azure/plan.md creation to open the deployment plan view
97+
const deploymentPlanWatcher = vscode.workspace.createFileSystemWatcher('**/.azure/plan.md');
98+
let deploymentPlanViewDebounceTimer: ReturnType<typeof setTimeout> | undefined;
99+
const debounceDeploymentPlanViewOpen = (uri: vscode.Uri) => {
100+
if (deploymentPlanViewDebounceTimer) { clearTimeout(deploymentPlanViewDebounceTimer); }
101+
deploymentPlanViewDebounceTimer = setTimeout(() => {
102+
deploymentPlanViewDebounceTimer = undefined;
103+
openDeploymentPlanView(uri);
104+
}, 2000);
105+
};
106+
deploymentPlanWatcher.onDidCreate((uri) => debounceDeploymentPlanViewOpen(uri));
107+
deploymentPlanWatcher.onDidChange((uri) => {
108+
if (deploymentPlanViewDebounceTimer) {
109+
debounceDeploymentPlanViewOpen(uri);
110+
} else {
111+
refreshDeploymentPlanView(uri);
112+
}
113+
});
114+
context.subscriptions.push(deploymentPlanWatcher);
115+
95116
// Fallback: detect plan files via workspace file creation events
96117
// (fires when files are created via VS Code API, e.g. WorkspaceEdit / Copilot tools)
97118
context.subscriptions.push(
@@ -102,11 +123,27 @@ export async function activate(context: vscode.ExtensionContext, perfStats: { lo
102123
debouncePlanViewOpen(file);
103124
} else if (filename === 'local-dev.plan.md') {
104125
debounceLocalPlanViewOpen(file);
126+
} else if (filename === 'plan.md' && file.path.includes('.azure/')) {
127+
debounceDeploymentPlanViewOpen(file);
105128
}
106129
}
107130
}),
108131
);
109132

133+
// Fallback: detect .azure/plan.md creation and changes for dot-directory watcher reliability
134+
context.subscriptions.push(
135+
vscode.workspace.onDidSaveTextDocument((doc) => {
136+
if (doc.fileName.replace(/\\/g, '/').includes('.azure/plan.md')) {
137+
refreshDeploymentPlanView(doc.uri);
138+
}
139+
}),
140+
vscode.workspace.onDidOpenTextDocument((doc) => {
141+
if (doc.fileName.replace(/\\/g, '/').includes('.azure/plan.md')) {
142+
debounceDeploymentPlanViewOpen(doc.uri);
143+
}
144+
}),
145+
);
146+
110147
// Register MCP server for scaffold-complete UI in chat
111148
context.subscriptions.push(
112149
vscode.lm.registerMcpServerDefinitionProvider('containerAppsMcp', {

0 commit comments

Comments
 (0)