@polymorphism-tech/morph-spec 2.4.0 → 3.0.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.
Files changed (218) hide show
  1. package/CLAUDE.md +158 -26
  2. package/LICENSE +72 -72
  3. package/bin/detect-agents.js +225 -225
  4. package/bin/morph-spec.js +8 -0
  5. package/bin/render-template.js +302 -302
  6. package/bin/semantic-detect-agents.js +246 -246
  7. package/bin/validate-agents-skills.js +251 -251
  8. package/bin/validate-agents.js +69 -69
  9. package/bin/validate-phase.js +263 -263
  10. package/content/.azure/README.md +293 -293
  11. package/content/.azure/docs/azure-devops-setup.md +454 -454
  12. package/content/.azure/docs/branch-strategy.md +398 -398
  13. package/content/.azure/docs/local-development.md +515 -515
  14. package/content/.azure/pipelines/pipeline-variables.yml +34 -34
  15. package/content/.azure/pipelines/prod-pipeline.yml +319 -319
  16. package/content/.azure/pipelines/staging-pipeline.yml +234 -234
  17. package/content/.azure/pipelines/templates/build-dotnet.yml +75 -75
  18. package/content/.azure/pipelines/templates/deploy-app-service.yml +94 -94
  19. package/content/.azure/pipelines/templates/deploy-container-app.yml +120 -120
  20. package/content/.azure/pipelines/templates/infra-deploy.yml +90 -90
  21. package/content/.claude/commands/morph-archive.md +79 -79
  22. package/content/.claude/commands/morph-deploy.md +529 -0
  23. package/content/.claude/commands/morph-infra.md +209 -209
  24. package/content/.claude/commands/morph-preflight.md +227 -227
  25. package/content/.claude/commands/morph-troubleshoot.md +122 -122
  26. package/content/.claude/settings.local.json +15 -15
  27. package/content/.claude/skills/infra/azure-deploy-specialist.md +699 -0
  28. package/content/.claude/skills/level-0-meta/README.md +7 -0
  29. package/content/.claude/skills/{checklists → level-0-meta}/morph-checklist.md +117 -117
  30. package/content/.claude/skills/level-1-workflows/README.md +7 -0
  31. package/content/.claude/skills/{workflows → level-1-workflows}/morph-replicate.md +213 -213
  32. package/content/.claude/skills/{workflows → level-1-workflows}/phase-clarify.md +131 -131
  33. package/content/.claude/skills/{workflows → level-1-workflows}/phase-design.md +213 -205
  34. package/content/.claude/skills/{workflows → level-1-workflows}/phase-setup.md +106 -92
  35. package/content/.claude/skills/{workflows → level-1-workflows}/phase-tasks.md +164 -164
  36. package/content/.claude/skills/{workflows → level-1-workflows}/phase-uiux.md +169 -138
  37. package/content/.claude/skills/level-2-domains/README.md +14 -0
  38. package/content/.claude/skills/{specialists → level-2-domains/quality}/testing-specialist.md +126 -126
  39. package/content/.claude/skills/level-3-technologies/README.md +7 -0
  40. package/content/.claude/skills/level-4-patterns/README.md +7 -0
  41. package/content/.claude/skills/specialists/prompt-engineer.md +189 -0
  42. package/content/.claude/skills/specialists/seo-growth-hacker.md +320 -0
  43. package/content/.morph/.morphversion +5 -5
  44. package/content/.morph/archive/.gitkeep +25 -25
  45. package/content/.morph/config/agents.json +742 -358
  46. package/content/.morph/config/config.template.json +33 -0
  47. package/content/.morph/docs/STORY-DRIVEN-DEVELOPMENT.md +392 -392
  48. package/content/.morph/docs/workflows/enforcement-pipeline.md +668 -0
  49. package/content/.morph/examples/api-nextjs/README.md +241 -241
  50. package/content/.morph/examples/api-nextjs/contracts.ts +307 -307
  51. package/content/.morph/examples/api-nextjs/spec.md +399 -399
  52. package/content/.morph/examples/api-nextjs/tasks.md +168 -168
  53. package/content/.morph/examples/micro-saas/README.md +125 -125
  54. package/content/.morph/examples/micro-saas/contracts.cs +358 -358
  55. package/content/.morph/examples/micro-saas/decisions.md +246 -246
  56. package/content/.morph/examples/micro-saas/spec.md +236 -236
  57. package/content/.morph/examples/micro-saas/tasks.md +150 -150
  58. package/content/.morph/examples/multi-agent/README.md +309 -309
  59. package/content/.morph/examples/multi-agent/contracts.cs +433 -433
  60. package/content/.morph/examples/multi-agent/spec.md +479 -479
  61. package/content/.morph/examples/multi-agent/tasks.md +185 -185
  62. package/content/.morph/examples/scheduled-reports/decisions.md +158 -158
  63. package/content/.morph/examples/scheduled-reports/proposal.md +95 -95
  64. package/content/.morph/examples/scheduled-reports/spec.md +267 -267
  65. package/content/.morph/examples/state-v3.json +188 -188
  66. package/content/.morph/features/.gitkeep +25 -25
  67. package/content/.morph/hooks/README.md +158 -0
  68. package/content/.morph/hooks/pre-commit-all.sh +48 -48
  69. package/content/.morph/hooks/pre-commit-specs.sh +49 -49
  70. package/content/.morph/hooks/pre-commit-tests.sh +60 -60
  71. package/content/.morph/hooks/task-completed.js +73 -0
  72. package/content/.morph/hooks/teammate-idle.js +68 -0
  73. package/content/.morph/project.md +160 -160
  74. package/content/.morph/schemas/agent.schema.json +296 -296
  75. package/content/.morph/schemas/tasks.schema.json +220 -220
  76. package/content/.morph/specs/.gitkeep +20 -20
  77. package/content/.morph/standards/agent-teams-workflow.md +474 -0
  78. package/content/.morph/standards/coding.md +377 -377
  79. package/content/.morph/standards/fluent-ui-setup.md +590 -590
  80. package/content/.morph/standards/migration-guide.md +514 -514
  81. package/content/.morph/standards/passkeys-auth.md +423 -423
  82. package/content/.morph/standards/vector-search-rag.md +536 -536
  83. package/content/.morph/state.json +17 -17
  84. package/content/.morph/templates/CONTEXT-FEATURE.md +276 -0
  85. package/content/.morph/templates/CONTEXT.md +170 -0
  86. package/content/.morph/templates/FluentDesignTheme.cs +149 -149
  87. package/content/.morph/templates/MudTheme.cs +281 -281
  88. package/content/.morph/templates/clarify-questions.md +159 -159
  89. package/content/.morph/templates/component.razor +239 -239
  90. package/content/.morph/templates/contracts/Commands.cs +74 -74
  91. package/content/.morph/templates/contracts/Entities.cs +25 -25
  92. package/content/.morph/templates/contracts/Queries.cs +74 -74
  93. package/content/.morph/templates/contracts/README.md +74 -74
  94. package/content/.morph/templates/contracts.cs +217 -217
  95. package/content/.morph/templates/design-system.css +226 -226
  96. package/content/.morph/templates/infra/.dockerignore.example +89 -89
  97. package/content/.morph/templates/infra/Dockerfile.example +82 -82
  98. package/content/.morph/templates/infra/README.md +286 -286
  99. package/content/.morph/templates/infra/app-insights.bicep +63 -63
  100. package/content/.morph/templates/infra/app-service.bicep +164 -164
  101. package/content/.morph/templates/infra/azure-pipelines-deploy.yml +480 -0
  102. package/content/.morph/templates/infra/container-app-env.bicep +49 -49
  103. package/content/.morph/templates/infra/container-app.bicep +156 -156
  104. package/content/.morph/templates/infra/deploy-checklist.md +426 -426
  105. package/content/.morph/templates/infra/deploy.ps1 +229 -229
  106. package/content/.morph/templates/infra/deploy.sh +208 -208
  107. package/content/.morph/templates/infra/key-vault.bicep +91 -91
  108. package/content/.morph/templates/infra/main.bicep +189 -189
  109. package/content/.morph/templates/infra/parameters.dev.json +29 -29
  110. package/content/.morph/templates/infra/parameters.prod.json +29 -29
  111. package/content/.morph/templates/infra/parameters.staging.json +29 -29
  112. package/content/.morph/templates/infra/sql-database.bicep +103 -103
  113. package/content/.morph/templates/infra/storage.bicep +106 -106
  114. package/content/.morph/templates/integrations/asaas-client.cs +387 -387
  115. package/content/.morph/templates/integrations/asaas-webhook.cs +351 -351
  116. package/content/.morph/templates/integrations/azure-identity-config.cs +288 -288
  117. package/content/.morph/templates/integrations/clerk-config.cs +258 -258
  118. package/content/.morph/templates/job.cs +171 -171
  119. package/content/.morph/templates/migration.cs +83 -83
  120. package/content/.morph/templates/repository.cs +141 -141
  121. package/content/.morph/templates/saas/subscription.cs +347 -347
  122. package/content/.morph/templates/saas/tenant.cs +338 -338
  123. package/content/.morph/templates/service.cs +139 -139
  124. package/content/.morph/templates/sprint-status.yaml +68 -68
  125. package/content/.morph/templates/story.md +143 -143
  126. package/content/.morph/templates/test.cs +239 -239
  127. package/content/.morph/templates/ui-design-system.md +286 -286
  128. package/content/.morph/templates/ui-flows.md +336 -336
  129. package/content/.morph/templates/ui-mockups.md +133 -133
  130. package/content/.morph/test-infra/example.bicep +59 -59
  131. package/content/README.md +79 -79
  132. package/detectors/config-detector.js +223 -223
  133. package/detectors/conversation-analyzer.js +163 -163
  134. package/detectors/index.js +84 -84
  135. package/detectors/standards-generator.js +275 -275
  136. package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.svg +977 -977
  137. package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.svg +1048 -1048
  138. package/docs/api/scripts/collapse.js +38 -38
  139. package/docs/api/scripts/commonNav.js +28 -28
  140. package/docs/api/scripts/linenumber.js +25 -25
  141. package/docs/api/scripts/nav.js +12 -12
  142. package/docs/api/scripts/polyfill.js +3 -3
  143. package/docs/api/scripts/prettify/Apache-License-2.0.txt +202 -202
  144. package/docs/api/scripts/prettify/lang-css.js +2 -2
  145. package/docs/api/scripts/prettify/prettify.js +28 -28
  146. package/docs/api/scripts/search.js +98 -98
  147. package/docs/api/styles/jsdoc.css +776 -776
  148. package/docs/api/styles/prettify.css +80 -80
  149. package/docs/examples.md +328 -328
  150. package/docs/templates.md +418 -418
  151. package/package.json +1 -1
  152. package/scripts/postinstall.js +132 -132
  153. package/src/commands/advance-phase.js +83 -0
  154. package/src/commands/analyze-blazor-concurrency.js +193 -193
  155. package/src/commands/create-story.js +351 -351
  156. package/src/commands/deploy.js +780 -0
  157. package/src/commands/detect-agents.js +34 -6
  158. package/src/commands/detect.js +104 -104
  159. package/src/commands/generate-context.js +40 -0
  160. package/src/commands/generate.js +149 -149
  161. package/src/commands/lint-fluent.js +352 -352
  162. package/src/commands/rollback-phase.js +185 -185
  163. package/src/commands/session-summary.js +291 -291
  164. package/src/commands/shard-spec.js +224 -224
  165. package/src/commands/sprint-status.js +250 -250
  166. package/src/commands/state.js +333 -333
  167. package/src/commands/sync.js +167 -167
  168. package/src/commands/troubleshoot.js +222 -222
  169. package/src/commands/validate-blazor-state.js +210 -210
  170. package/src/commands/validate-blazor.js +156 -156
  171. package/src/commands/validate-css.js +84 -84
  172. package/src/commands/validate-phase.js +221 -221
  173. package/src/lib/blazor-concurrency-analyzer.js +288 -288
  174. package/src/lib/blazor-state-validator.js +291 -291
  175. package/src/lib/blazor-validator.js +374 -374
  176. package/src/lib/context-generator.js +513 -0
  177. package/src/lib/css-validator.js +352 -352
  178. package/src/lib/design-system-detector.js +187 -0
  179. package/src/lib/design-system-generator.js +298 -298
  180. package/src/lib/design-system-scaffolder.js +299 -0
  181. package/src/lib/hook-executor.js +256 -0
  182. package/src/lib/learning-system.js +520 -520
  183. package/src/lib/mockup-generator.js +366 -366
  184. package/src/lib/spec-validator.js +258 -0
  185. package/src/lib/standards-context-injector.js +287 -0
  186. package/src/lib/team-orchestrator.js +322 -0
  187. package/src/lib/troubleshoot-grep.js +194 -194
  188. package/src/lib/troubleshoot-index.js +144 -144
  189. package/src/lib/ui-detector.js +350 -350
  190. package/src/lib/validation-runner.js +65 -13
  191. package/src/lib/validators/architecture-validator.js +387 -387
  192. package/src/lib/validators/design-system-validator.js +231 -0
  193. package/src/lib/validators/package-validator.js +360 -360
  194. package/src/lib/validators/ui-contrast-validator.js +422 -422
  195. package/src/utils/file-copier.js +9 -1
  196. package/src/utils/logger.js +32 -32
  197. package/src/utils/version-checker.js +175 -175
  198. /package/content/.claude/skills/{checklists → level-0-meta}/code-review.md +0 -0
  199. /package/content/.claude/skills/{checklists → level-0-meta}/simulation-checklist.md +0 -0
  200. /package/content/.claude/skills/{specialists → level-2-domains/ai-agents}/ai-system-architect.md +0 -0
  201. /package/content/.claude/skills/{specialists → level-2-domains/architecture}/po-pm-advisor.md +0 -0
  202. /package/content/.claude/skills/{specialists → level-2-domains/architecture}/standards-architect.md +0 -0
  203. /package/content/.claude/skills/{specialists → level-2-domains/backend}/dotnet-senior.md +0 -0
  204. /package/content/.claude/skills/{specialists → level-2-domains/backend}/ef-modeler.md +0 -0
  205. /package/content/.claude/skills/{specialists → level-2-domains/backend}/hangfire-orchestrator.md +0 -0
  206. /package/content/.claude/skills/{specialists → level-2-domains/backend}/ms-agent-expert.md +0 -0
  207. /package/content/.claude/skills/{stacks/dotnet-blazor.md → level-2-domains/frontend/blazor-builder.md} +0 -0
  208. /package/content/.claude/skills/{stacks/dotnet-nextjs.md → level-2-domains/frontend/nextjs-expert.md} +0 -0
  209. /package/content/.claude/skills/{specialists → level-2-domains/frontend}/ui-ux-designer.md +0 -0
  210. /package/content/.claude/skills/{specialists → level-2-domains/infrastructure}/azure-architect.md +0 -0
  211. /package/content/.claude/skills/{infra → level-2-domains/infrastructure}/bicep-architect.md +0 -0
  212. /package/content/.claude/skills/{infra → level-2-domains/infrastructure}/container-specialist.md +0 -0
  213. /package/content/.claude/skills/{infra → level-2-domains/infrastructure}/devops-engineer.md +0 -0
  214. /package/content/.claude/skills/{integrations → level-2-domains/integrations}/asaas-financial.md +0 -0
  215. /package/content/.claude/skills/{integrations → level-2-domains/integrations}/azure-identity.md +0 -0
  216. /package/content/.claude/skills/{integrations → level-2-domains/integrations}/clerk-auth.md +0 -0
  217. /package/content/.claude/skills/{integrations → level-2-domains/integrations}/resend-email.md +0 -0
  218. /package/content/.claude/skills/{specialists → level-2-domains/quality}/code-analyzer.md +0 -0
@@ -0,0 +1,480 @@
1
+ # ============================================================================
2
+ # Azure DevOps Pipeline - MORPH-SPEC Deploy
3
+ # ============================================================================
4
+ # Generated by MORPH-SPEC Azure Deploy Specialist
5
+ #
6
+ # This pipeline provides:
7
+ # - Multi-stage deployment (Build -> Dev -> Staging -> Prod)
8
+ # - Automatic deployment to dev on develop branch
9
+ # - Manual approval for staging and prod
10
+ # - Rollback support via MORPH-SPEC CLI
11
+ # - Cost validation before deployment
12
+ #
13
+ # Prerequisites:
14
+ # 1. Service connection 'azure-service-connection' with subscription access
15
+ # 2. Service connection 'acr-service-connection' for Container Registry
16
+ # 3. Variable group 'deploy-secrets-{env}' for each environment
17
+ # 4. Environment 'dev', 'staging', 'prod' configured in Azure DevOps
18
+ # ============================================================================
19
+
20
+ trigger:
21
+ branches:
22
+ include:
23
+ - main
24
+ - develop
25
+ paths:
26
+ exclude:
27
+ - '*.md'
28
+ - 'docs/**'
29
+ - '.morph/**'
30
+
31
+ pr:
32
+ branches:
33
+ include:
34
+ - main
35
+ - develop
36
+
37
+ variables:
38
+ # Project Configuration
39
+ - name: projectName
40
+ value: '{{PROJECT_NAME}}'
41
+ - name: acrName
42
+ value: '{{ACR_NAME}}'
43
+ - name: dockerfilePath
44
+ value: './Dockerfile'
45
+ - name: resourceGroupPrefix
46
+ value: 'rg-{{PROJECT_NAME}}'
47
+
48
+ # Azure Configuration
49
+ - name: azureSubscription
50
+ value: 'azure-service-connection'
51
+ - name: acrServiceConnection
52
+ value: 'acr-service-connection'
53
+ - name: azureLocation
54
+ value: 'brazilsouth'
55
+
56
+ # Build Configuration
57
+ - name: imageTag
58
+ value: '$(Build.BuildId)'
59
+ - name: imageRepository
60
+ value: '$(projectName)'
61
+
62
+ # ============================================================================
63
+ # STAGES
64
+ # ============================================================================
65
+
66
+ stages:
67
+ # --------------------------------------------------------------------------
68
+ # Stage: Build
69
+ # --------------------------------------------------------------------------
70
+ - stage: Build
71
+ displayName: 'Build and Push Docker Image'
72
+ jobs:
73
+ - job: BuildAndPush
74
+ displayName: 'Build Docker Image'
75
+ pool:
76
+ vmImage: 'ubuntu-latest'
77
+ steps:
78
+ # Checkout code
79
+ - checkout: self
80
+ fetchDepth: 1
81
+
82
+ # Install Node.js for MORPH-SPEC CLI
83
+ - task: NodeTool@0
84
+ displayName: 'Install Node.js'
85
+ inputs:
86
+ versionSpec: '20.x'
87
+
88
+ # Install MORPH-SPEC CLI
89
+ - script: npm install -g @polymorphism-tech/morph-spec
90
+ displayName: 'Install MORPH-SPEC CLI'
91
+
92
+ # Validate Bicep costs
93
+ - script: |
94
+ if [ -f "infra/main.bicep" ]; then
95
+ npx morph-spec cost infra/main.bicep --strict
96
+ else
97
+ echo "No Bicep files found, skipping cost validation"
98
+ fi
99
+ displayName: 'Validate Infrastructure Costs'
100
+ continueOnError: false
101
+
102
+ # Build and push Docker image
103
+ - task: Docker@2
104
+ displayName: 'Build and Push Image'
105
+ inputs:
106
+ containerRegistry: '$(acrServiceConnection)'
107
+ repository: '$(imageRepository)'
108
+ command: 'buildAndPush'
109
+ Dockerfile: '$(dockerfilePath)'
110
+ tags: |
111
+ $(imageTag)
112
+ latest
113
+
114
+ # Save image tag as artifact
115
+ - script: |
116
+ echo "$(imageTag)" > $(Build.ArtifactStagingDirectory)/imagetag.txt
117
+ displayName: 'Save Image Tag'
118
+
119
+ - task: PublishBuildArtifacts@1
120
+ displayName: 'Publish Artifacts'
121
+ inputs:
122
+ pathToPublish: '$(Build.ArtifactStagingDirectory)'
123
+ artifactName: 'build-artifacts'
124
+
125
+ # --------------------------------------------------------------------------
126
+ # Stage: Deploy to Dev
127
+ # --------------------------------------------------------------------------
128
+ - stage: DeployDev
129
+ displayName: 'Deploy to Development'
130
+ dependsOn: Build
131
+ condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/develop'))
132
+ variables:
133
+ - group: 'deploy-secrets-dev'
134
+ - name: environment
135
+ value: 'dev'
136
+ jobs:
137
+ - deployment: DeployToDev
138
+ displayName: 'Deploy to Dev Environment'
139
+ pool:
140
+ vmImage: 'ubuntu-latest'
141
+ environment: 'dev'
142
+ strategy:
143
+ runOnce:
144
+ deploy:
145
+ steps:
146
+ - download: current
147
+ artifact: 'build-artifacts'
148
+
149
+ - task: AzureCLI@2
150
+ displayName: 'Deploy Infrastructure'
151
+ inputs:
152
+ azureSubscription: '$(azureSubscription)'
153
+ scriptType: 'bash'
154
+ scriptLocation: 'inlineScript'
155
+ inlineScript: |
156
+ # Create resource group if not exists
157
+ az group create \
158
+ --name $(resourceGroupPrefix)-$(environment) \
159
+ --location $(azureLocation) \
160
+ --tags environment=$(environment) project=$(projectName)
161
+
162
+ # Deploy Bicep (if exists)
163
+ if [ -f "infra/main.bicep" ]; then
164
+ az deployment group create \
165
+ --resource-group $(resourceGroupPrefix)-$(environment) \
166
+ --template-file infra/main.bicep \
167
+ --parameters @infra/parameters.$(environment).json \
168
+ --name "deploy-$(Build.BuildId)"
169
+ fi
170
+
171
+ - task: AzureCLI@2
172
+ displayName: 'Deploy Application'
173
+ inputs:
174
+ azureSubscription: '$(azureSubscription)'
175
+ scriptType: 'bash'
176
+ scriptLocation: 'inlineScript'
177
+ inlineScript: |
178
+ IMAGE_TAG=$(cat $(Pipeline.Workspace)/build-artifacts/imagetag.txt)
179
+
180
+ # Get ACR credentials
181
+ ACR_PASSWORD=$(az acr credential show \
182
+ --name $(acrName) \
183
+ --query "passwords[0].value" -o tsv)
184
+
185
+ # Update Container App
186
+ az containerapp update \
187
+ --name $(projectName)-$(environment)-app \
188
+ --resource-group $(resourceGroupPrefix)-$(environment) \
189
+ --image $(acrName).azurecr.io/$(imageRepository):$IMAGE_TAG \
190
+ --set-env-vars \
191
+ "ConnectionStrings__DefaultConnection=$(SqlConnectionString)" \
192
+ "ASPNETCORE_ENVIRONMENT=Development"
193
+
194
+ # Enable sticky sessions for Blazor Server
195
+ az containerapp ingress sticky-sessions set \
196
+ --name $(projectName)-$(environment)-app \
197
+ --resource-group $(resourceGroupPrefix)-$(environment) \
198
+ --affinity sticky
199
+
200
+ - task: AzureCLI@2
201
+ displayName: 'Verify Deployment'
202
+ inputs:
203
+ azureSubscription: '$(azureSubscription)'
204
+ scriptType: 'bash'
205
+ scriptLocation: 'inlineScript'
206
+ inlineScript: |
207
+ # Wait for healthy state
208
+ for i in {1..30}; do
209
+ HEALTH=$(az containerapp revision list \
210
+ --name $(projectName)-$(environment)-app \
211
+ --resource-group $(resourceGroupPrefix)-$(environment) \
212
+ --query "[0].properties.healthState" -o tsv)
213
+
214
+ if [ "$HEALTH" == "Healthy" ]; then
215
+ echo "Deployment verified: Healthy"
216
+ exit 0
217
+ fi
218
+
219
+ echo "Waiting for healthy state... ($i/30)"
220
+ sleep 10
221
+ done
222
+
223
+ echo "Deployment verification timeout"
224
+ exit 1
225
+
226
+ # --------------------------------------------------------------------------
227
+ # Stage: Deploy to Staging
228
+ # --------------------------------------------------------------------------
229
+ - stage: DeployStaging
230
+ displayName: 'Deploy to Staging'
231
+ dependsOn: Build
232
+ condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
233
+ variables:
234
+ - group: 'deploy-secrets-staging'
235
+ - name: environment
236
+ value: 'staging'
237
+ jobs:
238
+ - deployment: DeployToStaging
239
+ displayName: 'Deploy to Staging Environment'
240
+ pool:
241
+ vmImage: 'ubuntu-latest'
242
+ environment: 'staging'
243
+ strategy:
244
+ runOnce:
245
+ deploy:
246
+ steps:
247
+ - download: current
248
+ artifact: 'build-artifacts'
249
+
250
+ - task: AzureCLI@2
251
+ displayName: 'Deploy Infrastructure'
252
+ inputs:
253
+ azureSubscription: '$(azureSubscription)'
254
+ scriptType: 'bash'
255
+ scriptLocation: 'inlineScript'
256
+ inlineScript: |
257
+ az group create \
258
+ --name $(resourceGroupPrefix)-$(environment) \
259
+ --location $(azureLocation) \
260
+ --tags environment=$(environment) project=$(projectName)
261
+
262
+ if [ -f "infra/main.bicep" ]; then
263
+ az deployment group create \
264
+ --resource-group $(resourceGroupPrefix)-$(environment) \
265
+ --template-file infra/main.bicep \
266
+ --parameters @infra/parameters.$(environment).json \
267
+ --name "deploy-$(Build.BuildId)"
268
+ fi
269
+
270
+ - task: AzureCLI@2
271
+ displayName: 'Deploy Application'
272
+ inputs:
273
+ azureSubscription: '$(azureSubscription)'
274
+ scriptType: 'bash'
275
+ scriptLocation: 'inlineScript'
276
+ inlineScript: |
277
+ IMAGE_TAG=$(cat $(Pipeline.Workspace)/build-artifacts/imagetag.txt)
278
+
279
+ ACR_PASSWORD=$(az acr credential show \
280
+ --name $(acrName) \
281
+ --query "passwords[0].value" -o tsv)
282
+
283
+ az containerapp update \
284
+ --name $(projectName)-$(environment)-app \
285
+ --resource-group $(resourceGroupPrefix)-$(environment) \
286
+ --image $(acrName).azurecr.io/$(imageRepository):$IMAGE_TAG \
287
+ --set-env-vars \
288
+ "ConnectionStrings__DefaultConnection=$(SqlConnectionString)" \
289
+ "ASPNETCORE_ENVIRONMENT=Staging"
290
+
291
+ az containerapp ingress sticky-sessions set \
292
+ --name $(projectName)-$(environment)-app \
293
+ --resource-group $(resourceGroupPrefix)-$(environment) \
294
+ --affinity sticky
295
+
296
+ - task: AzureCLI@2
297
+ displayName: 'Verify Deployment'
298
+ inputs:
299
+ azureSubscription: '$(azureSubscription)'
300
+ scriptType: 'bash'
301
+ scriptLocation: 'inlineScript'
302
+ inlineScript: |
303
+ for i in {1..30}; do
304
+ HEALTH=$(az containerapp revision list \
305
+ --name $(projectName)-$(environment)-app \
306
+ --resource-group $(resourceGroupPrefix)-$(environment) \
307
+ --query "[0].properties.healthState" -o tsv)
308
+
309
+ if [ "$HEALTH" == "Healthy" ]; then
310
+ echo "Deployment verified: Healthy"
311
+ exit 0
312
+ fi
313
+
314
+ echo "Waiting for healthy state... ($i/30)"
315
+ sleep 10
316
+ done
317
+
318
+ echo "Deployment verification timeout"
319
+ exit 1
320
+
321
+ # --------------------------------------------------------------------------
322
+ # Stage: Deploy to Production
323
+ # --------------------------------------------------------------------------
324
+ - stage: DeployProd
325
+ displayName: 'Deploy to Production'
326
+ dependsOn: DeployStaging
327
+ condition: succeeded()
328
+ variables:
329
+ - group: 'deploy-secrets-prod'
330
+ - name: environment
331
+ value: 'prod'
332
+ jobs:
333
+ - deployment: DeployToProd
334
+ displayName: 'Deploy to Production Environment'
335
+ pool:
336
+ vmImage: 'ubuntu-latest'
337
+ environment: 'prod'
338
+ strategy:
339
+ runOnce:
340
+ deploy:
341
+ steps:
342
+ - download: current
343
+ artifact: 'build-artifacts'
344
+
345
+ # Save previous revision for rollback
346
+ - task: AzureCLI@2
347
+ displayName: 'Save Previous Revision'
348
+ inputs:
349
+ azureSubscription: '$(azureSubscription)'
350
+ scriptType: 'bash'
351
+ scriptLocation: 'inlineScript'
352
+ inlineScript: |
353
+ PREVIOUS_REVISION=$(az containerapp revision list \
354
+ --name $(projectName)-$(environment)-app \
355
+ --resource-group $(resourceGroupPrefix)-$(environment) \
356
+ --query "[0].name" -o tsv 2>/dev/null || echo "none")
357
+
358
+ echo "##vso[task.setvariable variable=previousRevision]$PREVIOUS_REVISION"
359
+ echo "Previous revision: $PREVIOUS_REVISION"
360
+
361
+ - task: AzureCLI@2
362
+ displayName: 'Deploy Infrastructure'
363
+ inputs:
364
+ azureSubscription: '$(azureSubscription)'
365
+ scriptType: 'bash'
366
+ scriptLocation: 'inlineScript'
367
+ inlineScript: |
368
+ az group create \
369
+ --name $(resourceGroupPrefix)-$(environment) \
370
+ --location $(azureLocation) \
371
+ --tags environment=$(environment) project=$(projectName)
372
+
373
+ if [ -f "infra/main.bicep" ]; then
374
+ az deployment group create \
375
+ --resource-group $(resourceGroupPrefix)-$(environment) \
376
+ --template-file infra/main.bicep \
377
+ --parameters @infra/parameters.$(environment).json \
378
+ --name "deploy-$(Build.BuildId)"
379
+ fi
380
+
381
+ - task: AzureCLI@2
382
+ displayName: 'Deploy Application'
383
+ inputs:
384
+ azureSubscription: '$(azureSubscription)'
385
+ scriptType: 'bash'
386
+ scriptLocation: 'inlineScript'
387
+ inlineScript: |
388
+ IMAGE_TAG=$(cat $(Pipeline.Workspace)/build-artifacts/imagetag.txt)
389
+
390
+ ACR_PASSWORD=$(az acr credential show \
391
+ --name $(acrName) \
392
+ --query "passwords[0].value" -o tsv)
393
+
394
+ az containerapp update \
395
+ --name $(projectName)-$(environment)-app \
396
+ --resource-group $(resourceGroupPrefix)-$(environment) \
397
+ --image $(acrName).azurecr.io/$(imageRepository):$IMAGE_TAG \
398
+ --set-env-vars \
399
+ "ConnectionStrings__DefaultConnection=$(SqlConnectionString)" \
400
+ "ASPNETCORE_ENVIRONMENT=Production"
401
+
402
+ az containerapp ingress sticky-sessions set \
403
+ --name $(projectName)-$(environment)-app \
404
+ --resource-group $(resourceGroupPrefix)-$(environment) \
405
+ --affinity sticky
406
+
407
+ - task: AzureCLI@2
408
+ displayName: 'Verify Deployment'
409
+ inputs:
410
+ azureSubscription: '$(azureSubscription)'
411
+ scriptType: 'bash'
412
+ scriptLocation: 'inlineScript'
413
+ inlineScript: |
414
+ for i in {1..30}; do
415
+ HEALTH=$(az containerapp revision list \
416
+ --name $(projectName)-$(environment)-app \
417
+ --resource-group $(resourceGroupPrefix)-$(environment) \
418
+ --query "[0].properties.healthState" -o tsv)
419
+
420
+ if [ "$HEALTH" == "Healthy" ]; then
421
+ echo "Deployment verified: Healthy"
422
+
423
+ # Get app URL
424
+ APP_URL=$(az containerapp show \
425
+ --name $(projectName)-$(environment)-app \
426
+ --resource-group $(resourceGroupPrefix)-$(environment) \
427
+ --query "properties.configuration.ingress.fqdn" -o tsv)
428
+
429
+ echo "Application URL: https://$APP_URL"
430
+ exit 0
431
+ fi
432
+
433
+ echo "Waiting for healthy state... ($i/30)"
434
+ sleep 10
435
+ done
436
+
437
+ # Rollback on failure
438
+ echo "Deployment failed! Initiating rollback..."
439
+ if [ "$(previousRevision)" != "none" ]; then
440
+ az containerapp revision activate \
441
+ --name $(projectName)-$(environment)-app \
442
+ --resource-group $(resourceGroupPrefix)-$(environment) \
443
+ --revision $(previousRevision)
444
+ echo "Rolled back to $(previousRevision)"
445
+ fi
446
+ exit 1
447
+
448
+ # ============================================================================
449
+ # USAGE INSTRUCTIONS
450
+ # ============================================================================
451
+ #
452
+ # 1. Replace placeholders:
453
+ # - {{PROJECT_NAME}}: Your project name (e.g., "myapp")
454
+ # - {{ACR_NAME}}: Your Azure Container Registry name
455
+ #
456
+ # 2. Create Service Connections in Azure DevOps:
457
+ # - azure-service-connection: Azure Resource Manager connection
458
+ # - acr-service-connection: Docker Registry connection to ACR
459
+ #
460
+ # 3. Create Variable Groups:
461
+ # - deploy-secrets-dev: Variables for dev environment
462
+ # - deploy-secrets-staging: Variables for staging environment
463
+ # - deploy-secrets-prod: Variables for production environment
464
+ #
465
+ # Required variables in each group:
466
+ # - SqlConnectionString: Full SQL connection string
467
+ # - (Add other secrets as needed)
468
+ #
469
+ # 4. Create Environments in Azure DevOps:
470
+ # - dev: No approvals (auto-deploy on develop branch)
471
+ # - staging: Optional approval
472
+ # - prod: Required approval
473
+ #
474
+ # 5. Ensure Bicep templates exist:
475
+ # - infra/main.bicep
476
+ # - infra/parameters.dev.json
477
+ # - infra/parameters.staging.json
478
+ # - infra/parameters.prod.json
479
+ #
480
+ # ============================================================================
@@ -1,49 +1,49 @@
1
- // ==============================================================================
2
- // MORPH-SPEC - Container Apps Environment
3
- // Azure Container Apps managed environment
4
- // ==============================================================================
5
-
6
- @description('Environment name')
7
- param name string
8
-
9
- @description('Location')
10
- param location string
11
-
12
- @description('Tags')
13
- param tags object = {}
14
-
15
- @description('Log Analytics Workspace ID')
16
- param logAnalyticsWorkspaceId string
17
-
18
- // ==============================================================================
19
- // CONTAINER APPS ENVIRONMENT
20
- // ==============================================================================
21
-
22
- resource containerAppEnv 'Microsoft.App/managedEnvironments@2023-05-01' = {
23
- name: name
24
- location: location
25
- tags: tags
26
- properties: {
27
- appLogsConfiguration: {
28
- destination: 'log-analytics'
29
- logAnalyticsConfiguration: {
30
- customerId: reference(logAnalyticsWorkspaceId, '2022-10-01').customerId
31
- sharedKey: listKeys(logAnalyticsWorkspaceId, '2022-10-01').primarySharedKey
32
- }
33
- }
34
- zoneRedundant: false
35
- }
36
- }
37
-
38
- // ==============================================================================
39
- // OUTPUTS
40
- // ==============================================================================
41
-
42
- @description('Container Apps Environment ID')
43
- output id string = containerAppEnv.id
44
-
45
- @description('Container Apps Environment name')
46
- output name string = containerAppEnv.name
47
-
48
- @description('Default domain')
49
- output defaultDomain string = containerAppEnv.properties.defaultDomain
1
+ // ==============================================================================
2
+ // MORPH-SPEC - Container Apps Environment
3
+ // Azure Container Apps managed environment
4
+ // ==============================================================================
5
+
6
+ @description('Environment name')
7
+ param name string
8
+
9
+ @description('Location')
10
+ param location string
11
+
12
+ @description('Tags')
13
+ param tags object = {}
14
+
15
+ @description('Log Analytics Workspace ID')
16
+ param logAnalyticsWorkspaceId string
17
+
18
+ // ==============================================================================
19
+ // CONTAINER APPS ENVIRONMENT
20
+ // ==============================================================================
21
+
22
+ resource containerAppEnv 'Microsoft.App/managedEnvironments@2023-05-01' = {
23
+ name: name
24
+ location: location
25
+ tags: tags
26
+ properties: {
27
+ appLogsConfiguration: {
28
+ destination: 'log-analytics'
29
+ logAnalyticsConfiguration: {
30
+ customerId: reference(logAnalyticsWorkspaceId, '2022-10-01').customerId
31
+ sharedKey: listKeys(logAnalyticsWorkspaceId, '2022-10-01').primarySharedKey
32
+ }
33
+ }
34
+ zoneRedundant: false
35
+ }
36
+ }
37
+
38
+ // ==============================================================================
39
+ // OUTPUTS
40
+ // ==============================================================================
41
+
42
+ @description('Container Apps Environment ID')
43
+ output id string = containerAppEnv.id
44
+
45
+ @description('Container Apps Environment name')
46
+ output name string = containerAppEnv.name
47
+
48
+ @description('Default domain')
49
+ output defaultDomain string = containerAppEnv.properties.defaultDomain