swallowkit 1.0.3 ā 1.1.1
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/create-migration.d.ts +3 -0
- package/dist/cli/commands/create-migration.d.ts.map +1 -0
- package/dist/cli/commands/create-migration.js +16 -0
- package/dist/cli/commands/create-migration.js.map +1 -0
- package/dist/cli/commands/index.d.ts +2 -0
- package/dist/cli/commands/index.d.ts.map +1 -1
- package/dist/cli/commands/index.js +5 -1
- package/dist/cli/commands/index.js.map +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +2 -0
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/migrations.d.ts +3 -0
- package/dist/cli/commands/migrations.d.ts.map +1 -0
- package/dist/cli/commands/migrations.js +22 -0
- package/dist/cli/commands/migrations.js.map +1 -0
- package/dist/cli/commands/provision.d.ts +1 -1
- package/dist/cli/commands/provision.d.ts.map +1 -1
- package/dist/cli/commands/provision.js +95 -185
- package/dist/cli/commands/provision.js.map +1 -1
- package/dist/cli/commands/scaffold.d.ts.map +1 -1
- package/dist/cli/commands/scaffold.js +6 -130
- package/dist/cli/commands/scaffold.js.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +4 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/core/operations/migration-generator.d.ts +10 -0
- package/dist/core/operations/migration-generator.d.ts.map +1 -0
- package/dist/core/operations/migration-generator.js +202 -0
- package/dist/core/operations/migration-generator.js.map +1 -0
- package/dist/core/operations/provision-operations.d.ts +19 -0
- package/dist/core/operations/provision-operations.d.ts.map +1 -1
- package/dist/core/operations/provision-operations.js +299 -30
- package/dist/core/operations/provision-operations.js.map +1 -1
- package/dist/core/operations/scaffold-plan.d.ts.map +1 -1
- package/dist/core/operations/scaffold-plan.js +13 -1
- package/dist/core/operations/scaffold-plan.js.map +1 -1
- package/dist/core/project/capabilities.js +1 -1
- package/dist/core/project/capabilities.js.map +1 -1
- package/dist/core/project/infra.d.ts +4 -0
- package/dist/core/project/infra.d.ts.map +1 -1
- package/dist/core/project/infra.js +19 -1
- package/dist/core/project/infra.js.map +1 -1
- package/dist/core/project/migrations.d.ts +53 -0
- package/dist/core/project/migrations.d.ts.map +1 -0
- package/dist/core/project/migrations.js +262 -0
- package/dist/core/project/migrations.js.map +1 -0
- package/dist/core/project/workflows.d.ts.map +1 -1
- package/dist/core/project/workflows.js +7 -3
- package/dist/core/project/workflows.js.map +1 -1
- package/dist/machine/index.d.ts.map +1 -1
- package/dist/machine/index.js +6 -0
- package/dist/machine/index.js.map +1 -1
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +11 -2
- package/dist/mcp/index.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/agent-loop-phase34.test.ts +139 -0
- package/src/__tests__/init.test.ts +8 -0
- package/src/__tests__/migration.test.ts +116 -0
- package/src/__tests__/plan-apply.test.ts +132 -0
- package/src/__tests__/scaffold.test.ts +7 -4
- package/src/cli/commands/create-migration.ts +13 -0
- package/src/cli/commands/index.ts +2 -0
- package/src/cli/commands/init.ts +2 -0
- package/src/cli/commands/migrations.ts +20 -0
- package/src/cli/commands/provision.ts +115 -184
- package/src/cli/commands/scaffold.ts +6 -145
- package/src/cli/index.ts +4 -0
- package/src/core/operations/migration-generator.ts +206 -0
- package/src/core/operations/provision-operations.ts +391 -44
- package/src/core/operations/scaffold-plan.ts +17 -1
- package/src/core/project/capabilities.ts +1 -1
- package/src/core/project/infra.ts +23 -1
- package/src/core/project/migrations.ts +274 -0
- package/src/core/project/workflows.ts +7 -3
- package/src/machine/index.ts +7 -1
- package/src/mcp/index.ts +8 -2
|
@@ -1,204 +1,114 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
2
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
4
|
};
|
|
38
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
6
|
exports.provisionCommand = void 0;
|
|
40
7
|
const commander_1 = require("commander");
|
|
41
|
-
const child_process_1 = require("child_process");
|
|
42
|
-
const fs = __importStar(require("fs"));
|
|
43
|
-
const path = __importStar(require("path"));
|
|
44
8
|
const prompts_1 = __importDefault(require("prompts"));
|
|
45
9
|
const config_1 = require("../../core/config");
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
10
|
+
const provision_operations_1 = require("../../core/operations/provision-operations");
|
|
11
|
+
const PRIMARY_REGIONS = [
|
|
12
|
+
{ title: "Japan East (japaneast)", value: "japaneast" },
|
|
13
|
+
{ title: "Japan West (japanwest)", value: "japanwest" },
|
|
14
|
+
{ title: "East Asia (eastasia)", value: "eastasia" },
|
|
15
|
+
{ title: "Southeast Asia (southeastasia)", value: "southeastasia" },
|
|
16
|
+
{ title: "East US (eastus)", value: "eastus" },
|
|
17
|
+
{ title: "East US 2 (eastus2)", value: "eastus2" },
|
|
18
|
+
{ title: "West US 2 (westus2)", value: "westus2" },
|
|
19
|
+
{ title: "Central US (centralus)", value: "centralus" },
|
|
20
|
+
{ title: "West Europe (westeurope)", value: "westeurope" },
|
|
21
|
+
];
|
|
22
|
+
const SWA_REGIONS = [
|
|
23
|
+
{ title: "East Asia (eastasia) - Recommended for Japan", value: "eastasia" },
|
|
24
|
+
{ title: "West US 2 (westus2)", value: "westus2" },
|
|
25
|
+
{ title: "Central US (centralus)", value: "centralus" },
|
|
26
|
+
{ title: "East US 2 (eastus2)", value: "eastus2" },
|
|
27
|
+
{ title: "West Europe (westeurope)", value: "westeurope" },
|
|
28
|
+
];
|
|
29
|
+
async function resolveLocations(options) {
|
|
30
|
+
if (options.location && options.swaLocation) {
|
|
31
|
+
return { location: options.location, swaLocation: options.swaLocation };
|
|
32
|
+
}
|
|
33
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
34
|
+
throw new Error("Non-interactive provision requires --location and --swa-location.");
|
|
35
|
+
}
|
|
36
|
+
const answer = await (0, prompts_1.default)([
|
|
37
|
+
{
|
|
38
|
+
type: options.location ? null : "select",
|
|
39
|
+
name: "location",
|
|
40
|
+
message: "Primary location for Functions and Cosmos DB",
|
|
41
|
+
choices: PRIMARY_REGIONS,
|
|
42
|
+
initial: 0,
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
type: options.swaLocation ? null : "select",
|
|
46
|
+
name: "swaLocation",
|
|
47
|
+
message: "Static Web App location",
|
|
48
|
+
choices: SWA_REGIONS,
|
|
49
|
+
initial: 0,
|
|
50
|
+
},
|
|
51
|
+
]);
|
|
52
|
+
const location = options.location ?? answer.location;
|
|
53
|
+
const swaLocation = options.swaLocation ?? answer.swaLocation;
|
|
54
|
+
if (!location || !swaLocation)
|
|
55
|
+
throw new Error("Region selection cancelled.");
|
|
56
|
+
return { location, swaLocation };
|
|
57
|
+
}
|
|
58
|
+
exports.provisionCommand = new commander_1.Command("provision")
|
|
59
|
+
.description("Bootstrap, adopt, migrate, or explicitly reconcile Azure resources")
|
|
60
|
+
.requiredOption("-g, --resource-group <name>", "Resource group name")
|
|
61
|
+
.option("--subscription <id>", "Azure subscription ID")
|
|
62
|
+
.option("--location <region>", "Primary location for Functions and Cosmos DB")
|
|
63
|
+
.option("--swa-location <region>", "Static Web App location")
|
|
64
|
+
.option("--what-if", "Include Azure what-if evidence", false)
|
|
65
|
+
.option("--adopt-existing", "Adopt an existing untagged resource group without deploying main.bicep", false)
|
|
66
|
+
.option("--baseline <version>", "Baseline version for adoption (currently 0)")
|
|
67
|
+
.option("--reconcile", "Explicitly redeploy infra/main.bicep", false)
|
|
68
|
+
.option("--approve", "Apply the displayed plan without an interactive confirmation", false)
|
|
50
69
|
.action(async (options) => {
|
|
51
|
-
// SwallowKit ćććøć§ćÆććć£ć¬ćÆććŖćć©ćććę¤čؼ
|
|
52
70
|
(0, config_1.ensureSwallowKitProject)("provision");
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
71
|
+
const locations = await resolveLocations(options);
|
|
72
|
+
const baseline = options.baseline === undefined ? undefined : Number(options.baseline);
|
|
73
|
+
const plan = await (0, provision_operations_1.planProvisionOperation)({
|
|
74
|
+
resourceGroup: options.resourceGroup,
|
|
75
|
+
subscription: options.subscription,
|
|
76
|
+
location: locations.location,
|
|
77
|
+
swaLocation: locations.swaLocation,
|
|
78
|
+
whatIf: options.whatIf,
|
|
79
|
+
adoptExisting: options.adoptExisting,
|
|
80
|
+
baseline,
|
|
81
|
+
reconcile: options.reconcile,
|
|
82
|
+
});
|
|
83
|
+
console.log(`Provision mode: ${plan.mode}`);
|
|
84
|
+
for (const warning of plan.warnings)
|
|
85
|
+
console.warn(`Warning: ${warning}`);
|
|
86
|
+
if (plan.pendingMigrations.length > 0) {
|
|
87
|
+
console.log(`Pending migrations: ${plan.pendingMigrations.map((migration) => `${migration.version}-${migration.slug}`).join(", ")}`);
|
|
57
88
|
}
|
|
58
|
-
|
|
59
|
-
console.
|
|
60
|
-
|
|
89
|
+
for (const command of plan.commands)
|
|
90
|
+
console.log(` ${command}`);
|
|
91
|
+
if (plan.mode === "adoption-required") {
|
|
92
|
+
throw new Error('Existing resource group requires: migrations init --legacy-baseline, then provision --adopt-existing --baseline 0 --what-if.');
|
|
61
93
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if (!fs.existsSync(mainBicepPath)) {
|
|
67
|
-
console.error('ā Bicep files not found. Run "swallowkit init" first to generate infrastructure files.');
|
|
68
|
-
process.exit(1);
|
|
69
|
-
}
|
|
70
|
-
try {
|
|
71
|
-
// Prompt for region selection
|
|
72
|
-
console.log('š Select Azure regions for your resources:\n');
|
|
73
|
-
const regionChoices = await (0, prompts_1.default)([
|
|
74
|
-
{
|
|
75
|
-
type: 'select',
|
|
76
|
-
name: 'primaryLocation',
|
|
77
|
-
message: 'Primary location for Functions and Cosmos DB',
|
|
78
|
-
choices: [
|
|
79
|
-
{ title: 'Japan East (japaneast)', value: 'japaneast' },
|
|
80
|
-
{ title: 'Japan West (japanwest)', value: 'japanwest' },
|
|
81
|
-
{ title: 'East Asia (eastasia)', value: 'eastasia' },
|
|
82
|
-
{ title: 'Southeast Asia (southeastasia)', value: 'southeastasia' },
|
|
83
|
-
{ title: 'East US (eastus)', value: 'eastus' },
|
|
84
|
-
{ title: 'East US 2 (eastus2)', value: 'eastus2' },
|
|
85
|
-
{ title: 'West US 2 (westus2)', value: 'westus2' },
|
|
86
|
-
{ title: 'Central US (centralus)', value: 'centralus' },
|
|
87
|
-
{ title: 'West Europe (westeurope)', value: 'westeurope' },
|
|
88
|
-
],
|
|
89
|
-
initial: 0, // Default to japaneast
|
|
90
|
-
},
|
|
91
|
-
{
|
|
92
|
-
type: 'select',
|
|
93
|
-
name: 'swaLocation',
|
|
94
|
-
message: 'Static Web App location (limited availability)',
|
|
95
|
-
choices: [
|
|
96
|
-
{ title: 'East Asia (eastasia) - Recommended for Japan', value: 'eastasia' },
|
|
97
|
-
{ title: 'West US 2 (westus2)', value: 'westus2' },
|
|
98
|
-
{ title: 'Central US (centralus)', value: 'centralus' },
|
|
99
|
-
{ title: 'East US 2 (eastus2)', value: 'eastus2' },
|
|
100
|
-
{ title: 'West Europe (westeurope)', value: 'westeurope' },
|
|
101
|
-
],
|
|
102
|
-
initial: 0, // Default to eastasia
|
|
103
|
-
},
|
|
104
|
-
], {
|
|
105
|
-
onCancel: () => {
|
|
106
|
-
throw new Error('User cancelled');
|
|
107
|
-
}
|
|
108
|
-
});
|
|
109
|
-
if (!regionChoices.primaryLocation || !regionChoices.swaLocation) {
|
|
110
|
-
console.log('\nā Region selection cancelled.');
|
|
111
|
-
process.exit(1);
|
|
94
|
+
let approved = options.approve === true;
|
|
95
|
+
if (!approved) {
|
|
96
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
97
|
+
throw new Error("Non-interactive provision requires --approve after reviewing the plan.");
|
|
112
98
|
}
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
const confirmation = await (0, prompts_1.default)({
|
|
119
|
-
type: 'confirm',
|
|
120
|
-
name: 'proceed',
|
|
121
|
-
message: `Proceed with deployment to ${primaryLocation} (primary) and ${swaLocation} (SWA)?`,
|
|
122
|
-
initial: true
|
|
123
|
-
}, {
|
|
124
|
-
onCancel: () => {
|
|
125
|
-
throw new Error('User cancelled');
|
|
126
|
-
}
|
|
99
|
+
const answer = await (0, prompts_1.default)({
|
|
100
|
+
type: "confirm",
|
|
101
|
+
name: "approved",
|
|
102
|
+
message: `Apply the ${plan.mode} plan?`,
|
|
103
|
+
initial: false,
|
|
127
104
|
});
|
|
128
|
-
|
|
129
|
-
console.log('\nā Deployment cancelled by user.');
|
|
130
|
-
process.exit(0);
|
|
131
|
-
}
|
|
132
|
-
console.log('');
|
|
133
|
-
// Set subscription if provided
|
|
134
|
-
if (options.subscription) {
|
|
135
|
-
console.log(`š§ Setting subscription: ${options.subscription}`);
|
|
136
|
-
(0, child_process_1.execSync)(`az account set --subscription ${options.subscription}`, { stdio: 'inherit' });
|
|
137
|
-
}
|
|
138
|
-
// Create resource group if it doesn't exist (use primary location)
|
|
139
|
-
console.log(`š§ Ensuring resource group exists: ${options.resourceGroup}`);
|
|
140
|
-
(0, child_process_1.execSync)(`az group create --name ${options.resourceGroup} --location ${primaryLocation}`, { stdio: 'inherit' });
|
|
141
|
-
// Deploy Bicep template with both locations
|
|
142
|
-
console.log('\nš¦ Deploying resources (this may take several minutes)...\n');
|
|
143
|
-
const deployCommand = `az deployment group create --resource-group ${options.resourceGroup} --template-file "${mainBicepPath}" --parameters "${parametersPath}" --parameters location=${primaryLocation} --parameters swaLocation=${swaLocation}`;
|
|
144
|
-
const output = (0, child_process_1.execSync)(deployCommand, { encoding: 'utf-8', stdio: 'pipe' });
|
|
145
|
-
const deployment = JSON.parse(output);
|
|
146
|
-
// Display outputs
|
|
147
|
-
console.log('\nā
Deployment completed successfully!\n');
|
|
148
|
-
console.log('š Resource Information:');
|
|
149
|
-
let swaName = '<swa-name>';
|
|
150
|
-
let functionAppName = '<function-name>';
|
|
151
|
-
if (deployment.properties?.outputs) {
|
|
152
|
-
const outputs = deployment.properties.outputs;
|
|
153
|
-
if (outputs.staticWebAppName) {
|
|
154
|
-
swaName = outputs.staticWebAppName.value;
|
|
155
|
-
console.log(` - Static Web App: ${swaName}`);
|
|
156
|
-
console.log(` - URL: https://${outputs.staticWebAppUrl.value}`);
|
|
157
|
-
}
|
|
158
|
-
if (outputs.functionsAppName) {
|
|
159
|
-
functionAppName = outputs.functionsAppName.value;
|
|
160
|
-
console.log(` - Function App: ${functionAppName}`);
|
|
161
|
-
console.log(` - URL: https://${outputs.functionsAppUrl.value}`);
|
|
162
|
-
}
|
|
163
|
-
if (outputs.cosmosDbAccountName) {
|
|
164
|
-
console.log(` - Cosmos DB: ${outputs.cosmosDbAccountName.value}`);
|
|
165
|
-
console.log(` - Database: ${outputs.cosmosDatabaseName.value}`);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
// Next steps guidance
|
|
169
|
-
console.log('\nš Next Steps:');
|
|
170
|
-
console.log(' 1. Configure CI/CD secrets/variables:\n');
|
|
171
|
-
// Fetch SWA deployment token
|
|
172
|
-
console.log(' [AZURE_STATIC_WEB_APPS_API_TOKEN]');
|
|
173
|
-
try {
|
|
174
|
-
const swaToken = (0, child_process_1.execSync)(`az staticwebapp secrets list --name ${swaName} --resource-group ${options.resourceGroup} --query "properties.apiKey" -o tsv`, { encoding: 'utf-8', stdio: 'pipe' }).trim();
|
|
175
|
-
console.log(` ${swaToken}\n`);
|
|
176
|
-
}
|
|
177
|
-
catch {
|
|
178
|
-
console.log(' ā ļø Failed to retrieve. Run manually:');
|
|
179
|
-
console.log(` az staticwebapp secrets list --name ${swaName} --resource-group ${options.resourceGroup} --query "properties.apiKey" -o tsv\n`);
|
|
180
|
-
}
|
|
181
|
-
// Function App name (already known)
|
|
182
|
-
console.log(' [AZURE_FUNCTIONAPP_NAME]');
|
|
183
|
-
console.log(` ${functionAppName}\n`);
|
|
184
|
-
// Fetch Function App publish profile
|
|
185
|
-
console.log(' [AZURE_FUNCTIONAPP_PUBLISH_PROFILE]');
|
|
186
|
-
try {
|
|
187
|
-
const publishProfile = (0, child_process_1.execSync)(`az webapp deployment list-publishing-profiles --name ${functionAppName} --resource-group ${options.resourceGroup} --xml`, { encoding: 'utf-8', stdio: 'pipe' }).trim();
|
|
188
|
-
console.log(` ${publishProfile}\n`);
|
|
189
|
-
}
|
|
190
|
-
catch {
|
|
191
|
-
console.log(' ā ļø Failed to retrieve. Run manually:');
|
|
192
|
-
console.log(` az webapp deployment list-publishing-profiles --name ${functionAppName} --resource-group ${options.resourceGroup} --xml\n`);
|
|
193
|
-
}
|
|
194
|
-
console.log(' 2. Set up your CI/CD pipeline (GitHub Actions or Azure Pipelines)');
|
|
195
|
-
console.log(' 3. Manually trigger the first deployment in your CI/CD pipeline');
|
|
196
|
-
console.log(' (Automatic deployments will run on subsequent pushes)\n');
|
|
105
|
+
approved = answer.approved === true;
|
|
197
106
|
}
|
|
198
|
-
|
|
199
|
-
console.
|
|
200
|
-
|
|
201
|
-
process.exit(1);
|
|
107
|
+
if (!approved) {
|
|
108
|
+
console.log("Provision cancelled.");
|
|
109
|
+
return;
|
|
202
110
|
}
|
|
111
|
+
const result = await (0, provision_operations_1.applyProvisionOperation)({ planId: plan.planId, approve: true });
|
|
112
|
+
console.log(`Provision completed (${plan.mode}). ${result.executedCommands.length} command(s) executed.`);
|
|
203
113
|
});
|
|
204
114
|
//# sourceMappingURL=provision.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provision.js","sourceRoot":"","sources":["../../../src/cli/commands/provision.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"provision.js","sourceRoot":"","sources":["../../../src/cli/commands/provision.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/scaffold.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,EAAE,SAAS,EAA0D,MAAM,kCAAkC,CAAC;AAgBrH,OAAO,EAOL,aAAa,EACd,MAAM,kCAAkC,CAAC;AAG1C,OAAO,EAML,eAAe,EACf,UAAU,EACX,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,oBAAoB,EAEpB,sBAAsB,EACvB,MAAM,oCAAoC,CAAC;
|
|
1
|
+
{"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/scaffold.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,EAAE,SAAS,EAA0D,MAAM,kCAAkC,CAAC;AAgBrH,OAAO,EAOL,aAAa,EACd,MAAM,kCAAkC,CAAC;AAG1C,OAAO,EAML,eAAe,EACf,UAAU,EACX,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,oBAAoB,EAEpB,sBAAsB,EACvB,MAAM,oCAAoC,CAAC;AAM5C,UAAU,eAAe;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,sBAAsB,CAAC,EAAE,sBAAsB,CAAC;CACjD;AAsBD,wBAAsB,eAAe,CAAC,OAAO,EAAE,eAAe,iBA4K7D;AAyBD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,GAAG,SAAS,GAAG,eAAe,GAAG,SAAS,CAyBvH;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CA4C3D;AA8HD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,eAAe,EAAE,UAAU,EAAE,UAAU,GAAG,aAAa,CAYlG;AAqVD;;GAEG;AACH,wBAAsB,uBAAuB,CAAC,SAAS,EAAE,GAAG,EAAE,OAAO,GAAE,oBAAyD,GAAG,OAAO,CAAC,IAAI,CAAC,CAU/I;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CAAC,SAAS,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,aAAa,EAAE,OAAO,GAAE,oBAAyD,GAAG,OAAO,CAAC,IAAI,CAAC,CAwC1M"}
|
|
@@ -58,6 +58,7 @@ const package_manager_1 = require("../../utils/package-manager");
|
|
|
58
58
|
const manifest_1 = require("../../core/project/manifest");
|
|
59
59
|
const auth_generator_1 = require("../../core/scaffold/auth-generator");
|
|
60
60
|
const file_session_1 = require("../../core/operations/file-session");
|
|
61
|
+
const migration_generator_1 = require("../../core/operations/migration-generator");
|
|
61
62
|
const artifacts_1 = require("../../core/project/artifacts");
|
|
62
63
|
const SCAFFOLD_GENERATOR = "scaffold";
|
|
63
64
|
function getMachineAwareStdio() {
|
|
@@ -645,139 +646,14 @@ async function generateBFFRoutes(modelInfo, apiDir, sharedPackageName, session)
|
|
|
645
646
|
* Generate Cosmos DB container Bicep file
|
|
646
647
|
*/
|
|
647
648
|
async function generateCosmosContainer(modelInfo, session = new file_session_1.FileOperationSession("commit")) {
|
|
648
|
-
console.log("\nšļø Generating Cosmos DB container
|
|
649
|
-
const
|
|
650
|
-
|
|
651
|
-
const cwd = process.cwd();
|
|
652
|
-
// Check if infra directory exists
|
|
653
|
-
const infraDir = path.join(cwd, "infra");
|
|
654
|
-
if (!fs.existsSync(infraDir)) {
|
|
649
|
+
console.log("\nšļø Generating Cosmos DB container migration...");
|
|
650
|
+
const infraDir = path.join(process.cwd(), "infra");
|
|
651
|
+
if (!session.fileExists(infraDir) && !fs.existsSync(infraDir)) {
|
|
655
652
|
console.log("ā¹ļø infra directory not found. Skipping Cosmos DB container generation.");
|
|
656
653
|
return;
|
|
657
654
|
}
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
const containerFileName = `${modelKebab}-container.bicep`;
|
|
661
|
-
const containerFilePath = path.join(containersDir, containerFileName);
|
|
662
|
-
const bicepContent = `@description('Cosmos DB account name')
|
|
663
|
-
param cosmosAccountName string
|
|
664
|
-
|
|
665
|
-
@description('Database name')
|
|
666
|
-
param databaseName string
|
|
667
|
-
|
|
668
|
-
@description('Container name')
|
|
669
|
-
param containerName string = '${modelPascal}s'
|
|
670
|
-
|
|
671
|
-
@description('Partition key path')
|
|
672
|
-
param partitionKeyPath string = '${modelInfo.partitionKey}'
|
|
673
|
-
|
|
674
|
-
// Reference existing Cosmos DB account
|
|
675
|
-
resource cosmosAccount 'Microsoft.DocumentDB/databaseAccounts@2023-11-15' existing = {
|
|
676
|
-
name: cosmosAccountName
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
// Reference existing database
|
|
680
|
-
resource database 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases@2023-11-15' existing = {
|
|
681
|
-
parent: cosmosAccount
|
|
682
|
-
name: databaseName
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
// Container for ${modelPascal}
|
|
686
|
-
resource container 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers@2023-11-15' = {
|
|
687
|
-
parent: database
|
|
688
|
-
name: containerName
|
|
689
|
-
properties: {
|
|
690
|
-
resource: {
|
|
691
|
-
id: containerName
|
|
692
|
-
partitionKey: {
|
|
693
|
-
paths: [
|
|
694
|
-
partitionKeyPath
|
|
695
|
-
]
|
|
696
|
-
kind: 'Hash'
|
|
697
|
-
}
|
|
698
|
-
indexingPolicy: {
|
|
699
|
-
automatic: true
|
|
700
|
-
indexingMode: 'consistent'
|
|
701
|
-
includedPaths: [
|
|
702
|
-
{
|
|
703
|
-
path: '/*'
|
|
704
|
-
}
|
|
705
|
-
]
|
|
706
|
-
excludedPaths: [
|
|
707
|
-
{
|
|
708
|
-
path: '/_etag/?'
|
|
709
|
-
}
|
|
710
|
-
]
|
|
711
|
-
}
|
|
712
|
-
}
|
|
713
|
-
}
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
output containerName string = container.name
|
|
717
|
-
`;
|
|
718
|
-
// Write Bicep file (overwrite if exists)
|
|
719
|
-
session.writeFile(containerFilePath, bicepContent, { ownership: "managed", generator: SCAFFOLD_GENERATOR, sourceModel: modelInfo.name });
|
|
720
|
-
console.log(`ā
Created: ${containerFilePath}`);
|
|
721
|
-
// Update main.bicep to include this container module
|
|
722
|
-
await updateMainBicepWithContainer(modelKebab, modelPascal, session);
|
|
723
|
-
}
|
|
724
|
-
/**
|
|
725
|
-
* Update main.bicep to include new container module
|
|
726
|
-
*/
|
|
727
|
-
async function updateMainBicepWithContainer(modelKebab, modelPascal, session) {
|
|
728
|
-
const cwd = process.cwd();
|
|
729
|
-
const mainBicepPath = path.join(cwd, "infra", "main.bicep");
|
|
730
|
-
if (!session.fileExists(mainBicepPath)) {
|
|
731
|
-
console.log("ā¹ļø main.bicep not found. Please manually add the container module.");
|
|
732
|
-
return;
|
|
733
|
-
}
|
|
734
|
-
let mainBicepContent = session.readFile(mainBicepPath);
|
|
735
|
-
// Check if container module already exists
|
|
736
|
-
const containerModuleName = `${modelPascal.charAt(0).toLowerCase()}${modelPascal.slice(1)}Container`;
|
|
737
|
-
if (mainBicepContent.includes(`module ${containerModuleName}`)) {
|
|
738
|
-
console.log(`ā¹ļø Container module '${containerModuleName}' already exists in main.bicep`);
|
|
739
|
-
return;
|
|
740
|
-
}
|
|
741
|
-
// Find the position to insert the container module (after cosmosDb modules)
|
|
742
|
-
// Look for the end of both cosmosDb modules (FreeTier and Serverless)
|
|
743
|
-
const cosmosModulePattern = /module cosmosDbServerless ['"]modules\/cosmosdb-serverless\.bicep['"] = if \(cosmosDbMode == 'serverless'\) \{[\s\S]*?\n\}/;
|
|
744
|
-
const cosmosModuleMatch = mainBicepContent.match(cosmosModulePattern);
|
|
745
|
-
if (!cosmosModuleMatch) {
|
|
746
|
-
console.log("ā ļø Could not find Cosmos DB Serverless module in main.bicep. Please manually add the container module:");
|
|
747
|
-
console.log(`\nmodule ${containerModuleName} 'containers/${modelKebab}-container.bicep' = {
|
|
748
|
-
name: '${modelKebab}-container'
|
|
749
|
-
params: {
|
|
750
|
-
cosmosAccountName: cosmosDbMode == 'freetier' ? cosmosDbFreeTier.outputs.accountName : cosmosDbServerless.outputs.accountName
|
|
751
|
-
databaseName: databaseName
|
|
752
|
-
cosmosDbMode: cosmosDbMode
|
|
753
|
-
}
|
|
754
|
-
dependsOn: [
|
|
755
|
-
cosmosDbFreeTier
|
|
756
|
-
cosmosDbServerless
|
|
757
|
-
]
|
|
758
|
-
}\n`);
|
|
759
|
-
return;
|
|
760
|
-
}
|
|
761
|
-
// Find the end of the cosmosDbServerless module
|
|
762
|
-
const insertPosition = cosmosModuleMatch.index + cosmosModuleMatch[0].length;
|
|
763
|
-
// Create the container module declaration
|
|
764
|
-
const containerModule = `
|
|
765
|
-
|
|
766
|
-
// ${modelPascal} Container
|
|
767
|
-
module ${containerModuleName} 'containers/${modelKebab}-container.bicep' = {
|
|
768
|
-
name: '${modelKebab}-container'
|
|
769
|
-
params: {
|
|
770
|
-
cosmosAccountName: cosmosDbMode == 'freetier' ? cosmosDbFreeTier.outputs.accountName : cosmosDbServerless.outputs.accountName
|
|
771
|
-
databaseName: '\${projectName}Database'
|
|
772
|
-
}
|
|
773
|
-
}`;
|
|
774
|
-
// Insert the module
|
|
775
|
-
mainBicepContent =
|
|
776
|
-
mainBicepContent.slice(0, insertPosition) +
|
|
777
|
-
containerModule +
|
|
778
|
-
mainBicepContent.slice(insertPosition);
|
|
779
|
-
session.writeFile(mainBicepPath, mainBicepContent, { ownership: "extension-point", generator: SCAFFOLD_GENERATOR });
|
|
780
|
-
console.log(`ā
Added container module to main.bicep`);
|
|
655
|
+
const migration = (0, migration_generator_1.generateModelContainerMigration)(modelInfo, session);
|
|
656
|
+
console.log(`ā
Created migration ${migration.version}: ${migration.template}`);
|
|
781
657
|
}
|
|
782
658
|
/**
|
|
783
659
|
* Generate UI components (list, detail, form, create, edit pages)
|