diff --git a/eng/common/TestResources/New-TestResources.ps1 b/eng/common/TestResources/New-TestResources.ps1 index c106b904e..1acc02fe4 100755 --- a/eng/common/TestResources/New-TestResources.ps1 +++ b/eng/common/TestResources/New-TestResources.ps1 @@ -358,8 +358,15 @@ try { # Make sure the provisioner OID is set so we can pass it through to the deployment. if (!$ProvisionerApplicationId -and !$ProvisionerApplicationOid) { if ($context.Account.Type -eq 'User') { - # HomeAccountId format is '.' - $userAccountId = (Get-AzContext).Account.ExtendedProperties.HomeAccountId.Split('.')[0] + # Calls to graph API in corp tenant get blocked by conditional access policy now + # but not in TME. For corp tenant we get the user's id from the login context + # but for TME it is different so we have to source it from graph + $userAccountId = if ($wellKnownTMETenants.Contains($TenantId)) { + (Get-AzADUser -SignedIn).Id + } else { + # HomeAccountId format is '.' + (Get-AzContext).Account.ExtendedProperties.HomeAccountId.Split('.')[0] + } if ($null -eq $userAccountId) { throw "Failed to find entra object ID for the current user" } @@ -430,12 +437,20 @@ try { if (!$CI -and !$ServicePrincipalAuth) { if ($TestApplicationId) { - Write-Warning "The specified TestApplicationId '$TestApplicationId' will be ignored when -ServicePrincipalAutth is not set." + Write-Warning "The specified TestApplicationId '$TestApplicationId' will be ignored when -ServicePrincipalAuth is not set." } $userAccountName = (Get-AzContext).Account.Id # HomeAccountId format is '.' - $userAccountId = (Get-AzContext).Account.ExtendedProperties.HomeAccountId.Split('.')[0] + # Calls to graph API in corp tenant get blocked by conditional access policy now + # but not in TME. For corp tenant we get the user's id from the login context + # but for TME it is different so we have to source it from graph + $userAccountId = if ($wellKnownTMETenants.Contains($TenantId)) { + (Get-AzADUser -SignedIn).Id + } else { + # HomeAccountId format is '.' + (Get-AzContext).Account.ExtendedProperties.HomeAccountId.Split('.')[0] + } if ($null -eq $userAccountId) { throw "Failed to find entra object ID for the current user" } diff --git a/eng/common/instructions/azsdk-tools/check-api-readiness.instructions.md b/eng/common/instructions/azsdk-tools/check-api-readiness.instructions.md new file mode 100644 index 000000000..1daeeadf2 --- /dev/null +++ b/eng/common/instructions/azsdk-tools/check-api-readiness.instructions.md @@ -0,0 +1,5 @@ +--- +description: 'Check API Readiness for SDK Generation' +--- +Your goal is to check if API spec pull request is ready for SDK generation. Identify the next action required from user based on the comments on spec pull request if spec is not ready and notify the user. +Before running, get spec pull request link for current branch or from user if not available in current context. If pull request has APIView links, then highlight them to user. \ No newline at end of file diff --git a/eng/common/instructions/azsdk-tools/check-package-readiness.instructions.md b/eng/common/instructions/azsdk-tools/check-package-readiness.instructions.md new file mode 100644 index 000000000..bb4334dc5 --- /dev/null +++ b/eng/common/instructions/azsdk-tools/check-package-readiness.instructions.md @@ -0,0 +1,34 @@ +--- +description: 'This prompt is designed to check the release readiness of a SDK package.' +--- +## Goal +Check the release readiness of an SDK package by collecting the required information from the user and executing the readiness check. + +## Instructions +1. **Collect Required Information**: + - Prompt the user for the exact package name + - Prompt the user to select the programming language from the following options (case sensitive): + - Python + - Java + - JavaScript + - .NET + - Go + +2. **Execute Readiness Check**: + - Use the `azsdk_check_package_release_readiness` tool with the provided package name and selected language + - Do not check for existing pull requests to run this step. + - Do not ask the user to create a release plan to run this step. + +3. **Present Results**: + - If the package is ready for release, highlight and provide the link to the release pipeline + - If the package is not ready, display the specific issues that need to be resolved + +4. **Follow-up Actions**: + - Provide clear next steps based on the readiness status + - If issues are found, offer guidance on how to resolve them + +## Expected User Interaction Flow +1. Ask: "What is the exact name of the package you want to check for release readiness?" +2. Ask: "Please select the programming language for this package: Python, Java, JavaScript, .NET, or Go" +3. Execute the readiness check using the provided information +4. Display results and next steps diff --git a/eng/common/instructions/azsdk-tools/create-release-plan.instructions.md b/eng/common/instructions/azsdk-tools/create-release-plan.instructions.md new file mode 100644 index 000000000..fa55d8edb --- /dev/null +++ b/eng/common/instructions/azsdk-tools/create-release-plan.instructions.md @@ -0,0 +1,55 @@ +# Release Plan Creation Process +You goal is to create a valid release plan. You must prompt user to provide all required information and all input must match the format and requirement mentioned in step 3 below. +Follow these steps in order to create or manage a release plan for an API specification pull request: + +## Step 1: Validate Prerequisites +- Check if an API spec pull request is available in the current context +- If no pull request is available, prompt the user to provide the API spec pull request link +- Validate that the provided pull request link is accessible and valid + +## Step 2: Check Existing Release Plan +- Use `azsdk_get_release_plan_for_spec_pr` to check if a release plan already exists for the API spec pull request +- If a release plan exists: + - Display the existing release plan details to the user + - Skip to Step 5 (Link SDK Pull Requests) +- If no release plan exists, proceed to Step 3 + +## Step 3: Gather Release Plan Information +Collect the following required information from the user. Do not create a release plan with temporary values. Confirm the values with the user before proceeding to create the release plan. +If any details are missing, prompt the user accordingly: + +- **API Lifecycle Stage**: Must be one of: + - Private Preview + - Public Preview + - GA (Generally Available) +- **Service Tree ID**: GUID format identifier for the service in Service Tree. Before creating release plan, always show the value to user and ask them to confirm it's a valid value in service tree. +- **Product Service Tree ID**: GUID format identifier for the product in Service Tree. Before creating release plan, always show the value to user and ask them to confirm it's a valid value in service tree. +- **Expected Release Timeline**: Format must be in "Month YYYY" +- **API Version**: The version of the API being released +- **SDK Release Type**: Value must be beta or stable. + - "beta" for preview API versions + - "stable" for GA API versions + +## Step 4: Create Release Plan +- If the user doesn't know the required details, direct them to create a release plan using the release planner +- Provide this resource: [Release Plan Creation Guide](https://eng.ms/docs/products/azure-developer-experience/plan/release-plan-create) +- Once all information is gathered, use `azsdk_create_release_plan` to create the release plan +- Display the newly created release plan details to the user for confirmation +- Refer to #file:sdk-details-in-release-plan.instructions.md to identify languages configured in the TypeSpec project and add them to the release plan + +## Step 5: Update SDK Details in Release Plan +- Refer to #file:sdk-details-in-release-plan.instructions.md to add languages and package names to the release plan +- If the TypeSpec project is for a management plane, refer to #file:verify-namespace-approval.instructions.md if this is first release of SDK. + +## Step 6: Link SDK Pull Requests (if applicable) +- Ask the user if they have already created SDK pull requests locally for any programming language +- If SDK pull requests exist: + - Collect the pull request links from the user + - Use `azsdk_link_sdk_pull_request_to_release_plan` to link each SDK pull request to the release plan + - Confirm successful linking for each SDK pull request + +## Step 7: Summary +- Display a summary of the completed actions: + - Release plan status (created or existing) + - Linked SDK pull requests (if any) + - Next steps or recommendations for the user \ No newline at end of file diff --git a/eng/common/instructions/azsdk-tools/create-sdk-locally.instructions.md b/eng/common/instructions/azsdk-tools/create-sdk-locally.instructions.md new file mode 100644 index 000000000..20cc2f31b --- /dev/null +++ b/eng/common/instructions/azsdk-tools/create-sdk-locally.instructions.md @@ -0,0 +1,32 @@ +Your goal is to help guide the user to create SDK locally for TypeSpec changes. This is currently supported for **Python** only. User can generate SDK for other languages using SDK generation pipeline. +## Steps to create Python SDK locally from TypeSpec +### Step 1: Check for existing azure-sdk-for-python repository +- Prompt the user to provide the path to their cloned azure-sdk-for-python repository. +### Step 2: Validate repository path +- If the user provides a path to the azure-sdk-for-python repository: + - Check if the repository exists at the specified path. + - If the repository exists, proceed to Step 5. +### Step 3: Guide user to set up azure-sdk-for-python repository (if not found) +- If the user does not have the repository or the path is invalid: + - Go to parent directory of current repo root path. + - Provide instructions to fork https://github.com/Azure/azure-sdk-for-python repository to the user's GitHub account. + - Provide instructions to clone the forked repository to the local machine: + ```bash + git clone https://github.com//azure-sdk-for-python.git + ``` +### Step 4: Set repository path +- Consider the cloned path as the path to the azure-sdk-for-python repository. +### Step 5: Open azure-sdk-for-python repository in VSCode +- Do not ask the user to run tsp compile. +- Prompt user to open the azure-sdk-for-python repository in VSCode. +### Step 6: Provide SDK generation instructions +- Inform user to use the following prompt to start SDK generation using GitHub Copilot agent: + ``` + "Help me generate SDK for Python from TypeSpec API specification for project ." + ``` +### Step 7: Inform user about SDK generation +- Inform user to provide link to SDK pull request if they generate SDK locally and created a pull request for it. SDK generation +step below will skip it for the language and reuse the pull request link provided by the user. +- In some cases, user will come back and make more changes to TypeSpec so start the process from step 1 again. +- If user provides a link to SDK pull request then link SDK pull request to release plan if a release plan already exists and skip SDK generation for that language. +- If a release plan does not exits then link the SDK pull request when release plan is created. diff --git a/eng/common/instructions/azsdk-tools/create-spec-pullrequest.instructions.md b/eng/common/instructions/azsdk-tools/create-spec-pullrequest.instructions.md new file mode 100644 index 000000000..dca8369a8 --- /dev/null +++ b/eng/common/instructions/azsdk-tools/create-spec-pullrequest.instructions.md @@ -0,0 +1,3 @@ +Your goal is to identify modified TypeSpec project in current branch and create a pull request for it. +Check if a pull request already exists using GetPullRequestForCurrentBranch. If a pull request exists, inform the user and show the pull request details. If no pull request exists, create a new pull request using CreatePullRequest. + diff --git a/eng/common/instructions/azsdk-tools/run-sdk-gen-pipeline.instructions.md b/eng/common/instructions/azsdk-tools/run-sdk-gen-pipeline.instructions.md new file mode 100644 index 000000000..3ed5cafef --- /dev/null +++ b/eng/common/instructions/azsdk-tools/run-sdk-gen-pipeline.instructions.md @@ -0,0 +1,43 @@ +--- +description: 'Generate SDKs from TypeSpec using pipeline' +--- +Your goal is to generate SDKs from the TypeSpec spec pull request. Get API spec pull request link for current branch or from user if not available in current context. +Provide links to SDK pull request when generated for each language. + +## Steps for SDK Generation + +### Step 1: Check for Existing SDK Pull Requests +- Check if SDK pull requests exist from local SDK generation for any languages +- If SDK pull request exists for a language, skip SDK generation for that language +- Link existing SDK pull request to release plan + +### Step 2: Retrieve and Validate Release Plan +- Retrieve the release plan for the API spec +- If API Lifecycle Stage is `Private Preview` then inform user that SDK generation is not supported for this stage and complete the workflow. +- Check if SDK generation has already occurred for each language +- Verify if SDK pull requests exist for each language: + - If an SDK pull request exists, display its details + - If no pull request exists or regeneration is needed, proceed to next step + +### Step 3: Execute SDK Generation Pipeline +- Run SDK generation for each required language: Python, .NET, JavaScript, Java, and Go +- Execute the SDK generation pipeline with the following required parameters: + - TypeSpec project root path + - Pull request number (if the API spec is not merged to the main branch) + - API version + - SDK release type (beta for preview API versions, stable otherwise) + - Language options: `Python`, `.NET`, `JavaScript`, `Java`, `Go` + - Release plan work item ID + +### Step 4: Monitor Pipeline Status +- Check the status of SDK generation pipeline every 2 minutes +- Continue monitoring until pipeline succeeds or fails +- Get SDK pull request link from pipeline once available + +### Step 5: Display Results +- Show all pipeline details once pipeline is in completed status +- Highlight the language name for each SDK generation task when displaying details +- Once SDK pull request URL is available: + - Inform the user of successful SDK generation + - Display the pull request details for each language + - Provide links to each generated SDK pull request \ No newline at end of file diff --git a/eng/common/instructions/azsdk-tools/sdk-details-in-release-plan.instructions.md b/eng/common/instructions/azsdk-tools/sdk-details-in-release-plan.instructions.md new file mode 100644 index 000000000..d6574bb8b --- /dev/null +++ b/eng/common/instructions/azsdk-tools/sdk-details-in-release-plan.instructions.md @@ -0,0 +1,39 @@ +--- +description: 'Identify languages configured in the TypeSpec project and add it to release plan' +--- +# Step 1: Find the list of languages and package names +**Goal**: Identify languages configured in the TypeSpec project and generate the json object with language and package name. +1. Identify the language emitter configuration in the `tspconfig.yaml` file in the TypeSpec project root. +2. Identify the package name or namespace for each language emitter. +3. Map the language name in emitter to one of the following in Pascal case(except .NET): + - .NET + - Java + - Python + - JavaScript + - Go +4. Remove `github.com/Azure/azure-sdk-for-go/` from Go package name. +4. Create a JSON array object with the following structure: + ```json + [ + { + "language": "", + "packageName": "" + }, + ... + ] + ``` +5. If no languages are configured, inform the user: "No languages configured in TypeSpec project. Please add at least one language emitter in tspconfig.yaml." +**Success Criteria**: JSON object with languages and package names created. + +# Step 2: Check if release plan exists +**Goal**: Determine if a release plan exists for the API spec pull request or work item Id or release plan Id in current context. +1. Get release plan +2. If no release plan exists, inform the user: "No release plan exists for the API spec pull request. Please create a release plan first." +3. If a release plan exists, proceed to Step 3. +**Success Criteria**: Release plan exists or user informed to create one. + +# Step 3: Update Release Plan with SDK Information +**Goal**: Update the release plan with the languages and package names identified in Step 1. +1. Use `azsdk_update_sdk_details_in_release_plan` to update the release plan work item with the JSON object created in Step 1. +2. Confirm successful update of the release plan with the SDK information and summary of languages and package names. +**Success Criteria**: Release plan updated with languages and package names. \ No newline at end of file diff --git a/eng/common/instructions/azsdk-tools/typespec-to-sdk.instructions.md b/eng/common/instructions/azsdk-tools/typespec-to-sdk.instructions.md new file mode 100644 index 000000000..b8a8eb6b9 --- /dev/null +++ b/eng/common/instructions/azsdk-tools/typespec-to-sdk.instructions.md @@ -0,0 +1,120 @@ +--- +description: 'Generate SDKs from TypeSpec' +--- +Your goal is to guide user through the process of generating SDKs from TypeSpec projects. Show all the high level steps to the user to ensure they understand the flow. Use the provided tools to perform actions and gather information as needed. + +## Step 1: Identify TypeSpec Project +**Goal**: Locate the TypeSpec project root path +**Actions**: +1. Check if `tspconfig.yaml` or `main.tsp` files are open in editor +2. If found, use the parent directory as project root +3. If not found, prompt user: "Please provide the path to your TypeSpec project root directory" +4. Validate the provided path contains required TypeSpec files +**Success Criteria**: Valid TypeSpec project path identified + +## Step 2: Validate TypeSpec Specification +**Goal**: Ensure TypeSpec specification compiles without errors +**Actions**: +1. Refer to #file:validate-typespec.instructions.md +2. If validation succeeds, proceed to Step 3 +3. If validation fails: + - Display all compilation errors to user + - Prompt: "Please fix the TypeSpec compilation errors before proceeding" + - Wait for user to fix errors and re-run validation +**Success Criteria**: TypeSpec compilation passes without errors + +## Step 3: Verify Authentication and Repository Status +**Goal**: Ensure user is authenticated and working in correct repository +**Actions**: +1. Run `azsdk_get_github_user_details` to verify login status +2. If not logged in, prompt: "Please login to GitHub using `gh auth login`" +3. Once logged in, display user details to confirm identity +4. Run `azsdk_check_typespec_project_in_public_repo` to verify repository +5. If not in public repo, inform: "Please make spec changes in Azure/azure-rest-api-specs public repo to generate SDKs" +**Success Criteria**: User authenticated and working in public Azure repo + +## Step 4: Review and Commit Changes +**Goal**: Stage and commit TypeSpec modifications +**Actions**: +1. Run `azsdk_get_modified_typespec_projects` to identify changes +2. If no changes found, inform: "No TypeSpec projects were modified in current branch" +3. Display all modified files (excluding `.github` and `.vscode` folders) +4. Prompt user: "Please review the modified files. Do you want to commit these changes? (yes/no)" +5. If yes: + - If on main branch, prompt user: "You are currently on the main branch. Please create a new branch using `git checkout -b ` before proceeding." + - Wait for user confirmation before continuing + - Run `git add ` + - Prompt for commit message + - Run `git commit -m ""` + - Run `git push -u origin ` +**Success Criteria**: Changes committed and pushed to remote branch + +## Step 5: Choose SDK Generation Method +**Goal**: Determine how to generate SDKs +**Actions**: +1. Present options: "How would you like to generate SDKs?" + - Option A: "Generate SDK locally". This is currently supported only for Python. Do not recommend this for other languages. + - Option B: "Use SDK generation pipeline" +2. Based on selection: + - If Option A: Refer to #file:create-sdk-locally.instructions.md and then proceed to Step 6 + - If Option B: Continue to Step 6 +**Success Criteria**: SDK generation method selected + +## Step 6: Create Specification Pull Request +**Goal**: Create PR for TypeSpec changes if not already created +**Actions**: +1. Check if spec PR already exists using `azsdk_get_pull_request_link_for_current_branch` +2. If PR exists, display PR details and proceed to Step 7 +3. If no PR exists: + - Refer to #file:create-spec-pullrequest.instructions.md + - Wait for PR creation confirmation + - Display created PR details +**Success Criteria**: Specification pull request exists + +## Step 7: Generate SDKs via Pipeline +**Goal**: Create release plan and generate SDKs +**Actions**: +1. Refer to #file:create-release-plan.instructions.md +2. If SDK PRs exist, link them to the release plan +3. Refer to #file:sdk-details-in-release-plan.instructions.md to add languages and package names to the release plan +4. If TypeSpec project is for management plane, refer to #file:verify-namespace-approval.instructions.md to check package namespace approval. +5. Refer to #file:run-sdk-gen-pipeline.instructions.md with the spec PR +6. Monitor pipeline status and provide updates +7. Display generated SDK PR links when available +**Success Criteria**: SDK generation pipeline initiated and SDKs generated + +## Step 8: Show Generated SDK PRs +**Goal**: Display all created SDK pull requests +**Actions**: +1. Run `azsdk_get_sdk_pull_request_link` to fetch generated SDK PR info. + +## Step 9: Validate Label and Codeowners +**Goal**: Validate the label and all codeowners for a service. Create new label and codeowner entry if none exist. +**Actions**: +1. To validate a service label refer to #file:./validate-service-label.instructions.md +2. After service label is validated or created refer to #file:./validate-codeowners.instructions.md +3. Handle post-validation actions based on results: + - **If both label and codeowners were already valid**: Prompt user "Your service label and codeowners are already properly configured. Would you like to modify the existing codeowners entry for your service?" + - **If new label or codeowner entries were created**: Display details of the label and codeowners PR if they were created, then prompt user "The following PRs have been created for your service configuration: [list PRs]. Would you like to make any additional modifications to these entries?" +**Success Criteria**: Service label exists and codeowners are properly configured with at least 2 valid owners. For created entries, showcase all PR's. + +## Step 10: Create release plan +**Goal**: Create a release plan for the generated SDKs +**Actions**: +1. Refer to #file:create-release-plan.instructions.md to create a release plan using the spec pull request. +2. If the release plan already exists, display the existing plan details. + +## Step 11: Mark Spec PR as Ready for Review +**Goal**: Update spec PR to ready for review status +**Actions**: +1. Prompt user to change spec PR to ready for review: "Please change the spec pull request to ready for review status" +2. Get approval and merge the spec PR + +## Step 12: Release SDK Package +**Goal**: Release the SDK package using the release plan +**Actions**: +1. Run `ReleaseSdkPackage` to release the SDK package. +2. Inform user to approve the package release using release pipeline. + +## Process Complete +Display summary of all created PRs and next steps for user. \ No newline at end of file diff --git a/eng/common/instructions/azsdk-tools/validate-codeowners.instructions.md b/eng/common/instructions/azsdk-tools/validate-codeowners.instructions.md new file mode 100644 index 000000000..83167f459 --- /dev/null +++ b/eng/common/instructions/azsdk-tools/validate-codeowners.instructions.md @@ -0,0 +1,74 @@ +--- +mode: 'agent' +tools: ['azsdk_check_service_label', 'azsdk_engsys_validate_codeowners_entry_for_service', 'azsdk_engsys_codeowner_update'] +--- + +## Goal: +Validate service label and ensure at least 2 valid code owners exist for SDK repositories. + +## Step 1: Validate Service Label +Use `azsdk_check_service_label` to verify the service label exists: +- **DoesNotExist/NotAServiceLabel**: Direct user to create valid service label first. Stop validation process until service label is created. +- **Exists/InReview**: Proceed to Step 2 + +## Step 2: Validate Code Owners +Ask user to specify SDK repository they want to validate codeowners for or detect from context. + +Repository name mapping: +- .NET/dotnet: use "azure-sdk-for-net" +- Python: use "azure-sdk-for-python" +- Java: use "azure-sdk-for-java" +- JavaScript: use "azure-sdk-for-js" +- Go: use "azure-sdk-for-go" + +Use `azsdk_engsys_validate_codeowners_entry_for_service` with either `serviceLabel` OR `repoPath` or both, but at least one must be used. If one isn't provided, leave the parameter field empty. + +**If entry exists**: Go to Step 3 +**If no entry exists**: Go to Step 4 + +## Step 3: Check Existing Code Owners +Valid code owners must be: +- PUBLIC members of Microsoft and Azure GitHub organizations +- Have write access to the SDK repository + +**If at least 2 valid owners**: Success - optionally add or delete additional owners +**If less than 2 valid owners**: CRITICAL - must fix before proceeding: + +After any changes, re-validate with `azsdk_engsys_validate_codeowners_entry_for_service`. + +## Step 4: Create New Code Owner Entry +When no CODEOWNERS entry exists yet: +1. Ensure you have the following information + - repo - **Required** - Repository name mapping: + - .NET/dotnet: use "azure-sdk-for-net" + - Python: use "azure-sdk-for-python" + - Java: use "azure-sdk-for-java" + - JavaScript: use "azure-sdk-for-js" + - Go: use "azure-sdk-for-go" + - typeSpecProjectRoot - **Optional** This should be acquired only if the information is present in the previous chat history, if not, ignore and input `""`. + - path - **Optional** only if there is a service label and we're not making a new entry - This should be acquired when creating a new code owner entry, if no information is present ask the user. Typically looks like `/sdk/projectpath` + - serviceLabel - **Optional** only if there is a path and we're not making a new entry - This should be acquired from the previous step of Check or Create Service Label. + - serviceOwners - **Optional** if no ServiceLabel is present. Can be either owners to add or delete, depending on isAdding. + - sourceOwners - **Optional** if no path or PRLabel are present. Can be either owners to add or delete, depending on isAdding. + - isAdding - **Required** Should be true if adding owners to an existing entry, false if deleting owners from an existing entry. Should also be false when adding a brand new entry. +1. Provide information to the user about what codeowners is for: + - [Learn about CODEOWNERS](https://eng.ms/docs/products/azure-developer-experience/develop/supporting-sdk-customers/overview) + - Service owners is for getting mentioned on issues. + - Source owners is for getting mentioned in PRs. +2. Collect service owners and source owners (GitHub usernames) +3. Use `azsdk_engsys_codeowner_update` with required parameters +4. Must have at least 2 valid owners from the start + +### Fix Options: +1. **Fix invalid owners** - If there are invalid owners after modifing the CODEOWNERS file ALWAYS provide guidance. + Follow instructions [here](https://aka.ms/azsdk/access) for: + - Joining Microsoft and Azure GitHub orgs + - Setting public visibility + - Requesting write access +2. **Add new owners** using `azsdk_engsys_codeowner_update` with `isAdding: true` +3. **Remove invalid + add valid** owners using `azsdk_engsys_codeowner_update` + +## Requirements +- **MINIMUM**: At least 2 valid code owners at all times +- **NO EXCEPTIONS**: Cannot proceed with insufficient owners +- **RESPONSE HANDLING**: If any exception occurs during validation or creation, ALWAYS provide documentation link [CODEOWNERS documentation](https://eng.ms/docs/products/azure-developer-experience/develop/supporting-sdk-customers/codeowners) \ No newline at end of file diff --git a/eng/common/instructions/azsdk-tools/validate-service-label.instructions.md b/eng/common/instructions/azsdk-tools/validate-service-label.instructions.md new file mode 100644 index 000000000..95dd71e17 --- /dev/null +++ b/eng/common/instructions/azsdk-tools/validate-service-label.instructions.md @@ -0,0 +1,54 @@ +--- +mode: 'agent' +tools: ['azsdk_check_service_label', 'azsdk_create_service_label'] +--- + +## Goal +Validate service label exists or create new one for SDK release process. + +## Step 1: Provide Information + +Provide the following information about the importance of service labels: + +"Before your SDK is released, your service must have a valid service label in the Azure SDK repositories. Service labels enable automatic owner assignment and notifications across the Azure SDK ecosystem. + +When properly configured, service labels automatically: + +- Notify service owners when issues are filed against their SDK +- Add appropriate reviewers to pull requests +- Connect code changes to the right team members through CODEOWNERS integration + +Without a valid service label, the process to identify the correct service owners for issues and code reviews becomes manual and inefficient." + +## Step 2: Get Service Label + +Ask user for their service label. If they don't know their service label provide guidance: + +- Check out the [Common Labels CSV](https://github.com/Azure/azure-sdk-tools/blob/main/tools/github/data/common-labels.csv) file and look for a row whose first column contains your service's product name. + +If they don't have a service label - go to Step 3 for new service label + +## Step 3: Validate Label + +Use `azsdk_check_service_label` to check status: + +- **Exists**: Success - user can proceed with next steps in SDK release process +- **InReview**: Label pending approval - user can proceed (will be available once merged) +- **DoesNotExist**: Go to Step 3 to create new label +- **NotAServiceLabel**: Label exists but it is not a service label - go to Step 3 for new service label + +## Step 4: Create New Service Label + +If no valid service label exists, guide the user through creating a new one. + +1. **Check existing labels**: Search for related service labels, offer alternatives +2. **Generate recommendation**: Suggest label name following guidelines: + - Should match the service's official product name as described on Service Tree (e.g., "Event Hubs", "Kusto", "Cosmos", etc.) + - No "Microsoft/Azure" in name + - Title Case (except short prepositions) + - Avoid Service Groups: Use "Communication Rooms" instead of "Communication - Rooms" + - Single label per service +3. **Get confirmation**: User confirms or modifies suggested name +4. **Create label**: Use `azsdk_create_service_label` with confirmed name and documentation link given by user + +Inform user they can proceed. \ No newline at end of file diff --git a/eng/common/instructions/azsdk-tools/validate-typespec.instructions.md b/eng/common/instructions/azsdk-tools/validate-typespec.instructions.md new file mode 100644 index 000000000..c036d6cb1 --- /dev/null +++ b/eng/common/instructions/azsdk-tools/validate-typespec.instructions.md @@ -0,0 +1,6 @@ +--- +description: 'Validate TypeSpec' +--- +Your goal is identify the TypeSpec project root if not available in current context and validate TypeSpec project. +Before running, inform user that TypeSpec validation takes around 20 - 30 seconds. Provide complete summary after +running the tool and highlight any errors and help user fix them. \ No newline at end of file diff --git a/eng/common/instructions/azsdk-tools/verify-namespace-approval.instructions.md b/eng/common/instructions/azsdk-tools/verify-namespace-approval.instructions.md new file mode 100644 index 000000000..cee268de7 --- /dev/null +++ b/eng/common/instructions/azsdk-tools/verify-namespace-approval.instructions.md @@ -0,0 +1,22 @@ +--- +description: 'Verify SDK namespace approval for management plane' +--- +This task is required only for management plane API spec and only if a release plan exists for the API spec pull request. + +## Step 1: Check if release plan exists and it is for management plane SDK +**Goal**: Determine if a release plan exists for the API spec pull request or work item Id or release plan Id in current context. +**Actions**: +1. Get release plan and check if it is for management plane SDK +2. If not, inform user: "This task is only applicable for management plane SDKs. No action required." +3. Check if release plan already has namespace approval issue. Also prompt user to check if this is the first release of SDK. +4. If namespace approval issue exists, inform user: "Namespace approval issue already exists for this release plan.". Prompt user to +check if they want to link a different namespace approval issue to the release plan. Show namespace approval status. +5. Move to Step 2 if namespace approval issue does not exist or user wants to link a different namespace approval issue. + +## Step 2: Gather Namespace Approval Information +**Goal**: Link namespace approval issue to the release plan. +**Actions**: +1. Collect GitHub issue created in Azure/azure-sdk repo for namespace approval. Do not use any other repo name. +2. Run `azsdk_link_namespace_approval_issue` to link the issue to the release plan work item id. +3. Confirm successful linking of the namespace approval issue to the release plan. +**Success Criteria**: Namespace approval issue linked to the release plan or confirmed as already linked. diff --git a/eng/common/pipelines/templates/steps/mark-release-completion.yml b/eng/common/pipelines/templates/steps/mark-release-completion.yml new file mode 100644 index 000000000..91a39dc48 --- /dev/null +++ b/eng/common/pipelines/templates/steps/mark-release-completion.yml @@ -0,0 +1,17 @@ +parameters: + ConfigFileDir: '' + PackageArtifactName: '' + SourceRootPath: $(Build.SourcesDirectory) + +steps: + - task: AzureCLI@2 + inputs: + azureSubscription: opensource-api-connection + scriptType: pscore + scriptLocation: scriptPath + scriptPath: ${{ parameters.SourceRootPath }}/eng/common/scripts/Mark-ReleasePlanCompletion.ps1 + arguments: -PackageInfoFilePath '${{ parameters.ConfigFileDir }}/${{ parameters.PackageArtifactName }}.json' + workingDirectory: $(Pipeline.Workspace) + displayName: Mark package as released + continueOnError: true + condition: and(succeeded(), ne(variables['Skip.MarkReleaseCompletion'], 'true')) \ No newline at end of file diff --git a/eng/common/scripts/Helpers/DevOps-WorkItem-Helpers.ps1 b/eng/common/scripts/Helpers/DevOps-WorkItem-Helpers.ps1 index 9a46e2c12..9bf29a72b 100644 --- a/eng/common/scripts/Helpers/DevOps-WorkItem-Helpers.ps1 +++ b/eng/common/scripts/Helpers/DevOps-WorkItem-Helpers.ps1 @@ -1015,3 +1015,62 @@ function UpdateValidationStatus($pkgvalidationDetails, $BuildDefinition, $Pipeli Write-Host "[$($workItem.id)]$LanguageDisplayName - $pkgName($versionMajorMinor) - Updated" return $true } + + +function Get-LanguageDevOpsName($LanguageShort) +{ + switch ($LanguageShort.ToLower()) + { + "net" { return "Dotnet" } + "js" { return "JavaScript" } + "java" { return "Java" } + "go" { return "Go" } + "python" { return "Python" } + default { return $null } + } +} + +function Get-ReleasePlanForPackage($packageName) +{ + $devopsFieldLanguage = Get-LanguageDevOpsName -LanguageShort $LanguageShort + if (!$devopsFieldLanguage) + { + Write-Host "Unsupported language to check release plans, language [$LanguageShort]" + return $null + } + + $prStatusFieldName = "SDKPullRequestStatusFor$($devopsFieldLanguage)" + $packageNameFieldName = "$($devopsFieldLanguage) Package Name" + $fields = @() + $fields += "System.ID" + $fields += "System.State" + $fields += "System.AssignedTo" + $fields += "System.Parent" + $fields += "System.Tags" + + $fieldList = ($fields | ForEach-Object { "[$_]"}) -join ", " + $query = "SELECT ${fieldList} FROM WorkItems WHERE [Work Item Type] = 'Release Plan' AND [${packageNameFieldName}] = '${packageName}'" + $query += " AND [${prStatusFieldName}] = 'merged'" + $query += " AND [System.State] IN ('In Progress')" + $query += " AND [System.Tags] NOT CONTAINS 'Release Planner App Test'" + $workItems = Invoke-Query $fields $query + return $workItems +} + +function Update-ReleaseStatusInReleasePlan($releasePlanWorkItemId, $status, $version) +{ + $devopsFieldLanguage = Get-LanguageDevOpsName -LanguageShort $LanguageShort + if (!$devopsFieldLanguage) + { + Write-Host "Unsupported language to check release plans, language [$LanguageShort]" + return $null + } + + $fields = @() + $fields += "`"ReleaseStatusFor$($devopsFieldLanguage)=$status`"" + $fields += "`"ReleasedVersionFor$($devopsFieldLanguage)=$version`"" + + Write-Host "Updating Release Plan [$releasePlanWorkItemId] with status [$status] for language [$LanguageShort]." + $workItem = UpdateWorkItem -id $releasePlanWorkItemId -fields $fields + Write-Host "Updated release status for [$LanguageShort] in Release Plan [$releasePlanWorkItemId]" +} \ No newline at end of file diff --git a/eng/common/scripts/Helpers/Resource-Helpers.ps1 b/eng/common/scripts/Helpers/Resource-Helpers.ps1 index 546c5dd9a..f124ff217 100644 --- a/eng/common/scripts/Helpers/Resource-Helpers.ps1 +++ b/eng/common/scripts/Helpers/Resource-Helpers.ps1 @@ -38,6 +38,32 @@ function Get-PurgeableGroupResources { $purgeableResources += $deletedKeyVaults } + Write-Verbose "Retrieving AI resources from resource group $ResourceGroupName" + + # Get AI resources that will go into soft-deleted state when the resource group is deleted + $subscriptionId = (Get-AzContext).Subscription.Id + $aiResources = @() + + # Get active Cognitive Services accounts from the resource group + $response = Invoke-AzRestMethod -Method GET -Path "/subscriptions/$subscriptionId/resourceGroups/$ResourceGroupName/providers/Microsoft.CognitiveServices/accounts?api-version=2024-10-01" -ErrorAction Ignore + if ($response.StatusCode -ge 200 -and $response.StatusCode -lt 300 -and $response.Content) { + $content = $response.Content | ConvertFrom-Json + + foreach ($r in $content.value) { + $aiResources += [pscustomobject] @{ + AzsdkResourceType = "Cognitive Services ($($r.kind))" + AzsdkName = $r.name + Name = $r.name + Id = $r.id + } + } + } + + if ($aiResources) { + Write-Verbose "Found $($aiResources.Count) AI resources to potentially purge after resource group deletion." + $purgeableResources += $aiResources + } + return $purgeableResources } @@ -94,6 +120,29 @@ function Get-PurgeableResources { } catch { } + Write-Verbose "Retrieving deleted Cognitive Services accounts from subscription $subscriptionId" + + # Get deleted Cognitive Services accounts for the current subscription. + $response = Invoke-AzRestMethod -Method GET -Path "/subscriptions/$subscriptionId/providers/Microsoft.CognitiveServices/deletedAccounts?api-version=2024-10-01" -ErrorAction Ignore + if ($response.StatusCode -ge 200 -and $response.StatusCode -lt 300 -and $response.Content) { + $content = $response.Content | ConvertFrom-Json + + $deletedCognitiveServices = @() + foreach ($r in $content.value) { + $deletedCognitiveServices += [pscustomobject] @{ + AzsdkResourceType = "Cognitive Services ($($r.kind))" + AzsdkName = $r.name + Name = $r.name + Id = $r.id + } + } + + if ($deletedCognitiveServices) { + Write-Verbose "Found $($deletedCognitiveServices.Count) deleted Cognitive Services accounts to potentially purge." + $purgeableResources += $deletedCognitiveServices + } + } + return $purgeableResources } @@ -117,16 +166,18 @@ filter Remove-PurgeableResources { } $subscriptionId = (Get-AzContext).Subscription.Id + $verboseFlag = $VerbosePreference -eq 'Continue' foreach ($r in $Resource) { - Log "Attempting to purge $($r.AzsdkResourceType) '$($r.AzsdkName)'" switch ($r.AzsdkResourceType) { 'Key Vault' { if ($r.EnablePurgeProtection) { - Write-Warning "Key Vault '$($r.VaultName)' has purge protection enabled and may not be purged until $($r.ScheduledPurgeDate)" + Write-Verbose "Key Vault '$($r.VaultName)' has purge protection enabled and may not be purged until $($r.ScheduledPurgeDate)" -Verbose:$verboseFlag continue } + Log "Attempting to purge $($r.AzsdkResourceType) '$($r.AzsdkName)'" + # Use `-AsJob` to start a lightweight, cancellable job and pass to `Wait-PurgeableResoruceJob` for consistent behavior. Remove-AzKeyVault -VaultName $r.VaultName -Location $r.Location -InRemovedState -Force -ErrorAction Continue -AsJob ` | Wait-PurgeableResourceJob -Resource $r -Timeout $Timeout -PassThru:$PassThru @@ -134,16 +185,18 @@ filter Remove-PurgeableResources { 'Managed HSM' { if ($r.EnablePurgeProtection) { - Write-Warning "Managed HSM '$($r.Name)' has purge protection enabled and may not be purged until $($r.ScheduledPurgeDate)" + Write-Verbose "Managed HSM '$($r.Name)' has purge protection enabled and may not be purged until $($r.ScheduledPurgeDate)" -Verbose:$verboseFlag continue } + Log "Attempting to purge $($r.AzsdkResourceType) '$($r.AzsdkName)'" + # Use `GetNewClosure()` on the `-Action` ScriptBlock to make sure variables are captured. Invoke-AzRestMethod -Method POST -Path "/subscriptions/$subscriptionId/providers/Microsoft.KeyVault/locations/$($r.Location)/deletedManagedHSMs/$($r.Name)/purge?api-version=2023-02-01" -ErrorAction Ignore -AsJob ` | Wait-PurgeableResourceJob -Resource $r -Timeout $Timeout -PassThru:$PassThru -Action { param ( $response ) if ($response.StatusCode -ge 200 -and $response.StatusCode -lt 300) { - Write-Warning "Successfully requested that Managed HSM '$($r.Name)' be purged, but may take a few minutes before it is actually purged." + Write-Verbose "Successfully requested that Managed HSM '$($r.Name)' be purged, but may take a few minutes before it is actually purged." -Verbose:$verboseFlag } elseif ($response.Content) { $content = $response.Content | ConvertFrom-Json @@ -155,6 +208,22 @@ filter Remove-PurgeableResources { }.GetNewClosure() } + { $_.StartsWith('Cognitive Services') } + { + Log "Attempting to purge $($r.AzsdkResourceType) '$($r.AzsdkName)'" + # Use `GetNewClosure()` on the `-Action` ScriptBlock to make sure variables are captured. + Invoke-AzRestMethod -Method DELETE -Path "$($r.id)?api-version=2024-10-01" -ErrorAction Ignore -AsJob ` + | Wait-PurgeableResourceJob -Resource $r -Timeout $Timeout -PassThru:$PassThru -Action { + param ( $response ) + + if ($response.StatusCode -eq 200 -or $response.StatusCode -eq 202 -or $response.StatusCode -eq 204) { + Write-Verbose "Successfully purged $($r.AzsdkResourceType) '$($r.Name)'." -Verbose:$verboseFlag + } else { + Write-Warning "Failed purging $($r.AzsdkResourceType) '$($r.Name)' with status code $($response.StatusCode)." + } + }.GetNewClosure() + } + default { Write-Warning "Cannot purge $($r.AzsdkResourceType) '$($r.AzsdkName)'. Add support to https://github.com/Azure/azure-sdk-tools/blob/main/eng/common/scripts/Helpers/Resource-Helpers.ps1." } diff --git a/eng/common/scripts/Mark-ReleasePlanCompletion.ps1 b/eng/common/scripts/Mark-ReleasePlanCompletion.ps1 new file mode 100644 index 000000000..2e623672d --- /dev/null +++ b/eng/common/scripts/Mark-ReleasePlanCompletion.ps1 @@ -0,0 +1,69 @@ +param( + [Parameter(Mandatory = $true)] + [string]$PackageInfoFilePath +) + +<# +.SYNOPSIS + Marks release plan completion by identifying pull requests that changed files in a given path. + +.DESCRIPTION + This script helps to mark release plan completion by finding the active release plans for a package name + +.PARAMETER PackageInfoFilePath + The path to the package information file (required) or path to the directory containing package information files. +#> + +Set-StrictMode -Version 3 +. (Join-Path $PSScriptRoot common.ps1) +. (Join-Path $PSScriptRoot Helpers DevOps-WorkItem-Helpers.ps1) + + +#Get package properties +if (-Not (Test-Path $PackageInfoFilePath)) +{ + Write-Host "Package information file path $($PackageInfoFilePath) is invalid." + exit 0 +} + +function Process-Package([string]$packageInfoPath) +{ + # Get package info from json file created before updating version to daily dev + $pkgInfo = Get-Content $packageInfoPath | ConvertFrom-Json + $PackageVersion = $pkgInfo.Version + $PackageName = $pkgInfo.Name + if (!$PackageName -or !$PackageVersion) + { + Write-Host "Package name or version is not available in the package information file. Skipping the release plan status update for the package." + return + } + + # Check Azure DevOps Release Plan work items + Write-Host "Checking active release plan work items for package: $PackageName" + $workItems = Get-ReleasePlanForPackage $PackageName + if(!$workItems) + { + Write-Host "No active release plans found for package name: $PackageName." + return + } + + $activeReleasePlan = $workItems + if($workItems.Count -gt 1 -and ($workItems -is [System.Array])) + { + $concatenatedIds = ($workItems | Select-Object -ExpandProperty id) -join ',' + Write-Host "Multiple release plans found for package name: $PackageName with work item IDs: $concatenatedIds. Using the first release plan to update release status." + $activeReleasePlan = $workItems[0] + } + # Update release status + Write-Host "Release plan work item ID: $($activeReleasePlan["id"])" + Write-Host "Marking release completion for package, name: $PackageName version: $PackageVersion" + Update-ReleaseStatusInReleasePlan $activeReleasePlan.id "Released" $PackageVersion + Write-Host "Successfully marked release completion for package, name: $PackageName version: $PackageVersion." +} + +Write-Host "Finding all package info files in the path: $PackageInfoFilePath" +# Get all package info file under the directory given in input param and process +Get-ChildItem -Path $PackageInfoFilePath -Filter "*.json" | ForEach-Object { + Write-Host "Processing package info file: $_" + Process-Package $_.FullName +} \ No newline at end of file diff --git a/eng/common/spelling/Invoke-Cspell.ps1 b/eng/common/spelling/Invoke-Cspell.ps1 old mode 100644 new mode 100755