swallowkit 2.0.0-alpha.2 → 2.0.0-alpha.4
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/dist/cli/commands/configure-mcp.d.ts +12 -0
- package/dist/cli/commands/configure-mcp.d.ts.map +1 -0
- package/dist/cli/commands/configure-mcp.js +42 -0
- package/dist/cli/commands/configure-mcp.js.map +1 -0
- package/dist/cli/commands/dev.d.ts +2 -2
- package/dist/cli/commands/dev.d.ts.map +1 -1
- package/dist/cli/commands/dev.js +39 -27
- package/dist/cli/commands/dev.js.map +1 -1
- package/dist/cli/commands/init.d.ts +6 -2
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +43 -122
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/migrations.d.ts.map +1 -1
- package/dist/cli/commands/migrations.js +25 -0
- package/dist/cli/commands/migrations.js.map +1 -1
- package/dist/cli/commands/provision.d.ts +2 -0
- package/dist/cli/commands/provision.d.ts.map +1 -1
- package/dist/cli/commands/provision.js +45 -16
- package/dist/cli/commands/provision.js.map +1 -1
- package/dist/cli/commands/scaffold.d.ts +4 -0
- package/dist/cli/commands/scaffold.d.ts.map +1 -1
- package/dist/cli/commands/scaffold.js +40 -2
- package/dist/cli/commands/scaffold.js.map +1 -1
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +27 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/core/operations/model-migration.d.ts +22 -0
- package/dist/core/operations/model-migration.d.ts.map +1 -0
- package/dist/core/operations/model-migration.js +36 -0
- package/dist/core/operations/model-migration.js.map +1 -0
- package/dist/core/operations/provision-operations.d.ts +19 -1
- package/dist/core/operations/provision-operations.d.ts.map +1 -1
- package/dist/core/operations/provision-operations.js +62 -35
- package/dist/core/operations/provision-operations.js.map +1 -1
- package/dist/core/operations/scaffold-apply.d.ts +1 -0
- package/dist/core/operations/scaffold-apply.d.ts.map +1 -1
- package/dist/core/operations/scaffold-apply.js +1 -0
- package/dist/core/operations/scaffold-apply.js.map +1 -1
- package/dist/core/operations/scaffold-machine.d.ts +1 -0
- package/dist/core/operations/scaffold-machine.d.ts.map +1 -1
- package/dist/core/operations/scaffold-machine.js.map +1 -1
- package/dist/core/operations/scaffold-plan.d.ts +2 -0
- package/dist/core/operations/scaffold-plan.d.ts.map +1 -1
- package/dist/core/operations/scaffold-plan.js +3 -1
- package/dist/core/operations/scaffold-plan.js.map +1 -1
- package/dist/core/project/agent-instructions.d.ts.map +1 -1
- package/dist/core/project/agent-instructions.js +4 -1
- package/dist/core/project/agent-instructions.js.map +1 -1
- package/dist/core/project/boundaries.d.ts +5 -2
- package/dist/core/project/boundaries.d.ts.map +1 -1
- package/dist/core/project/boundaries.js +49 -18
- package/dist/core/project/boundaries.js.map +1 -1
- package/dist/core/project/capabilities.d.ts +1 -0
- package/dist/core/project/capabilities.d.ts.map +1 -1
- package/dist/core/project/capabilities.js +20 -2
- package/dist/core/project/capabilities.js.map +1 -1
- package/dist/core/project/home-page.d.ts +4 -0
- package/dist/core/project/home-page.d.ts.map +1 -0
- package/dist/core/project/home-page.js +113 -0
- package/dist/core/project/home-page.js.map +1 -0
- package/dist/core/project/mcp-config.d.ts +26 -0
- package/dist/core/project/mcp-config.d.ts.map +1 -0
- package/dist/core/project/mcp-config.js +117 -0
- package/dist/core/project/mcp-config.js.map +1 -0
- package/dist/core/project/react-project.d.ts +1 -1
- package/dist/core/project/react-project.d.ts.map +1 -1
- package/dist/core/project/react-project.js +45 -24
- package/dist/core/project/react-project.js.map +1 -1
- package/dist/core/project/workflows.d.ts.map +1 -1
- package/dist/core/project/workflows.js +9 -1
- package/dist/core/project/workflows.js.map +1 -1
- package/dist/core/scaffold/auth-generator.js +8 -8
- package/dist/core/scaffold/model-parser.d.ts +23 -0
- package/dist/core/scaffold/model-parser.d.ts.map +1 -1
- package/dist/core/scaffold/model-parser.js +407 -137
- package/dist/core/scaffold/model-parser.js.map +1 -1
- package/dist/core/scaffold/native-schema-generator.d.ts +11 -0
- package/dist/core/scaffold/native-schema-generator.d.ts.map +1 -1
- package/dist/core/scaffold/native-schema-generator.js +279 -91
- package/dist/core/scaffold/native-schema-generator.js.map +1 -1
- package/dist/core/scaffold/openapi-generator.d.ts +5 -0
- package/dist/core/scaffold/openapi-generator.d.ts.map +1 -1
- package/dist/core/scaffold/openapi-generator.js +53 -3
- package/dist/core/scaffold/openapi-generator.js.map +1 -1
- package/dist/core/scaffold/ui-generator.d.ts +0 -3
- package/dist/core/scaffold/ui-generator.d.ts.map +1 -1
- package/dist/core/scaffold/ui-generator.js +68 -7
- package/dist/core/scaffold/ui-generator.js.map +1 -1
- package/dist/core/verify/index.d.ts.map +1 -1
- package/dist/core/verify/index.js +24 -1
- package/dist/core/verify/index.js.map +1 -1
- package/dist/machine/index.d.ts.map +1 -1
- package/dist/machine/index.js +39 -15
- package/dist/machine/index.js.map +1 -1
- package/dist/mcp/index.d.ts +3 -2
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +137 -13
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/version-selection.d.ts +55 -0
- package/dist/mcp/version-selection.d.ts.map +1 -0
- package/dist/mcp/version-selection.js +152 -0
- package/dist/mcp/version-selection.js.map +1 -0
- package/dist/utils/azure-cli.d.ts +44 -0
- package/dist/utils/azure-cli.d.ts.map +1 -0
- package/dist/utils/azure-cli.js +164 -0
- package/dist/utils/azure-cli.js.map +1 -0
- package/dist/utils/package-manager.js +1 -1
- package/dist/utils/package-manager.js.map +1 -1
- package/dist/utils/python-uv.d.ts +5 -7
- package/dist/utils/python-uv.d.ts.map +1 -1
- package/dist/utils/python-uv.js +50 -24
- package/dist/utils/python-uv.js.map +1 -1
- package/dist/utils/react-dev.d.ts +4 -0
- package/dist/utils/react-dev.d.ts.map +1 -0
- package/dist/utils/react-dev.js +35 -0
- package/dist/utils/react-dev.js.map +1 -0
- package/dist/utils/semver.d.ts +14 -0
- package/dist/utils/semver.d.ts.map +1 -0
- package/dist/utils/semver.js +61 -0
- package/dist/utils/semver.js.map +1 -0
- package/package.json +1 -1
- package/src/__tests__/__snapshots__/boundaries.test.ts.snap +154 -0
- package/src/__tests__/agent-loop-phase34.test.ts +29 -0
- package/src/__tests__/auth.test.ts +9 -0
- package/src/__tests__/azure-cli.test.ts +199 -0
- package/src/__tests__/boundaries.test.ts +42 -0
- package/src/__tests__/csharp-schema.test.ts +261 -0
- package/src/__tests__/dev.test.ts +13 -6
- package/src/__tests__/home-page.test.ts +50 -0
- package/src/__tests__/init.test.ts +57 -0
- package/src/__tests__/mcp-version.test.ts +262 -0
- package/src/__tests__/mcp.test.ts +49 -3
- package/src/__tests__/package-manager.test.ts +2 -2
- package/src/__tests__/plan-apply.test.ts +68 -0
- package/src/__tests__/python-uv.test.ts +50 -14
- package/src/__tests__/python-venv.test.ts +48 -0
- package/src/__tests__/react-dev.test.ts +72 -0
- package/src/__tests__/react.test.ts +19 -0
- package/src/__tests__/schema-analysis.test.ts +329 -0
- package/src/cli/commands/configure-mcp.ts +54 -0
- package/src/cli/commands/dev.ts +40 -31
- package/src/cli/commands/init.ts +48 -125
- package/src/cli/commands/migrations.ts +22 -0
- package/src/cli/commands/provision.ts +53 -17
- package/src/cli/commands/scaffold.ts +40 -2
- package/src/cli/index.ts +26 -3
- package/src/core/operations/model-migration.ts +52 -0
- package/src/core/operations/provision-operations.ts +86 -42
- package/src/core/operations/scaffold-apply.ts +2 -0
- package/src/core/operations/scaffold-machine.ts +1 -0
- package/src/core/operations/scaffold-plan.ts +5 -1
- package/src/core/project/agent-instructions.ts +4 -1
- package/src/core/project/agent-templates/AGENTS.md +39 -3
- package/src/core/project/agent-templates/CLAUDE.md +3 -1
- package/src/core/project/agent-templates/copilot-instructions.md +3 -1
- package/src/core/project/boundaries.ts +60 -18
- package/src/core/project/capabilities.ts +22 -2
- package/src/core/project/home-page.ts +114 -0
- package/src/core/project/mcp-config.ts +84 -0
- package/src/core/project/react-project.ts +49 -27
- package/src/core/project/workflows.ts +9 -1
- package/src/core/scaffold/auth-generator.ts +8 -8
- package/src/core/scaffold/model-parser.ts +413 -129
- package/src/core/scaffold/native-schema-generator.ts +305 -105
- package/src/core/scaffold/openapi-generator.ts +63 -5
- package/src/core/scaffold/ui-generator.ts +70 -8
- package/src/core/verify/index.ts +23 -1
- package/src/machine/index.ts +42 -18
- package/src/mcp/index.ts +156 -14
- package/src/mcp/version-selection.ts +163 -0
- package/src/utils/azure-cli.ts +154 -0
- package/src/utils/package-manager.ts +1 -1
- package/src/utils/python-uv.ts +53 -33
- package/src/utils/react-dev.ts +31 -0
- package/src/utils/semver.ts +59 -0
package/src/cli/commands/dev.ts
CHANGED
|
@@ -19,15 +19,15 @@ import {
|
|
|
19
19
|
import { BackendLanguage } from '../../types';
|
|
20
20
|
import { detectFromProject, getCommands } from '../../utils/package-manager';
|
|
21
21
|
import {
|
|
22
|
-
buildProjectLocalUvEnv,
|
|
23
22
|
buildProjectLocalUvInstallerEnv,
|
|
24
23
|
buildUvPipInstallArgs,
|
|
25
|
-
|
|
24
|
+
ensureUvVirtualEnvironment,
|
|
26
25
|
getProjectLocalUvInstallerCommand,
|
|
27
26
|
getProjectLocalUvPaths,
|
|
28
27
|
getPythonProjectRoot,
|
|
29
28
|
} from '../../utils/python-uv';
|
|
30
29
|
import { ConnectorMockServer } from '../../core/mock/connector-mock-server';
|
|
30
|
+
import { buildReactDevConfigSource } from '../../utils/react-dev';
|
|
31
31
|
|
|
32
32
|
export interface DevOptions {
|
|
33
33
|
port?: string;
|
|
@@ -65,8 +65,8 @@ function normalizeParsedDevOptions(options: ParsedDevActionOptions): DevOptions
|
|
|
65
65
|
};
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
export function buildFunctionsStartArgs(functionsPort: string): string[] {
|
|
69
|
-
return ['start', '--port', functionsPort];
|
|
68
|
+
export function buildFunctionsStartArgs(functionsPort: string, backendLanguage: BackendLanguage = 'typescript'): string[] {
|
|
69
|
+
return [...(backendLanguage === 'csharp' ? [] : ['start']), '--port', functionsPort];
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
export function parseCoreToolsVersion(output: string): string | null {
|
|
@@ -105,15 +105,15 @@ export function buildFunctionsCoreToolsCommand(
|
|
|
105
105
|
|
|
106
106
|
return {
|
|
107
107
|
command: 'npm',
|
|
108
|
-
argsPrefix: ['exec', '--yes', NPM_CORE_TOOLS_PACKAGE, '--'],
|
|
109
|
-
label: `npm
|
|
108
|
+
argsPrefix: ['exec', '--yes', '--package', NPM_CORE_TOOLS_PACKAGE, '--', 'dotnet', 'run', '--no-launch-profile', '--'],
|
|
109
|
+
label: `dotnet run with npm ${NPM_CORE_TOOLS_PACKAGE} (${reason})`,
|
|
110
110
|
};
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
return {
|
|
114
|
-
command: 'func',
|
|
115
|
-
argsPrefix: [],
|
|
116
|
-
label: installedVersion ? `func ${installedVersion}` : 'func',
|
|
114
|
+
command: backendLanguage === 'csharp' ? 'dotnet' : 'func',
|
|
115
|
+
argsPrefix: backendLanguage === 'csharp' ? ['run', '--no-launch-profile', '--'] : [],
|
|
116
|
+
label: backendLanguage === 'csharp' ? 'dotnet run' : installedVersion ? `func ${installedVersion}` : 'func',
|
|
117
117
|
};
|
|
118
118
|
}
|
|
119
119
|
|
|
@@ -138,8 +138,8 @@ export function buildSwaStartArgs(host: string | undefined, nextPort: string, sw
|
|
|
138
138
|
];
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
-
export function buildReactDevArgs(pm: string, port: string, host = 'localhost'): string[] {
|
|
142
|
-
const args = ['vite', '--port', port, '--host', host, '--strictPort'];
|
|
141
|
+
export function buildReactDevArgs(pm: string, port: string, host = 'localhost', configPath?: string): string[] {
|
|
142
|
+
const args = ['vite', '--port', port, '--host', host, '--strictPort', ...(configPath ? ['--config', configPath] : [])];
|
|
143
143
|
return pm === 'pnpm' ? ['exec', ...args] : args;
|
|
144
144
|
}
|
|
145
145
|
|
|
@@ -270,7 +270,7 @@ async function checkCommand(
|
|
|
270
270
|
}
|
|
271
271
|
|
|
272
272
|
async function resolveProjectLocalUvCommand(projectRoot: string): Promise<{ command: string; env: NodeJS.ProcessEnv }> {
|
|
273
|
-
const uvEnv =
|
|
273
|
+
const uvEnv = { ...process.env };
|
|
274
274
|
const { localUvExecutable } = getProjectLocalUvPaths(projectRoot);
|
|
275
275
|
|
|
276
276
|
if (await checkCommand('uv', ['--version'], { shell: false })) {
|
|
@@ -471,22 +471,8 @@ async function bridgePythonCoreToolsForWindowsArm64(
|
|
|
471
471
|
async function preparePythonFunctionsEnvironment(functionsDir: string): Promise<NodeJS.ProcessEnv> {
|
|
472
472
|
const projectRoot = getPythonProjectRoot(functionsDir);
|
|
473
473
|
const { command: uvCommand, env: uvEnv } = await resolveProjectLocalUvCommand(projectRoot);
|
|
474
|
-
const { venvDir
|
|
475
|
-
const
|
|
476
|
-
cwd: functionsDir,
|
|
477
|
-
env: uvEnv,
|
|
478
|
-
shell: false,
|
|
479
|
-
});
|
|
480
|
-
|
|
481
|
-
if (!hasUsableVirtualEnv) {
|
|
482
|
-
const venvArgs = buildUvVenvArgs('.venv');
|
|
483
|
-
if (fs.existsSync(venvDir)) {
|
|
484
|
-
venvArgs.push('--clear');
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
console.log('📦 Creating Python virtual environment with uv...');
|
|
488
|
-
await runCommand(uvCommand, venvArgs, functionsDir, 'python virtual environment setup', uvEnv, false);
|
|
489
|
-
}
|
|
474
|
+
const { venvDir } = getPythonVirtualEnvPaths(functionsDir);
|
|
475
|
+
const pythonExecutable = await ensureUvVirtualEnvironment(uvCommand, venvDir, functionsDir, uvEnv);
|
|
490
476
|
|
|
491
477
|
console.log('📦 Installing Python Azure Functions dependencies with uv...');
|
|
492
478
|
await runCommand(
|
|
@@ -538,7 +524,7 @@ export async function assertDevPortAvailable(port: number, host: string): Promis
|
|
|
538
524
|
}
|
|
539
525
|
|
|
540
526
|
function stopDevProcesses(processes: ChildProcess[]): void {
|
|
541
|
-
const active = processes.filter(child => child.pid && !child.killed);
|
|
527
|
+
const active = processes.filter(child => child.pid && !child.killed && child.exitCode === null && child.signalCode === null);
|
|
542
528
|
if (process.platform === 'win32' && active.length) {
|
|
543
529
|
// Windows shell termination alone leaves Vite, Core Tools, and SWA running.
|
|
544
530
|
const ids = active.map(child => child.pid).join(',');
|
|
@@ -721,6 +707,14 @@ async function startDevEnvironment(options: DevOptions) {
|
|
|
721
707
|
const functionsBaseUrl = buildFunctionsBaseUrl(options.host, functionsPort);
|
|
722
708
|
let functionsReadinessPromise: Promise<boolean> | null = null;
|
|
723
709
|
let functionsReady = !!options.noFunctions;
|
|
710
|
+
let reactDevConfigDir: string | undefined;
|
|
711
|
+
const cleanupReactDevConfig = () => {
|
|
712
|
+
if (reactDevConfigDir) {
|
|
713
|
+
fs.rmSync(reactDevConfigDir, { recursive: true, force: true });
|
|
714
|
+
reactDevConfigDir = undefined;
|
|
715
|
+
}
|
|
716
|
+
};
|
|
717
|
+
process.once('exit', cleanupReactDevConfig);
|
|
724
718
|
|
|
725
719
|
// Cleanup processes on Ctrl+C
|
|
726
720
|
process.on('SIGINT', async () => {
|
|
@@ -939,7 +933,7 @@ async function startDevEnvironment(options: DevOptions) {
|
|
|
939
933
|
|
|
940
934
|
// Azure Functions を起動
|
|
941
935
|
const functionsCommand = functionsCoreToolsCommand ?? buildFunctionsCoreToolsCommand(backendLanguage, installedCoreToolsVersion);
|
|
942
|
-
const funcProcess = spawn(functionsCommand.command, [...functionsCommand.argsPrefix, ...buildFunctionsStartArgs(functionsPort)], {
|
|
936
|
+
const funcProcess = spawn(functionsCommand.command, [...functionsCommand.argsPrefix, ...buildFunctionsStartArgs(functionsPort, backendLanguage)], {
|
|
943
937
|
cwd: functionsDir,
|
|
944
938
|
shell: true,
|
|
945
939
|
stdio: 'pipe', // Always pipe to capture output
|
|
@@ -987,6 +981,8 @@ async function startDevEnvironment(options: DevOptions) {
|
|
|
987
981
|
console.error('⚠️ Azure Functions startup error:', error.message);
|
|
988
982
|
console.log('💡 Please ensure Azure Functions Core Tools is installed');
|
|
989
983
|
console.log(` ${pmCmd.addGlobal} azure-functions-core-tools@4`);
|
|
984
|
+
stopDevProcesses(processes);
|
|
985
|
+
process.exit(1);
|
|
990
986
|
});
|
|
991
987
|
|
|
992
988
|
funcProcess.on('close', (code) => {
|
|
@@ -997,6 +993,8 @@ async function startDevEnvironment(options: DevOptions) {
|
|
|
997
993
|
console.log(' Reinstall a matching Core Tools v4 package (Windows users should prefer the official x64/x86 MSI for their machine).');
|
|
998
994
|
console.log(' SwallowKit local Python dev uses functions/.venv and requirements.txt, but Core Tools still needs its own bundled Python worker.');
|
|
999
995
|
}
|
|
996
|
+
stopDevProcesses(processes);
|
|
997
|
+
process.exit(code || 1);
|
|
1000
998
|
}
|
|
1001
999
|
});
|
|
1002
1000
|
|
|
@@ -1083,7 +1081,17 @@ async function startDevEnvironment(options: DevOptions) {
|
|
|
1083
1081
|
}
|
|
1084
1082
|
|
|
1085
1083
|
// 5. Start Next.js development server
|
|
1086
|
-
|
|
1084
|
+
let reactDevConfigPath: string | undefined;
|
|
1085
|
+
if (react) {
|
|
1086
|
+
const stateDir = path.join(process.cwd(), '.swallowkit');
|
|
1087
|
+
fs.mkdirSync(stateDir, { recursive: true });
|
|
1088
|
+
reactDevConfigDir = fs.mkdtempSync(path.join(stateDir, 'vite-dev-'));
|
|
1089
|
+
reactDevConfigPath = path.join(reactDevConfigDir, 'config.mjs');
|
|
1090
|
+
fs.writeFileSync(reactDevConfigPath, buildReactDevConfigSource());
|
|
1091
|
+
}
|
|
1092
|
+
const nextArgs = react
|
|
1093
|
+
? buildReactDevArgs(pm, port, options.host, reactDevConfigPath && path.relative(process.cwd(), reactDevConfigPath))
|
|
1094
|
+
: buildNextDevArgs(pm, port);
|
|
1087
1095
|
|
|
1088
1096
|
// Ensure .env.local points to bffTargetPort so Next.js reads the correct backend URL.
|
|
1089
1097
|
// When --mock-connectors is active, bffTargetPort = mock port (7072); otherwise = Functions port (7071).
|
|
@@ -1141,6 +1149,7 @@ async function startDevEnvironment(options: DevOptions) {
|
|
|
1141
1149
|
|
|
1142
1150
|
nextProcess.on('error', (error) => {
|
|
1143
1151
|
console.error(`❌ ${frontendLabel} startup error:`, error.message);
|
|
1152
|
+
stopDevProcesses(processes);
|
|
1144
1153
|
process.exit(1);
|
|
1145
1154
|
});
|
|
1146
1155
|
|
package/src/cli/commands/init.ts
CHANGED
|
@@ -25,7 +25,9 @@ import {
|
|
|
25
25
|
buildCSharpCodegenToolManifestSource,
|
|
26
26
|
buildPythonCodegenRequirementsSource,
|
|
27
27
|
} from "../../core/scaffold/native-schema-generator";
|
|
28
|
-
import {
|
|
28
|
+
import { DEFAULT_AZURE_PYTHON_VERSION } from "../../utils/python-uv";
|
|
29
|
+
import { buildHomePage } from "../../core/project/home-page";
|
|
30
|
+
import { MCP_LATEST, McpPinDecision, lookupPublishedLatestVersion, resolveMcpPinVersion } from "../../core/project/mcp-config";
|
|
29
31
|
|
|
30
32
|
interface InitOptions {
|
|
31
33
|
name: string;
|
|
@@ -144,7 +146,7 @@ function getFunctionsRuntimeConfig(backendLanguage: BackendLanguage): { name: st
|
|
|
144
146
|
return { name: "dotnet-isolated", version: "10.0" };
|
|
145
147
|
}
|
|
146
148
|
if (backendLanguage === "python") {
|
|
147
|
-
return { name: "python", version:
|
|
149
|
+
return { name: "python", version: DEFAULT_AZURE_PYTHON_VERSION };
|
|
148
150
|
}
|
|
149
151
|
return { name: "node", version: "22" };
|
|
150
152
|
}
|
|
@@ -344,7 +346,7 @@ export async function initCommand(options: InitOptions) {
|
|
|
344
346
|
const azureConfig = await promptAzureConfig(options);
|
|
345
347
|
|
|
346
348
|
if (frontend === "react") {
|
|
347
|
-
createReactProject(projectDir, options.name);
|
|
349
|
+
createReactProject(projectDir, options.name, pm);
|
|
348
350
|
} else {
|
|
349
351
|
await createNextJsProject(options.name, pm);
|
|
350
352
|
await upgradeNextJs(projectDir, options.nextVersion || 'latest', pm);
|
|
@@ -711,7 +713,7 @@ export function buildCSharpFunctionsProjectSource(): string {
|
|
|
711
713
|
<None Remove="generated\\**\\bin\\**;generated\\**\\obj\\**" />
|
|
712
714
|
</ItemGroup>
|
|
713
715
|
<ItemGroup>
|
|
714
|
-
<PackageReference Include="Microsoft.Azure.Cosmos" Version="3.
|
|
716
|
+
<PackageReference Include="Microsoft.Azure.Cosmos" Version="3.63.2" />
|
|
715
717
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
|
716
718
|
<PackageReference Include="Azure.Identity" Version="1.17.0" />
|
|
717
719
|
<PackageReference Include="Microsoft.Azure.Functions.Worker" Version="2.52.0" />
|
|
@@ -778,14 +780,30 @@ function getSwallowKitPackageMetadata(): { name: string; version: string } {
|
|
|
778
780
|
};
|
|
779
781
|
}
|
|
780
782
|
|
|
781
|
-
export function buildGeneratedProjectDevDependencies(): Record<string, string> {
|
|
783
|
+
export function buildGeneratedProjectDevDependencies(frontend: FrontendFramework = "nextjs"): Record<string, string> {
|
|
782
784
|
const { name, version } = getSwallowKitPackageMetadata();
|
|
783
785
|
return {
|
|
784
786
|
[name]: version,
|
|
787
|
+
...(frontend === "nextjs" ? { "@tailwindcss/postcss": "^4.0.0" } : {}),
|
|
785
788
|
};
|
|
786
789
|
}
|
|
787
790
|
|
|
788
|
-
export function
|
|
791
|
+
export function ensureNextTailwindPostcssConfig(projectDir: string): void {
|
|
792
|
+
if (["postcss.config.mjs", "postcss.config.js", "postcss.config.cjs", "postcss.config.json"]
|
|
793
|
+
.some(file => fs.existsSync(path.join(projectDir, file)))) {
|
|
794
|
+
return;
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
// New create-next-app templates can configure Tailwind for Turbopack only.
|
|
798
|
+
fs.writeFileSync(path.join(projectDir, "postcss.config.mjs"), `export default {
|
|
799
|
+
plugins: {
|
|
800
|
+
"@tailwindcss/postcss": {},
|
|
801
|
+
},
|
|
802
|
+
};
|
|
803
|
+
`);
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
export function buildSwallowKitMcpProjectConfigSource(pm: PackageManager = 'pnpm', mcpVersion: string = MCP_LATEST): string {
|
|
789
807
|
const { name } = getSwallowKitPackageMetadata();
|
|
790
808
|
return JSON.stringify(
|
|
791
809
|
{
|
|
@@ -797,7 +815,7 @@ export function buildSwallowKitMcpProjectConfigSource(pm: PackageManager = 'pnpm
|
|
|
797
815
|
: ["--yes", "--package", `${name}@\${SWALLOWKIT_MCP_VERSION}`, "swallowkit-mcp"],
|
|
798
816
|
cwd: ".",
|
|
799
817
|
env: {
|
|
800
|
-
SWALLOWKIT_MCP_VERSION:
|
|
818
|
+
SWALLOWKIT_MCP_VERSION: mcpVersion,
|
|
801
819
|
},
|
|
802
820
|
},
|
|
803
821
|
},
|
|
@@ -807,6 +825,15 @@ export function buildSwallowKitMcpProjectConfigSource(pm: PackageManager = 'pnpm
|
|
|
807
825
|
);
|
|
808
826
|
}
|
|
809
827
|
|
|
828
|
+
/** latest by default; pinned to this CLI version only when it is newer than published latest. */
|
|
829
|
+
export function selectInitialMcpVersion(
|
|
830
|
+
lookupLatest: (packageName: string) => string | null = lookupPublishedLatestVersion
|
|
831
|
+
): McpPinDecision {
|
|
832
|
+
const { name, version } = getSwallowKitPackageMetadata();
|
|
833
|
+
if (version === MCP_LATEST) return { version: MCP_LATEST, reason: "SwallowKit package metadata is unavailable." };
|
|
834
|
+
return resolveMcpPinVersion(version, lookupLatest(name));
|
|
835
|
+
}
|
|
836
|
+
|
|
810
837
|
async function addSwallowKitFiles(
|
|
811
838
|
projectDir: string,
|
|
812
839
|
options: InitOptions,
|
|
@@ -831,7 +858,7 @@ async function addSwallowKitFiles(
|
|
|
831
858
|
};
|
|
832
859
|
packageJson.devDependencies = {
|
|
833
860
|
...packageJson.devDependencies,
|
|
834
|
-
...buildGeneratedProjectDevDependencies(),
|
|
861
|
+
...buildGeneratedProjectDevDependencies(frontend),
|
|
835
862
|
};
|
|
836
863
|
|
|
837
864
|
packageJson.scripts = {
|
|
@@ -865,6 +892,8 @@ async function addSwallowKitFiles(
|
|
|
865
892
|
|
|
866
893
|
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
|
|
867
894
|
|
|
895
|
+
if (frontend === "nextjs") ensureNextTailwindPostcssConfig(projectDir);
|
|
896
|
+
|
|
868
897
|
// Create pnpm-workspace.yaml for local pnpm development when needed.
|
|
869
898
|
if (wsConfig.type === 'file') {
|
|
870
899
|
fs.writeFileSync(path.join(projectDir, wsConfig.filename), wsConfig.content + (frontend === "react" ? " esbuild: true\n" : ""));
|
|
@@ -1492,7 +1521,6 @@ public sealed class GreetFunction
|
|
|
1492
1521
|
}
|
|
1493
1522
|
|
|
1494
1523
|
function createPythonFunctionsProject(projectDir: string, functionsDir: string): void {
|
|
1495
|
-
fs.writeFileSync(path.join(projectDir, '.python-version'), '3.11\n');
|
|
1496
1524
|
ensureProjectGitignoreEntry(projectDir, '.uv');
|
|
1497
1525
|
|
|
1498
1526
|
fs.writeFileSync(path.join(functionsDir, 'requirements.txt'), `azure-functions>=1.20.0
|
|
@@ -1632,117 +1660,8 @@ export async function POST(request: NextRequest) {
|
|
|
1632
1660
|
|
|
1633
1661
|
async function createHomePage(projectDir: string, pm: PackageManager = 'pnpm') {
|
|
1634
1662
|
console.log('📦 Creating home page...\n');
|
|
1635
|
-
|
|
1636
|
-
const pmCmd = getCommands(pm);
|
|
1637
|
-
|
|
1638
|
-
const pageContent = `'use client'
|
|
1639
1663
|
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
import { useState } from 'react';
|
|
1643
|
-
import { scaffoldConfig } from '@/lib/scaffold-config';
|
|
1644
|
-
|
|
1645
|
-
export default function Home() {
|
|
1646
|
-
const [greetingStatus, setGreetingStatus] = useState<'idle' | 'loading' | 'success' | 'error'>('idle');
|
|
1647
|
-
const [message, setMessage] = useState('');
|
|
1648
|
-
|
|
1649
|
-
const testConnection = async () => {
|
|
1650
|
-
setGreetingStatus('loading');
|
|
1651
|
-
try {
|
|
1652
|
-
const response = await fetch('/api/greet?name=SwallowKit');
|
|
1653
|
-
const data = await response.json();
|
|
1654
|
-
if (!response.ok) {
|
|
1655
|
-
throw new Error(data.error || \`Server error: \${response.status}\`);
|
|
1656
|
-
}
|
|
1657
|
-
setMessage(data.message);
|
|
1658
|
-
setGreetingStatus('success');
|
|
1659
|
-
} catch (error) {
|
|
1660
|
-
setMessage(error instanceof Error ? error.message : 'Failed to connect to Azure Functions');
|
|
1661
|
-
setGreetingStatus('error');
|
|
1662
|
-
}
|
|
1663
|
-
};
|
|
1664
|
-
|
|
1665
|
-
return (
|
|
1666
|
-
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-50 dark:from-gray-900 dark:to-gray-800">
|
|
1667
|
-
<div className="container mx-auto px-4 py-12">
|
|
1668
|
-
<header className="text-center mb-16">
|
|
1669
|
-
<h1 className="text-5xl font-bold text-gray-800 dark:text-white mb-4">
|
|
1670
|
-
Welcome to SwallowKit
|
|
1671
|
-
</h1>
|
|
1672
|
-
<p className="text-xl text-gray-600 dark:text-gray-400">
|
|
1673
|
-
Next.js on Azure Static Web Apps + Functions + Cosmos DB — Zod schema sharing
|
|
1674
|
-
</p>
|
|
1675
|
-
</header>
|
|
1676
|
-
|
|
1677
|
-
{/* Connection Test */}
|
|
1678
|
-
<section className="max-w-2xl mx-auto mb-12">
|
|
1679
|
-
<div className="bg-white dark:bg-gray-800 rounded-xl p-8 border border-gray-200 dark:border-gray-700">
|
|
1680
|
-
<h2 className="text-2xl font-semibold mb-4 text-gray-900 dark:text-gray-100">
|
|
1681
|
-
Test BFF → Functions Connection
|
|
1682
|
-
</h2>
|
|
1683
|
-
<button
|
|
1684
|
-
onClick={testConnection}
|
|
1685
|
-
disabled={greetingStatus === 'loading'}
|
|
1686
|
-
className="px-6 py-3 bg-blue-600 hover:bg-blue-700 disabled:bg-gray-400 text-white rounded-lg font-medium transition-colors"
|
|
1687
|
-
>
|
|
1688
|
-
{greetingStatus === 'loading' ? 'Testing...' : 'Test Connection'}
|
|
1689
|
-
</button>
|
|
1690
|
-
{greetingStatus === 'success' && (
|
|
1691
|
-
<div className="mt-4 p-4 bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-800 rounded-lg">
|
|
1692
|
-
<p className="text-green-800 dark:text-green-200 font-medium">✅ Connection successful!</p>
|
|
1693
|
-
<p className="text-green-700 dark:text-green-300 text-sm mt-1">{message}</p>
|
|
1694
|
-
</div>
|
|
1695
|
-
)}
|
|
1696
|
-
{greetingStatus === 'error' && (
|
|
1697
|
-
<div className="mt-4 p-4 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg">
|
|
1698
|
-
<p className="text-red-800 dark:text-red-200 font-medium">❌ Connection failed</p>
|
|
1699
|
-
<p className="text-red-700 dark:text-red-300 text-sm mt-1">{message}</p>
|
|
1700
|
-
</div>
|
|
1701
|
-
)}
|
|
1702
|
-
</div>
|
|
1703
|
-
</section>
|
|
1704
|
-
|
|
1705
|
-
{/* Scaffolded Models Menu */}
|
|
1706
|
-
{scaffoldConfig.models.length > 0 ? (
|
|
1707
|
-
<section className="max-w-6xl mx-auto">
|
|
1708
|
-
<h2 className="text-3xl font-bold mb-8 text-gray-900 dark:text-gray-100">Your Models</h2>
|
|
1709
|
-
<div className="grid gap-6 grid-cols-1 md:grid-cols-2 lg:grid-cols-3">
|
|
1710
|
-
{scaffoldConfig.models.map((model) => (
|
|
1711
|
-
<a
|
|
1712
|
-
key={model.name}
|
|
1713
|
-
href={model.path}
|
|
1714
|
-
className="block p-8 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-xl hover:shadow-lg hover:border-blue-400 dark:hover:border-blue-600 transition-all"
|
|
1715
|
-
>
|
|
1716
|
-
<h3 className="text-2xl font-semibold mb-2 text-gray-900 dark:text-gray-100">{model.label}</h3>
|
|
1717
|
-
<p className="text-gray-600 dark:text-gray-400">Manage {model.label.toLowerCase()}</p>
|
|
1718
|
-
</a>
|
|
1719
|
-
))}
|
|
1720
|
-
</div>
|
|
1721
|
-
</section>
|
|
1722
|
-
) : (
|
|
1723
|
-
<section className="max-w-2xl mx-auto text-center">
|
|
1724
|
-
<div className="bg-white dark:bg-gray-800 rounded-xl p-12 border border-gray-200 dark:border-gray-700">
|
|
1725
|
-
<h2 className="text-2xl font-semibold mb-4 text-gray-900 dark:text-gray-100">Get Started</h2>
|
|
1726
|
-
<p className="text-gray-600 dark:text-gray-400 mb-6">
|
|
1727
|
-
Create your first model with Zod and generate CRUD operations automatically.
|
|
1728
|
-
</p>
|
|
1729
|
-
<code className="block bg-gray-100 dark:bg-gray-900 p-4 rounded text-left text-sm">
|
|
1730
|
-
${pmCmd.exec} swallowkit scaffold shared/models/your-model.ts
|
|
1731
|
-
</code>
|
|
1732
|
-
</div>
|
|
1733
|
-
</section>
|
|
1734
|
-
)}
|
|
1735
|
-
|
|
1736
|
-
<footer className="mt-16 text-center text-gray-600 dark:text-gray-400 text-sm">
|
|
1737
|
-
<p>Built with SwallowKit</p>
|
|
1738
|
-
</footer>
|
|
1739
|
-
</div>
|
|
1740
|
-
</div>
|
|
1741
|
-
);
|
|
1742
|
-
}
|
|
1743
|
-
`;
|
|
1744
|
-
|
|
1745
|
-
fs.writeFileSync(path.join(projectDir, 'app', 'page.tsx'), pageContent);
|
|
1664
|
+
fs.writeFileSync(path.join(projectDir, 'app', 'page.tsx'), buildHomePage("nextjs", pm));
|
|
1746
1665
|
|
|
1747
1666
|
console.log('✅ Home page created\n');
|
|
1748
1667
|
|
|
@@ -1796,9 +1715,8 @@ function createReadme(
|
|
|
1796
1715
|
const backendScaffoldNote = backendLanguage === 'typescript'
|
|
1797
1716
|
? '- Azure Functions CRUD endpoints'
|
|
1798
1717
|
: `- Azure Functions ${backendLanguageLabel} CRUD handlers\n- OpenAPI export + native-generated ${backendLanguageLabel} schema assets`;
|
|
1799
|
-
const pythonUvPaths = getProjectLocalUvPaths(projectDir);
|
|
1800
1718
|
const pythonLocalDevNote = backendLanguage === 'python'
|
|
1801
|
-
? `\n**Python local dev note**: SwallowKit uses \`uv\`
|
|
1719
|
+
? `\n**Python local dev note**: SwallowKit uses normal \`uv\` interpreter discovery and settings, including installed Python, \`.python-version\`, and \`UV_PYTHON\`. It does not pin local Python to the Azure deployment version (${DEFAULT_AZURE_PYTHON_VERSION}). Local Functions uses \`functions/.venv\` and schema generation uses \`functions/.codegen-venv\`. Existing environments are preserved; explicitly recreate them with uv when switching interpreters. A missing uv binary is bootstrapped under \`.uv/bin\` without changing PATH. Keep \`functions/requirements.txt\` and \`functions/requirements.codegen.txt\` as the dependency sources of truth.\n`
|
|
1802
1720
|
: '';
|
|
1803
1721
|
|
|
1804
1722
|
const readme = `# ${projectName}
|
|
@@ -2031,11 +1949,16 @@ function createAiAgentFiles(
|
|
|
2031
1949
|
console.log(' ✅ .github/copilot-instructions.md (GitHub Copilot adapter)');
|
|
2032
1950
|
console.log(' ✅ .github/instructions/ (path-specific instructions)');
|
|
2033
1951
|
|
|
1952
|
+
const mcpVersion = selectInitialMcpVersion();
|
|
2034
1953
|
fs.writeFileSync(
|
|
2035
1954
|
path.join(projectDir, '.mcp.json'),
|
|
2036
|
-
buildSwallowKitMcpProjectConfigSource(pm)
|
|
1955
|
+
buildSwallowKitMcpProjectConfigSource(pm, mcpVersion.version)
|
|
1956
|
+
);
|
|
1957
|
+
console.log(
|
|
1958
|
+
mcpVersion.version === MCP_LATEST
|
|
1959
|
+
? ' ✅ .mcp.json (project-scoped MCP bootstrap, resolves latest)'
|
|
1960
|
+
: ` ✅ .mcp.json (project-scoped MCP bootstrap, pinned to ${mcpVersion.version}: ${mcpVersion.reason} Run "${runCmd} swallowkit configure-mcp" to re-select later.)`
|
|
2037
1961
|
);
|
|
2038
|
-
console.log(' ✅ .mcp.json (project-scoped MCP bootstrap)');
|
|
2039
1962
|
|
|
2040
1963
|
writeWorkflowDocs(projectDir, runCmd);
|
|
2041
1964
|
console.log(' ✅ .swallowkit/workflows/ (agent-agnostic autonomous-loop runbooks)');
|
|
@@ -2949,7 +2872,7 @@ jobs:
|
|
|
2949
2872
|
${commonSetup} - name: Setup Python
|
|
2950
2873
|
uses: actions/setup-python@v5
|
|
2951
2874
|
with:
|
|
2952
|
-
python-version: '
|
|
2875
|
+
python-version: '${DEFAULT_AZURE_PYTHON_VERSION}'
|
|
2953
2876
|
|
|
2954
2877
|
- name: Install Functions dependencies
|
|
2955
2878
|
run: |
|
|
@@ -3138,7 +3061,7 @@ variables:
|
|
|
3138
3061
|
steps:
|
|
3139
3062
|
${commonSetup} - task: UsePythonVersion@0
|
|
3140
3063
|
inputs:
|
|
3141
|
-
versionSpec: '
|
|
3064
|
+
versionSpec: '${DEFAULT_AZURE_PYTHON_VERSION}'
|
|
3142
3065
|
displayName: 'Install Python'
|
|
3143
3066
|
|
|
3144
3067
|
- script: |
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import { ensureSwallowKitProject } from "../../core/config";
|
|
3
3
|
import { initializeLegacyMigrations } from "../../core/operations/migration-generator";
|
|
4
|
+
import { addModelContainerMigration } from "../../core/operations/model-migration";
|
|
4
5
|
|
|
5
6
|
export const migrationsCommand = new Command("migrations")
|
|
6
7
|
.description("Manage versioned Azure infrastructure migrations");
|
|
@@ -18,3 +19,24 @@ migrationsCommand
|
|
|
18
19
|
console.log(`Migration baseline 0 created for project ${manifest.projectId}.`);
|
|
19
20
|
console.log("Commit infra/migrations/manifest.json, then run provision with --adopt-existing --baseline 0.");
|
|
20
21
|
});
|
|
22
|
+
|
|
23
|
+
migrationsCommand
|
|
24
|
+
.command("add-model")
|
|
25
|
+
.description("Generate only the Cosmos DB container migration for a model (e.g. after scaffold --skip-infra)")
|
|
26
|
+
.argument("<model>", "Model file or model name")
|
|
27
|
+
.option("--dry-run", "Show the planned infra changes without writing anything", false)
|
|
28
|
+
.action(async (model: string, options: { dryRun?: boolean }) => {
|
|
29
|
+
ensureSwallowKitProject("migrations add-model");
|
|
30
|
+
const result = await addModelContainerMigration(model, { dryRun: options.dryRun });
|
|
31
|
+
for (const operation of result.operations) {
|
|
32
|
+
if (operation.action !== "skip") console.log(` ${operation.action.padEnd(7)} ${operation.path}`);
|
|
33
|
+
}
|
|
34
|
+
if (!result.migration) {
|
|
35
|
+
console.log(`No migration needed for ${result.model}: ${result.reason}`);
|
|
36
|
+
} else if (result.operations.every((operation) => operation.action === "skip")) {
|
|
37
|
+
console.log(`Migration ${result.migration.version} for ${result.model} already exists: ${result.migration.template}`);
|
|
38
|
+
} else {
|
|
39
|
+
console.log(`${result.dryRun ? "Would create" : "Created"} migration ${result.migration.version} for ${result.model}: ${result.migration.template}`);
|
|
40
|
+
if (!result.dryRun) console.log("Commit the migration, then run provision to plan and apply it.");
|
|
41
|
+
}
|
|
42
|
+
});
|
|
@@ -1,7 +1,27 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import prompts from "prompts";
|
|
3
3
|
import { ensureSwallowKitProject } from "../../core/config";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
INTERACTIVE_PROVISION_RETRY_HINT,
|
|
6
|
+
ProvisionPlanData,
|
|
7
|
+
executeProvisionPlan,
|
|
8
|
+
planProvisionOperation,
|
|
9
|
+
} from "../../core/operations/provision-operations";
|
|
10
|
+
|
|
11
|
+
export function describeProvisionAction(plan: Pick<ProvisionPlanData, "mode" | "resourceGroup" | "location" | "pendingMigrations">): string {
|
|
12
|
+
switch (plan.mode) {
|
|
13
|
+
case "bootstrap":
|
|
14
|
+
return `Resource group ${plan.resourceGroup} does not exist yet. It will be created in ${plan.location} and the infrastructure in infra/main.bicep will be deployed.`;
|
|
15
|
+
case "migrate":
|
|
16
|
+
return `${plan.pendingMigrations.length} infrastructure migration(s) will be deployed to ${plan.resourceGroup}: ${plan.pendingMigrations.map((migration) => `${migration.version}-${migration.slug}`).join(", ")}.`;
|
|
17
|
+
case "adopt":
|
|
18
|
+
return `The existing resource group ${plan.resourceGroup} will be recorded as SwallowKit baseline 0. Nothing will be deployed.`;
|
|
19
|
+
case "reconcile":
|
|
20
|
+
return `infra/main.bicep will be redeployed to ${plan.resourceGroup}. Template-managed settings may be reset.`;
|
|
21
|
+
default:
|
|
22
|
+
return `Resource group ${plan.resourceGroup}:`;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
5
25
|
|
|
6
26
|
const PRIMARY_REGIONS = [
|
|
7
27
|
{ title: "Japan East (japaneast)", value: "japaneast" },
|
|
@@ -70,16 +90,17 @@ export const provisionCommand = new Command("provision")
|
|
|
70
90
|
.option("--subscription <id>", "Azure subscription ID")
|
|
71
91
|
.option("--location <region>", "Primary location for Functions and Cosmos DB")
|
|
72
92
|
.option("--swa-location <region>", "Static Web App location")
|
|
73
|
-
.option("--what-if", "Include Azure what-if evidence", false)
|
|
93
|
+
.option("--what-if", "Include Azure what-if evidence in the preview", false)
|
|
74
94
|
.option("--adopt-existing", "Adopt an existing untagged resource group without deploying main.bicep", false)
|
|
75
95
|
.option("--baseline <version>", "Baseline version for adoption (currently 0)")
|
|
76
96
|
.option("--reconcile", "Explicitly redeploy infra/main.bicep", false)
|
|
77
|
-
.option("--approve", "
|
|
97
|
+
.option("--approve", "Run the displayed Azure commands without the confirmation prompt", false)
|
|
78
98
|
.action(async (options: ProvisionCliOptions) => {
|
|
79
99
|
ensureSwallowKitProject("provision");
|
|
80
100
|
const locations = await resolveLocations(options);
|
|
81
101
|
const baseline = options.baseline === undefined ? undefined : Number(options.baseline);
|
|
82
|
-
|
|
102
|
+
// Preview in memory: the same Azure checks as the agent workflow, but no saved plan file.
|
|
103
|
+
const preview = await planProvisionOperation({
|
|
83
104
|
resourceGroup: options.resourceGroup,
|
|
84
105
|
subscription: options.subscription,
|
|
85
106
|
location: locations.location,
|
|
@@ -88,37 +109,52 @@ export const provisionCommand = new Command("provision")
|
|
|
88
109
|
adoptExisting: options.adoptExisting,
|
|
89
110
|
baseline,
|
|
90
111
|
reconcile: options.reconcile,
|
|
91
|
-
});
|
|
112
|
+
}, { persist: false });
|
|
92
113
|
|
|
93
|
-
console.
|
|
94
|
-
|
|
95
|
-
if (
|
|
96
|
-
console.log(
|
|
114
|
+
for (const warning of preview.warnings) console.warn(`Warning: ${warning}`);
|
|
115
|
+
|
|
116
|
+
if (preview.mode === "offline") {
|
|
117
|
+
console.log([
|
|
118
|
+
"",
|
|
119
|
+
"❌ Azure CLI is unavailable, so the current state of Azure could not be checked. Nothing was changed in Azure.",
|
|
120
|
+
" 1. Make sure `az --version` works in this terminal (install: https://aka.ms/azure-cli).",
|
|
121
|
+
" 2. Sign in with `az login` (and `az account set --subscription <id>` if needed).",
|
|
122
|
+
" 3. Run this command again.",
|
|
123
|
+
].join("\n"));
|
|
124
|
+
process.exitCode = 1;
|
|
125
|
+
return;
|
|
97
126
|
}
|
|
98
|
-
for (const command of plan.commands) console.log(` ${command}`);
|
|
99
127
|
|
|
100
|
-
if (
|
|
128
|
+
if (preview.mode === "adoption-required") {
|
|
101
129
|
throw new Error('Existing resource group requires: migrations init --legacy-baseline, then provision --adopt-existing --baseline 0 --what-if.');
|
|
102
130
|
}
|
|
103
131
|
|
|
132
|
+
if (preview.mode === "noop") {
|
|
133
|
+
console.log(`Resource group ${preview.resourceGroup} is already up to date. Nothing to do.`);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
console.log(`\n${describeProvisionAction(preview)}`);
|
|
138
|
+
console.log("The following Azure CLI commands will run:");
|
|
139
|
+
for (const command of preview.commands) console.log(` ${command}`);
|
|
140
|
+
|
|
104
141
|
let approved = options.approve === true;
|
|
105
142
|
if (!approved) {
|
|
106
143
|
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
107
|
-
throw new Error("Non-interactive provision requires --approve after reviewing the
|
|
144
|
+
throw new Error("Non-interactive provision requires --approve after reviewing the commands above.");
|
|
108
145
|
}
|
|
109
146
|
const answer = await prompts({
|
|
110
147
|
type: "confirm",
|
|
111
148
|
name: "approved",
|
|
112
|
-
message:
|
|
149
|
+
message: "Run these Azure commands? This may create billable resources.",
|
|
113
150
|
initial: false,
|
|
114
151
|
});
|
|
115
152
|
approved = answer.approved === true;
|
|
116
153
|
}
|
|
117
154
|
if (!approved) {
|
|
118
|
-
console.log("Provision cancelled.");
|
|
155
|
+
console.log("Provision cancelled. Nothing was changed in Azure.");
|
|
119
156
|
return;
|
|
120
157
|
}
|
|
121
158
|
|
|
122
|
-
const result = await
|
|
123
|
-
console.log(
|
|
124
|
-
});
|
|
159
|
+
const result = await executeProvisionPlan(preview, { approve: true, retryHint: INTERACTIVE_PROVISION_RETRY_HINT });
|
|
160
|
+
console.log(`✅ Provisioning completed. ${result.executedCommands.length} Azure CLI command(s) ran.`); });
|