diff --git a/cli/azd/README.md b/cli/azd/README.md index 544d89b52ea..9733455b421 100644 --- a/cli/azd/README.md +++ b/cli/azd/README.md @@ -7,3 +7,5 @@ Install Go extension https://marketplace.visualstudio.com/items?itemName=golang. ## Contribute See [CONTRIBUTING.md](./CONTRIBUTING.md) for information on contributing. + +Trivial change to trigger CI build. diff --git a/eng/common/TestResources/New-TestResources.ps1 b/eng/common/TestResources/New-TestResources.ps1 index 078991250e6..06fe2eb2034 100755 --- a/eng/common/TestResources/New-TestResources.ps1 +++ b/eng/common/TestResources/New-TestResources.ps1 @@ -129,6 +129,11 @@ param ( $wellKnownTMETenants = @('70a036f6-8e4d-4615-bad6-149c02e7720d') +# People keep passing this legacy parameter. Throw an error to save them future keystrokes +if ($NewTestResourcesRemainingArguments -like '*UserAuth*') { + throw "The -UserAuth parameter is deprecated and is now the default behavior" +} + if (!$ServicePrincipalAuth) { # Clear secrets if not using Service Principal auth. This prevents secrets # from being passed to pre- and post-scripts. @@ -174,7 +179,7 @@ try { } Write-Verbose "Overriding test resources search directory to '$root'" } - + $templateFiles = @() "$ResourceType-resources.json", "$ResourceType-resources.bicep" | ForEach-Object { @@ -198,7 +203,7 @@ try { # returns empty string if $ServiceDirectory is not set $serviceName = GetServiceLeafDirectoryName $ServiceDirectory - + # in ci, random names are used # in non-ci, without BaseName, ResourceGroupName or ServiceDirectory, all invocations will # generate the same resource group name and base name for a given user @@ -267,13 +272,13 @@ try { if ($context.Tenant.Name -like '*TME*') { if ($currentSubscriptionId -ne '4d042dc6-fe17-4698-a23f-ec6a8d1e98f4') { Log "Attempting to select subscription 'Azure SDK Test Resources - TME (4d042dc6-fe17-4698-a23f-ec6a8d1e98f4)'" - $null = Select-AzSubscription -Subscription '4d042dc6-fe17-4698-a23f-ec6a8d1e98f4' -ErrorAction Ignore + $null = Select-AzSubscription -Subscription '4d042dc6-fe17-4698-a23f-ec6a8d1e98f4' -ErrorAction Ignore -WarningAction Ignore # Update the context. $context = Get-AzContext } } elseif ($currentSubcriptionId -ne 'faa080af-c1d8-40ad-9cce-e1a450ca5b57') { Log "Attempting to select subscription 'Azure SDK Developer Playground (faa080af-c1d8-40ad-9cce-e1a450ca5b57)'" - $null = Select-AzSubscription -Subscription 'faa080af-c1d8-40ad-9cce-e1a450ca5b57' -ErrorAction Ignore + $null = Select-AzSubscription -Subscription 'faa080af-c1d8-40ad-9cce-e1a450ca5b57' -ErrorAction Ignore -WarningAction Ignore # Update the context. $context = Get-AzContext } @@ -305,7 +310,7 @@ try { } } - # This needs to happen after we set the TenantId but before we use the ResourceGroupName + # This needs to happen after we set the TenantId but before we use the ResourceGroupName if ($wellKnownTMETenants.Contains($TenantId)) { # Add a prefix to the resource group name to avoid flagging the usages of local auth # See details at https://eng.ms/docs/products/onecert-certificates-key-vault-and-dsms/key-vault-dsms/certandsecretmngmt/credfreefaqs#how-can-i-disable-s360-reporting-when-testing-customer-facing-3p-features-that-depend-on-use-of-unsafe-local-auth @@ -353,15 +358,19 @@ 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') { - # Support corp tenant and TME tenant user id lookups - $user = Get-AzADUser -Mail $context.Account.Id - if ($null -eq $user -or !$user.Id) { - $user = Get-AzADUser -UserPrincipalName $context.Account.Id + # 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 $user -or !$user.Id) { + if ($null -eq $userAccountId) { throw "Failed to find entra object ID for the current user" } - $ProvisionerApplicationOid = $user.Id + $ProvisionerApplicationOid = $userAccountId } elseif ($context.Account.Type -eq 'ServicePrincipal') { $sp = Get-AzADServicePrincipal -ApplicationId $context.Account.Id $ProvisionerApplicationOid = $sp.Id @@ -428,20 +437,25 @@ 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." } - # Support corp tenant and TME tenant user id lookups - $userAccount = (Get-AzADUser -Mail (Get-AzContext).Account.Id) - if ($null -eq $userAccount -or !$userAccount.Id) { - $userAccount = (Get-AzADUser -UserPrincipalName (Get-AzContext).Account) + $userAccountName = (Get-AzContext).Account.Id + # HomeAccountId format is '.' + # 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 $userAccount -or !$userAccount.Id) { + if ($null -eq $userAccountId) { throw "Failed to find entra object ID for the current user" } - $TestApplicationOid = $userAccount.Id + $TestApplicationOid = $userAccountId $TestApplicationId = $testApplicationOid - $userAccountName = $userAccount.UserPrincipalName Log "User authentication with user '$userAccountName' ('$TestApplicationId') will be used." } # If user has specified -ServicePrincipalAuth @@ -613,22 +627,44 @@ try { } Log $msg - $deployment = Retry { - New-AzResourceGroupDeployment ` + # Run a first pass outside of Retry to fail fast for + # template validation errors that won't be fixed with retries. + # Only run Test-AzResourceGroupDeployment after error because it can + # take a while for large templates even during success cases. + try { + $deployment = New-AzResourceGroupDeployment ` + -Name $BaseName ` + -ResourceGroupName $resourceGroup.ResourceGroupName ` + -TemplateFile $templateFile.jsonFilePath ` + -TemplateParameterObject $templateFileParameters ` + -Force:$Force + } catch { + # Throw if we hit a template validation error, otherwise proceed + if ($_.Exception.Message -like '*InvalidTemplateDeployment*') { + $validation = Test-AzResourceGroupDeployment ` + -ResourceGroupName $resourceGroup.ResourceGroupName ` + -TemplateFile $templateFile.jsonFilePath ` + -TemplateParameterObject $templateFileParameters + + HandleTemplateDeploymentError $validation + throw + } + } + + if (!$deployment -or $deployment.ProvisioningState -ne 'Succeeded') { + Write-Warning "Initial deployment attempt failed, retrying..." + $deployment = Retry -Attempts 4 -Action { + New-AzResourceGroupDeployment ` -Name $BaseName ` -ResourceGroupName $resourceGroup.ResourceGroupName ` -TemplateFile $templateFile.jsonFilePath ` -TemplateParameterObject $templateFileParameters ` -Force:$Force + } } + if ($deployment.ProvisioningState -ne 'Succeeded') { - Write-Host "Deployment '$($deployment.DeploymentName)' has state '$($deployment.ProvisioningState)' with CorrelationId '$($deployment.CorrelationId)'. Exiting..." - Write-Host @' -##################################################### -# For help debugging live test provisioning issues, # -# see http://aka.ms/azsdk/engsys/live-test-help # -##################################################### -'@ + HandleDeploymentFailure $deployment exit 1 } diff --git a/eng/common/TestResources/TestResources-Helpers.ps1 b/eng/common/TestResources/TestResources-Helpers.ps1 index cbe047ebc5f..c6c118cc503 100644 --- a/eng/common/TestResources/TestResources-Helpers.ps1 +++ b/eng/common/TestResources/TestResources-Helpers.ps1 @@ -185,8 +185,6 @@ function LintBicepFile([string] $path) { } # Work around lack of config file override: https://github.com/Azure/bicep/issues/5013 - $output = bicep lint $path 2>&1 - if ($useBicepCli) { $output = bicep lint $path 2>&1 } else { @@ -264,9 +262,10 @@ function SetDeploymentOutputs( ) { $deploymentEnvironmentVariables = $environmentVariables.Clone() $deploymentOutputs = BuildDeploymentOutputs $serviceName $azContext $deployment $deploymentEnvironmentVariables + $isBicep = $templateFile.originalFilePath -and $templateFile.originalFilePath.EndsWith(".bicep") - if ($OutFile) { - if ($IsWindows -and $Language -eq 'dotnet') { + # Azure SDK for .NET on Windows uses DPAPI-encrypted, JSON-encoded environment variables. + if ($OutFile -and $IsWindows -and $Language -eq 'dotnet') { $outputFile = "$($templateFile.originalFilePath).env" $environmentText = $deploymentOutputs | ConvertTo-Json; @@ -276,29 +275,29 @@ function SetDeploymentOutputs( Set-Content $outputFile -Value $protectedBytes -AsByteStream -Force Write-Host "Test environment settings`n$environmentText`nstored into encrypted $outputFile" + } + # Any Bicep template in a repo that has opted into .env files. + elseif ($OutFile -and $isBicep) { + $bicepTemplateFile = $templateFile.originalFilePath + + # Make sure the file would not write secrets to .env file. + if (!(LintBicepFile $bicepTemplateFile)) { + Write-Error "$bicepTemplateFile may write secrets. No file written." } - elseif ($templateFile.originalFilePath -and $templateFile.originalFilePath.EndsWith(".bicep")) { - $bicepTemplateFile = $templateFile.originalFilePath + $outputFile = $bicepTemplateFile | Split-Path | Join-Path -ChildPath '.env' - # Make sure the file would not write secrets to .env file. - if (!(LintBicepFile $bicepTemplateFile)) { - Write-Error "$bicepTemplateFile may write secrets. No file written." + # Make sure the file would be ignored. + git check-ignore -- "$outputFile" > $null + if ($?) { + $environmentText = foreach ($kv in $deploymentOutputs.GetEnumerator()) { + "$($kv.Key)=`"$($kv.Value)`"" } - $outputFile = $bicepTemplateFile | Split-Path | Join-Path -ChildPath '.env' - - # Make sure the file would be ignored. - git check-ignore -- "$outputFile" > $null - if ($?) { - $environmentText = foreach ($kv in $deploymentOutputs.GetEnumerator()) { - "$($kv.Key)=`"$($kv.Value)`"" - } - Set-Content $outputFile -Value $environmentText -Force - Write-Host "Test environment settings`n$environmentText`nstored in $outputFile" - } - else { - Write-Error "$outputFile is not ignored by .gitignore. No file written." - } + Set-Content $outputFile -Value $environmentText -Force + Write-Host "Test environment settings`n$environmentText`nstored in $outputFile" + } + else { + Write-Error "$outputFile is not ignored by .gitignore. No file written." } } else { @@ -349,3 +348,35 @@ function SetDeploymentOutputs( return $deploymentEnvironmentVariables, $deploymentOutputs } + +function HandleTemplateDeploymentError($templateValidationResult) { + Write-Warning "Deployment template validation failed" + + if (!$templateValidationResult.Details.Message) { + Write-Warning "Could not parse template validation error" + return + } + + # Retrieve one or more messages then decode the strings for readability (remove quote escapes, fix link readability, etc.) + $parsedMessage = ($templateValidationResult.Details.Message -join "$([System.Environment]::NewLine)$([System.Environment]::NewLine)") + $parsedMessage = [System.Net.WebUtility]::UrlDecode($parsedMessage) + + Write-Warning "#####################################################" + Write-Warning "######### TEMPLATE VALIDATION ERROR DETAILS #########" + Write-Warning "#####################################################" + Write-Host $parsedMessage + Write-Warning "#####################################################" +} + +function HandleDeploymentFailure($deployment) { + Write-Host "Deployment '$($deployment.DeploymentName)' has state '$($deployment.ProvisioningState)' with CorrelationId '$($deployment.CorrelationId)'. Exiting..." + Write-Host @' +##################################################### +# For help debugging live test provisioning issues, # +# see http://aka.ms/azsdk/engsys/live-test-help # +##################################################### +'@ + $queryTime = (Get-Date).AddMinutes(-10).ToString("o") + Write-Host "To check the activity log with the below command after waiting 2 minutes for propagation:" + Write-Host "(Get-AzActivityLog -CorrelationId '$($deployment.CorrelationId)' -StartTime '$queryTime').Properties.Content.statusMessage" +} diff --git a/eng/common/TestResources/deploy-test-resources.yml b/eng/common/TestResources/deploy-test-resources.yml index 30efe36e231..4429d6631aa 100644 --- a/eng/common/TestResources/deploy-test-resources.yml +++ b/eng/common/TestResources/deploy-test-resources.yml @@ -2,6 +2,7 @@ parameters: ServiceDirectory: '' TestResourcesDirectory: '' ArmTemplateParameters: '@{}' + AdditionalParameters: '@{}' DeleteAfterHours: 8 Location: '' EnvVars: {} @@ -103,7 +104,8 @@ steps: -Location '${{ parameters.Location }}' ` -DeleteAfterHours '${{ parameters.DeleteAfterHours }}' ` @subscriptionConfiguration ` - -AdditionalParameters ${{ parameters.ArmTemplateParameters }} ` + -ArmTemplateParameters ${{ parameters.ArmTemplateParameters }} ` + -AdditionalParameters ${{ parameters.AdditionalParameters }} ` -AllowIpRanges ('$(azsdk-corp-net-ip-ranges)' -split ',') ` -SelfContainedPostScript $postScriptPath ` -CI ` @@ -148,7 +150,8 @@ steps: -Location '${{ parameters.Location }}' ` -DeleteAfterHours '${{ parameters.DeleteAfterHours }}' ` @subscriptionConfiguration ` - -AdditionalParameters ${{ parameters.ArmTemplateParameters }} ` + -ArmTemplateParameters ${{ parameters.ArmTemplateParameters }} ` + -AdditionalParameters ${{ parameters.AdditionalParameters }} ` -AllowIpRanges ('$(azsdk-corp-net-ip-ranges)' -split ',') ` -CI ` -ServicePrincipalAuth ` 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 00000000000..486d59436be --- /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_release_sdk` tool with the provided package name, selected language, and set checkReady to true. + - 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/check-package-validation.instructions.md b/eng/common/instructions/azsdk-tools/check-package-validation.instructions.md new file mode 100644 index 00000000000..8a1238c81f0 --- /dev/null +++ b/eng/common/instructions/azsdk-tools/check-package-validation.instructions.md @@ -0,0 +1,23 @@ +--- +description: 'Run the package validation checks for the SDK package.' +--- +## Goal +Run the validation checks for the SDK package by collecting the required information from the user and executing the `azsdk_package_run_check` tool. + +## Instructions +1. **Collect Required Information**: + - Prompt the user for the absolute package path to the package directory for the SDK package they want to check. + - Prompt the user to specify values for each of the parameters required by the azsdk_package_run_check tool. Present the user with options to pick from for the allowed values specified by the parameter schema. + +2. **Execute Check**: + - Use the `azsdk_package_run_check` tool with the package path and check type. + +3. **Present Results**: + - If the package has passed all validation checks, highlight and finish + - If the package is not ready, display the specific check types that need to be fixed for each language. + +## Expected User Interaction Flow +1. Ask: "What is the exact path of the package you want to check for validation?" +2. Ask: "Please specify the check type you want to run (e.g., 'all', 'linting', 'format', etc.)" +3. Execute the check using the provided information +4. Display results and next steps \ No newline at end of file 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 00000000000..6d82d3272ec --- /dev/null +++ b/eng/common/instructions/azsdk-tools/create-release-plan.instructions.md @@ -0,0 +1,64 @@ +> **CRITICAL INSTRUCTIONS FOR LLM** +> 1. Backend processes may use Azure DevOps work item data as needed. However, do **not** mention or display the work item link/URL, or any reference to Azure DevOps work item resources directly in conversation with the user. +> 2. All manual updates to an existing release plan must be made through the [Release Planner Tool](https://aka.ms/sdk-release-planner) to ensure proper tracking and validation. +> 3. Only provide release plan details such as **Release Plan Link** and **Release Plan ID** to the user +> 4. Always check the `NextSteps` field in the tool response and follow any additional prompts provided. Do NOT proceed to other steps until the NextSteps are completed. + +# Release Plan Creation Process +Your 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 for Existing Release Plan +- Ask the user if they already have an existing release plan +- If they confirm: + - Query the existing release plan using either: + - The release plan number, or + - The API spec pull request link + - Display the existing release plan details (Release Plan ID, status, associated languages, SDK PRs). +- If no existing release plan is found, continue to Step 3 to gather required details for creating a new one. + +## 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: + +- **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 +- If existing release plans are found, extract and display key information: Release Plan ID, status, associated languages, SDK PRs +- 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 + > **GitHub CLI Authentication Required:** + > Before running any SDK PR linking steps, ensure you are authenticated with GitHub CLI. + > Run: `gh auth login` + > You can check authentication status with `gh auth status`. + - 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/local-sdk-workflow.instructions.md b/eng/common/instructions/azsdk-tools/local-sdk-workflow.instructions.md new file mode 100644 index 00000000000..507e9a6cce4 --- /dev/null +++ b/eng/common/instructions/azsdk-tools/local-sdk-workflow.instructions.md @@ -0,0 +1,157 @@ +--- +description: "Guide the user to generate and build SDKs locally for a TypeSpec based API spec" +--- + +# Goal + +Help the user generate and build SDKs locally from TypeSpec API specifications using the `azure-sdk-mcp` tools. +High level steps involved: +1. Generate SDK locally +2. Build / Compile SDK locally +3. Run package checks +4. Run package tests +5. Update change log, metadata and version + +--- + +## Generate SDK Locally + +### Step 1: Outline workflow + +**Goal**: Ensure the user understands the overall SDK generation and build process before starting. +**Actions**: + +- Present the high-level steps involved in generating and building SDK locally: + 1. Select target language + 2. Verify SDK repository + 3. Validate repository path + 4. Identify path to configuration file + 5. Verify setup for the selected language + 6. Generate SDK using `azsdk_package_generate_code` MCP tool + 7. Identify SDK project path + 8. Build/Compile SDK using `azsdk_package_build_code` MCP tool +- Ask the user to confirm readiness to proceed. + +--- + +### Step 2: Select language + +**Goal**: Confirm the target language for SDK generation. +**Actions**: + +- Prompt user to choose one of the supported languages: + - .NET + - Java + - JavaScript + - Python + - Go +- Validate input against the allowed list. + +--- + +### Step 3: Verify SDK repository + +**Goal**: Ensure the correct Azure SDK language repository is available locally. +**Actions**: + +- Prompt user to provide the path to their **locally cloned repository** for the selected language. +- Note: The **local folder name can be arbitrary**, but the repository must have originated from one of the official Azure SDK repositories: + - `azure-sdk-for-net` (.NET) + - `azure-sdk-for-java` (Java) + - `azure-sdk-for-js` (JavaScript) + - `azure-sdk-for-python` (Python) + - `azure-sdk-for-go` (Go) +- If the repository is not cloned → instruct user to clone the appropriate remote repository from GitHub. +- MCP tool will automatically validate the remote origin and repository structure. + +--- + +### Step 4: Validate repository path + +**Actions**: + +- Check if the provided repository path exists and matches the selected SDK language repository. +- If invalid → prompt user to re-enter a valid path. + +--- + +### Step 5: Identify path to configuration file + +**Goal**: Determine the correct path to the TypeSpec configuration file based on the working context. +**Actions**: + +- **Scenario A: Working in a repository cloned from `azure-rest-api-specs`** + - Identify the path to `tspconfig.yaml` (local path or HTTPS URL). + - The local folder name can be arbitrary; the MCP tool will validate that the remote origin URL points to the official `azure-rest-api-specs` repository. + - Example paths (pointing directly to tspconfig.yaml): + - `/home/usr/azure-rest-api-specs/specification/contosowidgetmanager/Contoso.Management/tspconfig.yaml` + - `https://github.com/Azure/azure-rest-api-specs/blob/4af373fc5826cf5a2365a20dde01c4b2efde48f0/specification/contosowidgetmanager/Contoso.Management/tspconfig.yaml` + +- **Scenario B: Working in one of the official Azure SDK language repositories** + (i.e., originally cloned from `azure-sdk-for-net`, `azure-sdk-for-java`, `azure-sdk-for-js`, `azure-sdk-for-python`, `azure-sdk-for-go`) + - Identify the path to `tsp-location.yaml`. + - The local folder name can be arbitrary; MCP tool will validate the remote origin URL. + - Example path: + `/home/usr/azure-sdk-for-net/sdk/contoso/Azure.ResourceManager.Contoso/tsp-location.yaml` + +--- + +### Step 6: Verify setup for selected language +**Actions**: +- Run `azsdk_verify_setup` MCP tool to ensure the local environment is correctly configured for the selected SDK language. + +--- + +### Step 7: Generate SDK + +**Actions**: +- Run `azsdk_package_generate_code` MCP tool to generate the SDK locally. + +--- + +## Build / Compile SDK Locally + +### Step 1: Identify SDK project path + +**Goal**: Locate the generated SDK project directory for building/compiling. +**Actions**: + +- Find the project directory inside the selected Azure SDK language repository. +- Typical structure: + `sdk/{service-name}/{package-name}/` +- Example: + `/path/to/azure-sdk-for-net/contoso/Azure.ResourceManager.Contoso/` + +--- + +### Step 2: Build/Compile the SDK + +**Actions**: + +- Run `azsdk_package_build_code` MCP tool to compile the SDK in the identified project directory. + +--- + +### Step 3: Run package validation + +**Actions**: + +- Run `azsdk_package_run_check` MCP tool to validate the generated SDK package in the identified project directory. + +--- + +### Step 4: Run package tests + +**Actions**: + +- Run `azsdk_package_run_tests` MCP tool to run tests on the generated SDK package in the identified project directory. + +--- + +### Step 5: Update change log, metadata and version + +**Actions**: + +- Run `azsdk_package_update_metadata` MCP tool to update metadata in the identified project directory. +- Run `azsdk_package_update_changelog_content` MCP tool to update change log in the identified project directory. +- Run `azsdk_package_update_version` MCP tool to update version in the identified project directory. \ 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 00000000000..bbf8b484119 --- /dev/null +++ b/eng/common/instructions/azsdk-tools/sdk-details-in-release-plan.instructions.md @@ -0,0 +1,52 @@ +--- +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. + - For Java and Python, use `emitter-output-dir` for package name if it exists. Otherwise use `package-dir` to get the package name as fallback approach. + - For .NET, use namespace property to get package name. + - For JavaScript, use `packagedetails:name` property to get package name. + - For Go, use module name and remove `github.com/Azure/azure-sdk-for-go/` to get package name. +3. Map the language name in emitter to one of the following in Pascal case(except .NET): + - .NET + - Java + - Python + - JavaScript + - Go +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 +> **(MANDATORY - DO NOT SKIP) ALWAYS validate all package names against the format rules AND the examples table before calling any update tool, even if the user provides SDK details directly. Auto-correct and inform the user of invalid package names.** +> - **JavaScript**: Must start with `@azure/` +> - **Go**: Must start with `sdk/` +> +> **Valid package name examples (compare against these to catch invalid formats):** +> | Language | Valid | Invalid | +> |----------|-------|---------| +> | JavaScript | `@azure/arm-compute` | `arm-compute`, `azure/arm-compute`,`@azure-arm-compute` | +> | Go (management plane) | `sdk/resourcemanager/compute/armcompute` | `sdk/armcompute`, `/sdk/compute`, `github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute` | + +**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-docs.instructions.md b/eng/common/instructions/azsdk-tools/typespec-docs.instructions.md new file mode 100644 index 00000000000..314555691f7 --- /dev/null +++ b/eng/common/instructions/azsdk-tools/typespec-docs.instructions.md @@ -0,0 +1,11 @@ +Your goal is to provide the most up-to-date documentation on the TypeSpec language, core libraries, and writing Azure services and clients in TypeSpec. Whenever a user asks a question about how something should be written in TypeSpec, you should use the documentation links below to pinpoint the most relevant and accurate information. + +## TypeSpec Azure + +- https://azure.github.io/typespec-azure/docs/llms.txt contains an index of up-to-date documentation for TypeSpec Azure libraries, including how to write service specifications for Azure Resource Manager (ARM) and Azure Data Plane services, as well as how to customize generated clients. +- Always refer to https://azure.github.io/typespec-azure/docs/llms.txt when a user asks TypeSpec related questions in case there is existing documentation that can help answer them. This also applies when a user asks to make changes to TypeSpec specifications. + +## TypeSpec documentation + +- https://typespec.io/docs/llms.txt contains an index of up-to-date documentation for TypeSpec language basics and core libraries, including how concepts like visibility and versioning work. +- Always refer to https://typespec.io/docs/llms.txt when a user asks TypeSpec related questions that aren't covered by the TypeSpec Azure topics. 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 00000000000..15d8b22acf0 --- /dev/null +++ b/eng/common/instructions/azsdk-tools/typespec-to-sdk.instructions.md @@ -0,0 +1,94 @@ +--- +description: 'Generate SDKs from TypeSpec' +--- +Your goal is to guide the user through the process of generating SDKs from TypeSpec projects. **Before starting**, show all the high level steps to the user and ask: + +> "Would you like to begin the SDK generation process now? (yes/no)" + +Wait for the user to respond with a confirmation before proceeding. Use the provided tools to perform actions and gather information as needed. + +SDK languages to be generated: +- Management Plane: .NET, Go, Java, JavaScript, Python +- Data Plane: .NET, Java, JavaScript, Python + +Pre-requisites: +- TypeSpec project path is available in the current context or provided by user. If not available, prompt user to provide the TypeSpec project root path (local path or GitHub URL). + +# SDK generation steps + +## Step: Generate SDKs + +As a first step, you must prompt the user to understand the intention of SDK generation. Based on the user input, you can either run SDK generation locally or use SDK generation pipeline. + +Generate SDK locally in cases below: +- If the user wants to walk through each SDK generation step locally. This approach requires user to have the setup for each language to generate SDK locally. User can create a pull request for each language after completing all the steps in SDK generation successfully to get the generated SDK reviewed and merged. +- If SDK or client.tsp customizations are needed before creating a pull request. +- If the user wants to add tests, samples or any customization to the generated SDK. + +Generate SDK using pipeline: +- If the user wants to generate SDK and get a PR automatically created in Azure SDK language repositories. This approach can be followed if the user does not want to setup local environment for each language. + +### Generate SDK locally: + +**Condition**: If user chooses to generate SDK locally +**Message to user**: "Generating SDKs locally requires you to have the development environment set up for each language. Prompt the user to create a pull request for each language after completing the following steps successfully: generation, validation, build, test and update of metadata, change log, and version." +**Actions**: +Follow the steps in #file:local-sdk-workflow.instructions.md to generate and build SDKs locally from TypeSpec project. + +### Generate SDK using pipeline: + +**Condition**: If user chooses to generate the SDK using pipeline or when generating SDK by GitHub coding agent +**Message to user**: "SDK generation will take approximately 15-20 minutes. SDKs are generated using the Azure DevOps pipeline. SDK generation is supported only from a merged API spec or from an API spec pull request in the https://github.com/Azure/azure-rest-api-specs repository." +**Actions**: +1. Identify whether TypeSpec is for Management Plane or Data Plane based on project structure and files. tspconfig.yaml file contains `resource-manager` for management plane and `data-plane` for data plane as resource provider. + - Execute the SDK generation pipeline with the following required parameters for all languages: + - TypeSpec project root path + - API spec pull request number (if the API spec is not merged to the main branch, otherwise use 0) + - API version + - SDK release type (`beta` for preview API versions, `stable` otherwise) + - Language options: + For management plane: `Python`, `.NET`, `JavaScript`, `Java`, `Go` + For data plane: `Python`, `.NET`, `JavaScript`, `Java` + - Each SDK generation tool call should show a label to indicate the language being generated. +2. Monitor pipeline status after 15 minutes and provide updates. If pipeline is in progress, inform user that it may take additional time and check the status later. +3. Display generated SDK PR links when available. If pipeline fails, inform user with error details and suggest to check pipeline logs for more information. +4. If SDK pull request is available for all languages, ask user to review generated SDK pull request and mark them as ready for review when they are ready to get them reviewed and merged. +5. Inform the user that they can checkout generated SDK pull request locally and add more tests, samples or code customizations if needed using local SDK generation tools. +6. If SDK pull request was created for test purposes, inform user to close the test SDK pull request. +**Success Criteria**: SDK generation pipeline initiated and SDKs generated + +## Step: SDK release plan +**Goal**: Create a release plan for the generated SDKs +**Condition**: Only if SDK PRs are created +**Message to user**: "Creating a release plan is essential to manage the release of the generated SDKs. Each release plan must include SDKs for all required languages based on the TypeSpec project type (Management Plane or Data Plane) or request exclusion approval for any excluded language. SDK pull request needs to get approval and merged to main branch before releasing the SDK package." +**Actions**: +1. Prompt the user to check if they generated the SDK just to test or do they intend to release it: "Do you want to create a release plan for the generated SDKs to publish them? (yes/no)" + - If no, inform user: "You can create a release plan later when ready to release the SDK" and end the workflow. +2. Ask user if they have already created a release plan for the generated SDKs: "Have you already created a release plan for the generated SDKs? (yes/no)" + - If no, proceed to create a new release plan + - If yes, get release plan details and show them to user +3. Prompt the user to provide the API spec pull request link if not already available in the current context. +4. If unsure, check if a release plan already exists for API spec pull request. +5. Prompt user to find the service id and product id in service tree `aka.ms/st` and provide them. Stress the importance of correct service id and product id for proper release plan creation. +6. If a new release plan is needed, refer to #file:create-release-plan.instructions.md to create a release plan using the spec pull request. API spec pull request is required to create a release plan. +7. Prompt user to change spec PR to ready for review: "Please change the spec pull request to ready for review status" +8. Suggest users to follow the instructions on spec PR to get approval from API reviewers and merge the spec PR. +9. Link SDK pull requests to the release plan. +10. Each release plan must release SDK for all languages based on the TypeSpec project type (Management Plane or Data Plane). If any language is missing, inform user: "The release plan must include SDKs for all required languages: Python, .NET, JavaScript, Java, Go (for Management Plane) or Python, .NET, JavaScript, Java (for Data Plane)". If it is intentional to exclude a language then user must provide a justification for it. +**Success Criteria**: Release plan created and linked to SDK PRs + +## Step: Release SDK Package +**Goal**: Release the SDK package using the release plan +**Actions**: +1. Prompt the user to confirm if they want to release the SDK package now: "Do you want to release the SDK package now? (yes/no)" + - If no, inform user: "You can release the SDK package later using the prompt `release for `" and end the workflow. +2. Get SDK pull request status for all languages. +3. Inform user that it needs to check SDK PR status. If any SDK pull request is not merged, inform user: "Please merge all SDK pull requests before releasing the package.". Show a summary of SDK PR status for all languages. +4. If an SDK pull request is merged then run release readiness of the package for that language. +5. Inform user if a language is ready for release and prompt user for a confirmation to proceed with the release. +6. If user confirms, run the tool to release the SDK package. +7. Inform user to approve the package release using release pipeline. Warn user that package will be published to public registries once approved. +8. Identify remaining languages to be released and inform user to release SDK for all languages to complete the release plan. Release plan completion is required for KPI attestation in service tree. + +## Process Complete +Display summary of all created PRs and next steps for user. 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 00000000000..83167f459d0 --- /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 00000000000..95dd71e174c --- /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/verify-namespace-approval.instructions.md b/eng/common/instructions/azsdk-tools/verify-namespace-approval.instructions.md new file mode 100644 index 00000000000..cee268de74e --- /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/instructions/azsdk-tools/verify-setup.instructions.md b/eng/common/instructions/azsdk-tools/verify-setup.instructions.md new file mode 100644 index 00000000000..8fe1427083c --- /dev/null +++ b/eng/common/instructions/azsdk-tools/verify-setup.instructions.md @@ -0,0 +1,28 @@ +--- +description: 'Verify Setup' +--- + +## Goal +This tool verifies the developer's environment for SDK development and release tasks. It returns what requirements are missing for the specified languages and repo, or success if all requirements are satisfied. It can help install supported requirements. + +Your goal is to identify the project repo root, and pass in the `packagePath` to the Verify Setup tool. For a language repo, pass in the language of the repo. + +## Instructions +1. Check what's missing by calling verify setup with just language and package path parameters. The tool responds with missing requirements, and if any are installable by the tool itself. +2. Ask the user if they want help installing any missing requirements that the tool can install. On approval, **use the tool again WITH THE EXACT PARAMETERS (langs=, packagePath=, requirementsToInstall=["req", "req2", ...])**. + +## Examples +- in `azure-sdk-for-js`, run `azsdk_verify_setup` with `(langs=javascript, packagePath=/azure-sdk-for-js)`. +- to install, run `azsdk_verify_setup` with `(langs=javascript, packagePath=/azure-sdk-for-js), requirementsToInstall=['pnpm', 'tsp'])`. + +## Parameter Requirements +The user can specify multiple languages to check. If the user wants to check all languages, pass in ALL supported languages. Passing in no languages will only check the core requirements. + +To help the user auto-install, YOU MUST pass in the exact approved list of requirements to install in `requirementsToInstall`. **The tool will not install any requirements that are not EXPLICITLY LISTED.** DO NOT PASS IN AN AUTOINSTALL BOOLEAN. + +## Output +Display clear, step-by-step instructions on how to resolve any missing requirements identified. Explain why the requirement is necessary if it has a `reason` field. Organize requirements into categorical sections. + +Based on the user's shell environment, enhance the tool instructions with shell-specific commands for resolving missing dependencies. + +When Python tool requirements fail, inform the user about the `AZSDKTOOLS_PYTHON_VENV_PATH` environment variable if they have setup issues. The verify-setup tool can only check Python requirements within the virtual environment specified by this environment variable. \ No newline at end of file diff --git a/eng/common/instructions/copilot/sdk-release.instructions.md b/eng/common/instructions/copilot/sdk-release.instructions.md new file mode 100644 index 00000000000..e9b9f62ca53 --- /dev/null +++ b/eng/common/instructions/copilot/sdk-release.instructions.md @@ -0,0 +1,19 @@ +# SDK release + +There are two tools to help with SDK releases: +- Check SDK release readiness +- Release SDK + +## Check SDK Release Readiness +Run `CheckPackageReleaseReadiness` to verify if the package is ready for release. This tool checks: +- API review status +- Change log status +- Package name approval(If package is new and releasing a preview version) +- Release date is set in release tracker + +## Release SDK +Run `ReleasePackage` to release the package. This tool requires package name and language as inputs. It will: +- Check if the package is ready for release +- Identify the release pipeline +- Trigger the release pipeline. +User needs to approve the release stage in the pipeline after it is triggered. \ No newline at end of file diff --git a/eng/common/knowledge/customizing-client-tsp.md b/eng/common/knowledge/customizing-client-tsp.md new file mode 100644 index 00000000000..e9a4520541b --- /dev/null +++ b/eng/common/knowledge/customizing-client-tsp.md @@ -0,0 +1,510 @@ +# TypeSpec Client Customizations Reference + +## Quick Setup + +### 1. Project Structure + +``` +project/ +├── main.tsp # Your service definition +├── client.tsp # Client customizations +└── tspconfig.yaml # Compiler configuration +``` + +### 2. Basic client.tsp Template + +```typespec +import "./main.tsp"; +import "@azure-tools/typespec-client-generator-core"; + +using Azure.ClientGenerator.Core; + +// Your customizations here +``` + +## Client Customizations Namespace + +`client.tsp` should have a file-level namespace if types (e.g. models, interfaces, operations, etc) are defined in `client.tsp`. +Do not add a file-level namespace if one already exists. They are only required when types are defined in `client.tsp`. + +```typespec +import "./main.tsp"; +import "@azure-tools/typespec-client-generator-core"; + +using Azure.ClientGenerator.Core; + +namespace ClientCustomizations; + +// Your customizations here +``` + +## Universal Scope Parameter + +**IMPORTANT**: All Azure.ClientGenerator.Core decorators support an optional `scope` parameter as their final parameter to target specific language emitters. + +### Scope Syntax + +```typespec +@decoratorName(/* decorator-specific params */, scope?: string) +``` + +### Scope Patterns + +#### Target Specific Languages + +```typespec +// Single language +@@clientName(Foo, "Bar", "python") +@@access(Foo.get, Access.internal, "csharp") + +// Multiple languages (comma-separated) +@@clientName(Foo, "Bar", "python, javascript") +``` + +#### Exclude Languages (Negation) + +```typespec +// Exclude one language +@@clientName(Foo, "Bar", "!csharp") // All languages EXCEPT C# + +// Exclude multiple languages +@@clientName(Foo, "Bar", "!python, !go") // All languages EXCEPT python and go +``` + +#### Language Identifiers + +- `"csharp"` - C#/.NET +- `"python"` - Python +- `"java"` - Java +- `"javascript"` - TypeScript/JavaScript +- `"go"` - Go +- `"rust"` - Rust + +### Scope Best Practices + +#### DO: Use negation for single language exclusions + +```typespec +// Good: Exclude only C# +@@clientName(get, "getFoo", "!csharp") + +// Bad: List all other languages +@@clientName(get, "getFoo", "python, java, javascript, go") +``` + +#### DO: Combine scopes when logic is identical + +```typespec +// Good: Same customization for multiple languages +@@clientName(name, "sharedName", "python, go") + +// Avoid: Duplicate decorators +@@clientName(name, "sharedName", "python") +@@clientName(name, "sharedName", "go") +``` + +#### DON'T: Overuse scopes without clear need + +```typespec +// Bad: Unnecessary scope for universal customization +@@clientName(MyService, "MyClient", "csharp, python, java, javascript, go") + +// Good: No scope means all languages +@@clientName(MyService, "MyClient") +``` + +## Core Decorators + +### @access + +**Purpose**: Control visibility of types and operations in generated clients. +**Syntax**: `@access(value: Access.public | Access.internal, scope?: string)` +**Usage**: + +```typespec +// Hide internal operations +@@access(getFoo, Access.internal) + +// Make models referenced by internal operations public +@@access(Foo, Access.public) + +// Language-specific access +@@access(getFoo, Access.internal, "csharp") +``` + +**Propagation Rules**: + +- Operations marked `Access.internal` make their models internal +- Operations marked `Access.public` make their models public +- Namespace access propagates to contained types +- Model access propagates to properties and inheritance hierarchy + +### @client + +**Purpose**: Define root clients in the SDK. +**Restrictions**: Cannot be used with `@clientLocation` decorator. Cannot be used as an augmentation (`@@`) decorator. +**Important**: `@client` has to be used on a type defined in `client.tsp`, so a file-level namespace (e.g. `namespace ClientCustomizations;`) should be added if one does not exist. +**Syntax**: `@client(options: ClientOptions, scope?: string)` +**Usage**: + +```typespec +// Basic client +@client({ service: MyService }) +interface MyClient {} + +// Named client +@client({ service: MyService, name: "CustomClient" }) +interface MyClient {} + +// Split operations into multiple clients +@client({ service: PetStore, name: "FoodClient" }) +interface FoodClient { + feed is PetStore.feed; +} + +@client({ service: PetStore, name: "PetClient" }) +interface PetClient { + pet is PetStore.pet; +} +``` + +### @operationGroup + +**Purpose**: Define sub-clients (operation groups). +**Restrictions**: Cannot be used with `@clientLocation` decorator. Cannot be used as an augmentation (`@@`) decorator. +**Important**: `@operationGroup` has to be used on a type defined in `client.tsp`, so a file-level namespace (e.g. `namespace ClientCustomizations;`) should be added if one does not exist. +**Syntax**: `@operationGroup(scope?: string)` +**Usage**: + +```typespec +@client({ service: MyService }) +namespace MyClient; + +@operationGroup +interface Pets { + list is MyService.listPets; + get is MyService.getPet; +} + +@operationGroup +interface Users { + list is MyService.listUsers; + create is MyService.createUser; +} +``` + +### @clientLocation + +**Purpose**: Move operations between clients without restructuring. +**Restrictions**: Cannot be used with `@client` or `@operationGroup` decorators. +**Syntax**: `@clientLocation(target: Interface | Namespace | string, scope?: string)` +**Usage**: + +```typespec +// Move to existing client +@@clientLocation(MyService.upload, AdminOperations); + +// Move to new client +@@clientLocation(MyService.archive, "ArchiveClient"); + +// Move to root client +@@clientLocation(MyService.SubClient.health, MyService); + +// Move parameter to client initialization +@@clientLocation(MyService.upload.subscriptionId, MyService); +``` + +### @clientName + +**Purpose**: Override generated names for SDK elements. Takes precedence over all other naming mechanisms. +**Important**: Always use PascalCase or camelCase for the rename parameter to make it easier to combine language scopes. SDKs will apply language-specific naming conventions automatically. +**Syntax**: `@clientName(rename: string, scope?: string)` +**Usage**: + +```typespec +// Rename Type +@@clientName(PetStore, "PetStoreClient"); + +// Language-specific names +@@clientName(foo, "pythonicFoo", "python") +@@clientName(foo, "CSharpFoo", "csharp") +``` + +### @clientNamespace + +**Purpose**: Change the namespace/package of generated types in the client SDK. +**Syntax**: `@clientNamespace(rename: string, scope?: string)` +**Usage**: + +```typespec +// Change client namespace +@@clientNamespace(MyService, "MyClient"); + +// Move model to different namespace +@@clientNamespace(MyService.MyModel, "MyClient.Models") +``` + +### @clientInitialization + +**Purpose**: Add custom parameters to client initialization. +**Important**: When `@clientInitialization` references a model defined in `client.tsp`, add a file-level namespace (e.g. `namespace ClientCustomizations;`) if one does not exist. +**Syntax**: `@clientInitialization(options: ClientInitializationOptions, scope?: string)` +**Usage**: + +```typespec +// Add initialization parameters +model MyClientOptions { + connectionString: string; +} + +@@clientInitialization(MyService, { parameters: MyClientOptions }); + +// With parameter aliasing +model MyClientOptions { + @paramAlias("subscriptionId") + subId: string; +} + +@@clientInitialization(MyService, { parameters: MyClientOptions }); +``` + +### @alternateType + +**Purpose**: Replace types in generated clients. +**Syntax**: `@alternateType(alternate: Type | ExternalType, scope?: string)` +**Usage**: + +```typespec +// Change property type +@@alternateType(Foo.date, string); + +// Language-specific alternates +@@alternateType(Foo.date, string, "python") + +// External type replacement +@@alternateType(uri, { + identity: "System.Uri", + package: "System", +}, "csharp") +``` + +### @override + +**Purpose**: Customize method signatures in generated clients. +**Restrictions**: Only operation parameter signatures can be customized. +**Important**: When `@override` references an operation defined in `client.tsp`, a file-level namespace (e.g. `namespace ClientCustomizations;`) should be added if one does not exist. +**Syntax**: `@override(override: Operation, scope?: string)` + +**Usage**: + +```typespec +// main.tsp +// Original operation +op myOperation(foo: string, bar: string): void; + +// client.tsp +// Custom signature - combine into options +model MyOperationOptions { + foo: string; + bar: string; +} + +op myOperationCustom(options: MyOperationOptions): void; + +@@override(myOperation, myOperationCustom); +``` + +### @scope + +**Purpose**: Include/exclude operations from specific languages. +**Usage**: + +```typespec +@@scope(Foo.create, "!csharp") // All languages except C# + +@@scope(Foo.create, "python") // Python only + +@@scope(Foo.create, "java, go") // Java and Go only +``` + +### @usage + +**Purpose**: Add usage information to models and enums. +**Note**: The usages provided are _additive_. +**Usage**: + +```typespec +// Add input and output usage to type +@@usage(MyModel, Usage.input | Usage.output) +``` + +**Usage Values**: + +- `Usage.input`: Used in request +- `Usage.output`: Used in response +- `Usage.json`: Used with JSON content type +- `Usage.xml`: Used with XML content type + +### @clientDoc + +**Purpose**: Override documentation for client libraries. +**Usage**: + +```typespec +// Replace type documentation with client documentation +@@clientDoc(myOperation, "Client-specific documentation", DocumentationMode.replace) + +// Append type documentation with client documentation - for only python +@@clientDoc(myModel, "Additional client notes", DocumentationMode.append, "python") +``` + +## Language-specific Customizations + +### @useSystemTextJsonConverter (C# only) + +**Purpose**: Use custom JSON converter for backward compatibility. +**Usage**: + +```typespec +@@useSystemTextJsonConverter(MyModel, "csharp") +``` + +## Best Practices + +### Do's + +- Use `client.tsp` for all customizations +- Use scope parameter for language-specific customizations +- Prefer scope negation (`"!csharp"`) when excluding single languages +- Combine scopes (`"python, java"`) when logic is identical across languages +- Use a file-level namespace (e.g. `namespace ClientCustomizations;`) in `client.tsp` if any types are defined in `client.tsp`. + +### Don'ts + +- Mix `@clientLocation` with `@client`/`@operationGroup` +- Over-customize - prefer defaults when possible +- Use legacy decorators for new services +- Rename without considering the impact on breaking changes +- Forget to specify scope for language-specific customizations + +## Common Scenarios + +### Scenario 1: Move Operations to Root Client + +```typespec +// Before: Operations in interfaces become sub-clients +interface Pets { feed(): void; } +interface Users { login(): void; } + +// After: All operations on root client +@@clientLocation(Pets.feed, MyService); +@@clientLocation(Users.login, MyService); +``` + +### Scenario 2: Rename for Consistency + +```typespec +// Standardize naming across languages +@@clientName(MyService, "MyServiceClient"); +@@clientName(getUserInfo, "GetUserInformation"); + +// Language-specific naming +@@clientName(uploadFile, "UploadFile", "csharp, python"); +``` + +### Scenario 3: Add Client Parameters + +```typespec +// Elevate common parameters to client +model MyServiceOptions { + subscriptionId: string; + resourceGroup: string; +} + +@@clientInitialization(MyService, { parameters: MyServiceOptions }); +``` + +### Scenario 4: Multi-Client Architecture + +```typespec +// Separate admin and user operations +@client({ service: MyService, name: "AdminClient" }) +interface AdminClient { + deleteUser is MyService.deleteUser; + manageRoles is MyService.manageRoles; +} + +@client({ service: MyService, name: "UserClient" }) +interface UserClient { + getProfile is MyService.getProfile; + updateProfile is MyService.updateProfile; +} +``` + +### Scenario 5: Language-specific clients + +```typespec +// Different client names for Java and others +@client({ service: MyService, name: "Foo.MyServiceClient" }, "java") +@client({ service: MyService, name: "MyServiceClient" }, "!java") +interface MyServiceClient { + getAllData is MyService.getAllData; +} + +// Different clients for python and Go +@client({ service: MyService, name: "MyClient" }, "python") +interface MyClientPython { + fetchData is MyService.fetchData; +} +@client({ service: MyService, name: "MyClient" }, "go") +interface MyClientGo { + fetchStream is MyService.fetchStream; +} +``` + +### Scenario 6: Rename custom client operations + +```typespec +// Create a client with operation names changed +@client({ service: MyService, name: "MyClient" }) +interface MyClient { + getFoo is MyService.getFooData; +} +``` + +This reference provides the essential patterns and decorators for TypeSpec client customizations. Focus on the core decorators (`@client`, `@operationGroup`, `@@clientLocation`, `@@clientName`, `@@access`) for most scenarios, and use advanced features selectively. +--- + +## When TypeSpec Isn't Enough: Code Customizations + +TypeSpec customizations (`client.tsp`) should be your **first choice** for SDK customization - they're clean, documented, and survive regeneration. + +For scenarios that TypeSpec cannot express, each language has its own post-generation code customization approach. Refer to the language-specific documentation below. + +### Language-Specific Code Customization Guides + +When you need post-generation customizations, refer to the language-specific documentation: + +| Language | Documentation | Pattern | +|----------|--------------|---------| +| **C#** | [C# Customization Guide](https://github.com/microsoft/typespec/blob/main/packages/http-client-csharp/.tspd/docs/customization.md) | Partial classes with `[CodeGenType]`, `[CodeGenMember]`, `[CodeGenSerialization]` attributes | +| **Python** | [Python Customization Guide](https://github.com/Azure/autorest.python/blob/main/docs/customizations.md) | `_patch.py` files at models, operations, and client levels | +| **Java** | [Java Customization Guide](https://github.com/Azure/autorest.java/blob/main/customization-base/README.md) | `Customization` class with `customizeAst()` method | +| **JavaScript** | [JS Customization Guide](https://github.com/Azure/azure-sdk-for-js/wiki/Modular-(DPG)-Customization-Guide) | Copy `src/` to `generated/`, add customizations in `src/` | +| **Go** | [Go Customization Guide](https://github.com/Azure/azure-sdk-for-go/blob/main/documentation/development/generate.md) | Prefer TypeSpec; use custom wrapper files for advanced cases | + +### Decision Flow + +``` +Need to customize SDK? + │ + ▼ + Can it be done in TypeSpec? + │ + ┌────┴────┐ + Yes No + │ │ + ▼ ▼ +Use client.tsp Use code customization +decorators (see language guide above) +``` \ No newline at end of file diff --git a/eng/common/mcp/README.md b/eng/common/mcp/README.md index 4a7c28e46b5..4e72c026b5f 100644 --- a/eng/common/mcp/README.md +++ b/eng/common/mcp/README.md @@ -24,15 +24,13 @@ The script will install the latest version of the azsdk cli executable from [too Azure SDK MCP server code is in [azure-sdk-tools/tools/azsdk-cli/Azure.Sdk.Tools.Cli](https://github.com/Azure/azure-sdk-tools/tree/main/tools/azsdk-cli/Azure.Sdk.Tools.Cli). -Azure SDK MCP servers should support [stdio and sse transports](https://modelcontextprotocol.io/docs/concepts/transports#server-sent-events-sse). +Azure SDK MCP servers [**MUST** support stdio and **SHOULD** support streamable HTTP using latest OAuth 2.1 Best Current Practices (BCPs)](https://modelcontextprotocol.io/docs/concepts/transports). -When running in copilot the default is stdio mode, but SSE is useful to support for external debugging. +When running in copilot the default is stdio mode. ### Developing MCP servers in C# See the [C# MCP SDK](https://github.com/modelcontextprotocol/csharp-sdk) -Add an [SSE transport](https://github.com/modelcontextprotocol/csharp-sdk/tree/main/samples/AspNetCoreSseServer) - TODO: Add the azsdk-cli project to pull in MCP server dependencies from the repo diff --git a/eng/common/mcp/azure-sdk-mcp.ps1 b/eng/common/mcp/azure-sdk-mcp.ps1 index b56fb4e96a2..6a3c23ee6ea 100755 --- a/eng/common/mcp/azure-sdk-mcp.ps1 +++ b/eng/common/mcp/azure-sdk-mcp.ps1 @@ -1,4 +1,7 @@ -#!/bin/env pwsh +#!/usr/bin/env pwsh + +#Requires -Version 7.0 +#Requires -PSEdition Core param( [string]$FileName = 'Azure.Sdk.Tools.Cli', @@ -9,20 +12,60 @@ param( [string]$RunDirectory = (Resolve-Path (Join-Path $PSScriptRoot .. .. ..)), [switch]$Run, [switch]$UpdateVsCodeConfig, - [switch]$Clean + [switch]$UpdatePathInProfile ) $ErrorActionPreference = "Stop" - -if (-not $InstallDirectory) -{ - $homeDir = if ($env:HOME) { $env:HOME } else { $env:USERPROFILE } - $InstallDirectory = (Join-Path $homeDir ".azure-sdk-mcp" "azsdk") -} . (Join-Path $PSScriptRoot '..' 'scripts' 'Helpers' 'AzSdkTool-Helpers.ps1') -if ($Clean) { - Clear-Directory -Path $InstallDirectory +$toolInstallDirectory = $InstallDirectory ? $InstallDirectory : (Get-CommonInstallDirectory) + +$mcpMode = $Run + +# Log to console or MCP client json-rpc +function log([object]$message, [switch]$warn, [switch]$err) { + [string]$messageString = $message + + # Assume we are in an MCP client context when `-Run` is specified + # otherwise print to console normally + if (!$mcpMode) { + if ($err) { + Write-Error $messageString -ErrorAction Continue + } + elseif ($warn) { + Write-Warning $messageString + } + else { + Write-Host $messageString + } + return; + } + + $level = switch ($message) { + { $_ -is [System.Management.Automation.ErrorRecord] } { 'error' } + { $_ -is [System.Management.Automation.WarningRecord] } { 'warning' } + default { 'notice' } + } + + # If message stringifies as a valid error message we want to print it + # otherwise print stack for calls to external binaries + if ($messageString -eq 'System.Management.Automation.RemoteException') { + $messageString = $message.ScriptStackTrace + } + + # Log json-rpc messages so the MCP client doesn't print + # '[warning] Failed to parse message:' + $messageObject = @{ + jsonrpc = "2.0" + method = "notifications/message" + params = @{ + level = $level + logger = "installer" + data = $messageString + } + } + + Write-Host ($messageObject | ConvertTo-Json -Depth 100 -Compress) } if ($UpdateVsCodeConfig) { @@ -50,17 +93,87 @@ if ($UpdateVsCodeConfig) { } } $vscodeConfig.servers = $orderedServers - Write-Host "Updating vscode mcp config at $vscodeConfigPath" + log "Updating vscode mcp config at $vscodeConfigPath" $vscodeConfig | ConvertTo-Json -Depth 10 | Set-Content -Path $vscodeConfigPath -Force } -$exe = Install-Standalone-Tool ` - -Version $Version ` - -FileName $FileName ` - -Package $Package ` - -Directory $InstallDirectory ` - -Repository $Repository +# Install to a temp directory first so we don't dump out all the other +# release zip contents to one of the users bin directories. +$tmp = $env:TEMP ? $env:TEMP : [System.IO.Path]::GetTempPath() +$guid = [System.Guid]::NewGuid() +$tempInstallDirectory = Join-Path $tmp "azsdk-install-$($guid)" + +if ($mcpMode) { + try { + # Swallow all output and re-log so we can wrap any + # output from the inner function as json-rpc + $tempExe = Install-Standalone-Tool ` + -Version $Version ` + -FileName $FileName ` + -Package $Package ` + -Directory $tempInstallDirectory ` + -Repository $Repository ` + *>&1 + | Tee-Object -Variable _ + | ForEach-Object { log $_; $_ } + | Select-Object -Last 1 + } + catch { + log -err $_ + exit 1 + } +} +else { + $tempExe = Install-Standalone-Tool ` + -Version $Version ` + -FileName $FileName ` + -Package $Package ` + -Directory $tempInstallDirectory ` + -Repository $Repository ` + +} + +if (-not (Test-Path $toolInstallDirectory)) { + New-Item -ItemType Directory -Path $toolInstallDirectory -Force | Out-Null +} +$exeName = Split-Path $tempExe -Leaf +$exeDestination = Join-Path $toolInstallDirectory $exeName + +# Try to copy the new version +$updateSucceeded = $false +try { + Copy-Item -Path $tempExe -Destination $exeDestination -Force + $updateSucceeded = $true +} +catch { + if ($Run -and (Test-Path $exeDestination)) { + # In MCP mode and the executable exists, warn and fall back to the existing installed version + log -warn "Could not update '$exeDestination': $($_.Exception.Message)" + log -warn "Falling back to the currently installed version." + } + else { + # In update-only mode or the executable does not exist, exit with error + log -err "Could not install or update '$exeDestination': $($_.Exception.Message)" + exit 1 + } +} + +# Clean up temp directory +if (Test-Path $tempInstallDirectory) { + Remove-Item -Path $tempInstallDirectory -Recurse -Force -ErrorAction SilentlyContinue +} + +if ($updateSucceeded) { + log "Executable $package is installed at $exeDestination" +} +if (!$UpdatePathInProfile) { + log -warn "To add the tool to PATH for new shell sessions, re-run with -UpdatePathInProfile to modify the shell profile file." +} +else { + Add-InstallDirectoryToPathInProfile -InstallDirectory $toolInstallDirectory + log -warn "'$exeName' will be available in PATH for new shell sessions." +} if ($Run) { - Start-Process -WorkingDirectory $RunDirectory -FilePath $exe -ArgumentList 'start' -NoNewWindow -Wait + Start-Process -WorkingDirectory $RunDirectory -FilePath $exeDestination -ArgumentList 'start' -NoNewWindow -Wait } diff --git a/eng/common/pipelines/ai-evals-tests.yml b/eng/common/pipelines/ai-evals-tests.yml new file mode 100644 index 00000000000..43a7ae5926d --- /dev/null +++ b/eng/common/pipelines/ai-evals-tests.yml @@ -0,0 +1,48 @@ +trigger: + branches: + include: + - main + paths: + include: + - .github/copilot-instructions.md + - eng/common/instructions/azsdk-tools/** + +pr: none + +parameters: + - name: EvalProject + type: string + default: 'tools/azsdk-cli/Azure.Sdk.Tools.Cli.Evaluations' + - name: OpenAIEndPoint + type: string + default: 'https://openai-shared.openai.azure.com/' + - name: Model + type: string + default: 'gpt-5' + - name: EvalRepoOwner + type: string + default: 'Azure' + - name: EvalRepoName + type: string + default: 'azure-sdk-tools' + - name: EvalRepoCommit + type: string + default: 'main' + +variables: + TargetRepoOwner: ${{ split(variables['Build.Repository.Name'], '/')[0] }} + TargetRepoName: ${{ split(variables['Build.Repository.Name'], '/')[1] }} + TargetRepoCommit: ${{ variables['Build.SourceVersion'] }} + +extends: + template: /eng/common/pipelines/templates/jobs/ai-eval-job.yml + parameters: + EvalProject: ${{ parameters.EvalProject }} + OpenAIEndPoint: ${{ parameters.OpenAIEndPoint }} + Model: ${{ parameters.Model }} + EvalRepoOwner: ${{ parameters.EvalRepoOwner }} + EvalRepoName: ${{ parameters.EvalRepoName }} + EvalRepoCommit: ${{ parameters.EvalRepoCommit }} + TargetRepoOwner: $(TargetRepoOwner) + TargetRepoName: $(TargetRepoName) + TargetRepoCommit: $(TargetRepoCommit) diff --git a/eng/common/pipelines/codeowners-linter.yml b/eng/common/pipelines/codeowners-linter.yml index cf4d113414d..b676a4d17a0 100644 --- a/eng/common/pipelines/codeowners-linter.yml +++ b/eng/common/pipelines/codeowners-linter.yml @@ -1,6 +1,5 @@ # Lint the CODEOWNERS file for a given repository and filter out baseline errors -# Note: Due to the nature of the verifications, which includes source paths/globs -# for the repository, this pipeline cannot use sparse-checkout. +# Uses partial clone (fetchFilter: tree:0) to avoid downloading full git history while allowing branch operations trigger: none pr: @@ -19,6 +18,7 @@ pr: stages: - stage: Run variables: + Codeql.SkipTaskAutoInjection: true skipComponentGovernanceDetection: true nugetMultiFeedWarnLevel: 'none' baseBranchBaseline: '' @@ -38,6 +38,10 @@ stages: UserOrgUri: "https://azuresdkartifacts.blob.core.windows.net/azure-sdk-write-teams/user-org-visibility-blob" steps: + - checkout: self + fetchFilter: tree:0 + fetchTags: false + - pwsh: | dotnet tool install --global --add-source "$(DotNetDevOpsFeed)" --version "$(CodeownersLinterVersion)" "Azure.Sdk.Tools.CodeownersLinter" displayName: Install CodeownersLinter diff --git a/eng/common/pipelines/templates/archetype-typespec-emitter.yml b/eng/common/pipelines/templates/archetype-typespec-emitter.yml index a92ad9d9935..443ac86a9f2 100644 --- a/eng/common/pipelines/templates/archetype-typespec-emitter.yml +++ b/eng/common/pipelines/templates/archetype-typespec-emitter.yml @@ -137,17 +137,56 @@ extends: $sourceBranch = '$(Build.SourceBranch)' $buildReason = '$(Build.Reason)' $buildNumber = '$(Build.BuildNumber)' + $emitterPackagePath = '${{ parameters.EmitterPackagePath }}' + + # Create emitter identifier from package path for disambiguation + $emitterIdentifier = "" + if (-not [string]::IsNullOrWhiteSpace($emitterPackagePath)) { + # Resolve emitterPackagePath to absolute path (it's relative to repo root) + # EmitterPackagePath is a directory, so append package.json + $absoluteEmitterPackagePath = Join-Path '$(Build.SourcesDirectory)' $emitterPackagePath + $packageJsonPath = Join-Path $absoluteEmitterPackagePath 'package.json' + + # Read the package name from package.json + if (Test-Path $packageJsonPath) { + try { + $packageJson = Get-Content $packageJsonPath -Raw | ConvertFrom-Json + if ($packageJson.name) { + $emitterIdentifier = $packageJson.name + } + } catch { + Write-Host "Warning: Could not read package name from $packageJsonPath" + } + } + + # If we still don't have an identifier, fall back to filename + if ([string]::IsNullOrWhiteSpace($emitterIdentifier)) { + Write-Host "Warning: Could not read emitter name from package.json, falling back to package path" + $emitterIdentifier = [System.IO.Path]::GetFileNameWithoutExtension($emitterPackagePath) + } + + # Clean up the identifier: remove @ prefix, replace invalid chars + $emitterIdentifier = $emitterIdentifier -replace '^@', '' + $emitterIdentifier = $emitterIdentifier -replace '[^a-zA-Z0-9\-_/]', '-' + $emitterIdentifier = $emitterIdentifier.Trim('-').ToLower() + + if (-not [string]::IsNullOrWhiteSpace($emitterIdentifier)) { + $emitterIdentifier = "-$emitterIdentifier" + } + } if ($buildReason -eq 'Schedule') { - $branchName = 'validate-typespec-scheduled' + $branchName = "validate-typespec-scheduled$emitterIdentifier" } elseif ($sourceBranch -match "^refs/pull/(\d+)/(head|merge)$") { - $branchName = "validate-typespec-pr-$($Matches[1])" + $branchName = "validate-typespec-pr-$($Matches[1])$emitterIdentifier" } else { - $branchName = "validate-typespec-$buildNumber" + $branchName = "validate-typespec-$buildNumber$emitterIdentifier" } Write-Host "Setting variable 'branchName' to '$branchName'" Write-Host "##vso[task.setvariable variable=branchName;isOutput=true]$branchName" + Write-Host "Setting variable 'emitterIdentifier' to '$emitterIdentifier'" + Write-Host "##vso[task.setvariable variable=emitterIdentifier;isOutput=true]$emitterIdentifier" displayName: Set branch name name: set_branch_name @@ -248,11 +287,7 @@ extends: jobs: - job: Initialize steps: - - template: /eng/common/pipelines/templates/steps/sparse-checkout.yml - parameters: - Paths: - - "/*" - - "!SessionRecords" + - checkout: self - task: UseNode@1 displayName: 'Install Node.js' @@ -263,27 +298,34 @@ extends: displayName: Download pipeline artifacts - pwsh: | - npm install -g @azure-tools/typespec-client-generator-cli@latest + npm ci displayName: Install tsp-client + workingDirectory: $(Build.SourcesDirectory)/eng/common/tsp-client - pwsh: | + # Resolve EmitterPackageJsonOutputPath to absolute path if it's relative + $emitterPath = '${{ parameters.EmitterPackageJsonOutputPath }}' + if (-not [System.IO.Path]::IsPathRooted($emitterPath)) { + $emitterPath = Join-Path '$(Build.SourcesDirectory)' $emitterPath + } + Write-Host "Overrides location: $(buildArtifactsPath)/packages/overrides.json" + Write-Host "Resolved emitter package path: $emitterPath" if (Test-Path -Path '$(buildArtifactsPath)/packages/overrides.json') { Write-Host "Using overrides.json to generate emitter-package.json" - tsp-client generate-config-files ` - --package-json '$(buildArtifactsPath)/lock-files/package.json' ` - --emitter-package-json-path '${{ parameters.EmitterPackageJsonOutputPath }}' ` - --overrides '$(buildArtifactsPath)/packages/overrides.json' + npm exec --no -- tsp-client generate-config-files ` + --package-json '$(buildArtifactsPath)/lock-files/package.json' ` + --emitter-package-json-path "$emitterPath" ` + --overrides '$(buildArtifactsPath)/packages/overrides.json' } else { Write-Host "No overrides.json found. Running tsp-client without overrides." - - tsp-client generate-config-files ` - --package-json '$(buildArtifactsPath)/lock-files/package.json' ` - --emitter-package-json-path '${{ parameters.EmitterPackageJsonOutputPath }}' + npm exec --no -- tsp-client generate-config-files ` + --package-json '$(buildArtifactsPath)/lock-files/package.json' ` + --emitter-package-json-path "$emitterPath" } displayName: Generate emitter-package.json and emitter-package-lock files - workingDirectory: $(Build.SourcesDirectory) + workingDirectory: $(Build.SourcesDirectory)/eng/common/tsp-client - ${{ parameters.InitializationSteps }} @@ -325,15 +367,12 @@ extends: matrixArtifactsPath: $(Pipeline.Workspace)/matrix_artifacts AzureSdkRepoName: $[format('azure-sdk/{0}', split(variables['Build.Repository.Name'], '/')[1])] steps: - - template: /eng/common/pipelines/templates/steps/sparse-checkout.yml - parameters: - Paths: - - "/*" - - "!SessionRecords" - Repositories: - - Name: $(AzureSdkRepoName) - Commitish: $(branchName) - WorkingDirectory: $(System.DefaultWorkingDirectory) + - checkout: self + - pwsh: | + git remote add azure-sdk https://github.com/$(AzureSdkRepoName).git + git fetch azure-sdk $(branchName) + git switch $(branchName) + displayName: 'Checkout PR branch $(branchName)' - task: UseNode@1 displayName: 'Install Node.js' @@ -368,15 +407,18 @@ extends: displayName: Create PR dependsOn: - Generate + condition: succeededOrFailed() variables: generateJobResult: $[dependencies.Generate.result] emitterVersion: $[stageDependencies.Build.Build.outputs['initialize.emitterVersion']] + emitterIdentifier: $[stageDependencies.Build.Build.outputs['set_branch_name.emitterIdentifier']] steps: - template: /eng/common/pipelines/templates/steps/sparse-checkout.yml - pwsh: | $generateJobResult = '$(generateJobResult)' $emitterVersion = '$(emitterVersion)' + $emitterIdentifier = '$(emitterIdentifier)' $collectionUri = '$(System.CollectionUri)' $project = '$(System.TeamProject)' $definitionName = '$(Build.DefinitionName)' @@ -388,6 +430,12 @@ extends: $buildNumber = '$(Build.BuildNumber)' $preRelease = '${{ parameters.BuildPrereleaseVersion }}' -eq 'true' + # Use emitterIdentifier for PR title (remove leading dash if present) + $emitterName = "TypeSpec emitter" + if (-not [string]::IsNullOrWhiteSpace($emitterIdentifier)) { + $emitterName = $emitterIdentifier.TrimStart('-') + } + $prBody = "Generated by $definitionName build [$buildNumber]($collectionUri/$project/_build/results?buildId=$buildId)
" if ($sourceBranch -match "^refs/heads/(.+)$") { @@ -404,9 +452,9 @@ extends: $prTitle = "Scheduled code regeneration test" } else { if ($preRelease) { - $prTitle = "Update TypeSpec emitter version to prerelease $emitterVersion" + $prTitle = "Update $emitterName version to prerelease $emitterVersion" } else { - $prTitle = "Update TypeSpec emitter version to $emitterVersion" + $prTitle = "Update $emitterName version to $emitterVersion" } if ($generateJobResult -ne 'Succeeded') { @@ -431,7 +479,8 @@ extends: Write-Error "Build.Repository.Name not in the expected {Owner}/{Name} format" } - $openAsDraft = -not ($reason -eq 'IndividualCI' -and $sourceBranch -eq 'refs/heads/main') + # Open PR as draft if generation failed, or if it's not an IndividualCI build from main + $openAsDraft = ($generateJobResult -ne 'Succeeded') -or (-not ($reason -eq 'IndividualCI' -and $sourceBranch -eq 'refs/heads/main')) Write-Host "Setting OpenAsDraftBool = $openAsDraft" Write-Host "##vso[task.setvariable variable=OpenAsDraft]$openAsDraft" if ($openAsDraft) { diff --git a/eng/common/pipelines/templates/jobs/ai-eval-job.yml b/eng/common/pipelines/templates/jobs/ai-eval-job.yml new file mode 100644 index 00000000000..b6e4ceeb7d1 --- /dev/null +++ b/eng/common/pipelines/templates/jobs/ai-eval-job.yml @@ -0,0 +1,100 @@ +parameters: + - name: EvalProject + type: string + default: 'tools/azsdk-cli/Azure.Sdk.Tools.Cli.Evaluations' + - name: OpenAIEndPoint + type: string + default: 'https://openai-shared.openai.azure.com/' + - name: Model + type: string + default: 'gpt-5' +# Repository where evals are located + - name: EvalRepoOwner + type: string + default: 'Azure' + - name: EvalRepoName + type: string + default: 'azure-sdk-tools' + - name: EvalRepoCommit + type: string + default: 'main' +# Target repository for copilot instructions + - name: TargetRepoOwner + type: string + - name: TargetRepoName + type: string + - name: TargetRepoCommit + type: string + +jobs: + - job: Run_Eval + variables: + - template: /eng/pipelines/templates/variables/globals.yml + - template: /eng/pipelines/templates/variables/image.yml + displayName: 'Run AI Eval' + pool: + name: $(LINUXPOOL) + image: $(LINUXVMIMAGE) + os: linux + + steps: + - checkout: none + - task: UseDotNet@2 + displayName: "Use .NET SDK 9.0.x" + retryCountOnTaskFailure: 3 + inputs: + packageType: sdk + version: 9.0.x + performMultiLevelLookup: true + + - task: UseDotNet@2 + displayName: "Use .NET SDK 8.0.x" + retryCountOnTaskFailure: 3 + inputs: + packageType: sdk + version: 8.0.x + performMultiLevelLookup: true + + - template: /eng/common/pipelines/templates/steps/sparse-checkout.yml + parameters: + SkipCheckoutNone: true + Repositories: + - Name: ${{ parameters.EvalRepoOwner }}/${{ parameters.EvalRepoName }} + Commitish: ${{ parameters.EvalRepoCommit }} + WorkingDirectory: $(System.DefaultWorkingDirectory)/${{ parameters.EvalRepoName }} + - Name: ${{ parameters.TargetRepoOwner }}/${{ parameters.TargetRepoName }} + Commitish: ${{ parameters.TargetRepoCommit }} + WorkingDirectory: $(System.DefaultWorkingDirectory)/${{ parameters.TargetRepoName }} + Paths: + - 'tools/**' + - 'eng/common/**' + - '.github/**' + + - task: AzureCLI@2 + displayName: 'Run eval' + condition: and(succeeded(), ne(variables['Skip.Eval'], 'true')) + inputs: + azureSubscription: opensource-api-connection + scriptType: 'bash' + scriptLocation: 'inlineScript' + workingDirectory: '$(System.DefaultWorkingDirectory)/${{ parameters.EvalRepoName }}/${{ parameters.EvalProject }}' + inlineScript: | + echo "Logged in to Azure" + echo "Running eval" + dotnet test --logger trx + env: + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1 + DOTNET_CLI_TELEMETRY_OPTOUT: 1 + DOTNET_MULTILEVEL_LOOKUP: 0 + AZURE_OPENAI_MODEL_DEPLOYMENT_NAME: ${{ parameters.Model }} + AZURE_OPENAI_ENDPOINT: ${{ parameters.OpenAIEndPoint }} + REPOSITORY_NAME: ${{ parameters.TargetRepoName }} + COPILOT_INSTRUCTIONS_PATH_MCP_EVALS: $(System.DefaultWorkingDirectory)/${{ parameters.TargetRepoName }}/.github/copilot-instructions.md + + - task: PublishTestResults@2 + condition: succeededOrFailed() + inputs: + testResultsFiles: '**/*.trx' + testRunTitle: $(System.JobDisplayName) + testResultsFormat: 'VSTest' + mergeTestResults: true diff --git a/eng/common/pipelines/templates/jobs/npm-publish.yml b/eng/common/pipelines/templates/jobs/npm-publish.yml index 9909ace96e9..ce51a7b583e 100644 --- a/eng/common/pipelines/templates/jobs/npm-publish.yml +++ b/eng/common/pipelines/templates/jobs/npm-publish.yml @@ -99,6 +99,7 @@ jobs: $packageTags = npm view $packageJson.name "dist-tags" -json -silent | ConvertFrom-Json if ($LASTEXITCODE -ne 0 -or !$packageTags) { Write-Warning "Failed to retrieve dist-tags for $packageJson.name. It is possible the package hasn't been indexed yet so ignoring." + $global:LASTEXITCODE = 0 continue } diff --git a/eng/common/pipelines/templates/jobs/perf.yml b/eng/common/pipelines/templates/jobs/perf.yml index bd53833282c..2257d783041 100644 --- a/eng/common/pipelines/templates/jobs/perf.yml +++ b/eng/common/pipelines/templates/jobs/perf.yml @@ -105,14 +105,44 @@ jobs: - Name: ${{ parameters.LanguageRepoName }} Commitish: ${{ parameters.LanguageRepoCommitish }} WorkingDirectory: $(System.DefaultWorkingDirectory) + + - template: /eng/common/pipelines/templates/steps/sparse-checkout.yml + parameters: + Paths: + - 'tools/perf-automation/Azure.Sdk.Tools.PerfAutomation' + Repositories: - Name: Azure/azure-sdk-tools Commitish: ${{ parameters.ToolsRepoCommitish }} WorkingDirectory: $(System.DefaultWorkingDirectory)/azure-sdk-tools + SkipCheckoutNone: true - template: /eng/common/pipelines/templates/steps/verify-agent-os.yml parameters: AgentImage: $(OSVmImage) - + + # Copied from eng/pipelines/templates/steps/install-dotnet.yml, but changed workingDirectory + - task: UseDotNet@2 # About UseDotNet@2 task: https://learn.microsoft.com/azure/devops/pipelines/tasks/reference/use-dotnet-v2?view=azure-pipelines + displayName: "Use .NET SDK from global.json" + retryCountOnTaskFailure: 3 + inputs: + useGlobalJson: true + workingDirectory: azure-sdk-tools + + # Copied from eng/pipelines/templates/steps/install-dotnet.yml, but changed workingDirectory + - task: UseDotNet@2 + displayName: "Use .NET SDK 8.0.x" + retryCountOnTaskFailure: 3 + inputs: + # We must install sdk, not just runtime, as it is required by some of our tools, like test-proxy. + # Specifically, test-proxy requires asp.net core runtime, which is installed only when sdk option + # is selected, per: https://github.com/microsoft/azure-pipelines-tasks/issues/14405 + packageType: sdk + version: 8.0.x + # performMultiLevelLookup comes into play when given .NET executable target runtime is different + # than the installed .NET SDK. Without this, such runtime would not be found. + performMultiLevelLookup: true + workingDirectory: azure-sdk-tools + - ${{ parameters.InstallLanguageSteps }} - template: /eng/common/TestResources/deploy-test-resources.yml @@ -185,7 +215,7 @@ jobs: inputs: targetPath: $(System.DefaultWorkingDirectory)/${{ parameters.Language }}-profile.zip artifactName: ${{ parameters.Language }}-profile.zip - condition: ${{ parameters.Profile }} + condition: and(succeeded(), ${{ parameters.Profile }}) - template: /eng/common/TestResources/remove-test-resources.yml parameters: diff --git a/eng/common/pipelines/templates/jobs/prepare-pipelines.yml b/eng/common/pipelines/templates/jobs/prepare-pipelines.yml index 0e54b11a1e8..906a9bfcf1f 100644 --- a/eng/common/pipelines/templates/jobs/prepare-pipelines.yml +++ b/eng/common/pipelines/templates/jobs/prepare-pipelines.yml @@ -82,7 +82,7 @@ jobs: $generateUnifiedWeekly = 'false' $testServiceConnections = '"Azure" "azure-sdk-tests" "azure-sdk-tests-preview" "azure-sdk-tests-public" "Azure SDK Test Resources - LiveTestSecrets"' - $internalServiceConnections = '"Azure" "Azure SDK Artifacts" "Azure SDK Engineering System" "opensource-api-connection" "AzureSDKEngKeyVault Secrets" "Azure SDK PME Managed Identity"' + $internalServiceConnections = '"Azure" "Azure SDK Artifacts" "Azure SDK Engineering System" "opensource-api-connection" "AzureSDKEngKeyVault Secrets" "Azure SDK PME Managed Identity" "APIView prod deployment"' # Map the language to the appropriate variable groups switch ($lang) diff --git a/eng/common/pipelines/templates/stages/archetype-sdk-tool-pwsh.yml b/eng/common/pipelines/templates/stages/archetype-sdk-tool-pwsh.yml index cbe9ef4331c..d76b0392df9 100644 --- a/eng/common/pipelines/templates/stages/archetype-sdk-tool-pwsh.yml +++ b/eng/common/pipelines/templates/stages/archetype-sdk-tool-pwsh.yml @@ -30,9 +30,6 @@ stages: Linux: Pool: azsdk-pool Image: ubuntu-24.04 - Mac: - Pool: Azure Pipelines - Image: macos-latest pool: name: $(Pool) diff --git a/eng/common/pipelines/templates/steps/check-spelling.yml b/eng/common/pipelines/templates/steps/check-spelling.yml index 8d7a716cbdf..d5bfff5ea2d 100644 --- a/eng/common/pipelines/templates/steps/check-spelling.yml +++ b/eng/common/pipelines/templates/steps/check-spelling.yml @@ -2,16 +2,28 @@ # and some ref (branch, tag, etc.) or commit hash. Only runs on PRs. # ContinueOnError - true: Pipeline warns on spelling error # false: Pipeline fails on spelling error -# TargetBranch - Target ref (e.g. main) to compare to create file change -# list. # CspellConfigPath - Path to cspell.json config location # +# ScriptToValidateUpgrade - Optional script to validate cspell upgrade. This +# is invoked only if package-lock.json for cspell is +# changed. This script should exit with a nonzero exit +# code if the upgrade is invalid. Upgrade check should +# check for errors which would prevent release (i.e. +# public API surface). +# # This check recognizes the setting of variable "Skip.SpellCheck" # if set to 'true', spellchecking will not be invoked. parameters: - ContinueOnError: true - CspellConfigPath: ./.vscode/cspell.json + - name: ContinueOnError + type: boolean + default: true + - name: CspellConfigPath + type: string + default: ./.vscode/cspell.json + - name: ScriptToValidateUpgrade + type: string + default: '' steps: - ${{ if eq(variables['Build.Reason'], 'PullRequest') }}: @@ -26,3 +38,16 @@ steps: -CspellConfigPath ${{ parameters.CspellConfigPath }} -ExitWithError:(!$${{ parameters.ContinueOnError }}) pwsh: true + - ${{ if ne('', parameters.ScriptToValidateUpgrade) }}: + - pwsh: | + $changedFiles = ./eng/common/scripts/get-changedfiles.ps1 + + if ($changedFiles -notcontains 'eng/common/spelling/package-lock.json') { + Write-Host "No changes to cspell package-lock.json detected." + exit 0 + } + + Write-Host "Detected change to cspell package-lock.json. Running upgrade verification." + & '${{ parameters.ScriptToValidateUpgrade }}' + displayName: Verify cspell upgrade + condition: and(succeeded(), ne('true', variables['Skip.SpellCheck'])) diff --git a/eng/common/pipelines/templates/steps/create-apireview.yml b/eng/common/pipelines/templates/steps/create-apireview.yml index 124e027308f..a6f94c01a37 100644 --- a/eng/common/pipelines/templates/steps/create-apireview.yml +++ b/eng/common/pipelines/templates/steps/create-apireview.yml @@ -1,45 +1,65 @@ parameters: - ArtifactPath: $(Build.ArtifactStagingDirectory) - Artifacts: [] - ConfigFileDir: $(Build.ArtifactStagingDirectory)/PackageInfo - MarkPackageAsShipped: false - GenerateApiReviewForManualOnly: false - ArtifactName: 'packages' - PackageName: '' - SourceRootPath: $(Build.SourcesDirectory) + - name: ArtifactPath + type: string + default: $(Build.ArtifactStagingDirectory) + - name: Artifacts + type: object + default: [] + - name: ConfigFileDir + type: string + default: $(Build.ArtifactStagingDirectory)/PackageInfo + - name: MarkPackageAsShipped + type: boolean + default: false + - name: GenerateApiReviewForManualOnly + type: boolean + default: false + - name: ArtifactName + type: string + default: 'packages' + - name: PackageName + type: string + default: '' + - name: SourceRootPath + type: string + default: $(Build.SourcesDirectory) + - name: PackageInfoFiles + type: object + default: [] steps: - # ideally this should be done as initial step of a job in caller template - # We can remove this step later once it is added in caller - - template: /eng/common/pipelines/templates/steps/set-default-branch.yml - parameters: - WorkingDirectory: ${{ parameters.SourceRootPath }} - # Automatic API review is generated for a package when pipeline runs irrespective of how pipeline gets triggered. - # Below condition ensures that API review is generated only for manual pipeline runs when flag GenerateApiReviewForManualOnly is set to true. + # Below condition ensures that API review is generated only for manual pipeline runs when flag GenerateApiReviewForManualOnly is set to true. - ${{ if or(ne(parameters.GenerateApiReviewForManualOnly, true), eq(variables['Build.Reason'], 'Manual')) }}: - - task: Powershell@2 - inputs: - filePath: ${{ parameters.SourceRootPath }}/eng/common/scripts/Create-APIReview.ps1 - arguments: > - -ArtifactList ('${{ convertToJson(parameters.Artifacts) }}' | ConvertFrom-Json | Select-Object Name) - -ArtifactPath '${{parameters.ArtifactPath}}' - -ArtifactName ${{ parameters.ArtifactName }} - -APIKey '$(azuresdk-apiview-apikey)' - -PackageName '${{parameters.PackageName}}' - -SourceBranch '$(Build.SourceBranchName)' - -DefaultBranch '$(DefaultBranch)' - -ConfigFileDir '${{parameters.ConfigFileDir}}' - -BuildId '$(Build.BuildId)' - -RepoName '$(Build.Repository.Name)' - -MarkPackageAsShipped $${{parameters.MarkPackageAsShipped}} - pwsh: true - displayName: Create API Review - condition: >- - and( - succeededOrFailed(), - ne(variables['Skip.CreateApiReview'], 'true'), - ne(variables['Build.Reason'],'PullRequest'), - eq(variables['System.TeamProject'], 'internal'), - not(endsWith(variables['Build.Repository.Name'], '-pr')) - ) + # ideally this should be done as initial step of a job in caller template + # We can remove this step later once it is added in caller + - template: /eng/common/pipelines/templates/steps/set-default-branch.yml + parameters: + WorkingDirectory: ${{ parameters.SourceRootPath }} + + - ${{ if and(eq(variables['System.TeamProject'], 'internal'), ne(variables['Build.Reason'], 'PullRequest'), not(endsWith(variables['Build.Repository.Name'], '-pr'))) }}: + - task: AzureCLI@2 + inputs: + azureSubscription: 'APIView prod deployment' + scriptType: pscore + scriptLocation: scriptPath + scriptPath: ${{ parameters.SourceRootPath }}/eng/common/scripts/Create-APIReview.ps1 + # PackageInfoFiles example: @('a/file1.json','a/file2.json') + arguments: > + -PackageInfoFiles @('${{ join(''',''', parameters.PackageInfoFiles) }}') + -ArtifactList ('${{ convertToJson(parameters.Artifacts) }}' | ConvertFrom-Json | Select-Object Name) + -ArtifactPath '${{parameters.ArtifactPath}}' + -ArtifactName ${{ parameters.ArtifactName }} + -PackageName '${{parameters.PackageName}}' + -SourceBranch '$(Build.SourceBranchName)' + -DefaultBranch '$(DefaultBranch)' + -ConfigFileDir '${{parameters.ConfigFileDir}}' + -BuildId '$(Build.BuildId)' + -RepoName '$(Build.Repository.Name)' + -MarkPackageAsShipped $${{parameters.MarkPackageAsShipped}} + displayName: Create API Review + condition: >- + and( + succeededOrFailed(), + ne(variables['Skip.CreateApiReview'], 'true') + ) diff --git a/eng/common/pipelines/templates/steps/create-authenticated-npmrc.yml b/eng/common/pipelines/templates/steps/create-authenticated-npmrc.yml index 52379684c4e..cb29d1312eb 100644 --- a/eng/common/pipelines/templates/steps/create-authenticated-npmrc.yml +++ b/eng/common/pipelines/templates/steps/create-authenticated-npmrc.yml @@ -6,6 +6,9 @@ parameters: - name: CustomCondition type: string default: succeeded() + - name: ServiceConnection + type: string + default: '' steps: - pwsh: | @@ -21,8 +24,10 @@ steps: $content | Out-File '${{ parameters.npmrcPath }}' displayName: 'Create .npmrc' condition: ${{ parameters.CustomCondition }} + - task: npmAuthenticate@0 displayName: Authenticate .npmrc condition: ${{ parameters.CustomCondition }} inputs: workingFile: ${{ parameters.npmrcPath }} + azureDevOpsServiceConnection: ${{ parameters.ServiceConnection }} diff --git a/eng/common/pipelines/templates/steps/daily-dev-build-variable.yml b/eng/common/pipelines/templates/steps/daily-dev-build-variable.yml index 37efd0bd031..3191a4928a2 100644 --- a/eng/common/pipelines/templates/steps/daily-dev-build-variable.yml +++ b/eng/common/pipelines/templates/steps/daily-dev-build-variable.yml @@ -2,6 +2,7 @@ # is used when this pipeline is going to be generating and publishing daily dev builds. parameters: ServiceDirectory: '' + Artifacts: [] Condition: succeeded() steps: - ${{if ne(parameters.ServiceDirectory, '')}}: @@ -11,6 +12,7 @@ steps: arguments: > -ServiceDirectory ${{parameters.ServiceDirectory}} -OutDirectory $(Build.ArtifactStagingDirectory)/PackageInfo + -artifactList @('${{ replace(convertToJson(parameters.Artifacts), '''', '`''') }}' | ConvertFrom-Json | Select-Object -ExpandProperty name) pwsh: true workingDirectory: $(Pipeline.Workspace) displayName: Dump Package properties diff --git a/eng/common/pipelines/templates/steps/detect-api-changes.yml b/eng/common/pipelines/templates/steps/detect-api-changes.yml index d997caa8491..b14630b0f6b 100644 --- a/eng/common/pipelines/templates/steps/detect-api-changes.yml +++ b/eng/common/pipelines/templates/steps/detect-api-changes.yml @@ -7,10 +7,10 @@ parameters: steps: - ${{ if eq(variables['Build.Reason'],'PullRequest') }}: - pwsh: | - $apiChangeDetectRequestUrl = "https://apiview.dev/api/PullRequests/CreateAPIRevisionIfAPIHasChanges" + $apiChangeDetectRequestUrl = "https://apiview.org/api/PullRequests/CreateAPIRevisionIfAPIHasChanges" echo "##vso[task.setvariable variable=ApiChangeDetectRequestUrl]$apiChangeDetectRequestUrl" displayName: "Set API change detect request URL" - condition: and(${{ parameters.Condition}}, eq(variables['ApiChangeDetectRequestUrl'], '')) + condition: and(succeededOrFailed(), ${{ parameters.Condition}}, eq(variables['ApiChangeDetectRequestUrl'], '')) - task: Powershell@2 inputs: diff --git a/eng/common/pipelines/templates/steps/install-azsdk-cli.yml b/eng/common/pipelines/templates/steps/install-azsdk-cli.yml new file mode 100644 index 00000000000..3cbd66d9c4e --- /dev/null +++ b/eng/common/pipelines/templates/steps/install-azsdk-cli.yml @@ -0,0 +1,16 @@ +parameters: + InstallDirectory: $(Agent.TempDirectory) + SourceRootPath: '$(System.DefaultWorkingDirectory)' + +steps: + - task: Powershell@2 + displayName: 'Install Azure SDK Tools CLI' + inputs: + targetType: 'inline' + script: | + & "${{ parameters.SourceRootPath }}/eng/common/mcp/azure-sdk-mcp.ps1" -InstallDirectory ${{ parameters.InstallDirectory }} + $azsdkPath = Join-Path "${{ parameters.InstallDirectory }}" "azsdk$(if ($IsWindows) { '.exe' } else { '' })" + Write-Host "##vso[task.setvariable variable=AZSDK]$azsdkPath" + Write-Host "Set AZSDK variable to: $azsdkPath" + pwsh: true + workingDirectory: $(Pipeline.Workspace) \ No newline at end of file diff --git a/eng/common/pipelines/templates/steps/login-to-github.yml b/eng/common/pipelines/templates/steps/login-to-github.yml new file mode 100644 index 00000000000..3df66925da2 --- /dev/null +++ b/eng/common/pipelines/templates/steps/login-to-github.yml @@ -0,0 +1,25 @@ +# Will output a variable named GH_TOKEN_ for each owner in TokenOwners if there is only one owner it will just output GH_TOKEN + +parameters: +- name: TokenOwners + type: object + default: + - Azure +- name: VariableNamePrefix + type: string + default: GH_TOKEN +- name: ScriptDirectory + default: eng/common/scripts + +steps: +- task: AzureCLI@2 + displayName: "Login to GitHub" + inputs: + azureSubscription: 'AzureSDKEngKeyVault Secrets' + scriptType: pscore + scriptLocation: scriptPath + scriptPath: ${{ parameters.ScriptDirectory }}/login-to-github.ps1 + arguments: > + -InstallationTokenOwners '${{ join(''',''', parameters.TokenOwners) }}' + -VariableNamePrefix '${{ parameters.VariableNamePrefix }}' + \ No newline at end of file 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 00000000000..0e4824872c6 --- /dev/null +++ b/eng/common/pipelines/templates/steps/mark-release-completion.yml @@ -0,0 +1,22 @@ +parameters: + ConfigFileDir: '' + PackageArtifactName: '' + SourceRootPath: $(Build.SourcesDirectory) + +steps: + - template: /eng/common/pipelines/templates/steps/install-azsdk-cli.yml + parameters: + SourceRootPath: ${{ parameters.SourceRootPath }} + + - 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' -AzsdkExePath '$(AZSDK)' + 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/pipelines/templates/steps/publish-1es-artifact.yml b/eng/common/pipelines/templates/steps/publish-1es-artifact.yml index 81ade0f1a44..30d38828e72 100644 --- a/eng/common/pipelines/templates/steps/publish-1es-artifact.yml +++ b/eng/common/pipelines/templates/steps/publish-1es-artifact.yml @@ -31,8 +31,8 @@ steps: inputs: artifactName: '$(PublishArtifactName)' targetPath: '${{ parameters.ArtifactPath }}' - # Disable sbom generation by default for our public validation builds to avoid unnecessary work - ${{ if eq(variables['System.TeamProject'], 'public') }}: + # Disable sbom generation by default for our public or pull request validation builds to avoid unnecessary work + ${{ if or(eq(variables['System.TeamProject'], 'public'), eq(variables['Build.Reason'], 'PullRequest')) }}: sbomEnabled: false ${{ else }}: sbomEnabled: ${{ parameters.SbomEnabled }} diff --git a/eng/common/pipelines/templates/steps/set-test-pipeline-version.yml b/eng/common/pipelines/templates/steps/set-test-pipeline-version.yml index 61d49cdb7db..1e54d16834b 100644 --- a/eng/common/pipelines/templates/steps/set-test-pipeline-version.yml +++ b/eng/common/pipelines/templates/steps/set-test-pipeline-version.yml @@ -1,12 +1,25 @@ parameters: - PackageName: '' - PackageNames: '' - ServiceDirectory: '' - TagSeparator: '_' - TestPipeline: false +- name: PackageName + type: string + default: '' +- name: PackageNames + type: string + default: '' +- name: ServiceDirectory + type: string + default: '' +- name: TagSeparator + type: string + default: '_' +- name: TestPipeline + type: boolean + default: false +- name: Artifacts + type: object + default: [] steps: -- ${{ if eq(parameters.TestPipeline, 'true') }}: +- ${{ if eq(parameters.TestPipeline, true) }}: - task: PowerShell@2 displayName: Prep template pipeline for release condition: and(succeeded(), ne(variables['Skip.SetTestPipelineVersion'], 'true')) @@ -18,4 +31,5 @@ steps: -PackageNames '${{ coalesce(parameters.PackageName, parameters.PackageNames) }}' -ServiceDirectory '${{ parameters.ServiceDirectory }}' -TagSeparator '${{ parameters.TagSeparator }}' + -Artifacts @('${{ replace(convertToJson(parameters.Artifacts), '''', '`''') }}' | ConvertFrom-Json) pwsh: true diff --git a/eng/common/pipelines/templates/steps/set-vcpkg-cache-vars.yml b/eng/common/pipelines/templates/steps/set-vcpkg-cache-vars.yml new file mode 100644 index 00000000000..c7ece396e0d --- /dev/null +++ b/eng/common/pipelines/templates/steps/set-vcpkg-cache-vars.yml @@ -0,0 +1,26 @@ + +parameters: + - name: TokenTimeoutInHours + type: number + default: 1 + +steps: + - pwsh: | + Write-Host "Setting vcpkg cache variables for read only access to vcpkg binary and asset caches" + Write-Host '##vso[task.setvariable variable=VCPKG_BINARY_SOURCES_SECRET;issecret=true;]clear;x-azcopy,https://azuresdkartifacts.blob.core.windows.net/public-vcpkg-container,read' + Write-Host '##vso[task.setvariable variable=X_VCPKG_ASSET_SOURCES_SECRET;issecret=true;]clear;x-azurl,https://azuresdkartifacts.blob.core.windows.net/public-vcpkg-container,,read' + displayName: Set vcpkg variables + + - ${{if eq(variables['System.TeamProject'], 'internal') }}: + - task: AzurePowerShell@5 + displayName: Set Vcpkg Write-mode Cache + inputs: + azureSubscription: 'Azure SDK Artifacts' + ScriptType: FilePath + ScriptPath: eng/common/scripts/Set-VcpkgWriteModeCache.ps1 + ScriptArguments: -TokenTimeoutInHours ${{ parameters.TokenTimeoutInHours }} + azurePowerShellVersion: LatestVersion + pwsh: true + # This step is idempotent and can be run multiple times in cases of + # failure and partial execution. + retryCountOnTaskFailure: 3 diff --git a/eng/common/pipelines/templates/steps/validate-all-packages.yml b/eng/common/pipelines/templates/steps/validate-all-packages.yml index 03a5f84cc29..4bcb1286738 100644 --- a/eng/common/pipelines/templates/steps/validate-all-packages.yml +++ b/eng/common/pipelines/templates/steps/validate-all-packages.yml @@ -1,20 +1,30 @@ parameters: - ArtifactPath: $(Build.ArtifactStagingDirectory) - Artifacts: [] - ConfigFileDir: $(Build.ArtifactStagingDirectory)/PackageInfo + - name: ArtifactPath + type: string + default: $(Build.ArtifactStagingDirectory) + - name: Artifacts + type: object + default: [] + - name: ConfigFileDir + type: string + default: $(Build.ArtifactStagingDirectory)/PackageInfo + - name: PackageInfoFiles + type: object + default: [] steps: - ${{ if and(ne(variables['Skip.PackageValidation'], 'true'), and(ne(variables['Build.Reason'], 'PullRequest'), eq(variables['System.TeamProject'], 'internal'))) }}: - pwsh: | echo "##vso[task.setvariable variable=SetAsReleaseBuild]false" displayName: "Set as release build" - condition: and(succeeded(), eq(variables['SetAsReleaseBuild'], '')) + condition: and(succeededOrFailed(), eq(variables['SetAsReleaseBuild'], '')) - task: AzureCLI@2 inputs: azureSubscription: opensource-api-connection scriptType: pscore scriptLocation: inlineScript + # PackageInfoFiles example: @('a/file1.json','a/file2.json') inlineScript: | $(Build.SourcesDirectory)/eng/common/scripts/Validate-All-Packages.ps1 ` -ArtifactList ('${{ convertToJson(parameters.Artifacts) }}' | ConvertFrom-Json | Select-Object Name) ` @@ -24,7 +34,8 @@ steps: -ConfigFileDir '${{ parameters.ConfigFileDir }}' ` -BuildDefinition $(System.CollectionUri)$(System.TeamProject)/_build?definitionId=$(System.DefinitionId) ` -PipelineUrl $(System.CollectionUri)$(System.TeamProject)/_build/results?buildId=$(Build.BuildId) ` - -IsReleaseBuild $$(SetAsReleaseBuild) + -IsReleaseBuild $$(SetAsReleaseBuild) ` + -PackageInfoFiles @('${{ join(''',''', parameters.PackageInfoFiles) }}') workingDirectory: $(Pipeline.Workspace) displayName: Validate packages and update work items continueOnError: true diff --git a/eng/common/pipelines/templates/steps/verify-changelogs.yml b/eng/common/pipelines/templates/steps/verify-changelogs.yml index 3f36954f4cd..5d6cdf26e99 100644 --- a/eng/common/pipelines/templates/steps/verify-changelogs.yml +++ b/eng/common/pipelines/templates/steps/verify-changelogs.yml @@ -4,6 +4,9 @@ parameters: - name: Condition type: string default: succeeded() +- name: ForRelease + type: boolean + default: false steps: - task: Powershell@2 @@ -11,6 +14,7 @@ steps: filePath: $(Build.SourcesDirectory)/eng/common/scripts/Verify-ChangeLogs.ps1 arguments: > -PackagePropertiesFolder '${{ parameters.PackagePropertiesFolder }}' + -ForRelease $${{ parameters.ForRelease }} pwsh: true displayName: Verify ChangeLogEntries condition: ${{ parameters.Condition }} diff --git a/eng/common/pipelines/templates/steps/verify-links.yml b/eng/common/pipelines/templates/steps/verify-links.yml index 896b30d0fe3..62a95aea8a8 100644 --- a/eng/common/pipelines/templates/steps/verify-links.yml +++ b/eng/common/pipelines/templates/steps/verify-links.yml @@ -6,7 +6,7 @@ parameters: Recursive: $false CheckLinkGuidance: $true Urls: '(Get-ChildItem -Path ./ -Recurse -Include *.md)' - BranchReplaceRegex: "^(${env:SYSTEM_PULLREQUEST_SOURCEREPOSITORYURI}/(?:blob|tree)/)$(DefaultBranch)(/.*)$" + BranchReplaceRegex: "^(${env:SYSTEM_PULLREQUEST_SOURCEREPOSITORYURI}(?:\\.git)?/(?:blob|tree)/)$(DefaultBranch)(/.*)$" BranchReplacementName: "${env:SYSTEM_PULLREQUEST_SOURCECOMMITID}" Condition: succeeded() # If you want to run on failure for the link checker, set it to `Condition: succeededOrFailed()`. diff --git a/eng/common/scripts/ChangeLog-Operations.ps1 b/eng/common/scripts/ChangeLog-Operations.ps1 index f29fc12068d..170157ff511 100644 --- a/eng/common/scripts/ChangeLog-Operations.ps1 +++ b/eng/common/scripts/ChangeLog-Operations.ps1 @@ -8,6 +8,15 @@ $CHANGELOG_UNRELEASED_STATUS = "(Unreleased)" $CHANGELOG_DATE_FORMAT = "yyyy-MM-dd" $RecommendedSectionHeaders = @("Features Added", "Breaking Changes", "Bugs Fixed", "Other Changes") +# Helper function to build the section header regex pattern +function Get-SectionHeaderRegex { + param( + [Parameter(Mandatory = $true)] + [string]$InitialAtxHeader + ) + return "^${InitialAtxHeader}${SECTION_HEADER_REGEX_SUFFIX}" +} + # Returns a Collection of changeLogEntry object containing changelog info for all versions present in the gived CHANGELOG function Get-ChangeLogEntries { param ( @@ -49,9 +58,10 @@ function Get-ChangeLogEntriesFromContent { $initialAtxHeader = $matches["HeaderLevel"] } - $sectionHeaderRegex = "^${initialAtxHeader}${SECTION_HEADER_REGEX_SUFFIX}" + $sectionHeaderRegex = Get-SectionHeaderRegex -InitialAtxHeader $initialAtxHeader $changeLogEntries | Add-Member -NotePropertyName "InitialAtxHeader" -NotePropertyValue $initialAtxHeader $releaseTitleAtxHeader = $initialAtxHeader + "#" + $headerLines = @() try { # walk the document, finding where the version specifiers are and creating lists @@ -83,6 +93,9 @@ function Get-ChangeLogEntriesFromContent { $changeLogEntry.ReleaseContent += $line } + else { + $headerLines += $line + } } } } @@ -90,6 +103,8 @@ function Get-ChangeLogEntriesFromContent { Write-Error "Error parsing Changelog." Write-Error $_ } + + $changeLogEntries | Add-Member -NotePropertyName "HeaderBlock" -NotePropertyValue ($headerLines -Join [Environment]::NewLine) return $changeLogEntries } @@ -265,8 +280,13 @@ function Set-ChangeLogContent { ) $changeLogContent = @() - $changeLogContent += "$($ChangeLogEntries.InitialAtxHeader) Release History" - $changeLogContent += "" + if ($ChangeLogEntries.HeaderBlock) { + $changeLogContent += $ChangeLogEntries.HeaderBlock + } + else { + $changeLogContent += "$($ChangeLogEntries.InitialAtxHeader) Release History" + $changeLogContent += "" + } $ChangeLogEntries = Sort-ChangeLogEntries -changeLogEntries $ChangeLogEntries @@ -290,7 +310,7 @@ function Remove-EmptySections { $InitialAtxHeader = "#" ) - $sectionHeaderRegex = "^${InitialAtxHeader}${SECTION_HEADER_REGEX_SUFFIX}" + $sectionHeaderRegex = Get-SectionHeaderRegex -InitialAtxHeader $InitialAtxHeader $releaseContent = $ChangeLogEntry.ReleaseContent if ($releaseContent.Count -gt 0) @@ -449,3 +469,135 @@ function Confirm-ChangeLogForRelease { } return $ChangeLogStatus.IsValid } + +function Parse-ChangelogContent { + <# + .SYNOPSIS + Parses raw changelog text into structured content with sections. + + .DESCRIPTION + Takes raw changelog text and parses it into structured arrays containing + ReleaseContent (all lines) and Sections (organized by section headers). + This function only generates content structure without modifying any files. + + .PARAMETER ChangelogText + The new changelog text containing sections (e.g., "### Breaking Changes", "### Features Added"). + + .PARAMETER InitialAtxHeader + The markdown header level used in the changelog (e.g., "#" for H1, "##" for H2). + Defaults to "#". + + .OUTPUTS + PSCustomObject with ReleaseContent and Sections properties. + + .EXAMPLE + $content = Parse-ChangelogContent -ChangelogText $changelogText -InitialAtxHeader "#" + $content.ReleaseContent # Array of all lines + $content.Sections # Hashtable of section name to content lines + #> + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [string]$ChangelogText, + + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [string]$InitialAtxHeader = "#" + ) + + Write-Verbose "Parsing changelog text into structured content..." + + # Parse the new changelog content into lines + $changelogLines = $ChangelogText -split "`r?`n" + + # Initialize content structure + $releaseContent = @() + $sections = @{} + + # Add an empty line after the version header + $releaseContent += "" + + # Parse the changelog content + # InitialAtxHeader represents the markdown header level (e.g., "#" for H1, "##" for H2) + # Section headers are two levels deeper than the changelog title + # (e.g., "### Breaking Changes" if InitialAtxHeader is "#") + $currentSection = $null + $sectionHeaderRegex = Get-SectionHeaderRegex -InitialAtxHeader $InitialAtxHeader + + foreach ($line in $changelogLines) { + if ($line.Trim() -match $sectionHeaderRegex) { + $currentSection = $matches["sectionName"].Trim() + $sections[$currentSection] = @() + $releaseContent += $line + Write-Verbose " Found section: $currentSection" + } + elseif ($currentSection) { + $sections[$currentSection] += $line + $releaseContent += $line + } + else { + $releaseContent += $line + } + } + + Write-Verbose " Parsed $($sections.Count) section(s)" + + # Return structured content + return [PSCustomObject]@{ + ReleaseContent = $releaseContent + Sections = $sections + } +} + +function Set-ChangeLogEntryContent { + <# + .SYNOPSIS + Updates a changelog entry with new content. + + .DESCRIPTION + Takes a changelog entry object and new changelog text, parses the text into + structured content, and updates the entry's ReleaseContent and Sections properties. + + .PARAMETER ChangeLogEntry + The changelog entry object to update (from Get-ChangeLogEntries). + + .PARAMETER NewContent + The new changelog text containing sections. + + .PARAMETER InitialAtxHeader + The markdown header level used in the changelog. Defaults to "#". + + .OUTPUTS + The updated changelog entry object. + + .EXAMPLE + $entries = Get-ChangeLogEntries -ChangeLogLocation $changelogPath + $entry = $entries["1.0.0"] + Set-ChangeLogEntryContent -ChangeLogEntry $entry -NewContent $newText -InitialAtxHeader $entries.InitialAtxHeader + Set-ChangeLogContent -ChangeLogLocation $changelogPath -ChangeLogEntries $entries + #> + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [ValidateNotNull()] + [PSCustomObject]$ChangeLogEntry, + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [string]$NewContent, + + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [string]$InitialAtxHeader = "#" + ) + + # Parse the new content into structured format + $parsedContent = Parse-ChangelogContent -ChangelogText $NewContent -InitialAtxHeader $InitialAtxHeader + + # Update the entry with the parsed content + $ChangeLogEntry.ReleaseContent = $parsedContent.ReleaseContent + $ChangeLogEntry.Sections = $parsedContent.Sections + + return $ChangeLogEntry +} diff --git a/eng/common/scripts/Create-APIReview.ps1 b/eng/common/scripts/Create-APIReview.ps1 index 5438df06710..e39490cb4e9 100644 --- a/eng/common/scripts/Create-APIReview.ps1 +++ b/eng/common/scripts/Create-APIReview.ps1 @@ -1,28 +1,47 @@ [CmdletBinding()] Param ( - [Parameter(Mandatory=$True)] + [Parameter(Mandatory=$False)] [array] $ArtifactList, [Parameter(Mandatory=$True)] - [string] $ArtifactPath, - [Parameter(Mandatory=$True)] - [string] $APIKey, + [string] $ArtifactPath, [string] $SourceBranch, [string] $DefaultBranch, [string] $RepoName, [string] $BuildId, [string] $PackageName = "", [string] $ConfigFileDir = "", - [string] $APIViewUri = "https://apiview.dev/AutoReview", + [string] $APIViewUri = "https://apiview.org/autoreview", [string] $ArtifactName = "packages", - [bool] $MarkPackageAsShipped = $false + [bool] $MarkPackageAsShipped = $false, + [Parameter(Mandatory=$False)] + [array] $PackageInfoFiles ) Set-StrictMode -Version 3 + . (Join-Path $PSScriptRoot common.ps1) . (Join-Path $PSScriptRoot Helpers ApiView-Helpers.ps1) +# Get Bearer token for APIView authentication +# In Azure DevOps, this uses the service connection's Managed Identity/Service Principal +function Get-ApiViewBearerToken() +{ + try { + $tokenResponse = az account get-access-token --resource "api://apiview" --output json 2>&1 + if ($LASTEXITCODE -ne 0) { + Write-Error "Failed to acquire access token: $tokenResponse" + return $null + } + return ($tokenResponse | ConvertFrom-Json).accessToken + } + catch { + Write-Error "Failed to acquire access token: $($_.Exception.Message)" + return $null + } +} + # Submit API review request and return status whether current revision is approved or pending or failed to create review -function Upload-SourceArtifact($filePath, $apiLabel, $releaseStatus, $packageVersion) +function Upload-SourceArtifact($filePath, $apiLabel, $releaseStatus, $packageVersion, $packageType) { Write-Host "File path: $filePath" $fileName = Split-Path -Leaf $filePath @@ -51,7 +70,7 @@ function Upload-SourceArtifact($filePath, $apiLabel, $releaseStatus, $packageVer $versionContent.Headers.ContentDisposition = $versionParam $multipartContent.Add($versionContent) Write-Host "Request param, packageVersion: $packageVersion" - + $releaseTagParam = [System.Net.Http.Headers.ContentDispositionHeaderValue]::new("form-data") $releaseTagParam.Name = "setReleaseTag" $releaseTagParamContent = [System.Net.Http.StringContent]::new($MarkPackageAsShipped) @@ -59,6 +78,13 @@ function Upload-SourceArtifact($filePath, $apiLabel, $releaseStatus, $packageVer $multipartContent.Add($releaseTagParamContent) Write-Host "Request param, setReleaseTag: $MarkPackageAsShipped" + $packageTypeParam = [System.Net.Http.Headers.ContentDispositionHeaderValue]::new("form-data") + $packageTypeParam.Name = "packageType" + $packageTypeParamContent = [System.Net.Http.StringContent]::new($packageType) + $packageTypeParamContent.Headers.ContentDisposition = $packageTypeParam + $multipartContent.Add($packageTypeParamContent) + Write-Host "Request param, packageType: $packageType" + if ($releaseStatus -and ($releaseStatus -ne "Unreleased")) { $compareAllParam = [System.Net.Http.Headers.ContentDispositionHeaderValue]::new("form-data") @@ -69,9 +95,17 @@ function Upload-SourceArtifact($filePath, $apiLabel, $releaseStatus, $packageVer Write-Host "Request param, compareAllRevisions: true" } - $uri = "${APIViewUri}/UploadAutoReview" + $uri = "${APIViewUri}/upload" + + # Get Bearer token for authentication + $bearerToken = Get-ApiViewBearerToken + if (-not $bearerToken) { + Write-Error "Failed to acquire Bearer token for APIView authentication." + return [System.Net.HttpStatusCode]::Unauthorized + } + $headers = @{ - "ApiKey" = $apiKey; + "Authorization" = "Bearer $bearerToken"; "content-type" = "multipart/form-data" } @@ -83,44 +117,62 @@ function Upload-SourceArtifact($filePath, $apiLabel, $releaseStatus, $packageVer } catch { - Write-Host "Exception details: $($_.Exception.Response)" + Write-Host "ERROR: API request failed" -ForegroundColor Red + Write-Host "Status Code: $($_.Exception.Response.StatusCode.Value__)" -ForegroundColor Yellow + Write-Host "Error: $($_.Exception.Message)" -ForegroundColor Yellow + if ($_.ErrorDetails.Message) { + Write-Host "Details: $($_.ErrorDetails.Message)" -ForegroundColor Yellow + } $StatusCode = $_.Exception.Response.StatusCode } return $StatusCode } -function Upload-ReviewTokenFile($packageName, $apiLabel, $releaseStatus, $reviewFileName, $packageVersion, $filePath) +function Upload-ReviewTokenFile($packageName, $apiLabel, $releaseStatus, $reviewFileName, $packageVersion, $filePath, $packageType) { Write-Host "Original File path: $filePath" $fileName = Split-Path -Leaf $filePath Write-Host "OriginalFile name: $fileName" - $params = "buildId=${BuildId}&artifactName=${ArtifactName}&originalFilePath=${fileName}&reviewFilePath=${reviewFileName}" - $params += "&label=${apiLabel}&repoName=${RepoName}&packageName=${packageName}&project=internal&packageVersion=${packageVersion}" + $params = "buildId=${BuildId}&artifactName=${ArtifactName}&originalFilePath=${fileName}&reviewFilePath=${reviewFileName}" + $params +="&label=${apiLabel}&repoName=${RepoName}&packageName=${packageName}&project=internal&packageVersion=${packageVersion}&packageType=${packageType}" if($MarkPackageAsShipped) { $params += "&setReleaseTag=true" } - $uri = "${APIViewUri}/CreateApiReview?${params}" + $uri = "${APIViewUri}/create?${params}" if ($releaseStatus -and ($releaseStatus -ne "Unreleased")) { $uri += "&compareAllRevisions=true" } Write-Host "Request to APIView: $uri" + + # Get Bearer token for authentication + $bearerToken = Get-ApiViewBearerToken + if (-not $bearerToken) { + Write-Error "Failed to acquire Bearer token for APIView authentication." + return [System.Net.HttpStatusCode]::Unauthorized + } + $headers = @{ - "ApiKey" = $APIKey; + "Authorization" = "Bearer $bearerToken" } try { - $Response = Invoke-WebRequest -Method 'GET' -Uri $uri -Headers $headers + $Response = Invoke-WebRequest -Method 'POST' -Uri $uri -Headers $headers Write-Host "API review: $($Response.Content)" $StatusCode = $Response.StatusCode } catch { - Write-Host "Exception details: $($_.Exception)" + Write-Host "ERROR: API request failed" -ForegroundColor Red + Write-Host "Status Code: $($_.Exception.Response.StatusCode.Value__)" -ForegroundColor Yellow + Write-Host "Error: $($_.Exception.Message)" -ForegroundColor Yellow + if ($_.ErrorDetails.Message) { + Write-Host "Details: $($_.ErrorDetails.Message)" -ForegroundColor Yellow + } $StatusCode = $_.Exception.Response.StatusCode } @@ -141,21 +193,21 @@ function Get-APITokenFileName($packageName) } } -function Submit-APIReview($packageInfo, $packagePath, $packageArtifactName) +function Submit-APIReview($packageInfo, $packagePath) { - $packageName = $packageInfo.Name $apiLabel = "Source Branch:${SourceBranch}" + $packageType = $packageInfo.SdkType # Get generated review token file if present # APIView processes request using different API if token file is already generated - $reviewTokenFileName = Get-APITokenFileName $packageArtifactName + $reviewTokenFileName = Get-APITokenFileName $packageInfo.ArtifactName if ($reviewTokenFileName) { Write-Host "Uploading review token file $reviewTokenFileName to APIView." - return Upload-ReviewTokenFile $packageArtifactName $apiLabel $packageInfo.ReleaseStatus $reviewTokenFileName $packageInfo.Version $packagePath + return Upload-ReviewTokenFile $packageInfo.ArtifactName $apiLabel $packageInfo.ReleaseStatus $reviewTokenFileName $packageInfo.Version $packagePath $packageType } else { Write-Host "Uploading $packagePath to APIView." - return Upload-SourceArtifact $packagePath $apiLabel $packageInfo.ReleaseStatus $packageInfo.Version + return Upload-SourceArtifact $packagePath $apiLabel $packageInfo.ReleaseStatus $packageInfo.Version $packageType } } @@ -172,12 +224,32 @@ function IsApiviewStatusCheckRequired($packageInfo) return $false } -function ProcessPackage($packageName) +function ProcessPackage($packageInfo) { $packages = @{} if ($FindArtifactForApiReviewFn -and (Test-Path "Function:$FindArtifactForApiReviewFn")) { - $packages = &$FindArtifactForApiReviewFn $ArtifactPath $packageName + $pkgArtifactName = $packageInfo.ArtifactName ?? $packageInfo.Name + + # Check if the function supports the packageInfo parameter + $functionInfo = Get-Command $FindArtifactForApiReviewFn -ErrorAction SilentlyContinue + $supportsPackageInfoParam = $false + + if ($functionInfo -and $functionInfo.Parameters) { + # Check if function specifically supports packageInfo parameter + $parameterNames = $functionInfo.Parameters.Keys + $supportsPackageInfoParam = $parameterNames -contains 'packageInfo' + } + + # Call function with appropriate parameters + if ($supportsPackageInfoParam) { + LogInfo "Calling $FindArtifactForApiReviewFn with packageInfo parameter" + $packages = &$FindArtifactForApiReviewFn $ArtifactPath $packageInfo + } + else { + LogInfo "Calling $FindArtifactForApiReviewFn with legacy parameters" + $packages = &$FindArtifactForApiReviewFn $ArtifactPath $pkgArtifactName + } } else { @@ -192,31 +264,23 @@ function ProcessPackage($packageName) foreach($pkgPath in $packages.Values) { $pkg = Split-Path -Leaf $pkgPath - $pkgPropPath = Join-Path -Path $ConfigFileDir "$packageName.json" - if (-Not (Test-Path $pkgPropPath)) + $version = [AzureEngSemanticVersion]::ParseVersionString($packageInfo.Version) + if ($null -eq $version) { - Write-Host " Package property file path $($pkgPropPath) is invalid." - continue - } - # Get package info from json file created before updating version to daily dev - $pkgInfo = Get-Content $pkgPropPath | ConvertFrom-Json - $version = [AzureEngSemanticVersion]::ParseVersionString($pkgInfo.Version) - if ($version -eq $null) - { - Write-Host "Version info is not available for package $packageName, because version '$(pkgInfo.Version)' is invalid. Please check if the version follows Azure SDK package versioning guidelines." + Write-Host "Version info is not available for package $($packageInfo.ArtifactName), because version '$($packageInfo.Version)' is invalid. Please check if the version follows Azure SDK package versioning guidelines." return 1 } - + Write-Host "Version: $($version)" - Write-Host "SDK Type: $($pkgInfo.SdkType)" - Write-Host "Release Status: $($pkgInfo.ReleaseStatus)" + Write-Host "SDK Type: $($packageInfo.SdkType)" + Write-Host "Release Status: $($packageInfo.ReleaseStatus)" # Run create review step only if build is triggered from main branch or if version is GA. # This is to avoid invalidating review status by a build triggered from feature branch if ( ($SourceBranch -eq $DefaultBranch) -or (-not $version.IsPrerelease) -or $MarkPackageAsShipped) { Write-Host "Submitting API Review request for package $($pkg), File path: $($pkgPath)" - $respCode = Submit-APIReview $pkgInfo $pkgPath $packageName + $respCode = Submit-APIReview $packageInfo $pkgPath Write-Host "HTTP Response code: $($respCode)" # no need to check API review status when marking a package as shipped @@ -224,10 +288,10 @@ function ProcessPackage($packageName) { if ($respCode -eq '500') { - Write-Host "Failed to mark package ${packageName} as released. Please reach out to Azure SDK engineering systems on teams channel." + Write-Host "Failed to mark package $($packageInfo.ArtifactName) as released. Please reach out to Azure SDK engineering systems on teams channel." return 1 } - Write-Host "Package ${packageName} is marked as released." + Write-Host "Package $($packageInfo.ArtifactName) is marked as released." return 0 } @@ -239,41 +303,41 @@ function ProcessPackage($packageName) IsApproved = $false Details = "" } - Process-ReviewStatusCode $respCode $packageName $apiStatus $pkgNameStatus + Process-ReviewStatusCode $respCode $packageInfo.ArtifactName $apiStatus $pkgNameStatus if ($apiStatus.IsApproved) { Write-Host "API status: $($apiStatus.Details)" } - elseif (!$pkgInfo.ReleaseStatus -or $pkgInfo.ReleaseStatus -eq "Unreleased") { + elseif (!$packageInfo.ReleaseStatus -or $packageInfo.ReleaseStatus -eq "Unreleased") { Write-Host "Release date is not set for current version in change log file for package. Ignoring API review approval status since package is not yet ready for release." } elseif ($version.IsPrerelease) { # Check if package name is approved. Preview version cannot be released without package name approval - if (!$pkgNameStatus.IsApproved) + if (!$pkgNameStatus.IsApproved) { - if (IsApiviewStatusCheckRequired $pkgInfo) + if (IsApiviewStatusCheckRequired $packageInfo) { Write-Error $($pkgNameStatus.Details) return 1 } else{ - Write-Host "Package name is not approved for package $($packageName), however it is not required for this package type so it can still be released without API review approval." - } + Write-Host "Package name is not approved for package $($packageInfo.ArtifactName), however it is not required for this package type so it can still be released without API review approval." + } } # Ignore API review status for prerelease version Write-Host "Package version is not GA. Ignoring API view approval status" - } + } else { # Return error code if status code is 201 for new data plane package # Temporarily enable API review for spring SDK types. Ideally this should be done be using 'IsReviewRequired' method in language side # to override default check of SDK type client - if (IsApiviewStatusCheckRequired $pkgInfo) + if (IsApiviewStatusCheckRequired $packageInfo) { if (!$apiStatus.IsApproved) { - Write-Host "Package version $($version) is GA and automatic API Review is not yet approved for package $($packageName)." + Write-Host "Package version $($version) is GA and automatic API Review is not yet approved for package $($packageInfo.ArtifactName)." Write-Host "Build and release is not allowed for GA package without API review approval." Write-Host "You will need to queue another build to proceed further after API review is approved" Write-Host "You can check http://aka.ms/azsdk/engsys/apireview/faq for more details on API Approval." @@ -281,7 +345,7 @@ function ProcessPackage($packageName) return 1 } else { - Write-Host "API review is not approved for package $($packageName), however it is not required for this package type so it can still be released without API review approval." + Write-Host "API review is not approved for package $($packageInfo.ArtifactName), however it is not required for this package type so it can still be released without API review approval." } } } @@ -296,42 +360,85 @@ function ProcessPackage($packageName) return 0 } -$responses = @{} -# Check if package config file is present. This file has package version, SDK type etc info. -if (-not $ConfigFileDir) -{ +Write-Host "Artifact path: $($ArtifactPath)" +Write-Host "Source branch: $($SourceBranch)" +Write-Host "Package Info Files: $($PackageInfoFiles)" +Write-Host "Artifact List: $($ArtifactList)" +Write-Host "Package Name: $($PackageName)" + +# Parameter priority and backward compatibility logic +# Priority order: PackageName > Artifacts > PackageInfoFiles (for backward compatibility) + +if (-not $ConfigFileDir) { $ConfigFileDir = Join-Path -Path $ArtifactPath "PackageInfo" } -Write-Host "Artifact path: $($ArtifactPath)" -Write-Host "Source branch: $($SourceBranch)" Write-Host "Config File directory: $($ConfigFileDir)" -# if package name param is not empty then process only that package -if ($PackageName) -{ - Write-Host "Processing $($PackageName)" - $result = ProcessPackage -packageName $PackageName - $responses[$PackageName] = $result +# Initialize working variable +$ProcessedPackageInfoFiles = @() + +if ($PackageName -and $PackageName -ne "") { + # Highest Priority: Single package mode (existing usage) + Write-Host "Using PackageName parameter: $PackageName" + $pkgPropPath = Join-Path -Path $ConfigFileDir "$PackageName.json" + if (Test-Path $pkgPropPath) { + $ProcessedPackageInfoFiles = @($pkgPropPath) + } + else { + Write-Error "Package property file path $pkgPropPath is invalid." + exit 1 + } } -else -{ - # process all packages in the artifact - foreach ($artifact in $ArtifactList) - { - Write-Host "Processing $($artifact.name)" - $result = ProcessPackage -packageName $artifact.name - $responses[$artifact.name] = $result +elseif ($ArtifactList -and $ArtifactList.Count -gt 0) { + # Second Priority: Multiple artifacts mode (existing usage) + Write-Host "Using ArtifactList parameter with $($ArtifactList.Count) artifacts" + foreach ($artifact in $ArtifactList) { + $pkgPropPath = Join-Path -Path $ConfigFileDir "$($artifact.name).json" + if (Test-Path $pkgPropPath) { + $ProcessedPackageInfoFiles += $pkgPropPath + } + else { + Write-Warning "Package property file path $pkgPropPath is invalid." + } } } +elseif ($PackageInfoFiles -and $PackageInfoFiles.Count -gt 0) { + # Lowest Priority: Direct PackageInfoFiles (new method) + Write-Host "Using PackageInfoFiles parameter with $($PackageInfoFiles.Count) files" + # Filter out empty strings or whitespace-only entries + $ProcessedPackageInfoFiles = @($PackageInfoFiles | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }) +} +else { + Write-Error "No package information provided. Please provide either 'PackageName', 'ArtifactList', or 'PackageInfoFiles' parameters." + exit 1 +} + +# Validate that we have package info files to process +if (-not $ProcessedPackageInfoFiles -or $ProcessedPackageInfoFiles.Count -eq 0) { + Write-Error "No package info files found after processing parameters. Or PackageInfoFiles parameter contains only empty or whitespace entries, please check the artifact settings." + exit 1 +} + +$responses = @{} +Write-Host "Processed Package Info Files: $($ProcessedPackageInfoFiles -join ', ')" + +# Process all packages using the processed PackageInfoFiles array +foreach ($packageInfoFile in $ProcessedPackageInfoFiles) +{ + $packageInfo = Get-Content $packageInfoFile | ConvertFrom-Json + Write-Host "Processing $($packageInfo.ArtifactName)" + $result = ProcessPackage -packageInfo $packageInfo + $responses[$packageInfo.ArtifactName] = $result +} $exitCode = 0 foreach($pkg in $responses.keys) -{ +{ if ($responses[$pkg] -eq 1) { Write-Host "API changes are not approved for $($pkg)" $exitCode = 1 } } -exit $exitCode \ No newline at end of file +exit $exitCode diff --git a/eng/common/scripts/Delete-RemoteBranches.ps1 b/eng/common/scripts/Delete-RemoteBranches.ps1 index 55b20f63126..9ff5006e1ab 100644 --- a/eng/common/scripts/Delete-RemoteBranches.ps1 +++ b/eng/common/scripts/Delete-RemoteBranches.ps1 @@ -8,6 +8,12 @@ param( # For sync-eng/common work, we use regex as "^sync-eng/common.*-(?\d+).*$". # For sync-.github/workflows work, we use regex as "^sync-.github/workflows.*-(?\d+).*$". $BranchRegex, + # When set, directly delete this exact branch name without querying all branches. + # This is a fast path that avoids the expensive GraphQL branch listing and rate limit + # checks, useful when the caller already knows the exact branch name to delete (e.g. + # the eng/common sync pipeline). If the branch does not exist, a warning is + # logged and the script exits successfully. + [string]$TargetBranch, # Date format: e.g. Tuesday, April 12, 2022 1:36:02 PM. Allow to use other date format. [AllowNull()] [DateTime]$LastCommitOlderThan, @@ -66,6 +72,33 @@ if ($AuthToken) { $owner, $repo = $RepoId -split "/" +# Fast path: when the caller already knows the exact branch name, skip the expensive +# GraphQL query that lists all branches and the associated rate limit checks. This +# reduces the number of GitHub API calls from ~5+ down to 1 per repo, which helps +# avoid hitting the secondary rate limit when deleting branches across many repos. +if ($TargetBranch) { + LogDebug "TargetBranch specified, using direct deletion for branch '$TargetBranch' in '$RepoId'" + if ($PSCmdlet.ShouldProcess("'$TargetBranch' in '$RepoId'", "Deleting branch (target)")) { + $response = gh api "repos/${RepoId}/git/refs/heads/${TargetBranch}" -X DELETE 2>&1 + if ($LASTEXITCODE) { + # GitHub returns 422 when the ref doesn't exist. Treat that as a non-fatal warning + # since the goal (branch gone) is already achieved. + if ($response -match "Reference does not exist" -or $response -match "HTTP 422") { + LogWarning "Branch '$TargetBranch' does not exist in '$RepoId'. It may have already been deleted." + } + else { + LogError "Deletion of branch '$TargetBranch' in '$RepoId' failed: $response" + exit 1 + } + } + else { + LogDebug "Successfully deleted branch '$TargetBranch' in '$RepoId'." + } + } + # Nothing else to do, exit early. + exit 0 +} + # These will always be output at the end of the script. Their only purpose is for information gathering # Total number returned from query $totalBranchesFromQuery = 0 diff --git a/eng/common/scripts/Detect-Api-Changes.ps1 b/eng/common/scripts/Detect-Api-Changes.ps1 index 25f3101a8b9..c8e00935cbe 100644 --- a/eng/common/scripts/Detect-Api-Changes.ps1 +++ b/eng/common/scripts/Detect-Api-Changes.ps1 @@ -22,8 +22,16 @@ Param ( $configFileDir = Join-Path -Path $ArtifactPath "PackageInfo" # Submit API review request and return status whether current revision is approved or pending or failed to create review -function Submit-Request($filePath, $packageName) +function Submit-Request($filePath, $packageInfo) { + $packageName = $packageInfo.ArtifactName ?? $packageInfo.Name + $packageType = $packageInfo.SdkType + + # Construct full package name with groupId if available + $fullPackageName = $packageName + if ($packageInfo.PSObject.Members.Name -contains "Group" -and $packageInfo.Group) { + $fullPackageName = "$($packageInfo.Group):$packageName" + } $repoName = $RepoFullName if (!$repoName) { $repoName = "azure/azure-sdk-for-$LanguageShort" @@ -36,9 +44,10 @@ function Submit-Request($filePath, $packageName) $query.Add('commitSha', $CommitSha) $query.Add('repoName', $repoName) $query.Add('pullRequestNumber', $PullRequestNumber) - $query.Add('packageName', $packageName) + $query.Add('packageName', $fullPackageName) $query.Add('language', $LanguageShort) $query.Add('project', $DevopsProject) + $query.Add('packageType', $packageType) $reviewFileFullName = Join-Path -Path $ArtifactPath $packageName $reviewFileName # If CI generates token file then it passes both token file name and original file (filePath) to APIView # If both files are passed then APIView downloads the parent directory as a zip @@ -73,29 +82,26 @@ function Submit-Request($filePath, $packageName) } catch { - LogError "Error $StatusCode - Exception details: $($_.Exception.Response)" + Write-Host "ERROR: API request failed" -ForegroundColor Red + Write-Host "Status Code: $($_.Exception.Response.StatusCode.Value__)" -ForegroundColor Yellow + Write-Host "Error: $($_.Exception.Message)" -ForegroundColor Yellow + if ($_.ErrorDetails.Message) { + Write-Host "Details: $($_.ErrorDetails.Message)" -ForegroundColor Yellow + } + LogError "Failed to detect API changes. See details above." $StatusCode = $_.Exception.Response.StatusCode } return $StatusCode } -function Should-Process-Package($pkgPath, $packageName) +function Should-Process-Package($packageInfo) { - $pkg = Split-Path -Leaf $pkgPath - $pkgPropPath = Join-Path -Path $configFileDir "$packageName.json" - if (!(Test-Path $pkgPropPath)) - { - LogWarning "Package property file path $($pkgPropPath) is invalid." - return $False - } - # Get package info from json file created before updating version to daily dev - $pkgInfo = Get-Content $pkgPropPath | ConvertFrom-Json - $packagePath = $pkgInfo.DirectoryPath + $packagePath = $packageInfo.DirectoryPath $modifiedFiles = @(Get-ChangedFiles -DiffPath "$packagePath/*" -DiffFilterType '') $filteredFileCount = $modifiedFiles.Count LogInfo "Number of modified files for package: $filteredFileCount" - return ($filteredFileCount -gt 0 -and $pkgInfo.IsNewSdk) + return ($filteredFileCount -gt 0 -and $packageInfo.IsNewSdk) } function Log-Input-Params() @@ -126,29 +132,48 @@ $responses = @{} LogInfo "Processing PackageInfo at $configFileDir" -$packageProperties = Get-ChildItem -Recurse -Force "$configFileDir" ` - | Where-Object { +$packageInfoFiles = Get-ChildItem -Recurse -Force "$configFileDir" ` + | Where-Object { $_.Extension -eq '.json' -and ($_.FullName.Substring($configFileDir.Length + 1) -notmatch '^_.*?[\\\/]') } -foreach ($packagePropFile in $packageProperties) +foreach ($packageInfoFile in $packageInfoFiles) { - $packageMetadata = Get-Content $packagePropFile | ConvertFrom-Json - $pkgArtifactName = $packageMetadata.ArtifactName ?? $packageMetadata.Name + $packageInfo = Get-Content $packageInfoFile | ConvertFrom-Json + $pkgArtifactName = $packageInfo.ArtifactName ?? $packageInfo.Name + $packageType = $packageInfo.SdkType LogInfo "Processing $($pkgArtifactName)" - $packages = &$FindArtifactForApiReviewFn $ArtifactPath $pkgArtifactName + # Check if the function supports the packageInfo parameter + $functionInfo = Get-Command $FindArtifactForApiReviewFn -ErrorAction SilentlyContinue + $supportsPackageInfoParam = $false + + if ($functionInfo -and $functionInfo.Parameters) { + # Check if function specifically supports packageInfo parameter + $parameterNames = $functionInfo.Parameters.Keys + $supportsPackageInfoParam = $parameterNames -contains 'packageInfo' + } + + # Call function with appropriate parameters + if ($supportsPackageInfoParam) { + LogInfo "Calling $FindArtifactForApiReviewFn with packageInfo parameter" + $packages = &$FindArtifactForApiReviewFn $ArtifactPath $packageInfo + } + else { + LogInfo "Calling $FindArtifactForApiReviewFn with legacy parameters" + $packages = &$FindArtifactForApiReviewFn $ArtifactPath $pkgArtifactName + } if ($packages) { $pkgPath = $packages.Values[0] - $isRequired = Should-Process-Package -pkgPath $pkgPath -packageName $pkgArtifactName + $isRequired = Should-Process-Package $packageInfo LogInfo "Is API change detect required for $($pkgArtifactName):$($isRequired)" if ($isRequired -eq $True) { $filePath = $pkgPath.Replace($ArtifactPath , "").Replace("\", "/") - $respCode = Submit-Request -filePath $filePath -packageName $pkgArtifactName + $respCode = Submit-Request -filePath $filePath -packageInfo $packageInfo if ($respCode -ne '200') { $responses[$pkgArtifactName] = $respCode @@ -156,7 +181,7 @@ foreach ($packagePropFile in $packageProperties) } else { - LogInfo "Pull request does not have any change for $($pkgArtifactName)). Skipping API change detect." + LogInfo "Pull request does not have any change for $($pkgArtifactName). Skipping API change detect." } } else diff --git a/eng/common/scripts/Helpers/ApiView-Helpers.ps1 b/eng/common/scripts/Helpers/ApiView-Helpers.ps1 index e8d867db9e9..e6537145230 100644 --- a/eng/common/scripts/Helpers/ApiView-Helpers.ps1 +++ b/eng/common/scripts/Helpers/ApiView-Helpers.ps1 @@ -124,7 +124,7 @@ function Set-ApiViewCommentForRelatedIssues { param ( [Parameter(Mandatory = $true)] [string]$HeadCommitish, - [string]$APIViewHost = "https://apiview.dev", + [string]$APIViewHost = "https://apiview.org", [ValidateNotNullOrEmpty()] [Parameter(Mandatory = $true)] $AuthToken @@ -215,7 +215,7 @@ function Set-ApiViewCommentForPR { try { $existingComment = Get-GitHubIssueComments -RepoOwner $RepoOwner -RepoName $RepoName -IssueNumber $PrNumber -AuthToken $AuthToken $existingAPIViewComment = $existingComment | Where-Object { - $_.body.StartsWith("**API Change Check**", [StringComparison]::OrdinalIgnoreCase) -or $_.body.StartsWith("## API Change Check", [StringComparison]::OrdinalIgnoreCase) } + $_.body.StartsWith("**API Change Check**", [StringComparison]::OrdinalIgnoreCase) -or $_.body.StartsWith("## API Change Check", [StringComparison]::OrdinalIgnoreCase) } | Select-Object -Last 1 } catch { LogWarning "Failed to get comments from Pull Request: $PrNumber in repo: $repoFullName" } @@ -241,7 +241,7 @@ function Set-ApiViewCommentForPR { # Helper function used to create API review requests for Spec generation SDKs pipelines function Create-API-Review { param ( - [string]$apiviewEndpoint = "https://apiview.dev/api/PullRequests/CreateAPIRevisionIfAPIHasChanges", + [string]$apiviewEndpoint = "https://apiview.org/api/PullRequests/CreateAPIRevisionIfAPIHasChanges", [string]$specGenSDKArtifactPath, [string]$apiviewArtifactName, [string]$buildId, diff --git a/eng/common/scripts/Helpers/AzSdkTool-Helpers.ps1 b/eng/common/scripts/Helpers/AzSdkTool-Helpers.ps1 index 4fa98119613..8d74ece07dd 100644 --- a/eng/common/scripts/Helpers/AzSdkTool-Helpers.ps1 +++ b/eng/common/scripts/Helpers/AzSdkTool-Helpers.ps1 @@ -192,3 +192,61 @@ function Install-Standalone-Tool ( return $executable_path } + +function Get-CommonInstallDirectory { + $installDirectory = Join-Path $HOME "bin" + if (-not (Test-Path $installDirectory)) { + New-Item -ItemType Directory -Path $installDirectory -Force | Out-Null + } + + # Update PATH in current session + if (-not ($env:PATH -like "*$InstallDirectory*")) { + $env:PATH += ";$InstallDirectory" + } + + return $installDirectory +} + +function Add-InstallDirectoryToPathInProfile( + [Parameter()] + [string]$InstallDirectory = (Get-CommonInstallDirectory) +) { + $powershellProfilePath = $PROFILE + $bashrcPath = Join-Path $HOME ".bashrc" + $zshrcPath = Join-Path $HOME ".zshrc" + $markerComment = " # azsdk install path" + $pathCommand = "" + $configFile = "" + + if ($IsWindows) { # expect powershell for windows, cmd.exe path update is not currently supported + $configFile = $powershellProfilePath + $pathCommand = "if (-not (`$env:PATH -like `'*$InstallDirectory*`')) { `$env:PATH += ';$InstallDirectory`' }" + $markerComment + } + elseif ($IsLinux) { # handle bash or zsh shells for linux + if (Test-Path $zshrcPath) { + $configFile = $zshrcPath + } + else { + $configFile = $bashrcPath + } + $pathCommand = "export PATH=`"`$PATH:$InstallDirectory`"" + $markerComment + } + elseif ($IsMacOS) { # mac os should use zsh by default + $configFile = $zshrcPath + $pathCommand = "export PATH=`"`$PATH:$InstallDirectory`"" + $markerComment + } + else { + throw "Unsupported platform" + } + + if (-not (Test-Path $configFile)) { + New-Item -ItemType File -Path $configFile -Force | Out-Null + } + + $configContent = Get-Content $configFile -Raw + + if (!$configContent -or !$configContent.Contains($markerComment)) { + Write-Host "Adding installation to PATH in shell profile at '$configFile'" + Add-Content -Path $configFile -Value ([Environment]::NewLine + $pathCommand) + } +} diff --git a/eng/common/scripts/Helpers/CommandInvocation-Helpers.ps1 b/eng/common/scripts/Helpers/CommandInvocation-Helpers.ps1 index 0b9f810b83a..48b81498728 100644 --- a/eng/common/scripts/Helpers/CommandInvocation-Helpers.ps1 +++ b/eng/common/scripts/Helpers/CommandInvocation-Helpers.ps1 @@ -1,4 +1,6 @@ -function Invoke-LoggedCommand +. $PSScriptRoot/../logging.ps1 + +function Invoke-LoggedMsbuildCommand { [CmdletBinding()] param @@ -8,12 +10,27 @@ function Invoke-LoggedCommand [switch] $GroupOutput, [int[]] $AllowedExitCodes = @(0) ) + return Invoke-LoggedCommand $Command -ExecutePath $ExecutePath -GroupOutput:$GroupOutput -AllowedExitCodes $AllowedExitCodes -OutputProcessor { param($line) ProcessMsBuildLogLine $line } + +} + +function Invoke-LoggedCommand +{ + [CmdletBinding()] + param + ( + [string] $Command, + [string] $ExecutePath, + [switch] $GroupOutput, + [int[]] $AllowedExitCodes = @(0), + [switch] $DoNotExitOnFailedExitCode, + [scriptblock] $OutputProcessor + ) - $pipelineBuild = !!$env:TF_BUILD $startTime = Get-Date - if($pipelineBuild -and $GroupOutput) { - Write-Host "##[group]$Command" + if($GroupOutput) { + LogGroupStart $Command } else { Write-Host "> $Command" } @@ -22,21 +39,24 @@ function Invoke-LoggedCommand Push-Location $ExecutePath } + if (!$OutputProcessor) { + $OutputProcessor = { param($line) $line } + } + try { - Invoke-Expression $Command + Invoke-Expression $Command | Foreach-Object { & $OutputProcessor $_ } $duration = (Get-Date) - $startTime - if($pipelineBuild -and $GroupOutput) { - Write-Host "##[endgroup]" + if($GroupOutput) { + LogGroupEnd } - if($LastExitCode -notin $AllowedExitCodes) + if($LASTEXITCODE -notin $AllowedExitCodes) { - if($pipelineBuild) { - Write-Error "##[error]Command failed to execute ($duration): $Command`n" - } else { - Write-Error "Command failed to execute ($duration): $Command`n" + LogError "Command failed to execute ($duration): $Command`n" + if (!$DoNotExitOnFailedExitCode) { + exit $LASTEXITCODE } } else { diff --git a/eng/common/scripts/Helpers/DevOps-WorkItem-Helpers.ps1 b/eng/common/scripts/Helpers/DevOps-WorkItem-Helpers.ps1 index 9a46e2c126f..f91693dcafd 100644 --- a/eng/common/scripts/Helpers/DevOps-WorkItem-Helpers.ps1 +++ b/eng/common/scripts/Helpers/DevOps-WorkItem-Helpers.ps1 @@ -1,8 +1,15 @@ +. (Join-Path $PSScriptRoot .. SemVer.ps1) + $ReleaseDevOpsOrgParameters = @("--organization", "https://dev.azure.com/azure-sdk") $ReleaseDevOpsCommonParameters = $ReleaseDevOpsOrgParameters + @("--output", "json") $ReleaseDevOpsCommonParametersWithProject = $ReleaseDevOpsCommonParameters + @("--project", "Release") +# This is used to determine whether or not the az login and azure-devops extension +# install have already been completed. +$global:AzLoginAndDevOpsExtensionInstallComplete = $false +$global:HasDevOpsAccess = $false + function Get-DevOpsRestHeaders() { # Get a temp access token from the logged in az cli user for azure devops resource @@ -17,27 +24,68 @@ function Get-DevOpsRestHeaders() return $headers } +# Function was created from the same code being in Update-DevOps-Release-WorkItem.ps1 +# and Validate-Package.ps1. The global variable is used to prevent az commands from +# being rerun multiple times +function CheckAzLoginAndDevOpsExtensionInstall() +{ + if (-not $global:AzLoginAndDevOpsExtensionInstallComplete) { + az account show *> $null + if (!$?) { + Write-Host 'Running az login...' + az login *> $null + } + + az extension show -n azure-devops *> $null + if (!$?){ + az extension add --name azure-devops + } else { + # Force update the extension to the latest version if it was already installed + # this is needed to ensure we have the authentication issue fixed from earlier versions + az extension update -n azure-devops *> $null + } + $global:AzLoginAndDevOpsExtensionInstallComplete = $true + } +} + function CheckDevOpsAccess() { - # Dummy test query to validate permissions - $query = "SELECT [System.ID] FROM WorkItems WHERE [Work Item Type] = 'Package' AND [Package] = 'azure-sdk-template'" + if (-not $global:HasDevOpsAccess) { + # Dummy test query to validate permissions + $query = "SELECT [System.ID] FROM WorkItems WHERE [Work Item Type] = 'Package' AND [Package] = 'azure-sdk-template'" - $response = Invoke-RestMethod -Method POST ` - -Uri "https://dev.azure.com/azure-sdk/Release/_apis/wit/wiql/?api-version=6.0" ` - -Headers (Get-DevOpsRestHeaders) -Body "{ ""query"": ""$query"" }" -ContentType "application/json" | ConvertTo-Json -Depth 10 | ConvertFrom-Json -AsHashTable + $response = Invoke-RestMethod -Method POST ` + -Uri "https://dev.azure.com/azure-sdk/Release/_apis/wit/wiql/?api-version=6.0" ` + -Headers (Get-DevOpsRestHeaders) -Body "{ ""query"": ""$query"" }" -ContentType "application/json" | ConvertTo-Json -Depth 10 | ConvertFrom-Json -AsHashTable - if ($response -isnot [HashTable] -or !$response.ContainsKey("workItems")) { - throw "Failed to run test query against Azure DevOps. Please ensure you are logged into the public azure cloud. Consider running 'az logout' and then 'az login'." + if ($response -isnot [HashTable] -or !$response.ContainsKey("workItems")) { + throw "Failed to run test query against Azure DevOps. Please ensure you are logged into the public azure cloud. Consider running 'az logout' and then 'az login'." + } + $global:HasDevOpsAccess = $true } } +function GetGroupId($pkg) +{ + if ($pkg.PSObject.Properties.Name -contains "GroupId") { + return $pkg.GroupId + } + return $null +} + function Invoke-AzBoardsCmd($subCmd, $parameters, $output = $true) { $azCmdStr = "az boards ${subCmd} $($parameters -join ' ')" if ($output) { Write-Host $azCmdStr } - return Invoke-Expression "$azCmdStr" | ConvertFrom-Json -AsHashTable + $response = Invoke-Expression "$azCmdStr" | ConvertFrom-Json -AsHashtable + + if ($LASTEXITCODE -ne 0) { + Write-Host "ERROR command failed: $azCmdStr" + } + + return $response } function Invoke-Query($fields, $wiql, $output = $true) @@ -175,16 +223,17 @@ function FindParentWorkItem($serviceName, $packageDisplayName, $outputCommand = $packageWorkItems = @{} $packageWorkItemWithoutKeyFields = @{} -function FindLatestPackageWorkItem($lang, $packageName, $outputCommand = $true, $ignoreReleasePlannerTests = $true, $tag = $null) +function FindLatestPackageWorkItem($lang, $packageName, $groupId = $null, $outputCommand = $true, $ignoreReleasePlannerTests = $true, $tag = $null) { # Cache all the versions of this package and language work items - $null = FindPackageWorkItem $lang $packageName -includeClosed $true -outputCommand $outputCommand -ignoreReleasePlannerTests $ignoreReleasePlannerTests -tag $tag + $null = FindPackageWorkItem $lang $packageName -includeClosed $true -outputCommand $outputCommand -ignoreReleasePlannerTests $ignoreReleasePlannerTests -tag $tag -groupId $groupId $latestWI = $null foreach ($wi in $packageWorkItems.Values) { if ($wi.fields["Custom.Language"] -ne $lang) { continue } if ($wi.fields["Custom.Package"] -ne $packageName) { continue } + if ($groupId -and $wi.fields["Custom.GroupId"] -ne $groupId) { continue } if (!$latestWI) { $latestWI = $wi @@ -198,9 +247,9 @@ function FindLatestPackageWorkItem($lang, $packageName, $outputCommand = $true, return $latestWI } -function FindPackageWorkItem($lang, $packageName, $version, $outputCommand = $true, $includeClosed = $false, $ignoreReleasePlannerTests = $true, $tag = $null) +function FindPackageWorkItem($lang, $packageName, $version, $groupId = $null, $outputCommand = $true, $includeClosed = $false, $ignoreReleasePlannerTests = $true, $tag = $null) { - $key = BuildHashKeyNoNull $lang $packageName $version + $key = BuildHashKey $lang $packageName $version $groupId if ($key -and $packageWorkItems.ContainsKey($key)) { return $packageWorkItems[$key] } @@ -213,6 +262,7 @@ function FindPackageWorkItem($lang, $packageName, $version, $outputCommand = $tr $fields += "System.Tags" $fields += "Custom.Language" $fields += "Custom.Package" + $fields += "Custom.GroupId" $fields += "Custom.PackageDisplayName" $fields += "System.Title" $fields += "Custom.PackageType" @@ -228,6 +278,7 @@ function FindPackageWorkItem($lang, $packageName, $version, $outputCommand = $tr $fields += "Custom.Generated" $fields += "Custom.RoadmapState" $fields += "Microsoft.VSTS.Common.StateChangeDate" + $fields += "Custom.SpecProjectPath" $fieldList = ($fields | ForEach-Object { "[$_]"}) -join ", " $query = "SELECT ${fieldList} FROM WorkItems WHERE [Work Item Type] = 'Package'" @@ -241,6 +292,9 @@ function FindPackageWorkItem($lang, $packageName, $version, $outputCommand = $tr if ($packageName) { $query += " AND [Package] = '${packageName}'" } + if ($groupId) { + $query += " AND [GroupId] = '${groupId}'" + } if ($version) { $query += " AND [PackageVersionMajorMinor] = '${version}'" } @@ -254,7 +308,7 @@ function FindPackageWorkItem($lang, $packageName, $version, $outputCommand = $tr foreach ($wi in $workItems) { - $localKey = BuildHashKeyNoNull $wi.fields["Custom.Language"] $wi.fields["Custom.Package"] $wi.fields["Custom.PackageVersionMajorMinor"] + $localKey = BuildHashKey $wi.fields["Custom.Language"] $wi.fields["Custom.Package"] $wi.fields["Custom.PackageVersionMajorMinor"] $wi.fields["Custom.GroupId"] if (!$localKey) { $packageWorkItemWithoutKeyFields[$wi.id] = $wi Write-Host "Skipping package [$($wi.id)]$($wi.fields['System.Title']) which is missing required fields language, package, or version." @@ -325,6 +379,18 @@ function CreateWorkItemParent($id, $parentId, $oldParentId, $outputCommand = $tr Invoke-AzBoardsCmd "work-item relation add" $parameters $outputCommand | Out-Null } +function CheckUser($user) +{ + $azCmdStr = "az devops user show --user ${user} $($ReleaseDevOpsCommonParameters -join ' ')" + Invoke-Expression "$azCmdStr" | Out-Null + + if ($LASTEXITCODE -ne 0) { + return $false + } + + return $true +} + function CreateWorkItem($title, $type, $iteration, $area, $fields, $assignedTo, $parentId, $relatedId = $null, $outputCommand = $true, $tag = $null) { $parameters = $ReleaseDevOpsCommonParametersWithProject @@ -332,7 +398,7 @@ function CreateWorkItem($title, $type, $iteration, $area, $fields, $assignedTo, $parameters += "--type", "`"${type}`"" $parameters += "--iteration", "`"${iteration}`"" $parameters += "--area", "`"${area}`"" - if ($assignedTo) { + if ($assignedTo -and (CheckUser $assignedTo)) { $parameters += "--assigned-to", "`"${assignedTo}`"" } if ($tag) @@ -354,7 +420,6 @@ function CreateWorkItem($title, $type, $iteration, $area, $fields, $assignedTo, Write-Host "Creating work item" $workItem = Invoke-AzBoardsCmd "work-item create" $parameters $outputCommand - Write-Host $workItem $workItemId = $workItem.id Write-Host "Created work item [$workItemId]." if ($parentId) @@ -390,7 +455,7 @@ function UpdateWorkItem($id, $fields, $title, $state, $assignedTo, $outputComman if ($state) { $parameters += "--state", "`"${state}`"" } - if ($assignedTo) { + if ($assignedTo -and (CheckUser $assignedTo)) { $parameters += "--assigned-to", "`"${assignedTo}`"" } if ($fields) { @@ -409,10 +474,11 @@ function UpdatePackageWorkItemReleaseState($id, $state, $releaseType, $outputCom function FindOrCreateClonePackageWorkItem($lang, $pkg, $verMajorMinor, $allowPrompt = $false, $outputCommand = $false, $relatedId = $null, $tag= $null, $ignoreReleasePlannerTests = $true) { - $workItem = FindPackageWorkItem -lang $lang -packageName $pkg.Package -version $verMajorMinor -includeClosed $true -outputCommand $outputCommand -tag $tag -ignoreReleasePlannerTests $ignoreReleasePlannerTests + $groupId = GetGroupId $pkg + $workItem = FindPackageWorkItem -lang $lang -packageName $pkg.Package -version $verMajorMinor -includeClosed $true -outputCommand $outputCommand -tag $tag -ignoreReleasePlannerTests $ignoreReleasePlannerTests -groupId $groupId if (!$workItem) { - $latestVersionItem = FindLatestPackageWorkItem -lang $lang -packageName $pkg.Package -outputCommand $outputCommand -tag $tag -ignoreReleasePlannerTests $ignoreReleasePlannerTests + $latestVersionItem = FindLatestPackageWorkItem -lang $lang -packageName $pkg.Package -outputCommand $outputCommand -tag $tag -ignoreReleasePlannerTests $ignoreReleasePlannerTests -groupId $groupId $assignedTo = "me" $extraFields = @() if ($latestVersionItem) { @@ -460,6 +526,9 @@ function CreateOrUpdatePackageWorkItem($lang, $pkg, $verMajorMinor, $existingIte Write-Host "Cannot create or update because one of lang, pkg or verMajorMinor aren't set. [$lang|$($pkg.Package)|$verMajorMinor]" return } + + # PackageProp object uses Group, while other places use GroupId, such as in work item fields and package csv files. + $pkgGroupId = GetGroupId $pkg $pkgName = $pkg.Package $pkgDisplayName = $pkg.DisplayName $pkgType = $pkg.Type @@ -471,6 +540,7 @@ function CreateOrUpdatePackageWorkItem($lang, $pkg, $verMajorMinor, $existingIte $fields = @() $fields += "`"Language=${lang}`"" $fields += "`"Package=${pkgName}`"" + $fields += "`"GroupId=${pkgGroupId}`"" $fields += "`"PackageDisplayName=${pkgDisplayName}`"" $fields += "`"PackageType=${pkgType}`"" $fields += "`"PackageTypeNewLibrary=${pkgNewLibrary}`"" @@ -488,6 +558,7 @@ function CreateOrUpdatePackageWorkItem($lang, $pkg, $verMajorMinor, $existingIte if ($lang -ne $existingItem.fields["Custom.Language"]) { $changedField = "Custom.Language" } if ($pkgName -ne $existingItem.fields["Custom.Package"]) { $changedField = "Custom.Package" } + if ($pkgGroupId -ne $existingItem.fields["Custom.GroupId"]) { $changedField = "Custom.GroupId" } if ($verMajorMinor -ne $existingItem.fields["Custom.PackageVersionMajorMinor"]) { $changedField = "Custom.PackageVersionMajorMinor" } if ($pkgDisplayName -ne $existingItem.fields["Custom.PackageDisplayName"]) { $changedField = "Custom.PackageDisplayName" } if ($pkgType -ne [string]$existingItem.fields["Custom.PackageType"]) { $changedField = "Custom.PackageType" } @@ -977,15 +1048,16 @@ function UpdatePackageVersions($pkgWorkItem, $plannedVersions, $shippedVersions) function UpdateValidationStatus($pkgvalidationDetails, $BuildDefinition, $PipelineUrl) { $pkgName = $pkgValidationDetails.Name + $groupId = GetGroupId $pkgValidationDetails $versionString = $pkgValidationDetails.Version $parsedNewVersion = [AzureEngSemanticVersion]::new($versionString) $versionMajorMinor = "" + $parsedNewVersion.Major + "." + $parsedNewVersion.Minor - $workItem = FindPackageWorkItem -lang $LanguageDisplayName -packageName $pkgName -version $versionMajorMinor -includeClosed $true -outputCommand $false + $workItem = FindPackageWorkItem -lang $LanguageDisplayName -packageName $pkgName -groupId $groupId -version $versionMajorMinor -includeClosed $true -outputCommand $false if (!$workItem) { - Write-Host"No work item found for package [$pkgName]." + Write-Host "No work item found for package [$pkgName] with groupId [$groupId]." return $false } @@ -1015,3 +1087,281 @@ 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]" +} + +function Update-PullRequestInReleasePlan($releasePlanWorkItemId, $pullRequestUrl, $status, $languageName) +{ + $devopsFieldLanguage = Get-LanguageDevOpsName -LanguageShort $languageName + if (!$devopsFieldLanguage) + { + Write-Host "Unsupported language to update release plan, language [$languageName]" + return $null + } + + $fields = @() + if ($pullRequestUrl) + { + $fields += "`"SDKPullRequestFor$($devopsFieldLanguage)=$pullRequestUrl`"" + } + $fields += "`"SDKPullRequestStatusFor$($devopsFieldLanguage)=$status`"" + $fields += "`"GenerationStatusFor$($devopsFieldLanguage)=Completed`"" + + Write-Host "Updating release plan [$releasePlanWorkItemId] with pull request details for language [$languageName]." + $workItem = UpdateWorkItem -id $releasePlanWorkItemId -fields $fields + Write-Host "Updated pull request details for [$languageName] in release plan [$releasePlanWorkItemId]" +} + +function Get-ReleasePlan-Link($releasePlanWorkItemId) +{ + $fields = @() + $fields += "System.Id" + $fields += "System.Title" + $fields += "Custom.ReleasePlanLink" + $fields += "Custom.ReleasePlanSubmittedby" + + $fieldList = ($fields | ForEach-Object { "[$_]"}) -join ", " + $query = "SELECT ${fieldList} FROM WorkItems WHERE [System.Id] = $releasePlanWorkItemId" + $workItem = Invoke-Query $fields $query + if (!$workItem) + { + Write-Host "Release plan with ID $releasePlanWorkItemId not found." + return $null + } + return $workItem["fields"] +} + +function Get-ReleasePlansForCPEXAttestation() +{ + $fields = @() + $fields += "Custom.ProductServiceTreeID" + $fields += "Custom.ReleasePlanType" + $fields += "Custom.ProductType" + $fields += "Custom.DataScope" + $fields += "Custom.MgmtScope" + $fields += "Custom.ProductName" + + $fieldList = ($fields | ForEach-Object { "[$_]"}) -join ", " + + $query = "SELECT ${fieldList} FROM WorkItems WHERE [System.WorkItemType] = 'Release Plan'" + $query += " AND [System.State] = 'Finished'" + $query += " AND [Custom.AttestationStatus] IN ('', 'Pending')" + $query += " AND [System.Tags] NOT CONTAINS 'Release Planner App Test'" + $query += " AND [System.Tags] NOT CONTAINS 'Release Planner Test App'" + $query += " AND [System.Tags] NOT CONTAINS 'non-APEX tracking'" + $query += " AND [System.Tags] NOT CONTAINS 'out of scope APEX'" + $query += " AND [System.Tags] NOT CONTAINS 'APEX out of scope'" + $query += " AND [System.Tags] NOT CONTAINS 'validate APEX out of scope'" + $query += " AND [Custom.ProductServiceTreeID] <> ''" + $query += " AND [Custom.ProductLifecycle] <> ''" + $query += " AND [Custom.ProductType] IN ('Feature', 'Offering', 'Sku')" + + $workItems = Invoke-Query $fields $query + return $workItems +} + +function Get-TriagesForCPEXAttestation() +{ + $fields = @() + $fields += "Custom.ProductServiceTreeID" + $fields += "Custom.ProductType" + $fields += "Custom.ProductLifecycle" + $fields += "Custom.DataScope" + $fields += "Custom.MgmtScope" + $fields += "Custom.DataplaneAttestationStatus" + $fields += "Custom.ManagementPlaneAttestationStatus" + $fields += "Custom.ProductName" + + $fieldList = ($fields | ForEach-Object { "[$_]"}) -join ", " + + $query = "SELECT ${fieldList} FROM WorkItems WHERE [System.WorkItemType] = 'Triage'" + $query += " AND ([Custom.DataplaneAttestationStatus] IN ('', 'Pending') OR [Custom.ManagementPlaneAttestationStatus] IN ('', 'Pending'))" + $query += " AND [System.Tags] NOT CONTAINS 'Release Planner App Test'" + $query += " AND [System.Tags] NOT CONTAINS 'Release Planner Test App'" + $query += " AND [System.Tags] NOT CONTAINS 'non-APEX tracking'" + $query += " AND [System.Tags] NOT CONTAINS 'out of scope APEX'" + $query += " AND [System.Tags] NOT CONTAINS 'APEX out of scope'" + $query += " AND [System.Tags] NOT CONTAINS 'validate APEX out of scope'" + $query += " AND [Custom.ProductServiceTreeID] <> ''" + $query += " AND [Custom.ProductLifecycle] <> ''" + $query += " AND [Custom.ProductType] IN ('Feature', 'Offering', 'Sku')" + + $workItems = Invoke-Query $fields $query + return $workItems +} + +function Update-AttestationStatusInWorkItem($workItemId, $fieldName, $status) +{ + $dateFieldName = $fieldName -replace 'Status$', 'Date' + + $fields = @() + $fields += "`"${fieldName}=${status}`"" + $fields += "`"${dateFieldName}=$(Get-Date)`"" + + Write-Host "Updating Work Item [$workItemId] with status [$status] for field [$fieldName]." + $workItem = UpdateWorkItem -id $workItemId -fields $fields + Write-Host "Updated attestation status for [$fieldName] in Work Item [$workItemId]" + return $true +} + +# This function was originally the entirety of what was in Update-DevOps-Release-WorkItem.ps1 +# and has been converted to a function. +function Update-DevOpsReleaseWorkItem { + param( + [Parameter(Mandatory=$true)] + [string]$language, + [Parameter(Mandatory=$true)] + [string]$packageName, + [Parameter(Mandatory=$true)] + [string]$version, + [string]$plannedDate, + [string]$groupId = $null, + [string]$serviceName = $null, + [string]$packageDisplayName = $null, + [string]$packageRepoPath = "NA", + [string]$packageType = "client", + [string]$packageNewLibrary = "true", + [string]$relatedWorkItemId = $null, + [string]$tag = $null, + [bool]$inRelease = $true, + [string]$specProjectPath = "" + ) + + if (!(Get-Command az -ErrorAction SilentlyContinue)) { + Write-Error 'You must have the Azure CLI installed: https://aka.ms/azure-cli' + return $false + } + + CheckAzLoginAndDevOpsExtensionInstall + + CheckDevOpsAccess + + $parsedNewVersion = [AzureEngSemanticVersion]::new($version) + $state = "In Release" + $releaseType = $parsedNewVersion.VersionType + $versionMajorMinor = "" + $parsedNewVersion.Major + "." + $parsedNewVersion.Minor + + $packageInfo = [PSCustomObject][ordered]@{ + Package = $packageName + GroupId = $groupId + DisplayName = $packageDisplayName + ServiceName = $serviceName + RepoPath = $packageRepoPath + Type = $packageType + New = $packageNewLibrary + SpecProjectPath = $specProjectPath + }; + + if (!$plannedDate) { + $plannedDate = Get-Date -Format "MM/dd/yyyy" + } + + $plannedVersions = @( + [PSCustomObject][ordered]@{ + Type = $releaseType + Version = $version + Date = $plannedDate + } + ) + $ignoreReleasePlannerTests = $true + if ($tag -and $tag.Contains("Release Planner App Test")) { + $ignoreReleasePlannerTests = $false + } + + $workItem = FindOrCreateClonePackageWorkItem $language $packageInfo $versionMajorMinor -allowPrompt $true -outputCommand $false -relatedId $relatedWorkItemId -tag $tag -ignoreReleasePlannerTests $ignoreReleasePlannerTests + + if (!$workItem) { + Write-Host "Something failed as we don't have a work-item so exiting." + return $false + } + + Write-Host "Updated or created a release work item for a package release with the following properties:" + Write-Host " Language: $($workItem.fields['Custom.Language'])" + Write-Host " Version: $($workItem.fields['Custom.PackageVersionMajorMinor'])" + Write-Host " Package: $($workItem.fields['Custom.Package'])" + if ($workItem.fields['System.AssignedTo']) { + Write-Host " AssignedTo: $($workItem.fields['System.AssignedTo']["uniqueName"])" + } + else { + Write-Host " AssignedTo: unassigned" + } + Write-Host " PackageDisplayName: $($workItem.fields['Custom.PackageDisplayName'])" + Write-Host " ServiceName: $($workItem.fields['Custom.ServiceName'])" + Write-Host " PackageType: $($workItem.fields['Custom.PackageType'])" + Write-Host "" + if ($inRelease) + { + Write-Host "Marking item [$($workItem.id)]$($workItem.fields['System.Title']) as '$state' for '$releaseType'" + $updatedWI = UpdatePackageWorkItemReleaseState -id $workItem.id -state "In Release" -releaseType $releaseType -outputCommand $false + } + $updatedWI = UpdatePackageVersions $workItem -plannedVersions $plannedVersions + + if ((!$workItem.fields.ContainsKey('Custom.SpecProjectPath') -and $packageInfo.SpecProjectPath) -or + ($workItem.fields.ContainsKey('Custom.SpecProjectPath') -and ($workItem.fields['Custom.SpecProjectPath'] -ne $packageInfo.SpecProjectPath)) + ) { + Write-Host "Updating SpecProjectPath to '$($packageInfo.SpecProjectPath)' for work item [$($workItem.id)]" + UpdateWorkItem ` + -id $workItem.id ` + -fields "`"Custom.SpecProjectPath=$($packageInfo.SpecProjectPath)`"" ` + -outputCommand $false + } + + Write-Host "Release tracking item is at https://dev.azure.com/azure-sdk/Release/_workitems/edit/$($updatedWI.id)/" + return $true +} diff --git a/eng/common/scripts/Helpers/Resource-Helpers.ps1 b/eng/common/scripts/Helpers/Resource-Helpers.ps1 index 546c5dd9aae..f124ff217d4 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 00000000000..17605957fd2 --- /dev/null +++ b/eng/common/scripts/Mark-ReleasePlanCompletion.ps1 @@ -0,0 +1,65 @@ +param( + [Parameter(Mandatory = $true)] + [string]$PackageInfoFilePath, + [Parameter(Mandatory = $true)] + [string]$AzsdkExePath +) + +<# +.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. + +.PARAMETER AzsdkExePath + The path to the azsdk executable used to mark the release completion. +#> + +Set-StrictMode -Version 3 +. (Join-Path $PSScriptRoot common.ps1) + +#Validate azsdk executable path +if (-Not (Test-Path $AzsdkExePath)) +{ + Write-Error "The azsdk executable was not found at path '$AzsdkExePath'. Please ensure the executable exists and the path is correct." + exit 1 +} + +#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 + $PackageName = $pkgInfo.Name + if (!$PackageName) + { + Write-Host "Package name is not available in the package information file. Skipping the release plan status update for the package." + return + } + + Write-Host "Marking release completion for package, name: $PackageName" + $releaseInfo = & $AzsdkExePath release-plan update-release-status --package-name $PackageName --language $LanguageDisplayName --status "Released" + if ($LASTEXITCODE -ne 0) + { + ## Not all releases have a release plan. So we should not fail the script even if a release plan is missing. + Write-Host "Failed to mark release completion for package '$PackageName' using azsdk. Exit code: $LASTEXITCODE" + } + Write-Host "Details: $releaseInfo" +} + +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/scripts/Package-Properties.ps1 b/eng/common/scripts/Package-Properties.ps1 index 0142017c846..7704bd04cea 100644 --- a/eng/common/scripts/Package-Properties.ps1 +++ b/eng/common/scripts/Package-Properties.ps1 @@ -22,6 +22,10 @@ class PackageProps { [HashTable]$ArtifactDetails [HashTable]$CIParameters + # Path from root of azure-rest-api-specs repo to spec project (read from + # tsp-location.yaml if it exists in the package directory) + [string]$SpecProjectPath + PackageProps([string]$name, [string]$version, [string]$directoryPath, [string]$serviceDirectory) { $this.Initialize($name, $version, $directoryPath, $serviceDirectory) } @@ -61,6 +65,13 @@ class PackageProps { $this.ChangeLogPath = $null } + if (Test-Path (Join-Path $directoryPath 'tsp-location.yaml')) { + $tspLocation = LoadFrom-Yaml (Join-Path $directoryPath 'tsp-location.yaml') + if ($tspLocation -and $tspLocation.directory) { + $this.SpecProjectPath = $tspLocation.directory + } + } + $this.CIParameters = @{"CIMatrixConfigs" = @()} $this.InitializeCIArtifacts() } @@ -219,16 +230,30 @@ class PackageProps { # Returns important properties of the package relative to the language repo # Returns a PS Object with properties @ { pkgName, pkgVersion, pkgDirectoryPath, pkgReadMePath, pkgChangeLogPath } # Note: python is required for parsing python package properties. +# GroupId is optional and is used to filter packages for languages that support group identifiers (e.g., Java). +# When GroupId is provided, the function will match both the package name and the group ID. function Get-PkgProperties { Param ( [Parameter(Mandatory = $true)] [string]$PackageName, - [string]$ServiceDirectory + [string]$ServiceDirectory, + [string]$GroupId ) + Write-Host "Get-PkgProperties called with PackageName: [$PackageName], ServiceDirectory: [$ServiceDirectory], GroupId: [$GroupId]" + $allPkgProps = Get-AllPkgProperties -ServiceDirectory $ServiceDirectory - $pkgProps = $allPkgProps.Where({ $_.Name -eq $PackageName -or $_.ArtifactName -eq $PackageName }); + + if ([string]::IsNullOrEmpty($GroupId)) { + $pkgProps = $allPkgProps.Where({ $_.Name -eq $PackageName -or $_.ArtifactName -eq $PackageName }); + } + else { + $pkgProps = $allPkgProps.Where({ + ($_.Name -eq $PackageName -or $_.ArtifactName -eq $PackageName) -and + ($_.PSObject.Properties.Name -contains "Group" -and $_.Group -eq $GroupId) + }); + } if ($pkgProps.Count -ge 1) { if ($pkgProps.Count -gt 1) { @@ -237,7 +262,12 @@ function Get-PkgProperties { return $pkgProps[0] } - LogError "Failed to retrieve Properties for [$PackageName]" + if ([string]::IsNullOrEmpty($GroupId)) { + LogError "Failed to retrieve Properties for [$PackageName]" + } + else { + LogError "Failed to retrieve Properties for [$PackageName] with GroupId [$GroupId]. Ensure the package has a Group property matching the specified GroupId." + } return $null } @@ -557,3 +587,25 @@ function Get-PkgPropsForEntireService ($serviceDirectoryPath) { return $projectProps } + +# Get the full package name based on packageInfo properties +# Returns Group+ArtifactName if Group exists and has a value, otherwise returns Name +# If UseColonSeparator switch is enabled, returns Group:ArtifactName format (colon separator) +function Get-FullPackageName { + param ( + [Parameter(Mandatory=$true)] + [PSCustomObject]$PackageInfo, + [switch]$UseColonSeparator + ) + + if ($PackageInfo.PSObject.Members.Name -contains "Group") { + $groupId = $PackageInfo.Group + if ($groupId) { + if ($UseColonSeparator) { + return "${groupId}:$($PackageInfo.Name)" + } + return "${groupId}+$($PackageInfo.Name)" + } + } + return $PackageInfo.Name +} diff --git a/eng/common/scripts/Prepare-Release.ps1 b/eng/common/scripts/Prepare-Release.ps1 old mode 100644 new mode 100755 index e82c5982ac9..6d57acf6d60 --- a/eng/common/scripts/Prepare-Release.ps1 +++ b/eng/common/scripts/Prepare-Release.ps1 @@ -1,3 +1,5 @@ +#!/usr/bin/env pwsh + #Requires -Version 6.0 <# @@ -28,6 +30,9 @@ If one isn't provided, then it will compute the next ship date or today's date i .PARAMETER ReleaseTrackingOnly Optional: If this switch is passed then the script will only update the release work items and not update the versions in the local repo or validate the changelog. +.PARAMETER GroupId +Optional: The group ID for the package. For Java packages, if not provided, the script will prompt for input with 'com.azure' as the default. + .EXAMPLE PS> ./eng/common/scripts/Prepare-Release.ps1 @@ -47,12 +52,26 @@ param( [string]$PackageName, [string]$ServiceDirectory, [string]$ReleaseDate, # Pass Date in the form MM/dd/yyyy" - [switch]$ReleaseTrackingOnly = $false + [switch]$ReleaseTrackingOnly = $false, + [string]$GroupId ) Set-StrictMode -Version 3 . ${PSScriptRoot}\common.ps1 . ${PSScriptRoot}\Helpers\ApiView-Helpers.ps1 +. ${PSScriptRoot}\Helpers\DevOps-WorkItem-Helpers.ps1 + +# Prompt for GroupId if language is Java and GroupId is not provided +if ($Language -eq 'java' -and [string]::IsNullOrEmpty($GroupId)) { + $userInput = Read-Host "Input the group id, or press Enter to use 'com.azure' as the group id" + if ([string]::IsNullOrWhiteSpace($userInput)) { + $GroupId = "com.azure" + } + else { + $GroupId = $userInput.Trim() + } + Write-Host "Using GroupId: $GroupId" -ForegroundColor Green +} function Get-ReleaseDay($baseDate) { @@ -71,7 +90,7 @@ function Get-ReleaseDay($baseDate) $ErrorPreference = 'Stop' $packageProperties = $null -$packageProperties = Get-PkgProperties -PackageName $PackageName -ServiceDirectory $ServiceDirectory +$packageProperties = Get-PkgProperties -PackageName $PackageName -ServiceDirectory $ServiceDirectory -GroupId $GroupId if (!$packageProperties) { @@ -125,7 +144,7 @@ if (Test-Path "Function:GetExistingPackageVersions") } $currentProjectVersion = $packageProperties.Version -$newVersion = Read-Host -Prompt "Input the new version, or press Enter to use use current project version '$currentProjectVersion'" +$newVersion = Read-Host -Prompt "Input the new version, or press Enter to use current project version '$currentProjectVersion'" if (!$newVersion) { @@ -139,18 +158,19 @@ if ($null -eq $newVersionParsed) exit 1 } -&$EngCommonScriptsDir/Update-DevOps-Release-WorkItem.ps1 ` - -language $LanguageDisplayName ` - -packageName $packageProperties.Name ` - -version $newVersion ` - -plannedDate $releaseDateString ` - -packageRepoPath $packageProperties.serviceDirectory ` - -packageType $packageProperties.SDKType ` - -packageNewLibrary $packageProperties.IsNewSDK +$result = Update-DevOpsReleaseWorkItem -language $LanguageDisplayName ` + -packageName $packageProperties.Name ` + -groupId $packageProperties.Group ` + -version $newVersion ` + -plannedDate $releaseDateString ` + -packageRepoPath $packageProperties.serviceDirectory ` + -packageType $packageProperties.SDKType ` + -packageNewLibrary $packageProperties.IsNewSDK -if ($LASTEXITCODE -ne 0) { - Write-Error "Updating of the Devops Release WorkItem failed." - exit 1 +if (-not $result) +{ + Write-Error "Update of the Devops Release WorkItem failed." + exit 1 } # Check API status @@ -163,7 +183,8 @@ try } $url = az keyvault secret show --name "APIURL" --vault-name "AzureSDKPrepRelease-KV" --query "value" --output "tsv" $apiKey = az keyvault secret show --name "APIKEY" --vault-name "AzureSDKPrepRelease-KV" --query "value" --output "tsv" - Check-ApiReviewStatus -PackageName $packageProperties.Name -packageVersion $newVersion -Language $LanguageDisplayName -url $url -apiKey $apiKey + $fullPackageNameInApiView = Get-FullPackageName -PackageInfo $packageProperties -UseColonSeparator + Check-ApiReviewStatus -PackageName $fullPackageNameInApiView -packageVersion $newVersion -Language $LanguageDisplayName -url $url -apiKey $apiKey } catch { @@ -191,7 +212,7 @@ if (Test-Path "Function:SetPackageVersion") } SetPackageVersion -PackageName $packageProperties.Name -Version $newVersion ` -ServiceDirectory $packageProperties.ServiceDirectory -ReleaseDate $releaseDateString ` - -PackageProperties $packageProperties -ReplaceLatestEntryTitle $replaceLatestEntryTitle + -PackageProperties $packageProperties -ReplaceLatestEntryTitle $replaceLatestEntryTitle -GroupId $packageProperties.Group } else { diff --git a/eng/common/scripts/Save-Package-Properties.ps1 b/eng/common/scripts/Save-Package-Properties.ps1 index 0db23f276b9..deccaa553c0 100644 --- a/eng/common/scripts/Save-Package-Properties.ps1 +++ b/eng/common/scripts/Save-Package-Properties.ps1 @@ -30,7 +30,11 @@ package properties JSON file. If the package properties JSON file already exists, read the Version property from the existing package properties JSON file and set that as the Version property for the new output. This has the effect of "adding" a DevVersion property to the file which could be different from the -Verison property in that file. +Version property in that file. + +.PARAMETER artifactList +Optional array of artifact names to filter the package properties. Only packages +with artifact names matching entries in this list will be processed. #> [CmdletBinding()] @@ -39,7 +43,8 @@ Param ( [Parameter(Mandatory = $True)] [string] $outDirectory, [string] $prDiff, - [switch] $addDevVersion + [switch] $addDevVersion, + [array] $artifactList ) . (Join-Path $PSScriptRoot common.ps1) @@ -132,6 +137,38 @@ if (-not (Test-Path -Path $outDirectory)) New-Item -ItemType Directory -Force -Path $outDirectory | Out-Null } +if ($artifactList) +{ + # Filter out null, empty, or whitespace-only entries + $filteredArtifacts = @($artifactList | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }) + + if ($filteredArtifacts.Count -eq 0) + { + Write-Warning "Artifact list contains no valid entries" + } + else + { + Write-Host "Filtering package properties to match artifact list: $($filteredArtifacts -join ', ')" + $artifactSet = New-Object 'System.Collections.Generic.HashSet[string]' ([System.StringComparer]::OrdinalIgnoreCase) + foreach ($artifact in $filteredArtifacts) { + $artifactSet.Add($artifact) | Out-Null + } + + # Warn about packages missing ArtifactName property + $missingArtifactName = $allPackageProperties | Where-Object { $_.PSObject.Properties.Name -notcontains 'ArtifactName' } + foreach ($pkg in $missingArtifactName) { + Write-Warning "Package '$($pkg.PackageName)' does not have an 'ArtifactName' property and will be excluded from artifact filtering." + } + $allPackageProperties = $allPackageProperties | Where-Object { $_.ArtifactName -and $artifactSet.Contains($_.ArtifactName) } + + if (!$allPackageProperties) + { + Write-Error "No packages found matching the provided artifact list" + exit 1 + } + } +} + foreach ($pkg in $allPackageProperties) { if ($pkg.Name) @@ -144,6 +181,7 @@ foreach ($pkg in $allPackageProperties) if (-not [System.String]::IsNullOrEmpty($pkg.Group)) { Write-Host "GroupId: $($pkg.Group)" } + Write-Host "Spec Project Path: $($pkg.SpecProjectPath)" Write-Host "Release date: $($pkg.ReleaseStatus)" $configFilePrefix = $pkg.Name diff --git a/eng/common/scripts/Set-VcpkgWriteModeCache.ps1 b/eng/common/scripts/Set-VcpkgWriteModeCache.ps1 new file mode 100755 index 00000000000..37bca90019b --- /dev/null +++ b/eng/common/scripts/Set-VcpkgWriteModeCache.ps1 @@ -0,0 +1,23 @@ +#!/bin/env pwsh +param( + [string] $StorageAccountName = 'azuresdkartifacts', + [string] $StorageContainerName = 'public-vcpkg-container', + [int] $TokenTimeoutInHours = 1 +) + +$ctx = New-AzStorageContext ` + -StorageAccountName $StorageAccountName ` + -UseConnectedAccount + +$vcpkgBinarySourceSas = New-AzStorageContainerSASToken ` + -Name $StorageContainerName ` + -Permission "rwcl" ` + -Context $ctx ` + -ExpiryTime (Get-Date).AddHours($TokenTimeoutInHours) + +# Ensure redaction of SAS tokens in logs +Write-Host "##vso[task.setvariable variable=VCPKG_BINARY_SAS_TOKEN;issecret=true;]$vcpkgBinarySourceSas" + +Write-Host "Setting vcpkg binary cache to read and write" +Write-Host "##vso[task.setvariable variable=VCPKG_BINARY_SOURCES_SECRET;issecret=true;]clear;x-azcopy-sas,https://$StorageAccountName.blob.core.windows.net/$StorageContainerName,$vcpkgBinarySourceSas,readwrite" +Write-Host "##vso[task.setvariable variable=X_VCPKG_ASSET_SOURCES_SECRET;issecret=true;]clear;x-azurl,https://$StorageAccountName.blob.core.windows.net/$StorageContainerName,$vcpkgBinarySourceSas,readwrite" diff --git a/eng/common/scripts/SetTestPipelineVersion.ps1 b/eng/common/scripts/SetTestPipelineVersion.ps1 index 2b2ee70ef97..17177a32b57 100644 --- a/eng/common/scripts/SetTestPipelineVersion.ps1 +++ b/eng/common/scripts/SetTestPipelineVersion.ps1 @@ -3,56 +3,161 @@ param ( [Parameter(mandatory = $true)] [string]$BuildID, - [Parameter(mandatory = $true)] - [string]$PackageNames, - [Parameter(mandatory = $true)] + [Parameter(mandatory = $false)] + [string]$PackageNames = "", + [Parameter(mandatory = $false)] + # ServiceDirectory is required when using PackageNames, + # or when Artifacts do not include their own ServiceDirectory property. [string]$ServiceDirectory, [Parameter(mandatory = $false)] - [string]$TagSeparator = "_" + [string]$TagSeparator = "_", + [Parameter(mandatory = $false)] + [object[]]$Artifacts = @() ) . (Join-Path $PSScriptRoot common.ps1) +# Ensure Artifacts is always an array +$Artifacts = @($Artifacts) + Write-Host "PackageNames: $PackageNames" Write-Host "ServiceDirectory: $ServiceDirectory" Write-Host "BuildID: $BuildID" +Write-Host "Artifacts count: $($Artifacts.Count)" -$packageNamesArray = @() +if ($Artifacts -and $Artifacts.Count -gt 0) { + # When using Artifacts, process each artifact with its name and groupId (if applicable) + try { + foreach ($artifact in $Artifacts) { + # Validate required properties + if (-not (Get-Member -InputObject $artifact -Name 'name' -MemberType Properties)) { + LogError "Artifact is missing required 'name' property." + exit 1 + } -if ([String]::IsNullOrWhiteSpace($PackageNames)) { - LogError "PackageNames cannot be empty." - exit 1 -} -else { - $packageNamesArray = $PackageNames.Split(',') -} + $packageName = $artifact.name + if ([String]::IsNullOrWhiteSpace($packageName)) { + LogError "Artifact 'name' property is null or empty." + exit 1 + } -foreach ($packageName in $packageNamesArray) { - Write-Host "Processing $packageName" - $newVersion = [AzureEngSemanticVersion]::new("1.0.0") - $prefix = "$packageName$TagSeparator" - Write-Host "Get Latest Tag : git tag -l $prefix*" - $latestTags = git tag -l "$prefix*" + $artifactServiceDirectory = $null + # Check for ServiceDirectory property + if (Get-Member -InputObject $artifact -Name 'ServiceDirectory' -MemberType Properties) { + if (![String]::IsNullOrWhiteSpace($artifact.ServiceDirectory)) { + $artifactServiceDirectory = $artifact.ServiceDirectory + } + } - $semVars = @() + if ([String]::IsNullOrWhiteSpace($artifactServiceDirectory)) { + $artifactServiceDirectory = $ServiceDirectory + } + + # Validate ServiceDirectory is available + if ([String]::IsNullOrWhiteSpace($artifactServiceDirectory)) { + LogError "ServiceDirectory is required but not provided for artifact '$packageName'. Provide it via script parameter or artifact property." + exit 1 + } - if ($latestTags -and ($latestTags.Length -gt 0)) { - foreach ($tag in $latestTags) { - $semVars += $tag.Substring($prefix.Length) - } + $newVersion = [AzureEngSemanticVersion]::new("1.0.0") + $prefix = "$packageName$TagSeparator" + + if ($Language -eq "java") { + # Check for groupId property + if (-not (Get-Member -InputObject $artifact -Name 'groupId' -MemberType Properties)) { + LogError "Artifact '$packageName' is missing required 'groupId' property for Java language." + exit 1 + } + + $groupId = $artifact.groupId + if ([String]::IsNullOrWhiteSpace($groupId)) { + LogError "GroupId is missing for package $packageName." + exit 1 + } + + Write-Host "Processing $packageName with groupId $groupId" + # Use groupId+artifactName format for tag prefix (e.g., "com.azure.v2+azure-sdk-template_") + $prefix = "$groupId+$packageName$TagSeparator" + } + else { + Write-Host "Processing $packageName" + } + + Write-Host "Get Latest Tag : git tag -l $prefix*" + $latestTags = git tag -l "$prefix*" + + $semVars = @() - $semVarsSorted = [AzureEngSemanticVersion]::SortVersionStrings($semVars) - Write-Host "Last Published Version $($semVarsSorted[0])" - $newVersion = [AzureEngSemanticVersion]::new($semVarsSorted[0]) + if ($latestTags -and ($latestTags.Length -gt 0)) { + foreach ($tag in $latestTags) { + $semVars += $tag.Substring($prefix.Length) + } + + $semVarsSorted = [AzureEngSemanticVersion]::SortVersionStrings($semVars) + Write-Host "Last Published Version $($semVarsSorted[0])" + $newVersion = [AzureEngSemanticVersion]::new($semVarsSorted[0]) + } + + $newVersion.PrereleaseLabel = $newVersion.DefaultPrereleaseLabel + $newVersion.PrereleaseNumber = $BuildID + $newVersion.IsPrerelease = $True + + Write-Host "Version to publish [ $($newVersion.ToString()) ]" + + if ($Language -ne "java") { + SetPackageVersion -PackageName $packageName ` + -Version $newVersion.ToString() ` + -ServiceDirectory $artifactServiceDirectory + } else { + SetPackageVersion -PackageName $packageName ` + -Version $newVersion.ToString() ` + -ServiceDirectory $artifactServiceDirectory ` + -GroupId $groupId + } + } + } + catch { + LogError "Failed to process Artifacts: exception: $($_.Exception.Message)" + exit 1 + } +} elseif (![String]::IsNullOrWhiteSpace($PackageNames)) { + # Fallback to original logic when using PackageNames string + if ([String]::IsNullOrWhiteSpace($ServiceDirectory)) { + LogError "ServiceDirectory is required when using PackageNames." + exit 1 } - $newVersion.PrereleaseLabel = $newVersion.DefaultPrereleaseLabel - $newVersion.PrereleaseNumber = $BuildID - $newVersion.IsPrerelease = $True + $packageNamesArray = $PackageNames.Split(',') + foreach ($packageName in $packageNamesArray) { + Write-Host "Processing $packageName" + $newVersion = [AzureEngSemanticVersion]::new("1.0.0") + $prefix = "$packageName$TagSeparator" + Write-Host "Get Latest Tag : git tag -l $prefix*" + $latestTags = git tag -l "$prefix*" - Write-Host "Version to publish [ $($newVersion.ToString()) ]" + $semVars = @() - SetPackageVersion -PackageName $packageName ` - -Version $newVersion.ToString() ` - -ServiceDirectory $ServiceDirectory + if ($latestTags -and ($latestTags.Length -gt 0)) { + foreach ($tag in $latestTags) { + $semVars += $tag.Substring($prefix.Length) + } + + $semVarsSorted = [AzureEngSemanticVersion]::SortVersionStrings($semVars) + Write-Host "Last Published Version $($semVarsSorted[0])" + $newVersion = [AzureEngSemanticVersion]::new($semVarsSorted[0]) + } + + $newVersion.PrereleaseLabel = $newVersion.DefaultPrereleaseLabel + $newVersion.PrereleaseNumber = $BuildID + $newVersion.IsPrerelease = $True + + Write-Host "Version to publish [ $($newVersion.ToString()) ]" + + SetPackageVersion -PackageName $packageName ` + -Version $newVersion.ToString() ` + -ServiceDirectory $ServiceDirectory + } +} else { + LogError "Either PackageNames or Artifacts must be provided." + exit 1 } diff --git a/eng/common/scripts/Submit-PullRequest.ps1 b/eng/common/scripts/Submit-PullRequest.ps1 index a4c6545b8ca..a2de0a4f0c2 100644 --- a/eng/common/scripts/Submit-PullRequest.ps1 +++ b/eng/common/scripts/Submit-PullRequest.ps1 @@ -130,7 +130,8 @@ else { # ensure that the user that was used to create the PR is not attempted to add as a reviewer # we cast to an array to ensure that length-1 arrays actually stay as array values - $cleanedUsers = @(SplitParameterArray -members $UserReviewers) | ? { $_ -ne $prOwnerUser -and $null -ne $_ } + # we also filter out dependabot user who doesn't have write permission to avoid errors + $cleanedUsers = @(SplitParameterArray -members $UserReviewers) | ? { $_ -ne $prOwnerUser -and $null -ne $_ -and $_ -inotlike "dependabot*" } $cleanedTeamReviewers = @(SplitParameterArray -members $TeamReviewers) | ? { $_ -ne $prOwnerUser -and $null -ne $_ } if ($cleanedUsers -or $cleanedTeamReviewers) { @@ -146,8 +147,13 @@ else { $prState = "open" } + # Clean assignees - remove null entries and bot accounts + $cleanedAssignees = @(SplitParameterArray -members $Assignees) | ? { + $null -ne $_ -and $_ -inotlike "dependabot*" -and $_ -inotlike "copilot*" + } + Update-GitHubIssue -RepoOwner $RepoOwner -RepoName $RepoName -IssueNumber $prNumber ` - -State $prState -Labels $PRLabels -Assignees $Assignees -AuthToken $AuthToken + -State $prState -Labels $PRLabels -Assignees $cleanedAssignees -AuthToken $AuthToken if ($AddBuildSummary) { $summaryPath = New-TemporaryFile diff --git a/eng/common/scripts/Update-ChangeLog.ps1 b/eng/common/scripts/Update-ChangeLog.ps1 index 3541cd48940..3059002f351 100644 --- a/eng/common/scripts/Update-ChangeLog.ps1 +++ b/eng/common/scripts/Update-ChangeLog.ps1 @@ -4,6 +4,7 @@ # Version : Version to add or replace in change log # Unreleased: Default is true. If it is set to false, then today's date will be set in verion title. If it is True then title will show "Unreleased" # ReplaceLatestEntryTitle: Replaces the latest changelog entry title. +# GroupId: Optional. The group ID for the package. Used for filtering packages in languages that support group identifiers (e.g., Java). [CmdletBinding()] param ( @@ -14,7 +15,8 @@ param ( [Boolean]$Unreleased = $true, [Boolean]$ReplaceLatestEntryTitle = $false, [String]$ChangelogPath, - [String]$ReleaseDate + [String]$ReleaseDate, + [String]$GroupId ) Set-StrictMode -Version 3 @@ -59,7 +61,7 @@ if ($null -eq [AzureEngSemanticVersion]::ParseVersionString($Version)) if ([string]::IsNullOrEmpty($ChangelogPath)) { - $pkgProperties = Get-PkgProperties -PackageName $PackageName -ServiceDirectory $ServiceDirectory + $pkgProperties = Get-PkgProperties -PackageName $PackageName -ServiceDirectory $ServiceDirectory -GroupId $GroupId $ChangelogPath = $pkgProperties.ChangeLogPath } diff --git a/eng/common/scripts/Update-DocsMsMetadata.ps1 b/eng/common/scripts/Update-DocsMsMetadata.ps1 index 01a6c9349b4..b2c997a5841 100644 --- a/eng/common/scripts/Update-DocsMsMetadata.ps1 +++ b/eng/common/scripts/Update-DocsMsMetadata.ps1 @@ -64,7 +64,12 @@ function GetAdjustedReadmeContent($ReadmeContent, $PackageInfo, $PackageMetadata } Write-Host "The service of package: $service" # Generate the release tag for use in link substitution - $tag = "$($PackageInfo.Name)_$($PackageInfo.Version)" + if ($Language -eq 'java' -and $PackageInfo.Group) { + $groupId = $PackageInfo.Group + $tag = "$($groupId)+$($PackageInfo.Name)_$($PackageInfo.Version)" + } else { + $tag = "$($PackageInfo.Name)_$($PackageInfo.Version)" + } Write-Host "The tag of package: $tag" $date = Get-Date -Format "MM/dd/yyyy" diff --git a/eng/common/scripts/Update-DocsMsPackages.ps1 b/eng/common/scripts/Update-DocsMsPackages.ps1 index 06ee79102ea..f4a61bba476 100644 --- a/eng/common/scripts/Update-DocsMsPackages.ps1 +++ b/eng/common/scripts/Update-DocsMsPackages.ps1 @@ -26,7 +26,9 @@ param ( [string] $DocRepoLocation, # the location of the cloned doc repo [Parameter(Mandatory = $false)] - [string] $PackageSourceOverride + [string] $PackageSourceOverride, + + [switch] $SkipLegacyOnboarding ) . (Join-Path $PSScriptRoot common.ps1) @@ -57,7 +59,11 @@ function PackageIsValidForDocsOnboarding($package) { -DocRepoLocation $DocRepoLocation } -$MONIKERS = @('latest', 'preview', 'legacy') +$MONIKERS = @('latest', 'preview') +if (!$SkipLegacyOnboarding) { + $MONIKERS += 'legacy' +} + foreach ($moniker in $MONIKERS) { try { Write-Host "Onboarding packages for moniker: $moniker" diff --git a/eng/common/scripts/Update-PullRequest-In-ReleasePlan.ps1 b/eng/common/scripts/Update-PullRequest-In-ReleasePlan.ps1 new file mode 100644 index 00000000000..cb40f577306 --- /dev/null +++ b/eng/common/scripts/Update-PullRequest-In-ReleasePlan.ps1 @@ -0,0 +1,37 @@ +param( + [Parameter(Mandatory = $true)] + $ReleasePlanWorkItemId, + [Parameter(Mandatory = $true)] + $PullRequestUrl, + [Parameter(Mandatory = $true)] + $Status, + [Parameter(Mandatory = $true)] + $LanguageName +) + +<# +.SYNOPSIS +Updates the pull request URL and status in the specified release plan work item for a given programming language. + +.PARAMETER ReleasePlanWorkItemId +The ID of the release plan work item to update. + +.PARAMETER PullRequestUrl +The URL of the pull request to set in the release plan. + +.PARAMETER Status +The status of the pull request. + +.PARAMETER LanguageName +The programming language associated with the pull request. + +#> + +Set-StrictMode -Version 3 +. (Join-Path $PSScriptRoot common.ps1) +. (Join-Path $PSScriptRoot Helpers DevOps-WorkItem-Helpers.ps1) + + +LogDebug "Updating pull request in release plan" +Update-PullRequestInReleasePlan $ReleasePlanWorkItemId $PullRequestUrl $Status $LanguageName +LogDebug "Updated pull request in release plan" \ No newline at end of file diff --git a/eng/common/scripts/Validate-All-Packages.ps1 b/eng/common/scripts/Validate-All-Packages.ps1 index f327c455291..f7edb2e5563 100644 --- a/eng/common/scripts/Validate-All-Packages.ps1 +++ b/eng/common/scripts/Validate-All-Packages.ps1 @@ -1,6 +1,6 @@ [CmdletBinding()] Param ( - [Parameter(Mandatory=$True)] + [Parameter(Mandatory=$False)] [array]$ArtifactList, [Parameter(Mandatory=$True)] [string]$ArtifactPath, @@ -11,40 +11,290 @@ Param ( [string]$ConfigFileDir, [string]$BuildDefinition, [string]$PipelineUrl, - [string]$APIViewUri = "https://apiview.dev/AutoReview/GetReviewStatus", - [bool] $IsReleaseBuild = $false + [string]$APIViewUri = "https://apiview.org/AutoReview/GetReviewStatus", + [bool] $IsReleaseBuild = $false, + [Parameter(Mandatory=$False)] + [array] $PackageInfoFiles ) +# Validate-All-Packages.ps1 folds in the code that was originally in Validate-Package.ps1 +# since Validate-Package.ps1 was only called from Validate-All-Packages.ps1. This replaces +# script calls with function calls and also allows calling CheckAzLoginAndDevOpsExtensionInstall +# and CheckDevOpsAccess once for all of the PackageInfo files being processed instead of once +# per artifact in Validate-Package.ps1 and then again in Update-DevOps-Release-WorkItem.ps1 + Set-StrictMode -Version 3 . (Join-Path $PSScriptRoot common.ps1) +. (Join-Path $PSScriptRoot Helpers\ApiView-Helpers.ps1) +. (Join-Path $PSScriptRoot Helpers\DevOps-WorkItem-Helpers.ps1) + +# Function to validate change log +function ValidateChangeLog($changeLogPath, $versionString, $validationStatus) +{ + try + { + $ChangeLogStatus = [PSCustomObject]@{ + IsValid = $false + Message = "" + } + $changeLogFullPath = Join-Path $RepoRoot $changeLogPath + Write-Host "Path to change log: [$changeLogFullPath]" + if (Test-Path $changeLogFullPath) + { + Confirm-ChangeLogEntry -ChangeLogLocation $changeLogFullPath -VersionString $versionString -ForRelease $true -ChangeLogStatus $ChangeLogStatus -SuppressErrors $true + $validationStatus.Status = if ($ChangeLogStatus.IsValid) { "Success" } else { "Failed" } + $validationStatus.Message = $ChangeLogStatus.Message + } + else { + $validationStatus.Status = "Failed" + $validationStatus.Message = "Change log is not found in [$changeLogPath]. Change log file must be present in package root directory." + } + } + catch + { + Write-Host "Current directory: $(Get-Location)" + $validationStatus.Status = "Failed" + $validationStatus.Message = $_.Exception.Message + } +} + +# Function to verify API review status +function VerifyAPIReview($packageName, $packageVersion, $language) +{ + $APIReviewValidation = [PSCustomObject]@{ + Name = "API Review Approval" + Status = "Pending" + Message = "" + } + $PackageNameValidation = [PSCustomObject]@{ + Name = "Package Name Approval" + Status = "Pending" + Message = "" + } + + try + { + $apiStatus = [PSCustomObject]@{ + IsApproved = $false + Details = "" + } + $packageNameStatus = [PSCustomObject]@{ + IsApproved = $false + Details = "" + } + Write-Host "Checking API review status for package $packageName with version $packageVersion. language [$language]." + Check-ApiReviewStatus $packageName $packageVersion $language $APIViewUri $APIKey $apiStatus $packageNameStatus + + Write-Host "API review approval details: $($apiStatus.Details)" + Write-Host "Package name approval details: $($packageNameStatus.Details)" + #API review approval status + $APIReviewValidation.Message = $apiStatus.Details + $APIReviewValidation.Status = if ($apiStatus.IsApproved) { "Approved" } else { "Pending" } + + # Package name approval status + $PackageNameValidation.Status = if ($packageNameStatus.IsApproved) { "Approved" } else { "Pending" } + $PackageNameValidation.Message = $packageNameStatus.Details + } + catch + { + Write-Warning "Failed to get API review status. Error: $_" + $PackageNameValidation.Status = "Failed" + $PackageNameValidation.Message = $_.Exception.Message + $APIReviewValidation.Status = "Failed" + $APIReviewValidation.Message = $_.Exception.Message + } + + return [PSCustomObject]@{ + ApiviewApproval = $APIReviewValidation + PackageNameApproval = $PackageNameValidation + } +} -function ProcessPackage($PackageName, $ConfigFileDir) + +function IsVersionShipped($packageName, $packageVersion, $groupId = $null) { - Write-Host "Artifact path: $($ArtifactPath)" - Write-Host "Package Name: $($PackageName)" - Write-Host "Config File directory: $($ConfigFileDir)" + # This function will decide if a package version is already shipped or not + Write-Host "Checking if a version is already shipped for package $packageName with version $packageVersion." + $parsedNewVersion = [AzureEngSemanticVersion]::new($packageVersion) + $versionMajorMinor = "" + $parsedNewVersion.Major + "." + $parsedNewVersion.Minor + $workItem = FindPackageWorkItem -lang $LanguageDisplayName -packageName $packageName -groupId $groupId -version $versionMajorMinor -includeClosed $true -outputCommand $false + if ($workItem) + { + # Check if the package version is already shipped + $shippedVersionSet = ParseVersionSetFromMDField $workItem.fields["Custom.ShippedPackages"] + if ($shippedVersionSet.ContainsKey($packageVersion)) { + return $true + } + } + else { + Write-Host "No work item found for package [$packageName], group [$groupId]. Creating new work item for package." + } + return $false +} - &$EngCommonScriptsDir/Validate-Package.ps1 ` - -PackageName $PackageName ` - -ArtifactPath $ArtifactPath ` - -RepoRoot $RepoRoot ` - -APIViewUri $APIViewUri ` - -APIKey $APIKey ` - -BuildDefinition $BuildDefinition ` - -PipelineUrl $PipelineUrl ` - -ConfigFileDir $ConfigFileDir - if ($LASTEXITCODE -ne 0) { - Write-Error "Failed to validate package $PackageName" - exit 1 +function CreateUpdatePackageWorkItem($pkgInfo) +{ + # This function will create or update package work item in Azure DevOps + $versionString = $pkgInfo.Version + $packageName = $pkgInfo.Name + $plannedDate = $pkgInfo.ReleaseStatus + $setReleaseState = $true + if (!$plannedDate -or $plannedDate -eq "Unreleased") + { + $setReleaseState = $false + $plannedDate = "unknown" + } + + # Create or update package work item + $result = Update-DevOpsReleaseWorkItem -language $LanguageDisplayName ` + -packageName $packageName ` + -groupId $pkgInfo.Group ` + -version $versionString ` + -plannedDate $plannedDate ` + -packageRepoPath $pkgInfo.serviceDirectory ` + -packageType $pkgInfo.SDKType ` + -packageNewLibrary $pkgInfo.IsNewSDK ` + -serviceName "unknown" ` + -packageDisplayName "unknown" ` + -inRelease $IsReleaseBuild ` + -specProjectPath $pkgInfo.SpecProjectPath + + if (-not $result) + { + Write-Host "Update of the Devops Release WorkItem failed." } + return [bool]$result } +function ProcessPackage($packageInfo) +{ + # Read package property file and identify all packages to process + # $packageInfo.Name is the package name published to package managers, e.g. @azure/azure-template + # $packageInfo.ArtifactName is the name can be used in path and file names, e.g. azure-template + Write-Host "Processing artifact: $($packageInfo.ArtifactName)" + Write-Host "Is Release Build: $IsReleaseBuild" + $pkgName = $packageInfo.Name + $changeLogPath = $packageInfo.ChangeLogPath + $versionString = $packageInfo.Version + Write-Host "Checking if we need to create or update work item for package $pkgName and groupId $packageInfo.Group with version $versionString." + Write-Host "Package name before checking groupId: $pkgName" + $isShipped = IsVersionShipped $pkgName $versionString $packageInfo.Group + if ($isShipped) { + Write-Host "Package work item already exists for version [$versionString] that is marked as shipped. Skipping the update of package work item." + return + } + + # Change log validation + $changeLogStatus = [PSCustomObject]@{ + Name = "Change Log Validation" + Status = "Success" + Message = "" + } + ValidateChangeLog $changeLogPath $versionString $changeLogStatus + + # API review and package name validation + $fullPackageName = $pkgName + + # If there's a groupId that means this is Java and pkgName = GroupId+ArtifactName + # but the VerifyAPIReview requires GroupId:ArtifactName + # Technically we can use groupId+artifactName format in api view, + # however it will need to migrate the existing data and Java parser also needs the change. + $fullPackageName = Get-FullPackageName -PackageInfo $packageInfo -UseColonSeparator + Write-Host "Checking API review status for package $fullPackageName" + $apireviewDetails = VerifyAPIReview $fullPackageName $packageInfo.Version $Language + + # The following object will be used to update package work item, the name should be package name only without groupId + $pkgValidationDetails= [PSCustomObject]@{ + Name = $pkgName + GroupId = $packageInfo.Group + Version = $packageInfo.Version + ChangeLogValidation = $changeLogStatus + APIReviewValidation = $apireviewDetails.ApiviewApproval + PackageNameValidation = $apireviewDetails.PackageNameApproval + } + + $output = ConvertTo-Json $pkgValidationDetails + Write-Host "Output: $($output)" + + # Create json token file in artifact path + # Does the following validation file name also need to use full package name with groupId? + $tokenFile = Join-Path $ArtifactPath "$($packageInfo.ArtifactName)-Validation.json" + $output | Out-File -FilePath $tokenFile -Encoding utf8 + + # Create DevOps work item + $updatedWi = CreateUpdatePackageWorkItem $packageInfo + + # Update validation status in package work item + if ($updatedWi) { + Write-Host "Updating validation status in package work item." + $updatedWi = UpdateValidationStatus $pkgValidationDetails $BuildDefinition $PipelineUrl + } + + # Fail the build if any validation is not successful for a release build + Write-Host "Change log status:" $changeLogStatus.Status + Write-Host "API Review status:" $apireviewDetails.ApiviewApproval.Status + Write-Host "Package Name status:" $apireviewDetails.PackageNameApproval.Status + + if ($IsReleaseBuild) + { + if (!$updatedWi -or $changeLogStatus.Status -ne "Success" -or $apireviewDetails.ApiviewApproval.Status -ne "Approved" -or $apireviewDetails.PackageNameApproval.Status -ne "Approved") { + Write-Error "At least one of the Validations above failed for package $pkgName with version $versionString." + exit 1 + } + } +} + +CheckAzLoginAndDevOpsExtensionInstall + +CheckDevOpsAccess + +Write-Host "Artifact path: $($ArtifactPath)" +Write-Host "Artifact List: $($ArtifactList -join ', ')" +Write-Host "Package Info Files: $($PackageInfoFiles -join ', ')" +Write-Host "IsReleaseBuild: $IsReleaseBuild" + # Check if package config file is present. This file has package version, SDK type etc info. if (-not $ConfigFileDir) { $ConfigFileDir = Join-Path -Path $ArtifactPath "PackageInfo" } -foreach ($artifact in $ArtifactList) + +Write-Host "Config file path: $($ConfigFileDir)" +# Initialize working variable +$ProcessedPackageInfoFiles = @() + +if ($ArtifactList -and $ArtifactList.Count -gt 0) +{ + # Multiple artifacts mode (existing usage) + Write-Host "Using ArtifactList parameter with $($ArtifactList.Count) artifacts" + foreach ($artifact in $ArtifactList) + { + $pkgPropPath = Join-Path -Path $ConfigFileDir "$($artifact.name).json" + if (Test-Path $pkgPropPath) { + $ProcessedPackageInfoFiles += $pkgPropPath + } + else { + Write-Warning "Package property file path $pkgPropPath is invalid." + } + } +} +elseif ($PackageInfoFiles -and $PackageInfoFiles.Count -gt 0) { + # Direct PackageInfoFiles (new method) + Write-Host "Using PackageInfoFiles parameter with $($PackageInfoFiles.Count) files" + # Filter out empty strings or whitespace-only entries + $ProcessedPackageInfoFiles = @($PackageInfoFiles | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }) +} + +# Validate that we have package info files to process +if (-not $ProcessedPackageInfoFiles -or $ProcessedPackageInfoFiles.Count -eq 0) { + Write-Error "No package info files found after processing parameters. Or PackageInfoFiles parameter contains only empty or whitespace entries, please check the artifact settings." + exit 1 +} + +Write-Host "Processed Package Info Files: $($ProcessedPackageInfoFiles -join ', ')" + +# Process all packages using the processed PackageInfoFiles array +foreach ($packageInfoFile in $ProcessedPackageInfoFiles) { - Write-Host "Processing $($artifact.name)" - ProcessPackage -PackageName $artifact.name -ConfigFileDir $ConfigFileDir -} \ No newline at end of file + $packageInfo = Get-Content $packageInfoFile | ConvertFrom-Json + ProcessPackage -packageInfo $packageInfo +} diff --git a/eng/common/scripts/Verify-ChangeLogs.ps1 b/eng/common/scripts/Verify-ChangeLogs.ps1 index 9598472720c..3b09a8ceebf 100644 --- a/eng/common/scripts/Verify-ChangeLogs.ps1 +++ b/eng/common/scripts/Verify-ChangeLogs.ps1 @@ -1,7 +1,8 @@ # Wrapper Script for ChangeLog Verification in a PR [CmdletBinding()] param ( - [String]$PackagePropertiesFolder + [String]$PackagePropertiesFolder, + [boolean]$ForRelease = $False ) Set-StrictMode -Version 3 @@ -10,8 +11,10 @@ Set-StrictMode -Version 3 function ShouldVerifyChangeLog ($PkgArtifactDetails) { if ($PkgArtifactDetails) { - if ($PkgArtifactDetails.PSObject.Properties["skipVerifyChangeLog"] -eq $true) { - return $false + if ($PkgArtifactDetails.PSObject.Properties.Name -contains "skipVerifyChangeLog") { + if ($PkgArtifactDetails.skipVerifyChangeLog) { + return $false + } } return $true @@ -33,7 +36,9 @@ foreach($propertiesFile in $packageProperties) { continue } - $validChangeLog = Confirm-ChangeLogEntry -ChangeLogLocation $PackageProp.ChangeLogPath -VersionString $PackageProp.Version -ForRelease $false + Write-Host "Verifying changelog for $($PackageProp.Name)" + + $validChangeLog = Confirm-ChangeLogEntry -ChangeLogLocation $PackageProp.ChangeLogPath -VersionString $PackageProp.Version -ForRelease $ForRelease if (-not $validChangeLog) { $allPassing = $false diff --git a/eng/common/scripts/Verify-Links.ps1 b/eng/common/scripts/Verify-Links.ps1 index d4406c609d0..0eb1798da6c 100644 --- a/eng/common/scripts/Verify-Links.ps1 +++ b/eng/common/scripts/Verify-Links.ps1 @@ -120,6 +120,9 @@ function ProcessLink([System.Uri]$linkUri) { # See comment in function below for details. return ProcessCratesIoLink $linkUri $matches['path'] } + elseif ($linkUri -match '^https?://(www\.)?npmjs\.com/package/.+') { + return ProcessNpmLink $linkUri + } else { return ProcessStandardLink $linkUri } @@ -157,6 +160,31 @@ function ProcessCratesIoLink([System.Uri]$linkUri, $path) { return $true } +function ProcessNpmLink([System.Uri]$linkUri) { + # npmjs.com started using Cloudflare which returns 403 and we need to instead check the registry api for existence checks + # https://github.com/orgs/community/discussions/174098#discussioncomment-14461226 + + # Handle versioned URLs: https://www.npmjs.com/package/@azure/ai-agents/v/1.1.0 -> https://registry.npmjs.org/@azure/ai-agents/1.1.0 + # Handle non-versioned URLs: https://www.npmjs.com/package/@azure/ai-agents -> https://registry.npmjs.org/@azure/ai-agents + # The regex captures the package name (which may contain a slash for scoped packages) and optionally the version. + # Query parameters and URL fragments are excluded from the transformation. + $urlString = $linkUri.ToString() + if ($urlString -match '^https?://(?:www\.)?npmjs\.com/package/([^?#]+)/v/([^?#]+)') { + # Versioned URL: remove the /v/ segment but keep the version + $apiUrl = "https://registry.npmjs.org/$($matches[1])/$($matches[2])" + } + elseif ($urlString -match '^https?://(?:www\.)?npmjs\.com/package/([^?#]+)') { + # Non-versioned URL: just replace the domain + $apiUrl = "https://registry.npmjs.org/$($matches[1])" + } + else { + # Fallback: use the original URL if it doesn't match expected patterns + $apiUrl = $urlString + } + + return ProcessStandardLink ([System.Uri]$apiUrl) +} + function ProcessStandardLink([System.Uri]$linkUri) { $headRequestSucceeded = $true try { @@ -530,6 +558,8 @@ foreach ($url in $urls) { LogGroupStart "Link checking details" +$originalcheckLinkGuidance = $checkLinkGuidance + while ($pageUrisToCheck.Count -ne 0) { $pageUri = $pageUrisToCheck.Dequeue(); @@ -538,6 +568,11 @@ while ($pageUrisToCheck.Count -ne 0) if ($checkedPages.ContainsKey($pageUri)) { continue } $checkedPages[$pageUri] = $true; + # copilot instructions require the use of relative links which is against our general guidance + # but we mainly care about those guidelines for docs publishing and not copilot instructions + # so we can disable the guidelines while validating copilot instruction files. + if ($pageUri -match "instructions.md$") { $checkLinkGuidance = $false } + [string[]] $linkUris = GetLinks $pageUri Write-Host "Checking $($linkUris.Count) links found on page $pageUri"; $badLinksPerPage = @(); @@ -561,6 +596,8 @@ while ($pageUrisToCheck.Count -ne 0) } catch { Write-Host "Exception encountered while processing pageUri $pageUri : $($_.Exception)" throw + } finally { + $checkLinkGuidance = $originalcheckLinkGuidance } } diff --git a/eng/common/scripts/Verify-Readme.ps1 b/eng/common/scripts/Verify-Readme.ps1 index 4e2cd652249..05779d9aa3d 100644 --- a/eng/common/scripts/Verify-Readme.ps1 +++ b/eng/common/scripts/Verify-Readme.ps1 @@ -9,7 +9,7 @@ param ( [string]$SettingsPath ) . (Join-Path $PSScriptRoot common.ps1) -$DefaultDocWardenVersion = "0.7.2" +$DefaultDocWardenVersion = "0.7.3" $script:FoundError = $false function Test-Readme-Files { diff --git a/eng/common/scripts/check-spelling-in-changed-files.ps1 b/eng/common/scripts/check-spelling-in-changed-files.ps1 index 8ac20c18253..aee066a5c0c 100644 --- a/eng/common/scripts/check-spelling-in-changed-files.ps1 +++ b/eng/common/scripts/check-spelling-in-changed-files.ps1 @@ -114,7 +114,7 @@ foreach ($file in $changedFiles) { $spellingErrors = &"$PSScriptRoot/../spelling/Invoke-Cspell.ps1" ` -CspellConfigPath $CspellConfigPath ` -SpellCheckRoot $SpellCheckRoot ` - -ScanGlobs $changedFilePaths + -FileList $changedFilePaths if ($spellingErrors) { $errorLoggingFunction = Get-Item 'Function:LogWarning' diff --git a/eng/common/scripts/common.ps1 b/eng/common/scripts/common.ps1 index 6bef283e449..f9411ccc13f 100644 --- a/eng/common/scripts/common.ps1 +++ b/eng/common/scripts/common.ps1 @@ -1,3 +1,4 @@ +#Requires -Version 7.0 # cSpell:ignore Apireview # cSpell:ignore Onboarded $RepoRoot = Resolve-Path (Join-Path $PSScriptRoot .. .. ..) diff --git a/eng/common/scripts/job-matrix/Create-JobMatrix.ps1 b/eng/common/scripts/job-matrix/Create-JobMatrix.ps1 index 3fc4a3255dd..2ca67e92a7d 100644 --- a/eng/common/scripts/job-matrix/Create-JobMatrix.ps1 +++ b/eng/common/scripts/job-matrix/Create-JobMatrix.ps1 @@ -21,15 +21,27 @@ param ( ) . $PSScriptRoot/job-matrix-functions.ps1 +. $PSScriptRoot/../logging.ps1 if (!(Test-Path $ConfigPath)) { Write-Error "ConfigPath '$ConfigPath' does not exist." exit 1 } -$config = GetMatrixConfigFromFile (Get-Content $ConfigPath -Raw) +$rawConfig = Get-Content $ConfigPath -Raw +$config = GetMatrixConfigFromFile $rawConfig # Strip empty string filters in order to be able to use azure pipelines yaml join() $Filters = $Filters | Where-Object { $_ } +LogGroupStart "Matrix generation configuration" +Write-Host "Configuration File: $ConfigPath" +Write-Host $rawConfig +Write-Host "SelectionType: $Selection" +Write-Host "DisplayNameFilter: $DisplayNameFilter" +Write-Host "Filters: $Filters" +Write-Host "Replace: $Replace" +Write-Host "NonSparseParameters: $NonSparseParameters" +LogGroupEnd + [array]$matrix = GenerateMatrix ` -config $config ` -selectFromMatrixType $Selection ` @@ -41,6 +53,9 @@ $Filters = $Filters | Where-Object { $_ } $serialized = SerializePipelineMatrix $matrix +Write-Host "Generated matrix:" + +# Write-Output required to support other scripts that call this script directly Write-Output $serialized.pretty if ($CI) { diff --git a/eng/common/scripts/logging.ps1 b/eng/common/scripts/logging.ps1 index 94dc900dba9..ae0c8543865 100644 --- a/eng/common/scripts/logging.ps1 +++ b/eng/common/scripts/logging.ps1 @@ -38,7 +38,7 @@ function LogWarning { Write-Host ("::warning::$args" -replace "`n", "%0D%0A") } else { - Write-Warning "$args" + Write-Host "$args" -ForegroundColor Yellow } } @@ -59,7 +59,7 @@ function LogErrorForFile($file, $errorString) Write-Host ("::error file=$file,line=1,col=1::$errorString" -replace "`n", "%0D%0A") } else { - Write-Error "[Error in file $file]$errorString" + Write-Host "[Error in file $file]$errorString" -ForegroundColor Red } } @@ -71,7 +71,7 @@ function LogError { Write-Host ("::error::$args" -replace "`n", "%0D%0A") } else { - Write-Error "$args" + Write-Host "$args" -ForegroundColor Red } } @@ -94,6 +94,9 @@ function LogGroupStart() { elseif (Test-SupportsGitHubLogging) { Write-Host "::group::$args" } + else { + Write-Host "> $args" + } } function LogGroupEnd() { @@ -111,3 +114,15 @@ function LogJobFailure() { } # No equivalent for GitHub Actions. Failure is only determined by nonzero exit code. } + +function ProcessMsBuildLogLine($line) { + if (Test-SupportsDevOpsLogging) { + if ($line -like "*: error*") { + return ("##vso[task.LogIssue type=error;]$line" -replace "`n", "%0D%0A") + } + elseif ($line -like "*: warning*") { + return ("##vso[task.LogIssue type=warning;]$line" -replace "`n", "%0D%0A") + } + } + return $line +} diff --git a/eng/common/scripts/login-to-github.ps1 b/eng/common/scripts/login-to-github.ps1 new file mode 100644 index 00000000000..e911f31eb12 --- /dev/null +++ b/eng/common/scripts/login-to-github.ps1 @@ -0,0 +1,185 @@ +<# +.SYNOPSIS + Mints a GitHub App installation access token using Azure Key Vault 'sign' (non-exportable key), + and logs in the GitHub CLI by setting GH_TOKEN. + +.PARAMETER KeyVaultName + Name of the Azure Key Vault containing the non-exportable RSA key. + +.PARAMETER KeyName + Name of the RSA key in Key Vault (imported as a *key*, not a secret). + +.PARAMETER GitHubAppId + Numeric App ID (not client ID) of your GitHub App. + +.PARAMETER InstallationTokenOwners + List of GitHub organizations or users for which to obtain installation tokens. + +.PARAMETER VariableNamePrefix + Name of the ADO variable to set when -SetPipelineVariable is used (default: GH_TOKEN). + +.OUTPUTS + Writes minimal info to stdout. Token is placed in $env:GH_TOKEN if there is only one owner otherwise $env:GH_TOKEN_ for each owner. +#> + +[CmdletBinding()] +param( + [string] $KeyVaultName = "azuresdkengkeyvault", + [string] $KeyName = "azure-sdk-automation", + [string] $GitHubAppId = '1086291', # Azure SDK Automation App ID + [string[]] $InstallationTokenOwners = @("Azure"), + [string] $VariableNamePrefix = "GH_TOKEN" +) + +$ErrorActionPreference = 'Stop' +Set-StrictMode -Version Latest + +$GitHubApiBaseUrl = "https://api.github.com" +$GitHubApiVersion = "2022-11-28" + +function Get-Headers { + param( + [Parameter(Mandatory)][string] $Jwt, + [Parameter(Mandatory)][string] $ApiVersion + ) + return @{ + 'Authorization' = "Bearer $Jwt" + 'Accept' = 'application/vnd.github+json' + 'X-GitHub-Api-Version' = $ApiVersion + 'User-Agent' = 'ado-pwsh-ghapp' + } +} + +function New-GitHubAppJwt { + param( + [Parameter(Mandatory)] [string] $VaultName, + [Parameter(Mandatory)] [string] $KeyName, + [Parameter(Mandatory)] [string] $AppId + ) + + function Base64UrlEncode { + param( + [string]$Data, + [switch]$IsBase64String + ) + if ($IsBase64String) { + $base64 = $Data + } else { + $bytes = [System.Text.Encoding]::UTF8.GetBytes($Data) + $base64 = [Convert]::ToBase64String($bytes) + } + return $base64.TrimEnd('=') -replace '\+', '-' -replace '/', '_' + } + + # === STEP 1: Create JWT Header and Payload === + $Header = @{ + alg = "RS256" + typ = "JWT" + } + $Now = [int][double]::Parse((Get-Date -UFormat %s)) + $Payload = @{ + iat = $Now - 10 # 10 seconds clock skew + exp = $Now + 600 # 10 minutes + iss = $AppId + } + + $EncodedHeader = Base64UrlEncode (ConvertTo-Json $Header -Compress) + $EncodedPayload = Base64UrlEncode (ConvertTo-Json $Payload -Compress) + $UnsignedToken = "$EncodedHeader.$EncodedPayload" + + # === STEP 2: Sign the token using Azure CLI === + $UnsignedTokenBytes = [System.Security.Cryptography.SHA256]::Create().ComputeHash([Text.Encoding]::ASCII.GetBytes($UnsignedToken)) + $Base64Value = [Convert]::ToBase64String($UnsignedTokenBytes) + + $SignResultJson = az keyvault key sign ` + --vault-name $VaultName ` + --name $KeyName ` + --algorithm RS256 ` + --digest $Base64Value | ConvertFrom-Json + + if ($LASTEXITCODE -ne 0) { + throw "Failed to sign JWT with Azure Key Vault. Error: $SignResult" + } + + if (!$SignResultJson.signature) { + throw "Azure Key Vault response does not contain a signature. Response: $($SignResultJson | ConvertTo-Json -Compress)" + } + + $Signature = Base64UrlEncode -Data $SignResultJson.signature -IsBase64String + return "$UnsignedToken.$Signature" +} + +function Get-GitHubInstallationId { + param( + [Parameter(Mandatory)][string] $Jwt, + [Parameter(Mandatory)][string] $ApiBase, + [Parameter(Mandatory)][string] $ApiVersion, + [Parameter(Mandatory)][string] $InstallationTokenOwner + ) + + $headers = Get-Headers -Jwt $Jwt -ApiVersion $ApiVersion + + $uri = "$ApiBase/app/installations" + $resp = Invoke-RestMethod -Method Get -Headers $headers -Uri $uri -TimeoutSec 30 -MaximumRetryCount 3 + + $resp | Foreach-Object { Write-Host " $($_.id): $($_.account.login) [$($_.target_type)]" } + + $resp = $resp | Where-Object { $_.account.login -ieq $InstallationTokenOwner } + if (!$resp.id) { throw "No installations found for this App." } + return $resp.id +} + +function New-GitHubInstallationToken { + param( + [Parameter(Mandatory)] [string] $Jwt, + [Parameter(Mandatory)] [string] $InstallationId, + [Parameter(Mandatory)] [string] $ApiBase, + [Parameter(Mandatory)] [string] $ApiVersion + ) + $headers = Get-Headers -Jwt $Jwt -ApiVersion $ApiVersion + $uri = "$ApiBase/app/installations/$InstallationId/access_tokens" + $resp = Invoke-RestMethod -Method Post -Headers $headers -Uri $uri -TimeoutSec 30 -MaximumRetryCount 3 + if (!$resp.token) { throw "Failed to obtain installation access token for installation $InstallationId." } + return $resp.token +} + +Write-Host "Generating GitHub App JWT by signing via Azure Key Vault (no key export)..." +$jwt = New-GitHubAppJwt -VaultName $KeyVaultName -KeyName $KeyName -AppId $GitHubAppId + +foreach ($InstallationTokenOwner in $InstallationTokenOwners) +{ + Write-Host "Fetching installation ID for $InstallationTokenOwner ..." + $installationId = Get-GitHubInstallationId -Jwt $jwt -ApiBase $GitHubApiBaseUrl -ApiVersion $GitHubApiVersion -InstallationTokenOwner $InstallationTokenOwner + + Write-Host "Installation ID resolved: $installationId" + + Write-Host "Exchanging JWT for installation access token..." + $installationToken = New-GitHubInstallationToken -Jwt $jwt -InstallationId $installationId -ApiBase $GitHubApiBaseUrl -ApiVersion $GitHubApiVersion + + $variableName = $VariableNamePrefix + if ($InstallationTokenOwners.Count -gt 1) + { + $variableName = $VariableNamePrefix + "_" + $InstallationTokenOwner + } + + Set-Item -Path Env:$variableName -Value $installationToken + + # Export for gh CLI & git + Write-Host "$variableName has been set in the current process." + + # Optionally set an Azure DevOps secret variable (so later tasks can reuse it) + if ($null -ne $env:SYSTEM_TEAMPROJECTID) { + Write-Host "##vso[task.setvariable variable=$variableName;issecret=true]$installationToken" + Write-Host "Azure DevOps variable '$variableName' has been set (secret)." + } + + try { + Write-Host "`n--- gh auth status ---" + $gh_token_value_before = $env:GH_TOKEN + $env:GH_TOKEN = $installationToken + & gh auth status + } + finally{ + $env:GH_TOKEN = $gh_token_value_before + } +} diff --git a/eng/common/scripts/stress-testing/deploy-stress-tests.ps1 b/eng/common/scripts/stress-testing/deploy-stress-tests.ps1 index cf763b5bb1c..3f8c0f63bb4 100755 --- a/eng/common/scripts/stress-testing/deploy-stress-tests.ps1 +++ b/eng/common/scripts/stress-testing/deploy-stress-tests.ps1 @@ -42,5 +42,30 @@ param( . $PSScriptRoot/stress-test-deployment-lib.ps1 +# If there are local changes to values.yaml it's almost certain that the user +# is an admin and has provisioned a new stress cluster but not published a +# new addons chart with the new infra config values. In these cases we should +# just fail, as deploying without the local addons override causes misleading +# errors in the cluster with pods not able to mount storage accounts using the +# old values.yaml reference from the published stress-test-addons helm chart. +if (!$LocalAddonsPath) { + try { + $repoRoot = git -C $PSScriptRoot rev-parse --show-toplevel 2>$null + } catch { + $repoRoot = $null + } + + if ($repoRoot -and (Split-Path $repoRoot -Leaf) -eq "azure-sdk-tools") { + $valuesFile = Join-Path $repoRoot "tools/stress-cluster/cluster/kubernetes/stress-test-addons/values.yaml" + if (Test-Path $valuesFile) { + $valuesStatus = git -C $repoRoot status --porcelain -- $valuesFile + if ($valuesStatus) { + $localAddonsDir = Split-Path $valuesFile -Parent + throw "Detected changes to '$valuesFile' without -LocalAddonsPath. Re-run with '-LocalAddonsPath $localAddonsDir' to apply local addon values." + } + } + } +} + CheckDependencies DeployStressTests @PSBoundParameters diff --git a/eng/common/scripts/stress-testing/find-all-stress-packages.ps1 b/eng/common/scripts/stress-testing/find-all-stress-packages.ps1 index 64df21a3a82..f595f8e7fd4 100644 --- a/eng/common/scripts/stress-testing/find-all-stress-packages.ps1 +++ b/eng/common/scripts/stress-testing/find-all-stress-packages.ps1 @@ -31,8 +31,10 @@ function FindStressPackages( $filters['stressTest'] = 'true' $packages = @() $chartFiles = Get-ChildItem -Recurse -Filter 'Chart.yaml' $directory - Write-Host "Found chart files:" - Write-Host ($chartFiles -join "`n") + if ($chartFiles) { + Write-Host "Found chart files:" + Write-Host ($chartFiles -join "`n") + } if (!$MatrixFileName) { $MatrixFileName = 'scenarios-matrix.yaml' diff --git a/eng/common/scripts/stress-testing/stress-test-deployment-lib.ps1 b/eng/common/scripts/stress-testing/stress-test-deployment-lib.ps1 index a2ec15999ad..64d850bf896 100644 --- a/eng/common/scripts/stress-testing/stress-test-deployment-lib.ps1 +++ b/eng/common/scripts/stress-testing/stress-test-deployment-lib.ps1 @@ -122,21 +122,21 @@ function DeployStressTests( if ($clusterGroup -or $subscription) { Write-Warning "Overriding cluster group and subscription with defaults for 'pg' environment." } - $clusterGroup = 'rg-stress-cluster-pg' + $clusterGroup = 'SSS3PT_rg-stress-cluster-pg' $subscription = 'Azure SDK Test Resources - TME' $tenant = '70a036f6-8e4d-4615-bad6-149c02e7720d' } elseif ($environment -eq 'prod') { if ($clusterGroup -or $subscription) { Write-Warning "Overriding cluster group and subscription with defaults for 'prod' environment." } - $clusterGroup = 'rg-stress-cluster-prod' + $clusterGroup = 'SSS3PT_rg-stress-cluster-prod' $subscription = 'Azure SDK Test Resources - TME' $tenant = '70a036f6-8e4d-4615-bad6-149c02e7720d' } elseif ($environment -eq 'storage') { if ($clusterGroup -or $subscription) { Write-Warning "Overriding cluster group and subscription with defaults for 'storage' environment." } - $clusterGroup = 'rg-stress-cluster-storage' + $clusterGroup = 'SSS3PT_rg-stress-cluster-storage' $subscription = 'Azure SDK Test Resources - TME' $tenant = '72f988bf-86f1-41af-91ab-2d7cd011db47' } elseif (!$clusterGroup -or !$subscription -or $tenant) { @@ -172,6 +172,10 @@ function DeployStressTests( -MatrixFilters $MatrixFilters ` -MatrixReplace $MatrixReplace ` -MatrixNonSparseParameters $MatrixNonSparseParameters) + if (!$pkgs -or !$pkgs.Length) { + Write-Warning "No stress test packages found in $searchDirectory" + exit 0 + } Write-Host "" "Found $($pkgs.Length) stress test packages:" Write-Host $pkgs.Directory "" foreach ($pkg in $pkgs) { diff --git a/eng/common/spelling/Invoke-Cspell.ps1 b/eng/common/spelling/Invoke-Cspell.ps1 old mode 100644 new mode 100755 diff --git a/eng/common/spelling/README.md b/eng/common/spelling/README.md new file mode 100644 index 00000000000..06e711c90a2 --- /dev/null +++ b/eng/common/spelling/README.md @@ -0,0 +1,84 @@ +# Spelling Check Scripts + +This directory contains a script to run cspell (Code Spell Checker) on the repository using the dependencies defined in the adjacent `package*.json` files. + +## Adding Legitimate Words + +If the spell checker flags legitimate words as misspelled, you can add them to the dictionary configuration file located at `.vscode/cspell.json`. + +### Where to Add Words + +There are two main places to add legitimate words. Maintain alphabetical order when adding words to keep the dictionary organized: + +1. **Root-level words array**: Add words to the `"words"` array at the root level of the configuration file. This is the preferred location for project-specific terms, technical vocabulary, and commonly used words. + +2. **Baseline dictionary**: Add words to the `"baseline"` dictionary under `"dictionaryDefinitions"`. This is typically used for words that were already present in the codebase when the spell checker was first introduced. + + +### Example + +To add new words, edit `.vscode/cspell.json` and add them to the `"words"` array: + +```json +{ + "words": [ + "myprojectname", + "customterm", + "technicalword" + ] +} +``` + +### Guidelines + +- Use lowercase for words +- Consider whether the word is truly legitimate or if it might be a typo + +## Available Scripts + +### PowerShell Version (Windows) +- **File**: `Invoke-Cspell.ps1` +- **Usage**: For Windows PowerShell environments + +## Usage Examples + +```powershell +# Check all files (default) +./eng/common/spelling/Invoke-Cspell.ps1 + +# Check specific files +./eng/common/spelling/Invoke-Cspell.ps1 -ScanGlobs 'sdk/*/*/PublicAPI/**/*.md' + +# Check multiple globs (powershell invocation only) +./eng/common/spelling/Invoke-Cspell.ps1 -ScanGlobs @('sdk/storage/**', 'sdk/keyvault/**') + +# Check single file +./eng/common/spelling/Invoke-Cspell.ps1 -ScanGlobs './README.md' +``` + +## Parameters + +- **Job Type**: The cspell command to run (default: `lint`) +- **Scan Globs**: File patterns to check for spelling +- **Config Path**: Location of the cspell.json configuration file +- **Spell Check Root**: Root directory for relative paths +- **Package Cache**: Working directory for npm dependencies +- **Leave Cache**: Option to preserve the npm package cache + +## Requirements + +- Node.js and npm must be installed +- The `.vscode/cspell.json` configuration file must exist +- `jq` command-line JSON processor (for bash version) + +## How It Works + +1. Creates a temporary working directory for npm packages +2. Copies `package.json` and `package-lock.json` to the working directory +3. Installs npm dependencies using `npm ci` +4. Modifies the cspell configuration to include specified file globs +5. Runs cspell with the modified configuration +6. Restores the original configuration +7. Cleans up temporary files + +The scripts ensure that a LICENSE file (or temporary file) is always included in the scan to meet cspell's requirements for the "files" array. diff --git a/eng/common/spelling/package-lock.json b/eng/common/spelling/package-lock.json index d8c4a8faaa9..a0199b95496 100644 --- a/eng/common/spelling/package-lock.json +++ b/eng/common/spelling/package-lock.json @@ -8,66 +8,66 @@ "name": "cspell-version-pin", "version": "1.0.0", "dependencies": { - "cspell": "^9.3.2" + "cspell": "^9.6.3" } }, "node_modules/@cspell/cspell-bundled-dicts": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/@cspell/cspell-bundled-dicts/-/cspell-bundled-dicts-9.3.2.tgz", - "integrity": "sha512-OmKzq/0FATHU671GKMzBrTyLdm25Wnziva7h4ylumVn1wnwWsXGef5bgXD7iuApqfqH9SzxsU0NtTB8m8vwEHQ==", + "version": "9.6.3", + "resolved": "https://registry.npmjs.org/@cspell/cspell-bundled-dicts/-/cspell-bundled-dicts-9.6.3.tgz", + "integrity": "sha512-0Ibx+ynlSd8wMAoUmc2Ck4SDN27eJheM45aCmCvCA8saROwkDh7j8zBz7aTTUTtFN+pcotJoNxr71LLD+KXhgQ==", "dependencies": { "@cspell/dict-ada": "^4.1.1", "@cspell/dict-al": "^1.1.1", - "@cspell/dict-aws": "^4.0.16", + "@cspell/dict-aws": "^4.0.17", "@cspell/dict-bash": "^4.2.2", - "@cspell/dict-companies": "^3.2.7", - "@cspell/dict-cpp": "^6.0.14", + "@cspell/dict-companies": "^3.2.10", + "@cspell/dict-cpp": "^7.0.2", "@cspell/dict-cryptocurrencies": "^5.0.5", - "@cspell/dict-csharp": "^4.0.7", - "@cspell/dict-css": "^4.0.18", - "@cspell/dict-dart": "^2.3.1", - "@cspell/dict-data-science": "^2.0.11", - "@cspell/dict-django": "^4.1.5", - "@cspell/dict-docker": "^1.1.16", - "@cspell/dict-dotnet": "^5.0.10", + "@cspell/dict-csharp": "^4.0.8", + "@cspell/dict-css": "^4.0.19", + "@cspell/dict-dart": "^2.3.2", + "@cspell/dict-data-science": "^2.0.13", + "@cspell/dict-django": "^4.1.6", + "@cspell/dict-docker": "^1.1.17", + "@cspell/dict-dotnet": "^5.0.11", "@cspell/dict-elixir": "^4.0.8", - "@cspell/dict-en_us": "^4.4.24", - "@cspell/dict-en-common-misspellings": "^2.1.8", - "@cspell/dict-en-gb-mit": "^3.1.14", - "@cspell/dict-filetypes": "^3.0.14", + "@cspell/dict-en_us": "^4.4.28", + "@cspell/dict-en-common-misspellings": "^2.1.12", + "@cspell/dict-en-gb-mit": "^3.1.17", + "@cspell/dict-filetypes": "^3.0.15", "@cspell/dict-flutter": "^1.1.1", "@cspell/dict-fonts": "^4.0.5", "@cspell/dict-fsharp": "^1.1.1", - "@cspell/dict-fullstack": "^3.2.7", + "@cspell/dict-fullstack": "^3.2.8", "@cspell/dict-gaming-terms": "^1.1.2", - "@cspell/dict-git": "^3.0.7", - "@cspell/dict-golang": "^6.0.24", + "@cspell/dict-git": "^3.1.0", + "@cspell/dict-golang": "^6.0.26", "@cspell/dict-google": "^1.0.9", "@cspell/dict-haskell": "^4.0.6", - "@cspell/dict-html": "^4.0.12", - "@cspell/dict-html-symbol-entities": "^4.0.4", + "@cspell/dict-html": "^4.0.14", + "@cspell/dict-html-symbol-entities": "^4.0.5", "@cspell/dict-java": "^5.0.12", "@cspell/dict-julia": "^1.1.1", "@cspell/dict-k8s": "^1.0.12", "@cspell/dict-kotlin": "^1.1.1", - "@cspell/dict-latex": "^4.0.4", + "@cspell/dict-latex": "^5.0.0", "@cspell/dict-lorem-ipsum": "^4.0.5", "@cspell/dict-lua": "^4.0.8", "@cspell/dict-makefile": "^1.0.5", - "@cspell/dict-markdown": "^2.0.12", - "@cspell/dict-monkeyc": "^1.0.11", - "@cspell/dict-node": "^5.0.8", - "@cspell/dict-npm": "^5.2.22", - "@cspell/dict-php": "^4.1.0", + "@cspell/dict-markdown": "^2.0.14", + "@cspell/dict-monkeyc": "^1.0.12", + "@cspell/dict-node": "^5.0.9", + "@cspell/dict-npm": "^5.2.32", + "@cspell/dict-php": "^4.1.1", "@cspell/dict-powershell": "^5.0.15", "@cspell/dict-public-licenses": "^2.0.15", - "@cspell/dict-python": "^4.2.21", + "@cspell/dict-python": "^4.2.25", "@cspell/dict-r": "^2.1.1", - "@cspell/dict-ruby": "^5.0.9", - "@cspell/dict-rust": "^4.0.12", - "@cspell/dict-scala": "^5.0.8", + "@cspell/dict-ruby": "^5.1.0", + "@cspell/dict-rust": "^4.1.2", + "@cspell/dict-scala": "^5.0.9", "@cspell/dict-shell": "^1.1.2", - "@cspell/dict-software-terms": "^5.1.13", + "@cspell/dict-software-terms": "^5.1.20", "@cspell/dict-sql": "^2.2.1", "@cspell/dict-svelte": "^1.0.7", "@cspell/dict-swift": "^2.0.6", @@ -81,28 +81,36 @@ } }, "node_modules/@cspell/cspell-json-reporter": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/@cspell/cspell-json-reporter/-/cspell-json-reporter-9.3.2.tgz", - "integrity": "sha512-YRgpeHN9uY8kUlIw9q+8zJ0tRTAJMbfBTGzCq9Puah09NeMWlRMFPUkXVrkdic6NA7etboZ+zEdoZwRO9EmhiA==", + "version": "9.6.3", + "resolved": "https://registry.npmjs.org/@cspell/cspell-json-reporter/-/cspell-json-reporter-9.6.3.tgz", + "integrity": "sha512-4jhqXKGCFAQXomtk4/Ldy/GiGEocC61v8GISiUgn1mH+HPgEvkJijyxKRNFYvzXOoJr4y0KrkGBoKk5FI7cipw==", "dependencies": { - "@cspell/cspell-types": "9.3.2" + "@cspell/cspell-types": "9.6.3" }, "engines": { "node": ">=20" } }, + "node_modules/@cspell/cspell-performance-monitor": { + "version": "9.6.3", + "resolved": "https://registry.npmjs.org/@cspell/cspell-performance-monitor/-/cspell-performance-monitor-9.6.3.tgz", + "integrity": "sha512-Y1I0wg7MUTouubREyPeTJe2dKNbBZPBnd6kDNcVet5DRZ3Cck2udS9m1a6DhJO2sGQgHTBULPDBEUU5tcAZ+nQ==", + "engines": { + "node": ">=20.18" + } + }, "node_modules/@cspell/cspell-pipe": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/@cspell/cspell-pipe/-/cspell-pipe-9.3.2.tgz", - "integrity": "sha512-REF7ibG79WLEynIMUss/IRDCdYEb1nlE1rj/gt2CbPFzLa6t5MRwW2lajEvXS6/WgbMtsTVHAWi3ALqJzCwxng==", + "version": "9.6.3", + "resolved": "https://registry.npmjs.org/@cspell/cspell-pipe/-/cspell-pipe-9.6.3.tgz", + "integrity": "sha512-Q0M58JbgHWOOOYvSyBodEysXLP5t8Q58Ff08msj2/XxRFpowR4iIUyfg3k+/d06uyOy89l+RD5AmyTEa5410xg==", "engines": { "node": ">=20" } }, "node_modules/@cspell/cspell-resolver": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/@cspell/cspell-resolver/-/cspell-resolver-9.3.2.tgz", - "integrity": "sha512-jLN2Aa/vxm8+IBvTd884SwPEfjxnDwIEPBT3hmqgLlKuUHQ3FMG27lsM4Ik9L2KWBXMgV/wGz4BaxfhKI41Ttw==", + "version": "9.6.3", + "resolved": "https://registry.npmjs.org/@cspell/cspell-resolver/-/cspell-resolver-9.6.3.tgz", + "integrity": "sha512-yUMTC8vl+LeRcIeXRrxn0GXL+rurf4OR3uyqZbG6bbtuttvq/Icleeibv9Wts+n700HLp+sVXJ7BrCn0Ehobsw==", "dependencies": { "global-directory": "^4.0.1" }, @@ -111,21 +119,32 @@ } }, "node_modules/@cspell/cspell-service-bus": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/@cspell/cspell-service-bus/-/cspell-service-bus-9.3.2.tgz", - "integrity": "sha512-/rB8LazM0JzKL+AvZa5fEpLutmwy5QFMpzw8HJd+rDGkzb5r79hURWSRo84QArgaskUqA9XlOHSieDE9pt+WAA==", + "version": "9.6.3", + "resolved": "https://registry.npmjs.org/@cspell/cspell-service-bus/-/cspell-service-bus-9.6.3.tgz", + "integrity": "sha512-gDUDq0PcMcdSG/5HwaQzkptn29E0zCWJUqmP0W/4scSC80AVh/8hgws4tuqv1GWvr4MiBZhIbUEO4f3Pc58OYQ==", "engines": { "node": ">=20" } }, "node_modules/@cspell/cspell-types": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/@cspell/cspell-types/-/cspell-types-9.3.2.tgz", - "integrity": "sha512-l4H8bMAmdzCbXHO8y1JZiAKszrPEiuLFKWrbhCacHF0iP+PIc/yuQp7cO70m0p70vArRfih6kgGyHFaCy47CfA==", + "version": "9.6.3", + "resolved": "https://registry.npmjs.org/@cspell/cspell-types/-/cspell-types-9.6.3.tgz", + "integrity": "sha512-oDTzZ2kG/jJk3TaupESUckDraGJbBpRBEW1xgTrWyIU+uGwdmMXPcGr9XZq9dRS5Zxm01nTgLwxpVM/3BVTvWQ==", "engines": { "node": ">=20" } }, + "node_modules/@cspell/cspell-worker": { + "version": "9.6.3", + "resolved": "https://registry.npmjs.org/@cspell/cspell-worker/-/cspell-worker-9.6.3.tgz", + "integrity": "sha512-zZccf1jLSrqIvlYAqwnnBi4RrLVfSEfWT4eYpiyIzHXHMzWnkUXEstQCG6LFK4L9A44M/w9s8szi7R4DLBzKsA==", + "dependencies": { + "cspell-lib": "9.6.3" + }, + "engines": { + "node": ">=20.18" + } + }, "node_modules/@cspell/dict-ada": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/@cspell/dict-ada/-/dict-ada-4.1.1.tgz", @@ -137,9 +156,9 @@ "integrity": "sha512-sD8GCaZetgQL4+MaJLXqbzWcRjfKVp8x+px3HuCaaiATAAtvjwUQ5/Iubiqwfd1boIh2Y1/3EgM3TLQ7Q8e0wQ==" }, "node_modules/@cspell/dict-aws": { - "version": "4.0.16", - "resolved": "https://registry.npmjs.org/@cspell/dict-aws/-/dict-aws-4.0.16.tgz", - "integrity": "sha512-a681zShZbtTo947NvTYGLer95ZDQw1ROKvIFydak1e0OlfFCsNdtcYTupn0nbbYs53c9AO7G2DU8AcNEAnwXPA==" + "version": "4.0.17", + "resolved": "https://registry.npmjs.org/@cspell/dict-aws/-/dict-aws-4.0.17.tgz", + "integrity": "sha512-ORcblTWcdlGjIbWrgKF+8CNEBQiLVKdUOFoTn0KPNkAYnFcdPP0muT4892h7H4Xafh3j72wqB4/loQ6Nti9E/w==" }, "node_modules/@cspell/dict-bash": { "version": "4.2.2", @@ -150,14 +169,14 @@ } }, "node_modules/@cspell/dict-companies": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/@cspell/dict-companies/-/dict-companies-3.2.7.tgz", - "integrity": "sha512-fEyr3LmpFKTaD0LcRhB4lfW1AmULYBqzg4gWAV0dQCv06l+TsA+JQ+3pZJbUcoaZirtgsgT3dL3RUjmGPhUH0A==" + "version": "3.2.10", + "resolved": "https://registry.npmjs.org/@cspell/dict-companies/-/dict-companies-3.2.10.tgz", + "integrity": "sha512-bJ1qnO1DkTn7JYGXvxp8FRQc4yq6tRXnrII+jbP8hHmq5TX5o1Wu+rdfpoUQaMWTl6balRvcMYiINDesnpR9Bw==" }, "node_modules/@cspell/dict-cpp": { - "version": "6.0.14", - "resolved": "https://registry.npmjs.org/@cspell/dict-cpp/-/dict-cpp-6.0.14.tgz", - "integrity": "sha512-dkmpSwvVfVdtoZ4mW/CK2Ep1v8mJlp6uiKpMNbSMOdJl4kq28nQS4vKNIX3B2bJa0Ha5iHHu+1mNjiLeO3g7Xg==" + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@cspell/dict-cpp/-/dict-cpp-7.0.2.tgz", + "integrity": "sha512-dfbeERiVNeqmo/npivdR6rDiBCqZi3QtjH2Z0HFcXwpdj6i97dX1xaKyK2GUsO/p4u1TOv63Dmj5Vm48haDpuA==" }, "node_modules/@cspell/dict-cryptocurrencies": { "version": "5.0.5", @@ -165,39 +184,39 @@ "integrity": "sha512-R68hYYF/rtlE6T/dsObStzN5QZw+0aQBinAXuWCVqwdS7YZo0X33vGMfChkHaiCo3Z2+bkegqHlqxZF4TD3rUA==" }, "node_modules/@cspell/dict-csharp": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/@cspell/dict-csharp/-/dict-csharp-4.0.7.tgz", - "integrity": "sha512-H16Hpu8O/1/lgijFt2lOk4/nnldFtQ4t8QHbyqphqZZVE5aS4J/zD/WvduqnLY21aKhZS6jo/xF5PX9jyqPKUA==" + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@cspell/dict-csharp/-/dict-csharp-4.0.8.tgz", + "integrity": "sha512-qmk45pKFHSxckl5mSlbHxmDitSsGMlk/XzFgt7emeTJWLNSTUK//MbYAkBNRtfzB4uD7pAFiKgpKgtJrTMRnrQ==" }, "node_modules/@cspell/dict-css": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@cspell/dict-css/-/dict-css-4.0.18.tgz", - "integrity": "sha512-EF77RqROHL+4LhMGW5NTeKqfUd/e4OOv6EDFQ/UQQiFyWuqkEKyEz0NDILxOFxWUEVdjT2GQ2cC7t12B6pESwg==" + "version": "4.0.19", + "resolved": "https://registry.npmjs.org/@cspell/dict-css/-/dict-css-4.0.19.tgz", + "integrity": "sha512-VYHtPnZt/Zd/ATbW3rtexWpBnHUohUrQOHff/2JBhsVgxOrksAxJnLAO43Q1ayLJBJUUwNVo+RU0sx0aaysZfg==" }, "node_modules/@cspell/dict-dart": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-dart/-/dict-dart-2.3.1.tgz", - "integrity": "sha512-xoiGnULEcWdodXI6EwVyqpZmpOoh8RA2Xk9BNdR7DLamV/QMvEYn8KJ7NlRiTSauJKPNkHHQ5EVHRM6sTS7jdg==" + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@cspell/dict-dart/-/dict-dart-2.3.2.tgz", + "integrity": "sha512-sUiLW56t9gfZcu8iR/5EUg+KYyRD83Cjl3yjDEA2ApVuJvK1HhX+vn4e4k4YfjpUQMag8XO2AaRhARE09+/rqw==" }, "node_modules/@cspell/dict-data-science": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@cspell/dict-data-science/-/dict-data-science-2.0.11.tgz", - "integrity": "sha512-Dt+83nVCcF+dQyvFSaZjCKt1H5KbsVJFtH2X7VUfmIzQu8xCnV1fUmkhBzGJ+NiFs99Oy9JA6I9EjeqExzXk7g==" + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/@cspell/dict-data-science/-/dict-data-science-2.0.13.tgz", + "integrity": "sha512-l1HMEhBJkPmw4I2YGVu2eBSKM89K9pVF+N6qIr5Uo5H3O979jVodtuwP8I7LyPrJnC6nz28oxeGRCLh9xC5CVA==" }, "node_modules/@cspell/dict-django": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@cspell/dict-django/-/dict-django-4.1.5.tgz", - "integrity": "sha512-AvTWu99doU3T8ifoMYOMLW2CXKvyKLukPh1auOPwFGHzueWYvBBN+OxF8wF7XwjTBMMeRleVdLh3aWCDEX/ZWg==" + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@cspell/dict-django/-/dict-django-4.1.6.tgz", + "integrity": "sha512-SdbSFDGy9ulETqNz15oWv2+kpWLlk8DJYd573xhIkeRdcXOjskRuxjSZPKfW7O3NxN/KEf3gm3IevVOiNuFS+w==" }, "node_modules/@cspell/dict-docker": { - "version": "1.1.16", - "resolved": "https://registry.npmjs.org/@cspell/dict-docker/-/dict-docker-1.1.16.tgz", - "integrity": "sha512-UiVQ5RmCg6j0qGIxrBnai3pIB+aYKL3zaJGvXk1O/ertTKJif9RZikKXCEgqhaCYMweM4fuLqWSVmw3hU164Iw==" + "version": "1.1.17", + "resolved": "https://registry.npmjs.org/@cspell/dict-docker/-/dict-docker-1.1.17.tgz", + "integrity": "sha512-OcnVTIpHIYYKhztNTyK8ShAnXTfnqs43hVH6p0py0wlcwRIXe5uj4f12n7zPf2CeBI7JAlPjEsV0Rlf4hbz/xQ==" }, "node_modules/@cspell/dict-dotnet": { - "version": "5.0.10", - "resolved": "https://registry.npmjs.org/@cspell/dict-dotnet/-/dict-dotnet-5.0.10.tgz", - "integrity": "sha512-ooar8BP/RBNP1gzYfJPStKEmpWy4uv/7JCq6FOnJLeD1yyfG3d/LFMVMwiJo+XWz025cxtkM3wuaikBWzCqkmg==" + "version": "5.0.11", + "resolved": "https://registry.npmjs.org/@cspell/dict-dotnet/-/dict-dotnet-5.0.11.tgz", + "integrity": "sha512-LSVKhpFf/ASTWJcfYeS0Sykcl1gVMsv2Z5Eo0TnTMSTLV3738HH+66pIsjUTChqU6SF3gKPuCe6EOaRYqb/evA==" }, "node_modules/@cspell/dict-elixir": { "version": "4.0.8", @@ -205,24 +224,24 @@ "integrity": "sha512-CyfphrbMyl4Ms55Vzuj+mNmd693HjBFr9hvU+B2YbFEZprE5AG+EXLYTMRWrXbpds4AuZcvN3deM2XVB80BN/Q==" }, "node_modules/@cspell/dict-en_us": { - "version": "4.4.24", - "resolved": "https://registry.npmjs.org/@cspell/dict-en_us/-/dict-en_us-4.4.24.tgz", - "integrity": "sha512-JE+/H2YicHJTneRmgH4GSI21rS+1yGZVl1jfOQgl8iHLC+yTTMtCvueNDMK94CgJACzYAoCsQB70MqiFJJfjLQ==" + "version": "4.4.28", + "resolved": "https://registry.npmjs.org/@cspell/dict-en_us/-/dict-en_us-4.4.28.tgz", + "integrity": "sha512-/rzhbZaZDsDWmXbc9Fmmr4/ngmaNcG2b+TGT+ZjGqpOXVQYI75yZ9+XduyI43xJ5O38QcX3QIbJY5GWaJqxPEg==" }, "node_modules/@cspell/dict-en-common-misspellings": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@cspell/dict-en-common-misspellings/-/dict-en-common-misspellings-2.1.8.tgz", - "integrity": "sha512-vDsjRFPQGuAADAiitf82z9Mz3DcqKZi6V5hPAEIFkLLKjFVBcjUsSq59SfL59ElIFb76MtBO0BLifdEbBj+DoQ==" + "version": "2.1.12", + "resolved": "https://registry.npmjs.org/@cspell/dict-en-common-misspellings/-/dict-en-common-misspellings-2.1.12.tgz", + "integrity": "sha512-14Eu6QGqyksqOd4fYPuRb58lK1Va7FQK9XxFsRKnZU8LhL3N+kj7YKDW+7aIaAN/0WGEqslGP6lGbQzNti8Akw==" }, "node_modules/@cspell/dict-en-gb-mit": { - "version": "3.1.14", - "resolved": "https://registry.npmjs.org/@cspell/dict-en-gb-mit/-/dict-en-gb-mit-3.1.14.tgz", - "integrity": "sha512-b+vEerlHP6rnNf30tmTJb7JZnOq4WAslYUvexOz/L3gDna9YJN3bAnwRJ3At3bdcOcMG7PTv3Pi+C73IR22lNg==" + "version": "3.1.17", + "resolved": "https://registry.npmjs.org/@cspell/dict-en-gb-mit/-/dict-en-gb-mit-3.1.17.tgz", + "integrity": "sha512-MLx+3XN9rj+EGwLIFmh0gpEDNalCyQqjcszp+WkedCHcvTCzQgWXQMZK6cPuhO/OKYyW9GOwsx4t0wjU5tRVNg==" }, "node_modules/@cspell/dict-filetypes": { - "version": "3.0.14", - "resolved": "https://registry.npmjs.org/@cspell/dict-filetypes/-/dict-filetypes-3.0.14.tgz", - "integrity": "sha512-KSXaSMYYNMLLdHEnju1DyRRH3eQWPRYRnOXpuHUdOh2jC44VgQoxyMU7oB3NAhDhZKBPCihabzECsAGFbdKfEA==" + "version": "3.0.15", + "resolved": "https://registry.npmjs.org/@cspell/dict-filetypes/-/dict-filetypes-3.0.15.tgz", + "integrity": "sha512-uDMeqYlLlK476w/muEFQGBy9BdQWS0mQ7BJiy/iQv5XUWZxE2O54ZQd9nW8GyQMzAgoyg5SG4hf9l039Qt66oA==" }, "node_modules/@cspell/dict-flutter": { "version": "1.1.1", @@ -240,9 +259,9 @@ "integrity": "sha512-imhs0u87wEA4/cYjgzS0tAyaJpwG7vwtC8UyMFbwpmtw+/bgss+osNfyqhYRyS/ehVCWL17Ewx2UPkexjKyaBA==" }, "node_modules/@cspell/dict-fullstack": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/@cspell/dict-fullstack/-/dict-fullstack-3.2.7.tgz", - "integrity": "sha512-IxEk2YAwAJKYCUEgEeOg3QvTL4XLlyArJElFuMQevU1dPgHgzWElFevN5lsTFnvMFA1riYsVinqJJX0BanCFEg==" + "version": "3.2.8", + "resolved": "https://registry.npmjs.org/@cspell/dict-fullstack/-/dict-fullstack-3.2.8.tgz", + "integrity": "sha512-J6EeoeThvx/DFrcA2rJiCA6vfqwJMbkG0IcXhlsmRZmasIpanmxgt90OEaUazbZahFiuJT8wrhgQ1QgD1MsqBw==" }, "node_modules/@cspell/dict-gaming-terms": { "version": "1.1.2", @@ -250,14 +269,14 @@ "integrity": "sha512-9XnOvaoTBscq0xuD6KTEIkk9hhdfBkkvJAIsvw3JMcnp1214OCGW8+kako5RqQ2vTZR3Tnf3pc57o7VgkM0q1Q==" }, "node_modules/@cspell/dict-git": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@cspell/dict-git/-/dict-git-3.0.7.tgz", - "integrity": "sha512-odOwVKgfxCQfiSb+nblQZc4ErXmnWEnv8XwkaI4sNJ7cNmojnvogYVeMqkXPjvfrgEcizEEA4URRD2Ms5PDk1w==" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-git/-/dict-git-3.1.0.tgz", + "integrity": "sha512-KEt9zGkxqGy2q1nwH4CbyqTSv5nadpn8BAlDnzlRcnL0Xb3LX9xTgSGShKvzb0bw35lHoYyLWN2ZKAqbC4pgGQ==" }, "node_modules/@cspell/dict-golang": { - "version": "6.0.24", - "resolved": "https://registry.npmjs.org/@cspell/dict-golang/-/dict-golang-6.0.24.tgz", - "integrity": "sha512-rY7PlC3MsHozmjrZWi0HQPUl0BVCV0+mwK0rnMT7pOIXqOe4tWCYMULDIsEk4F0gbIxb5badd2dkCPDYjLnDgA==" + "version": "6.0.26", + "resolved": "https://registry.npmjs.org/@cspell/dict-golang/-/dict-golang-6.0.26.tgz", + "integrity": "sha512-YKA7Xm5KeOd14v5SQ4ll6afe9VSy3a2DWM7L9uBq4u3lXToRBQ1W5PRa+/Q9udd+DTURyVVnQ+7b9cnOlNxaRg==" }, "node_modules/@cspell/dict-google": { "version": "1.0.9", @@ -270,14 +289,14 @@ "integrity": "sha512-ib8SA5qgftExpYNjWhpYIgvDsZ/0wvKKxSP+kuSkkak520iPvTJumEpIE+qPcmJQo4NzdKMN8nEfaeci4OcFAQ==" }, "node_modules/@cspell/dict-html": { - "version": "4.0.12", - "resolved": "https://registry.npmjs.org/@cspell/dict-html/-/dict-html-4.0.12.tgz", - "integrity": "sha512-JFffQ1dDVEyJq6tCDWv0r/RqkdSnV43P2F/3jJ9rwLgdsOIXwQbXrz6QDlvQLVvNSnORH9KjDtenFTGDyzfCaA==" + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/@cspell/dict-html/-/dict-html-4.0.14.tgz", + "integrity": "sha512-2bf7n+kS92g+cMKV0wr9o/Oq9n8JzU7CcrB96gIh2GHgnF+0xDOqO2W/1KeFAqOfqosoOVE48t+4dnEMkkoJ2Q==" }, "node_modules/@cspell/dict-html-symbol-entities": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@cspell/dict-html-symbol-entities/-/dict-html-symbol-entities-4.0.4.tgz", - "integrity": "sha512-afea+0rGPDeOV9gdO06UW183Qg6wRhWVkgCFwiO3bDupAoyXRuvupbb5nUyqSTsLXIKL8u8uXQlJ9pkz07oVXw==" + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@cspell/dict-html-symbol-entities/-/dict-html-symbol-entities-4.0.5.tgz", + "integrity": "sha512-429alTD4cE0FIwpMucvSN35Ld87HCyuM8mF731KU5Rm4Je2SG6hmVx7nkBsLyrmH3sQukTcr1GaiZsiEg8svPA==" }, "node_modules/@cspell/dict-java": { "version": "5.0.12", @@ -300,9 +319,9 @@ "integrity": "sha512-J3NzzfgmxRvEeOe3qUXnSJQCd38i/dpF9/t3quuWh6gXM+krsAXP75dY1CzDmS8mrJAlBdVBeAW5eAZTD8g86Q==" }, "node_modules/@cspell/dict-latex": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@cspell/dict-latex/-/dict-latex-4.0.4.tgz", - "integrity": "sha512-YdTQhnTINEEm/LZgTzr9Voz4mzdOXH7YX+bSFs3hnkUHCUUtX/mhKgf1CFvZ0YNM2afjhQcmLaR9bDQVyYBvpA==" + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-latex/-/dict-latex-5.0.0.tgz", + "integrity": "sha512-HUrIqUVohM6P0+5b7BsdAdb0STIv0aaFBvguI7pLcreljlcX3FSPUxea7ticzNlCNeVrEaiEn/ws9m6rYUeuNw==" }, "node_modules/@cspell/dict-lorem-ipsum": { "version": "4.0.5", @@ -320,35 +339,35 @@ "integrity": "sha512-4vrVt7bGiK8Rx98tfRbYo42Xo2IstJkAF4tLLDMNQLkQ86msDlYSKG1ZCk8Abg+EdNcFAjNhXIiNO+w4KflGAQ==" }, "node_modules/@cspell/dict-markdown": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/@cspell/dict-markdown/-/dict-markdown-2.0.12.tgz", - "integrity": "sha512-ufwoliPijAgWkD/ivAMC+A9QD895xKiJRF/fwwknQb7kt7NozTLKFAOBtXGPJAB4UjhGBpYEJVo2elQ0FCAH9A==", + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/@cspell/dict-markdown/-/dict-markdown-2.0.14.tgz", + "integrity": "sha512-uLKPNJsUcumMQTsZZgAK9RgDLyQhUz/uvbQTEkvF/Q4XfC1i/BnA8XrOrd0+Vp6+tPOKyA+omI5LRWfMu5K/Lw==", "peerDependencies": { - "@cspell/dict-css": "^4.0.18", - "@cspell/dict-html": "^4.0.12", - "@cspell/dict-html-symbol-entities": "^4.0.4", + "@cspell/dict-css": "^4.0.19", + "@cspell/dict-html": "^4.0.14", + "@cspell/dict-html-symbol-entities": "^4.0.5", "@cspell/dict-typescript": "^3.2.3" } }, "node_modules/@cspell/dict-monkeyc": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@cspell/dict-monkeyc/-/dict-monkeyc-1.0.11.tgz", - "integrity": "sha512-7Q1Ncu0urALI6dPTrEbSTd//UK0qjRBeaxhnm8uY5fgYNFYAG+u4gtnTIo59S6Bw5P++4H3DiIDYoQdY/lha8w==" + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@cspell/dict-monkeyc/-/dict-monkeyc-1.0.12.tgz", + "integrity": "sha512-MN7Vs11TdP5mbdNFQP5x2Ac8zOBm97ARg6zM5Sb53YQt/eMvXOMvrep7+/+8NJXs0jkp70bBzjqU4APcqBFNAw==" }, "node_modules/@cspell/dict-node": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/@cspell/dict-node/-/dict-node-5.0.8.tgz", - "integrity": "sha512-AirZcN2i84ynev3p2/1NCPEhnNsHKMz9zciTngGoqpdItUb2bDt1nJBjwlsrFI78GZRph/VaqTVFwYikmncpXg==" + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/@cspell/dict-node/-/dict-node-5.0.9.tgz", + "integrity": "sha512-hO+ga+uYZ/WA4OtiMEyKt5rDUlUyu3nXMf8KVEeqq2msYvAPdldKBGH7lGONg6R/rPhv53Rb+0Y1SLdoK1+7wQ==" }, "node_modules/@cspell/dict-npm": { - "version": "5.2.22", - "resolved": "https://registry.npmjs.org/@cspell/dict-npm/-/dict-npm-5.2.22.tgz", - "integrity": "sha512-bepGmmRv7KmxULqfw88G9wxkpIAQA96YyrfKpfg4RHnQLxpmzoRnZGtK5S9JH7Hlf5LEd1gkOF7dtCE6cek67w==" + "version": "5.2.32", + "resolved": "https://registry.npmjs.org/@cspell/dict-npm/-/dict-npm-5.2.32.tgz", + "integrity": "sha512-H0XD0eg4d96vevle8VUKVoPhsgsw003ByJ47XzipyiMKoQTZ2IAUW+VTkQq8wU1floarNjmThQJOoKL9J4UYuw==" }, "node_modules/@cspell/dict-php": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-php/-/dict-php-4.1.0.tgz", - "integrity": "sha512-dTDeabyOj7eFvn2Q4Za3uVXM2+SzeFMqX8ly2P0XTo4AzbCmI2hulFD/QIADwWmwiRrInbbf8cxwFHNIYrXl4w==" + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-php/-/dict-php-4.1.1.tgz", + "integrity": "sha512-EXelI+4AftmdIGtA8HL8kr4WlUE11OqCSVlnIgZekmTkEGSZdYnkFdiJ5IANSALtlQ1mghKjz+OFqVs6yowgWA==" }, "node_modules/@cspell/dict-powershell": { "version": "5.0.15", @@ -361,11 +380,11 @@ "integrity": "sha512-cJEOs901H13Pfy0fl4dCD1U+xpWIMaEPq8MeYU83FfDZvellAuSo4GqWCripfIqlhns/L6+UZEIJSOZnjgy7Wg==" }, "node_modules/@cspell/dict-python": { - "version": "4.2.21", - "resolved": "https://registry.npmjs.org/@cspell/dict-python/-/dict-python-4.2.21.tgz", - "integrity": "sha512-M9OgwXWhpZqEZqKU2psB2DFsT8q5SwEahkQeIpNIRWIErjwG7I9yYhhfvPz6s5gMCMhhb3hqcPJTnmdgqGrQyg==", + "version": "4.2.25", + "resolved": "https://registry.npmjs.org/@cspell/dict-python/-/dict-python-4.2.25.tgz", + "integrity": "sha512-hDdN0YhKgpbtZVRjQ2c8jk+n0wQdidAKj1Fk8w7KEHb3YlY5uPJ0mAKJk7AJKPNLOlILoUmN+HAVJz+cfSbWYg==", "dependencies": { - "@cspell/dict-data-science": "^2.0.11" + "@cspell/dict-data-science": "^2.0.13" } }, "node_modules/@cspell/dict-r": { @@ -374,19 +393,19 @@ "integrity": "sha512-71Ka+yKfG4ZHEMEmDxc6+blFkeTTvgKbKAbwiwQAuKl3zpqs1Y0vUtwW2N4b3LgmSPhV3ODVY0y4m5ofqDuKMw==" }, "node_modules/@cspell/dict-ruby": { - "version": "5.0.9", - "resolved": "https://registry.npmjs.org/@cspell/dict-ruby/-/dict-ruby-5.0.9.tgz", - "integrity": "sha512-H2vMcERMcANvQshAdrVx0XoWaNX8zmmiQN11dZZTQAZaNJ0xatdJoSqY8C8uhEMW89bfgpN+NQgGuDXW2vmXEw==" + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-ruby/-/dict-ruby-5.1.0.tgz", + "integrity": "sha512-9PJQB3cfkBULrMLp5kSAcFPpzf8oz9vFN+QYZABhQwWkGbuzCIXSorHrmWSASlx4yejt3brjaWS57zZ/YL5ZQQ==" }, "node_modules/@cspell/dict-rust": { - "version": "4.0.12", - "resolved": "https://registry.npmjs.org/@cspell/dict-rust/-/dict-rust-4.0.12.tgz", - "integrity": "sha512-z2QiH+q9UlNhobBJArvILRxV8Jz0pKIK7gqu4TgmEYyjiu1TvnGZ1tbYHeu9w3I/wOP6UMDoCBTty5AlYfW0mw==" + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@cspell/dict-rust/-/dict-rust-4.1.2.tgz", + "integrity": "sha512-O1FHrumYcO+HZti3dHfBPUdnDFkI+nbYK3pxYmiM1sr+G0ebOd6qchmswS0Wsc6ZdEVNiPYJY/gZQR6jfW3uOg==" }, "node_modules/@cspell/dict-scala": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/@cspell/dict-scala/-/dict-scala-5.0.8.tgz", - "integrity": "sha512-YdftVmumv8IZq9zu1gn2U7A4bfM2yj9Vaupydotyjuc+EEZZSqAafTpvW/jKLWji2TgybM1L2IhmV0s/Iv9BTw==" + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/@cspell/dict-scala/-/dict-scala-5.0.9.tgz", + "integrity": "sha512-AjVcVAELgllybr1zk93CJ5wSUNu/Zb5kIubymR/GAYkMyBdYFCZ3Zbwn4Zz8GJlFFAbazABGOu0JPVbeY59vGg==" }, "node_modules/@cspell/dict-shell": { "version": "1.1.2", @@ -394,9 +413,9 @@ "integrity": "sha512-WqOUvnwcHK1X61wAfwyXq04cn7KYyskg90j4lLg3sGGKMW9Sq13hs91pqrjC44Q+lQLgCobrTkMDw9Wyl9nRFA==" }, "node_modules/@cspell/dict-software-terms": { - "version": "5.1.13", - "resolved": "https://registry.npmjs.org/@cspell/dict-software-terms/-/dict-software-terms-5.1.13.tgz", - "integrity": "sha512-bmWMOpqzCmmKFHM9hFC6o+GP20t2+uTuwksR3a6crHQqvNv3revzwQHorsWvSYx75xsdcvRYrAz7PqPEV/FG1g==" + "version": "5.1.20", + "resolved": "https://registry.npmjs.org/@cspell/dict-software-terms/-/dict-software-terms-5.1.20.tgz", + "integrity": "sha512-TEk1xHvetTI4pv7Vzje1D322m6QEjaH2P6ucOOf6q7EJCppQIdC0lZSXkgHJAFU5HGSvEXSzvnVeW2RHW86ziQ==" }, "node_modules/@cspell/dict-sql": { "version": "2.2.1", @@ -434,11 +453,11 @@ "integrity": "sha512-XibBIxBlVosU06+M6uHWkFeT0/pW5WajDRYdXG2CgHnq85b0TI/Ks0FuBJykmsgi2CAD3Qtx8UHFEtl/DSFnAQ==" }, "node_modules/@cspell/dynamic-import": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/@cspell/dynamic-import/-/dynamic-import-9.3.2.tgz", - "integrity": "sha512-au7FyuIHUNI2r9sO3pUBKVTeD/v7c9x/nPUStaAK1bG4rdKt4w+/jUY2IaldAraW5w29z528BboXbiV87SM1kw==", + "version": "9.6.3", + "resolved": "https://registry.npmjs.org/@cspell/dynamic-import/-/dynamic-import-9.6.3.tgz", + "integrity": "sha512-J2RArbv02H0fZCZxfGe2RjhEBFrcBlWgEESh2kDdF3CyoXBoPneevS5L3X4dk2XpOR0LU6bCJEsVIuzJqoFDbw==", "dependencies": { - "@cspell/url": "9.3.2", + "@cspell/url": "9.6.3", "import-meta-resolve": "^4.2.0" }, "engines": { @@ -446,29 +465,48 @@ } }, "node_modules/@cspell/filetypes": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/@cspell/filetypes/-/filetypes-9.3.2.tgz", - "integrity": "sha512-0bUxQlmJPRHZrRQD7adbc4lFizO8tGD/6+1cBgU3kV3+NVrpr12y4jU8twCSChhYibZyPr7bnvhkM3cQgb8RzA==", + "version": "9.6.3", + "resolved": "https://registry.npmjs.org/@cspell/filetypes/-/filetypes-9.6.3.tgz", + "integrity": "sha512-FocAActFpUanhQblokOQYlsV3yt3xC6FNMnziGURG9f1a5LYPGwmb6v40qkkhsTwK8L9L7Ai1KRtPJ3zyowNCA==", "engines": { "node": ">=20" } }, + "node_modules/@cspell/rpc": { + "version": "9.6.3", + "resolved": "https://registry.npmjs.org/@cspell/rpc/-/rpc-9.6.3.tgz", + "integrity": "sha512-D8ouhYPOZR11CI66GKsFu/05sSTSjxwfyUsTZ0m7lQOcILxkJ0P/y4DJ9HffFT6uY62mEbxDeibvlfCLBzWO4w==", + "engines": { + "node": ">=20.18" + } + }, "node_modules/@cspell/strong-weak-map": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/@cspell/strong-weak-map/-/strong-weak-map-9.3.2.tgz", - "integrity": "sha512-pFcmOTWCoFMRETb9PCkCmaiZiLb5i2qOZmGH/p/tFEH8kIYhMGfhaulnXwKwS+Ke6PKceQd2YL98bGmo8hL4aQ==", + "version": "9.6.3", + "resolved": "https://registry.npmjs.org/@cspell/strong-weak-map/-/strong-weak-map-9.6.3.tgz", + "integrity": "sha512-2OmE4RCSJG1mAfVsZqEvThJaQY//rYjDBycHfb1pfLb3KlV+O5jNKlYrI6E/+JpiTb1cI9MnzIRw6uzTPgCN1g==", "engines": { "node": ">=20" } }, "node_modules/@cspell/url": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/@cspell/url/-/url-9.3.2.tgz", - "integrity": "sha512-TobUlZl7Z7VehhNOMNAg1ABuGizieseftlG94OZJ934JptOhK8TC/1o2ldKrbDH50jyt6E7rPTMV2BW/vWuTzQ==", + "version": "9.6.3", + "resolved": "https://registry.npmjs.org/@cspell/url/-/url-9.6.3.tgz", + "integrity": "sha512-k+o5Dp8Hp8mjex8Lpe9m9B4LsrMoEzAaaYfvagUPQW1BKPwi3ugkH9YkC7uUOvzD6hOWq77WJZ2t946zY2NDSg==", "engines": { "node": ">=20" } }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, "node_modules/array-timsort": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/array-timsort/-/array-timsort-1.0.3.tgz", @@ -525,17 +563,17 @@ } }, "node_modules/commander": { - "version": "14.0.2", - "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz", - "integrity": "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==", + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", + "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", "engines": { "node": ">=20" } }, "node_modules/comment-json": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.4.1.tgz", - "integrity": "sha512-r1To31BQD5060QdkC+Iheai7gHwoSZobzunqkf2/kQ6xIAfJyrKNAFUwdKvkK7Qgu7pVTKQEa7ok7Ed3ycAJgg==", + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.5.1.tgz", + "integrity": "sha512-taEtr3ozUmOB7it68Jll7s0Pwm+aoiHyXKrEC8SEodL4rNpdfDLqa7PfBlrgFoCNNdR8ImL+muti5IGvktJAAg==", "dependencies": { "array-timsort": "^1.0.3", "core-util-is": "^1.0.3", @@ -551,24 +589,27 @@ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" }, "node_modules/cspell": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/cspell/-/cspell-9.3.2.tgz", - "integrity": "sha512-3xFyVSTYrYa/QJzLfzsCRMkMXqOsytP8E26DuGrVMJQoLPFmbOXNNtnMu4wrtr17QVloxpvutW77U4vb2L/LDQ==", + "version": "9.6.3", + "resolved": "https://registry.npmjs.org/cspell/-/cspell-9.6.3.tgz", + "integrity": "sha512-eGfq/qCJXDNLtqvwo3RFOJ8ECQD45NSEqlEdxJPdMlu/MA7YXSm4QptkJTybdodZmQpIGNpAz8UtVjKwhseqZg==", "dependencies": { - "@cspell/cspell-json-reporter": "9.3.2", - "@cspell/cspell-pipe": "9.3.2", - "@cspell/cspell-types": "9.3.2", - "@cspell/dynamic-import": "9.3.2", - "@cspell/url": "9.3.2", + "@cspell/cspell-json-reporter": "9.6.3", + "@cspell/cspell-performance-monitor": "9.6.3", + "@cspell/cspell-pipe": "9.6.3", + "@cspell/cspell-types": "9.6.3", + "@cspell/cspell-worker": "9.6.3", + "@cspell/dynamic-import": "9.6.3", + "@cspell/url": "9.6.3", + "ansi-regex": "^6.2.2", "chalk": "^5.6.2", "chalk-template": "^1.1.2", - "commander": "^14.0.2", - "cspell-config-lib": "9.3.2", - "cspell-dictionary": "9.3.2", - "cspell-gitignore": "9.3.2", - "cspell-glob": "9.3.2", - "cspell-io": "9.3.2", - "cspell-lib": "9.3.2", + "commander": "^14.0.3", + "cspell-config-lib": "9.6.3", + "cspell-dictionary": "9.6.3", + "cspell-gitignore": "9.6.3", + "cspell-glob": "9.6.3", + "cspell-io": "9.6.3", + "cspell-lib": "9.6.3", "fast-json-stable-stringify": "^2.1.0", "flatted": "^3.3.3", "semver": "^7.7.3", @@ -579,48 +620,49 @@ "cspell-esm": "bin.mjs" }, "engines": { - "node": ">=20" + "node": ">=20.18" }, "funding": { "url": "https://github.com/streetsidesoftware/cspell?sponsor=1" } }, "node_modules/cspell-config-lib": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/cspell-config-lib/-/cspell-config-lib-9.3.2.tgz", - "integrity": "sha512-zXhmA4rqgWQRTVijI+g/mgiep76TvTO4d+P3CHwcqLG57BKVzoW+jkO4qDLC+Neh4b8+CcNWEIr3w16BfuEJAA==", + "version": "9.6.3", + "resolved": "https://registry.npmjs.org/cspell-config-lib/-/cspell-config-lib-9.6.3.tgz", + "integrity": "sha512-gQnpAzyf7rPaIb2hjVDtkvfhNc+7hAeHjK+RFAHj7fXiMfdPOWMxnl5IZBOdd9aYHZzVbxYkeyvJbLwF4M3YSg==", "dependencies": { - "@cspell/cspell-types": "9.3.2", - "comment-json": "^4.4.1", - "smol-toml": "^1.5.2", - "yaml": "^2.8.1" + "@cspell/cspell-types": "9.6.3", + "comment-json": "^4.5.1", + "smol-toml": "^1.6.0", + "yaml": "^2.8.2" }, "engines": { "node": ">=20" } }, "node_modules/cspell-dictionary": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/cspell-dictionary/-/cspell-dictionary-9.3.2.tgz", - "integrity": "sha512-E3YhOhZzZt1a+AEbFV2B3THCyZ576PDg0mDNUDrU1Y65SyIhf4DC6itfPoAb6R3FI/DI218RqWZg/FTT8lJ2gA==", + "version": "9.6.3", + "resolved": "https://registry.npmjs.org/cspell-dictionary/-/cspell-dictionary-9.6.3.tgz", + "integrity": "sha512-y5te3Wu89WIdUYPYqMKUliviFxSu7xImuEacct6f2XFQd2c4G9vNduRMJjHKJT49cwkRM/fZKtOzJyPy8jzQ4g==", "dependencies": { - "@cspell/cspell-pipe": "9.3.2", - "@cspell/cspell-types": "9.3.2", - "cspell-trie-lib": "9.3.2", - "fast-equals": "^5.3.3" + "@cspell/cspell-performance-monitor": "9.6.3", + "@cspell/cspell-pipe": "9.6.3", + "@cspell/cspell-types": "9.6.3", + "cspell-trie-lib": "9.6.3", + "fast-equals": "^6.0.0" }, "engines": { "node": ">=20" } }, "node_modules/cspell-gitignore": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/cspell-gitignore/-/cspell-gitignore-9.3.2.tgz", - "integrity": "sha512-G2bLR+Dfb9GX4Sdm75GfCCa9V/sQYkRbLckuCuVmJxvcDB0xfczAtb6TfAXIziF3oUI6cOB1g+PoNLWBelcK5w==", + "version": "9.6.3", + "resolved": "https://registry.npmjs.org/cspell-gitignore/-/cspell-gitignore-9.6.3.tgz", + "integrity": "sha512-S6XAddSjGEP7opCfEkhWaTnNVCI8ypdyZWgOW/gUW3VMpq8zK7OJ/ddyOLUCIMW1Qwtkx7X4Mf06xJSBWEjKjQ==", "dependencies": { - "@cspell/url": "9.3.2", - "cspell-glob": "9.3.2", - "cspell-io": "9.3.2" + "@cspell/url": "9.6.3", + "cspell-glob": "9.6.3", + "cspell-io": "9.6.3" }, "bin": { "cspell-gitignore": "bin.mjs" @@ -630,11 +672,11 @@ } }, "node_modules/cspell-glob": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/cspell-glob/-/cspell-glob-9.3.2.tgz", - "integrity": "sha512-TuSupENEKyOCupOUZ3vnPxaTOghxY/rD1JIkb8e5kjzRprYVilO/rYqEk/52iLwJVd+4Npe8fNhR3KhU7u/UUg==", + "version": "9.6.3", + "resolved": "https://registry.npmjs.org/cspell-glob/-/cspell-glob-9.6.3.tgz", + "integrity": "sha512-tcyQP0LXoAEExVzWZMxW4afyo7w/8nea0wskoWQ4G+k36Gk6P52CARARFymHCH4IZLbbybZuLoVDxQ6OgIm3fQ==", "dependencies": { - "@cspell/url": "9.3.2", + "@cspell/url": "9.6.3", "picomatch": "^4.0.3" }, "engines": { @@ -642,12 +684,12 @@ } }, "node_modules/cspell-grammar": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/cspell-grammar/-/cspell-grammar-9.3.2.tgz", - "integrity": "sha512-ysonrFu9vJvF/derDlEjUfmvLeCfNOWPh00t6Yh093AKrJFoWQiyaS/5bEN/uB5/n1sa4k3ItnWvuTp3+YuZsA==", + "version": "9.6.3", + "resolved": "https://registry.npmjs.org/cspell-grammar/-/cspell-grammar-9.6.3.tgz", + "integrity": "sha512-26Pn/zo9hY3/5hGkM4tpV/Nnn4Op1J0DGnA34vA/lDBGtDOFexUEcUHZOieuNtCvZO735ltZPrP3zVxts1CwRg==", "dependencies": { - "@cspell/cspell-pipe": "9.3.2", - "@cspell/cspell-types": "9.3.2" + "@cspell/cspell-pipe": "9.6.3", + "@cspell/cspell-types": "9.6.3" }, "bin": { "cspell-grammar": "bin.mjs" @@ -657,38 +699,40 @@ } }, "node_modules/cspell-io": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/cspell-io/-/cspell-io-9.3.2.tgz", - "integrity": "sha512-ahoULCp0j12TyXXmIcdO/7x65A/2mzUQO1IkOC65OXEbNT+evt0yswSO5Nr1F6kCHDuEKc46EZWwsYAzj78pMg==", + "version": "9.6.3", + "resolved": "https://registry.npmjs.org/cspell-io/-/cspell-io-9.6.3.tgz", + "integrity": "sha512-dZYowJVTWPEjik3y/UrJnX/3PG20N1jgnP//lwX6cLGtHjv05W89lnHxsh5Suzxf3mkN/YZ1JUgmxdjj9HduNw==", "dependencies": { - "@cspell/cspell-service-bus": "9.3.2", - "@cspell/url": "9.3.2" + "@cspell/cspell-service-bus": "9.6.3", + "@cspell/url": "9.6.3" }, "engines": { "node": ">=20" } }, "node_modules/cspell-lib": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/cspell-lib/-/cspell-lib-9.3.2.tgz", - "integrity": "sha512-kdk11kib68zNANNICuOA8h4oA9kENQUAdeX/uvT4+7eHbHHV8WSgjXm4k4o/pRIbg164UJTX/XxKb/65ftn5jw==", + "version": "9.6.3", + "resolved": "https://registry.npmjs.org/cspell-lib/-/cspell-lib-9.6.3.tgz", + "integrity": "sha512-LFZsCWJX5EjcD0EzF/tb9Sx1d/nKEW6436EDqqb5u/Vxr86LxgX9RbhwkHI7e0XyX3YV64QzPrYGvEkXEuGsCQ==", "dependencies": { - "@cspell/cspell-bundled-dicts": "9.3.2", - "@cspell/cspell-pipe": "9.3.2", - "@cspell/cspell-resolver": "9.3.2", - "@cspell/cspell-types": "9.3.2", - "@cspell/dynamic-import": "9.3.2", - "@cspell/filetypes": "9.3.2", - "@cspell/strong-weak-map": "9.3.2", - "@cspell/url": "9.3.2", + "@cspell/cspell-bundled-dicts": "9.6.3", + "@cspell/cspell-performance-monitor": "9.6.3", + "@cspell/cspell-pipe": "9.6.3", + "@cspell/cspell-resolver": "9.6.3", + "@cspell/cspell-types": "9.6.3", + "@cspell/dynamic-import": "9.6.3", + "@cspell/filetypes": "9.6.3", + "@cspell/rpc": "9.6.3", + "@cspell/strong-weak-map": "9.6.3", + "@cspell/url": "9.6.3", "clear-module": "^4.1.2", - "cspell-config-lib": "9.3.2", - "cspell-dictionary": "9.3.2", - "cspell-glob": "9.3.2", - "cspell-grammar": "9.3.2", - "cspell-io": "9.3.2", - "cspell-trie-lib": "9.3.2", - "env-paths": "^3.0.0", + "cspell-config-lib": "9.6.3", + "cspell-dictionary": "9.6.3", + "cspell-glob": "9.6.3", + "cspell-grammar": "9.6.3", + "cspell-io": "9.6.3", + "cspell-trie-lib": "9.6.3", + "env-paths": "^4.0.0", "gensequence": "^8.0.8", "import-fresh": "^3.3.1", "resolve-from": "^5.0.0", @@ -701,24 +745,25 @@ } }, "node_modules/cspell-trie-lib": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/cspell-trie-lib/-/cspell-trie-lib-9.3.2.tgz", - "integrity": "sha512-1Af7Mq9jIccFQyJl/ZCcqQbtJwuDqpQVkk8xfs/92x4OI6gW1iTVRMtsrh0RTw1HZoR8aQD7tRRCiLPf/D+UiQ==", - "dependencies": { - "@cspell/cspell-pipe": "9.3.2", - "@cspell/cspell-types": "9.3.2", - "gensequence": "^8.0.8" - }, + "version": "9.6.3", + "resolved": "https://registry.npmjs.org/cspell-trie-lib/-/cspell-trie-lib-9.6.3.tgz", + "integrity": "sha512-bTlL3UjcBJyQXx67RMFcgHh0AXKd2w6Zw8iZkAcU7KVgAgRpGwCiF3LJLHignnMm+Pn8D3Xrx0raENaMh4uGaQ==", "engines": { "node": ">=20" + }, + "peerDependencies": { + "@cspell/cspell-types": "9.6.3" } }, "node_modules/env-paths": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-3.0.0.tgz", - "integrity": "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-4.0.0.tgz", + "integrity": "sha512-pxP8eL2SwwaTRi/KHYwLYXinDs7gL3jxFcBYmEdYfZmZXbaVDvdppd0XBU8qVz03rDfKZMXg1omHCbsJjZrMsw==", + "dependencies": { + "is-safe-filename": "^0.1.0" + }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -737,9 +782,9 @@ } }, "node_modules/fast-equals": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.3.3.tgz", - "integrity": "sha512-/boTcHZeIAQ2r/tL11voclBHDeP9WPxLt+tyAbVSyyXuUFyh0Tne7gJZTqGbxnvj79TjLdCXLOY7UIPhyG5MTw==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-6.0.0.tgz", + "integrity": "sha512-PFhhIGgdM79r5Uztdj9Zb6Tt1zKafqVfdMGwVca1z5z6fbX7DmsySSuJd8HiP6I1j505DCS83cLxo5rmSNeVEA==", "engines": { "node": ">=6.0.0" } @@ -845,6 +890,17 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/is-safe-filename": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-safe-filename/-/is-safe-filename-0.1.1.tgz", + "integrity": "sha512-4SrR7AdnY11LHfDKTZY1u6Ga3RuxZdl3YKWWShO5iyuG5h8QS4GD2tOb04peBJ5I7pXbR+CGBNEhTcwK+FzN3g==", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/parent-module": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-2.0.0.tgz", @@ -889,9 +945,9 @@ } }, "node_modules/smol-toml": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.5.2.tgz", - "integrity": "sha512-QlaZEqcAH3/RtNyet1IPIYPsEWAaYyXXv1Krsi+1L/QHppjX4Ifm8MQsBISz9vE8cHicIq3clogsheili5vhaQ==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.6.0.tgz", + "integrity": "sha512-4zemZi0HvTnYwLfrpk/CF9LOd9Lt87kAt50GnqhMpyF9U3poDAP2+iukq2bZsO/ufegbYehBkqINbsWxj4l4cw==", "engines": { "node": ">= 18" }, @@ -937,75 +993,78 @@ } }, "node_modules/yaml": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", - "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", + "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", "bin": { "yaml": "bin.mjs" }, "engines": { "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" } } }, "dependencies": { "@cspell/cspell-bundled-dicts": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/@cspell/cspell-bundled-dicts/-/cspell-bundled-dicts-9.3.2.tgz", - "integrity": "sha512-OmKzq/0FATHU671GKMzBrTyLdm25Wnziva7h4ylumVn1wnwWsXGef5bgXD7iuApqfqH9SzxsU0NtTB8m8vwEHQ==", + "version": "9.6.3", + "resolved": "https://registry.npmjs.org/@cspell/cspell-bundled-dicts/-/cspell-bundled-dicts-9.6.3.tgz", + "integrity": "sha512-0Ibx+ynlSd8wMAoUmc2Ck4SDN27eJheM45aCmCvCA8saROwkDh7j8zBz7aTTUTtFN+pcotJoNxr71LLD+KXhgQ==", "requires": { "@cspell/dict-ada": "^4.1.1", "@cspell/dict-al": "^1.1.1", - "@cspell/dict-aws": "^4.0.16", + "@cspell/dict-aws": "^4.0.17", "@cspell/dict-bash": "^4.2.2", - "@cspell/dict-companies": "^3.2.7", - "@cspell/dict-cpp": "^6.0.14", + "@cspell/dict-companies": "^3.2.10", + "@cspell/dict-cpp": "^7.0.2", "@cspell/dict-cryptocurrencies": "^5.0.5", - "@cspell/dict-csharp": "^4.0.7", - "@cspell/dict-css": "^4.0.18", - "@cspell/dict-dart": "^2.3.1", - "@cspell/dict-data-science": "^2.0.11", - "@cspell/dict-django": "^4.1.5", - "@cspell/dict-docker": "^1.1.16", - "@cspell/dict-dotnet": "^5.0.10", + "@cspell/dict-csharp": "^4.0.8", + "@cspell/dict-css": "^4.0.19", + "@cspell/dict-dart": "^2.3.2", + "@cspell/dict-data-science": "^2.0.13", + "@cspell/dict-django": "^4.1.6", + "@cspell/dict-docker": "^1.1.17", + "@cspell/dict-dotnet": "^5.0.11", "@cspell/dict-elixir": "^4.0.8", - "@cspell/dict-en_us": "^4.4.24", - "@cspell/dict-en-common-misspellings": "^2.1.8", - "@cspell/dict-en-gb-mit": "^3.1.14", - "@cspell/dict-filetypes": "^3.0.14", + "@cspell/dict-en_us": "^4.4.28", + "@cspell/dict-en-common-misspellings": "^2.1.12", + "@cspell/dict-en-gb-mit": "^3.1.17", + "@cspell/dict-filetypes": "^3.0.15", "@cspell/dict-flutter": "^1.1.1", "@cspell/dict-fonts": "^4.0.5", "@cspell/dict-fsharp": "^1.1.1", - "@cspell/dict-fullstack": "^3.2.7", + "@cspell/dict-fullstack": "^3.2.8", "@cspell/dict-gaming-terms": "^1.1.2", - "@cspell/dict-git": "^3.0.7", - "@cspell/dict-golang": "^6.0.24", + "@cspell/dict-git": "^3.1.0", + "@cspell/dict-golang": "^6.0.26", "@cspell/dict-google": "^1.0.9", "@cspell/dict-haskell": "^4.0.6", - "@cspell/dict-html": "^4.0.12", - "@cspell/dict-html-symbol-entities": "^4.0.4", + "@cspell/dict-html": "^4.0.14", + "@cspell/dict-html-symbol-entities": "^4.0.5", "@cspell/dict-java": "^5.0.12", "@cspell/dict-julia": "^1.1.1", "@cspell/dict-k8s": "^1.0.12", "@cspell/dict-kotlin": "^1.1.1", - "@cspell/dict-latex": "^4.0.4", + "@cspell/dict-latex": "^5.0.0", "@cspell/dict-lorem-ipsum": "^4.0.5", "@cspell/dict-lua": "^4.0.8", "@cspell/dict-makefile": "^1.0.5", - "@cspell/dict-markdown": "^2.0.12", - "@cspell/dict-monkeyc": "^1.0.11", - "@cspell/dict-node": "^5.0.8", - "@cspell/dict-npm": "^5.2.22", - "@cspell/dict-php": "^4.1.0", + "@cspell/dict-markdown": "^2.0.14", + "@cspell/dict-monkeyc": "^1.0.12", + "@cspell/dict-node": "^5.0.9", + "@cspell/dict-npm": "^5.2.32", + "@cspell/dict-php": "^4.1.1", "@cspell/dict-powershell": "^5.0.15", "@cspell/dict-public-licenses": "^2.0.15", - "@cspell/dict-python": "^4.2.21", + "@cspell/dict-python": "^4.2.25", "@cspell/dict-r": "^2.1.1", - "@cspell/dict-ruby": "^5.0.9", - "@cspell/dict-rust": "^4.0.12", - "@cspell/dict-scala": "^5.0.8", + "@cspell/dict-ruby": "^5.1.0", + "@cspell/dict-rust": "^4.1.2", + "@cspell/dict-scala": "^5.0.9", "@cspell/dict-shell": "^1.1.2", - "@cspell/dict-software-terms": "^5.1.13", + "@cspell/dict-software-terms": "^5.1.20", "@cspell/dict-sql": "^2.2.1", "@cspell/dict-svelte": "^1.0.7", "@cspell/dict-swift": "^2.0.6", @@ -1016,35 +1075,48 @@ } }, "@cspell/cspell-json-reporter": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/@cspell/cspell-json-reporter/-/cspell-json-reporter-9.3.2.tgz", - "integrity": "sha512-YRgpeHN9uY8kUlIw9q+8zJ0tRTAJMbfBTGzCq9Puah09NeMWlRMFPUkXVrkdic6NA7etboZ+zEdoZwRO9EmhiA==", + "version": "9.6.3", + "resolved": "https://registry.npmjs.org/@cspell/cspell-json-reporter/-/cspell-json-reporter-9.6.3.tgz", + "integrity": "sha512-4jhqXKGCFAQXomtk4/Ldy/GiGEocC61v8GISiUgn1mH+HPgEvkJijyxKRNFYvzXOoJr4y0KrkGBoKk5FI7cipw==", "requires": { - "@cspell/cspell-types": "9.3.2" + "@cspell/cspell-types": "9.6.3" } }, + "@cspell/cspell-performance-monitor": { + "version": "9.6.3", + "resolved": "https://registry.npmjs.org/@cspell/cspell-performance-monitor/-/cspell-performance-monitor-9.6.3.tgz", + "integrity": "sha512-Y1I0wg7MUTouubREyPeTJe2dKNbBZPBnd6kDNcVet5DRZ3Cck2udS9m1a6DhJO2sGQgHTBULPDBEUU5tcAZ+nQ==" + }, "@cspell/cspell-pipe": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/@cspell/cspell-pipe/-/cspell-pipe-9.3.2.tgz", - "integrity": "sha512-REF7ibG79WLEynIMUss/IRDCdYEb1nlE1rj/gt2CbPFzLa6t5MRwW2lajEvXS6/WgbMtsTVHAWi3ALqJzCwxng==" + "version": "9.6.3", + "resolved": "https://registry.npmjs.org/@cspell/cspell-pipe/-/cspell-pipe-9.6.3.tgz", + "integrity": "sha512-Q0M58JbgHWOOOYvSyBodEysXLP5t8Q58Ff08msj2/XxRFpowR4iIUyfg3k+/d06uyOy89l+RD5AmyTEa5410xg==" }, "@cspell/cspell-resolver": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/@cspell/cspell-resolver/-/cspell-resolver-9.3.2.tgz", - "integrity": "sha512-jLN2Aa/vxm8+IBvTd884SwPEfjxnDwIEPBT3hmqgLlKuUHQ3FMG27lsM4Ik9L2KWBXMgV/wGz4BaxfhKI41Ttw==", + "version": "9.6.3", + "resolved": "https://registry.npmjs.org/@cspell/cspell-resolver/-/cspell-resolver-9.6.3.tgz", + "integrity": "sha512-yUMTC8vl+LeRcIeXRrxn0GXL+rurf4OR3uyqZbG6bbtuttvq/Icleeibv9Wts+n700HLp+sVXJ7BrCn0Ehobsw==", "requires": { "global-directory": "^4.0.1" } }, "@cspell/cspell-service-bus": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/@cspell/cspell-service-bus/-/cspell-service-bus-9.3.2.tgz", - "integrity": "sha512-/rB8LazM0JzKL+AvZa5fEpLutmwy5QFMpzw8HJd+rDGkzb5r79hURWSRo84QArgaskUqA9XlOHSieDE9pt+WAA==" + "version": "9.6.3", + "resolved": "https://registry.npmjs.org/@cspell/cspell-service-bus/-/cspell-service-bus-9.6.3.tgz", + "integrity": "sha512-gDUDq0PcMcdSG/5HwaQzkptn29E0zCWJUqmP0W/4scSC80AVh/8hgws4tuqv1GWvr4MiBZhIbUEO4f3Pc58OYQ==" }, "@cspell/cspell-types": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/@cspell/cspell-types/-/cspell-types-9.3.2.tgz", - "integrity": "sha512-l4H8bMAmdzCbXHO8y1JZiAKszrPEiuLFKWrbhCacHF0iP+PIc/yuQp7cO70m0p70vArRfih6kgGyHFaCy47CfA==" + "version": "9.6.3", + "resolved": "https://registry.npmjs.org/@cspell/cspell-types/-/cspell-types-9.6.3.tgz", + "integrity": "sha512-oDTzZ2kG/jJk3TaupESUckDraGJbBpRBEW1xgTrWyIU+uGwdmMXPcGr9XZq9dRS5Zxm01nTgLwxpVM/3BVTvWQ==" + }, + "@cspell/cspell-worker": { + "version": "9.6.3", + "resolved": "https://registry.npmjs.org/@cspell/cspell-worker/-/cspell-worker-9.6.3.tgz", + "integrity": "sha512-zZccf1jLSrqIvlYAqwnnBi4RrLVfSEfWT4eYpiyIzHXHMzWnkUXEstQCG6LFK4L9A44M/w9s8szi7R4DLBzKsA==", + "requires": { + "cspell-lib": "9.6.3" + } }, "@cspell/dict-ada": { "version": "4.1.1", @@ -1057,9 +1129,9 @@ "integrity": "sha512-sD8GCaZetgQL4+MaJLXqbzWcRjfKVp8x+px3HuCaaiATAAtvjwUQ5/Iubiqwfd1boIh2Y1/3EgM3TLQ7Q8e0wQ==" }, "@cspell/dict-aws": { - "version": "4.0.16", - "resolved": "https://registry.npmjs.org/@cspell/dict-aws/-/dict-aws-4.0.16.tgz", - "integrity": "sha512-a681zShZbtTo947NvTYGLer95ZDQw1ROKvIFydak1e0OlfFCsNdtcYTupn0nbbYs53c9AO7G2DU8AcNEAnwXPA==" + "version": "4.0.17", + "resolved": "https://registry.npmjs.org/@cspell/dict-aws/-/dict-aws-4.0.17.tgz", + "integrity": "sha512-ORcblTWcdlGjIbWrgKF+8CNEBQiLVKdUOFoTn0KPNkAYnFcdPP0muT4892h7H4Xafh3j72wqB4/loQ6Nti9E/w==" }, "@cspell/dict-bash": { "version": "4.2.2", @@ -1070,14 +1142,14 @@ } }, "@cspell/dict-companies": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/@cspell/dict-companies/-/dict-companies-3.2.7.tgz", - "integrity": "sha512-fEyr3LmpFKTaD0LcRhB4lfW1AmULYBqzg4gWAV0dQCv06l+TsA+JQ+3pZJbUcoaZirtgsgT3dL3RUjmGPhUH0A==" + "version": "3.2.10", + "resolved": "https://registry.npmjs.org/@cspell/dict-companies/-/dict-companies-3.2.10.tgz", + "integrity": "sha512-bJ1qnO1DkTn7JYGXvxp8FRQc4yq6tRXnrII+jbP8hHmq5TX5o1Wu+rdfpoUQaMWTl6balRvcMYiINDesnpR9Bw==" }, "@cspell/dict-cpp": { - "version": "6.0.14", - "resolved": "https://registry.npmjs.org/@cspell/dict-cpp/-/dict-cpp-6.0.14.tgz", - "integrity": "sha512-dkmpSwvVfVdtoZ4mW/CK2Ep1v8mJlp6uiKpMNbSMOdJl4kq28nQS4vKNIX3B2bJa0Ha5iHHu+1mNjiLeO3g7Xg==" + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@cspell/dict-cpp/-/dict-cpp-7.0.2.tgz", + "integrity": "sha512-dfbeERiVNeqmo/npivdR6rDiBCqZi3QtjH2Z0HFcXwpdj6i97dX1xaKyK2GUsO/p4u1TOv63Dmj5Vm48haDpuA==" }, "@cspell/dict-cryptocurrencies": { "version": "5.0.5", @@ -1085,39 +1157,39 @@ "integrity": "sha512-R68hYYF/rtlE6T/dsObStzN5QZw+0aQBinAXuWCVqwdS7YZo0X33vGMfChkHaiCo3Z2+bkegqHlqxZF4TD3rUA==" }, "@cspell/dict-csharp": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/@cspell/dict-csharp/-/dict-csharp-4.0.7.tgz", - "integrity": "sha512-H16Hpu8O/1/lgijFt2lOk4/nnldFtQ4t8QHbyqphqZZVE5aS4J/zD/WvduqnLY21aKhZS6jo/xF5PX9jyqPKUA==" + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@cspell/dict-csharp/-/dict-csharp-4.0.8.tgz", + "integrity": "sha512-qmk45pKFHSxckl5mSlbHxmDitSsGMlk/XzFgt7emeTJWLNSTUK//MbYAkBNRtfzB4uD7pAFiKgpKgtJrTMRnrQ==" }, "@cspell/dict-css": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@cspell/dict-css/-/dict-css-4.0.18.tgz", - "integrity": "sha512-EF77RqROHL+4LhMGW5NTeKqfUd/e4OOv6EDFQ/UQQiFyWuqkEKyEz0NDILxOFxWUEVdjT2GQ2cC7t12B6pESwg==" + "version": "4.0.19", + "resolved": "https://registry.npmjs.org/@cspell/dict-css/-/dict-css-4.0.19.tgz", + "integrity": "sha512-VYHtPnZt/Zd/ATbW3rtexWpBnHUohUrQOHff/2JBhsVgxOrksAxJnLAO43Q1ayLJBJUUwNVo+RU0sx0aaysZfg==" }, "@cspell/dict-dart": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-dart/-/dict-dart-2.3.1.tgz", - "integrity": "sha512-xoiGnULEcWdodXI6EwVyqpZmpOoh8RA2Xk9BNdR7DLamV/QMvEYn8KJ7NlRiTSauJKPNkHHQ5EVHRM6sTS7jdg==" + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@cspell/dict-dart/-/dict-dart-2.3.2.tgz", + "integrity": "sha512-sUiLW56t9gfZcu8iR/5EUg+KYyRD83Cjl3yjDEA2ApVuJvK1HhX+vn4e4k4YfjpUQMag8XO2AaRhARE09+/rqw==" }, "@cspell/dict-data-science": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@cspell/dict-data-science/-/dict-data-science-2.0.11.tgz", - "integrity": "sha512-Dt+83nVCcF+dQyvFSaZjCKt1H5KbsVJFtH2X7VUfmIzQu8xCnV1fUmkhBzGJ+NiFs99Oy9JA6I9EjeqExzXk7g==" + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/@cspell/dict-data-science/-/dict-data-science-2.0.13.tgz", + "integrity": "sha512-l1HMEhBJkPmw4I2YGVu2eBSKM89K9pVF+N6qIr5Uo5H3O979jVodtuwP8I7LyPrJnC6nz28oxeGRCLh9xC5CVA==" }, "@cspell/dict-django": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@cspell/dict-django/-/dict-django-4.1.5.tgz", - "integrity": "sha512-AvTWu99doU3T8ifoMYOMLW2CXKvyKLukPh1auOPwFGHzueWYvBBN+OxF8wF7XwjTBMMeRleVdLh3aWCDEX/ZWg==" + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@cspell/dict-django/-/dict-django-4.1.6.tgz", + "integrity": "sha512-SdbSFDGy9ulETqNz15oWv2+kpWLlk8DJYd573xhIkeRdcXOjskRuxjSZPKfW7O3NxN/KEf3gm3IevVOiNuFS+w==" }, "@cspell/dict-docker": { - "version": "1.1.16", - "resolved": "https://registry.npmjs.org/@cspell/dict-docker/-/dict-docker-1.1.16.tgz", - "integrity": "sha512-UiVQ5RmCg6j0qGIxrBnai3pIB+aYKL3zaJGvXk1O/ertTKJif9RZikKXCEgqhaCYMweM4fuLqWSVmw3hU164Iw==" + "version": "1.1.17", + "resolved": "https://registry.npmjs.org/@cspell/dict-docker/-/dict-docker-1.1.17.tgz", + "integrity": "sha512-OcnVTIpHIYYKhztNTyK8ShAnXTfnqs43hVH6p0py0wlcwRIXe5uj4f12n7zPf2CeBI7JAlPjEsV0Rlf4hbz/xQ==" }, "@cspell/dict-dotnet": { - "version": "5.0.10", - "resolved": "https://registry.npmjs.org/@cspell/dict-dotnet/-/dict-dotnet-5.0.10.tgz", - "integrity": "sha512-ooar8BP/RBNP1gzYfJPStKEmpWy4uv/7JCq6FOnJLeD1yyfG3d/LFMVMwiJo+XWz025cxtkM3wuaikBWzCqkmg==" + "version": "5.0.11", + "resolved": "https://registry.npmjs.org/@cspell/dict-dotnet/-/dict-dotnet-5.0.11.tgz", + "integrity": "sha512-LSVKhpFf/ASTWJcfYeS0Sykcl1gVMsv2Z5Eo0TnTMSTLV3738HH+66pIsjUTChqU6SF3gKPuCe6EOaRYqb/evA==" }, "@cspell/dict-elixir": { "version": "4.0.8", @@ -1125,24 +1197,24 @@ "integrity": "sha512-CyfphrbMyl4Ms55Vzuj+mNmd693HjBFr9hvU+B2YbFEZprE5AG+EXLYTMRWrXbpds4AuZcvN3deM2XVB80BN/Q==" }, "@cspell/dict-en_us": { - "version": "4.4.24", - "resolved": "https://registry.npmjs.org/@cspell/dict-en_us/-/dict-en_us-4.4.24.tgz", - "integrity": "sha512-JE+/H2YicHJTneRmgH4GSI21rS+1yGZVl1jfOQgl8iHLC+yTTMtCvueNDMK94CgJACzYAoCsQB70MqiFJJfjLQ==" + "version": "4.4.28", + "resolved": "https://registry.npmjs.org/@cspell/dict-en_us/-/dict-en_us-4.4.28.tgz", + "integrity": "sha512-/rzhbZaZDsDWmXbc9Fmmr4/ngmaNcG2b+TGT+ZjGqpOXVQYI75yZ9+XduyI43xJ5O38QcX3QIbJY5GWaJqxPEg==" }, "@cspell/dict-en-common-misspellings": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@cspell/dict-en-common-misspellings/-/dict-en-common-misspellings-2.1.8.tgz", - "integrity": "sha512-vDsjRFPQGuAADAiitf82z9Mz3DcqKZi6V5hPAEIFkLLKjFVBcjUsSq59SfL59ElIFb76MtBO0BLifdEbBj+DoQ==" + "version": "2.1.12", + "resolved": "https://registry.npmjs.org/@cspell/dict-en-common-misspellings/-/dict-en-common-misspellings-2.1.12.tgz", + "integrity": "sha512-14Eu6QGqyksqOd4fYPuRb58lK1Va7FQK9XxFsRKnZU8LhL3N+kj7YKDW+7aIaAN/0WGEqslGP6lGbQzNti8Akw==" }, "@cspell/dict-en-gb-mit": { - "version": "3.1.14", - "resolved": "https://registry.npmjs.org/@cspell/dict-en-gb-mit/-/dict-en-gb-mit-3.1.14.tgz", - "integrity": "sha512-b+vEerlHP6rnNf30tmTJb7JZnOq4WAslYUvexOz/L3gDna9YJN3bAnwRJ3At3bdcOcMG7PTv3Pi+C73IR22lNg==" + "version": "3.1.17", + "resolved": "https://registry.npmjs.org/@cspell/dict-en-gb-mit/-/dict-en-gb-mit-3.1.17.tgz", + "integrity": "sha512-MLx+3XN9rj+EGwLIFmh0gpEDNalCyQqjcszp+WkedCHcvTCzQgWXQMZK6cPuhO/OKYyW9GOwsx4t0wjU5tRVNg==" }, "@cspell/dict-filetypes": { - "version": "3.0.14", - "resolved": "https://registry.npmjs.org/@cspell/dict-filetypes/-/dict-filetypes-3.0.14.tgz", - "integrity": "sha512-KSXaSMYYNMLLdHEnju1DyRRH3eQWPRYRnOXpuHUdOh2jC44VgQoxyMU7oB3NAhDhZKBPCihabzECsAGFbdKfEA==" + "version": "3.0.15", + "resolved": "https://registry.npmjs.org/@cspell/dict-filetypes/-/dict-filetypes-3.0.15.tgz", + "integrity": "sha512-uDMeqYlLlK476w/muEFQGBy9BdQWS0mQ7BJiy/iQv5XUWZxE2O54ZQd9nW8GyQMzAgoyg5SG4hf9l039Qt66oA==" }, "@cspell/dict-flutter": { "version": "1.1.1", @@ -1160,9 +1232,9 @@ "integrity": "sha512-imhs0u87wEA4/cYjgzS0tAyaJpwG7vwtC8UyMFbwpmtw+/bgss+osNfyqhYRyS/ehVCWL17Ewx2UPkexjKyaBA==" }, "@cspell/dict-fullstack": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/@cspell/dict-fullstack/-/dict-fullstack-3.2.7.tgz", - "integrity": "sha512-IxEk2YAwAJKYCUEgEeOg3QvTL4XLlyArJElFuMQevU1dPgHgzWElFevN5lsTFnvMFA1riYsVinqJJX0BanCFEg==" + "version": "3.2.8", + "resolved": "https://registry.npmjs.org/@cspell/dict-fullstack/-/dict-fullstack-3.2.8.tgz", + "integrity": "sha512-J6EeoeThvx/DFrcA2rJiCA6vfqwJMbkG0IcXhlsmRZmasIpanmxgt90OEaUazbZahFiuJT8wrhgQ1QgD1MsqBw==" }, "@cspell/dict-gaming-terms": { "version": "1.1.2", @@ -1170,14 +1242,14 @@ "integrity": "sha512-9XnOvaoTBscq0xuD6KTEIkk9hhdfBkkvJAIsvw3JMcnp1214OCGW8+kako5RqQ2vTZR3Tnf3pc57o7VgkM0q1Q==" }, "@cspell/dict-git": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@cspell/dict-git/-/dict-git-3.0.7.tgz", - "integrity": "sha512-odOwVKgfxCQfiSb+nblQZc4ErXmnWEnv8XwkaI4sNJ7cNmojnvogYVeMqkXPjvfrgEcizEEA4URRD2Ms5PDk1w==" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-git/-/dict-git-3.1.0.tgz", + "integrity": "sha512-KEt9zGkxqGy2q1nwH4CbyqTSv5nadpn8BAlDnzlRcnL0Xb3LX9xTgSGShKvzb0bw35lHoYyLWN2ZKAqbC4pgGQ==" }, "@cspell/dict-golang": { - "version": "6.0.24", - "resolved": "https://registry.npmjs.org/@cspell/dict-golang/-/dict-golang-6.0.24.tgz", - "integrity": "sha512-rY7PlC3MsHozmjrZWi0HQPUl0BVCV0+mwK0rnMT7pOIXqOe4tWCYMULDIsEk4F0gbIxb5badd2dkCPDYjLnDgA==" + "version": "6.0.26", + "resolved": "https://registry.npmjs.org/@cspell/dict-golang/-/dict-golang-6.0.26.tgz", + "integrity": "sha512-YKA7Xm5KeOd14v5SQ4ll6afe9VSy3a2DWM7L9uBq4u3lXToRBQ1W5PRa+/Q9udd+DTURyVVnQ+7b9cnOlNxaRg==" }, "@cspell/dict-google": { "version": "1.0.9", @@ -1190,14 +1262,14 @@ "integrity": "sha512-ib8SA5qgftExpYNjWhpYIgvDsZ/0wvKKxSP+kuSkkak520iPvTJumEpIE+qPcmJQo4NzdKMN8nEfaeci4OcFAQ==" }, "@cspell/dict-html": { - "version": "4.0.12", - "resolved": "https://registry.npmjs.org/@cspell/dict-html/-/dict-html-4.0.12.tgz", - "integrity": "sha512-JFffQ1dDVEyJq6tCDWv0r/RqkdSnV43P2F/3jJ9rwLgdsOIXwQbXrz6QDlvQLVvNSnORH9KjDtenFTGDyzfCaA==" + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/@cspell/dict-html/-/dict-html-4.0.14.tgz", + "integrity": "sha512-2bf7n+kS92g+cMKV0wr9o/Oq9n8JzU7CcrB96gIh2GHgnF+0xDOqO2W/1KeFAqOfqosoOVE48t+4dnEMkkoJ2Q==" }, "@cspell/dict-html-symbol-entities": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@cspell/dict-html-symbol-entities/-/dict-html-symbol-entities-4.0.4.tgz", - "integrity": "sha512-afea+0rGPDeOV9gdO06UW183Qg6wRhWVkgCFwiO3bDupAoyXRuvupbb5nUyqSTsLXIKL8u8uXQlJ9pkz07oVXw==" + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@cspell/dict-html-symbol-entities/-/dict-html-symbol-entities-4.0.5.tgz", + "integrity": "sha512-429alTD4cE0FIwpMucvSN35Ld87HCyuM8mF731KU5Rm4Je2SG6hmVx7nkBsLyrmH3sQukTcr1GaiZsiEg8svPA==" }, "@cspell/dict-java": { "version": "5.0.12", @@ -1220,9 +1292,9 @@ "integrity": "sha512-J3NzzfgmxRvEeOe3qUXnSJQCd38i/dpF9/t3quuWh6gXM+krsAXP75dY1CzDmS8mrJAlBdVBeAW5eAZTD8g86Q==" }, "@cspell/dict-latex": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@cspell/dict-latex/-/dict-latex-4.0.4.tgz", - "integrity": "sha512-YdTQhnTINEEm/LZgTzr9Voz4mzdOXH7YX+bSFs3hnkUHCUUtX/mhKgf1CFvZ0YNM2afjhQcmLaR9bDQVyYBvpA==" + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-latex/-/dict-latex-5.0.0.tgz", + "integrity": "sha512-HUrIqUVohM6P0+5b7BsdAdb0STIv0aaFBvguI7pLcreljlcX3FSPUxea7ticzNlCNeVrEaiEn/ws9m6rYUeuNw==" }, "@cspell/dict-lorem-ipsum": { "version": "4.0.5", @@ -1240,30 +1312,30 @@ "integrity": "sha512-4vrVt7bGiK8Rx98tfRbYo42Xo2IstJkAF4tLLDMNQLkQ86msDlYSKG1ZCk8Abg+EdNcFAjNhXIiNO+w4KflGAQ==" }, "@cspell/dict-markdown": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/@cspell/dict-markdown/-/dict-markdown-2.0.12.tgz", - "integrity": "sha512-ufwoliPijAgWkD/ivAMC+A9QD895xKiJRF/fwwknQb7kt7NozTLKFAOBtXGPJAB4UjhGBpYEJVo2elQ0FCAH9A==", + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/@cspell/dict-markdown/-/dict-markdown-2.0.14.tgz", + "integrity": "sha512-uLKPNJsUcumMQTsZZgAK9RgDLyQhUz/uvbQTEkvF/Q4XfC1i/BnA8XrOrd0+Vp6+tPOKyA+omI5LRWfMu5K/Lw==", "requires": {} }, "@cspell/dict-monkeyc": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@cspell/dict-monkeyc/-/dict-monkeyc-1.0.11.tgz", - "integrity": "sha512-7Q1Ncu0urALI6dPTrEbSTd//UK0qjRBeaxhnm8uY5fgYNFYAG+u4gtnTIo59S6Bw5P++4H3DiIDYoQdY/lha8w==" + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@cspell/dict-monkeyc/-/dict-monkeyc-1.0.12.tgz", + "integrity": "sha512-MN7Vs11TdP5mbdNFQP5x2Ac8zOBm97ARg6zM5Sb53YQt/eMvXOMvrep7+/+8NJXs0jkp70bBzjqU4APcqBFNAw==" }, "@cspell/dict-node": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/@cspell/dict-node/-/dict-node-5.0.8.tgz", - "integrity": "sha512-AirZcN2i84ynev3p2/1NCPEhnNsHKMz9zciTngGoqpdItUb2bDt1nJBjwlsrFI78GZRph/VaqTVFwYikmncpXg==" + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/@cspell/dict-node/-/dict-node-5.0.9.tgz", + "integrity": "sha512-hO+ga+uYZ/WA4OtiMEyKt5rDUlUyu3nXMf8KVEeqq2msYvAPdldKBGH7lGONg6R/rPhv53Rb+0Y1SLdoK1+7wQ==" }, "@cspell/dict-npm": { - "version": "5.2.22", - "resolved": "https://registry.npmjs.org/@cspell/dict-npm/-/dict-npm-5.2.22.tgz", - "integrity": "sha512-bepGmmRv7KmxULqfw88G9wxkpIAQA96YyrfKpfg4RHnQLxpmzoRnZGtK5S9JH7Hlf5LEd1gkOF7dtCE6cek67w==" + "version": "5.2.32", + "resolved": "https://registry.npmjs.org/@cspell/dict-npm/-/dict-npm-5.2.32.tgz", + "integrity": "sha512-H0XD0eg4d96vevle8VUKVoPhsgsw003ByJ47XzipyiMKoQTZ2IAUW+VTkQq8wU1floarNjmThQJOoKL9J4UYuw==" }, "@cspell/dict-php": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-php/-/dict-php-4.1.0.tgz", - "integrity": "sha512-dTDeabyOj7eFvn2Q4Za3uVXM2+SzeFMqX8ly2P0XTo4AzbCmI2hulFD/QIADwWmwiRrInbbf8cxwFHNIYrXl4w==" + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-php/-/dict-php-4.1.1.tgz", + "integrity": "sha512-EXelI+4AftmdIGtA8HL8kr4WlUE11OqCSVlnIgZekmTkEGSZdYnkFdiJ5IANSALtlQ1mghKjz+OFqVs6yowgWA==" }, "@cspell/dict-powershell": { "version": "5.0.15", @@ -1276,11 +1348,11 @@ "integrity": "sha512-cJEOs901H13Pfy0fl4dCD1U+xpWIMaEPq8MeYU83FfDZvellAuSo4GqWCripfIqlhns/L6+UZEIJSOZnjgy7Wg==" }, "@cspell/dict-python": { - "version": "4.2.21", - "resolved": "https://registry.npmjs.org/@cspell/dict-python/-/dict-python-4.2.21.tgz", - "integrity": "sha512-M9OgwXWhpZqEZqKU2psB2DFsT8q5SwEahkQeIpNIRWIErjwG7I9yYhhfvPz6s5gMCMhhb3hqcPJTnmdgqGrQyg==", + "version": "4.2.25", + "resolved": "https://registry.npmjs.org/@cspell/dict-python/-/dict-python-4.2.25.tgz", + "integrity": "sha512-hDdN0YhKgpbtZVRjQ2c8jk+n0wQdidAKj1Fk8w7KEHb3YlY5uPJ0mAKJk7AJKPNLOlILoUmN+HAVJz+cfSbWYg==", "requires": { - "@cspell/dict-data-science": "^2.0.11" + "@cspell/dict-data-science": "^2.0.13" } }, "@cspell/dict-r": { @@ -1289,19 +1361,19 @@ "integrity": "sha512-71Ka+yKfG4ZHEMEmDxc6+blFkeTTvgKbKAbwiwQAuKl3zpqs1Y0vUtwW2N4b3LgmSPhV3ODVY0y4m5ofqDuKMw==" }, "@cspell/dict-ruby": { - "version": "5.0.9", - "resolved": "https://registry.npmjs.org/@cspell/dict-ruby/-/dict-ruby-5.0.9.tgz", - "integrity": "sha512-H2vMcERMcANvQshAdrVx0XoWaNX8zmmiQN11dZZTQAZaNJ0xatdJoSqY8C8uhEMW89bfgpN+NQgGuDXW2vmXEw==" + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-ruby/-/dict-ruby-5.1.0.tgz", + "integrity": "sha512-9PJQB3cfkBULrMLp5kSAcFPpzf8oz9vFN+QYZABhQwWkGbuzCIXSorHrmWSASlx4yejt3brjaWS57zZ/YL5ZQQ==" }, "@cspell/dict-rust": { - "version": "4.0.12", - "resolved": "https://registry.npmjs.org/@cspell/dict-rust/-/dict-rust-4.0.12.tgz", - "integrity": "sha512-z2QiH+q9UlNhobBJArvILRxV8Jz0pKIK7gqu4TgmEYyjiu1TvnGZ1tbYHeu9w3I/wOP6UMDoCBTty5AlYfW0mw==" + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@cspell/dict-rust/-/dict-rust-4.1.2.tgz", + "integrity": "sha512-O1FHrumYcO+HZti3dHfBPUdnDFkI+nbYK3pxYmiM1sr+G0ebOd6qchmswS0Wsc6ZdEVNiPYJY/gZQR6jfW3uOg==" }, "@cspell/dict-scala": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/@cspell/dict-scala/-/dict-scala-5.0.8.tgz", - "integrity": "sha512-YdftVmumv8IZq9zu1gn2U7A4bfM2yj9Vaupydotyjuc+EEZZSqAafTpvW/jKLWji2TgybM1L2IhmV0s/Iv9BTw==" + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/@cspell/dict-scala/-/dict-scala-5.0.9.tgz", + "integrity": "sha512-AjVcVAELgllybr1zk93CJ5wSUNu/Zb5kIubymR/GAYkMyBdYFCZ3Zbwn4Zz8GJlFFAbazABGOu0JPVbeY59vGg==" }, "@cspell/dict-shell": { "version": "1.1.2", @@ -1309,9 +1381,9 @@ "integrity": "sha512-WqOUvnwcHK1X61wAfwyXq04cn7KYyskg90j4lLg3sGGKMW9Sq13hs91pqrjC44Q+lQLgCobrTkMDw9Wyl9nRFA==" }, "@cspell/dict-software-terms": { - "version": "5.1.13", - "resolved": "https://registry.npmjs.org/@cspell/dict-software-terms/-/dict-software-terms-5.1.13.tgz", - "integrity": "sha512-bmWMOpqzCmmKFHM9hFC6o+GP20t2+uTuwksR3a6crHQqvNv3revzwQHorsWvSYx75xsdcvRYrAz7PqPEV/FG1g==" + "version": "5.1.20", + "resolved": "https://registry.npmjs.org/@cspell/dict-software-terms/-/dict-software-terms-5.1.20.tgz", + "integrity": "sha512-TEk1xHvetTI4pv7Vzje1D322m6QEjaH2P6ucOOf6q7EJCppQIdC0lZSXkgHJAFU5HGSvEXSzvnVeW2RHW86ziQ==" }, "@cspell/dict-sql": { "version": "2.2.1", @@ -1349,28 +1421,38 @@ "integrity": "sha512-XibBIxBlVosU06+M6uHWkFeT0/pW5WajDRYdXG2CgHnq85b0TI/Ks0FuBJykmsgi2CAD3Qtx8UHFEtl/DSFnAQ==" }, "@cspell/dynamic-import": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/@cspell/dynamic-import/-/dynamic-import-9.3.2.tgz", - "integrity": "sha512-au7FyuIHUNI2r9sO3pUBKVTeD/v7c9x/nPUStaAK1bG4rdKt4w+/jUY2IaldAraW5w29z528BboXbiV87SM1kw==", + "version": "9.6.3", + "resolved": "https://registry.npmjs.org/@cspell/dynamic-import/-/dynamic-import-9.6.3.tgz", + "integrity": "sha512-J2RArbv02H0fZCZxfGe2RjhEBFrcBlWgEESh2kDdF3CyoXBoPneevS5L3X4dk2XpOR0LU6bCJEsVIuzJqoFDbw==", "requires": { - "@cspell/url": "9.3.2", + "@cspell/url": "9.6.3", "import-meta-resolve": "^4.2.0" } }, "@cspell/filetypes": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/@cspell/filetypes/-/filetypes-9.3.2.tgz", - "integrity": "sha512-0bUxQlmJPRHZrRQD7adbc4lFizO8tGD/6+1cBgU3kV3+NVrpr12y4jU8twCSChhYibZyPr7bnvhkM3cQgb8RzA==" + "version": "9.6.3", + "resolved": "https://registry.npmjs.org/@cspell/filetypes/-/filetypes-9.6.3.tgz", + "integrity": "sha512-FocAActFpUanhQblokOQYlsV3yt3xC6FNMnziGURG9f1a5LYPGwmb6v40qkkhsTwK8L9L7Ai1KRtPJ3zyowNCA==" + }, + "@cspell/rpc": { + "version": "9.6.3", + "resolved": "https://registry.npmjs.org/@cspell/rpc/-/rpc-9.6.3.tgz", + "integrity": "sha512-D8ouhYPOZR11CI66GKsFu/05sSTSjxwfyUsTZ0m7lQOcILxkJ0P/y4DJ9HffFT6uY62mEbxDeibvlfCLBzWO4w==" }, "@cspell/strong-weak-map": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/@cspell/strong-weak-map/-/strong-weak-map-9.3.2.tgz", - "integrity": "sha512-pFcmOTWCoFMRETb9PCkCmaiZiLb5i2qOZmGH/p/tFEH8kIYhMGfhaulnXwKwS+Ke6PKceQd2YL98bGmo8hL4aQ==" + "version": "9.6.3", + "resolved": "https://registry.npmjs.org/@cspell/strong-weak-map/-/strong-weak-map-9.6.3.tgz", + "integrity": "sha512-2OmE4RCSJG1mAfVsZqEvThJaQY//rYjDBycHfb1pfLb3KlV+O5jNKlYrI6E/+JpiTb1cI9MnzIRw6uzTPgCN1g==" }, "@cspell/url": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/@cspell/url/-/url-9.3.2.tgz", - "integrity": "sha512-TobUlZl7Z7VehhNOMNAg1ABuGizieseftlG94OZJ934JptOhK8TC/1o2ldKrbDH50jyt6E7rPTMV2BW/vWuTzQ==" + "version": "9.6.3", + "resolved": "https://registry.npmjs.org/@cspell/url/-/url-9.6.3.tgz", + "integrity": "sha512-k+o5Dp8Hp8mjex8Lpe9m9B4LsrMoEzAaaYfvagUPQW1BKPwi3ugkH9YkC7uUOvzD6hOWq77WJZ2t946zY2NDSg==" + }, + "ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==" }, "array-timsort": { "version": "1.0.3", @@ -1405,14 +1487,14 @@ } }, "commander": { - "version": "14.0.2", - "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz", - "integrity": "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==" + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", + "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==" }, "comment-json": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.4.1.tgz", - "integrity": "sha512-r1To31BQD5060QdkC+Iheai7gHwoSZobzunqkf2/kQ6xIAfJyrKNAFUwdKvkK7Qgu7pVTKQEa7ok7Ed3ycAJgg==", + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.5.1.tgz", + "integrity": "sha512-taEtr3ozUmOB7it68Jll7s0Pwm+aoiHyXKrEC8SEodL4rNpdfDLqa7PfBlrgFoCNNdR8ImL+muti5IGvktJAAg==", "requires": { "array-timsort": "^1.0.3", "core-util-is": "^1.0.3", @@ -1425,24 +1507,27 @@ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" }, "cspell": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/cspell/-/cspell-9.3.2.tgz", - "integrity": "sha512-3xFyVSTYrYa/QJzLfzsCRMkMXqOsytP8E26DuGrVMJQoLPFmbOXNNtnMu4wrtr17QVloxpvutW77U4vb2L/LDQ==", + "version": "9.6.3", + "resolved": "https://registry.npmjs.org/cspell/-/cspell-9.6.3.tgz", + "integrity": "sha512-eGfq/qCJXDNLtqvwo3RFOJ8ECQD45NSEqlEdxJPdMlu/MA7YXSm4QptkJTybdodZmQpIGNpAz8UtVjKwhseqZg==", "requires": { - "@cspell/cspell-json-reporter": "9.3.2", - "@cspell/cspell-pipe": "9.3.2", - "@cspell/cspell-types": "9.3.2", - "@cspell/dynamic-import": "9.3.2", - "@cspell/url": "9.3.2", + "@cspell/cspell-json-reporter": "9.6.3", + "@cspell/cspell-performance-monitor": "9.6.3", + "@cspell/cspell-pipe": "9.6.3", + "@cspell/cspell-types": "9.6.3", + "@cspell/cspell-worker": "9.6.3", + "@cspell/dynamic-import": "9.6.3", + "@cspell/url": "9.6.3", + "ansi-regex": "^6.2.2", "chalk": "^5.6.2", "chalk-template": "^1.1.2", - "commander": "^14.0.2", - "cspell-config-lib": "9.3.2", - "cspell-dictionary": "9.3.2", - "cspell-gitignore": "9.3.2", - "cspell-glob": "9.3.2", - "cspell-io": "9.3.2", - "cspell-lib": "9.3.2", + "commander": "^14.0.3", + "cspell-config-lib": "9.6.3", + "cspell-dictionary": "9.6.3", + "cspell-gitignore": "9.6.3", + "cspell-glob": "9.6.3", + "cspell-io": "9.6.3", + "cspell-lib": "9.6.3", "fast-json-stable-stringify": "^2.1.0", "flatted": "^3.3.3", "semver": "^7.7.3", @@ -1450,85 +1535,88 @@ } }, "cspell-config-lib": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/cspell-config-lib/-/cspell-config-lib-9.3.2.tgz", - "integrity": "sha512-zXhmA4rqgWQRTVijI+g/mgiep76TvTO4d+P3CHwcqLG57BKVzoW+jkO4qDLC+Neh4b8+CcNWEIr3w16BfuEJAA==", + "version": "9.6.3", + "resolved": "https://registry.npmjs.org/cspell-config-lib/-/cspell-config-lib-9.6.3.tgz", + "integrity": "sha512-gQnpAzyf7rPaIb2hjVDtkvfhNc+7hAeHjK+RFAHj7fXiMfdPOWMxnl5IZBOdd9aYHZzVbxYkeyvJbLwF4M3YSg==", "requires": { - "@cspell/cspell-types": "9.3.2", - "comment-json": "^4.4.1", - "smol-toml": "^1.5.2", - "yaml": "^2.8.1" + "@cspell/cspell-types": "9.6.3", + "comment-json": "^4.5.1", + "smol-toml": "^1.6.0", + "yaml": "^2.8.2" } }, "cspell-dictionary": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/cspell-dictionary/-/cspell-dictionary-9.3.2.tgz", - "integrity": "sha512-E3YhOhZzZt1a+AEbFV2B3THCyZ576PDg0mDNUDrU1Y65SyIhf4DC6itfPoAb6R3FI/DI218RqWZg/FTT8lJ2gA==", + "version": "9.6.3", + "resolved": "https://registry.npmjs.org/cspell-dictionary/-/cspell-dictionary-9.6.3.tgz", + "integrity": "sha512-y5te3Wu89WIdUYPYqMKUliviFxSu7xImuEacct6f2XFQd2c4G9vNduRMJjHKJT49cwkRM/fZKtOzJyPy8jzQ4g==", "requires": { - "@cspell/cspell-pipe": "9.3.2", - "@cspell/cspell-types": "9.3.2", - "cspell-trie-lib": "9.3.2", - "fast-equals": "^5.3.3" + "@cspell/cspell-performance-monitor": "9.6.3", + "@cspell/cspell-pipe": "9.6.3", + "@cspell/cspell-types": "9.6.3", + "cspell-trie-lib": "9.6.3", + "fast-equals": "^6.0.0" } }, "cspell-gitignore": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/cspell-gitignore/-/cspell-gitignore-9.3.2.tgz", - "integrity": "sha512-G2bLR+Dfb9GX4Sdm75GfCCa9V/sQYkRbLckuCuVmJxvcDB0xfczAtb6TfAXIziF3oUI6cOB1g+PoNLWBelcK5w==", + "version": "9.6.3", + "resolved": "https://registry.npmjs.org/cspell-gitignore/-/cspell-gitignore-9.6.3.tgz", + "integrity": "sha512-S6XAddSjGEP7opCfEkhWaTnNVCI8ypdyZWgOW/gUW3VMpq8zK7OJ/ddyOLUCIMW1Qwtkx7X4Mf06xJSBWEjKjQ==", "requires": { - "@cspell/url": "9.3.2", - "cspell-glob": "9.3.2", - "cspell-io": "9.3.2" + "@cspell/url": "9.6.3", + "cspell-glob": "9.6.3", + "cspell-io": "9.6.3" } }, "cspell-glob": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/cspell-glob/-/cspell-glob-9.3.2.tgz", - "integrity": "sha512-TuSupENEKyOCupOUZ3vnPxaTOghxY/rD1JIkb8e5kjzRprYVilO/rYqEk/52iLwJVd+4Npe8fNhR3KhU7u/UUg==", + "version": "9.6.3", + "resolved": "https://registry.npmjs.org/cspell-glob/-/cspell-glob-9.6.3.tgz", + "integrity": "sha512-tcyQP0LXoAEExVzWZMxW4afyo7w/8nea0wskoWQ4G+k36Gk6P52CARARFymHCH4IZLbbybZuLoVDxQ6OgIm3fQ==", "requires": { - "@cspell/url": "9.3.2", + "@cspell/url": "9.6.3", "picomatch": "^4.0.3" } }, "cspell-grammar": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/cspell-grammar/-/cspell-grammar-9.3.2.tgz", - "integrity": "sha512-ysonrFu9vJvF/derDlEjUfmvLeCfNOWPh00t6Yh093AKrJFoWQiyaS/5bEN/uB5/n1sa4k3ItnWvuTp3+YuZsA==", + "version": "9.6.3", + "resolved": "https://registry.npmjs.org/cspell-grammar/-/cspell-grammar-9.6.3.tgz", + "integrity": "sha512-26Pn/zo9hY3/5hGkM4tpV/Nnn4Op1J0DGnA34vA/lDBGtDOFexUEcUHZOieuNtCvZO735ltZPrP3zVxts1CwRg==", "requires": { - "@cspell/cspell-pipe": "9.3.2", - "@cspell/cspell-types": "9.3.2" + "@cspell/cspell-pipe": "9.6.3", + "@cspell/cspell-types": "9.6.3" } }, "cspell-io": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/cspell-io/-/cspell-io-9.3.2.tgz", - "integrity": "sha512-ahoULCp0j12TyXXmIcdO/7x65A/2mzUQO1IkOC65OXEbNT+evt0yswSO5Nr1F6kCHDuEKc46EZWwsYAzj78pMg==", + "version": "9.6.3", + "resolved": "https://registry.npmjs.org/cspell-io/-/cspell-io-9.6.3.tgz", + "integrity": "sha512-dZYowJVTWPEjik3y/UrJnX/3PG20N1jgnP//lwX6cLGtHjv05W89lnHxsh5Suzxf3mkN/YZ1JUgmxdjj9HduNw==", "requires": { - "@cspell/cspell-service-bus": "9.3.2", - "@cspell/url": "9.3.2" + "@cspell/cspell-service-bus": "9.6.3", + "@cspell/url": "9.6.3" } }, "cspell-lib": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/cspell-lib/-/cspell-lib-9.3.2.tgz", - "integrity": "sha512-kdk11kib68zNANNICuOA8h4oA9kENQUAdeX/uvT4+7eHbHHV8WSgjXm4k4o/pRIbg164UJTX/XxKb/65ftn5jw==", + "version": "9.6.3", + "resolved": "https://registry.npmjs.org/cspell-lib/-/cspell-lib-9.6.3.tgz", + "integrity": "sha512-LFZsCWJX5EjcD0EzF/tb9Sx1d/nKEW6436EDqqb5u/Vxr86LxgX9RbhwkHI7e0XyX3YV64QzPrYGvEkXEuGsCQ==", "requires": { - "@cspell/cspell-bundled-dicts": "9.3.2", - "@cspell/cspell-pipe": "9.3.2", - "@cspell/cspell-resolver": "9.3.2", - "@cspell/cspell-types": "9.3.2", - "@cspell/dynamic-import": "9.3.2", - "@cspell/filetypes": "9.3.2", - "@cspell/strong-weak-map": "9.3.2", - "@cspell/url": "9.3.2", + "@cspell/cspell-bundled-dicts": "9.6.3", + "@cspell/cspell-performance-monitor": "9.6.3", + "@cspell/cspell-pipe": "9.6.3", + "@cspell/cspell-resolver": "9.6.3", + "@cspell/cspell-types": "9.6.3", + "@cspell/dynamic-import": "9.6.3", + "@cspell/filetypes": "9.6.3", + "@cspell/rpc": "9.6.3", + "@cspell/strong-weak-map": "9.6.3", + "@cspell/url": "9.6.3", "clear-module": "^4.1.2", - "cspell-config-lib": "9.3.2", - "cspell-dictionary": "9.3.2", - "cspell-glob": "9.3.2", - "cspell-grammar": "9.3.2", - "cspell-io": "9.3.2", - "cspell-trie-lib": "9.3.2", - "env-paths": "^3.0.0", + "cspell-config-lib": "9.6.3", + "cspell-dictionary": "9.6.3", + "cspell-glob": "9.6.3", + "cspell-grammar": "9.6.3", + "cspell-io": "9.6.3", + "cspell-trie-lib": "9.6.3", + "env-paths": "^4.0.0", "gensequence": "^8.0.8", "import-fresh": "^3.3.1", "resolve-from": "^5.0.0", @@ -1538,19 +1626,18 @@ } }, "cspell-trie-lib": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/cspell-trie-lib/-/cspell-trie-lib-9.3.2.tgz", - "integrity": "sha512-1Af7Mq9jIccFQyJl/ZCcqQbtJwuDqpQVkk8xfs/92x4OI6gW1iTVRMtsrh0RTw1HZoR8aQD7tRRCiLPf/D+UiQ==", - "requires": { - "@cspell/cspell-pipe": "9.3.2", - "@cspell/cspell-types": "9.3.2", - "gensequence": "^8.0.8" - } + "version": "9.6.3", + "resolved": "https://registry.npmjs.org/cspell-trie-lib/-/cspell-trie-lib-9.6.3.tgz", + "integrity": "sha512-bTlL3UjcBJyQXx67RMFcgHh0AXKd2w6Zw8iZkAcU7KVgAgRpGwCiF3LJLHignnMm+Pn8D3Xrx0raENaMh4uGaQ==", + "requires": {} }, "env-paths": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-3.0.0.tgz", - "integrity": "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-4.0.0.tgz", + "integrity": "sha512-pxP8eL2SwwaTRi/KHYwLYXinDs7gL3jxFcBYmEdYfZmZXbaVDvdppd0XBU8qVz03rDfKZMXg1omHCbsJjZrMsw==", + "requires": { + "is-safe-filename": "^0.1.0" + } }, "esprima": { "version": "4.0.1", @@ -1558,9 +1645,9 @@ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" }, "fast-equals": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.3.3.tgz", - "integrity": "sha512-/boTcHZeIAQ2r/tL11voclBHDeP9WPxLt+tyAbVSyyXuUFyh0Tne7gJZTqGbxnvj79TjLdCXLOY7UIPhyG5MTw==" + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-6.0.0.tgz", + "integrity": "sha512-PFhhIGgdM79r5Uztdj9Zb6Tt1zKafqVfdMGwVca1z5z6fbX7DmsySSuJd8HiP6I1j505DCS83cLxo5rmSNeVEA==" }, "fast-json-stable-stringify": { "version": "2.1.0", @@ -1625,6 +1712,11 @@ "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz", "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==" }, + "is-safe-filename": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-safe-filename/-/is-safe-filename-0.1.1.tgz", + "integrity": "sha512-4SrR7AdnY11LHfDKTZY1u6Ga3RuxZdl3YKWWShO5iyuG5h8QS4GD2tOb04peBJ5I7pXbR+CGBNEhTcwK+FzN3g==" + }, "parent-module": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-2.0.0.tgz", @@ -1649,9 +1741,9 @@ "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==" }, "smol-toml": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.5.2.tgz", - "integrity": "sha512-QlaZEqcAH3/RtNyet1IPIYPsEWAaYyXXv1Krsi+1L/QHppjX4Ifm8MQsBISz9vE8cHicIq3clogsheili5vhaQ==" + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.6.0.tgz", + "integrity": "sha512-4zemZi0HvTnYwLfrpk/CF9LOd9Lt87kAt50GnqhMpyF9U3poDAP2+iukq2bZsO/ufegbYehBkqINbsWxj4l4cw==" }, "tinyglobby": { "version": "0.2.15", @@ -1678,9 +1770,9 @@ "integrity": "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==" }, "yaml": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", - "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==" + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", + "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==" } } } diff --git a/eng/common/spelling/package.json b/eng/common/spelling/package.json index 5e906545311..11e19df811c 100644 --- a/eng/common/spelling/package.json +++ b/eng/common/spelling/package.json @@ -2,6 +2,6 @@ "name": "cspell-version-pin", "version": "1.0.0", "dependencies": { - "cspell": "^9.3.2" + "cspell": "^9.6.3" } } diff --git a/eng/common/testproxy/dotnet-devcert.crt b/eng/common/testproxy/dotnet-devcert.crt index 764cf84c6c0..931b6e73972 100644 --- a/eng/common/testproxy/dotnet-devcert.crt +++ b/eng/common/testproxy/dotnet-devcert.crt @@ -1,21 +1,21 @@ -----BEGIN CERTIFICATE----- -MIIDZzCCAk+gAwIBAgIUUGsnTw0cjASpYrNVxo7S41oMGtUwDQYJKoZIhvcNAQEL -BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI0MDcyODA2NTAzOFoXDTI1MDcy -ODA2NTAzOFowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF -AAOCAQ8AMIIBCgKCAQEAs9w2heE1VvnZcD3aR3jLgI/tiwjJScf+ljOMor9lxcIA -80NvcnfH3T7eH4Gsk0xcQ+J94FpFC5sDcsumcuEV1W6flvqj9k8vASoBpvpkoUnA -tr6aChLEp5hpwz59NAhdLrzd1eU+PNtB2CS0Blb6OEKUsVvoscmP5BwOWe+fuAdW -rQisHYrYzT9K5oWQA+AntrRJPbow7LCGQtT1viIlqxKaI8mTor+aRB0AmrAMJlPZ -4scmrpNs7+ertRVTz+uZ6HWHN5CJsVKnFKgTTuYYustRV2Oz9tF4W0+j1fBKCkYM -WQdga3md/9t51mG0naezJNDsMlT45IFOadqOozqsswIDAQABo4GwMIGtMA8GA1Ud +MIIDZzCCAk+gAwIBAgIUXUCvBB5U6rYQyAEu5rApzjp3RQYwDQYJKoZIhvcNAQEL +BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI1MDcyMzE3NTUyM1oXDTI2MDcy +MzE3NTUyM1owFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEA67HYsBPB865zJS8TDwUBgFSKqWgJ7dGTQh3aTlvamFED +5/kHnL7oTr3x/6Hylnajf0v4vGWmSok0/3SAcbpr/9l19/7zbpA1LLGzu8G909o5 +38Wl5sNbGvQIMK91KxbHRXlVMKoYTIL38cNdZvhzfb9m9Tew6vPmz4ABMPiwYS9T +R19lAPwmQYwce00NkKaQE5+6pzsPhnG/o/Ww9rBE370fidXn8jhqLSOEk+hbp3ju +KlxeSrVHAqlvTzlvSTZGRyxioRLDEMFT3ka1cyLo6HP3U7lj76mlJBibahE+ylL+ +z594fzHnfYPQaN5g13G9H2oxTg+VwwNtL1U737FNiwIDAQABo4GwMIGtMA8GA1Ud EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGmMBYGA1UdJQEB/wQMMAoGCCsGAQUF BwMBMBcGA1UdEQEB/wQNMAuCCWxvY2FsaG9zdDA6BgorBgEEAYI3VAEBBCwMKkFT UC5ORVQgQ29yZSBIVFRQUyBkZXZlbG9wbWVudCBjZXJ0aWZpY2F0ZTAdBgNVHQ4E -FgQUhYGoJsA6zisErkL5EcbKAO3LoQ8wDQYJKoZIhvcNAQELBQADggEBAHOBgMVZ -a27y41edktHaLC4kayKHe3bLBtiXUd1vjWEZaPrcvQH6FItJdB5Z3xbi/4LA6QuU -dZ33z44Lt4AzOiyvR7AGojytM2UYimtBBOFvi9CZuyrMC6ls4SLy6c/Rq1ATmyet -QfkT1a7awC1cJUn7jeuHfly7hnYqrw+67HFY7Ajugv+Uwu6wCessKloWM127AZj/ -V0Gw0rblOmX+uX7fvH3BrYOkrNiHQdzCYZ1P1WU7eWm55JgBfaCrheNZ4L5TVeB2 -uTZPnlsBKQ23qgZ0dtcJzL0scC7zSuYI+qm1QNNyz13K+1ORXPQGQJXULlt7UeHB -ZYN060pO6IO4Ti8= +FgQUg5xH/y5mh7lGgF+y+1sJC7ZMN9owDQYJKoZIhvcNAQELBQADggEBAK/gRk/L +5q4/xXXYW77WawygxpGmTgBLDHkiJViMnJ4VDk6q97/DMABIqCp3sv3FNP9sOssG +ypgmX4jYbyrLNwfvdtpVaiHAHvrTfAtrfHgG0EVM1DRUJ1e0/SRfYf1QTdVxEZv+ +3IM+0roYa8EtQq8BxSsAZz+zhNMoav8nXQVhR7+v5NI5vzT0TVncfXIYYYfLhllb +wcmh9iQuXMifj7WohOFE1XK4O6Bats/6V85ZSGDl3npEpYcgBwyxNQE5hKn/lG5b +3DDkpCTeoMZxAHLo39RzAy0WJTF1KPHQ2EzUa+MfoTNWNrhYSW3IqI3xfPzevSDx +BTBk4gS9MSt2Jj8= -----END CERTIFICATE----- diff --git a/eng/common/testproxy/dotnet-devcert.pfx b/eng/common/testproxy/dotnet-devcert.pfx index 1d59ff89aec..93a5617aea6 100644 Binary files a/eng/common/testproxy/dotnet-devcert.pfx and b/eng/common/testproxy/dotnet-devcert.pfx differ diff --git a/eng/common/testproxy/target_version.txt b/eng/common/testproxy/target_version.txt index 977b480f4fd..3b2404bfbb0 100644 --- a/eng/common/testproxy/target_version.txt +++ b/eng/common/testproxy/target_version.txt @@ -1 +1 @@ -1.0.0-dev.20250501.1 +1.0.0-dev.20260128.1 diff --git a/eng/common/testproxy/test-proxy-standalone-tool.yml b/eng/common/testproxy/test-proxy-standalone-tool.yml index fb9696e6de1..1ced596eb64 100644 --- a/eng/common/testproxy/test-proxy-standalone-tool.yml +++ b/eng/common/testproxy/test-proxy-standalone-tool.yml @@ -74,6 +74,9 @@ steps: # nohup does NOT continue beyond the current session if you use it within powershell - bash: | + if [[ "$(uname)" == "Darwin" ]]; then + export DOTNET_ROOT="$HOME/.dotnet" + fi echo "nohup $(PROXY_EXE) 1>${{ parameters.rootFolder }}/test-proxy.log 2>${{ parameters.rootFolder }}/test-proxy-error.log &" nohup $(PROXY_EXE) 1>${{ parameters.rootFolder }}/test-proxy.log 2>${{ parameters.rootFolder }}/test-proxy-error.log & @@ -84,6 +87,8 @@ steps: displayName: "Run the testproxy - linux/mac" condition: and(succeeded(), ne(variables['Agent.OS'],'Windows_NT'), ${{ parameters.condition }}) workingDirectory: "${{ parameters.rootFolder }}" + env: + DOTNET_ROLL_FORWARD: 'Major' - pwsh: | for ($i = 0; $i -lt 10; $i++) { diff --git a/eng/common/testproxy/test-proxy-tool.yml b/eng/common/testproxy/test-proxy-tool.yml index 03c9dbaa00c..f2d0fe6e30b 100644 --- a/eng/common/testproxy/test-proxy-tool.yml +++ b/eng/common/testproxy/test-proxy-tool.yml @@ -87,6 +87,9 @@ steps: # nohup does NOT continue beyond the current session if you use it within powershell - bash: | + if [[ "$(uname)" == "Darwin" ]]; then + export DOTNET_ROOT="$HOME/.dotnet" + fi nohup $(Build.BinariesDirectory)/test-proxy/test-proxy 1>${{ parameters.rootFolder }}/test-proxy.log 2>${{ parameters.rootFolder }}/test-proxy-error.log & echo $! > $(Build.SourcesDirectory)/test-proxy.pid diff --git a/eng/common/tsp-client/README.md b/eng/common/tsp-client/README.md new file mode 100644 index 00000000000..6f88d536c9e --- /dev/null +++ b/eng/common/tsp-client/README.md @@ -0,0 +1,82 @@ +# TypeSpec Client Generator CLI + +This directory contains npm package definitions for `@azure-tools/typespec-client-generator-cli` (tsp-client) with pinned versions to ensure reproducible builds across environments. + +## Files + +- **`package.json`** - npm package definition with pinned tsp-client version +- **`package-lock.json`** - Lock file ensuring exact dependency versions + +## Prerequisites + +- **Node.js** (with npm) - Required to install and run tsp-client + +## Installation + +### Install dependencies + +```bash +# Navigate to this directory +cd eng/common/tsp-client + +# Install dependencies +npm ci +``` + +## Usage + +After installation, you can run `tsp-client` using `npm exec --prefix {path_to_the_eng/common/tsp-client}`. +Note that you should *not* navigate into the `eng/common/tsp-client` folder, since several `tsp-client` commands require the current working directory to be the client library's root. + +```bash +# Set the tsp-client directory path relative to your current working directory +_TspClientDir=eng/common/tsp-client + +# Get help +npm exec --prefix ${_TspClientDir} --no -- tsp-client --help + +# Check version +npm exec --prefix ${_TspClientDir} --no -- tsp-client version + +# Generate client code +npm exec --prefix ${_TspClientDir} --no -- tsp-client generate --output-dir ./generated + +# Initialize a new project +npm exec --prefix ${_TspClientDir} --no -- tsp-client init --tsp-config ./tspconfig.yaml +``` + +## CI/CD Best Practices + +```bash +_TspClientDir=eng/common/tsp-client +npm ci --prefix ${_TspClientDir} +npm exec --prefix ${_TspClientDir} --no -- tsp-client init --update-if-exists --tsp-config https://github.com/Azure/azure-rest-api-specs/blob/dee71463cbde1d416c47cf544e34f7966a94ddcb/specification/contosowidgetmanager/Contoso.WidgetManager/tspconfig.yaml +``` + +## Package Management + +### Automatic Updates via Dependabot + +Dependabot is configured to automatically check for updates to `@azure-tools/typespec-client-generator-cli` daily and create pull requests with updated `package.json` and `package-lock.json` files. This ensures the package stays current with the latest versions while maintaining security through the PR review process. + +### Manual Version Updates + +If you need to manually update the tsp-client version: + +1. Edit `package.json` to update the version: + + ```json + { + "dependencies": { + "@azure-tools/typespec-client-generator-cli": "0.28.1" + } + } + ``` + +2. Update the lock file: + + ```bash + npm install + ``` + +3. Commit both `package.json` and `package-lock.json` diff --git a/eng/common/tsp-client/package-lock.json b/eng/common/tsp-client/package-lock.json new file mode 100644 index 00000000000..a125f5d6cab --- /dev/null +++ b/eng/common/tsp-client/package-lock.json @@ -0,0 +1,2273 @@ +{ + "name": "tools", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "@azure-tools/typespec-client-generator-cli": "0.31.0" + } + }, + "node_modules/@autorest/codemodel": { + "version": "4.20.1", + "resolved": "https://registry.npmjs.org/@autorest/codemodel/-/codemodel-4.20.1.tgz", + "integrity": "sha512-MdI4G0EdQ8yOxGzgT1rCOXxXkCrUQLjVykOvdAyByIgHbnpRop1UzUQuuKmXO8gQPSy7xwYhnfVSgETbHIJZgg==", + "license": "MIT", + "dependencies": { + "@azure-tools/codegen": "~2.10.1", + "js-yaml": "~4.1.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@autorest/core": { + "version": "3.10.8", + "resolved": "https://registry.npmjs.org/@autorest/core/-/core-3.10.8.tgz", + "integrity": "sha512-7tj+zPUYu42lrzOZUC2hNaH7Xt53IVaEbWzV23aEYzDhXF0zD9TTpVexFXKTT4idBV0njsAKEKjPMkmQuHLbgQ==", + "license": "MIT", + "bin": { + "autorest-core": "entrypoints/app.js", + "autorest-language-service": "entrypoints/language-service.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@autorest/extension-base": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@autorest/extension-base/-/extension-base-3.6.1.tgz", + "integrity": "sha512-FWfO6LM3p+R1dW87wnGdJsCpZw67/h1hj09LsQuY0ywKBKv9lrLAW6AlVPrFyvUUIaCMMgd01U6TifCz/FRG9g==", + "license": "MIT", + "dependencies": { + "@azure-tools/codegen": "~2.10.1", + "js-yaml": "~4.1.0", + "vscode-jsonrpc": "^3.5.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@autorest/openapi-to-typespec": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/@autorest/openapi-to-typespec/-/openapi-to-typespec-0.11.10.tgz", + "integrity": "sha512-1bP26wuUioqHxhV11o48VthMCkOwHhiHwJ9ebaINf/NvVKU9YNTXbzStKUclS4vpRpk2Zb/VSmE1TZfAomI/mg==", + "license": "MIT", + "dependencies": { + "@autorest/codemodel": "~4.20.1", + "@autorest/extension-base": "~3.6.1", + "@azure-tools/codegen": "~2.10.1", + "@azure-tools/openapi": "~3.6.1", + "@typespec/prettier-plugin-typespec": "^1.4.0", + "change-case-all": "~2.1.0", + "lodash": "~4.17.20", + "pluralize": "^8.0.0", + "prettier": "~3.5.3" + } + }, + "node_modules/@azure-tools/async-io": { + "version": "3.0.254", + "resolved": "https://registry.npmjs.org/@azure-tools/async-io/-/async-io-3.0.254.tgz", + "integrity": "sha512-X1C7XdyCuo50ch9FzKtTvmK18FgDxxf1Bbt3cSoknQqeDaRegHSSCO+zByq2YA4NvUzKXeZ1engh29IDxZXgpQ==", + "license": "MIT", + "dependencies": { + "@azure-tools/tasks": "~3.0.255", + "proper-lockfile": "~2.0.1" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/@azure-tools/codegen": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/@azure-tools/codegen/-/codegen-2.10.1.tgz", + "integrity": "sha512-fZfREKjQnBTscjObgK4LuyZNFaofoCNQDNz0jl1i8fYNwCM5EOF9BXwtEtobuEyCpPUNDxQ/KKO65eWzirqk4w==", + "license": "MIT", + "dependencies": { + "@azure-tools/async-io": "~3.0.0", + "js-yaml": "~4.1.0", + "semver": "^7.7.2" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@azure-tools/json": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@azure-tools/json/-/json-1.3.1.tgz", + "integrity": "sha512-0f4kQ6c513ycuk0Z29Nm09D/3dQHrHkduUW8wsFR1QTQ5uqgdYaDWg5I4cZbA8OkOIrJG73TzB/3G0liVCQ+Fw==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@azure-tools/jsonschema": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@azure-tools/jsonschema/-/jsonschema-1.3.1.tgz", + "integrity": "sha512-P4KnJzZJjCATcn3nRcF5MPja2wrPdP48Us643+0eqGtNBL4O20CFVEm6WFeFeR8JhvNCsZfeayHiE6VOspe1rg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@azure-tools/openapi": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@azure-tools/openapi/-/openapi-3.6.1.tgz", + "integrity": "sha512-vkIu0CUg09bzxqrlrNHdoOPu9AFhObp0FqG40M2WaF2dcVgLalsUc+wK5s4LpftlZAxcBmzVHna22JhI5/0X9g==", + "license": "MIT", + "dependencies": { + "@azure-tools/json": "~1.3.1", + "@azure-tools/jsonschema": "~1.3.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@azure-tools/tasks": { + "version": "3.0.255", + "resolved": "https://registry.npmjs.org/@azure-tools/tasks/-/tasks-3.0.255.tgz", + "integrity": "sha512-GjALNLz7kWMEdRVbaN5g0cJHNAr3XVTbP0611Mv2UzMgGL6FOhNZJK+oPHJKLDR8EEDZNnkwPlyi7B+INXUSQA==", + "license": "MIT", + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/@azure-tools/typespec-autorest": { + "version": "0.60.0", + "resolved": "https://registry.npmjs.org/@azure-tools/typespec-autorest/-/typespec-autorest-0.60.0.tgz", + "integrity": "sha512-aIRr1e4g3irkjLTpxqzJ8BFnNFYwj4nlcG6cKGPuhNtiHhJgHjUhLVUNIW1A9O4jx+3RSErL9AkAl1ep+ZbiuA==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@azure-tools/typespec-azure-core": "^0.60.0", + "@azure-tools/typespec-azure-resource-manager": "^0.60.0", + "@azure-tools/typespec-client-generator-core": "^0.60.0", + "@typespec/compiler": "^1.4.0", + "@typespec/http": "^1.4.0", + "@typespec/openapi": "^1.4.0", + "@typespec/rest": "^0.74.0", + "@typespec/versioning": "^0.74.0" + } + }, + "node_modules/@azure-tools/typespec-azure-core": { + "version": "0.60.0", + "resolved": "https://registry.npmjs.org/@azure-tools/typespec-azure-core/-/typespec-azure-core-0.60.0.tgz", + "integrity": "sha512-Pmm7blxnEZZ7lhMJWWsiIqMrFthaCK6uu7f+ONN7dq0Mjc/O9w8+43tAIXwnGz1OKAWmiToh3EDbaxeWyt/FhQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@typespec/compiler": "^1.4.0", + "@typespec/http": "^1.4.0", + "@typespec/rest": "^0.74.0" + } + }, + "node_modules/@azure-tools/typespec-azure-resource-manager": { + "version": "0.60.0", + "resolved": "https://registry.npmjs.org/@azure-tools/typespec-azure-resource-manager/-/typespec-azure-resource-manager-0.60.0.tgz", + "integrity": "sha512-u0gqgSO5lIcpZN11MBAmRnR7kM1eoBLO8cKB86bXrxLt2vAkYpuL4hWp0A+QJjQy/YI5rrijeBF0visqBJnR4A==", + "license": "MIT", + "peer": true, + "dependencies": { + "change-case": "~5.4.4", + "pluralize": "^8.0.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@azure-tools/typespec-azure-core": "^0.60.0", + "@typespec/compiler": "^1.4.0", + "@typespec/http": "^1.4.0", + "@typespec/openapi": "^1.4.0", + "@typespec/rest": "^0.74.0", + "@typespec/versioning": "^0.74.0" + } + }, + "node_modules/@azure-tools/typespec-client-generator-cli": { + "version": "0.31.0", + "resolved": "https://registry.npmjs.org/@azure-tools/typespec-client-generator-cli/-/typespec-client-generator-cli-0.31.0.tgz", + "integrity": "sha512-ukz9IROlYhr0TAXeVLT6oHb/SbZyDFp+rTKYw5XblNBJChLiq+PDalrjyWXsSF8J15TfKS4vixEOc+LZUQQb0A==", + "license": "MIT", + "dependencies": { + "@autorest/core": "^3.10.2", + "@autorest/openapi-to-typespec": ">=0.10.6 <1.0.0", + "@azure-tools/typespec-autorest": ">=0.53.0 <1.0.0", + "@azure/core-rest-pipeline": "^1.12.0", + "@types/yargs": "^17.0.32", + "autorest": "^3.7.1", + "chalk": "^5.3.0", + "dotenv": "^16.4.5", + "prompt-sync": "^4.2.0", + "simple-git": "^3.20.0", + "yaml": "^2.3.1", + "yargs": "^17.2.1" + }, + "bin": { + "tsp-client": "cmd/tsp-client.js" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@typespec/compiler": "1.0.0-rc.1 || >=1.0.0 <2.0.0" + } + }, + "node_modules/@azure-tools/typespec-client-generator-core": { + "version": "0.60.0", + "resolved": "https://registry.npmjs.org/@azure-tools/typespec-client-generator-core/-/typespec-client-generator-core-0.60.0.tgz", + "integrity": "sha512-JV9uiqxDCzVVFeMctm4ebmUI4982B3dCdKx0nmonjfRf9a2/MXilEKExPIaNVR3ZqbFPXM/IepheY8t9YZNqSQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "change-case": "~5.4.4", + "pluralize": "^8.0.0", + "yaml": "~2.8.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@azure-tools/typespec-azure-core": "^0.60.0", + "@typespec/compiler": "^1.4.0", + "@typespec/events": "^0.74.0", + "@typespec/http": "^1.4.0", + "@typespec/openapi": "^1.4.0", + "@typespec/rest": "^0.74.0", + "@typespec/sse": "^0.74.0", + "@typespec/streams": "^0.74.0", + "@typespec/versioning": "^0.74.0", + "@typespec/xml": "^0.74.0" + } + }, + "node_modules/@azure/abort-controller": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-auth": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.10.1.tgz", + "integrity": "sha512-ykRMW8PjVAn+RS6ww5cmK9U2CyH9p4Q88YJwvUslfuMmN98w/2rdGRLPqJYObapBCdzBVeDgYWdJnFPFb7qzpg==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-util": "^1.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/core-rest-pipeline": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.22.1.tgz", + "integrity": "sha512-UVZlVLfLyz6g3Hy7GNDpooMQonUygH7ghdiSASOOHy97fKj/mPLqgDX7aidOijn+sCMU+WU8NjlPlNTgnvbcGA==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.10.0", + "@azure/core-tracing": "^1.3.0", + "@azure/core-util": "^1.13.0", + "@azure/logger": "^1.3.0", + "@typespec/ts-http-runtime": "^0.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/core-tracing": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.3.1.tgz", + "integrity": "sha512-9MWKevR7Hz8kNzzPLfX4EAtGM2b8mr50HPDBvio96bURP/9C+HjdH3sBlLSNNrvRAr5/k/svoH457gB5IKpmwQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/core-util": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.13.1.tgz", + "integrity": "sha512-XPArKLzsvl0Hf0CaGyKHUyVgF7oDnhKoP85Xv6M4StF/1AhfORhZudHtOyf2s+FcbuQ9dPRAjB8J2KvRRMUK2A==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@typespec/ts-http-runtime": "^0.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/logger": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.3.0.tgz", + "integrity": "sha512-fCqPIfOcLE+CGqGPd66c8bZpwAji98tZ4JI9i/mlTNTlsIWslCfpg48s/ypyLxZTump5sypjrKn2/kY7q8oAbA==", + "license": "MIT", + "dependencies": { + "@typespec/ts-http-runtime": "^0.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@inquirer/checkbox": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.2.2.tgz", + "integrity": "sha512-E+KExNurKcUJJdxmjglTl141EwxWyAHplvsYJQgSwXf8qiNWkTxTuCCqmhFEmbIXd4zLaGMfQFJ6WrZ7fSeV3g==", + "license": "MIT", + "peer": true, + "dependencies": { + "@inquirer/core": "^10.2.0", + "@inquirer/figures": "^1.0.13", + "@inquirer/type": "^3.0.8", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/confirm": { + "version": "5.1.16", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.16.tgz", + "integrity": "sha512-j1a5VstaK5KQy8Mu8cHmuQvN1Zc62TbLhjJxwHvKPPKEoowSF6h/0UdOpA9DNdWZ+9Inq73+puRq1df6OJ8Sag==", + "license": "MIT", + "peer": true, + "dependencies": { + "@inquirer/core": "^10.2.0", + "@inquirer/type": "^3.0.8" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/core": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.2.0.tgz", + "integrity": "sha512-NyDSjPqhSvpZEMZrLCYUquWNl+XC/moEcVFqS55IEYIYsY0a1cUCevSqk7ctOlnm/RaSBU5psFryNlxcmGrjaA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@inquirer/figures": "^1.0.13", + "@inquirer/type": "^3.0.8", + "ansi-escapes": "^4.3.2", + "cli-width": "^4.1.0", + "mute-stream": "^2.0.0", + "signal-exit": "^4.1.0", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/editor": { + "version": "4.2.18", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.18.tgz", + "integrity": "sha512-yeQN3AXjCm7+Hmq5L6Dm2wEDeBRdAZuyZ4I7tWSSanbxDzqM0KqzoDbKM7p4ebllAYdoQuPJS6N71/3L281i6w==", + "license": "MIT", + "peer": true, + "dependencies": { + "@inquirer/core": "^10.2.0", + "@inquirer/external-editor": "^1.0.1", + "@inquirer/type": "^3.0.8" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/expand": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.18.tgz", + "integrity": "sha512-xUjteYtavH7HwDMzq4Cn2X4Qsh5NozoDHCJTdoXg9HfZ4w3R6mxV1B9tL7DGJX2eq/zqtsFjhm0/RJIMGlh3ag==", + "license": "MIT", + "peer": true, + "dependencies": { + "@inquirer/core": "^10.2.0", + "@inquirer/type": "^3.0.8", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/external-editor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.1.tgz", + "integrity": "sha512-Oau4yL24d2B5IL4ma4UpbQigkVhzPDXLoqy1ggK4gnHg/stmkffJE4oOXHXF3uz0UEpywG68KcyXsyYpA1Re/Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "chardet": "^2.1.0", + "iconv-lite": "^0.6.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/figures": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.13.tgz", + "integrity": "sha512-lGPVU3yO9ZNqA7vTYz26jny41lE7yoQansmqdMLBEfqaGsmdg7V3W9mK9Pvb5IL4EVZ9GnSDGMO/cJXud5dMaw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/input": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.2.2.tgz", + "integrity": "sha512-hqOvBZj/MhQCpHUuD3MVq18SSoDNHy7wEnQ8mtvs71K8OPZVXJinOzcvQna33dNYLYE4LkA9BlhAhK6MJcsVbw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@inquirer/core": "^10.2.0", + "@inquirer/type": "^3.0.8" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/number": { + "version": "3.0.18", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.18.tgz", + "integrity": "sha512-7exgBm52WXZRczsydCVftozFTrrwbG5ySE0GqUd2zLNSBXyIucs2Wnm7ZKLe/aUu6NUg9dg7Q80QIHCdZJiY4A==", + "license": "MIT", + "peer": true, + "dependencies": { + "@inquirer/core": "^10.2.0", + "@inquirer/type": "^3.0.8" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/password": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.18.tgz", + "integrity": "sha512-zXvzAGxPQTNk/SbT3carAD4Iqi6A2JS2qtcqQjsL22uvD+JfQzUrDEtPjLL7PLn8zlSNyPdY02IiQjzoL9TStA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@inquirer/core": "^10.2.0", + "@inquirer/type": "^3.0.8", + "ansi-escapes": "^4.3.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/prompts": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.8.4.tgz", + "integrity": "sha512-MuxVZ1en1g5oGamXV3DWP89GEkdD54alcfhHd7InUW5BifAdKQEK9SLFa/5hlWbvuhMPlobF0WAx7Okq988Jxg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@inquirer/checkbox": "^4.2.2", + "@inquirer/confirm": "^5.1.16", + "@inquirer/editor": "^4.2.18", + "@inquirer/expand": "^4.0.18", + "@inquirer/input": "^4.2.2", + "@inquirer/number": "^3.0.18", + "@inquirer/password": "^4.0.18", + "@inquirer/rawlist": "^4.1.6", + "@inquirer/search": "^3.1.1", + "@inquirer/select": "^4.3.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/rawlist": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.6.tgz", + "integrity": "sha512-KOZqa3QNr3f0pMnufzL7K+nweFFCCBs6LCXZzXDrVGTyssjLeudn5ySktZYv1XiSqobyHRYYK0c6QsOxJEhXKA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@inquirer/core": "^10.2.0", + "@inquirer/type": "^3.0.8", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/search": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.1.1.tgz", + "integrity": "sha512-TkMUY+A2p2EYVY3GCTItYGvqT6LiLzHBnqsU1rJbrpXUijFfM6zvUx0R4civofVwFCmJZcKqOVwwWAjplKkhxA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@inquirer/core": "^10.2.0", + "@inquirer/figures": "^1.0.13", + "@inquirer/type": "^3.0.8", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/select": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.3.2.tgz", + "integrity": "sha512-nwous24r31M+WyDEHV+qckXkepvihxhnyIaod2MG7eCE6G0Zm/HUF6jgN8GXgf4U7AU6SLseKdanY195cwvU6w==", + "license": "MIT", + "peer": true, + "dependencies": { + "@inquirer/core": "^10.2.0", + "@inquirer/figures": "^1.0.13", + "@inquirer/type": "^3.0.8", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/type": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.8.tgz", + "integrity": "sha512-lg9Whz8onIHRthWaN1Q9EGLa/0LFJjyM8mEUbL1eTi6yMGvBf8gvyDLtxSXztQsxMvhxxNpJYrwa1YHdq+w4Jw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "license": "ISC", + "peer": true, + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@kwsites/file-exists": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz", + "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==", + "license": "MIT", + "dependencies": { + "debug": "^4.1.1" + } + }, + "node_modules/@kwsites/promise-deferred": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz", + "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==", + "license": "MIT" + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "license": "MIT", + "peer": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", + "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "license": "MIT" + }, + "node_modules/@typespec/compiler": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@typespec/compiler/-/compiler-1.4.0.tgz", + "integrity": "sha512-/AFiU3ImuhH/vHKzSGv7I2peewdJ7YLhgMCfFDNk6Ae0a5Ylrc8R1GOATVilisEPBFG9lnjHn3uUcyaZs5VWRw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "~7.27.1", + "@inquirer/prompts": "^7.4.0", + "ajv": "~8.17.1", + "change-case": "~5.4.4", + "env-paths": "^3.0.0", + "globby": "~14.1.0", + "is-unicode-supported": "^2.1.0", + "mustache": "~4.2.0", + "picocolors": "~1.1.1", + "prettier": "~3.6.2", + "semver": "^7.7.1", + "tar": "^7.4.3", + "temporal-polyfill": "^0.3.0", + "vscode-languageserver": "~9.0.1", + "vscode-languageserver-textdocument": "~1.0.12", + "yaml": "~2.8.0", + "yargs": "~18.0.0" + }, + "bin": { + "tsp": "cmd/tsp.js", + "tsp-server": "cmd/tsp-server.js" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@typespec/compiler/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@typespec/compiler/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@typespec/compiler/node_modules/cliui": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", + "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", + "license": "ISC", + "peer": true, + "dependencies": { + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@typespec/compiler/node_modules/emoji-regex": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.5.0.tgz", + "integrity": "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg==", + "license": "MIT", + "peer": true + }, + "node_modules/@typespec/compiler/node_modules/prettier": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "license": "MIT", + "peer": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/@typespec/compiler/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@typespec/compiler/node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@typespec/compiler/node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@typespec/compiler/node_modules/yargs": { + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz", + "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==", + "license": "MIT", + "peer": true, + "dependencies": { + "cliui": "^9.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "string-width": "^7.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^22.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/@typespec/compiler/node_modules/yargs-parser": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", + "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", + "license": "ISC", + "peer": true, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/@typespec/events": { + "version": "0.74.0", + "resolved": "https://registry.npmjs.org/@typespec/events/-/events-0.74.0.tgz", + "integrity": "sha512-CY6JTtheMKAUlxiPmwx2fLIAWEwezsXmQYUMRhyuW44Q73unQIkexE43LUnNWOJSZckYucqUp+ihXh7jxzWeVQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@typespec/compiler": "^1.4.0" + } + }, + "node_modules/@typespec/http": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@typespec/http/-/http-1.4.0.tgz", + "integrity": "sha512-Y0PDDtBu+oZnwivfhbL0lN6Mk3QiCxZ66DgB5kFjcgKNpnXf0u440PPyaL42a8lbchzz5lVwz+cinyIMI89FIQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@typespec/compiler": "^1.4.0", + "@typespec/streams": "^0.74.0" + }, + "peerDependenciesMeta": { + "@typespec/streams": { + "optional": true + } + } + }, + "node_modules/@typespec/openapi": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@typespec/openapi/-/openapi-1.4.0.tgz", + "integrity": "sha512-ZfrCsmZG/Zt1laLaWC0pKvnZr4jqrm/YS/YuZe/gVrSYKBxGLopXle7H0wrSSMYkIVCNCLiC68/HqRxV6XTfoA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@typespec/compiler": "^1.4.0", + "@typespec/http": "^1.4.0" + } + }, + "node_modules/@typespec/prettier-plugin-typespec": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@typespec/prettier-plugin-typespec/-/prettier-plugin-typespec-1.4.0.tgz", + "integrity": "sha512-P4ATX7Ryu4IKlzJWv36WbEPCn4xrusaJdb2tbm2wJWobhHW6jt7bWGpJXrXvdHMcfk2IYyIpbOx0+EZb7Aof+g==", + "license": "MIT", + "dependencies": { + "prettier": "~3.6.2" + } + }, + "node_modules/@typespec/prettier-plugin-typespec/node_modules/prettier": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/@typespec/rest": { + "version": "0.74.0", + "resolved": "https://registry.npmjs.org/@typespec/rest/-/rest-0.74.0.tgz", + "integrity": "sha512-dE+Xmv01AQ7m8jUvEbGsUQLSVo3sLzMpnHRbQEOnJX42oDqtIsz/2GEOXKQpNm1AKBISK66E2FFB5boz999Ziw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@typespec/compiler": "^1.4.0", + "@typespec/http": "^1.4.0" + } + }, + "node_modules/@typespec/sse": { + "version": "0.74.0", + "resolved": "https://registry.npmjs.org/@typespec/sse/-/sse-0.74.0.tgz", + "integrity": "sha512-+m7/elbGp7q/kqCGaBRj8v8wVMWKVEV8AsZOjf1PY2MkMUrux9ivOijBIktgoLBXDn+ocO2qVfFrHWG2slZSaw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@typespec/compiler": "^1.4.0", + "@typespec/events": "^0.74.0", + "@typespec/http": "^1.4.0", + "@typespec/streams": "^0.74.0" + } + }, + "node_modules/@typespec/streams": { + "version": "0.74.0", + "resolved": "https://registry.npmjs.org/@typespec/streams/-/streams-0.74.0.tgz", + "integrity": "sha512-LIWizQgzGt8qN8ravte4DrPLPNOk9ge73bV9Us2TOECagTVQWwgMVy7+o/Beff3sOLQO/sEOwfzvmnNpSlauHg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@typespec/compiler": "^1.4.0" + } + }, + "node_modules/@typespec/ts-http-runtime": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.1.tgz", + "integrity": "sha512-SnbaqayTVFEA6/tYumdF0UmybY0KHyKwGPBXnyckFlrrKdhWFrL3a2HIPXHjht5ZOElKGcXfD2D63P36btb+ww==", + "license": "MIT", + "dependencies": { + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@typespec/versioning": { + "version": "0.74.0", + "resolved": "https://registry.npmjs.org/@typespec/versioning/-/versioning-0.74.0.tgz", + "integrity": "sha512-eFIa23tycWJgv3Lxyu6jUlRi02dhtQE4Jjx3Ui5vEbwHW8pMEzuyF7ALt1c+V9HOLkfDkS4dJkiOVIoikZHPvQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@typespec/compiler": "^1.4.0" + } + }, + "node_modules/@typespec/xml": { + "version": "0.74.0", + "resolved": "https://registry.npmjs.org/@typespec/xml/-/xml-0.74.0.tgz", + "integrity": "sha512-NiXatOfpyPxU94f2tEBAygxJeS7CvIr5lvnfZkC0tUHwkiJeLrI1jt13kDVB5CE6zNK6I3d7c37xsQs9WXGFAQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@typespec/compiler": "^1.4.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "license": "MIT", + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/autorest": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/autorest/-/autorest-3.7.2.tgz", + "integrity": "sha512-yEeF0tJjx2fROK9VWIVHKFiUSzD0cxwqnq7z+v7kIIRRZjyOM3rpBS9OPp6tQv5d3mmxPAUNh57G1ZumQNqQGg==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "autorest": "entrypoints/app.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "license": "MIT", + "peer": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/change-case": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/change-case/-/change-case-5.4.4.tgz", + "integrity": "sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==", + "license": "MIT" + }, + "node_modules/change-case-all": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/change-case-all/-/change-case-all-2.1.0.tgz", + "integrity": "sha512-v6b0WWWkZUMHVuYk82l+WROgkUm4qEN2w5hKRNWtEOYwWqUGoi8C6xH0l1RLF1EoWqDFK6MFclmN3od6ws3/uw==", + "license": "MIT", + "dependencies": { + "change-case": "^5.2.0", + "sponge-case": "^2.0.2", + "swap-case": "^3.0.2", + "title-case": "^3.0.3" + } + }, + "node_modules/chardet": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.0.tgz", + "integrity": "sha512-bNFETTG/pM5ryzQ9Ad0lJOTa6HWD/YsScAR3EnCPZRPlQh77JocYktSHOUHelyhm8IARL+o4c4F1bP5KVOjiRA==", + "license": "MIT", + "peer": true + }, + "node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "license": "BlueOak-1.0.0", + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "license": "ISC", + "peer": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/env-paths": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-3.0.0.tgz", + "integrity": "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==", + "license": "MIT", + "peer": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT", + "peer": true + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "license": "ISC", + "peer": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "license": "MIT", + "peer": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz", + "integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "peer": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globby": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-14.1.0.tgz", + "integrity": "sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@sindresorhus/merge-streams": "^2.1.0", + "fast-glob": "^3.3.3", + "ignore": "^7.0.3", + "path-type": "^6.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "peer": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "license": "MIT", + "peer": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT", + "peer": true + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT", + "peer": true + }, + "node_modules/lodash": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "license": "MIT", + "peer": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "peer": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minizlib": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", + "license": "MIT", + "peer": true, + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/mustache": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", + "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", + "license": "MIT", + "peer": true, + "bin": { + "mustache": "bin/mustache" + } + }, + "node_modules/mute-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", + "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", + "license": "ISC", + "peer": true, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/path-type": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-6.0.0.tgz", + "integrity": "sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC", + "peer": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/prettier": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", + "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prompt-sync": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/prompt-sync/-/prompt-sync-4.2.0.tgz", + "integrity": "sha512-BuEzzc5zptP5LsgV5MZETjDaKSWfchl5U9Luiu8SKp7iZWD5tZalOxvNcZRwv+d2phNFr8xlbxmFNcRKfJOzJw==", + "license": "MIT", + "dependencies": { + "strip-ansi": "^5.0.0" + } + }, + "node_modules/proper-lockfile": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-2.0.1.tgz", + "integrity": "sha512-rjaeGbsmhNDcDInmwi4MuI6mRwJu6zq8GjYCLuSuE7GF+4UjgzkL69sVKKJ2T2xH61kK7rXvGYpvaTu909oXaQ==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "retry": "^0.10.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "peer": true + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/retry": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz", + "integrity": "sha512-ZXUSQYTHdl3uS7IuCehYfMzKyIDBNoAuUblvy5oGO5UJSUTmStUUVPXbA9Qxd173Bgre53yCQczQuHgRWAdvJQ==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "license": "MIT", + "peer": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT", + "peer": true + }, + "node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "peer": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/simple-git": { + "version": "3.28.0", + "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.28.0.tgz", + "integrity": "sha512-Rs/vQRwsn1ILH1oBUy8NucJlXmnnLeLCfcvbSehkPzbv3wwoFWIdtfd6Ndo6ZPhlPsCZ60CPI4rxurnwAa+a2w==", + "license": "MIT", + "dependencies": { + "@kwsites/file-exists": "^1.1.1", + "@kwsites/promise-deferred": "^1.1.1", + "debug": "^4.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/steveukx/git-js?sponsor=1" + } + }, + "node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/sponge-case": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/sponge-case/-/sponge-case-2.0.3.tgz", + "integrity": "sha512-i4h9ZGRfxV6Xw3mpZSFOfbXjf0cQcYmssGWutgNIfFZ2VM+YIWfD71N/kjjwK6X/AAHzBr+rciEcn/L34S8TGw==", + "license": "MIT" + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/swap-case": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/swap-case/-/swap-case-3.0.3.tgz", + "integrity": "sha512-6p4op8wE9CQv7uDFzulI6YXUw4lD9n4oQierdbFThEKVWVQcbQcUjdP27W8XE7V4QnWmnq9jueSHceyyQnqQVA==", + "license": "MIT" + }, + "node_modules/tar": { + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.6.tgz", + "integrity": "sha512-xqUeu2JAIJpXyvskvU3uvQW8PAmHrtXp2KDuMJwQqW8Sqq0CaZBAQ+dKS3RBXVhU4wC5NjAdKrmh84241gO9cA==", + "license": "BlueOak-1.0.0", + "peer": true, + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/temporal-polyfill": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/temporal-polyfill/-/temporal-polyfill-0.3.0.tgz", + "integrity": "sha512-qNsTkX9K8hi+FHDfHmf22e/OGuXmfBm9RqNismxBrnSmZVJKegQ+HYYXT+R7Ha8F/YSm2Y34vmzD4cxMu2u95g==", + "license": "MIT", + "peer": true, + "dependencies": { + "temporal-spec": "0.3.0" + } + }, + "node_modules/temporal-spec": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/temporal-spec/-/temporal-spec-0.3.0.tgz", + "integrity": "sha512-n+noVpIqz4hYgFSMOSiINNOUOMFtV5cZQNCmmszA6GiVFVRt3G7AqVyhXjhCSmowvQn+NsGn+jMDMKJYHd3bSQ==", + "license": "ISC", + "peer": true + }, + "node_modules/title-case": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/title-case/-/title-case-3.0.3.tgz", + "integrity": "sha512-e1zGYRvbffpcHIrnuqT0Dh+gEJtDaxDSoG4JAIpq4oDFyooziLBIiYQv0GBT4FUAnUop5uZ1hiIAj7oAF6sOCA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "license": "(MIT OR CC0-1.0)", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vscode-jsonrpc": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-3.6.2.tgz", + "integrity": "sha512-T24Jb5V48e4VgYliUXMnZ379ItbrXgOimweKaJshD84z+8q7ZOZjJan0MeDe+Ugb+uqERDVV8SBmemaGMSMugA==", + "license": "MIT", + "engines": { + "node": ">=4.0.0 || >=6.0.0" + } + }, + "node_modules/vscode-languageserver": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz", + "integrity": "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==", + "license": "MIT", + "peer": true, + "dependencies": { + "vscode-languageserver-protocol": "3.17.5" + }, + "bin": { + "installServerIntoExtension": "bin/installServerIntoExtension" + } + }, + "node_modules/vscode-languageserver-protocol": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", + "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", + "license": "MIT", + "peer": true, + "dependencies": { + "vscode-jsonrpc": "8.2.0", + "vscode-languageserver-types": "3.17.5" + } + }, + "node_modules/vscode-languageserver-protocol/node_modules/vscode-jsonrpc": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", + "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/vscode-languageserver-textdocument": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz", + "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==", + "license": "MIT", + "peer": true + }, + "node_modules/vscode-languageserver-types": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", + "license": "MIT", + "peer": true + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "license": "BlueOak-1.0.0", + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/yaml": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", + "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yoctocolors-cjs": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz", + "integrity": "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/eng/common/tsp-client/package.json b/eng/common/tsp-client/package.json new file mode 100644 index 00000000000..290f69672cf --- /dev/null +++ b/eng/common/tsp-client/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "@azure-tools/typespec-client-generator-cli": "0.31.0" + } +}