@polymorphism-tech/morph-spec 2.2.0 → 2.4.0
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/CLAUDE.md +314 -1673
- package/LICENSE +72 -72
- package/README.md +515 -516
- package/bin/detect-agents.js +225 -225
- package/bin/morph-spec.js +358 -173
- package/bin/render-template.js +302 -302
- package/bin/semantic-detect-agents.js +246 -246
- package/bin/task-manager.js +429 -0
- package/bin/validate-agents-skills.js +251 -251
- package/bin/validate-agents.js +69 -69
- package/bin/validate-phase.js +263 -263
- package/bin/validate.js +369 -0
- package/content/.azure/README.md +293 -293
- package/content/.azure/docs/azure-devops-setup.md +454 -454
- package/content/.azure/docs/branch-strategy.md +398 -398
- package/content/.azure/docs/local-development.md +515 -515
- package/content/.azure/pipelines/pipeline-variables.yml +34 -34
- package/content/.azure/pipelines/prod-pipeline.yml +319 -319
- package/content/.azure/pipelines/staging-pipeline.yml +234 -234
- package/content/.azure/pipelines/templates/build-dotnet.yml +75 -75
- package/content/.azure/pipelines/templates/deploy-app-service.yml +94 -94
- package/content/.azure/pipelines/templates/deploy-container-app.yml +120 -120
- package/content/.azure/pipelines/templates/infra-deploy.yml +90 -90
- package/content/.claude/commands/morph-apply.md +221 -158
- package/content/.claude/commands/morph-archive.md +79 -79
- package/content/.claude/commands/morph-infra.md +209 -209
- package/content/.claude/commands/morph-preflight.md +227 -0
- package/content/.claude/commands/morph-proposal.md +122 -101
- package/content/.claude/commands/morph-status.md +86 -86
- package/content/.claude/commands/morph-troubleshoot.md +122 -0
- package/content/.claude/settings.local.json +15 -15
- package/content/.claude/skills/checklists/code-review.md +226 -0
- package/content/.claude/skills/checklists/morph-checklist.md +117 -0
- package/content/.claude/skills/checklists/simulation-checklist.md +77 -0
- package/content/.claude/skills/infra/bicep-architect.md +126 -419
- package/content/.claude/skills/infra/container-specialist.md +131 -437
- package/content/.claude/skills/infra/devops-engineer.md +119 -405
- package/content/.claude/skills/integrations/asaas-financial.md +130 -333
- package/content/.claude/skills/integrations/azure-identity.md +142 -309
- package/content/.claude/skills/integrations/clerk-auth.md +108 -290
- package/content/.claude/skills/integrations/resend-email.md +119 -0
- package/content/.claude/skills/specialists/ai-system-architect.md +192 -604
- package/content/.claude/skills/specialists/azure-architect.md +142 -142
- package/content/.claude/skills/specialists/code-analyzer.md +235 -0
- package/content/.claude/skills/specialists/dotnet-senior.md +287 -0
- package/content/.claude/skills/specialists/ef-modeler.md +113 -200
- package/content/.claude/skills/specialists/hangfire-orchestrator.md +126 -245
- package/content/.claude/skills/specialists/ms-agent-expert.md +109 -263
- package/content/.claude/skills/specialists/po-pm-advisor.md +197 -197
- package/content/.claude/skills/specialists/standards-architect.md +156 -78
- package/content/.claude/skills/specialists/testing-specialist.md +126 -0
- package/content/.claude/skills/specialists/ui-ux-designer.md +191 -1060
- package/content/.claude/skills/stacks/dotnet-blazor.md +210 -588
- package/content/.claude/skills/stacks/dotnet-nextjs.md +154 -402
- package/content/.claude/skills/workflows/morph-replicate.md +213 -0
- package/content/.claude/{commands/morph-clarify.md → skills/workflows/phase-clarify.md} +5 -58
- package/content/.claude/{commands/morph-design.md → skills/workflows/phase-design.md} +16 -86
- package/content/.claude/{commands/morph-setup.md → skills/workflows/phase-setup.md} +9 -17
- package/content/.claude/skills/workflows/phase-tasks.md +164 -0
- package/content/.claude/{commands/morph-uiux.md → skills/workflows/phase-uiux.md} +15 -88
- package/content/.morph/.morphversion +5 -5
- package/content/.morph/archive/.gitkeep +25 -25
- package/content/.morph/config/agents.json +378 -242
- package/content/.morph/config/config.template.json +89 -108
- package/content/.morph/docs/STORY-DRIVEN-DEVELOPMENT.md +392 -392
- package/content/.morph/docs/workflows/design-impl.md +37 -0
- package/content/.morph/docs/workflows/fast-track.md +29 -0
- package/content/.morph/docs/workflows/full-morph.md +76 -0
- package/content/.morph/docs/workflows/standard.md +44 -0
- package/content/.morph/docs/workflows/ui-refresh.md +39 -0
- package/content/.morph/examples/api-nextjs/README.md +241 -241
- package/content/.morph/examples/api-nextjs/contracts.ts +307 -307
- package/content/.morph/examples/api-nextjs/spec.md +399 -399
- package/content/.morph/examples/api-nextjs/tasks.md +168 -168
- package/content/.morph/examples/micro-saas/README.md +125 -125
- package/content/.morph/examples/micro-saas/contracts.cs +358 -358
- package/content/.morph/examples/micro-saas/decisions.md +246 -246
- package/content/.morph/examples/micro-saas/spec.md +236 -236
- package/content/.morph/examples/micro-saas/tasks.md +150 -150
- package/content/.morph/examples/multi-agent/README.md +309 -309
- package/content/.morph/examples/multi-agent/contracts.cs +433 -433
- package/content/.morph/examples/multi-agent/spec.md +479 -479
- package/content/.morph/examples/multi-agent/tasks.md +185 -185
- package/content/.morph/examples/scheduled-reports/decisions.md +158 -0
- package/content/.morph/examples/scheduled-reports/proposal.md +95 -0
- package/content/.morph/examples/scheduled-reports/spec.md +267 -0
- package/content/.morph/examples/state-v3.json +188 -0
- package/content/.morph/features/.gitkeep +25 -25
- package/content/.morph/hooks/README.md +190 -239
- package/content/.morph/hooks/pre-commit-agents.sh +24 -24
- package/content/.morph/hooks/pre-commit-all.sh +48 -48
- package/content/.morph/hooks/pre-commit-specs.sh +49 -49
- package/content/.morph/hooks/pre-commit-tests.sh +60 -60
- package/content/.morph/project.md +160 -160
- package/content/.morph/schemas/agent.schema.json +296 -296
- package/content/.morph/schemas/tasks.schema.json +220 -0
- package/content/.morph/specs/.gitkeep +20 -20
- package/content/.morph/standards/agent-framework-blazor-ui.md +359 -0
- package/content/.morph/standards/agent-framework-production.md +410 -0
- package/content/.morph/standards/agent-framework-setup.md +413 -453
- package/content/.morph/standards/agent-framework-workflows.md +349 -0
- package/content/.morph/standards/architecture.md +325 -325
- package/content/.morph/standards/azure.md +605 -379
- package/content/.morph/standards/coding.md +377 -377
- package/content/.morph/standards/dotnet10-migration.md +520 -494
- package/content/.morph/standards/fluent-ui-setup.md +590 -590
- package/content/.morph/standards/migration-guide.md +514 -514
- package/content/.morph/standards/passkeys-auth.md +423 -423
- package/content/.morph/standards/vector-search-rag.md +536 -536
- package/content/.morph/state.json +17 -17
- package/content/.morph/templates/FluentDesignTheme.cs +149 -149
- package/content/.morph/templates/MudTheme.cs +281 -281
- package/content/.morph/templates/agent.cs +163 -172
- package/content/.morph/templates/clarify-questions.md +159 -0
- package/content/.morph/templates/component.razor +239 -239
- package/content/.morph/templates/contracts/Commands.cs +74 -0
- package/content/.morph/templates/contracts/Entities.cs +25 -0
- package/content/.morph/templates/contracts/Queries.cs +74 -0
- package/content/.morph/templates/contracts/README.md +74 -0
- package/content/.morph/templates/contracts.cs +217 -217
- package/content/.morph/templates/decisions.md +123 -106
- package/content/.morph/templates/design-system.css +226 -226
- package/content/.morph/templates/infra/.dockerignore.example +89 -89
- package/content/.morph/templates/infra/Dockerfile.example +82 -82
- package/content/.morph/templates/infra/README.md +286 -286
- package/content/.morph/templates/infra/app-insights.bicep +63 -63
- package/content/.morph/templates/infra/app-service.bicep +164 -164
- package/content/.morph/templates/infra/container-app-env.bicep +49 -49
- package/content/.morph/templates/infra/container-app.bicep +156 -156
- package/content/.morph/templates/infra/deploy-checklist.md +426 -0
- package/content/.morph/templates/infra/deploy.ps1 +229 -229
- package/content/.morph/templates/infra/deploy.sh +208 -208
- package/content/.morph/templates/infra/key-vault.bicep +91 -91
- package/content/.morph/templates/infra/main.bicep +189 -189
- package/content/.morph/templates/infra/parameters.dev.json +29 -29
- package/content/.morph/templates/infra/parameters.prod.json +29 -29
- package/content/.morph/templates/infra/parameters.staging.json +29 -29
- package/content/.morph/templates/infra/sql-database.bicep +103 -103
- package/content/.morph/templates/infra/storage.bicep +106 -106
- package/content/.morph/templates/integrations/asaas-client.cs +387 -387
- package/content/.morph/templates/integrations/asaas-webhook.cs +351 -351
- package/content/.morph/templates/integrations/azure-identity-config.cs +288 -288
- package/content/.morph/templates/integrations/clerk-config.cs +258 -258
- package/content/.morph/templates/job.cs +171 -171
- package/content/.morph/templates/migration.cs +83 -83
- package/content/.morph/templates/proposal.md +141 -155
- package/content/.morph/templates/recap.md +94 -105
- package/content/.morph/templates/repository.cs +141 -141
- package/content/.morph/templates/saas/subscription.cs +347 -347
- package/content/.morph/templates/saas/tenant.cs +338 -338
- package/content/.morph/templates/service.cs +139 -139
- package/content/.morph/templates/simulation.md +353 -0
- package/content/.morph/templates/spec.md +149 -148
- package/content/.morph/templates/sprint-status.yaml +68 -68
- package/content/.morph/templates/state.template.json +222 -222
- package/content/.morph/templates/story.md +143 -143
- package/content/.morph/templates/tasks.md +257 -235
- package/content/.morph/templates/test.cs +239 -239
- package/content/.morph/templates/ui-components.md +362 -276
- package/content/.morph/templates/ui-design-system.md +286 -286
- package/content/.morph/templates/ui-flows.md +336 -336
- package/content/.morph/templates/ui-mockups.md +133 -133
- package/content/.morph/test-infra/example.bicep +59 -59
- package/content/CLAUDE.md +150 -442
- package/content/README.md +79 -79
- package/detectors/config-detector.js +223 -223
- package/detectors/conversation-analyzer.js +163 -163
- package/detectors/index.js +84 -84
- package/detectors/standards-generator.js +275 -275
- package/detectors/structure-detector.js +245 -250
- package/docs/README.md +144 -149
- package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.svg +977 -977
- package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.svg +1048 -1048
- package/docs/api/scripts/collapse.js +38 -38
- package/docs/api/scripts/commonNav.js +28 -28
- package/docs/api/scripts/linenumber.js +25 -25
- package/docs/api/scripts/nav.js +12 -12
- package/docs/api/scripts/polyfill.js +3 -3
- package/docs/api/scripts/prettify/Apache-License-2.0.txt +202 -202
- package/docs/api/scripts/prettify/lang-css.js +2 -2
- package/docs/api/scripts/prettify/prettify.js +28 -28
- package/docs/api/scripts/search.js +98 -98
- package/docs/api/styles/jsdoc.css +776 -776
- package/docs/api/styles/prettify.css +80 -80
- package/docs/examples.md +328 -328
- package/docs/getting-started.md +301 -302
- package/docs/installation.md +361 -361
- package/docs/templates.md +418 -418
- package/docs/validation-checklist.md +265 -266
- package/package.json +80 -80
- package/scripts/postinstall.js +132 -132
- package/src/commands/advance-phase.js +183 -0
- package/src/commands/analyze-blazor-concurrency.js +193 -0
- package/src/commands/create-story.js +351 -351
- package/src/commands/detect-agents.js +139 -0
- package/src/commands/detect.js +104 -104
- package/src/commands/doctor.js +356 -280
- package/src/commands/generate.js +149 -149
- package/src/commands/init.js +258 -245
- package/src/commands/lint-fluent.js +352 -0
- package/src/commands/rollback-phase.js +185 -0
- package/src/commands/session-summary.js +291 -0
- package/src/commands/shard-spec.js +224 -224
- package/src/commands/sprint-status.js +250 -250
- package/src/commands/state.js +333 -333
- package/src/commands/sync.js +167 -167
- package/src/commands/task.js +78 -0
- package/src/commands/troubleshoot.js +222 -0
- package/src/commands/update.js +192 -159
- package/src/commands/validate-blazor-state.js +210 -0
- package/src/commands/validate-blazor.js +156 -0
- package/src/commands/validate-css.js +84 -0
- package/src/commands/validate-phase.js +221 -0
- package/src/lib/blazor-concurrency-analyzer.js +288 -0
- package/src/lib/blazor-state-validator.js +291 -0
- package/src/lib/blazor-validator.js +374 -0
- package/src/lib/complexity-analyzer.js +441 -292
- package/src/lib/continuous-validator.js +421 -0
- package/src/lib/css-validator.js +352 -0
- package/src/lib/decision-constraint-loader.js +109 -0
- package/src/lib/design-system-generator.js +298 -298
- package/src/lib/learning-system.js +520 -0
- package/src/lib/mockup-generator.js +366 -0
- package/src/lib/recap-generator.js +205 -0
- package/src/lib/state-manager.js +397 -340
- package/src/lib/troubleshoot-grep.js +194 -0
- package/src/lib/troubleshoot-index.js +144 -0
- package/src/lib/ui-detector.js +350 -0
- package/src/lib/validation-runner.js +231 -0
- package/src/lib/validators/architecture-validator.js +387 -0
- package/src/lib/validators/contract-compliance-validator.js +273 -0
- package/src/lib/validators/package-validator.js +360 -0
- package/src/lib/validators/ui-contrast-validator.js +422 -0
- package/src/utils/file-copier.js +179 -139
- package/src/utils/logger.js +32 -32
- package/src/utils/version-checker.js +175 -175
- package/content/.claude/commands/morph-costs.md +0 -206
- package/content/.claude/commands/morph-tasks.md +0 -319
- package/content/.claude/skills/specialists/cost-guardian.md +0 -110
- package/content/.claude/skills/stacks/shopify.md +0 -445
- package/content/.morph/config/azure-pricing.json +0 -70
- package/content/.morph/config/azure-pricing.schema.json +0 -50
- package/content/.morph/hooks/pre-commit-costs.sh +0 -91
- package/docs/api/cost-calculator.js.html +0 -513
- package/docs/api/design-system-generator.js.html +0 -382
- package/docs/api/global.html +0 -5263
- package/docs/api/index.html +0 -96
- package/docs/api/state-manager.js.html +0 -423
- package/src/commands/cost.js +0 -181
- package/src/commands/update-pricing.js +0 -206
- package/src/lib/cost-calculator.js +0 -429
|
@@ -1,208 +1,208 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# ==============================================================================
|
|
3
|
-
# MORPH-SPEC - Deploy Script
|
|
4
|
-
# Automated deployment of Azure infrastructure
|
|
5
|
-
# ==============================================================================
|
|
6
|
-
|
|
7
|
-
set -e # Exit on error
|
|
8
|
-
|
|
9
|
-
# ==============================================================================
|
|
10
|
-
# CONFIGURATION
|
|
11
|
-
# ==============================================================================
|
|
12
|
-
|
|
13
|
-
# Required variables (override via environment)
|
|
14
|
-
APP_NAME="${APP_NAME:-myapp}"
|
|
15
|
-
ENVIRONMENT="${ENVIRONMENT:-dev}"
|
|
16
|
-
LOCATION="${LOCATION:-eastus}"
|
|
17
|
-
SUBSCRIPTION_ID="${SUBSCRIPTION_ID:-}"
|
|
18
|
-
|
|
19
|
-
# Optional variables
|
|
20
|
-
HOSTING_TYPE="${HOSTING_TYPE:-appservice}" # appservice or containerapp
|
|
21
|
-
APP_SERVICE_SKU="${APP_SERVICE_SKU:-F1}"
|
|
22
|
-
CONTAINER_IMAGE="${CONTAINER_IMAGE:-mcr.microsoft.com/hello-world:latest}"
|
|
23
|
-
|
|
24
|
-
# Derived variables
|
|
25
|
-
RESOURCE_GROUP="rg-${APP_NAME}-${ENVIRONMENT}"
|
|
26
|
-
DEPLOYMENT_NAME="deploy-${APP_NAME}-$(date +%Y%m%d-%H%M%S)"
|
|
27
|
-
|
|
28
|
-
# ==============================================================================
|
|
29
|
-
# COLORS
|
|
30
|
-
# ==============================================================================
|
|
31
|
-
|
|
32
|
-
RED='\033[0;31m'
|
|
33
|
-
GREEN='\033[0;32m'
|
|
34
|
-
YELLOW='\033[1;33m'
|
|
35
|
-
BLUE='\033[0;34m'
|
|
36
|
-
NC='\033[0m' # No Color
|
|
37
|
-
|
|
38
|
-
# ==============================================================================
|
|
39
|
-
# FUNCTIONS
|
|
40
|
-
# ==============================================================================
|
|
41
|
-
|
|
42
|
-
log_info() {
|
|
43
|
-
echo -e "${BLUE}ℹ️ $1${NC}"
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
log_success() {
|
|
47
|
-
echo -e "${GREEN}✅ $1${NC}"
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
log_warning() {
|
|
51
|
-
echo -e "${YELLOW}⚠️ $1${NC}"
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
log_error() {
|
|
55
|
-
echo -e "${RED}❌ $1${NC}"
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
check_prerequisites() {
|
|
59
|
-
log_info "Checking prerequisites..."
|
|
60
|
-
|
|
61
|
-
# Check Azure CLI
|
|
62
|
-
if ! command -v az &> /dev/null; then
|
|
63
|
-
log_error "Azure CLI not found. Install from: https://aka.ms/azure-cli"
|
|
64
|
-
exit 1
|
|
65
|
-
fi
|
|
66
|
-
|
|
67
|
-
# Check login
|
|
68
|
-
if ! az account show &> /dev/null; then
|
|
69
|
-
log_error "Not logged in to Azure. Run: az login"
|
|
70
|
-
exit 1
|
|
71
|
-
fi
|
|
72
|
-
|
|
73
|
-
# Set subscription if provided
|
|
74
|
-
if [ -n "$SUBSCRIPTION_ID" ]; then
|
|
75
|
-
log_info "Setting subscription to: $SUBSCRIPTION_ID"
|
|
76
|
-
az account set --subscription "$SUBSCRIPTION_ID"
|
|
77
|
-
fi
|
|
78
|
-
|
|
79
|
-
log_success "Prerequisites checked"
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
create_resource_group() {
|
|
83
|
-
log_info "Creating resource group: $RESOURCE_GROUP"
|
|
84
|
-
|
|
85
|
-
if az group exists -n "$RESOURCE_GROUP" | grep -q true; then
|
|
86
|
-
log_warning "Resource group already exists"
|
|
87
|
-
else
|
|
88
|
-
az group create \
|
|
89
|
-
--name "$RESOURCE_GROUP" \
|
|
90
|
-
--location "$LOCATION" \
|
|
91
|
-
--tags \
|
|
92
|
-
environment="$ENVIRONMENT" \
|
|
93
|
-
application="$APP_NAME" \
|
|
94
|
-
managedBy="bicep" \
|
|
95
|
-
framework="morph-spec"
|
|
96
|
-
|
|
97
|
-
log_success "Resource group created"
|
|
98
|
-
fi
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
generate_sql_password() {
|
|
102
|
-
# Generate secure random password
|
|
103
|
-
SQL_PASSWORD=$(openssl rand -base64 32 | tr -d "=+/" | cut -c1-25)
|
|
104
|
-
log_success "SQL password generated (stored in Key Vault after deploy)"
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
deploy_infrastructure() {
|
|
108
|
-
log_info "Deploying infrastructure..."
|
|
109
|
-
log_info " App Name: $APP_NAME"
|
|
110
|
-
log_info " Environment: $ENVIRONMENT"
|
|
111
|
-
log_info " Hosting Type: $HOSTING_TYPE"
|
|
112
|
-
log_info " Location: $LOCATION"
|
|
113
|
-
|
|
114
|
-
# Prepare parameters file
|
|
115
|
-
PARAMS_FILE="parameters.${ENVIRONMENT}.json"
|
|
116
|
-
|
|
117
|
-
if [ ! -f "$PARAMS_FILE" ]; then
|
|
118
|
-
log_error "Parameters file not found: $PARAMS_FILE"
|
|
119
|
-
exit 1
|
|
120
|
-
fi
|
|
121
|
-
|
|
122
|
-
# Deploy
|
|
123
|
-
az deployment group create \
|
|
124
|
-
--resource-group "$RESOURCE_GROUP" \
|
|
125
|
-
--name "$DEPLOYMENT_NAME" \
|
|
126
|
-
--template-file main.bicep \
|
|
127
|
-
--parameters "@$PARAMS_FILE" \
|
|
128
|
-
--parameters \
|
|
129
|
-
appName="$APP_NAME" \
|
|
130
|
-
environment="$ENVIRONMENT" \
|
|
131
|
-
location="$LOCATION" \
|
|
132
|
-
hostingType="$HOSTING_TYPE" \
|
|
133
|
-
appServiceSku="$APP_SERVICE_SKU" \
|
|
134
|
-
containerImage="$CONTAINER_IMAGE" \
|
|
135
|
-
sqlAdminPassword="$SQL_PASSWORD" \
|
|
136
|
-
--output table
|
|
137
|
-
|
|
138
|
-
log_success "Infrastructure deployed"
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
show_outputs() {
|
|
142
|
-
log_info "Retrieving deployment outputs..."
|
|
143
|
-
|
|
144
|
-
APP_URL=$(az deployment group show \
|
|
145
|
-
-g "$RESOURCE_GROUP" \
|
|
146
|
-
-n "$DEPLOYMENT_NAME" \
|
|
147
|
-
--query properties.outputs.appUrl.value -o tsv)
|
|
148
|
-
|
|
149
|
-
SQL_CONNECTION=$(az deployment group show \
|
|
150
|
-
-g "$RESOURCE_GROUP" \
|
|
151
|
-
-n "$DEPLOYMENT_NAME" \
|
|
152
|
-
--query properties.outputs.sqlConnectionString.value -o tsv)
|
|
153
|
-
|
|
154
|
-
APPINSIGHTS_CONNECTION=$(az deployment group show \
|
|
155
|
-
-g "$RESOURCE_GROUP" \
|
|
156
|
-
-n "$DEPLOYMENT_NAME" \
|
|
157
|
-
--query properties.outputs.appInsightsConnectionString.value -o tsv)
|
|
158
|
-
|
|
159
|
-
echo ""
|
|
160
|
-
echo "╔════════════════════════════════════════════════════════════════╗"
|
|
161
|
-
echo "║ DEPLOYMENT SUCCESSFUL ║"
|
|
162
|
-
echo "╚════════════════════════════════════════════════════════════════╝"
|
|
163
|
-
echo ""
|
|
164
|
-
echo "🌐 Application URL:"
|
|
165
|
-
echo " $APP_URL"
|
|
166
|
-
echo ""
|
|
167
|
-
echo "🗄️ SQL Connection String:"
|
|
168
|
-
echo " $SQL_CONNECTION"
|
|
169
|
-
echo ""
|
|
170
|
-
echo "📊 App Insights Connection String:"
|
|
171
|
-
echo " $APPINSIGHTS_CONNECTION"
|
|
172
|
-
echo ""
|
|
173
|
-
echo "💡 Next steps:"
|
|
174
|
-
|
|
175
|
-
if [ "$HOSTING_TYPE" = "appservice" ]; then
|
|
176
|
-
echo " 1. Deploy your code: az webapp up --name app-${APP_NAME}-${ENVIRONMENT}"
|
|
177
|
-
echo " 2. View logs: az webapp log tail --name app-${APP_NAME}-${ENVIRONMENT} -g $RESOURCE_GROUP"
|
|
178
|
-
else
|
|
179
|
-
echo " 1. Build and push container: az acr build --registry <ACR> --image ${APP_NAME}:latest ."
|
|
180
|
-
echo " 2. Update container app: az containerapp update -n ca-${APP_NAME}-${ENVIRONMENT} -g $RESOURCE_GROUP --image <IMAGE>"
|
|
181
|
-
echo " 3. View logs: az containerapp logs show -n ca-${APP_NAME}-${ENVIRONMENT} -g $RESOURCE_GROUP --follow"
|
|
182
|
-
fi
|
|
183
|
-
|
|
184
|
-
echo ""
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
# ==============================================================================
|
|
188
|
-
# MAIN
|
|
189
|
-
# ==============================================================================
|
|
190
|
-
|
|
191
|
-
main() {
|
|
192
|
-
echo ""
|
|
193
|
-
echo "╔════════════════════════════════════════════════════════════════╗"
|
|
194
|
-
echo "║ MORPH-SPEC - Azure Infrastructure Deploy ║"
|
|
195
|
-
echo "╚════════════════════════════════════════════════════════════════╝"
|
|
196
|
-
echo ""
|
|
197
|
-
|
|
198
|
-
check_prerequisites
|
|
199
|
-
create_resource_group
|
|
200
|
-
generate_sql_password
|
|
201
|
-
deploy_infrastructure
|
|
202
|
-
show_outputs
|
|
203
|
-
|
|
204
|
-
log_success "Deployment complete! 🚀"
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
# Run main function
|
|
208
|
-
main
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# ==============================================================================
|
|
3
|
+
# MORPH-SPEC - Deploy Script
|
|
4
|
+
# Automated deployment of Azure infrastructure
|
|
5
|
+
# ==============================================================================
|
|
6
|
+
|
|
7
|
+
set -e # Exit on error
|
|
8
|
+
|
|
9
|
+
# ==============================================================================
|
|
10
|
+
# CONFIGURATION
|
|
11
|
+
# ==============================================================================
|
|
12
|
+
|
|
13
|
+
# Required variables (override via environment)
|
|
14
|
+
APP_NAME="${APP_NAME:-myapp}"
|
|
15
|
+
ENVIRONMENT="${ENVIRONMENT:-dev}"
|
|
16
|
+
LOCATION="${LOCATION:-eastus}"
|
|
17
|
+
SUBSCRIPTION_ID="${SUBSCRIPTION_ID:-}"
|
|
18
|
+
|
|
19
|
+
# Optional variables
|
|
20
|
+
HOSTING_TYPE="${HOSTING_TYPE:-appservice}" # appservice or containerapp
|
|
21
|
+
APP_SERVICE_SKU="${APP_SERVICE_SKU:-F1}"
|
|
22
|
+
CONTAINER_IMAGE="${CONTAINER_IMAGE:-mcr.microsoft.com/hello-world:latest}"
|
|
23
|
+
|
|
24
|
+
# Derived variables
|
|
25
|
+
RESOURCE_GROUP="rg-${APP_NAME}-${ENVIRONMENT}"
|
|
26
|
+
DEPLOYMENT_NAME="deploy-${APP_NAME}-$(date +%Y%m%d-%H%M%S)"
|
|
27
|
+
|
|
28
|
+
# ==============================================================================
|
|
29
|
+
# COLORS
|
|
30
|
+
# ==============================================================================
|
|
31
|
+
|
|
32
|
+
RED='\033[0;31m'
|
|
33
|
+
GREEN='\033[0;32m'
|
|
34
|
+
YELLOW='\033[1;33m'
|
|
35
|
+
BLUE='\033[0;34m'
|
|
36
|
+
NC='\033[0m' # No Color
|
|
37
|
+
|
|
38
|
+
# ==============================================================================
|
|
39
|
+
# FUNCTIONS
|
|
40
|
+
# ==============================================================================
|
|
41
|
+
|
|
42
|
+
log_info() {
|
|
43
|
+
echo -e "${BLUE}ℹ️ $1${NC}"
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
log_success() {
|
|
47
|
+
echo -e "${GREEN}✅ $1${NC}"
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
log_warning() {
|
|
51
|
+
echo -e "${YELLOW}⚠️ $1${NC}"
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
log_error() {
|
|
55
|
+
echo -e "${RED}❌ $1${NC}"
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
check_prerequisites() {
|
|
59
|
+
log_info "Checking prerequisites..."
|
|
60
|
+
|
|
61
|
+
# Check Azure CLI
|
|
62
|
+
if ! command -v az &> /dev/null; then
|
|
63
|
+
log_error "Azure CLI not found. Install from: https://aka.ms/azure-cli"
|
|
64
|
+
exit 1
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
# Check login
|
|
68
|
+
if ! az account show &> /dev/null; then
|
|
69
|
+
log_error "Not logged in to Azure. Run: az login"
|
|
70
|
+
exit 1
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
# Set subscription if provided
|
|
74
|
+
if [ -n "$SUBSCRIPTION_ID" ]; then
|
|
75
|
+
log_info "Setting subscription to: $SUBSCRIPTION_ID"
|
|
76
|
+
az account set --subscription "$SUBSCRIPTION_ID"
|
|
77
|
+
fi
|
|
78
|
+
|
|
79
|
+
log_success "Prerequisites checked"
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
create_resource_group() {
|
|
83
|
+
log_info "Creating resource group: $RESOURCE_GROUP"
|
|
84
|
+
|
|
85
|
+
if az group exists -n "$RESOURCE_GROUP" | grep -q true; then
|
|
86
|
+
log_warning "Resource group already exists"
|
|
87
|
+
else
|
|
88
|
+
az group create \
|
|
89
|
+
--name "$RESOURCE_GROUP" \
|
|
90
|
+
--location "$LOCATION" \
|
|
91
|
+
--tags \
|
|
92
|
+
environment="$ENVIRONMENT" \
|
|
93
|
+
application="$APP_NAME" \
|
|
94
|
+
managedBy="bicep" \
|
|
95
|
+
framework="morph-spec"
|
|
96
|
+
|
|
97
|
+
log_success "Resource group created"
|
|
98
|
+
fi
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
generate_sql_password() {
|
|
102
|
+
# Generate secure random password
|
|
103
|
+
SQL_PASSWORD=$(openssl rand -base64 32 | tr -d "=+/" | cut -c1-25)
|
|
104
|
+
log_success "SQL password generated (stored in Key Vault after deploy)"
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
deploy_infrastructure() {
|
|
108
|
+
log_info "Deploying infrastructure..."
|
|
109
|
+
log_info " App Name: $APP_NAME"
|
|
110
|
+
log_info " Environment: $ENVIRONMENT"
|
|
111
|
+
log_info " Hosting Type: $HOSTING_TYPE"
|
|
112
|
+
log_info " Location: $LOCATION"
|
|
113
|
+
|
|
114
|
+
# Prepare parameters file
|
|
115
|
+
PARAMS_FILE="parameters.${ENVIRONMENT}.json"
|
|
116
|
+
|
|
117
|
+
if [ ! -f "$PARAMS_FILE" ]; then
|
|
118
|
+
log_error "Parameters file not found: $PARAMS_FILE"
|
|
119
|
+
exit 1
|
|
120
|
+
fi
|
|
121
|
+
|
|
122
|
+
# Deploy
|
|
123
|
+
az deployment group create \
|
|
124
|
+
--resource-group "$RESOURCE_GROUP" \
|
|
125
|
+
--name "$DEPLOYMENT_NAME" \
|
|
126
|
+
--template-file main.bicep \
|
|
127
|
+
--parameters "@$PARAMS_FILE" \
|
|
128
|
+
--parameters \
|
|
129
|
+
appName="$APP_NAME" \
|
|
130
|
+
environment="$ENVIRONMENT" \
|
|
131
|
+
location="$LOCATION" \
|
|
132
|
+
hostingType="$HOSTING_TYPE" \
|
|
133
|
+
appServiceSku="$APP_SERVICE_SKU" \
|
|
134
|
+
containerImage="$CONTAINER_IMAGE" \
|
|
135
|
+
sqlAdminPassword="$SQL_PASSWORD" \
|
|
136
|
+
--output table
|
|
137
|
+
|
|
138
|
+
log_success "Infrastructure deployed"
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
show_outputs() {
|
|
142
|
+
log_info "Retrieving deployment outputs..."
|
|
143
|
+
|
|
144
|
+
APP_URL=$(az deployment group show \
|
|
145
|
+
-g "$RESOURCE_GROUP" \
|
|
146
|
+
-n "$DEPLOYMENT_NAME" \
|
|
147
|
+
--query properties.outputs.appUrl.value -o tsv)
|
|
148
|
+
|
|
149
|
+
SQL_CONNECTION=$(az deployment group show \
|
|
150
|
+
-g "$RESOURCE_GROUP" \
|
|
151
|
+
-n "$DEPLOYMENT_NAME" \
|
|
152
|
+
--query properties.outputs.sqlConnectionString.value -o tsv)
|
|
153
|
+
|
|
154
|
+
APPINSIGHTS_CONNECTION=$(az deployment group show \
|
|
155
|
+
-g "$RESOURCE_GROUP" \
|
|
156
|
+
-n "$DEPLOYMENT_NAME" \
|
|
157
|
+
--query properties.outputs.appInsightsConnectionString.value -o tsv)
|
|
158
|
+
|
|
159
|
+
echo ""
|
|
160
|
+
echo "╔════════════════════════════════════════════════════════════════╗"
|
|
161
|
+
echo "║ DEPLOYMENT SUCCESSFUL ║"
|
|
162
|
+
echo "╚════════════════════════════════════════════════════════════════╝"
|
|
163
|
+
echo ""
|
|
164
|
+
echo "🌐 Application URL:"
|
|
165
|
+
echo " $APP_URL"
|
|
166
|
+
echo ""
|
|
167
|
+
echo "🗄️ SQL Connection String:"
|
|
168
|
+
echo " $SQL_CONNECTION"
|
|
169
|
+
echo ""
|
|
170
|
+
echo "📊 App Insights Connection String:"
|
|
171
|
+
echo " $APPINSIGHTS_CONNECTION"
|
|
172
|
+
echo ""
|
|
173
|
+
echo "💡 Next steps:"
|
|
174
|
+
|
|
175
|
+
if [ "$HOSTING_TYPE" = "appservice" ]; then
|
|
176
|
+
echo " 1. Deploy your code: az webapp up --name app-${APP_NAME}-${ENVIRONMENT}"
|
|
177
|
+
echo " 2. View logs: az webapp log tail --name app-${APP_NAME}-${ENVIRONMENT} -g $RESOURCE_GROUP"
|
|
178
|
+
else
|
|
179
|
+
echo " 1. Build and push container: az acr build --registry <ACR> --image ${APP_NAME}:latest ."
|
|
180
|
+
echo " 2. Update container app: az containerapp update -n ca-${APP_NAME}-${ENVIRONMENT} -g $RESOURCE_GROUP --image <IMAGE>"
|
|
181
|
+
echo " 3. View logs: az containerapp logs show -n ca-${APP_NAME}-${ENVIRONMENT} -g $RESOURCE_GROUP --follow"
|
|
182
|
+
fi
|
|
183
|
+
|
|
184
|
+
echo ""
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
# ==============================================================================
|
|
188
|
+
# MAIN
|
|
189
|
+
# ==============================================================================
|
|
190
|
+
|
|
191
|
+
main() {
|
|
192
|
+
echo ""
|
|
193
|
+
echo "╔════════════════════════════════════════════════════════════════╗"
|
|
194
|
+
echo "║ MORPH-SPEC - Azure Infrastructure Deploy ║"
|
|
195
|
+
echo "╚════════════════════════════════════════════════════════════════╝"
|
|
196
|
+
echo ""
|
|
197
|
+
|
|
198
|
+
check_prerequisites
|
|
199
|
+
create_resource_group
|
|
200
|
+
generate_sql_password
|
|
201
|
+
deploy_infrastructure
|
|
202
|
+
show_outputs
|
|
203
|
+
|
|
204
|
+
log_success "Deployment complete! 🚀"
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
# Run main function
|
|
208
|
+
main
|
|
@@ -1,91 +1,91 @@
|
|
|
1
|
-
// ==============================================================================
|
|
2
|
-
// MORPH-SPEC - Key Vault
|
|
3
|
-
// Azure Key Vault for secrets management
|
|
4
|
-
// ==============================================================================
|
|
5
|
-
|
|
6
|
-
@description('Key Vault name')
|
|
7
|
-
@minLength(3)
|
|
8
|
-
@maxLength(24)
|
|
9
|
-
param name string
|
|
10
|
-
|
|
11
|
-
@description('Location')
|
|
12
|
-
param location string
|
|
13
|
-
|
|
14
|
-
@description('Tags')
|
|
15
|
-
param tags object = {}
|
|
16
|
-
|
|
17
|
-
@description('Enable soft delete')
|
|
18
|
-
param enableSoftDelete bool = true
|
|
19
|
-
|
|
20
|
-
@description('Soft delete retention days')
|
|
21
|
-
@minValue(7)
|
|
22
|
-
@maxValue(90)
|
|
23
|
-
param softDeleteRetentionDays int = 30
|
|
24
|
-
|
|
25
|
-
@description('Enable purge protection')
|
|
26
|
-
param enablePurgeProtection bool = false
|
|
27
|
-
|
|
28
|
-
@description('Object IDs to grant access (optional)')
|
|
29
|
-
param accessPoliciesObjectIds array = []
|
|
30
|
-
|
|
31
|
-
// ==============================================================================
|
|
32
|
-
// KEY VAULT
|
|
33
|
-
// ==============================================================================
|
|
34
|
-
|
|
35
|
-
resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' = {
|
|
36
|
-
name: name
|
|
37
|
-
location: location
|
|
38
|
-
tags: tags
|
|
39
|
-
properties: {
|
|
40
|
-
tenantId: subscription().tenantId
|
|
41
|
-
sku: {
|
|
42
|
-
family: 'A'
|
|
43
|
-
name: 'standard'
|
|
44
|
-
}
|
|
45
|
-
enabledForDeployment: true
|
|
46
|
-
enabledForDiskEncryption: false
|
|
47
|
-
enabledForTemplateDeployment: true
|
|
48
|
-
enableSoftDelete: enableSoftDelete
|
|
49
|
-
softDeleteRetentionInDays: softDeleteRetentionDays
|
|
50
|
-
enablePurgeProtection: enablePurgeProtection ? true : null
|
|
51
|
-
enableRbacAuthorization: true
|
|
52
|
-
publicNetworkAccess: 'Enabled'
|
|
53
|
-
networkAcls: {
|
|
54
|
-
defaultAction: 'Allow'
|
|
55
|
-
bypass: 'AzureServices'
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// ==============================================================================
|
|
61
|
-
// ACCESS POLICIES (Optional - if not using RBAC)
|
|
62
|
-
// ==============================================================================
|
|
63
|
-
|
|
64
|
-
resource accessPolicies 'Microsoft.KeyVault/vaults/accessPolicies@2023-07-01' = if (length(accessPoliciesObjectIds) > 0) {
|
|
65
|
-
parent: keyVault
|
|
66
|
-
name: 'add'
|
|
67
|
-
properties: {
|
|
68
|
-
accessPolicies: [for objectId in accessPoliciesObjectIds: {
|
|
69
|
-
tenantId: subscription().tenantId
|
|
70
|
-
objectId: objectId
|
|
71
|
-
permissions: {
|
|
72
|
-
secrets: ['get', 'list', 'set', 'delete']
|
|
73
|
-
keys: ['get', 'list', 'create', 'delete']
|
|
74
|
-
certificates: ['get', 'list', 'create', 'delete']
|
|
75
|
-
}
|
|
76
|
-
}]
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// ==============================================================================
|
|
81
|
-
// OUTPUTS
|
|
82
|
-
// ==============================================================================
|
|
83
|
-
|
|
84
|
-
@description('Key Vault ID')
|
|
85
|
-
output id string = keyVault.id
|
|
86
|
-
|
|
87
|
-
@description('Key Vault name')
|
|
88
|
-
output name string = keyVault.name
|
|
89
|
-
|
|
90
|
-
@description('Key Vault URI')
|
|
91
|
-
output uri string = keyVault.properties.vaultUri
|
|
1
|
+
// ==============================================================================
|
|
2
|
+
// MORPH-SPEC - Key Vault
|
|
3
|
+
// Azure Key Vault for secrets management
|
|
4
|
+
// ==============================================================================
|
|
5
|
+
|
|
6
|
+
@description('Key Vault name')
|
|
7
|
+
@minLength(3)
|
|
8
|
+
@maxLength(24)
|
|
9
|
+
param name string
|
|
10
|
+
|
|
11
|
+
@description('Location')
|
|
12
|
+
param location string
|
|
13
|
+
|
|
14
|
+
@description('Tags')
|
|
15
|
+
param tags object = {}
|
|
16
|
+
|
|
17
|
+
@description('Enable soft delete')
|
|
18
|
+
param enableSoftDelete bool = true
|
|
19
|
+
|
|
20
|
+
@description('Soft delete retention days')
|
|
21
|
+
@minValue(7)
|
|
22
|
+
@maxValue(90)
|
|
23
|
+
param softDeleteRetentionDays int = 30
|
|
24
|
+
|
|
25
|
+
@description('Enable purge protection')
|
|
26
|
+
param enablePurgeProtection bool = false
|
|
27
|
+
|
|
28
|
+
@description('Object IDs to grant access (optional)')
|
|
29
|
+
param accessPoliciesObjectIds array = []
|
|
30
|
+
|
|
31
|
+
// ==============================================================================
|
|
32
|
+
// KEY VAULT
|
|
33
|
+
// ==============================================================================
|
|
34
|
+
|
|
35
|
+
resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' = {
|
|
36
|
+
name: name
|
|
37
|
+
location: location
|
|
38
|
+
tags: tags
|
|
39
|
+
properties: {
|
|
40
|
+
tenantId: subscription().tenantId
|
|
41
|
+
sku: {
|
|
42
|
+
family: 'A'
|
|
43
|
+
name: 'standard'
|
|
44
|
+
}
|
|
45
|
+
enabledForDeployment: true
|
|
46
|
+
enabledForDiskEncryption: false
|
|
47
|
+
enabledForTemplateDeployment: true
|
|
48
|
+
enableSoftDelete: enableSoftDelete
|
|
49
|
+
softDeleteRetentionInDays: softDeleteRetentionDays
|
|
50
|
+
enablePurgeProtection: enablePurgeProtection ? true : null
|
|
51
|
+
enableRbacAuthorization: true
|
|
52
|
+
publicNetworkAccess: 'Enabled'
|
|
53
|
+
networkAcls: {
|
|
54
|
+
defaultAction: 'Allow'
|
|
55
|
+
bypass: 'AzureServices'
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ==============================================================================
|
|
61
|
+
// ACCESS POLICIES (Optional - if not using RBAC)
|
|
62
|
+
// ==============================================================================
|
|
63
|
+
|
|
64
|
+
resource accessPolicies 'Microsoft.KeyVault/vaults/accessPolicies@2023-07-01' = if (length(accessPoliciesObjectIds) > 0) {
|
|
65
|
+
parent: keyVault
|
|
66
|
+
name: 'add'
|
|
67
|
+
properties: {
|
|
68
|
+
accessPolicies: [for objectId in accessPoliciesObjectIds: {
|
|
69
|
+
tenantId: subscription().tenantId
|
|
70
|
+
objectId: objectId
|
|
71
|
+
permissions: {
|
|
72
|
+
secrets: ['get', 'list', 'set', 'delete']
|
|
73
|
+
keys: ['get', 'list', 'create', 'delete']
|
|
74
|
+
certificates: ['get', 'list', 'create', 'delete']
|
|
75
|
+
}
|
|
76
|
+
}]
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ==============================================================================
|
|
81
|
+
// OUTPUTS
|
|
82
|
+
// ==============================================================================
|
|
83
|
+
|
|
84
|
+
@description('Key Vault ID')
|
|
85
|
+
output id string = keyVault.id
|
|
86
|
+
|
|
87
|
+
@description('Key Vault name')
|
|
88
|
+
output name string = keyVault.name
|
|
89
|
+
|
|
90
|
+
@description('Key Vault URI')
|
|
91
|
+
output uri string = keyVault.properties.vaultUri
|