@polymorphism-tech/morph-spec 1.0.2 → 2.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.
- package/CLAUDE.md +1381 -0
- package/LICENSE +72 -0
- package/README.md +114 -12
- package/bin/detect-agents.js +225 -0
- package/bin/morph-spec.js +120 -0
- package/bin/render-template.js +302 -0
- package/bin/semantic-detect-agents.js +246 -0
- package/bin/validate-agents-skills.js +239 -0
- package/bin/validate-agents.js +69 -0
- package/bin/validate-phase.js +263 -0
- package/content/.azure/README.md +293 -0
- package/content/.azure/docs/azure-devops-setup.md +454 -0
- package/content/.azure/docs/branch-strategy.md +398 -0
- package/content/.azure/docs/local-development.md +515 -0
- package/content/.azure/pipelines/pipeline-variables.yml +34 -0
- package/content/.azure/pipelines/prod-pipeline.yml +319 -0
- package/content/.azure/pipelines/staging-pipeline.yml +234 -0
- package/content/.azure/pipelines/templates/build-dotnet.yml +75 -0
- package/content/.azure/pipelines/templates/deploy-app-service.yml +94 -0
- package/content/.azure/pipelines/templates/deploy-container-app.yml +120 -0
- package/content/.azure/pipelines/templates/infra-deploy.yml +90 -0
- package/content/.claude/commands/morph-apply.md +118 -26
- package/content/.claude/commands/morph-archive.md +9 -9
- package/content/.claude/commands/morph-clarify.md +184 -0
- package/content/.claude/commands/morph-design.md +275 -0
- package/content/.claude/commands/morph-proposal.md +56 -15
- package/content/.claude/commands/morph-setup.md +100 -0
- package/content/.claude/commands/morph-status.md +47 -32
- package/content/.claude/commands/morph-tasks.md +319 -0
- package/content/.claude/commands/morph-uiux.md +211 -0
- package/content/.claude/skills/specialists/ai-system-architect.md +604 -0
- package/content/.claude/skills/specialists/ms-agent-expert.md +143 -89
- package/content/.claude/skills/specialists/ui-ux-designer.md +744 -9
- package/content/.claude/skills/stacks/dotnet-blazor.md +244 -8
- package/content/.claude/skills/stacks/dotnet-nextjs.md +2 -2
- package/content/.morph/.morphversion +5 -0
- package/content/.morph/config/agents.json +101 -8
- package/content/.morph/config/azure-pricing.json +70 -0
- package/content/.morph/config/azure-pricing.schema.json +50 -0
- package/content/.morph/config/config.template.json +15 -3
- package/content/.morph/docs/STORY-DRIVEN-DEVELOPMENT.md +392 -0
- package/content/.morph/hooks/README.md +239 -0
- package/content/.morph/hooks/pre-commit-agents.sh +24 -0
- package/content/.morph/hooks/pre-commit-all.sh +48 -0
- package/content/.morph/hooks/pre-commit-costs.sh +91 -0
- package/content/.morph/hooks/pre-commit-specs.sh +49 -0
- package/content/.morph/hooks/pre-commit-tests.sh +60 -0
- package/content/.morph/project.md +5 -4
- package/content/.morph/schemas/agent.schema.json +296 -0
- package/content/.morph/standards/agent-framework-setup.md +453 -0
- package/content/.morph/standards/architecture.md +142 -7
- package/content/.morph/standards/azure.md +218 -23
- package/content/.morph/standards/coding.md +47 -12
- package/content/.morph/standards/dotnet10-migration.md +494 -0
- package/content/.morph/standards/fluent-ui-setup.md +590 -0
- package/content/.morph/standards/migration-guide.md +514 -0
- package/content/.morph/standards/passkeys-auth.md +423 -0
- package/content/.morph/standards/vector-search-rag.md +536 -0
- package/content/.morph/state.json +18 -0
- package/content/.morph/templates/FluentDesignTheme.cs +149 -0
- package/content/.morph/templates/MudTheme.cs +281 -0
- package/content/.morph/templates/contracts.cs +55 -55
- package/content/.morph/templates/decisions.md +4 -4
- package/content/.morph/templates/design-system.css +226 -0
- package/content/.morph/templates/infra/.dockerignore.example +89 -0
- package/content/.morph/templates/infra/Dockerfile.example +82 -0
- package/content/.morph/templates/infra/README.md +286 -0
- package/content/.morph/templates/infra/app-service.bicep +164 -0
- package/content/.morph/templates/infra/deploy.ps1 +229 -0
- package/content/.morph/templates/infra/deploy.sh +208 -0
- package/content/.morph/templates/infra/main.bicep +41 -7
- package/content/.morph/templates/infra/parameters.dev.json +6 -0
- package/content/.morph/templates/infra/parameters.prod.json +6 -0
- package/content/.morph/templates/infra/parameters.staging.json +29 -0
- package/content/.morph/templates/proposal.md +3 -3
- package/content/.morph/templates/recap.md +3 -3
- package/content/.morph/templates/spec.md +9 -8
- package/content/.morph/templates/sprint-status.yaml +68 -0
- package/content/.morph/templates/state.template.json +222 -0
- package/content/.morph/templates/story.md +143 -0
- package/content/.morph/templates/tasks.md +1 -1
- package/content/.morph/templates/ui-components.md +276 -0
- package/content/.morph/templates/ui-design-system.md +286 -0
- package/content/.morph/templates/ui-flows.md +336 -0
- package/content/.morph/templates/ui-mockups.md +133 -0
- package/content/.morph/test-infra/example.bicep +59 -0
- package/content/CLAUDE.md +124 -0
- package/content/README.md +79 -0
- package/detectors/config-detector.js +223 -0
- package/detectors/conversation-analyzer.js +163 -0
- package/detectors/index.js +84 -0
- package/detectors/standards-generator.js +275 -0
- package/detectors/structure-detector.js +221 -0
- package/docs/README.md +149 -0
- package/docs/api/cost-calculator.js.html +513 -0
- package/docs/api/design-system-generator.js.html +382 -0
- package/docs/api/fonts/Montserrat/Montserrat-Bold.eot +0 -0
- package/docs/api/fonts/Montserrat/Montserrat-Bold.ttf +0 -0
- package/docs/api/fonts/Montserrat/Montserrat-Bold.woff +0 -0
- package/docs/api/fonts/Montserrat/Montserrat-Bold.woff2 +0 -0
- package/docs/api/fonts/Montserrat/Montserrat-Regular.eot +0 -0
- package/docs/api/fonts/Montserrat/Montserrat-Regular.ttf +0 -0
- package/docs/api/fonts/Montserrat/Montserrat-Regular.woff +0 -0
- package/docs/api/fonts/Montserrat/Montserrat-Regular.woff2 +0 -0
- package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.eot +0 -0
- package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.svg +978 -0
- package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.ttf +0 -0
- package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff +0 -0
- package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff2 +0 -0
- package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.eot +0 -0
- package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.svg +1049 -0
- package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.ttf +0 -0
- package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff +0 -0
- package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff2 +0 -0
- package/docs/api/global.html +5263 -0
- package/docs/api/index.html +96 -0
- package/docs/api/scripts/collapse.js +39 -0
- package/docs/api/scripts/commonNav.js +28 -0
- package/docs/api/scripts/linenumber.js +25 -0
- package/docs/api/scripts/nav.js +12 -0
- package/docs/api/scripts/polyfill.js +4 -0
- package/docs/api/scripts/prettify/Apache-License-2.0.txt +202 -0
- package/docs/api/scripts/prettify/lang-css.js +2 -0
- package/docs/api/scripts/prettify/prettify.js +28 -0
- package/docs/api/scripts/search.js +99 -0
- package/docs/api/state-manager.js.html +423 -0
- package/docs/api/styles/jsdoc.css +776 -0
- package/docs/api/styles/prettify.css +80 -0
- package/docs/examples.md +328 -0
- package/docs/getting-started.md +302 -0
- package/docs/installation.md +361 -0
- package/docs/templates.md +418 -0
- package/docs/validation-checklist.md +266 -0
- package/package.json +39 -12
- package/src/commands/cost.js +181 -0
- package/src/commands/create-story.js +283 -0
- package/src/commands/detect.js +104 -0
- package/src/commands/doctor.js +67 -0
- package/src/commands/generate.js +149 -0
- package/src/commands/init.js +71 -46
- package/src/commands/shard-spec.js +224 -0
- package/src/commands/sprint-status.js +250 -0
- package/src/commands/state.js +333 -0
- package/src/commands/sync.js +167 -0
- package/src/commands/update-pricing.js +206 -0
- package/src/commands/update.js +88 -13
- package/src/lib/complexity-analyzer.js +292 -0
- package/src/lib/cost-calculator.js +429 -0
- package/src/lib/design-system-generator.js +298 -0
- package/src/lib/state-manager.js +340 -0
- package/src/utils/file-copier.js +63 -0
- package/src/utils/version-checker.js +175 -0
|
@@ -0,0 +1,514 @@
|
|
|
1
|
+
# Guia de Migração: App Service → Container Apps
|
|
2
|
+
|
|
3
|
+
> **MORPH-SPEC Framework**
|
|
4
|
+
> Quando seu projeto crescer além do Free Tier
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 📊 Quando Migrar?
|
|
9
|
+
|
|
10
|
+
### Sinais de que é hora de migrar
|
|
11
|
+
|
|
12
|
+
| Indicador | Descrição | Ação |
|
|
13
|
+
|-----------|-----------|------|
|
|
14
|
+
| 🔴 **CPU Quota Esgotada** | Erro `CPU quota exceeded` frequente | Migrar imediatamente |
|
|
15
|
+
| 🔴 **Cold Starts Críticos** | App dormindo afeta UX/negócio | Avaliar impacto → Migrar |
|
|
16
|
+
| 🟡 **Tráfego Crescente** | >500 req/dia consistente | Planejar migração |
|
|
17
|
+
| 🟡 **SSL Customizado** | Necessita domínio próprio com SSL | Avaliar alternativas |
|
|
18
|
+
| 🟢 **Background Jobs** | Precisa rodar Hangfire 24/7 | Migrar ou separar worker |
|
|
19
|
+
|
|
20
|
+
### Matriz de Decisão
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
Tráfego < 100 req/dia → App Service Free F1
|
|
24
|
+
Tráfego 100-1000 req/dia → App Service B1 ($13/mês)
|
|
25
|
+
Tráfego > 1000 req/dia → Container Apps (~$5-15/mês)
|
|
26
|
+
Background Jobs 24/7 → Container Apps com minReplicas: 1
|
|
27
|
+
SSL Customizado → Container Apps ou App Service B1+
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 🗺️ Roadmap de Migração
|
|
33
|
+
|
|
34
|
+
### Fase 1: Preparação (1-2 dias)
|
|
35
|
+
|
|
36
|
+
#### 1.1. Auditoria do Código
|
|
37
|
+
|
|
38
|
+
**Checklist:**
|
|
39
|
+
- [ ] App usa configuração via `IConfiguration`
|
|
40
|
+
- [ ] Não há paths hardcoded (usar `IWebHostEnvironment`)
|
|
41
|
+
- [ ] Logging para stdout/stderr (não arquivos locais)
|
|
42
|
+
- [ ] Health checks implementados (`/health`, `/health/ready`)
|
|
43
|
+
- [ ] Secrets em Key Vault (não `appsettings.json`)
|
|
44
|
+
|
|
45
|
+
**Verificar:**
|
|
46
|
+
```csharp
|
|
47
|
+
// ✅ BOM: Configuração via IConfiguration
|
|
48
|
+
var connectionString = configuration["ConnectionStrings:Default"];
|
|
49
|
+
|
|
50
|
+
// ❌ RUIM: Hardcoded
|
|
51
|
+
var path = "C:\\Data\\uploads";
|
|
52
|
+
|
|
53
|
+
// ✅ BOM: Usar IWebHostEnvironment
|
|
54
|
+
var path = Path.Combine(webHostEnv.ContentRootPath, "uploads");
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
#### 1.2. Criar Dockerfile
|
|
58
|
+
|
|
59
|
+
```dockerfile
|
|
60
|
+
# ============================================
|
|
61
|
+
# MORPH-SPEC - .NET 8 Blazor Container
|
|
62
|
+
# ============================================
|
|
63
|
+
|
|
64
|
+
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
|
|
65
|
+
WORKDIR /app
|
|
66
|
+
EXPOSE 8080
|
|
67
|
+
EXPOSE 8081
|
|
68
|
+
|
|
69
|
+
# Create non-root user
|
|
70
|
+
RUN adduser --disabled-password --gecos '' appuser && chown -R appuser /app
|
|
71
|
+
USER appuser
|
|
72
|
+
|
|
73
|
+
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
|
|
74
|
+
WORKDIR /src
|
|
75
|
+
COPY ["YourApp/YourApp.csproj", "YourApp/"]
|
|
76
|
+
RUN dotnet restore "YourApp/YourApp.csproj"
|
|
77
|
+
COPY . .
|
|
78
|
+
WORKDIR "/src/YourApp"
|
|
79
|
+
RUN dotnet build "YourApp.csproj" -c Release -o /app/build
|
|
80
|
+
|
|
81
|
+
FROM build AS publish
|
|
82
|
+
RUN dotnet publish "YourApp.csproj" -c Release -o /app/publish /p:UseAppHost=false
|
|
83
|
+
|
|
84
|
+
FROM base AS final
|
|
85
|
+
WORKDIR /app
|
|
86
|
+
COPY --from=publish /app/publish .
|
|
87
|
+
|
|
88
|
+
# Health check
|
|
89
|
+
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
|
90
|
+
CMD curl -f http://localhost:8080/health || exit 1
|
|
91
|
+
|
|
92
|
+
ENTRYPOINT ["dotnet", "YourApp.dll"]
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
#### 1.3. Implementar Health Checks
|
|
96
|
+
|
|
97
|
+
```csharp
|
|
98
|
+
// Program.cs
|
|
99
|
+
builder.Services.AddHealthChecks()
|
|
100
|
+
.AddSqlServer(
|
|
101
|
+
connectionString: builder.Configuration["ConnectionStrings:Default"],
|
|
102
|
+
name: "sql",
|
|
103
|
+
timeout: TimeSpan.FromSeconds(3))
|
|
104
|
+
.AddCheck("self", () => HealthCheckResult.Healthy());
|
|
105
|
+
|
|
106
|
+
var app = builder.Build();
|
|
107
|
+
|
|
108
|
+
app.MapHealthChecks("/health", new HealthCheckOptions
|
|
109
|
+
{
|
|
110
|
+
Predicate = _ => true,
|
|
111
|
+
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
app.MapHealthChecks("/health/ready", new HealthCheckOptions
|
|
115
|
+
{
|
|
116
|
+
Predicate = check => check.Tags.Contains("ready")
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
app.MapHealthChecks("/health/live", new HealthCheckOptions
|
|
120
|
+
{
|
|
121
|
+
Predicate = _ => false
|
|
122
|
+
});
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
### Fase 2: Setup Azure Container Registry (ACR)
|
|
128
|
+
|
|
129
|
+
#### 2.1. Criar ACR via Bicep
|
|
130
|
+
|
|
131
|
+
```bicep
|
|
132
|
+
// acr.bicep
|
|
133
|
+
resource acr 'Microsoft.ContainerRegistry/registries@2023-01-01-preview' = {
|
|
134
|
+
name: 'acr${uniqueString(resourceGroup().id)}'
|
|
135
|
+
location: location
|
|
136
|
+
sku: {
|
|
137
|
+
name: 'Basic' // $5/mês
|
|
138
|
+
}
|
|
139
|
+
properties: {
|
|
140
|
+
adminUserEnabled: false // Usar Managed Identity
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
#### 2.2. Deploy ACR
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
# Via Azure CLI
|
|
149
|
+
az deployment group create \
|
|
150
|
+
--resource-group rg-myapp-dev \
|
|
151
|
+
--template-file acr.bicep
|
|
152
|
+
|
|
153
|
+
# Obter ACR login server
|
|
154
|
+
ACR_NAME=$(az acr list -g rg-myapp-dev --query "[0].name" -o tsv)
|
|
155
|
+
ACR_LOGIN_SERVER=$(az acr show -n $ACR_NAME --query loginServer -o tsv)
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
#### 2.3. Build e Push Imagem
|
|
159
|
+
|
|
160
|
+
**Via Azure DevOps Pipeline:**
|
|
161
|
+
```yaml
|
|
162
|
+
# staging-pipeline.yml
|
|
163
|
+
- task: Docker@2
|
|
164
|
+
displayName: 'Build and push container'
|
|
165
|
+
inputs:
|
|
166
|
+
containerRegistry: 'ACR-Connection'
|
|
167
|
+
repository: 'myapp'
|
|
168
|
+
command: 'buildAndPush'
|
|
169
|
+
Dockerfile: 'Dockerfile'
|
|
170
|
+
tags: |
|
|
171
|
+
v1.0.0
|
|
172
|
+
staging-latest
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
**Via Azure CLI (local):**
|
|
176
|
+
```bash
|
|
177
|
+
# Build local e push para ACR
|
|
178
|
+
az acr build \
|
|
179
|
+
--registry $ACR_NAME \
|
|
180
|
+
--image myapp:latest \
|
|
181
|
+
--image myapp:v1.0.0 \
|
|
182
|
+
--file Dockerfile \
|
|
183
|
+
.
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
### Fase 3: Deploy Container Apps
|
|
189
|
+
|
|
190
|
+
#### 3.1. Atualizar Parameters
|
|
191
|
+
|
|
192
|
+
**parameters.dev.json**
|
|
193
|
+
```json
|
|
194
|
+
{
|
|
195
|
+
"hostingType": {
|
|
196
|
+
"value": "containerapp"
|
|
197
|
+
},
|
|
198
|
+
"containerImage": {
|
|
199
|
+
"value": "acrXXXX.azurecr.io/myapp:latest"
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
#### 3.2. Deploy Infraestrutura
|
|
205
|
+
|
|
206
|
+
**Via Azure DevOps Pipeline:**
|
|
207
|
+
```yaml
|
|
208
|
+
# Use o template pronto
|
|
209
|
+
- template: templates/infra-deploy.yml
|
|
210
|
+
parameters:
|
|
211
|
+
azureSubscription: 'Azure-Staging-Connection'
|
|
212
|
+
resourceGroupName: 'rg-myapp-staging'
|
|
213
|
+
bicepTemplateFile: 'content/.morph/templates/infra/main.bicep'
|
|
214
|
+
parametersFile: 'content/.morph/templates/infra/parameters.staging.json'
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
**Via Azure CLI (local):**
|
|
218
|
+
```bash
|
|
219
|
+
# Deploy via Azure CLI
|
|
220
|
+
az deployment group create \
|
|
221
|
+
--resource-group rg-myapp-dev \
|
|
222
|
+
--template-file main.bicep \
|
|
223
|
+
--parameters @parameters.dev.json
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
#### 3.3. Configurar Managed Identity para ACR
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
# Obter Container App Managed Identity
|
|
230
|
+
CA_PRINCIPAL_ID=$(az containerapp show \
|
|
231
|
+
-n ca-myapp-dev \
|
|
232
|
+
-g rg-myapp-dev \
|
|
233
|
+
--query identity.principalId -o tsv)
|
|
234
|
+
|
|
235
|
+
# Dar permissão AcrPull
|
|
236
|
+
az role assignment create \
|
|
237
|
+
--assignee $CA_PRINCIPAL_ID \
|
|
238
|
+
--role AcrPull \
|
|
239
|
+
--scope $(az acr show -n $ACR_NAME --query id -o tsv)
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
### Fase 4: Teste e Validação
|
|
245
|
+
|
|
246
|
+
#### 4.1. Smoke Tests
|
|
247
|
+
|
|
248
|
+
```bash
|
|
249
|
+
# Obter URL do Container App
|
|
250
|
+
CA_URL=$(az containerapp show \
|
|
251
|
+
-n ca-myapp-dev \
|
|
252
|
+
-g rg-myapp-dev \
|
|
253
|
+
--query properties.configuration.ingress.fqdn -o tsv)
|
|
254
|
+
|
|
255
|
+
# Testar health checks
|
|
256
|
+
curl https://$CA_URL/health
|
|
257
|
+
curl https://$CA_URL/health/ready
|
|
258
|
+
|
|
259
|
+
# Testar funcionalidade principal
|
|
260
|
+
curl https://$CA_URL/api/status
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
#### 4.2. Load Test (opcional)
|
|
264
|
+
|
|
265
|
+
```bash
|
|
266
|
+
# Instalar hey (HTTP load generator)
|
|
267
|
+
go install github.com/rakyll/hey@latest
|
|
268
|
+
|
|
269
|
+
# Simular carga
|
|
270
|
+
hey -z 30s -c 10 https://$CA_URL/
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
#### 4.3. Validar Auto-Scaling
|
|
274
|
+
|
|
275
|
+
```bash
|
|
276
|
+
# Gerar carga
|
|
277
|
+
hey -z 2m -c 50 https://$CA_URL/
|
|
278
|
+
|
|
279
|
+
# Monitorar réplicas
|
|
280
|
+
watch -n 2 "az containerapp revision list \
|
|
281
|
+
-n ca-myapp-dev \
|
|
282
|
+
-g rg-myapp-dev \
|
|
283
|
+
--query '[0].properties.replicas' -o tsv"
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
### Fase 5: Cutover (Mudança de DNS)
|
|
289
|
+
|
|
290
|
+
#### 5.1. Estratégia Blue-Green
|
|
291
|
+
|
|
292
|
+
```bash
|
|
293
|
+
# Manter App Service rodando (blue)
|
|
294
|
+
# Container Apps já rodando (green)
|
|
295
|
+
|
|
296
|
+
# Testar green profundamente
|
|
297
|
+
# Quando confiante, mudar DNS para green
|
|
298
|
+
# Aguardar 24h
|
|
299
|
+
# Se OK, desligar blue (App Service)
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
#### 5.2. Atualizar DNS
|
|
303
|
+
|
|
304
|
+
Se usando domínio customizado:
|
|
305
|
+
|
|
306
|
+
```
|
|
307
|
+
# Old (App Service)
|
|
308
|
+
myapp.azurewebsites.net → CNAME → myapp.com
|
|
309
|
+
|
|
310
|
+
# New (Container Apps)
|
|
311
|
+
myapp.region.azurecontainerapps.io → CNAME → myapp.com
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
## 💰 Comparativo de Custos
|
|
317
|
+
|
|
318
|
+
### Antes (App Service Free F1)
|
|
319
|
+
```
|
|
320
|
+
App Service F1: $0/mês
|
|
321
|
+
Azure SQL Free: $0/mês
|
|
322
|
+
App Insights Free: $0/mês
|
|
323
|
+
────────────────────────────
|
|
324
|
+
TOTAL: $0/mês
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
### Depois (Container Apps)
|
|
328
|
+
```
|
|
329
|
+
Container Apps: $0-8/mês (scale-to-zero, uso variável)
|
|
330
|
+
ACR Basic: $5/mês
|
|
331
|
+
Azure SQL Free: $0/mês
|
|
332
|
+
App Insights Free: $0/mês
|
|
333
|
+
────────────────────────────
|
|
334
|
+
TOTAL: $5-13/mês
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
### Alternativa (App Service B1)
|
|
338
|
+
```
|
|
339
|
+
App Service B1: $13/mês (always-on)
|
|
340
|
+
Azure SQL Free: $0/mês
|
|
341
|
+
App Insights Free: $0/mês
|
|
342
|
+
────────────────────────────
|
|
343
|
+
TOTAL: $13/mês
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
**Análise:**
|
|
347
|
+
- Container Apps mais econômico que B1 para tráfego variável
|
|
348
|
+
- Container Apps melhor auto-scaling que App Service
|
|
349
|
+
- Container Apps suporta múltiplos containers (arquitetura futura)
|
|
350
|
+
|
|
351
|
+
---
|
|
352
|
+
|
|
353
|
+
## 🔄 Rollback Plan
|
|
354
|
+
|
|
355
|
+
### Se algo der errado
|
|
356
|
+
|
|
357
|
+
#### Opção 1: Rollback via DNS (mais rápido)
|
|
358
|
+
```bash
|
|
359
|
+
# Reverter CNAME para App Service
|
|
360
|
+
# Tempo: ~5 minutos (propagação DNS)
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
#### Opção 2: Rollback via código
|
|
364
|
+
```bash
|
|
365
|
+
# Atualizar parameters.dev.json
|
|
366
|
+
{
|
|
367
|
+
"hostingType": {
|
|
368
|
+
"value": "appservice"
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
# Redeploy
|
|
373
|
+
az deployment group create \
|
|
374
|
+
--resource-group rg-myapp-dev \
|
|
375
|
+
--template-file main.bicep \
|
|
376
|
+
--parameters @parameters.dev.json
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
---
|
|
380
|
+
|
|
381
|
+
## ✅ Checklist Final
|
|
382
|
+
|
|
383
|
+
### Antes de Desligar App Service
|
|
384
|
+
|
|
385
|
+
- [ ] Container Apps rodando estável por 7+ dias
|
|
386
|
+
- [ ] Todos os health checks passando
|
|
387
|
+
- [ ] Monitoramento no App Insights funcionando
|
|
388
|
+
- [ ] Logs sendo coletados corretamente
|
|
389
|
+
- [ ] Performance igual ou melhor que App Service
|
|
390
|
+
- [ ] Custo dentro do esperado
|
|
391
|
+
- [ ] Backup do código e configuração
|
|
392
|
+
- [ ] Equipe treinada no novo ambiente
|
|
393
|
+
|
|
394
|
+
### Pós-Migração
|
|
395
|
+
|
|
396
|
+
- [ ] Deletar App Service Plan (economizar recursos)
|
|
397
|
+
- [ ] Deletar deployment slots antigos
|
|
398
|
+
- [ ] Atualizar documentação interna
|
|
399
|
+
- [ ] Atualizar `.morph/project.md` com nova infra
|
|
400
|
+
- [ ] Arquivar configurações antigas
|
|
401
|
+
|
|
402
|
+
---
|
|
403
|
+
|
|
404
|
+
## 🔄 Automatizar com CI/CD
|
|
405
|
+
|
|
406
|
+
### Azure DevOps Pipelines
|
|
407
|
+
|
|
408
|
+
Após migração manual bem-sucedida, automatize com pipelines:
|
|
409
|
+
|
|
410
|
+
**1. Configurar Workload Identity:**
|
|
411
|
+
```bash
|
|
412
|
+
# Ver guia completo em: .azure/docs/azure-devops-setup.md
|
|
413
|
+
|
|
414
|
+
# Criar App Registration
|
|
415
|
+
az ad app create --display-name "myapp-staging-pipeline"
|
|
416
|
+
|
|
417
|
+
# Configurar federated credential
|
|
418
|
+
az ad app federated-credential create \
|
|
419
|
+
--id <APP_ID> \
|
|
420
|
+
--parameters @federated-credential.json
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
**2. Usar Pipeline Pronto:**
|
|
424
|
+
```bash
|
|
425
|
+
# Copiar pipeline para seu projeto
|
|
426
|
+
cp .azure/pipelines/staging-pipeline.yml azure-pipelines.yml
|
|
427
|
+
|
|
428
|
+
# Configurar variáveis no Azure DevOps:
|
|
429
|
+
# - ACR_NAME
|
|
430
|
+
# - APP_NAME
|
|
431
|
+
# - SUBSCRIPTION_ID
|
|
432
|
+
|
|
433
|
+
# Commit e push
|
|
434
|
+
git add azure-pipelines.yml
|
|
435
|
+
git commit -m "ci: add staging pipeline"
|
|
436
|
+
git push origin main
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
**3. Deploy Automático:**
|
|
440
|
+
- Dev: Triggado automaticamente em push para `develop`
|
|
441
|
+
- Staging: Triggado automaticamente em push para `main`
|
|
442
|
+
- Prod: Manual ou via tag `v*`
|
|
443
|
+
|
|
444
|
+
**Pipelines inclusos no framework:**
|
|
445
|
+
- `.azure/pipelines/dev-pipeline.yml` - App Service Free F1
|
|
446
|
+
- `.azure/pipelines/staging-pipeline.yml` - Container Apps (scale-to-zero)
|
|
447
|
+
- `.azure/pipelines/prod-pipeline.yml` - Container Apps (always-on, com approval)
|
|
448
|
+
|
|
449
|
+
---
|
|
450
|
+
|
|
451
|
+
## 📚 Referências
|
|
452
|
+
|
|
453
|
+
- [Azure Container Apps Docs](https://learn.microsoft.com/azure/container-apps/)
|
|
454
|
+
- [ACR Best Practices](https://learn.microsoft.com/azure/container-registry/container-registry-best-practices)
|
|
455
|
+
- [Dockerfile Best Practices](https://docs.docker.com/develop/dev-best-practices/)
|
|
456
|
+
- [Health Checks ASP.NET](https://learn.microsoft.com/aspnet/core/host-and-deploy/health-checks)
|
|
457
|
+
- [Azure DevOps Workload Identity](https://learn.microsoft.com/azure/devops/pipelines/library/connect-to-azure)
|
|
458
|
+
- [MORPH-SPEC Azure DevOps Setup](.azure/docs/azure-devops-setup.md)
|
|
459
|
+
|
|
460
|
+
---
|
|
461
|
+
|
|
462
|
+
## 🆘 Troubleshooting
|
|
463
|
+
|
|
464
|
+
### Container não inicia
|
|
465
|
+
|
|
466
|
+
```bash
|
|
467
|
+
# Ver logs do container
|
|
468
|
+
az containerapp logs show \
|
|
469
|
+
-n ca-myapp-dev \
|
|
470
|
+
-g rg-myapp-dev \
|
|
471
|
+
--follow
|
|
472
|
+
|
|
473
|
+
# Ver eventos do container
|
|
474
|
+
az containerapp revision list \
|
|
475
|
+
-n ca-myapp-dev \
|
|
476
|
+
-g rg-myapp-dev \
|
|
477
|
+
--query '[0].properties.provisioningState'
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
### Erro de autenticação ACR
|
|
481
|
+
|
|
482
|
+
```bash
|
|
483
|
+
# Verificar permissões Managed Identity
|
|
484
|
+
az role assignment list \
|
|
485
|
+
--assignee $CA_PRINCIPAL_ID \
|
|
486
|
+
--scope $(az acr show -n $ACR_NAME --query id -o tsv)
|
|
487
|
+
|
|
488
|
+
# Recriar permissão se necessário
|
|
489
|
+
az role assignment create \
|
|
490
|
+
--assignee $CA_PRINCIPAL_ID \
|
|
491
|
+
--role AcrPull \
|
|
492
|
+
--scope $(az acr show -n $ACR_NAME --query id -o tsv)
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
### App lento ou timeout
|
|
496
|
+
|
|
497
|
+
```bash
|
|
498
|
+
# Verificar recursos
|
|
499
|
+
az containerapp show \
|
|
500
|
+
-n ca-myapp-dev \
|
|
501
|
+
-g rg-myapp-dev \
|
|
502
|
+
--query 'properties.template.containers[0].resources'
|
|
503
|
+
|
|
504
|
+
# Aumentar CPU/memória se necessário
|
|
505
|
+
az containerapp update \
|
|
506
|
+
-n ca-myapp-dev \
|
|
507
|
+
-g rg-myapp-dev \
|
|
508
|
+
--cpu 0.5 \
|
|
509
|
+
--memory 1Gi
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
---
|
|
513
|
+
|
|
514
|
+
*MORPH-SPEC by Polymorphism Tech*
|