diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index c21d04bd..e2f24dc2 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "powershell": { - "version": "7.5.0", + "version": "7.5.3", "commands": [ "pwsh" ], diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 85d91565..4a440efc 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,5 +1,5 @@ # Refer to https://hub.docker.com/_/microsoft-dotnet-sdk for available versions -FROM mcr.microsoft.com/dotnet/sdk:9.0.203-noble@sha256:2c9a4956a61fc45d9111fb36ec9fb86932e4842af9eb9bc9306bf8757f53674d +FROM mcr.microsoft.com/dotnet/sdk:9.0.305-noble@sha256:604ef064c6d91068eeb9d946036d8ffadbe25589c4cd77a230fc96e0f6d01d72 # Installing mono makes `dotnet test` work without errors even for net472. # But installing it takes a long time, so it's excluded by default. diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 00000000..08593502 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,23 @@ +# Copilot instructions for this repository + +## High level guidance + +* Review the `CONTRIBUTING.md` file for instructions to build and test the software. +* Set the `NBGV_GitEngine` environment variable to `Disabled` before running any `dotnet` or `msbuild` commands. + +## Software Design + +* Design APIs to be highly testable, and all functionality should be tested. +* Avoid introducing binary breaking changes in public APIs of projects under `src` unless their project files have `IsPackable` set to `false`. + +## Testing + +* There should generally be one test project (under the `test` directory) per shipping project (under the `src` directory). Test projects are named after the project being tested with a `.Test` suffix. +* Tests should use the Xunit testing framework. +* Some tests are known to be unstable. When running tests, you should skip the unstable ones by running `dotnet test --filter "TestCategory!=FailsInCloudTest"`. + +## Coding style + +* Honor StyleCop rules and fix any reported build warnings *after* getting tests to pass. +* In C# files, use namespace *statements* instead of namespace *blocks* for all new files. +* Add API doc comments to all new public and internal members. diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml new file mode 100644 index 00000000..eb69d92e --- /dev/null +++ b/.github/workflows/copilot-setup-steps.yml @@ -0,0 +1,41 @@ +name: 💪🏼 Copilot Setup Steps + +# Automatically run the setup steps when they are changed to allow for easy validation, and +# allow manual testing through the repository's "Actions" tab +on: + workflow_dispatch: + push: + branches: + - main + paths: + - .github/workflows/copilot-setup-steps.yml + pull_request: + paths: + - .github/workflows/copilot-setup-steps.yml + +jobs: + # The job MUST be called `copilot-setup-steps` or it will not be picked up by Copilot. + copilot-setup-steps: + runs-on: ubuntu-latest + # Set the permissions to the lowest permissions possible needed for your steps. + # Copilot will be given its own token for its operations. + permissions: + # If you want to clone the repository as part of your setup steps, for example to install dependencies, you'll need the `contents: read` permission. If you don't clone the repository in your setup steps, Copilot will do this for you automatically after the steps complete. + contents: read + + # You can define any steps you want, and they will run before the agent starts. + # If you do not check out your code, Copilot will do this for you. + steps: + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + with: + fetch-depth: 0 # avoid shallow clone so nbgv can do its work. + - name: ⚙ Install prerequisites + run: | + ./init.ps1 -UpgradePrerequisites -NoNuGetCredProvider + dotnet --info + + # Print mono version if it is present. + if (Get-Command mono -ErrorAction SilentlyContinue) { + mono --version + } + shell: pwsh diff --git a/.gitignore b/.gitignore index cc2b1247..1b779930 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. ## -## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore +## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore # User-specific files *.rsuser diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cde5e823..ff095b47 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -45,7 +45,7 @@ This repository can be built on Windows, Linux, and OSX. Building, testing, and packing this repository can be done by using the standard dotnet CLI commands (e.g. `dotnet build`, `dotnet test`, `dotnet pack`, etc.). -[pwsh]: https://docs.microsoft.com/powershell/scripting/install/installing-powershell?view=powershell-6 +[pwsh]: https://learn.microsoft.com/powershell/scripting/install/installing-powershell ## Releases @@ -59,9 +59,9 @@ Push the tag. When your repo is hosted by GitHub and you are using GitHub Actions, you should create a GitHub Release using the standard GitHub UI. Having previously used `nbgv tag` and pushing the tag will help you identify the precise commit and name to use for this release. -After publishing the release, the `.github\workflows\release.yml` workflow will be automatically triggered, which will: +After publishing the release, the `.github/workflows/release.yml` workflow will be automatically triggered, which will: -1. Find the most recent `.github\workflows\build.yml` GitHub workflow run of the tagged release. +1. Find the most recent `.github/workflows/build.yml` GitHub workflow run of the tagged release. 1. Upload the `deployables` artifact from that workflow run to your GitHub Release. 1. If you have `NUGET_API_KEY` defined as a secret variable for your repo or org, any nuget packages in the `deployables` artifact will be pushed to nuget.org. @@ -72,7 +72,7 @@ Trigger the pipeline by adding the `auto-release` tag on a run of your main `azu ## Tutorial and API documentation -API and hand-written docs are found under the `docfx/` directory. and are built by [docfx](https://dotnet.github.io/docfx/). +API and hand-written docs are found under the `docfx/` directory and are built by [docfx](https://dotnet.github.io/docfx/). You can make changes and host the site locally to preview them by switching to that directory and running the `dotnet docfx --serve` command. After making a change, you can rebuild the docs site while the localhost server is running by running `dotnet docfx` again from a separate terminal. @@ -94,11 +94,11 @@ If Renovate is not creating pull requests when you expect it to, check that the ### Maintaining your repo based on this template The best way to keep your repo in sync with Library.Template's evolving features and best practices is to periodically merge the template into your repo: -` + ```ps1 git fetch git checkout origin/main -.\tools\MergeFrom-Template.ps1 +./tools/MergeFrom-Template.ps1 # resolve any conflicts, then commit the merge commit. git push origin -u HEAD ``` diff --git a/CodeQL.yml b/CodeQL.yml new file mode 100644 index 00000000..903500b5 --- /dev/null +++ b/CodeQL.yml @@ -0,0 +1,3 @@ +path_classifiers: + library: + - 'test/**' diff --git a/Directory.Build.props b/Directory.Build.props index c1d04dc2..af3cca15 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -7,6 +7,8 @@ $(RepoRootPath)bin\$(MSBuildProjectName)\ $(RepoRootPath)bin\Packages\$(Configuration)\NuGet\ $(RepoRootPath)bin\Packages\$(Configuration)\Vsix\$(Platform)\ + $(RepoRootPath)bin\Packages\$(Configuration)\Vsix\ + $(VSIXOutputPath) enable disable @@ -15,12 +17,14 @@ true true + + true + true - - false + true $(MSBuildThisFileDirectory) @@ -62,4 +66,6 @@ $(RepositoryUrl)/releases/tag/v$(Version) + + diff --git a/Directory.Build.targets b/Directory.Build.targets index 5f8e2a6d..b4afce0a 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -4,6 +4,4 @@ - - diff --git a/Directory.Packages.props b/Directory.Packages.props index 7a7239fc..e5423a30 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -5,7 +5,7 @@ true true - 2.0.187 + 2.0.201 @@ -16,10 +16,10 @@ - + - - + + @@ -29,7 +29,8 @@ - + + diff --git a/SECURITY.md b/SECURITY.md index 0dc4b6a7..29306956 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -4,7 +4,7 @@ Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). -If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/previous-versions/tn-archive/cc751383(v=technet.10)), please report it to us as described below. +If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://learn.microsoft.com/previous-versions/tn-archive/cc751383(v=technet.10)), please report it to us as described below. ## Reporting Security Issues diff --git a/SUPPORT.md b/SUPPORT.md index ecbe64c0..a324f300 100644 --- a/SUPPORT.md +++ b/SUPPORT.md @@ -2,7 +2,7 @@ **REPO OWNER**: Do you want Customer Service & Support (CSS) support for this product/project? -- **No CSS support:** Fill out this template with information about how to file issues and get help. +- **No CSS support:** Review the template below and edit as necessary. - **Yes CSS support:** Fill out an intake form at [aka.ms/spot](https://aka.ms/spot). CSS will work with/help you to determine next steps. More details also available at [aka.ms/onboardsupport](https://aka.ms/onboardsupport). - **Not sure?** Fill out a SPOT intake as though the answer were "Yes". CSS will help you decide. @@ -12,14 +12,15 @@ ## How to file issues and get help -This project uses GitHub Issues to track bugs and feature requests. Please search the existing -issues before filing new issues to avoid duplicates. For new issues, file your bug or -feature request as a new Issue. +This project uses GitHub Issues to track bugs and feature requests. +Please search the existing issues before filing new issues to avoid duplicates. +For new issues, file your bug or feature request as a new Issue. -For help and questions about using this project, please **REPO MAINTAINER: INSERT INSTRUCTIONS HERE -FOR HOW TO ENGAGE REPO OWNERS OR COMMUNITY FOR HELP. COULD BE A STACK OVERFLOW TAG OR OTHER -CHANNEL. WHERE WILL YOU HELP PEOPLE?**. +Note that this repo is primarily used for Visual Studio and related products and support will be focused on those scenarios. ## Microsoft Support Policy -Support for this **PROJECT or PRODUCT** is limited to the resources listed above. +Microsoft support for this software is available only for its use in officially supported products such as Visual Studio. +Support and servicing is limited to the latest released version. +For more information, see [Visual Studio Product Lifecycle and Servicing](https://learn.microsoft.com/visualstudio/productinfo/vs-servicing). +Assisted support is available from a professional support engineer by opening a ticket with the [Microsoft assisted support team](https://support.serviceshub.microsoft.com/supportforbusiness/onboarding). diff --git a/azure-pipelines/Install-NuGetPackage.ps1 b/azure-pipelines/Install-NuGetPackage.ps1 deleted file mode 100644 index f1db577a..00000000 --- a/azure-pipelines/Install-NuGetPackage.ps1 +++ /dev/null @@ -1,55 +0,0 @@ -<# -.SYNOPSIS - Installs a NuGet package. -.PARAMETER PackageID - The Package ID to install. -.PARAMETER Version - The version of the package to install. If unspecified, the latest stable release is installed. -.PARAMETER Source - The package source feed to find the package to install from. -.PARAMETER PackagesDir - The directory to install the package to. By default, it uses the Packages folder at the root of the repo. -.PARAMETER ConfigFile - The nuget.config file to use. By default, it uses :/nuget.config. -.OUTPUTS - System.String. The path to the installed package. -#> -[CmdletBinding(SupportsShouldProcess=$true,ConfirmImpact='Low')] -Param( - [Parameter(Position=1,Mandatory=$true)] - [string]$PackageId, - [Parameter()] - [string]$Version, - [Parameter()] - [string]$Source, - [Parameter()] - [switch]$Prerelease, - [Parameter()] - [string]$PackagesDir="$PSScriptRoot\..\packages", - [Parameter()] - [string]$ConfigFile="$PSScriptRoot\..\nuget.config", - [Parameter()] - [ValidateSet('Quiet','Normal','Detailed')] - [string]$Verbosity='normal' -) - -$nugetPath = & "$PSScriptRoot\..\tools\Get-NuGetTool.ps1" - -try { - Write-Verbose "Installing $PackageId..." - $nugetArgs = "Install",$PackageId,"-OutputDirectory",$PackagesDir,'-ConfigFile',$ConfigFile - if ($Version) { $nugetArgs += "-Version",$Version } - if ($Source) { $nugetArgs += "-FallbackSource",$Source } - if ($Prerelease) { $nugetArgs += "-Prerelease" } - $nugetArgs += '-Verbosity',$Verbosity - - if ($PSCmdlet.ShouldProcess($PackageId, 'nuget install')) { - $p = Start-Process $nugetPath $nugetArgs -NoNewWindow -Wait -PassThru - if ($null -ne $p.ExitCode -and $p.ExitCode -ne 0) { throw } - } - - # Provide the path to the installed package directory to our caller. - Write-Output (Get-ChildItem "$PackagesDir\$PackageId.*")[0].FullName -} finally { - Pop-Location -} diff --git a/azure-pipelines/NuGetSbom.props b/azure-pipelines/NuGetSbom.props new file mode 100644 index 00000000..dbfee864 --- /dev/null +++ b/azure-pipelines/NuGetSbom.props @@ -0,0 +1,6 @@ + + + true + 2 + + diff --git a/azure-pipelines/NuGetSbom.targets b/azure-pipelines/NuGetSbom.targets deleted file mode 100644 index a2599e88..00000000 --- a/azure-pipelines/NuGetSbom.targets +++ /dev/null @@ -1,12 +0,0 @@ - - - true - $(TargetsForTfmSpecificBuildOutput);IncludeSbomInNupkg - - - - - - - - diff --git a/azure-pipelines/PostPRMessage.ps1 b/azure-pipelines/PostPRMessage.ps1 index 4a2b7886..4075f392 100644 --- a/azure-pipelines/PostPRMessage.ps1 +++ b/azure-pipelines/PostPRMessage.ps1 @@ -8,7 +8,7 @@ param( $CommentState='Active' ) -# See https://docs.microsoft.com/en-us/dotnet/api/microsoft.teamfoundation.sourcecontrol.webapi.commentthreadstatus?view=azure-devops-dotnet +# See https://learn.microsoft.com/dotnet/api/microsoft.teamfoundation.sourcecontrol.webapi.commentthreadstatus if ($CommentState -eq 'Active') { $StatusCode = 1 } elseif ($CommentState -eq 'ByDesign') { @@ -38,7 +38,7 @@ $body = ConvertTo-Json @{ Write-Verbose "Posting JSON payload: `n$Body" # Post the message to the Pull Request -# https://docs.microsoft.com/en-us/rest/api/azure/devops/git/pull%20request%20threads?view=azure-devops-rest-5.1 +# https://learn.microsoft.com/rest/api/azure/devops/git/pull-request-threads $url = "$($env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI)$env:SYSTEM_TEAMPROJECTID/_apis/git/repositories/$($env:BUILD_REPOSITORY_NAME)/pullRequests/$($env:SYSTEM_PULLREQUEST_PULLREQUESTID)/threads?api-version=5.1" if ($PSCmdlet.ShouldProcess($url, 'Post comment via REST call')) { try { diff --git a/azure-pipelines/apiscan.yml b/azure-pipelines/apiscan.yml index c3c8aed1..a1d07e10 100644 --- a/azure-pipelines/apiscan.yml +++ b/azure-pipelines/apiscan.yml @@ -49,8 +49,10 @@ jobs: isLargeApp: false toolVersion: Latest preserveLogsFolder: true + azureSubscription: VSEng-APIScanSC env: - AzureServicesAuthConnectionString: runAs=App;AppId=$(ApiScanClientId) + AzureServicesAuthConnectionString: $(APIScanAuthConnectionString) + SYSTEM_ACCESSTOKEN: $(System.AccessToken) # File bugs when APIScan finds issues - task: TSAUpload@2 diff --git a/azure-pipelines/build.yml b/azure-pipelines/build.yml index c246d514..e7d2e299 100644 --- a/azure-pipelines/build.yml +++ b/azure-pipelines/build.yml @@ -123,6 +123,7 @@ jobs: signType: test sbom: enabled: true + sbomToolVersion: 5.0.3 localization: enabled: ${{ parameters.EnableLocalization }} ${{ if eq(variables['Build.Reason'], 'pullRequest') }}: @@ -141,6 +142,9 @@ jobs: - ${{ if parameters.EnableOptProf }}: - powershell: Write-Host "##vso[task.setvariable variable=PROFILINGINPUTSDROPNAME]$(tools/variables/ProfilingInputsDropName.ps1)" displayName: ⚙ Set ProfilingInputsDropName for optprof + sdl: + binskim: + analyzeTargetGlob: $(Build.ArtifactStagingDirectory)\symbols-Windows\** outputParentDirectory: $(Build.ArtifactStagingDirectory) outputs: @@ -199,8 +203,10 @@ jobs: IsOptProf: ${{ parameters.IsOptProf }} - ${{ if and(parameters.EnableDotNetFormatCheck, not(parameters.EnableLinuxBuild)) }}: - - script: dotnet format --verify-no-changes --no-restore + - script: dotnet format --verify-no-changes displayName: 💅 Verify formatted code + env: + dotnetformat: true # part of a workaround for https://github.com/dotnet/sdk/issues/44951 - ${{ if eq(variables['system.collectionId'], '011b8bdf-6d56-4f87-be0d-0092136884d9') }}: - template: microbuild.after.yml @@ -220,6 +226,7 @@ jobs: signing: enabled: false # enable when building unique artifacts on this agent that must be signed signType: real + signWithProd: true outputParentDirectory: $(Build.ArtifactStagingDirectory) outputs: - ${{ each artifact_name in parameters.artifact_names }}: @@ -238,9 +245,12 @@ jobs: parameters: Is1ESPT: ${{ parameters.Is1ESPT }} RunTests: ${{ parameters.RunTests }} + BuildRequiresAccessToken: ${{ parameters.RealSign }} # Real signing on non-Windows machines requires passing through access token to build steps that sign - ${{ if parameters.EnableDotNetFormatCheck }}: - - script: dotnet format --verify-no-changes --no-restore + - script: dotnet format --verify-no-changes displayName: 💅 Verify formatted code + env: + dotnetformat: true # part of a workaround for https://github.com/dotnet/sdk/issues/44951 - ${{ if parameters.EnableMacOSBuild }}: - job: macOS @@ -252,6 +262,7 @@ jobs: signing: enabled: false # enable when building unique artifacts on this agent that must be signed signType: real + signWithProd: true outputParentDirectory: $(Build.ArtifactStagingDirectory) outputs: - ${{ each artifact_name in parameters.artifact_names }}: @@ -270,6 +281,7 @@ jobs: parameters: Is1ESPT: ${{ parameters.Is1ESPT }} RunTests: ${{ parameters.RunTests }} + BuildRequiresAccessToken: ${{ parameters.RealSign }} # Real signing on non-Windows machines requires passing through access token to build steps that sign - job: WrapUp dependsOn: @@ -280,6 +292,8 @@ jobs: - macOS pool: ${{ parameters.windowsPool }} # Use Windows agent because PublishSymbols task requires it (https://github.com/microsoft/azure-pipelines-tasks/issues/13821). condition: succeededOrFailed() + variables: + ONEES_ENFORCED_CODEQL_ENABLED: false # CodeQL runs on build jobs, we don't need it here ${{ if eq(variables['system.collectionId'], '011b8bdf-6d56-4f87-be0d-0092136884d9') }}: templateContext: ${{ if not(parameters.RealSign) }}: diff --git a/azure-pipelines/dotnet.yml b/azure-pipelines/dotnet.yml index 2abca9eb..947cfc1a 100644 --- a/azure-pipelines/dotnet.yml +++ b/azure-pipelines/dotnet.yml @@ -5,11 +5,17 @@ parameters: default: false - name: Is1ESPT type: boolean +- name: BuildRequiresAccessToken + type: boolean + default: false steps: - script: dotnet build -t:build,pack --no-restore -c $(BuildConfiguration) -warnAsError -warnNotAsError:NU1901,NU1902,NU1903,NU1904,LOCTASK002 /bl:"$(Build.ArtifactStagingDirectory)/build_logs/build.binlog" displayName: 🛠 dotnet build + ${{ if parameters.BuildRequiresAccessToken }}: + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) - ${{ if not(parameters.IsOptProf) }}: - powershell: tools/dotnet-test-cloud.ps1 -Configuration $(BuildConfiguration) -Agent $(Agent.JobName) -PublishResults diff --git a/azure-pipelines/libtemplate-update.yml b/azure-pipelines/libtemplate-update.yml index 3d7fb7e3..384be7cf 100644 --- a/azure-pipelines/libtemplate-update.yml +++ b/azure-pipelines/libtemplate-update.yml @@ -35,6 +35,8 @@ extends: name: AzurePipelines-EO demands: - ImageOverride -equals 1ESPT-Windows2022 + credscan: + enabled: false stages: - stage: Merge diff --git a/azure-pipelines/microbuild.before.yml b/azure-pipelines/microbuild.before.yml index 05acd319..d09310b1 100644 --- a/azure-pipelines/microbuild.before.yml +++ b/azure-pipelines/microbuild.before.yml @@ -17,9 +17,9 @@ parameters: steps: - ${{ if and(not(parameters.IsOptProf), ne(variables['Build.Reason'], 'PullRequest')) }}: # notice@0 requires CG detection to run first, and non-default branches don't inject it automatically. - - ${{ if ne(variables['Build.SourceBranch'], 'refs/heads/main') }}: - - task: ComponentGovernanceComponentDetection@0 - displayName: 🔍 Component Detection + # default branch injection (main) is happening too late for notice@0 to run successfully. Adding this as a workaround. + - task: ComponentGovernanceComponentDetection@0 + displayName: 🔍 Component Detection - task: notice@0 displayName: 🛠️ Generate NOTICE file @@ -42,7 +42,7 @@ steps: - task: MicroBuildSigningPlugin@4 inputs: - signType: Real + signType: Test zipSources: false displayName: 🔧 Install MicroBuild Signing Plugin diff --git a/azure-pipelines/prepare-insertion-stages.yml b/azure-pipelines/prepare-insertion-stages.yml index cfe28743..fdce906a 100644 --- a/azure-pipelines/prepare-insertion-stages.yml +++ b/azure-pipelines/prepare-insertion-stages.yml @@ -30,12 +30,15 @@ stages: - download: current artifact: symbols-legacy displayName: 🔻 Download symbols-legacy artifact - - task: MicroBuildArchiveSymbols@5 + - task: MicroBuildArchiveSymbols@6 displayName: 🔣 Archive symbols to Symweb inputs: SymbolsFeatureName: $(SymbolsFeatureName) SymbolsProject: VS SymbolsAgentPath: $(Pipeline.Workspace)/symbols-legacy + azureSubscription: Vseng-SymbolsUpload + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) - ${{ if parameters.PackagePush }}: - job: push diff --git a/azure-pipelines/unofficial.yml b/azure-pipelines/unofficial.yml index 9ca100ea..d7232735 100644 --- a/azure-pipelines/unofficial.yml +++ b/azure-pipelines/unofficial.yml @@ -57,6 +57,8 @@ extends: parameters: sdl: sourceAnalysisPool: VSEngSS-MicroBuild2022-1ES + credscan: + enabled: false suppression: suppressionFile: $(System.DefaultWorkingDirectory)\azure-pipelines\falsepositives.gdnsuppress enableProductionSDL: ${{ parameters.EnableProductionSDL }} diff --git a/azure-pipelines/vs-validation.yml b/azure-pipelines/vs-validation.yml index 3a40395e..b9d46b7c 100644 --- a/azure-pipelines/vs-validation.yml +++ b/azure-pipelines/vs-validation.yml @@ -28,6 +28,8 @@ extends: parameters: sdl: sourceAnalysisPool: VSEngSS-MicroBuild2022-1ES + credscan: + enabled: false stages: - stage: Build diff --git a/azurepipelines-coverage.yml b/azurepipelines-coverage.yml index 0cd5dad3..e2dd1f50 100644 --- a/azurepipelines-coverage.yml +++ b/azurepipelines-coverage.yml @@ -1,4 +1,4 @@ -# https://learn.microsoft.com/azure/devops/pipelines/test/codecoverage-for-pullrequests?view=azure-devops +# https://learn.microsoft.com/azure/devops/pipelines/test/codecoverage-for-pullrequests coverage: status: comments: on # add comment to PRs reporting diff in coverage of modified files diff --git a/global.json b/global.json index ae74daa7..a5461699 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "9.0.203", + "version": "9.0.305", "rollForward": "patch", "allowPrerelease": false }, diff --git a/init.ps1 b/init.ps1 index e8c2602e..8bb78ad8 100755 --- a/init.ps1 +++ b/init.ps1 @@ -126,7 +126,7 @@ try { } } - $InstallNuGetPkgScriptPath = "$PSScriptRoot\azure-pipelines\Install-NuGetPackage.ps1" + $InstallNuGetPkgScriptPath = "$PSScriptRoot\tools\Install-NuGetPackage.ps1" $nugetVerbosity = 'quiet' if ($Verbose) { $nugetVerbosity = 'normal' } $MicroBuildPackageSource = 'https://pkgs.dev.azure.com/devdiv/_packaging/MicroBuildToolset%40Local/nuget/v3/index.json' diff --git a/tools/Convert-PDB.ps1 b/tools/Convert-PDB.ps1 index f119a164..7e1303a2 100644 --- a/tools/Convert-PDB.ps1 +++ b/tools/Convert-PDB.ps1 @@ -30,7 +30,13 @@ if (-not (Test-Path $pdb2pdbpath)) { if (-not (Test-Path $baseDir)) { New-Item -Type Directory -Path $baseDir | Out-Null } $baseDir = (Resolve-Path $baseDir).Path # Normalize it Write-Verbose "& (& $PSScriptRoot/Get-NuGetTool.ps1) install Microsoft.DiaSymReader.Pdb2Pdb -version $version -PackageSaveMode nuspec -OutputDirectory $baseDir -Source https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json | Out-Null" - & (& $PSScriptRoot/Get-NuGetTool.ps1) install Microsoft.DiaSymReader.Pdb2Pdb -version $version -PackageSaveMode nuspec -OutputDirectory $baseDir -Source https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json | Out-Null + # This package originally comes from the https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json feed. + # Add this feed as an upstream to whatever feed is in nuget.config if this step fails. + & (& $PSScriptRoot/Get-NuGetTool.ps1) install Microsoft.DiaSymReader.Pdb2Pdb -version $version -PackageSaveMode nuspec -OutputDirectory $baseDir | Out-Null + if ($LASTEXITCODE -ne 0) { + Write-Error "Failed to install Microsoft.DiaSymReader.Pdb2Pdb. Consider adding https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json as an upstream to your nuget.config feed." + return + } } $args = $DllPath,'/out',$OutputPath,'/nowarn','0021' diff --git a/tools/Get-3rdPartySymbolFiles.ps1 b/tools/Get-3rdPartySymbolFiles.ps1 new file mode 100644 index 00000000..ef6bbef2 --- /dev/null +++ b/tools/Get-3rdPartySymbolFiles.ps1 @@ -0,0 +1,91 @@ +Function Get-FileFromWeb([Uri]$Uri, $OutFile) { + $OutDir = Split-Path $OutFile + if (!(Test-Path $OutFile)) { + Write-Verbose "Downloading $Uri..." + if (!(Test-Path $OutDir)) { New-Item -ItemType Directory -Path $OutDir | Out-Null } + try { + (New-Object System.Net.WebClient).DownloadFile($Uri, $OutFile) + } + finally { + # This try/finally causes the script to abort + } + } +} + +Function Unzip($Path, $OutDir) { + $OutDir = (New-Item -ItemType Directory -Path $OutDir -Force).FullName + Add-Type -AssemblyName System.IO.Compression.FileSystem + + # Start by extracting to a temporary directory so that there are no file conflicts. + [System.IO.Compression.ZipFile]::ExtractToDirectory($Path, "$OutDir.out") + + # Now move all files from the temp directory to $OutDir, overwriting any files. + Get-ChildItem -Path "$OutDir.out" -Recurse -File | ForEach-Object { + $destinationPath = Join-Path -Path $OutDir -ChildPath $_.FullName.Substring("$OutDir.out".Length).TrimStart([io.path]::DirectorySeparatorChar, [io.path]::AltDirectorySeparatorChar) + if (!(Test-Path -Path (Split-Path -Path $destinationPath -Parent))) { + New-Item -ItemType Directory -Path (Split-Path -Path $destinationPath -Parent) | Out-Null + } + Move-Item -Path $_.FullName -Destination $destinationPath -Force + } + Remove-Item -Path "$OutDir.out" -Recurse -Force +} + +Function Get-SymbolsFromPackage($id, $version) { + $symbolPackagesPath = "$PSScriptRoot/../obj/SymbolsPackages" + New-Item -ItemType Directory -Path $symbolPackagesPath -Force | Out-Null + $nupkgPath = Join-Path $symbolPackagesPath "$id.$version.nupkg" + $snupkgPath = Join-Path $symbolPackagesPath "$id.$version.snupkg" + $unzippedPkgPath = Join-Path $symbolPackagesPath "$id.$version" + Get-FileFromWeb -Uri "https://www.nuget.org/api/v2/package/$id/$version" -OutFile $nupkgPath + Get-FileFromWeb -Uri "https://www.nuget.org/api/v2/symbolpackage/$id/$version" -OutFile $snupkgPath + + Unzip -Path $nupkgPath -OutDir $unzippedPkgPath + Unzip -Path $snupkgPath -OutDir $unzippedPkgPath + + Get-ChildItem -Recurse -LiteralPath $unzippedPkgPath -Filter *.pdb | % { + # Collect the DLLs/EXEs as well. + $rootName = Join-Path $_.Directory $_.BaseName + if ($rootName.EndsWith('.ni')) { + $rootName = $rootName.Substring(0, $rootName.Length - 3) + } + + $dllPath = "$rootName.dll" + $exePath = "$rootName.exe" + if (Test-Path $dllPath) { + $BinaryImagePath = $dllPath + } + elseif (Test-Path $exePath) { + $BinaryImagePath = $exePath + } + else { + Write-Warning "`"$_`" found with no matching binary file." + $BinaryImagePath = $null + } + + if ($BinaryImagePath) { + Write-Output $BinaryImagePath + Write-Output $_.FullName + } + } +} + +Function Get-PackageVersion($id) { + $versionProps = [xml](Get-Content -LiteralPath $PSScriptRoot\..\Directory.Packages.props) + $version = $versionProps.Project.ItemGroup.PackageVersion | ? { $_.Include -eq $id } | % { $_.Version } + if (!$version) { + Write-Error "No package version found in Directory.Packages.props for the package '$id'" + } + + $version +} + +# All 3rd party packages for which symbols packages are expected should be listed here. +# These must all be sourced from nuget.org, as it is the only feed that supports symbol packages. +$3rdPartyPackageIds = @() + +$3rdPartyPackageIds | % { + $version = Get-PackageVersion $_ + if ($version) { + Get-SymbolsFromPackage -id $_ -version $version + } +} diff --git a/tools/Get-NuGetTool.ps1 b/tools/Get-NuGetTool.ps1 index 8a3b9eed..bed0cba1 100644 --- a/tools/Get-NuGetTool.ps1 +++ b/tools/Get-NuGetTool.ps1 @@ -6,7 +6,7 @@ #> Param( [Parameter()] - [string]$NuGetVersion='6.12.2' + [string]$NuGetVersion='6.14.0' ) $toolsPath = & "$PSScriptRoot\Get-TempToolsPath.ps1" diff --git a/tools/Get-ProcDump.ps1 b/tools/Get-ProcDump.ps1 index 1493fe4b..6fba954d 100644 --- a/tools/Get-ProcDump.ps1 +++ b/tools/Get-ProcDump.ps1 @@ -8,7 +8,7 @@ $procDumpToolPath = "$baseDir\procdump.$version\bin" if (-not (Test-Path $procDumpToolPath)) { if (-not (Test-Path $baseDir)) { New-Item -Type Directory -Path $baseDir | Out-Null } $baseDir = (Resolve-Path $baseDir).Path # Normalize it - & (& $PSScriptRoot\Get-NuGetTool.ps1) install procdump -version $version -PackageSaveMode nuspec -OutputDirectory $baseDir -Source https://api.nuget.org/v3/index.json | Out-Null + & (& $PSScriptRoot\Get-NuGetTool.ps1) install procdump -version $version -PackageSaveMode nuspec -OutputDirectory $baseDir | Out-Null } (Resolve-Path $procDumpToolPath).Path diff --git a/tools/Get-SymbolFiles.ps1 b/tools/Get-SymbolFiles.ps1 index b5063cec..70656c0f 100644 --- a/tools/Get-SymbolFiles.ps1 +++ b/tools/Get-SymbolFiles.ps1 @@ -43,7 +43,7 @@ $PDBs |% { } } |% { # Collect the DLLs/EXEs as well. - $rootName = "$($_.Directory)/$($_.BaseName)" + $rootName = Join-Path $_.Directory $_.BaseName if ($rootName.EndsWith('.ni')) { $rootName = $rootName.Substring(0, $rootName.Length - 3) } diff --git a/tools/Install-DotNetSdk.ps1 b/tools/Install-DotNetSdk.ps1 index e08571bc..402b4307 100644 --- a/tools/Install-DotNetSdk.ps1 +++ b/tools/Install-DotNetSdk.ps1 @@ -36,7 +36,11 @@ if (!(Test-Path $DotNetInstallScriptRoot)) { New-Item -ItemType Directory -Path $DotNetInstallScriptRoot = Resolve-Path $DotNetInstallScriptRoot # Look up actual required .NET SDK version from global.json -$sdkVersion = & "$PSScriptRoot/variables/DotNetSdkVersion.ps1" +$sdks = @(New-Object PSObject -Property @{ Version = & "$PSScriptRoot/variables/DotNetSdkVersion.ps1" }) + +# Sometimes a repo requires extra SDKs to be installed (e.g. msbuild.locator scenarios running in tests). +# In such a circumstance, a precise SDK version or a channel can be added as in the example below: +# $sdks += New-Object PSObject -Property @{ Channel = '8.0' } If ($IncludeX86 -and ($IsMacOS -or $IsLinux)) { Write-Verbose "Ignoring -IncludeX86 switch because 32-bit runtimes are only supported on Windows." @@ -191,13 +195,16 @@ if ($InstallLocality -eq 'machine') { $DotNetInstallDir = '/usr/share/dotnet' } else { $restartRequired = $false - if ($PSCmdlet.ShouldProcess(".NET SDK $sdkVersion", "Install")) { - Install-DotNet -Version $sdkVersion -Architecture $arch - $restartRequired = $restartRequired -or ($LASTEXITCODE -eq 3010) - - if ($IncludeX86) { - Install-DotNet -Version $sdkVersion -Architecture x86 + $sdks |% { + if ($_.Version) { $version = $_.Version } else { $version = $_.Channel } + if ($PSCmdlet.ShouldProcess(".NET SDK $_", "Install")) { + Install-DotNet -Version $version -Architecture $arch $restartRequired = $restartRequired -or ($LASTEXITCODE -eq 3010) + + if ($IncludeX86) { + Install-DotNet -Version $version -Architecture x86 + $restartRequired = $restartRequired -or ($LASTEXITCODE -eq 3010) + } } } @@ -296,29 +303,33 @@ $DotNetInstallScriptPathExpression = "& '$DotNetInstallScriptPathExpression'" $anythingInstalled = $false $global:LASTEXITCODE = 0 -if ($PSCmdlet.ShouldProcess(".NET SDK $sdkVersion", "Install")) { - $anythingInstalled = $true - Invoke-Expression -Command "$DotNetInstallScriptPathExpression -Version $sdkVersion -Architecture $arch -InstallDir $DotNetInstallDir $switches" +$sdks |% { + if ($_.Version) { $parameters = '-Version', $_.Version } else { $parameters = '-Channel', $_.Channel } - if ($LASTEXITCODE -ne 0) { - Write-Error ".NET SDK installation failure: $LASTEXITCODE" - exit $LASTEXITCODE - } -} else { - Invoke-Expression -Command "$DotNetInstallScriptPathExpression -Version $sdkVersion -Architecture $arch -InstallDir $DotNetInstallDir $switches -DryRun" -} - -if ($IncludeX86) { - if ($PSCmdlet.ShouldProcess(".NET x86 SDK $sdkVersion", "Install")) { + if ($PSCmdlet.ShouldProcess(".NET SDK $_", "Install")) { $anythingInstalled = $true - Invoke-Expression -Command "$DotNetInstallScriptPathExpression -Version $sdkVersion -Architecture x86 -InstallDir $DotNetX86InstallDir $switches" + Invoke-Expression -Command "$DotNetInstallScriptPathExpression $parameters -Architecture $arch -InstallDir $DotNetInstallDir $switches" if ($LASTEXITCODE -ne 0) { - Write-Error ".NET x86 SDK installation failure: $LASTEXITCODE" + Write-Error ".NET SDK installation failure: $LASTEXITCODE" exit $LASTEXITCODE } } else { - Invoke-Expression -Command "$DotNetInstallScriptPathExpression -Version $sdkVersion -Architecture x86 -InstallDir $DotNetX86InstallDir $switches -DryRun" + Invoke-Expression -Command "$DotNetInstallScriptPathExpression $parameters -Architecture $arch -InstallDir $DotNetInstallDir $switches -DryRun" + } + + if ($IncludeX86) { + if ($PSCmdlet.ShouldProcess(".NET x86 SDK $_", "Install")) { + $anythingInstalled = $true + Invoke-Expression -Command "$DotNetInstallScriptPathExpression $parameters -Architecture x86 -InstallDir $DotNetX86InstallDir $switches" + + if ($LASTEXITCODE -ne 0) { + Write-Error ".NET x86 SDK installation failure: $LASTEXITCODE" + exit $LASTEXITCODE + } + } else { + Invoke-Expression -Command "$DotNetInstallScriptPathExpression $parameters -Architecture x86 -InstallDir $DotNetX86InstallDir $switches -DryRun" + } } } diff --git a/tools/Install-NuGetPackage.ps1 b/tools/Install-NuGetPackage.ps1 new file mode 100644 index 00000000..3c11b0f6 --- /dev/null +++ b/tools/Install-NuGetPackage.ps1 @@ -0,0 +1,63 @@ +<# +.SYNOPSIS + Installs a NuGet package. +.PARAMETER PackageID + The Package ID to install. +.PARAMETER Version + The version of the package to install. If unspecified, the latest stable release is installed. +.PARAMETER Source + The package source feed to find the package to install from. +.PARAMETER Prerelease + Include prerelease packages when searching for the latest version. +.PARAMETER ExcludeVersion + Installs the package without adding the version to the folder name. +.PARAMETER DirectDownload + Bypass the local cache when downloading packages. +.PARAMETER PackagesDir + The directory to install the package to. By default, it uses the Packages folder at the root of the repo. +.PARAMETER ConfigFile + The nuget.config file to use. By default, it uses :/nuget.config. +.OUTPUTS + System.String. The path to the installed package. +#> +[CmdletBinding(SupportsShouldProcess=$true,ConfirmImpact='Low')] +Param( + [Parameter(Position=1,Mandatory=$true)] + [string]$PackageId, + [Parameter()] + [string]$Version, + [Parameter()] + [string]$Source, + [Parameter()] + [switch]$Prerelease, + [Parameter()] + [switch]$ExcludeVersion, + [Parameter()] + [switch]$DirectDownload, + [Parameter()] + [string]$PackagesDir="$PSScriptRoot\..\packages", + [Parameter()] + [string]$ConfigFile="$PSScriptRoot\..\nuget.config", + [Parameter()] + [ValidateSet('Quiet','Normal','Detailed')] + [string]$Verbosity='normal' +) + +$nugetPath = & "$PSScriptRoot\Get-NuGetTool.ps1" + +Write-Verbose "Installing $PackageId..." +$nugetArgs = "Install",$PackageId,"-OutputDirectory",$PackagesDir,'-ConfigFile',$ConfigFile +if ($Version) { $nugetArgs += "-Version",$Version } +if ($Source) { $nugetArgs += "-FallbackSource",$Source } +if ($Prerelease) { $nugetArgs += "-Prerelease" } +if ($ExcludeVersion) { $nugetArgs += '-ExcludeVersion' } +if ($DirectDownload) { $nugetArgs += '-DirectDownload' } +$nugetArgs += '-Verbosity',$Verbosity + +if ($PSCmdlet.ShouldProcess($PackageId, 'nuget install')) { + $p = Start-Process $nugetPath $nugetArgs -NoNewWindow -Wait -PassThru + if ($null -ne $p.ExitCode -and $p.ExitCode -ne 0) { throw } +} + +# Provide the path to the installed package directory to our caller. +Write-Output (Get-ChildItem "$PackagesDir\$PackageId.*")[0].FullName diff --git a/tools/artifacts/VSInsertion.ps1 b/tools/artifacts/VSInsertion.ps1 index ffc7e29a..a5b940b5 100644 --- a/tools/artifacts/VSInsertion.ps1 +++ b/tools/artifacts/VSInsertion.ps1 @@ -20,8 +20,8 @@ $PackagesRoot = "$RepoRoot/bin/Packages/$BuildConfiguration" $NuGetPackages = "$PackagesRoot/NuGet" $VsixPackages = "$PackagesRoot/Vsix" -if (!(Test-Path $NuGetPackages)) { - Write-Warning "Skipping because NuGet packages haven't been built yet." +if (!(Test-Path $NuGetPackages) -and !(Test-Path $VsixPackages)) { + Write-Warning "Skipping because NuGet and VSIX packages haven't been built yet." return @{} } diff --git a/tools/artifacts/symbols.ps1 b/tools/artifacts/symbols.ps1 index 9e2c7bd5..b5882678 100644 --- a/tools/artifacts/symbols.ps1 +++ b/tools/artifacts/symbols.ps1 @@ -1,7 +1,10 @@ $BinPath = [System.IO.Path]::GetFullPath("$PSScriptRoot/../../bin") +$3rdPartyPath = [System.IO.Path]::GetFullPath("$PSScriptRoot/../../obj/SymbolsPackages") if (!(Test-Path $BinPath)) { return } $symbolfiles = & "$PSScriptRoot/../Get-SymbolFiles.ps1" -Path $BinPath | Get-Unique +$3rdPartyFiles = & "$PSScriptRoot/../Get-3rdPartySymbolFiles.ps1" @{ "$BinPath" = $SymbolFiles; + "$3rdPartyPath" = $3rdPartyFiles; }