Add Spring Boot to Azure Container Apps migration skill#1569
Add Spring Boot to Azure Container Apps migration skill#1569deepganguly wants to merge 13 commits intomicrosoft:mainfrom
Conversation
New skill: spring-apps-to-aca - Migrate Spring Boot applications from Azure Spring Apps or any deployment to Azure Container Apps - Based on official Microsoft Learn documentation - Includes pre-migration assessment, containerization, deployment, and optimization Files added: - SKILL.md: Main skill definition (~480 tokens) - LICENSE.txt: MIT License - references/assessment-guide.md: Pre-migration assessment checklist (~1,850 tokens) - references/deployment-guide.md: Step-by-step deployment guide (~1,950 tokens) Features: - Pre-migration assessment (local state, file system, platform compatibility) - External resources inventory (databases, Redis, message brokers, identity providers) - Scheduled jobs assessment and migration strategies - Containerization with Dockerfile examples - Azure Container Apps deployment with Bash and PowerShell scripts - Key Vault integration for secrets management - Storage mounts for persistent data - Spring Cloud components integration (Config Server, Eureka, Gateway, Admin) - CI/CD examples (GitHub Actions, Azure Pipelines) - Comprehensive troubleshooting guide Token limits met: - SKILL.md: ~480/500 tokens ✓ - assessment-guide.md: ~1,850/2,000 tokens ✓ - deployment-guide.md: ~1,950/2,000 tokens ✓ Registered in tests/skills.json skills array and integration schedule.
There was a problem hiding this comment.
Pull request overview
Adds a new agent skill (spring-apps-to-aca) to guide migration of Spring Boot applications to Azure Container Apps, and registers it for scheduled integration testing.
Changes:
- Added new skill
spring-apps-to-acawith core workflow and MCP tool pointers. - Added reference docs for pre-migration assessment and deployment steps.
- Registered the new skill in
tests/skills.jsonskills list and integration test schedule.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/skills.json | Registers spring-apps-to-aca in the skills list and scheduled integration runs. |
| plugin/skills/spring-apps-to-aca/SKILL.md | Defines the skill frontmatter and high-level migration workflow + MCP tools. |
| plugin/skills/spring-apps-to-aca/references/assessment-guide.md | Adds a checklist-style pre-migration assessment guide (state, platform, dependencies). |
| plugin/skills/spring-apps-to-aca/references/deployment-guide.md | Adds step-by-step deployment guidance (env setup, containerization, Key Vault, deploy, optimization). |
| plugin/skills/spring-apps-to-aca/LICENSE.txt | Adds MIT license for the new skill content. |
| $acr = az acr show --name $ACR_NAME | ConvertFrom-Json | ||
| $ACR_ID = $acr.id | ||
| az role assignment create --assignee $PRINCIPAL_ID --role AcrPull --scope $ACR_ID |
There was a problem hiding this comment.
In the PowerShell Phase 5 snippet, $ACR_NAME is used in az acr show --name $ACR_NAME but it is never set within this phase (unlike Phase 3). If Phase 5 is run standalone, this will fail; consider defining $ACR_NAME here (or explicitly referencing the Phase 3 prerequisite).
| az storage account create --name springstore --resource-group spring-rg --location eastus --sku Standard_LRS | ||
| STORAGE_KEY=$(az storage account keys list --account-name springstore --resource-group spring-rg --query "[0].value" -o tsv) | ||
| az storage share create --name spring-data --account-name springstore --account-key "$STORAGE_KEY" | ||
| az containerapp env storage set --name spring-env --resource-group spring-rg --storage-name spring-storage --azure-file-account-name springstore --azure-file-account-key "$STORAGE_KEY" --azure-file-share-name spring-data --access-mode ReadWrite |
There was a problem hiding this comment.
az storage account create --name springstore uses a fixed storage account name, but storage account names must be globally unique; this will frequently fail when readers copy/paste. Use a <storage-account-name> placeholder or generate a unique suffix (and mention naming constraints).
| az storage account create --name springstore --resource-group spring-rg --location eastus --sku Standard_LRS | |
| STORAGE_KEY=$(az storage account keys list --account-name springstore --resource-group spring-rg --query "[0].value" -o tsv) | |
| az storage share create --name spring-data --account-name springstore --account-key "$STORAGE_KEY" | |
| az containerapp env storage set --name spring-env --resource-group spring-rg --storage-name spring-storage --azure-file-account-name springstore --azure-file-account-key "$STORAGE_KEY" --azure-file-share-name spring-data --access-mode ReadWrite | |
| # Set a globally unique storage account name: | |
| # - 3–24 characters | |
| # - lowercase letters and numbers only | |
| # - must be globally unique across Azure | |
| STORAGE_ACCOUNT_NAME="${STORAGE_ACCOUNT_NAME:-<storage-account-name>}" | |
| az storage account create --name "$STORAGE_ACCOUNT_NAME" --resource-group spring-rg --location eastus --sku Standard_LRS | |
| STORAGE_KEY=$(az storage account keys list --account-name "$STORAGE_ACCOUNT_NAME" --resource-group spring-rg --query "[0].value" -o tsv) | |
| az storage share create --name spring-data --account-name "$STORAGE_ACCOUNT_NAME" --account-key "$STORAGE_KEY" | |
| az containerapp env storage set --name spring-env --resource-group spring-rg --storage-name spring-storage --azure-file-account-name "$STORAGE_ACCOUNT_NAME" --azure-file-account-key "$STORAGE_KEY" --azure-file-share-name spring-data --access-mode ReadWrite |
| - Java 8, 11, 17, 21 (verify with `java -version`) | ||
|
|
||
| ### Spring Boot Version Requirements | ||
| - **Required**: Spring Boot 3.x | ||
| - **Migration needed**: Spring Boot 2.x → follow [Spring Boot 3.0 Migration Guide](https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.0-Migration-Guide) |
There was a problem hiding this comment.
This section lists Java 8/11 as supported, but the same document states Spring Boot 3.x is required. Spring Boot 3.x requires Java 17+; either relax the Spring Boot requirement or update the supported Java versions to align (and call out Boot 2.x vs 3.x implications).
| - Java 8, 11, 17, 21 (verify with `java -version`) | |
| ### Spring Boot Version Requirements | |
| - **Required**: Spring Boot 3.x | |
| - **Migration needed**: Spring Boot 2.x → follow [Spring Boot 3.0 Migration Guide](https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.0-Migration-Guide) | |
| - **For Spring Boot 2.x source apps**: Java 8 or 11 (LTS) supported for assessment and migration | |
| - **For Spring Boot 3.x target apps**: Java 17 or 21 (LTS) required (verify with `java -version`) | |
| ### Spring Boot Version Requirements | |
| - **Recommended target**: Spring Boot 3.x (requires Java 17+) | |
| - **Supported source**: Spring Boot 2.x on Java 8/11 → plan upgrade to Java 17+ and follow the [Spring Boot 3.0 Migration Guide](https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.0-Migration-Guide) |
|
|
||
| **application.properties:** | ||
| ```properties | ||
| spring.activemq.brokerurl=broker:(tcp://localhost:61616) |
There was a problem hiding this comment.
The ActiveMQ property key spring.activemq.brokerurl in this example doesn’t match Spring Boot’s standard configuration key (spring.activemq.broker-url). Using the wrong key will cause readers’ configuration to be ignored.
| spring.activemq.brokerurl=broker:(tcp://localhost:61616) | |
| spring.activemq.broker-url=broker:(tcp://localhost:61616) |
| |----------|---------------| | ||
| | OAuth2 | Spring Security reference docs | | ||
| | Auth0 | Auth0 Spring Security documentation | | ||
| | PingFederate | Auth0 PingFederate instructions | |
There was a problem hiding this comment.
The PingFederate row points to “Auth0 PingFederate instructions”, which appears incorrect/misleading (PingFederate isn’t an Auth0 product). Consider linking to PingIdentity/PingFederate documentation or relevant Spring Security SAML/OIDC docs instead.
| | PingFederate | Auth0 PingFederate instructions | | |
| | PingFederate | [Ping Identity PingFederate docs](https://docs.pingidentity.com/bundle/pingfederate/page/pf_c_oview.html), [Spring Security SAML 2.0](https://docs.spring.io/spring-security/reference/servlet/saml2/index.html) | |
| | File system writes | Use Azure Files storage mounts or Azure Blob Storage | | ||
| | Platform incompatibility | Upgrade to Java 8/11/17/21, Spring Boot 3.x | | ||
|
|
There was a problem hiding this comment.
The error handling guidance suggests upgrading to “Java 8/11/17/21” while also stating Spring Boot 3.x. Spring Boot 3.x requires Java 17+, so listing Java 8/11 here is inconsistent and could mislead users about supported runtimes.
| SECRET_FILE=$(mktemp) | ||
| trap 'shred -u "$SECRET_FILE" 2>/dev/null || rm -f "$SECRET_FILE"' EXIT | ||
| echo -n "your-db-password" > "$SECRET_FILE" | ||
| az keyvault secret set --vault-name spring-kv --name db-password --file "$SECRET_FILE" |
There was a problem hiding this comment.
In this Bash snippet, $ACR_NAME is referenced when computing ACR_ID, but it isn’t set in Phase 5. With set -euo pipefail, this will exit if the user runs Phase 5 independently. Define ACR_NAME in this snippet (or restate the prerequisite from Phase 3) before using it.
| az keyvault secret set --vault-name spring-kv --name db-password --file "$SECRET_FILE" | |
| az keyvault secret set --vault-name spring-kv --name db-password --file "$SECRET_FILE" | |
| # Name of the Azure Container Registry created earlier (for example, in Phase 3) | |
| ACR_NAME="<your-acr-name-from-phase-3>" |
| az keyvault create --name spring-kv --resource-group spring-rg --location eastus | ||
| IDENTITY_ID=$(az identity create --name spring-id --resource-group spring-rg --location eastus --query id -o tsv) | ||
| PRINCIPAL_ID=$(az identity show --ids "$IDENTITY_ID" --query principalId -o tsv) | ||
| az keyvault set-policy --name spring-kv --object-id "$PRINCIPAL_ID" --secret-permissions get list |
There was a problem hiding this comment.
az keyvault create --name spring-kv hard-codes the Key Vault name. Key Vault names must be globally unique, so this is likely to fail for users copy/pasting the guide. Use a placeholder or add guidance to generate a unique name.
| echo -n "your-db-password" > "$SECRET_FILE" | ||
| az keyvault secret set --vault-name spring-kv --name db-password --file "$SECRET_FILE" | ||
| ACR_ID=$(az acr show --name "$ACR_NAME" --query id -o tsv) |
There was a problem hiding this comment.
The example writes the literal string your-db-password into a temp file and uploads it to Key Vault. Since this is documentation meant for copy/paste, it’s safer/clearer to use an explicit placeholder (e.g., <db-password>) and/or show az keyvault secret set --value with a placeholder to avoid readers accidentally storing the sample value.
| ```properties | ||
| spring.datasource.url=jdbc:mysql://localhost:3306/mydb | ||
| spring.datasource.username=dbuser | ||
| spring.datasource.driver-class-name=com.mysql.jdbc.Driver |
There was a problem hiding this comment.
The MySQL driver class com.mysql.jdbc.Driver is deprecated and may not work as expected with modern Connector/J versions (Spring Boot 3.x typically uses com.mysql.cj.jdbc.Driver, and often you can omit driver-class-name entirely). Updating the example avoids confusing readers.
| spring.datasource.driver-class-name=com.mysql.jdbc.Driver | |
| spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver |
1. Java version alignment: - SKILL.md: Updated error handling to Java 17/21 only (Boot 3.x requirement) - assessment-guide.md: Clarified Java 8/11 for Boot 2.x source, Java 17/21 for Boot 3.x target 2. Globally unique resource names with variables: - Phase 4: Storage account uses STORAGE_ACCOUNT variable with naming guidance - Phase 5: Key Vault uses KEY_VAULT variable with naming guidance (3-24 chars) - Phase 6: Reference KEY_VAULT variable instead of hardcoded 'spring-kv' - Troubleshooting: Use KEY_VAULT variable 3. Variable definitions for standalone execution: - Phase 5 Bash: Added ACR_NAME and KEY_VAULT variable definitions - Phase 5 PowerShell: Added ACR_NAME and KEY_VAULT variable definitions 4. Fixed configuration property keys: - assessment-guide.md: spring.activemq.brokerurl → spring.activemq.broker-url - assessment-guide.md: com.mysql.jdbc.Driver → com.mysql.cj.jdbc.Driver (modern driver) 5. Fixed documentation references: - assessment-guide.md: PingFederate now references Spring Security SAML/OIDC docs 6. Secure placeholder values: - Phase 5: Changed 'your-db-password' to '<your-db-password>' placeholder All phases now use environment variables with fallback placeholders and include naming constraints for globally unique Azure resources.
|
Add skill invocation, unit and trigger tests |
|
|
||
| **Azure Files for persistent storage:** | ||
|
|
||
| **Bash:** |
|
|
||
| ## Phase 6: Deploy Container App | ||
|
|
||
| **Bash:** |
| ## Phase 7: Validation | ||
|
|
||
| **Check app status:** | ||
| ```bash |
| ### Add Spring Cloud Config Server | ||
|
|
||
| **Bash:** | ||
| ```bash |
| version: "1.0.0" | ||
| author: Microsoft | ||
| --- | ||
|
|
There was a problem hiding this comment.
@deepganguly - Here's some example of trigger phrases - https://github.com/microsoft/GitHub-Copilot-for-Azure/blob/main/plugin/skills/azure-prepare/SKILL.md#triggers
|
- Add comprehensive test suite (unit, trigger, integration tests) - Add PowerShell scripts for Phase 6 (Deploy Container App) - Add PowerShell scripts for Phase 7 (Validation) - Add PowerShell scripts for Spring Cloud Config Server - Expand trigger phrases in SKILL.md description for better skill activation Addresses reviewer feedback from @kvenkatrajan
- Remove generic phrases conflicting with azure-prepare: 'modernize Spring applications', 'containerize Spring Boot', 'deploy Spring Boot containers' - Focus trigger phrases on migration-specific scenarios: 'migrate existing Spring Boot app', 'Spring Boot platform migration', 'replatform Spring Boot to ACA' - Add DO NOT USE FOR section clarifying boundaries with azure-prepare and azure-cloud-migrate - Emphasize 'existing' apps in When to Use section to distinguish from new deployments Addresses skill routing concern from PR review
| description: "Migrate Spring Boot applications from Azure Spring Apps or any Spring Boot deployment to Azure Container Apps with pre-migration assessment, deployment automation, and cloud-native optimization. WHEN: migrate Spring Boot to Container Apps, move Azure Spring Apps to ACA, Azure Spring Apps to Container Apps migration, Spring Boot migration to ACA, migrate Spring microservices to ACA, convert Spring Boot JAR to container on ACA, Spring Boot ACA migration assessment, move Spring Boot from VM to Container Apps, Spring Boot to ACA conversion, Azure Spring Apps migration assessment, migrate existing Spring Boot app, Spring Boot platform migration, replatform Spring Boot to ACA." | ||
| license: MIT | ||
| metadata: | ||
| version: "1.0.0" |
There was a problem hiding this comment.
The frontmatter description is far longer than the unit test constraint in tests/spring-apps-to-aca/unit.test.ts (expects < 500 chars) and likely exceeds the repo’s usual 1024-char guideline. This will fail CI; please shorten the description to a concise summary + a small set of trigger phrases and update snapshots accordingly.
| description: "Migrate Spring Boot applications from Azure Spring Apps or any Spring Boot deployment to Azure Container Apps with pre-migration assessment, deployment automation, and cloud-native optimization. WHEN: migrate Spring Boot to Container Apps, move Azure Spring Apps to ACA, Azure Spring Apps to Container Apps migration, Spring Boot migration to ACA, migrate Spring microservices to ACA, convert Spring Boot JAR to container on ACA, Spring Boot ACA migration assessment, move Spring Boot from VM to Container Apps, Spring Boot to ACA conversion, Azure Spring Apps migration assessment, migrate existing Spring Boot app, Spring Boot platform migration, replatform Spring Boot to ACA." | |
| license: MIT | |
| metadata: | |
| version: "1.0.0" | |
| description: "Migrate existing Spring Boot applications from Azure Spring Apps, VMs, or other platforms to Azure Container Apps with assessment, containerization, and deployment guidance. Trigger phrases: migrate Spring Boot to Container Apps; Azure Spring Apps to ACA; Spring Boot migration assessment." | |
| license: MIT | |
| metadata: | |
| version: "1.0.1" |
| ## Quick Reference | ||
|
|
||
| | Item | Details | | ||
| |------|---------| | ||
| | **Source** | Spring Boot (Azure Spring Apps, on-prem, other cloud) | | ||
| | **Target** | Azure Container Apps | | ||
| | **Steps** | Assess → Containerize → Deploy → Optimize | |
There was a problem hiding this comment.
The markdown tables are using a double leading pipe (|| ...) which creates an unintended empty first column and renders inconsistently compared to other skills (which use | ... |). Replace || with a single | in these tables.
| version: "1.0.0" | ||
| author: Microsoft |
There was a problem hiding this comment.
For consistency with other skills’ frontmatter, metadata typically lists author before version (e.g., plugin/skills/azure-compliance/SKILL.md). Consider reordering these keys to match the common pattern.
| version: "1.0.0" | |
| author: Microsoft | |
| author: Microsoft | |
| version: "1.0.1" |
| ```bash | ||
| #!/bin/bash | ||
| set -euo pipefail | ||
| KEY_VAULT="${KEY_VAULT:-<keyvault-name>}" | ||
| SECRET_URI=$(az keyvault secret show --vault-name "$KEY_VAULT" --name db-password --query id -o tsv) | ||
| az containerapp create --name spring-app --resource-group spring-rg --environment spring-env \ | ||
| --image "${ACR_NAME}.azurecr.io/spring-app:v1.0" --target-port 8080 --ingress external \ | ||
| --cpu 2.0 --memory 4Gi --min-replicas 2 --max-replicas 10 \ | ||
| --user-assigned "$IDENTITY_ID" --registry-identity "$IDENTITY_ID" --registry-server "${ACR_NAME}.azurecr.io" \ | ||
| --secrets db-password=keyvaultref:"${SECRET_URI}",identityref:"${IDENTITY_ID}" \ | ||
| --env-vars SPRING_DATASOURCE_PASSWORD=secretref:db-password SPRING_PROFILES_ACTIVE=prod | ||
| ``` |
There was a problem hiding this comment.
Phase 6 Bash snippet uses ACR_NAME and IDENTITY_ID but doesn’t define them, and set -u will cause the script to fail if the user runs this phase independently. Define these variables in the snippet (or explicitly require exporting them from earlier phases) before az containerapp create.
| ## Phase 4: Configure Storage (if needed) | ||
|
|
||
| **Azure Files for persistent storage:** | ||
|
|
||
| **Bash:** | ||
| ```bash | ||
| #!/bin/bash | ||
| set -euo pipefail | ||
| # Storage account name must be globally unique: 3-24 chars, lowercase letters/numbers only | ||
| STORAGE_ACCOUNT="${STORAGE_ACCOUNT:-<storage-account>}" | ||
| az storage account create --name "$STORAGE_ACCOUNT" --resource-group spring-rg --location eastus --sku Standard_LRS | ||
| STORAGE_KEY=$(az storage account keys list --account-name "$STORAGE_ACCOUNT" --resource-group spring-rg --query "[0].value" -o tsv) | ||
| az storage share create --name spring-data --account-name "$STORAGE_ACCOUNT" --account-key "$STORAGE_KEY" | ||
| az containerapp env storage set --name spring-env --resource-group spring-rg --storage-name spring-storage --azure-file-account-name "$STORAGE_ACCOUNT" --azure-file-account-key "$STORAGE_KEY" --azure-file-share-name spring-data --access-mode ReadWrite | ||
| ``` |
There was a problem hiding this comment.
Phase 4 (storage) provides only a Bash script. For cross-platform parity (and to match the rest of this guide), add the equivalent PowerShell steps for creating the storage account/share and configuring az containerapp env storage set.
| spring: | ||
| data: | ||
| mongodb: | ||
| uri: mongodb://user:pass@server:27017 |
There was a problem hiding this comment.
This example includes an inline username/password in a connection string (mongodb://user:pass@...). Even as sample data, it’s better to use placeholders like <username>:<password> to avoid normalizing credential-in-config patterns.
| uri: mongodb://user:pass@server:27017 | |
| uri: mongodb://<username>:<password>@<host>:<port> |
| import { | ||
| useAgentRunner, | ||
| areToolCallsSuccess, | ||
| doesAssistantMessageIncludeKeyword, | ||
| shouldSkipIntegrationTests | ||
| } from "../utils/agent-runner"; | ||
| import { isSkillInvoked, softCheckSkill, shouldEarlyTerminateForSkillInvocation, withTestResult } from "../utils/evaluate"; |
There was a problem hiding this comment.
areToolCallsSuccess is imported but never used; the tests repo’s ESLint config treats unused imports as errors. Remove the import or add a test assertion that uses it (as shown in tests/_template/integration.test.ts).
| exports[`spring-apps-to-aca - Trigger Tests Trigger Keywords Snapshot skill description triggers match snapshot 1`] = ` | ||
| { | ||
| "description": "Migrate Spring Boot applications from Azure Spring Apps or any Spring Boot deployment to Azure Container Apps with pre-migration assessment, deployment automation, and cloud-native optimization. WHEN: migrate Spring Boot to Container Apps, move Azure Spring Apps to ACA, modernize Spring applications, containerize Spring Boot, migrate microservices to Azure.", | ||
| "extractedKeywords": [ | ||
| "aca", | ||
| "applications", | ||
| "apps", | ||
| "assessment", | ||
| "automation", | ||
| "azure", | ||
| "boot", | ||
| "cloud-native", | ||
| "container", | ||
| "containerize", | ||
| "cosmos", | ||
| "deploy", | ||
| "deployment", | ||
| "from", | ||
| "key vault", | ||
| "mcp", | ||
| "microservices", | ||
| "migrate", | ||
| "modernize", | ||
| "move", | ||
| "optimization", | ||
| "pre-migration", | ||
| "spring", | ||
| "sql", | ||
| "storage", | ||
| "when", | ||
| "with", | ||
| ], | ||
| "name": "spring-apps-to-aca", | ||
| } |
There was a problem hiding this comment.
The stored snapshot description/keywords don’t match the current SKILL.md frontmatter (the snapshot has a much shorter description and different extracted keywords). Regenerate/update the snapshot after finalizing the SKILL.md description so triggers.test.ts is deterministic and passes.
| **Bash with storage mount:** | ||
| ```bash | ||
| az containerapp create --name spring-app --resource-group spring-rg --environment spring-env \ | ||
| --image "${ACR_NAME}.azurecr.io/spring-app:v1.0" --target-port 8080 --ingress external \ | ||
| --cpu 2.0 --memory 4Gi --min-replicas 2 --max-replicas 10 \ | ||
| --user-assigned "$IDENTITY_ID" --registry-identity "$IDENTITY_ID" --registry-server "${ACR_NAME}.azurecr.io" \ | ||
| --secrets db-password=keyvaultref:"${SECRET_URI}",identityref:"${IDENTITY_ID}" \ | ||
| --env-vars SPRING_DATASOURCE_PASSWORD=secretref:db-password \ | ||
| --bind-storage-name spring-storage --mount-path /mnt/data | ||
| ``` |
There was a problem hiding this comment.
The "Bash with storage mount" command also relies on ACR_NAME, IDENTITY_ID, and SECRET_URI without defining them locally. If these are meant to be carried over from earlier phases, call that out explicitly (or re-declare them) to avoid copy/paste failures.
|
@copilot apply changes based on the comments in this thread |
Remove unused 'areToolCallsSuccess' import to address CodeQL warning
|
|
||
| test("description is concise and actionable", () => { | ||
| expect(skill.metadata.description.length).toBeGreaterThan(50); | ||
| expect(skill.metadata.description.length).toBeLessThan(500); |
There was a problem hiding this comment.
The test asserts the SKILL.md frontmatter description is < 500 chars, but the skill’s current description line in plugin/skills/spring-apps-to-aca/SKILL.md appears substantially longer (long WHEN trigger list). This will make the unit test fail unless the description is shortened or the threshold is increased (consistent with other skills like tests/azure-cost/unit.test.ts which allows up to 1000).
| expect(skill.metadata.description.length).toBeLessThan(500); | |
| expect(skill.metadata.description.length).toBeLessThan(1000); |
| exports[`spring-apps-to-aca - Trigger Tests Trigger Keywords Snapshot skill description triggers match snapshot 1`] = ` | ||
| { | ||
| "description": "Migrate Spring Boot applications from Azure Spring Apps or any Spring Boot deployment to Azure Container Apps with pre-migration assessment, deployment automation, and cloud-native optimization. WHEN: migrate Spring Boot to Container Apps, move Azure Spring Apps to ACA, modernize Spring applications, containerize Spring Boot, migrate microservices to Azure.", | ||
| "extractedKeywords": [ | ||
| "aca", | ||
| "applications", | ||
| "apps", | ||
| "assessment", | ||
| "automation", | ||
| "azure", | ||
| "boot", | ||
| "cloud-native", | ||
| "container", | ||
| "containerize", | ||
| "cosmos", | ||
| "deploy", | ||
| "deployment", | ||
| "from", | ||
| "key vault", | ||
| "mcp", | ||
| "microservices", | ||
| "migrate", | ||
| "modernize", | ||
| "move", | ||
| "optimization", | ||
| "pre-migration", | ||
| "spring", | ||
| "sql", | ||
| "storage", | ||
| "when", | ||
| "with", | ||
| ], | ||
| "name": "spring-apps-to-aca", | ||
| } |
There was a problem hiding this comment.
The stored snapshot doesn’t match the current skill metadata/keyword extraction logic. For example, TriggerMatcher adds keywords like "aks"/"kubernetes" when they appear in skill.content, but those entries are missing here, and the description string shown doesn’t match the current SKILL.md description. Regenerate/update the snapshot after finalizing SKILL.md content to avoid failing tests.
| ## MCP Tools | ||
|
|
||
| | Tool | Parameters | | ||
| |------|-----------| | ||
| | `mcp_azure_mcp_documentation` | `resource: "container-apps"` | | ||
| | `mcp_azure_mcp_get_bestpractices` | `resource: "container-apps"`, `action: "deploy"` | | ||
|
|
There was a problem hiding this comment.
The MCP Tools table has the same malformed Markdown table syntax (extra leading ||), which can break rendering. Use standard Markdown table pipes (| Tool | ... |) to match the rest of the repo’s skill docs.
| ## Error Handling | ||
|
|
||
| | Error | Resolution | | ||
| |-------|------------| | ||
| | Local state issues | Migrate to Azure Cache, Cosmos DB, or Azure SQL | | ||
| | File system writes | Use Azure Files storage mounts or Azure Blob Storage | | ||
| | Platform incompatibility | Upgrade to Java 17/21, Spring Boot 3.x | |
There was a problem hiding this comment.
The Error Handling table also uses an extra leading pipe (|| Error | Resolution |), which is inconsistent with standard Markdown and other skills’ tables. Fix the table formatting so it renders correctly.
| | Issue | Impact | Solution | | ||
| |-------|--------|----------| | ||
| | Singleton patterns | Multiple instances may run during updates | Refactor to stateless design | | ||
| | In-memory sessions | Lost during restarts/scaling | Migrate to Azure Cache for Redis | | ||
| | Local caching | Not shared across replicas | Use Azure Cache for Redis with Spring Data Redis | | ||
| | File-based state | Lost on restart | Migrate to Azure Cosmos DB, Azure SQL, or Azure Storage | |
There was a problem hiding this comment.
Markdown tables in this guide use a non-standard || prefix (e.g., || Issue | Impact | Solution |), which renders with an empty first column in typical Markdown viewers. Other repo docs use standard | ... | ... | table formatting; update these tables (and the other || tables below) for consistent rendering.
| | Issue | Solution | | ||
| |-------|----------| | ||
| | Image pull fails | Verify ACR role: `az role assignment list --assignee $PRINCIPAL_ID --scope $ACR_ID` | | ||
| | App won't start | Check logs: `az containerapp logs show --name spring-app -g spring-rg --tail 100` | | ||
| | Health check fails | Verify port 8080 matches `server.port` in application.properties | | ||
| | Secrets not accessible | Check Key Vault policy: `az keyvault show --name $KEY_VAULT --query properties.accessPolicies` | | ||
| | Storage mount fails | Verify storage configuration: `az containerapp env storage list --name spring-env -g spring-rg` | | ||
| | High memory usage | Reduce max heap: add `--env-vars JAVA_OPTS="-Xmx2g"` to container app | |
There was a problem hiding this comment.
The Troubleshooting table uses ||-prefixed Markdown rows (extra leading pipe), which typically renders as an empty first column. For consistent rendering across the repo, switch to standard | Issue | Solution | table syntax.
| ## Phase 4: Configure Storage (if needed) | ||
|
|
||
| **Azure Files for persistent storage:** | ||
|
|
||
| **Bash:** | ||
| ```bash | ||
| #!/bin/bash | ||
| set -euo pipefail | ||
| # Storage account name must be globally unique: 3-24 chars, lowercase letters/numbers only | ||
| STORAGE_ACCOUNT="${STORAGE_ACCOUNT:-<storage-account>}" | ||
| az storage account create --name "$STORAGE_ACCOUNT" --resource-group spring-rg --location eastus --sku Standard_LRS | ||
| STORAGE_KEY=$(az storage account keys list --account-name "$STORAGE_ACCOUNT" --resource-group spring-rg --query "[0].value" -o tsv) | ||
| az storage share create --name spring-data --account-name "$STORAGE_ACCOUNT" --account-key "$STORAGE_KEY" | ||
| az containerapp env storage set --name spring-env --resource-group spring-rg --storage-name spring-storage --azure-file-account-name "$STORAGE_ACCOUNT" --azure-file-account-key "$STORAGE_KEY" --azure-file-share-name spring-data --access-mode ReadWrite | ||
| ``` |
There was a problem hiding this comment.
Phase 4 provides only a Bash script for storage mount setup, but the rest of the guide provides both Bash and PowerShell variants for non-trivial scripts. Add the equivalent PowerShell commands here (or explicitly state that only Bash is supported for this phase) to keep the guide consistent and usable across shells.
| ```bash | ||
| #!/bin/bash | ||
| set -euo pipefail | ||
| KEY_VAULT="${KEY_VAULT:-<keyvault-name>}" | ||
| SECRET_URI=$(az keyvault secret show --vault-name "$KEY_VAULT" --name db-password --query id -o tsv) | ||
| az containerapp create --name spring-app --resource-group spring-rg --environment spring-env \ | ||
| --image "${ACR_NAME}.azurecr.io/spring-app:v1.0" --target-port 8080 --ingress external \ | ||
| --cpu 2.0 --memory 4Gi --min-replicas 2 --max-replicas 10 \ | ||
| --user-assigned "$IDENTITY_ID" --registry-identity "$IDENTITY_ID" --registry-server "${ACR_NAME}.azurecr.io" \ | ||
| --secrets db-password=keyvaultref:"${SECRET_URI}",identityref:"${IDENTITY_ID}" \ | ||
| --env-vars SPRING_DATASOURCE_PASSWORD=secretref:db-password SPRING_PROFILES_ACTIVE=prod | ||
| ``` |
There was a problem hiding this comment.
The Phase 6 Bash deployment snippet references ${ACR_NAME} and ${IDENTITY_ID} but doesn’t set them in that snippet (unlike the PowerShell version). Since each phase is presented as a copy/paste block, this will fail unless the reader carried variables forward. Define these variables in the snippet (or explicitly document required exports/inputs) and apply the same fix to the “Bash with storage mount” block below.
Replace TypeScript 'any' type casts with explicit type interfaces to address ESLint errors:
- skill.metadata.metadata as { version?: string }
- skill.metadata.metadata as { author?: string }
- Trimmed description from 679 to 413 characters - Kept migration-specific trigger phrases to avoid routing conflicts - Updated 2 snapshot tests to match new keywords
| - **Cross-cloud migration assessment** (AWS/GCP to Azure) → use `azure-cloud-migrate` | ||
| - **Creating new Spring Boot projects** from scratch → use `azure-prepare` | ||
| - **AKS or Kubernetes deployments** → use `azure-kubernetes` or `k8s-to-container-apps` |
There was a problem hiding this comment.
k8s-to-container-apps is referenced here, but there is no corresponding skill directory under plugin/skills/ and it is not registered in tests/skills.json. This will send users to a non-existent skill; please remove it or replace with an existing skill name.
| KEY_VAULT="${KEY_VAULT:-<keyvault-name>}" | ||
| SECRET_URI=$(az keyvault secret show --vault-name "$KEY_VAULT" --name db-password --query id -o tsv) | ||
| az containerapp create --name spring-app --resource-group spring-rg --environment spring-env \ | ||
| --image "${ACR_NAME}.azurecr.io/spring-app:v1.0" --target-port 8080 --ingress external \ | ||
| --cpu 2.0 --memory 4Gi --min-replicas 2 --max-replicas 10 \ |
There was a problem hiding this comment.
This Bash snippet uses ACR_NAME and IDENTITY_ID but doesn’t define them in the script (unlike the PowerShell version). If Phase 6 is run independently, the command will fail; please define the variables here or explicitly note they must be exported from earlier phases.
| ## Phase 4: Configure Storage (if needed) | ||
|
|
||
| **Azure Files for persistent storage:** | ||
|
|
||
| **Bash:** |
There was a problem hiding this comment.
Phase 4 provides only a Bash script, but the rest of this guide generally offers both Bash and PowerShell variants. Please add a PowerShell equivalent for the storage setup (or explicitly state why it’s omitted) to keep the guide runnable cross-platform.
|
@copilot apply changes based on the comments in this thread |
…valid skill reference, add Phase 6 variables, improve integration test prompt
kvenkatrajan
left a comment
There was a problem hiding this comment.
This should be in azure-cloud-migrate, not a separate skill.
| version: "1.0.0" | ||
| author: Microsoft | ||
| --- | ||
|
|
There was a problem hiding this comment.
@deepganguly - Here's some example of trigger phrases - https://github.com/microsoft/GitHub-Copilot-for-Azure/blob/main/plugin/skills/azure-prepare/SKILL.md#triggers
…grate skill - Add Spring Boot → Container Apps migration scenario - Create references/services/container-apps/spring-apps-to-aca.md - Copy assessment-guide.md and deployment-guide.md (optimized <2000 tokens) - Update azure-cloud-migrate triggers for Spring Boot migrations - Add 3 Spring Apps-specific unit tests - Add 8 Spring Apps trigger tests - Add 2 Spring Apps integration tests - Remove standalone spring-apps-to-aca skill - Update tests/skills.json registry - All 43 tests passing with zero coverage loss - Description optimized to 58 words (60 word limit)
|
|
||
| test("includes Azure Spring Apps source in scenario table", () => { | ||
| const content = skill.content; | ||
| expect(content).toMatch(/Spring Boot|Azure Spring Apps/); |
There was a problem hiding this comment.
Test intent says it should verify the scenario table includes Azure Spring Apps as a source, but the regex /Spring Boot|Azure Spring Apps/ will pass if either phrase exists (and earlier assertions already ensure "Spring Boot"), so this test can pass even if "Azure Spring Apps" is missing. Update the assertion to require "Azure Spring Apps" (or verify both terms) so it actually guards the intended behavior.
| expect(content).toMatch(/Spring Boot|Azure Spring Apps/); | |
| expect(content).toContain("Azure Spring Apps"); |
| --- | ||
| name: azure-cloud-migrate | ||
| description: "Assess and migrate cross-cloud workloads to Azure with migration reports and code conversion guidance. Supports AWS, GCP, and other providers. WHEN: migrate Lambda to Azure Functions, migrate AWS to Azure, Lambda migration assessment, convert AWS serverless to Azure, migration readiness report, migrate from AWS, migrate from GCP, cross-cloud migration." | ||
| description: "Assess and migrate workloads to Azure: AWS Lambda→Functions, Spring Boot→Container Apps. WHEN: migrate Lambda to Functions, AWS to Azure, Lambda assessment, cross-cloud migration, migrate Spring Boot to Container Apps, Azure Spring Apps to ACA, Spring Boot ACA migration." | ||
| license: MIT | ||
| metadata: | ||
| author: Microsoft | ||
| version: "1.0.1" | ||
| version: "1.0.2" |
There was a problem hiding this comment.
PR description says this change adds a new standalone skill (and registers it in tests/skills.json), but this diff only extends the existing azure-cloud-migrate skill and there is no spring-apps-to-aca entry in tests/skills.json. Please either update the PR description to match the implementation, or add the new skill + registration as described.
| | Source | Target | Reference | | ||
| |--------|--------|-----------| | ||
| | AWS Lambda | Azure Functions | [lambda-to-functions.md](references/services/functions/lambda-to-functions.md) | | ||
| | Spring Boot (Azure Spring Apps/VMs) | Azure Container Apps | [spring-apps-to-aca.md](references/services/container-apps/spring-apps-to-aca.md) | | ||
|
|
||
| > No matching scenario? Use `mcp_azure_mcp_documentation` and `mcp_azure_mcp_get_bestpractices` tools. |
There was a problem hiding this comment.
Adding the Spring Boot → Container Apps scenario is good, but the skill’s global "Steps" section later still points to Functions-specific references (references/services/functions/assessment.md and code-migration.md). For Spring Boot/ACA prompts this will likely drive the agent to load incorrect guidance. Consider making the Steps section scenario-dependent (e.g., link to the selected scenario’s assessment/deployment docs) or adding Container Apps equivalents and routing based on the scenario table.
| ACR_ID=$(az acr show --name "$ACR_NAME" --query id -o tsv) | ||
| az role assignment create --assignee "$PRINCIPAL_ID" --role AcrPull --scope "$ACR_ID" | ||
| ``` | ||
| ``` |
There was a problem hiding this comment.
There is an extra closing Markdown code fence after the Key Vault script block, which will break formatting for the rest of the document. Remove the stray ``` so the code fences are balanced.
| ``` |
| describe("Spring Boot to Container Apps migration scenario", () => { | ||
| test("invokes skill for Spring Boot to ACA migration prompt", async () => { | ||
| await withTestResult(async () => { | ||
| const agentMetadata = await agent.run({ | ||
| prompt: "I want to migrate my Spring Boot application from Azure Spring Apps to Azure Container Apps. Can you help me assess compatibility and create a migration plan?", | ||
| nonInteractive: true, | ||
| }); | ||
|
|
||
| const isSkillUsed = isSkillInvoked(agentMetadata, SKILL_NAME); | ||
| expect(isSkillUsed).toBe(true); | ||
| }); | ||
| }, 30000); |
There was a problem hiding this comment.
These new integration tests use a 30s timeout, while the rest of this file uses a much larger timeout for agent runs. In CI, agent sessions can exceed 30s even for simple prompts, which may make these tests flaky. Consider using the existing migrationTestTimeoutMs (or a higher, consistent timeout) here as well.
1. Fix test assertion to explicitly check for 'Azure Spring Apps' 2. Remove stray code fence in deployment-guide.md 3. Make Steps section scenario-agnostic (remove Functions-specific links) 4. Update integration test timeouts to use migrationTestTimeoutMs (consistent with other tests) All 43 tests still passing.
…ion.md Add references to Lambda-specific assessment and code-migration guides under the Migration Scenarios section to fix orphaned file validation error. Fixes CI failure in Markdown References job.
| 1. **Create** `<source-folder>-azure/` at workspace root | ||
| 2. **Assess** — Analyze source, map services, generate report → [assessment.md](references/services/functions/assessment.md) | ||
| 3. **Migrate** — Convert code using target programming model → [code-migration.md](references/services/functions/code-migration.md) | ||
| 2. **Assess** — Analyze source workload and generate migration report (use scenario-specific assessment guide) | ||
| 3. **Migrate** — Convert code using target Azure service programming model (use scenario-specific deployment guide) | ||
| 4. **Ask User** — "Migration complete. Test locally or deploy to Azure?" |
There was a problem hiding this comment.
The global step 3 says to use a “scenario-specific deployment guide”, but the Lambda scenario’s guidance is a code-migration guide (not a deployment guide). This wording is likely to misroute the agent for Lambda migrations; consider changing to “scenario-specific migration guide (assessment + code-migration/deployment as applicable)” or similar.
| | Spring Apps Feature | Container Apps Equivalent | | ||
| |-------------------|---------------------------| | ||
| | App Deployment | Container App | | ||
| | Service Registry (Eureka) | Azure Service Bus / Dapr service invocation | | ||
| | Config Server | Azure App Configuration + Key Vault | | ||
| | Spring Cloud Gateway | Azure API Management / Container Apps ingress | |
There was a problem hiding this comment.
In the Service Mapping table, “Service Registry (Eureka) → Azure Service Bus” is not an equivalent mapping (Service Bus is a message broker, not service discovery/registry). This is likely to mislead migrations; suggest mapping to Container Apps internal DNS + Dapr service invocation (and/or explaining when to use Dapr pub/sub vs a registry).
| # Deployment Guide: Spring Boot to Azure Container Apps | ||
|
|
||
| ## Phase 1: Create Container Apps Environment | ||
|
|
||
| ```bash | ||
| #!/bin/bash | ||
| set -euo pipefail | ||
| az group create --name spring-rg --location eastus | ||
| az monitor log-analytics workspace create --resource-group spring-rg --workspace-name spring-logs --location eastus | ||
| LOG_ID=$(az monitor log-analytics workspace show --resource-group spring-rg --workspace-name spring-logs --query customerId -o tsv) | ||
| LOG_KEY=$(az monitor log-analytics workspace get-shared-keys --resource-group spring-rg --workspace-name spring-logs --query primarySharedKey -o tsv) | ||
| az containerapp env create --name spring-env --resource-group spring-rg --location eastus --logs-workspace-id "$LOG_ID" --logs-workspace-key "$LOG_KEY" | ||
| ``` |
There was a problem hiding this comment.
The PR description mentions providing both Bash and PowerShell scripts, but this deployment guide currently only includes Bash examples. Either add equivalent PowerShell snippets for the key phases (RG/LAW/env, ACR build/push, Key Vault/identity, containerapp create/update) or adjust the PR description/claims accordingly.
| **With storage mount:** | ||
| ```bash | ||
| az containerapp create ... --bind-storage-name spring-storage --mount-path /mnt/data |
There was a problem hiding this comment.
The “With storage mount” example uses az containerapp create ... with an ellipsis and placeholder flags. As written, this is not copy/pasteable and may confuse readers; consider either providing the full command/YAML snippet for mounting environment storage, or explicitly labeling it as pseudocode and pointing to the exact CLI/YAML fields to use.
| **With storage mount:** | |
| ```bash | |
| az containerapp create ... --bind-storage-name spring-storage --mount-path /mnt/data | |
| **With storage mount** (assuming the Container Apps environment already has an environment storage binding named `spring-storage`): | |
| ```bash | |
| SECRET_URI=$(az keyvault secret show --vault-name "$KEY_VAULT" --name db-password --query id -o tsv) | |
| az containerapp create --name spring-app --resource-group spring-rg --environment spring-env \ | |
| --image "${ACR_NAME}.azurecr.io/spring-app:v1.0" --target-port 8080 --ingress external \ | |
| --cpu 2.0 --memory 4Gi --min-replicas 2 --max-replicas 10 \ | |
| --user-assigned "$IDENTITY_ID" --registry-identity "$IDENTITY_ID" --registry-server "${ACR_NAME}.azurecr.io" \ | |
| --secrets db-password=keyvaultref:"${SECRET_URI}",identityref:"${IDENTITY_ID}" \ | |
| --env-vars SPRING_DATASOURCE_PASSWORD=secretref:db-password SPRING_PROFILES_ACTIVE=prod \ | |
| --bind-storage-name spring-storage --mount-path /mnt/data |
1. Fix Steps wording: change 'deployment guide' to 'migration guide' (covers both code-migration and deployment scenarios) 2. Fix Service Registry mapping: Dapr service invocation + internal DNS (not Service Bus which is messaging) 3. Add note that only Bash examples included (PowerShell removed for token savings) 4. Fix storage mount example: clarify as flags to add to previous command (not incomplete ellipsis) All 43 tests passing. Markdown references valid.
PR Information: Spring Boot to Container Apps IntegrationSummaryThis PR consolidates the standalone Commit History1. fb3b07a - Integrate Spring Boot to Container Apps migration into azure-cloud-migrate skillDate: Mon Apr 6, 2026 Initial integration that consolidates the standalone skill:
Stats: 16 files changed, 321 insertions(+), 943 deletions(-) 2. 2ab4b39 - Address PR review commentsDate: Sun Apr 6, 2026 Fixed issues from first round of PR review:
All 43 tests still passing. 3. 56add5b - Fix markdown reference validation: link assessment.md and code-migration.mdDate: Sun Apr 6, 2026 Fixed CI failure in Markdown References job:
4. 866ad04 - Address second round of PR review commentsDate: Mon Apr 7, 2026 Fixed issues from second round of PR review:
All 43 tests passing. Markdown references valid. Key ChangesFiles Added:
Files Modified:
Files Deleted:
Net Change: 16 files changed, 321 insertions(+), 943 deletions(-) Validation Status✅ All 43 tests passing (14 unit + 29 trigger tests, integration tests skipped locally) Test CoverageUnit Tests (3 new):
Trigger Tests (8 new prompts):
Integration Tests (2 new):
|

New skill: spring-apps-to-aca
Files added:
Features:
Token limits met:
Registered in tests/skills.json skills array and integration schedule.