├── .azuredevops └── dependabot.yml ├── .config └── dotnet-tools.json ├── .devcontainer ├── Dockerfile └── devcontainer.json ├── .editorconfig ├── .gitattributes ├── .github ├── .editorconfig ├── actions │ └── publish-artifacts │ │ └── action.yaml ├── renovate.json └── workflows │ ├── docs.yml │ ├── docs_validate.yml │ └── libtemplate-update.yml ├── .gitignore ├── .prettierrc.yaml ├── .vscode ├── extensions.json └── settings.json ├── CONTRIBUTING.md ├── Directory.Build.props ├── Directory.Build.rsp ├── Directory.Build.targets ├── Directory.Packages.props ├── LICENSE ├── Microsoft.VisualStudio.Validation.sln ├── README.md ├── SECURITY.md ├── SUPPORT.md ├── azure-pipelines.yml ├── azure-pipelines ├── Archive-SourceCode.ps1 ├── BuildStageVariables.yml ├── Get-InsertionPRId.ps1 ├── GlobalVariables.yml ├── Install-NuGetPackage.ps1 ├── Merge-CodeCoverage.ps1 ├── NuGetSbom.props ├── OptProf.yml ├── OptProf_part2.yml ├── PoliCheckExclusions.xml ├── PostPRMessage.ps1 ├── TSAOptions.json ├── WIFtoPATauth.yml ├── apiscan.yml ├── archive-sourcecode.yml ├── build.yml ├── dotnet.yml ├── falsepositives.gdnsuppress ├── install-dependencies.yml ├── libtemplate-update.yml ├── microbuild.after.yml ├── microbuild.before.yml ├── no_authenticode.txt ├── no_strongname.txt ├── official.yml ├── prepare-insertion-stages.yml ├── publish-codecoverage.yml ├── publish-symbols.yml ├── publish_artifacts.ps1 ├── release-deployment-prep.yml ├── release.yml ├── schedule-only-steps.yml ├── unofficial.yml ├── vs-insertion.yml └── vs-validation.yml ├── azurepipelines-coverage.yml ├── docfx ├── .gitignore ├── docfx.json ├── docs │ ├── features.md │ ├── getting-started.md │ └── toc.yml ├── index.md └── toc.yml ├── global.json ├── init.cmd ├── init.ps1 ├── loc ├── lci │ └── Microsoft.VisualStudio.Validation.dll.lci └── lcl │ ├── CHS │ └── Microsoft.VisualStudio.Validation.dll.lcl │ ├── CHT │ └── Microsoft.VisualStudio.Validation.dll.lcl │ ├── CSY │ └── Microsoft.VisualStudio.Validation.dll.lcl │ ├── DEU │ └── Microsoft.VisualStudio.Validation.dll.lcl │ ├── ESN │ └── Microsoft.VisualStudio.Validation.dll.lcl │ ├── FRA │ └── Microsoft.VisualStudio.Validation.dll.lcl │ ├── ITA │ └── Microsoft.VisualStudio.Validation.dll.lcl │ ├── JPN │ └── Microsoft.VisualStudio.Validation.dll.lcl │ ├── KOR │ └── Microsoft.VisualStudio.Validation.dll.lcl │ ├── PLK │ └── Microsoft.VisualStudio.Validation.dll.lcl │ ├── PTB │ └── Microsoft.VisualStudio.Validation.dll.lcl │ ├── RUS │ └── Microsoft.VisualStudio.Validation.dll.lcl │ └── TRK │ └── Microsoft.VisualStudio.Validation.dll.lcl ├── nuget.config ├── settings.VisualStudio.json ├── src ├── .editorconfig ├── AssemblyInfo.cs ├── AssemblyInfo.vb ├── Directory.Build.props ├── Directory.Build.targets ├── Microsoft.VisualStudio.Validation │ ├── AssemblyInfo.cs │ ├── Assumes.InternalErrorException.cs │ ├── Assumes.cs │ ├── EventHandlerExtensions.cs │ ├── ExceptionExtensions.cs │ ├── IDisposableObservable.cs │ ├── Microsoft.VisualStudio.Validation.csproj │ ├── Polyfill.cs │ ├── PrivateErrorHelpers.cs │ ├── Report.cs │ ├── Requires.cs │ ├── Strings.resx │ ├── ValidatedNotNullAttribute.cs │ ├── ValidationInterpolatedStringHandler.cs │ ├── ValidationInterpolatedStringHandlerInvertedCondition.cs │ └── Verify.cs ├── OptProf.targets ├── PackageIcon.png └── VSInsertionMetadata │ ├── Microsoft.VisualStudio.Validation.VSInsertionMetadata.proj │ ├── ProfilingInputs.props │ └── VSInsertionMetadata.targets ├── stylecop.json ├── test ├── .editorconfig ├── Directory.Build.props ├── Directory.Build.targets └── Microsoft.VisualStudio.Validation.Tests │ ├── AssemblyInfo.cs │ ├── AssertDialogSuppression.cs │ ├── AssumesTests.cs │ ├── DisposableValue{T}.cs │ ├── EventHandlerExtensionsTests.cs │ ├── ExceptionExtensionsTests.cs │ ├── Microsoft.VisualStudio.Validation.Tests.csproj │ ├── OverrideCulture.cs │ ├── ReportTests.Debug.cs │ ├── ReportTests.Release.cs │ ├── RequiresTests.cs │ ├── TestStrings.resx │ ├── VerifyTests.cs │ └── xunit.runner.json ├── tools ├── Check-DotNetRuntime.ps1 ├── Check-DotNetSdk.ps1 ├── Convert-PDB.ps1 ├── Get-3rdPartySymbolFiles.ps1 ├── Get-ArtifactsStagingDirectory.ps1 ├── Get-CodeCovTool.ps1 ├── Get-LibTemplateBasis.ps1 ├── Get-NuGetTool.ps1 ├── Get-ProcDump.ps1 ├── Get-SymbolFiles.ps1 ├── Get-TempToolsPath.ps1 ├── Install-DotNetSdk.ps1 ├── Install-NuGetCredProvider.ps1 ├── MergeFrom-Template.ps1 ├── Prepare-Legacy-Symbols.ps1 ├── Set-EnvVars.ps1 ├── artifacts │ ├── APIScanInputs.ps1 │ ├── LocBin.ps1 │ ├── VSInsertion.ps1 │ ├── Variables.ps1 │ ├── _all.ps1 │ ├── _stage_all.ps1 │ ├── build_logs.ps1 │ ├── coverageResults.ps1 │ ├── deployables.ps1 │ ├── projectAssetsJson.ps1 │ ├── symbols.ps1 │ ├── testResults.ps1 │ └── test_symbols.ps1 ├── dotnet-test-cloud.ps1 ├── publish-CodeCov.ps1 ├── test.runsettings └── variables │ ├── BusinessGroupName.ps1 │ ├── DotNetSdkVersion.ps1 │ ├── InsertJsonValues.ps1 │ ├── InsertPropsValues.ps1 │ ├── InsertTargetBranch.ps1 │ ├── InsertVersionsValues.ps1 │ ├── LocLanguages.ps1 │ ├── ProfilingInputsDropName.ps1 │ ├── SymbolsFeatureName.ps1 │ ├── VstsDropNames.ps1 │ ├── _all.ps1 │ └── _define.ps1 └── version.json /.azuredevops/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Please see the documentation for all configuration options: 2 | # https://eng.ms/docs/products/dependabot/configuration/version_updates 3 | 4 | version: 2 5 | updates: 6 | - package-ecosystem: nuget 7 | directory: / 8 | schedule: 9 | interval: monthly 10 | -------------------------------------------------------------------------------- /.config/dotnet-tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "isRoot": true, 4 | "tools": { 5 | "powershell": { 6 | "version": "7.5.1", 7 | "commands": [ 8 | "pwsh" 9 | ], 10 | "rollForward": false 11 | }, 12 | "dotnet-coverage": { 13 | "version": "17.14.2", 14 | "commands": [ 15 | "dotnet-coverage" 16 | ], 17 | "rollForward": false 18 | }, 19 | "nbgv": { 20 | "version": "3.7.115", 21 | "commands": [ 22 | "nbgv" 23 | ], 24 | "rollForward": false 25 | }, 26 | "docfx": { 27 | "version": "2.78.3", 28 | "commands": [ 29 | "docfx" 30 | ], 31 | "rollForward": false 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | # Refer to https://hub.docker.com/_/microsoft-dotnet-sdk for available versions 2 | FROM mcr.microsoft.com/dotnet/sdk:9.0.300-noble@sha256:9f7bd4d010026e15a57d9cf876f2f7d08c3eeed6a0ea987b8c5ba8c75e68e948 3 | 4 | # Installing mono makes `dotnet test` work without errors even for net472. 5 | # But installing it takes a long time, so it's excluded by default. 6 | #RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF 7 | #RUN echo "deb https://download.mono-project.com/repo/ubuntu stable-bionic main" | tee /etc/apt/sources.list.d/mono-official-stable.list 8 | #RUN apt-get update 9 | #RUN DEBIAN_FRONTEND=noninteractive apt-get install -y mono-devel 10 | 11 | # Clear the NUGET_XMLDOC_MODE env var so xml api doc files get unpacked, allowing a rich experience in Intellisense. 12 | # See https://github.com/dotnet/dotnet-docker/issues/2790 for a discussion on this, where the prioritized use case 13 | # was *not* devcontainers, sadly. 14 | ENV NUGET_XMLDOC_MODE= 15 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Dev space", 3 | "dockerFile": "Dockerfile", 4 | "customizations": { 5 | "vscode": { 6 | "settings": { 7 | "terminal.integrated.shell.linux": "/usr/bin/pwsh" 8 | }, 9 | "extensions": [ 10 | "ms-azure-devops.azure-pipelines", 11 | "ms-dotnettools.csharp", 12 | "k--kato.docomment", 13 | "editorconfig.editorconfig", 14 | "esbenp.prettier-vscode", 15 | "pflannery.vscode-versionlens", 16 | "davidanson.vscode-markdownlint", 17 | "dotjoshjohnson.xml", 18 | "ms-vscode-remote.remote-containers", 19 | "ms-azuretools.vscode-docker", 20 | "tintoy.msbuild-project-tools" 21 | ] 22 | } 23 | }, 24 | "postCreateCommand": "./init.ps1 -InstallLocality machine" 25 | } 26 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | # Ensure shell scripts use LF line endings (linux only accepts LF) 7 | *.sh eol=lf 8 | *.ps1 eol=lf 9 | 10 | # The macOS codesign tool is extremely picky, and requires LF line endings. 11 | *.plist eol=lf 12 | 13 | ############################################################################### 14 | # Set default behavior for command prompt diff. 15 | # 16 | # This is need for earlier builds of msysgit that does not have it on by 17 | # default for csharp files. 18 | # Note: This is only used by command line 19 | ############################################################################### 20 | #*.cs diff=csharp 21 | 22 | ############################################################################### 23 | # Set the merge driver for project and solution files 24 | # 25 | # Merging from the command prompt will add diff markers to the files if there 26 | # are conflicts (Merging from VS is not affected by the settings below, in VS 27 | # the diff markers are never inserted). Diff markers may cause the following 28 | # file extensions to fail to load in VS. An alternative would be to treat 29 | # these files as binary and thus will always conflict and require user 30 | # intervention with every merge. To do so, just uncomment the entries below 31 | ############################################################################### 32 | #*.sln merge=binary 33 | #*.csproj merge=binary 34 | #*.vbproj merge=binary 35 | #*.vcxproj merge=binary 36 | #*.vcproj merge=binary 37 | #*.dbproj merge=binary 38 | #*.fsproj merge=binary 39 | #*.lsproj merge=binary 40 | #*.wixproj merge=binary 41 | #*.modelproj merge=binary 42 | #*.sqlproj merge=binary 43 | #*.wwaproj merge=binary 44 | 45 | ############################################################################### 46 | # behavior for image files 47 | # 48 | # image files are treated as binary by default. 49 | ############################################################################### 50 | #*.jpg binary 51 | #*.png binary 52 | #*.gif binary 53 | 54 | ############################################################################### 55 | # diff behavior for common document formats 56 | # 57 | # Convert binary document formats to text before diffing them. This feature 58 | # is only available from the command line. Turn it on by uncommenting the 59 | # entries below. 60 | ############################################################################### 61 | #*.doc diff=astextplain 62 | #*.DOC diff=astextplain 63 | #*.docx diff=astextplain 64 | #*.DOCX diff=astextplain 65 | #*.dot diff=astextplain 66 | #*.DOT diff=astextplain 67 | #*.pdf diff=astextplain 68 | #*.PDF diff=astextplain 69 | #*.rtf diff=astextplain 70 | #*.RTF diff=astextplain 71 | -------------------------------------------------------------------------------- /.github/.editorconfig: -------------------------------------------------------------------------------- 1 | [renovate.json*] 2 | indent_style = tab 3 | -------------------------------------------------------------------------------- /.github/actions/publish-artifacts/action.yaml: -------------------------------------------------------------------------------- 1 | name: Publish artifacts 2 | description: Publish artifacts 3 | 4 | runs: 5 | using: composite 6 | steps: 7 | - name: 📥 Collect artifacts 8 | run: tools/artifacts/_stage_all.ps1 9 | shell: pwsh 10 | if: always() 11 | 12 | # TODO: replace this hard-coded list with a loop that utilizes the NPM package at 13 | # https://github.com/actions/toolkit/tree/main/packages/artifact (or similar) to push the artifacts. 14 | 15 | - name: 📢 Upload project.assets.json files 16 | if: always() 17 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 18 | with: 19 | name: projectAssetsJson-${{ runner.os }} 20 | path: ${{ runner.temp }}/_artifacts/projectAssetsJson 21 | continue-on-error: true 22 | - name: 📢 Upload variables 23 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 24 | with: 25 | name: variables-${{ runner.os }} 26 | path: ${{ runner.temp }}/_artifacts/Variables 27 | continue-on-error: true 28 | - name: 📢 Upload build_logs 29 | if: always() 30 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 31 | with: 32 | name: build_logs-${{ runner.os }} 33 | path: ${{ runner.temp }}/_artifacts/build_logs 34 | continue-on-error: true 35 | - name: 📢 Upload testResults 36 | if: always() 37 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 38 | with: 39 | name: testResults-${{ runner.os }} 40 | path: ${{ runner.temp }}/_artifacts/testResults 41 | continue-on-error: true 42 | - name: 📢 Upload coverageResults 43 | if: always() 44 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 45 | with: 46 | name: coverageResults-${{ runner.os }} 47 | path: ${{ runner.temp }}/_artifacts/coverageResults 48 | continue-on-error: true 49 | - name: 📢 Upload symbols 50 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 51 | with: 52 | name: symbols-${{ runner.os }} 53 | path: ${{ runner.temp }}/_artifacts/symbols 54 | continue-on-error: true 55 | - name: 📢 Upload deployables 56 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 57 | with: 58 | name: deployables-${{ runner.os }} 59 | path: ${{ runner.temp }}/_artifacts/deployables 60 | if: always() 61 | -------------------------------------------------------------------------------- /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "github>microsoft/vs-renovate-presets:microbuild", 5 | "github>microsoft/vs-renovate-presets:vs_main_dependencies", 6 | "github>microsoft/vs-renovate-presets:dotnet_packages_LTS" 7 | ], 8 | "packageRules": [] 9 | } 10 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: 📚 Docs 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 9 | permissions: 10 | actions: read 11 | pages: write 12 | id-token: write 13 | contents: read 14 | 15 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 16 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 17 | concurrency: 18 | group: pages 19 | cancel-in-progress: false 20 | 21 | jobs: 22 | publish-docs: 23 | environment: 24 | name: github-pages 25 | url: ${{ steps.deployment.outputs.page_url }} 26 | runs-on: ubuntu-latest 27 | steps: 28 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 29 | with: 30 | fetch-depth: 0 # avoid shallow clone so nbgv can do its work. 31 | - name: ⚙ Install prerequisites 32 | run: ./init.ps1 -UpgradePrerequisites 33 | 34 | - run: dotnet docfx docfx/docfx.json 35 | name: 📚 Generate documentation 36 | 37 | - name: Upload artifact 38 | uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3 39 | with: 40 | path: docfx/_site 41 | 42 | - name: Deploy to GitHub Pages 43 | id: deployment 44 | uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4 45 | -------------------------------------------------------------------------------- /.github/workflows/docs_validate.yml: -------------------------------------------------------------------------------- 1 | name: 📃 Docfx Validate 2 | 3 | on: 4 | pull_request: 5 | workflow_dispatch: 6 | push: 7 | branches: 8 | - main 9 | - microbuild 10 | 11 | jobs: 12 | build: 13 | name: 📚 Doc validation 14 | runs-on: ubuntu-24.04 15 | steps: 16 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 17 | with: 18 | fetch-depth: 0 # avoid shallow clone so nbgv can do its work. 19 | - name: 🔗 Markup Link Checker (mlc) 20 | uses: becheran/mlc@c925f90a9a25e16e4c4bfa29058f6f9ffa9f0d8c # v0.21.0 21 | with: 22 | args: --do-not-warn-for-redirect-to https://learn.microsoft.com*,https://dotnet.microsoft.com/*,https://dev.azure.com/*,https://app.codecov.io/* -p docfx -i https://aka.ms/onboardsupport,https://aka.ms/spot,https://msrc.microsoft.com/*,https://www.microsoft.com/msrc*,https://microsoft.com/msrc* 23 | - name: ⚙ Install prerequisites 24 | run: | 25 | ./init.ps1 -UpgradePrerequisites 26 | dotnet --info 27 | shell: pwsh 28 | - name: 📚 Verify docfx build 29 | run: dotnet docfx docfx/docfx.json --warningsAsErrors --disableGitFeatures 30 | if: runner.os == 'Linux' 31 | -------------------------------------------------------------------------------- /.github/workflows/libtemplate-update.yml: -------------------------------------------------------------------------------- 1 | name: ⛜ Library.Template update 2 | 3 | # PREREQUISITE: This workflow requires the repo to be configured to allow workflows to create pull requests. 4 | # Visit https://github.com/USER/REPO/settings/actions 5 | # Under "Workflow permissions" check "Allow GitHub Actions to create ...pull requests" 6 | # Click Save. 7 | 8 | on: 9 | schedule: 10 | - cron: "0 3 * * Mon" # Sun @ 8 or 9 PM Mountain Time (depending on DST) 11 | workflow_dispatch: 12 | 13 | jobs: 14 | merge: 15 | runs-on: ubuntu-latest 16 | permissions: 17 | contents: write 18 | pull-requests: write 19 | steps: 20 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 21 | with: 22 | fetch-depth: 0 # avoid shallow clone so nbgv can do its work. 23 | 24 | - name: merge 25 | id: merge 26 | shell: pwsh 27 | run: | 28 | $LibTemplateBranch = & ./tools/Get-LibTemplateBasis.ps1 -ErrorIfNotRelated 29 | if ($LASTEXITCODE -ne 0) { 30 | exit $LASTEXITCODE 31 | } 32 | 33 | git fetch https://github.com/aarnott/Library.Template $LibTemplateBranch 34 | if ($LASTEXITCODE -ne 0) { 35 | exit $LASTEXITCODE 36 | } 37 | $LibTemplateCommit = git rev-parse FETCH_HEAD 38 | git diff --stat ...FETCH_HEAD 39 | 40 | if ((git rev-list FETCH_HEAD ^HEAD --count) -eq 0) { 41 | Write-Host "There are no Library.Template updates to merge." 42 | echo "uptodate=true" >> $env:GITHUB_OUTPUT 43 | exit 0 44 | } 45 | 46 | # Pushing commits that add or change files under .github/workflows will cause our workflow to fail. 47 | # But it usually isn't necessary because the target branch already has (or doesn't have) these changes. 48 | # So if the merge doesn't bring in any changes to these files, try the merge locally and push that 49 | # to keep github happy. 50 | if ((git rev-list FETCH_HEAD ^HEAD --count -- .github/workflows) -eq 0) { 51 | # Indeed there are no changes in that area. So merge locally to try to appease GitHub. 52 | git checkout -b auto/libtemplateUpdate 53 | git config user.name "Andrew Arnott" 54 | git config user.email "andrewarnott@live.com" 55 | git merge FETCH_HEAD 56 | if ($LASTEXITCODE -ne 0) { 57 | Write-Host "Merge conflicts prevent creating the pull request. Please run tools/MergeFrom-Template.ps1 locally and push the result as a pull request." 58 | exit 2 59 | } 60 | 61 | git -c http.extraheader="AUTHORIZATION: bearer $env:GH_TOKEN" push origin -u HEAD 62 | } else { 63 | Write-Host "Changes to github workflows are included in this update. Please run tools/MergeFrom-Template.ps1 locally and push the result as a pull request." 64 | exit 1 65 | } 66 | - name: pull request 67 | shell: pwsh 68 | if: success() && steps.merge.outputs.uptodate != 'true' 69 | run: | 70 | # If there is already an active pull request, don't create a new one. 71 | $existingPR = gh pr list -H auto/libtemplateUpdate --json url | ConvertFrom-Json 72 | if ($existingPR) { 73 | Write-Host "::warning::Skipping pull request creation because one already exists at $($existingPR[0].url)" 74 | exit 0 75 | } 76 | 77 | $prTitle = "Merge latest Library.Template" 78 | $prBody = "This merges the latest features and fixes from [Library.Template's branch](https://github.com/AArnott/Library.Template/tree/). 79 | 80 | ⚠️ Do **not** squash this pull request when completing it. You must *merge* it. 81 | 82 |
83 | Merge conflicts? 84 | Resolve merge conflicts locally by carrying out these steps: 85 | 86 | ``` 87 | git fetch 88 | git checkout auto/libtemplateUpdate 89 | git merge origin/main 90 | # resolve conflicts 91 | git commit 92 | git push 93 | ``` 94 |
" 95 | 96 | gh pr create -H auto/libtemplateUpdate -b $prBody -t $prTitle 97 | env: 98 | GH_TOKEN: ${{ github.token }} 99 | -------------------------------------------------------------------------------- /.prettierrc.yaml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vs-validation/28c82310309d02f6f9bdbceb8b393296d6d8e2ee/.prettierrc.yaml -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. 3 | // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp 4 | // List of extensions which should be recommended for users of this workspace. 5 | "recommendations": [ 6 | "ms-azure-devops.azure-pipelines", 7 | "ms-dotnettools.csharp", 8 | "k--kato.docomment", 9 | "editorconfig.editorconfig", 10 | "esbenp.prettier-vscode", 11 | "pflannery.vscode-versionlens", 12 | "davidanson.vscode-markdownlint", 13 | "dotjoshjohnson.xml", 14 | "ms-vscode-remote.remote-containers", 15 | "ms-azuretools.vscode-docker", 16 | "tintoy.msbuild-project-tools" 17 | ], 18 | // List of extensions recommended by VS Code that should not be recommended for users of this workspace. 19 | "unwantedRecommendations": [] 20 | } 21 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.trimTrailingWhitespace": true, 3 | "files.insertFinalNewline": true, 4 | "files.trimFinalNewlines": true, 5 | "azure-pipelines.1ESPipelineTemplatesSchemaFile": true, 6 | "omnisharp.enableEditorConfigSupport": true, 7 | "omnisharp.enableRoslynAnalyzers": true, 8 | "dotnet.completion.showCompletionItemsFromUnimportedNamespaces": true, 9 | "editor.formatOnSave": true, 10 | "[xml]": { 11 | "editor.wordWrap": "off" 12 | }, 13 | // Treat these files as Azure Pipelines files 14 | "files.associations": { 15 | "**/azure-pipelines/**/*.yml": "azure-pipelines", 16 | "azure-pipelines.yml": "azure-pipelines" 17 | }, 18 | // Use Prettier as the default formatter for Azure Pipelines files. 19 | // Needs to be explicitly configured: https://github.com/Microsoft/azure-pipelines-vscode#document-formatting 20 | "[azure-pipelines]": { 21 | "editor.defaultFormatter": "esbenp.prettier-vscode", 22 | "editor.formatOnSave": false // enable this when they conform 23 | }, 24 | } 25 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Microsoft.VisualStudio.Validation 2 | 3 | This project has adopted the [Microsoft Open Source Code of 4 | Conduct](https://opensource.microsoft.com/codeofconduct/). 5 | For more information see the [Code of Conduct 6 | FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or 7 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) 8 | with any additional questions or comments. 9 | 10 | ## Best practices 11 | 12 | * Use Windows PowerShell or [PowerShell Core][pwsh] (including on Linux/OSX) to run .ps1 scripts. 13 | Some scripts set environment variables to help you, but they are only retained if you use PowerShell as your shell. 14 | 15 | ## Prerequisites 16 | 17 | All dependencies can be installed by running the `init.ps1` script at the root of the repository 18 | using Windows PowerShell or [PowerShell Core][pwsh] (on any OS). 19 | Some dependencies installed by `init.ps1` may only be discoverable from the same command line environment the init script was run from due to environment variables, so be sure to launch Visual Studio or build the repo from that same environment. 20 | Alternatively, run `init.ps1 -InstallLocality Machine` (which may require elevation) in order to install dependencies at machine-wide locations so Visual Studio and builds work everywhere. 21 | 22 | The only prerequisite for building, testing, and deploying from this repository 23 | is the [.NET SDK](https://get.dot.net/). 24 | You should install the version specified in `global.json` or a later version within 25 | the same major.minor.Bxx "hundreds" band. 26 | For example if 2.2.300 is specified, you may install 2.2.300, 2.2.301, or 2.2.310 27 | while the 2.2.400 version would not be considered compatible by .NET SDK. 28 | See [.NET Core Versioning](https://learn.microsoft.com/dotnet/core/versions/) for more information. 29 | 30 | ## Package restore 31 | 32 | The easiest way to restore packages may be to run `init.ps1` which automatically authenticates 33 | to the feeds that packages for this repo come from, if any. 34 | `dotnet restore` or `nuget restore` also work but may require extra steps to authenticate to any applicable feeds. 35 | 36 | ## Building 37 | 38 | This repository can be built on Windows, Linux, and OSX. 39 | 40 | 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.). 41 | 42 | [pwsh]: https://docs.microsoft.com/powershell/scripting/install/installing-powershell?view=powershell-6 43 | 44 | ## Releases 45 | 46 | Use `nbgv tag` to create a tag for a particular commit that you mean to release. 47 | [Learn more about `nbgv` and its `tag` and `prepare-release` commands](https://dotnet.github.io/Nerdbank.GitVersioning/docs/nbgv-cli.html). 48 | 49 | Push the tag. 50 | 51 | ### GitHub Actions 52 | 53 | When your repo is hosted by GitHub and you are using GitHub Actions, you should create a GitHub Release using the standard GitHub UI. 54 | Having previously used `nbgv tag` and pushing the tag will help you identify the precise commit and name to use for this release. 55 | 56 | After publishing the release, the `.github\workflows\release.yml` workflow will be automatically triggered, which will: 57 | 58 | 1. Find the most recent `.github\workflows\build.yml` GitHub workflow run of the tagged release. 59 | 1. Upload the `deployables` artifact from that workflow run to your GitHub Release. 60 | 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. 61 | 62 | ### Azure Pipelines 63 | 64 | When your repo builds with Azure Pipelines, use the `azure-pipelines/release.yml` pipeline. 65 | Trigger the pipeline by adding the `auto-release` tag on a run of your main `azure-pipelines.yml` pipeline. 66 | 67 | ## Tutorial and API documentation 68 | 69 | API and hand-written docs are found under the `docfx/` directory. and are built by [docfx](https://dotnet.github.io/docfx/). 70 | 71 | You can make changes and host the site locally to preview them by switching to that directory and running the `dotnet docfx --serve` command. 72 | 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. 73 | 74 | The `.github/workflows/docs.yml` GitHub Actions workflow publishes the content of these docs to github.io if the workflow itself and [GitHub Pages is enabled for your repository](https://docs.github.com/en/pages/quickstart). 75 | 76 | ## Updating dependencies 77 | 78 | This repo uses Renovate to keep dependencies current. 79 | Configuration is in the `.github/renovate.json` file. 80 | [Learn more about configuring Renovate](https://docs.renovatebot.com/configuration-options/). 81 | 82 | When changing the renovate.json file, follow [these validation steps](https://docs.renovatebot.com/config-validation/). 83 | 84 | If Renovate is not creating pull requests when you expect it to, check that the [Renovate GitHub App](https://github.com/apps/renovate) is configured for your account or repo. 85 | 86 | ## Merging latest from Library.Template 87 | 88 | ### Maintaining your repo based on this template 89 | 90 | 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: 91 | ` 92 | ```ps1 93 | git fetch 94 | git checkout origin/main 95 | .\tools\MergeFrom-Template.ps1 96 | # resolve any conflicts, then commit the merge commit. 97 | git push origin -u HEAD 98 | ``` 99 | -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | $(MSBuildThisFileDirectory) 6 | $(RepoRootPath)obj\$([MSBuild]::MakeRelative($(RepoRootPath), $(MSBuildProjectDirectory)))\ 7 | $(RepoRootPath)bin\$(MSBuildProjectName)\ 8 | $(RepoRootPath)bin\Packages\$(Configuration)\NuGet\ 9 | $(RepoRootPath)bin\Packages\$(Configuration)\Vsix\$(Platform)\ 10 | $(VSIXOutputPath) 11 | enable 12 | enable 13 | latest 14 | true 15 | true 16 | true 17 | 18 | 19 | true 20 | 21 | 22 | 23 | false 24 | 25 | 26 | $(MSBuildThisFileDirectory) 27 | 28 | 29 | embedded 30 | 31 | https://github.com/microsoft/vs-validation 32 | Microsoft 33 | Microsoft 34 | © Microsoft Corporation. All rights reserved. 35 | MIT 36 | true 37 | true 38 | true 39 | snupkg 40 | 41 | 42 | 43 | 13 44 | 16.9 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | $(RepositoryUrl)/releases/tag/v$(Version) 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /Directory.Build.rsp: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------ 2 | # This file contains command-line options that MSBuild will process as part of 3 | # every build, unless the "/noautoresponse" switch is specified. 4 | # 5 | # MSBuild processes the options in this file first, before processing the 6 | # options on the command line. As a result, options on the command line can 7 | # override the options in this file. However, depending on the options being 8 | # set, the overriding can also result in conflicts. 9 | # 10 | # NOTE: The "/noautoresponse" switch cannot be specified in this file, nor in 11 | # any response file that is referenced by this file. 12 | #------------------------------------------------------------------------------ 13 | /nr:false 14 | /m 15 | /verbosity:minimal 16 | /clp:Summary;ForceNoAlign 17 | -------------------------------------------------------------------------------- /Directory.Build.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Directory.Packages.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | true 6 | true 7 | 8 | 2.0.187 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Microsoft.VisualStudio.Validation 2 | Copyright (c) Microsoft Corporation 3 | All rights reserved.  4 | 5 | MIT License 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. -------------------------------------------------------------------------------- /Microsoft.VisualStudio.Validation.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.31728.443 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.Validation", "src\Microsoft.VisualStudio.Validation\Microsoft.VisualStudio.Validation.csproj", "{4D36901F-A937-4A2F-B086-CB2CD4DFB3FB}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{871C5DB2-2E11-428A-A9FA-78E269B6E9B0}" 9 | ProjectSection(SolutionItems) = preProject 10 | .editorconfig = .editorconfig 11 | Directory.Build.props = Directory.Build.props 12 | Directory.Build.targets = Directory.Build.targets 13 | Directory.Packages.props = Directory.Packages.props 14 | global.json = global.json 15 | nuget.config = nuget.config 16 | stylecop.json = stylecop.json 17 | version.json = version.json 18 | EndProjectSection 19 | EndProject 20 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.Validation.Tests", "test\Microsoft.VisualStudio.Validation.Tests\Microsoft.VisualStudio.Validation.Tests.csproj", "{B68F4565-AC94-4533-A9D6-5701F67D9C85}" 21 | EndProject 22 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{9A13F1F2-7491-4082-BCEF-B9217AECAF30}" 23 | ProjectSection(SolutionItems) = preProject 24 | test\.editorconfig = test\.editorconfig 25 | test\Directory.Build.props = test\Directory.Build.props 26 | test\Directory.Build.targets = test\Directory.Build.targets 27 | EndProjectSection 28 | EndProject 29 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{542E44B7-F474-4822-8EA1-180DC2FB2884}" 30 | ProjectSection(SolutionItems) = preProject 31 | src\.editorconfig = src\.editorconfig 32 | src\Directory.Build.props = src\Directory.Build.props 33 | src\Directory.Build.targets = src\Directory.Build.targets 34 | EndProjectSection 35 | EndProject 36 | Global 37 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 38 | Debug|Any CPU = Debug|Any CPU 39 | Release|Any CPU = Release|Any CPU 40 | EndGlobalSection 41 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 42 | {4D36901F-A937-4A2F-B086-CB2CD4DFB3FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 43 | {4D36901F-A937-4A2F-B086-CB2CD4DFB3FB}.Debug|Any CPU.Build.0 = Debug|Any CPU 44 | {4D36901F-A937-4A2F-B086-CB2CD4DFB3FB}.Release|Any CPU.ActiveCfg = Release|Any CPU 45 | {4D36901F-A937-4A2F-B086-CB2CD4DFB3FB}.Release|Any CPU.Build.0 = Release|Any CPU 46 | {B68F4565-AC94-4533-A9D6-5701F67D9C85}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 47 | {B68F4565-AC94-4533-A9D6-5701F67D9C85}.Debug|Any CPU.Build.0 = Debug|Any CPU 48 | {B68F4565-AC94-4533-A9D6-5701F67D9C85}.Release|Any CPU.ActiveCfg = Release|Any CPU 49 | {B68F4565-AC94-4533-A9D6-5701F67D9C85}.Release|Any CPU.Build.0 = Release|Any CPU 50 | EndGlobalSection 51 | GlobalSection(SolutionProperties) = preSolution 52 | HideSolutionNode = FALSE 53 | EndGlobalSection 54 | GlobalSection(NestedProjects) = preSolution 55 | {4D36901F-A937-4A2F-B086-CB2CD4DFB3FB} = {542E44B7-F474-4822-8EA1-180DC2FB2884} 56 | {B68F4565-AC94-4533-A9D6-5701F67D9C85} = {9A13F1F2-7491-4082-BCEF-B9217AECAF30} 57 | EndGlobalSection 58 | GlobalSection(ExtensibilityGlobals) = postSolution 59 | SolutionGuid = {1AFE11BB-B1CE-40D5-AA63-E80706C7682C} 60 | EndGlobalSection 61 | EndGlobal 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Microsoft.VisualStudio.Validation 2 | ================================= 3 | 4 | [![NuGet package](https://img.shields.io/nuget/v/Microsoft.VisualStudio.Validation.svg)](https://www.nuget.org/packages/Microsoft.VisualStudio.Validation) 5 | [![Build Status](https://dev.azure.com/azure-public/vside/_apis/build/status/vs-validation?branchName=main)](https://dev.azure.com/azure-public/vside/_build/latest?definitionId=11&branchName=main) 6 | [![codecov](https://codecov.io/gh/Microsoft/vs-validation/branch/main/graph/badge.svg)](https://codecov.io/gh/Microsoft/vs-validation) 7 | 8 | This project is available as the [Microsoft.VisualStudio.Validation][1] NuGet package. 9 | 10 | Basic input validation via the `Requires` class throws an ArgumentException. 11 | 12 | ```csharp 13 | Requires.NotNull(arg1, nameof(arg1)); 14 | Requires.NotNullOrEmpty(arg2, nameof(arg2)); 15 | ``` 16 | 17 | State validation via the `Verify` class throws an InvalidOperationException. 18 | 19 | ```csharp 20 | Verify.Operation(condition, "some error occurred."); 21 | ``` 22 | 23 | Internal integrity checks via the `Assumes` class throws an 24 | InternalErrorException. 25 | 26 | ```csharp 27 | Assumes.True(condition, "some error"); 28 | ``` 29 | 30 | Warning signs that should not throw exceptions via the `Report` class. 31 | 32 | ```csharp 33 | Report.IfNot(condition, "some error"); 34 | ``` 35 | 36 | [1]: http://www.nuget.org/packages/Microsoft.VisualStudio.Validation "Microsoft.VisualStudio.Validation NuGet package" 37 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | 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/). 6 | 7 | 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. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/msrc/pgp-key-msrc). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/msrc/cvd). 40 | 41 | 42 | -------------------------------------------------------------------------------- /SUPPORT.md: -------------------------------------------------------------------------------- 1 | # Support 2 | 3 | ## How to file issues and get help 4 | 5 | This project uses GitHub Issues to track bugs and feature requests. 6 | Please search the existing issues before filing new issues to avoid duplicates. 7 | For new issues, file your bug or feature request as a new Issue. 8 | 9 | Note that this repo is primarily used for Visual Studio and related products and support will be focused on those scenarios. 10 | 11 | ## Microsoft Support Policy 12 | 13 | Microsoft support for this software is available only for its use in officially supported products such as Visual Studio. 14 | Support and servicing is limited to the latest released version. 15 | For more information, see [Visual Studio Product Lifecycle and Servicing](https://learn.microsoft.com/visualstudio/productinfo/vs-servicing). 16 | 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). 17 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | trigger: 2 | batch: true 3 | branches: 4 | include: 5 | - main 6 | - 'v*.*' 7 | - 'validate/*' 8 | paths: 9 | exclude: 10 | - doc/ 11 | - '*.md' 12 | - .vscode/ 13 | - .github/ 14 | - azure-pipelines/release.yml 15 | 16 | parameters: 17 | - name: EnableMacOSBuild 18 | displayName: Build on macOS 19 | type: boolean 20 | default: false # macOS is often bogged down in Azure Pipelines 21 | - name: RunTests 22 | displayName: Run tests 23 | type: boolean 24 | default: true 25 | 26 | variables: 27 | - template: /azure-pipelines/BuildStageVariables.yml@self 28 | 29 | jobs: 30 | - template: azure-pipelines/build.yml 31 | parameters: 32 | Is1ESPT: false 33 | EnableMacOSBuild: ${{ parameters.EnableMacOSBuild }} 34 | RunTests: ${{ parameters.RunTests }} 35 | -------------------------------------------------------------------------------- /azure-pipelines/BuildStageVariables.yml: -------------------------------------------------------------------------------- 1 | variables: 2 | DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true 3 | BuildConfiguration: Release 4 | NUGET_PACKAGES: $(Agent.TempDirectory)/.nuget/packages/ 5 | codecov_token: 1c079a51-729f-4e18-9792-2a75f2e074e0 6 | -------------------------------------------------------------------------------- /azure-pipelines/Get-InsertionPRId.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Look up the pull request URL of the insertion PR. 4 | #> 5 | $stagingFolder = $env:BUILD_STAGINGDIRECTORY 6 | if (!$stagingFolder) { 7 | $stagingFolder = $env:SYSTEM_DEFAULTWORKINGDIRECTORY 8 | if (!$stagingFolder) { 9 | Write-Error "This script must be run in an Azure Pipeline." 10 | exit 1 11 | } 12 | } 13 | $markdownFolder = Join-Path $stagingFolder (Join-Path 'MicroBuild' 'Output') 14 | $markdownFile = Join-Path $markdownFolder 'PullRequestUrl.md' 15 | if (!(Test-Path $markdownFile)) { 16 | Write-Error "This script should be run after the MicroBuildInsertVsPayload task." 17 | exit 2 18 | } 19 | 20 | $insertionPRUrl = Get-Content $markdownFile 21 | if (!($insertionPRUrl -match 'https:.+?/pullrequest/(\d+)')) { 22 | Write-Error "Failed to parse pull request URL: $insertionPRUrl" 23 | exit 3 24 | } 25 | 26 | $Matches[1] 27 | -------------------------------------------------------------------------------- /azure-pipelines/GlobalVariables.yml: -------------------------------------------------------------------------------- 1 | variables: 2 | # These variables are required for MicroBuild tasks 3 | TeamName: VS IDE 4 | TeamEmail: vsidemicrobuild@microsoft.com 5 | # These variables influence insertion pipelines 6 | ContainsVsix: false # This should be true when the repo builds a VSIX that should be inserted to VS. 7 | -------------------------------------------------------------------------------- /azure-pipelines/Install-NuGetPackage.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Installs a NuGet package. 4 | .PARAMETER PackageID 5 | The Package ID to install. 6 | .PARAMETER Version 7 | The version of the package to install. If unspecified, the latest stable release is installed. 8 | .PARAMETER Source 9 | The package source feed to find the package to install from. 10 | .PARAMETER PackagesDir 11 | The directory to install the package to. By default, it uses the Packages folder at the root of the repo. 12 | .PARAMETER ConfigFile 13 | The nuget.config file to use. By default, it uses :/nuget.config. 14 | .OUTPUTS 15 | System.String. The path to the installed package. 16 | #> 17 | [CmdletBinding(SupportsShouldProcess=$true,ConfirmImpact='Low')] 18 | Param( 19 | [Parameter(Position=1,Mandatory=$true)] 20 | [string]$PackageId, 21 | [Parameter()] 22 | [string]$Version, 23 | [Parameter()] 24 | [string]$Source, 25 | [Parameter()] 26 | [switch]$Prerelease, 27 | [Parameter()] 28 | [string]$PackagesDir="$PSScriptRoot\..\packages", 29 | [Parameter()] 30 | [string]$ConfigFile="$PSScriptRoot\..\nuget.config", 31 | [Parameter()] 32 | [ValidateSet('Quiet','Normal','Detailed')] 33 | [string]$Verbosity='normal' 34 | ) 35 | 36 | $nugetPath = & "$PSScriptRoot\..\tools\Get-NuGetTool.ps1" 37 | 38 | try { 39 | Write-Verbose "Installing $PackageId..." 40 | $nugetArgs = "Install",$PackageId,"-OutputDirectory",$PackagesDir,'-ConfigFile',$ConfigFile 41 | if ($Version) { $nugetArgs += "-Version",$Version } 42 | if ($Source) { $nugetArgs += "-FallbackSource",$Source } 43 | if ($Prerelease) { $nugetArgs += "-Prerelease" } 44 | $nugetArgs += '-Verbosity',$Verbosity 45 | 46 | if ($PSCmdlet.ShouldProcess($PackageId, 'nuget install')) { 47 | $p = Start-Process $nugetPath $nugetArgs -NoNewWindow -Wait -PassThru 48 | if ($null -ne $p.ExitCode -and $p.ExitCode -ne 0) { throw } 49 | } 50 | 51 | # Provide the path to the installed package directory to our caller. 52 | Write-Output (Get-ChildItem "$PackagesDir\$PackageId.*")[0].FullName 53 | } finally { 54 | Pop-Location 55 | } 56 | -------------------------------------------------------------------------------- /azure-pipelines/Merge-CodeCoverage.ps1: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env pwsh 2 | 3 | <# 4 | .SYNOPSIS 5 | Merges code coverage reports. 6 | .PARAMETER Path 7 | The path(s) to search for Cobertura code coverage reports. 8 | .PARAMETER Format 9 | The format for the merged result. The default is Cobertura 10 | .PARAMETER OutputDir 11 | The directory the merged result will be written to. The default is `coveragereport` in the root of this repo. 12 | #> 13 | [CmdletBinding()] 14 | Param( 15 | [Parameter(Mandatory=$true)] 16 | [string[]]$Path, 17 | [ValidateSet('Badges', 'Clover', 'Cobertura', 'CsvSummary', 'Html', 'Html_Dark', 'Html_Light', 'HtmlChart', 'HtmlInline', 'HtmlInline_AzurePipelines', 'HtmlInline_AzurePipelines_Dark', 'HtmlInline_AzurePipelines_Light', 'HtmlSummary', 'JsonSummary', 'Latex', 'LatexSummary', 'lcov', 'MarkdownSummary', 'MHtml', 'PngChart', 'SonarQube', 'TeamCitySummary', 'TextSummary', 'Xml', 'XmlSummary')] 18 | [string]$Format='Cobertura', 19 | [string]$OutputFile=("$PSScriptRoot/../coveragereport/merged.cobertura.xml") 20 | ) 21 | 22 | $RepoRoot = [string](Resolve-Path $PSScriptRoot/..) 23 | Push-Location $RepoRoot 24 | try { 25 | Write-Verbose "Searching $Path for *.cobertura.xml files" 26 | $reports = Get-ChildItem -Recurse $Path -Filter *.cobertura.xml 27 | 28 | if ($reports) { 29 | $reports |% { $_.FullName } |% { 30 | # In addition to replacing {reporoot}, we also normalize on one kind of slash so that the report aggregates data for a file whether data was collected on Windows or not. 31 | $xml = [xml](Get-Content -LiteralPath $_) 32 | $xml.coverage.packages.package.classes.class |? { $_.filename} |% { 33 | $_.filename = $_.filename.Replace('{reporoot}', $RepoRoot).Replace([IO.Path]::AltDirectorySeparatorChar, [IO.Path]::DirectorySeparatorChar) 34 | } 35 | 36 | $xml.Save($_) 37 | } 38 | 39 | $Inputs = $reports |% { Resolve-Path -relative $_.FullName } 40 | 41 | if ((Split-Path $OutputFile) -and -not (Test-Path (Split-Path $OutputFile))) { 42 | New-Item -Type Directory -Path (Split-Path $OutputFile) | Out-Null 43 | } 44 | 45 | & dotnet dotnet-coverage merge $Inputs -o $OutputFile -f cobertura 46 | } else { 47 | Write-Error "No reports found to merge." 48 | } 49 | } finally { 50 | Pop-Location 51 | } 52 | -------------------------------------------------------------------------------- /azure-pipelines/NuGetSbom.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | 2 5 | 6 | 7 | -------------------------------------------------------------------------------- /azure-pipelines/OptProf.yml: -------------------------------------------------------------------------------- 1 | trigger: none 2 | pr: none 3 | schedules: 4 | - cron: "0 3 * * Fri" # Thu @ 8 or 9 PM Mountain Time (depending on DST) 5 | displayName: Weekly OptProf run 6 | branches: 7 | include: 8 | - 'v*.*' 9 | - main 10 | always: true # we must keep data fresh since optimizationdata drops are purged after 30 days 11 | 12 | # Avoid errant CI builds: https://developercommunity.visualstudio.com/content/problem/1154409/azure-pipeline-is-triggering-due-to-events-that-ne.html 13 | #resources: 14 | # repositories: 15 | # - repository: scripts 16 | # type: git 17 | # name: DeploymentScripts 18 | # ref: refs/heads/test 19 | 20 | parameters: 21 | - name: ShouldSkipOptimize 22 | displayName: Skip OptProf optimization 23 | type: boolean 24 | default: false # Should usually be false so that optprof LKG can apply when tests fail, but may need to be set to true in a manually queued pipeline run if all drops have expired. 25 | 26 | variables: 27 | - template: GlobalVariables.yml 28 | - name: PublicRelease 29 | value: false # avoid using nice version since we're building a preliminary/unoptimized package 30 | - name: IsOptProf 31 | value: true 32 | - name: MicroBuild_NuPkgSigningEnabled 33 | value: false # test-signed nuget packages fail to restore in the VS insertion PR validations. Just don't sign them *at all*. 34 | 35 | stages: 36 | - stage: Library 37 | variables: 38 | - name: OptProf 39 | value: true 40 | - template: BuildStageVariables.yml 41 | jobs: 42 | - template: build.yml 43 | parameters: 44 | Is1ESPT: false 45 | RealSign: false 46 | windowsPool: VSEngSS-MicroBuild2022-1ES 47 | EnableMacOSBuild: false 48 | ShouldSkipOptimize: ${{ parameters.ShouldSkipOptimize }} 49 | IsOptProf: true 50 | RunTests: false 51 | SkipCodesignVerify: true 52 | - stage: QueueVSBuild 53 | jobs: 54 | - job: QueueOptProf 55 | pool: VSEngSS-MicroBuild2022-1ES 56 | variables: 57 | InsertPayloadName: LibraryName 58 | InsertTopicBranch: team/VS-IDE/LibraryName-OptProf-run-$(Build.BuildId) 59 | steps: 60 | - checkout: none # We don't need source from our own repo 61 | clean: true 62 | 63 | # Pipeline YAML does not yet support checking out other repos. So we'll do it by hand. 64 | # - checkout: scripts # We DO need source from the DeploymentScripts repo 65 | # clean: true 66 | # path: $(Agent.TempDirectory)/DeploymentScripts 67 | # fetchDepth: 1 68 | - script: 'git -c http.extraheader="AUTHORIZATION: bearer $(System.AccessToken)" clone https://devdiv.visualstudio.com/DevDiv/_git/DeploymentScripts --depth 1 --branch test "$(Agent.TempDirectory)/DeploymentScripts"' 69 | displayName: Download DeploymentScripts repo 70 | 71 | - task: DownloadBuildArtifacts@0 72 | displayName: Download insertion artifacts 73 | inputs: 74 | artifactName: VSInsertion-Windows 75 | downloadPath: $(Agent.TempDirectory) 76 | - task: DownloadBuildArtifacts@0 77 | displayName: Download variables artifacts 78 | inputs: 79 | artifactName: Variables-Windows 80 | downloadPath: $(Agent.TempDirectory) 81 | - task: PowerShell@2 82 | displayName: Set pipeline variables based on artifacts 83 | inputs: 84 | targetType: filePath 85 | filePath: $(Agent.TempDirectory)/Variables-Windows/_define.ps1 86 | - task: NuGetCommand@2 87 | displayName: Push VS-repo packages to VS feed 88 | inputs: 89 | command: push 90 | packagesToPush: $(Agent.TempDirectory)/VSInsertion-Windows/*.nupkg 91 | publishVstsFeed: 97a41293-2972-4f48-8c0e-05493ae82010 # VS feed 92 | allowPackageConflicts: true 93 | - task: MicroBuildInsertVsPayload@5 94 | displayName: Insert VS Payload 95 | inputs: 96 | TeamName: $(TeamName) 97 | TeamEmail: $(TeamEmail) 98 | SkipCreatePR: true 99 | CustomScriptExecutionCommand: src\VSSDK\NuGet\AllowUnstablePackages.ps1 100 | - task: benjhuser.tfs-extensions-build-tasks.trigger-build-task.TriggerBuild@3 101 | displayName: Trigger a new build of DD-CB-TestSignVS-devCI 102 | inputs: 103 | buildDefinition: DD-CB-TestSignVS-devCI 104 | useSameBranch: false 105 | branchToUse: $(InsertTopicBranch) 106 | storeInEnvironmentVariable: true 107 | queueBuildForUserThatTriggeredBuild: false 108 | authenticationMethod: OAuth Token 109 | password: $(System.AccessToken) 110 | - task: PowerShell@2 111 | displayName: Associate InsertionOutputs artifacts with CloudBuild 112 | inputs: 113 | targetType: filePath 114 | filePath: $(Agent.TempDirectory)/DeploymentScripts/Scripts/Insertion/WriteArtifact.ps1 115 | arguments: '-oldBuildID $(Build.BuildId) -newBuildID $(TriggeredBuildIds) -artifactName "InsertionOutputs" -accessToken $(System.AccessToken)' 116 | - task: PowerShell@2 117 | displayName: Tag the build with LibraryName-insertion 118 | inputs: 119 | targetType: filePath 120 | filePath: $(Agent.TempDirectory)/DeploymentScripts/Scripts/Insertion/TagBuild.ps1 121 | arguments: '-buildID $(TriggeredBuildIds) -tagName "LibraryName-insertion" -accessToken $(System.AccessToken)' 122 | -------------------------------------------------------------------------------- /azure-pipelines/OptProf_part2.yml: -------------------------------------------------------------------------------- 1 | trigger: none 2 | pr: none 3 | 4 | resources: 5 | pipelines: 6 | - pipeline: VisualStudioBuildUnderTest 7 | source: DD-CB-TestSignVS-devCI 8 | trigger: 9 | tags: 10 | - LibraryName-insertion 11 | - pipeline: DartLab 12 | source: DartLab 13 | branch: main 14 | - pipeline: DartLab.OptProf 15 | source: DartLab.OptProf 16 | branch: main 17 | tags: 18 | - production 19 | repositories: 20 | - repository: DartLabTemplates 21 | type: git 22 | name: DartLab.Templates 23 | ref: refs/heads/main 24 | - repository: DartLabOptProfTemplates 25 | type: git 26 | name: DartLab.OptProf 27 | ref: refs/tags/Production 28 | 29 | parameters: 30 | 31 | # The prefix naming of the OptimizationInputs drop 32 | - name: optimizationDropPrefix 33 | type: string 34 | default: OptimizationInputs/$(System.TeamProject)/$(Build.Repository.Name) 35 | 36 | stages: 37 | - template: \templates\stages\visual-studio\single-runsettings.yml@DartLabOptProfTemplates 38 | parameters: 39 | ##### Required ##### 40 | runSettingsURI: $(Pipeline.Workspace)\VisualStudioBuildUnderTest\BuildArtifacts\runsettings\LibraryName.OptProf.runsettings 41 | visualStudioBootstrapperURI: https://vsdrop.corp.microsoft.com/file/v1/$(VisualStudio.BuildUnderTest.ProductsDropName);bootstrappers/Enterprise/vs_enterprise.exe 42 | ##### Optional ##### 43 | name: OptProfProfilingWorkflow 44 | displayName: OptProf Profiling Workflow 45 | optOptimizationInputsDropName: $(OptimizationInputsDropName) 46 | previousOptimizationInputsDropName: $(PreviousOptimizationInputsDropName) 47 | testLabPoolName: VS-Platform 48 | ##### Step Hooks ##### 49 | preTestMachineConfigurationStepList: 50 | - download: VisualStudioBuildUnderTest 51 | - task: PowerShell@2 52 | name: SetProductsDropName 53 | displayName: Set 'VisualStudio.BuildUnderTest.ProductsDropName' 54 | inputs: 55 | filePath: $(DartLab.Path)\Scripts\VisualStudio\Build\Get-VisualStudioDropName.ps1 56 | arguments: -DropNamePrefix 'Products' -VstsDropUrlsJson '$(Pipeline.Workspace)\VisualStudioBuildUnderTest\BuildArtifacts\VstsDropUrls.json' -OutVariableName 'VisualStudio.BuildUnderTest.ProductsDropName' 57 | preDeployAndRunTestsStepList: 58 | - download: VisualStudioBuildUnderTest 59 | prePublishOptimizationInputsDropStepList: 60 | # Set parameter for PreviousOptimizationInputsDropName, MicroBuildCommitID, and OptimizationInputsDropName 61 | - powershell: | 62 | try { 63 | $artifactName = 'InsertionOutputs' 64 | $BuildID = $(resources.pipeline.VisualStudioBuildUnderTest.runID) 65 | $artifact = Get-BuildArtifact -InstanceURL 'https://dev.azure.com/devdiv' -ProjectName 'DevDiv' -BuildID $BuildID -ArtifactName $artifactName -OAuthAccessToken (ConvertTo-SecureString '$(System.AccessToken)' -AsPlainText -Force) 66 | $containerName = $artifact.Resource.Data -Split '/' | Select-Object -Last 1 67 | $fileName = Join-Path $containerName 'Metadata.json' 68 | $jsonString = Read-BuildArtifactFile -InstanceURL 'https://dev.azure.com/devdiv' -ProjectName 'DevDiv' -BuildID $BuildID -ArtifactName $artifactName -FileName $fileName -OAuthAccessToken (ConvertTo-SecureString '$(System.AccessToken)' -AsPlainText -Force) 69 | $json = $jsonString | ConvertFrom-Json 70 | 71 | Write-Host "The content of the metadata.json file was $json" 72 | 73 | $dropname = $json.OptimizationData 74 | $commitID = $json.CommitID 75 | $OptimizationInputsDropName = "${{parameters.optimizationDropPrefix}}/$($commitID)/$(Build.BuildId)/$(System.StageId)/$(System.StageAttempt)" 76 | 77 | Write-Host "PreviousOptimizationInputsDropName: $dropname" 78 | Set-AzurePipelinesVariable 'PreviousOptimizationInputsDropName' $dropname 79 | 80 | Write-Host "MicroBuildCommitID: $commitID" 81 | Set-AzurePipelinesVariable 'MicroBuildCommitID' $commitID 82 | 83 | Write-Host "OptimizationInputsDropName: $OptimizationInputsDropName" 84 | Set-AzurePipelinesVariable 'OptimizationInputsDropName' $OptimizationInputsDropName 85 | } 86 | catch { 87 | Write-Host $_ 88 | Write-Error "Failed to set OptimizationInputsDropName pipeline variable" 89 | throw 90 | } 91 | displayName: Set MicroBuildCommitID, PreviousOptimizationInputsDropName, and OptimizationInputsDropName 92 | -------------------------------------------------------------------------------- /azure-pipelines/PoliCheckExclusions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | NODE_MODULES|.STORE 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /azure-pipelines/PostPRMessage.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding(SupportsShouldProcess = $true)] 2 | param( 3 | [Parameter(Mandatory=$true)] 4 | $AccessToken, 5 | [Parameter(Mandatory=$true)] 6 | $Markdown, 7 | [ValidateSet('Active','ByDesign','Closed','Fixed','Pending','Unknown','WontFix')] 8 | $CommentState='Active' 9 | ) 10 | 11 | # See https://docs.microsoft.com/en-us/dotnet/api/microsoft.teamfoundation.sourcecontrol.webapi.commentthreadstatus?view=azure-devops-dotnet 12 | if ($CommentState -eq 'Active') { 13 | $StatusCode = 1 14 | } elseif ($CommentState -eq 'ByDesign') { 15 | $StatusCode = 5 16 | } elseif ($CommentState -eq 'Closed') { 17 | $StatusCode = 4 18 | } elseif ($CommentState -eq 'Fixed') { 19 | $StatusCode = 2 20 | } elseif ($CommentState -eq 'Pending') { 21 | $StatusCode = 6 22 | } elseif ($CommentState -eq 'Unknown') { 23 | $StatusCode = 0 24 | } elseif ($CommentState -eq 'WontFix') { 25 | $StatusCode = 3 26 | } 27 | 28 | # Build the JSON body up 29 | $body = ConvertTo-Json @{ 30 | comments = @(@{ 31 | parentCommentId = 0 32 | content = $Markdown 33 | commentType = 1 34 | }) 35 | status = $StatusCode 36 | } 37 | 38 | Write-Verbose "Posting JSON payload: `n$Body" 39 | 40 | # Post the message to the Pull Request 41 | # https://docs.microsoft.com/en-us/rest/api/azure/devops/git/pull%20request%20threads?view=azure-devops-rest-5.1 42 | $url = "$($env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI)$env:SYSTEM_TEAMPROJECTID/_apis/git/repositories/$($env:BUILD_REPOSITORY_NAME)/pullRequests/$($env:SYSTEM_PULLREQUEST_PULLREQUESTID)/threads?api-version=5.1" 43 | if ($PSCmdlet.ShouldProcess($url, 'Post comment via REST call')) { 44 | try { 45 | if (!$env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI) { 46 | Write-Error "Posting to the pull request requires that the script is running in an Azure Pipelines context." 47 | exit 1 48 | } 49 | Write-Host "Posting PR comment to: $url" 50 | Invoke-RestMethod -Uri $url -Method POST -Headers @{Authorization = "Bearer $AccessToken"} -Body $Body -ContentType application/json 51 | } 52 | catch { 53 | Write-Error $_ 54 | Write-Error $_.Exception.Message 55 | exit 2 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /azure-pipelines/TSAOptions.json: -------------------------------------------------------------------------------- 1 | { 2 | "tsaVersion": "TsaV2", 3 | "codebase": "NewOrUpdate", 4 | "codebaseName": "Microsoft.VisualStudio.Validation", 5 | "tsaStamp": "DevDiv", 6 | "tsaEnvironment": "PROD", 7 | "notificationAliases": [ 8 | "andarno@microsoft.com" 9 | ], 10 | "codebaseAdmins": [ 11 | "REDMOND\\andarno" 12 | ], 13 | "instanceUrl": "https://devdiv.visualstudio.com", 14 | "projectName": "DevDiv", 15 | "areaPath": "DevDiv\\VS Core\\Special Projects", 16 | "iterationPath": "DevDiv", 17 | "alltools": true, 18 | "repositoryName": "vs-validation" 19 | } 20 | -------------------------------------------------------------------------------- /azure-pipelines/WIFtoPATauth.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | - name: deadPATServiceConnectionId # The GUID of the PAT-based service connection whose access token must be replaced. 3 | type: string 4 | - name: wifServiceConnectionName # The name of the WIF service connection to use to get the access token. 5 | type: string 6 | - name: resource # The scope for which the access token is requested. 7 | type: string 8 | default: 499b84ac-1321-427f-aa17-267ca6975798 # Azure Artifact feeds (any of them) 9 | 10 | steps: 11 | - task: AzureCLI@2 12 | displayName: 🔏 Authenticate with WIF service connection 13 | inputs: 14 | azureSubscription: ${{ parameters.wifServiceConnectionName }} 15 | scriptType: pscore 16 | scriptLocation: inlineScript 17 | inlineScript: | 18 | $accessToken = az account get-access-token --query accessToken --resource '${{ parameters.resource }}' -o tsv 19 | # Set the access token as a secret, so it doesn't get leaked in the logs 20 | Write-Host "##vso[task.setsecret]$accessToken" 21 | # Override the apitoken of the nuget service connection, for the duration of this stage 22 | Write-Host "##vso[task.setendpoint id=${{ parameters.deadPATServiceConnectionId }};field=authParameter;key=apitoken]$accessToken" 23 | -------------------------------------------------------------------------------- /azure-pipelines/apiscan.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | - name: windowsPool 3 | type: object 4 | - name: RealSign 5 | type: boolean 6 | 7 | jobs: 8 | - job: apiscan 9 | displayName: APIScan 10 | dependsOn: Windows 11 | pool: ${{ parameters.windowsPool }} 12 | timeoutInMinutes: 120 13 | templateContext: 14 | ${{ if not(parameters.RealSign) }}: 15 | mb: 16 | signing: # if the build is test-signed, install the signing plugin so that CSVTestSignPolicy.xml is available 17 | enabled: true 18 | zipSources: false 19 | signType: test 20 | outputs: 21 | - output: pipelineArtifact 22 | displayName: 📢 collect apiscan artifact 23 | targetPath: $(Pipeline.Workspace)/.gdn/.r/apiscan/001/Logs 24 | artifactName: apiscan-logs 25 | condition: succeededOrFailed() 26 | variables: 27 | - name: SymbolsFeatureName 28 | value: $[ dependencies.Windows.outputs['SetPipelineVariables.SymbolsFeatureName'] ] 29 | - name: NBGV_MajorMinorVersion 30 | value: $[ dependencies.Windows.outputs['nbgv.NBGV_MajorMinorVersion'] ] 31 | - ${{ if eq(variables['system.collectionId'], '011b8bdf-6d56-4f87-be0d-0092136884d9') }}: 32 | # https://dev.azure.com/devdiv/DevDiv/_wiki/wikis/DevDiv.wiki/25351/APIScan-step-by-step-guide-to-setting-up-a-Pipeline 33 | - group: VSEng sponsored APIScan # Expected to provide ApiScanClientId 34 | steps: 35 | # We need TSAOptions.json 36 | - checkout: self 37 | fetchDepth: 1 38 | 39 | - download: current 40 | artifact: APIScanInputs 41 | displayName: 🔻 Download APIScanInputs artifact 42 | 43 | - task: APIScan@2 44 | displayName: 🔍 Run APIScan 45 | inputs: 46 | softwareFolder: $(Pipeline.Workspace)/APIScanInputs 47 | softwareName: $(SymbolsFeatureName) 48 | softwareVersionNum: $(NBGV_MajorMinorVersion) 49 | isLargeApp: false 50 | toolVersion: Latest 51 | preserveLogsFolder: true 52 | env: 53 | AzureServicesAuthConnectionString: runAs=App;AppId=$(ApiScanClientId) 54 | 55 | # File bugs when APIScan finds issues 56 | - task: TSAUpload@2 57 | displayName: 🪳 TSA upload 58 | inputs: 59 | GdnPublishTsaOnboard: True 60 | GdnPublishTsaConfigFile: $(Build.SourcesDirectory)\azure-pipelines\TSAOptions.json 61 | condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main')) 62 | -------------------------------------------------------------------------------- /azure-pipelines/archive-sourcecode.yml: -------------------------------------------------------------------------------- 1 | trigger: none # We only want to trigger manually or based on resources 2 | pr: none 3 | 4 | # Source archival requirements come from a compliance tenet. Review a sample task here: https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1550985 5 | # Source code should be archived within 30 days of the release date, and at least every quarter if your product is releasing more than once every 6 months. 6 | # If your sources on GitHub are public open source project, then using GitHub Public Archive is sufficient. 7 | schedules: 8 | - cron: "13 13 13 */3 *" # Every three months 9 | displayName: Periodic source archival 10 | branches: 11 | include: 12 | - main 13 | 14 | resources: 15 | repositories: 16 | - repository: MicroBuildTemplate 17 | type: git 18 | name: 1ESPipelineTemplates/MicroBuildTemplate 19 | ref: refs/tags/release 20 | 21 | parameters: 22 | - name: notes 23 | displayName: Notes to include in the SCA request 24 | type: string 25 | default: ' ' # optional parameters require a non-empty default. 26 | - name: whatif 27 | displayName: Only simulate the request 28 | type: boolean 29 | default: false 30 | 31 | variables: 32 | - group: VS Core team # Expected to provide ManagerAlias, SourceCodeArchivalUri 33 | - template: GlobalVariables.yml 34 | 35 | extends: 36 | template: azure-pipelines/MicroBuild.1ES.Official.yml@MicroBuildTemplate 37 | parameters: 38 | sdl: 39 | sourceAnalysisPool: VSEngSS-MicroBuild2022-1ES 40 | 41 | stages: 42 | - stage: archive 43 | jobs: 44 | - job: archive 45 | pool: 46 | name: AzurePipelines-EO 47 | demands: 48 | - ImageOverride -equals 1ESPT-Ubuntu22.04 49 | os: Linux 50 | 51 | steps: 52 | - checkout: self 53 | clean: true 54 | fetchDepth: 0 55 | - powershell: tools/Install-DotNetSdk.ps1 56 | displayName: ⚙ Install .NET SDK 57 | - task: NuGetAuthenticate@1 58 | displayName: 🔏 Authenticate NuGet feeds 59 | inputs: 60 | forceReinstallCredentialProvider: true 61 | - script: dotnet tool restore 62 | displayName: ⚙️ Restore CLI tools 63 | - powershell: tools/variables/_define.ps1 64 | failOnStderr: true 65 | displayName: ⚙ Set pipeline variables based on source 66 | - task: AzureCLI@2 67 | displayName: 🔏 Authenticate with WIF service connection 68 | inputs: 69 | azureSubscription: VS Core Source Code Archival 70 | scriptType: pscore 71 | scriptLocation: inlineScript 72 | inlineScript: | 73 | $accessToken = az account get-access-token --query accessToken --resource api://177cf50a-4bf5-4481-8b7e-f32900dfc8e6 -o tsv 74 | Write-Host "##vso[task.setvariable variable=scaToken;issecret=true]$accessToken" 75 | - pwsh: > 76 | $TeamAlias = '$(TeamEmail)'.Substring(0, '$(TeamEmail)'.IndexOf('@')) 77 | 78 | azure-pipelines/Archive-SourceCode.ps1 79 | -ManagerAlias '$(ManagerAlias)' 80 | -TeamAlias $TeamAlias 81 | -BusinessGroupName '$(BusinessGroupName)' 82 | -ProductName '$(SymbolsFeatureName)' 83 | -ProductLanguage English 84 | -Notes '${{ parameters.notes }}' 85 | -AccessToken '$(scaToken)' 86 | -Verbose 87 | -WhatIf:$${{ parameters.whatif }} 88 | displayName: 🗃️ Submit archival request 89 | -------------------------------------------------------------------------------- /azure-pipelines/dotnet.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | - name: RunTests 3 | - name: IsOptProf 4 | type: boolean 5 | default: false 6 | - name: Is1ESPT 7 | type: boolean 8 | 9 | steps: 10 | 11 | - script: dotnet build -t:build,pack --no-restore -c $(BuildConfiguration) -warnAsError -warnNotAsError:NU1901,NU1902,NU1903,NU1904,LOCTASK002 /bl:"$(Build.ArtifactStagingDirectory)/build_logs/build.binlog" 12 | displayName: 🛠 dotnet build 13 | 14 | - ${{ if not(parameters.IsOptProf) }}: 15 | - powershell: tools/dotnet-test-cloud.ps1 -Configuration $(BuildConfiguration) -Agent $(Agent.JobName) -PublishResults 16 | displayName: 🧪 dotnet test 17 | condition: and(succeeded(), ${{ parameters.RunTests }}) 18 | 19 | - ${{ if parameters.IsOptProf }}: 20 | - script: dotnet pack src\VSInsertionMetadata -c $(BuildConfiguration) -warnaserror /bl:"$(Build.ArtifactStagingDirectory)/build_logs/VSInsertion-Pack.binlog" 21 | displayName: 🔧 dotnet pack VSInsertionMetadata 22 | 23 | - powershell: tools/variables/_define.ps1 24 | failOnStderr: true 25 | displayName: ⚙ Update pipeline variables based on build outputs 26 | condition: succeededOrFailed() 27 | 28 | - ${{ if parameters.Is1ESPT }}: 29 | - powershell: azure-pipelines/publish_artifacts.ps1 -StageOnly -AvoidSymbolicLinks -ArtifactNameSuffix "-$(Agent.JobName)" -Verbose 30 | failOnStderr: true 31 | displayName: 📢 Stage artifacts 32 | condition: succeededOrFailed() 33 | - ${{ else }}: 34 | - powershell: azure-pipelines/publish_artifacts.ps1 -ArtifactNameSuffix "-$(Agent.JobName)" -Verbose 35 | failOnStderr: true 36 | displayName: 📢 Publish artifacts 37 | condition: succeededOrFailed() 38 | 39 | - ${{ if and(ne(variables['codecov_token'], ''), parameters.RunTests) }}: 40 | - powershell: | 41 | $ArtifactStagingFolder = & "tools/Get-ArtifactsStagingDirectory.ps1" 42 | $CoverageResultsFolder = Join-Path $ArtifactStagingFolder "coverageResults-$(Agent.JobName)" 43 | tools/publish-CodeCov.ps1 -CodeCovToken "$(codecov_token)" -PathToCodeCoverage "$CoverageResultsFolder" -Name "$(Agent.JobName) Coverage Results" -Flags "$(Agent.JobName)" 44 | displayName: 📢 Publish code coverage results to codecov.io 45 | timeoutInMinutes: 3 46 | continueOnError: true 47 | -------------------------------------------------------------------------------- /azure-pipelines/falsepositives.gdnsuppress: -------------------------------------------------------------------------------- 1 | { 2 | "version": "latest", 3 | "suppressionSets": { 4 | "falsepositives": { 5 | "name": "falsepositives", 6 | "createdDate": "2021-12-03 00:23:08Z", 7 | "lastUpdatedDate": "2021-12-03 00:23:08Z" 8 | } 9 | }, 10 | "results": { 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /azure-pipelines/install-dependencies.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | - name: initArgs 3 | type: string 4 | default: '' 5 | - name: needsAzurePublicFeeds 6 | type: boolean 7 | default: true # If nuget.config pulls from the azure-public account, we need to authenticate when building on the devdiv account. 8 | 9 | steps: 10 | - ${{ if and(parameters.needsAzurePublicFeeds, eq(variables['system.collectionId'], '011b8bdf-6d56-4f87-be0d-0092136884d9')) }}: 11 | - template: WIFtoPATauth.yml 12 | parameters: 13 | wifServiceConnectionName: azure-public/vside package pull 14 | deadPATServiceConnectionId: 46f0d4d4-9fff-4c58-a1ab-3b8f97e3b78a # azure-public/msft_consumption_public 15 | 16 | - task: NuGetAuthenticate@1 17 | displayName: 🔏 Authenticate NuGet feeds 18 | inputs: 19 | ${{ if and(parameters.needsAzurePublicFeeds, eq(variables['system.collectionId'], '011b8bdf-6d56-4f87-be0d-0092136884d9')) }}: 20 | nuGetServiceConnections: azure-public/msft_consumption_public 21 | 22 | - powershell: | 23 | $AccessToken = '$(System.AccessToken)' # Avoid specifying the access token directly on the init.ps1 command line to avoid it showing up in errors 24 | .\init.ps1 -AccessToken $AccessToken ${{ parameters['initArgs'] }} -UpgradePrerequisites -NoNuGetCredProvider 25 | dotnet --info 26 | 27 | # Print mono version if it is present. 28 | if (Get-Command mono -ErrorAction SilentlyContinue) { 29 | mono --version 30 | } 31 | displayName: ⚙ Install prerequisites 32 | 33 | - powershell: tools/variables/_define.ps1 34 | failOnStderr: true 35 | displayName: ⚙ Set pipeline variables based on source 36 | name: SetPipelineVariables 37 | -------------------------------------------------------------------------------- /azure-pipelines/microbuild.after.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | - name: EnableOptProf 3 | type: boolean 4 | default: false 5 | - name: IsOptProf 6 | type: boolean 7 | default: false 8 | - name: SkipCodesignVerify 9 | type: boolean 10 | 11 | steps: 12 | - ${{ if not(parameters.SkipCodesignVerify) }}: 13 | - task: MicroBuildCodesignVerify@3 14 | displayName: 🔍 Verify Signed Files 15 | inputs: 16 | ApprovalListPathForSigs: $(Build.SourcesDirectory)\azure-pipelines\no_strongname.txt 17 | ApprovalListPathForCerts: $(Build.SourcesDirectory)\azure-pipelines\no_authenticode.txt 18 | TargetFolders: | 19 | $(Build.SourcesDirectory)/bin/Packages/$(BuildConfiguration) 20 | condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT')) 21 | 22 | - ${{ if parameters.IsOptProf }}: 23 | - task: ms-vscs-artifact.build-tasks.artifactDropTask-1.artifactDropTask@0 24 | inputs: 25 | dropServiceURI: https://devdiv.artifacts.visualstudio.com 26 | buildNumber: $(ProfilingInputsDropName) 27 | sourcePath: $(Build.ArtifactStagingDirectory)\OptProf\ProfilingInputs 28 | toLowerCase: false 29 | usePat: true 30 | displayName: 📢 Publish to Artifact Services - ProfilingInputs 31 | condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) 32 | 33 | - task: PublishBuildArtifacts@1 34 | inputs: 35 | PathtoPublish: $(Build.ArtifactStagingDirectory)/InsertionOutputs 36 | ArtifactName: InsertionOutputs 37 | ArtifactType: Container 38 | displayName: 📢 Publish InsertionOutputs as Azure DevOps artifacts 39 | -------------------------------------------------------------------------------- /azure-pipelines/microbuild.before.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | - name: EnableLocalization 3 | type: boolean 4 | default: false 5 | - name: EnableOptProf 6 | type: boolean 7 | default: false 8 | - name: IsOptProf 9 | type: boolean 10 | default: false 11 | - name: ShouldSkipOptimize 12 | type: boolean 13 | default: false 14 | - name: RealSign 15 | type: boolean 16 | 17 | steps: 18 | - ${{ if and(not(parameters.IsOptProf), ne(variables['Build.Reason'], 'PullRequest')) }}: 19 | # notice@0 requires CG detection to run first, and non-default branches don't inject it automatically. 20 | - ${{ if ne(variables['Build.SourceBranch'], 'refs/heads/main') }}: 21 | - task: ComponentGovernanceComponentDetection@0 22 | displayName: 🔍 Component Detection 23 | 24 | - task: notice@0 25 | displayName: 🛠️ Generate NOTICE file 26 | inputs: 27 | outputfile: $(System.DefaultWorkingDirectory)/obj/NOTICE 28 | outputformat: text 29 | retryCountOnTaskFailure: 3 # fails when the cloud service is overloaded 30 | continueOnError: ${{ not(parameters.RealSign) }} # Tolerate failures when we're not building something that may ship. 31 | 32 | - ${{ if parameters.IsOptProf }}: 33 | # We have to install these plugins ourselves for Optprof runs because those pipelines haven't migrated to 1ES PT yet. 34 | - task: MicroBuildOptProfPlugin@6 35 | inputs: 36 | ProfilingInputsDropName: $(ProfilingInputsDropName) 37 | OptimizationInputsLookupMethod: DropPrefix 38 | DropNamePrefix: OptimizationInputs/$(System.TeamProject)/$(Build.Repository.Name) 39 | ShouldSkipOptimize: ${{ parameters.ShouldSkipOptimize }} 40 | AccessToken: $(System.AccessToken) 41 | displayName: 🔧 Install OptProf Plugin 42 | 43 | - task: MicroBuildSigningPlugin@4 44 | inputs: 45 | signType: Real 46 | zipSources: false 47 | displayName: 🔧 Install MicroBuild Signing Plugin 48 | 49 | - ${{ if parameters.EnableLocalization }}: 50 | - task: MicroBuildLocalizationPlugin@4 51 | inputs: 52 | languages: $(LocLanguages) 53 | displayName: 🔧 Install MicroBuild Localization Plugin 54 | -------------------------------------------------------------------------------- /azure-pipelines/no_authenticode.txt: -------------------------------------------------------------------------------- 1 | bin\packages\release\vsix\_manifest\manifest.cat,sbom signed 2 | bin\packages\release\vsix\_manifest\spdx_2.2\manifest.cat,sbom signed 3 | -------------------------------------------------------------------------------- /azure-pipelines/no_strongname.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vs-validation/28c82310309d02f6f9bdbceb8b393296d6d8e2ee/azure-pipelines/no_strongname.txt -------------------------------------------------------------------------------- /azure-pipelines/official.yml: -------------------------------------------------------------------------------- 1 | trigger: none # We only want to trigger manually or based on a schedule 2 | pr: none 3 | #schedules: 4 | #- cron: "0 3 * * *" # Daily @ 8 PM PST 5 | # displayName: Daily vs-insertion 6 | # branches: 7 | # include: 8 | # - main 9 | # - 'v16.*' 10 | # - 'v17.*' 11 | 12 | parameters: 13 | # As an entrypoint pipeline yml file, all parameters here show up in the Queue Run dialog. 14 | # If any paramaters should NOT be queue-time options, they should be removed from here 15 | # and references to them in this file replaced with hard-coded values. 16 | # - name: ShouldSkipOptimize # Uncomment this and references to it below when setting EnableOptProf to true in build.yml. 17 | # displayName: Skip OptProf optimization 18 | # type: boolean 19 | # default: false 20 | - name: EnableMacOSBuild 21 | displayName: Build on macOS 22 | type: boolean 23 | default: false # macOS is often bogged down in Azure Pipelines 24 | - name: RunTests 25 | displayName: Run tests 26 | type: boolean 27 | default: true 28 | - name: EnableAPIScan 29 | displayName: Include APIScan with compliance tools 30 | type: boolean 31 | default: true 32 | - name: PublishCodeCoverage 33 | displayName: Publish code coverage 34 | type: boolean 35 | default: true 36 | 37 | resources: 38 | repositories: 39 | - repository: MicroBuildTemplate 40 | type: git 41 | name: 1ESPipelineTemplates/MicroBuildTemplate 42 | ref: refs/tags/release 43 | 44 | variables: 45 | - template: GlobalVariables.yml 46 | 47 | extends: 48 | template: azure-pipelines/MicroBuild.1ES.Official.yml@MicroBuildTemplate 49 | parameters: 50 | sdl: 51 | sourceAnalysisPool: VSEngSS-MicroBuild2022-1ES 52 | codeSignValidation: 53 | enabled: true 54 | break: true 55 | additionalTargetsGlobPattern: -|Variables-*\*.ps1;-|LocBin-*\**;-|APIScanInputs-*\**;-|test_symbols-*\**;-|MicroBuild\** 56 | policheck: 57 | enabled: true 58 | exclusionsFile: $(System.DefaultWorkingDirectory)\azure-pipelines\PoliCheckExclusions.xml 59 | suppression: 60 | suppressionFile: $(System.DefaultWorkingDirectory)\azure-pipelines\falsepositives.gdnsuppress 61 | sbom: 62 | enabled: false # Skip 1ES SBOM because microbuild has our own sbom system 63 | stages: 64 | - stage: Build 65 | variables: 66 | - template: /azure-pipelines/BuildStageVariables.yml@self 67 | jobs: 68 | - template: /azure-pipelines/build.yml@self 69 | parameters: 70 | Is1ESPT: true 71 | RealSign: true 72 | # ShouldSkipOptimize: ${{ parameters.ShouldSkipOptimize }} 73 | EnableAPIScan: ${{ parameters.EnableAPIScan }} 74 | windowsPool: VSEngSS-MicroBuild2022-1ES 75 | linuxPool: 76 | name: AzurePipelines-EO 77 | demands: 78 | - ImageOverride -equals 1ESPT-Ubuntu22.04 79 | os: Linux 80 | macOSPool: 81 | name: Azure Pipelines 82 | vmImage: macOS-14 83 | os: macOS 84 | EnableMacOSBuild: ${{ parameters.EnableMacOSBuild }} 85 | RunTests: ${{ parameters.RunTests }} 86 | PublishCodeCoverage: ${{ parameters.PublishCodeCoverage }} 87 | - template: /azure-pipelines/prepare-insertion-stages.yml@self 88 | parameters: 89 | RealSign: true 90 | -------------------------------------------------------------------------------- /azure-pipelines/prepare-insertion-stages.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | - name: ArchiveSymbols 3 | type: boolean 4 | default: true 5 | - name: RealSign 6 | displayName: Real sign? 7 | type: boolean 8 | - name: PackagePush 9 | type: boolean 10 | default: true 11 | 12 | stages: 13 | - ${{ if or(parameters.ArchiveSymbols, parameters.PackagePush) }}: 14 | - stage: release 15 | displayName: Publish 16 | jobs: 17 | - ${{ if parameters.ArchiveSymbols }}: 18 | - job: symbol_archive 19 | displayName: Archive symbols 20 | pool: VSEngSS-MicroBuild2022-1ES 21 | variables: 22 | ONEES_ENFORCED_CODEQL_ENABLED: false # CodeQL runs on build stages, we don't need it here 23 | steps: 24 | - checkout: none 25 | - download: current 26 | artifact: Variables-Windows 27 | displayName: 🔻 Download Variables-Windows artifact 28 | - powershell: $(Pipeline.Workspace)/Variables-Windows/_define.ps1 29 | displayName: ⚙️ Set pipeline variables based on artifacts 30 | - download: current 31 | artifact: symbols-legacy 32 | displayName: 🔻 Download symbols-legacy artifact 33 | - task: MicroBuildArchiveSymbols@5 34 | displayName: 🔣 Archive symbols to Symweb 35 | inputs: 36 | SymbolsFeatureName: $(SymbolsFeatureName) 37 | SymbolsProject: VS 38 | SymbolsAgentPath: $(Pipeline.Workspace)/symbols-legacy 39 | 40 | - ${{ if parameters.PackagePush }}: 41 | - job: push 42 | ${{ if parameters.RealSign }}: 43 | displayName: azure-public/vssdk feed 44 | ${{ else }}: 45 | displayName: devdiv/vs-impl feed # Leave this as-is, since non-signed builds must not be pushed to public feeds. 46 | ${{ if parameters.ArchiveSymbols }}: 47 | dependsOn: symbol_archive 48 | pool: 49 | name: AzurePipelines-EO 50 | demands: 51 | - ImageOverride -equals 1ESPT-Ubuntu22.04 52 | os: Linux 53 | templateContext: 54 | outputs: 55 | - output: nuget 56 | displayName: 📦 Push nuget packages 57 | packagesToPush: '$(Pipeline.Workspace)/deployables-Windows/NuGet/*.nupkg' 58 | packageParentPath: $(Pipeline.Workspace)/deployables-Windows/NuGet 59 | allowPackageConflicts: true 60 | ${{ if parameters.RealSign }}: 61 | nuGetFeedType: external 62 | publishFeedCredentials: azure-public/vssdk 63 | ${{ else }}: 64 | nuGetFeedType: internal 65 | publishVstsFeed: vs-impl # Leave this as-is, since non-signed builds must not be pushed to public feeds. 66 | variables: 67 | ONEES_ENFORCED_CODEQL_ENABLED: false # CodeQL runs on build stages, we don't need it here 68 | steps: 69 | - checkout: none 70 | - download: current 71 | artifact: Variables-Windows 72 | displayName: 🔻 Download Variables-Windows artifact 73 | - powershell: $(Pipeline.Workspace)/Variables-Windows/_define.ps1 74 | displayName: ⚙️ Set pipeline variables based on artifacts 75 | - download: current 76 | artifact: deployables-Windows 77 | displayName: 🔻 Download deployables-Windows artifact 78 | - ${{ if parameters.RealSign }}: 79 | - template: WIFtoPATauth.yml 80 | parameters: 81 | wifServiceConnectionName: azure-public/vside package push 82 | deadPATServiceConnectionId: 42175e93-c771-4a4f-a132-3cca78f44b3b # azure-public/vssdk 83 | -------------------------------------------------------------------------------- /azure-pipelines/publish-codecoverage.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | - name: EnableMacOSBuild 3 | type: boolean 4 | - name: EnableLinuxBuild 5 | type: boolean 6 | 7 | steps: 8 | - download: current 9 | artifact: coverageResults-Windows 10 | displayName: 🔻 Download Windows code coverage results 11 | continueOnError: true 12 | - ${{ if parameters.EnableLinuxBuild }}: 13 | - download: current 14 | artifact: coverageResults-Linux 15 | displayName: 🔻 Download Linux code coverage results 16 | continueOnError: true 17 | - ${{ if parameters.EnableMacOSBuild }}: 18 | - download: current 19 | artifact: coverageResults-macOS 20 | displayName: 🔻 Download macOS code coverage results 21 | continueOnError: true 22 | - powershell: azure-pipelines/Merge-CodeCoverage.ps1 -Path '$(Pipeline.Workspace)' -OutputFile coveragereport/merged.cobertura.xml -Format Cobertura -Verbose 23 | displayName: ⚙ Merge coverage 24 | - task: PublishCodeCoverageResults@2 25 | displayName: 📢 Publish code coverage results to Azure DevOps 26 | inputs: 27 | summaryFileLocation: coveragereport/merged.cobertura.xml 28 | failIfCoverageEmpty: true 29 | -------------------------------------------------------------------------------- /azure-pipelines/publish-symbols.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | - name: EnableMacOSBuild 3 | type: boolean 4 | - name: EnableLinuxBuild 5 | type: boolean 6 | 7 | steps: 8 | - task: DownloadPipelineArtifact@2 9 | inputs: 10 | artifact: symbols-Windows 11 | path: $(Pipeline.Workspace)/symbols/Windows 12 | displayName: 🔻 Download Windows symbols 13 | continueOnError: true 14 | - ${{ if parameters.EnableLinuxBuild }}: 15 | - task: DownloadPipelineArtifact@2 16 | inputs: 17 | artifact: symbols-Linux 18 | path: $(Pipeline.Workspace)/symbols/Linux 19 | displayName: 🔻 Download Linux symbols 20 | continueOnError: true 21 | - ${{ if parameters.EnableMacOSBuild }}: 22 | - task: DownloadPipelineArtifact@2 23 | inputs: 24 | artifact: symbols-macOS 25 | path: $(Pipeline.Workspace)/symbols/macOS 26 | displayName: 🔻 Download macOS symbols 27 | continueOnError: true 28 | 29 | - task: DownloadPipelineArtifact@2 30 | inputs: 31 | artifact: test_symbols-Windows 32 | path: $(Pipeline.Workspace)/test_symbols/Windows 33 | displayName: 🔻 Download Windows test symbols 34 | continueOnError: true 35 | - ${{ if parameters.EnableLinuxBuild }}: 36 | - task: DownloadPipelineArtifact@2 37 | inputs: 38 | artifact: test_symbols-Linux 39 | path: $(Pipeline.Workspace)/test_symbols/Linux 40 | displayName: 🔻 Download Linux test symbols 41 | continueOnError: true 42 | - ${{ if parameters.EnableMacOSBuild }}: 43 | - task: DownloadPipelineArtifact@2 44 | inputs: 45 | artifact: test_symbols-macOS 46 | path: $(Pipeline.Workspace)/test_symbols/macOS 47 | displayName: 🔻 Download macOS test symbols 48 | continueOnError: true 49 | 50 | - task: PublishSymbols@2 51 | inputs: 52 | SymbolsFolder: $(Pipeline.Workspace)/symbols 53 | SearchPattern: '**/*.pdb' 54 | IndexSources: false 55 | SymbolServerType: TeamServices 56 | displayName: 📢 Publish symbols 57 | 58 | - task: PublishSymbols@2 59 | inputs: 60 | SymbolsFolder: $(Pipeline.Workspace)/test_symbols 61 | SearchPattern: '**/*.pdb' 62 | IndexSources: false 63 | SymbolServerType: TeamServices 64 | displayName: 📢 Publish test symbols 65 | 66 | - powershell: tools/Prepare-Legacy-Symbols.ps1 -Path $(Pipeline.Workspace)/symbols/Windows 67 | displayName: ⚙ Prepare symbols for symbol archival 68 | -------------------------------------------------------------------------------- /azure-pipelines/publish_artifacts.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | This script translates all the artifacts described by _all.ps1 4 | into commands that instruct Azure Pipelines to actually collect those artifacts. 5 | #> 6 | 7 | [CmdletBinding()] 8 | param ( 9 | [string]$ArtifactNameSuffix, 10 | [switch]$StageOnly, 11 | [switch]$AvoidSymbolicLinks 12 | ) 13 | 14 | Function Set-PipelineVariable($name, $value) { 15 | if ((Test-Path "Env:\$name") -and (Get-Item "Env:\$name").Value -eq $value) { 16 | return # already set 17 | } 18 | 19 | #New-Item -LiteralPath "Env:\$name".ToUpper() -Value $value -Force | Out-Null 20 | Write-Host "##vso[task.setvariable variable=$name]$value" 21 | } 22 | 23 | Function Test-ArtifactUploaded($artifactName) { 24 | $varName = "ARTIFACTUPLOADED_$($artifactName.ToUpper())" 25 | Test-Path "env:$varName" 26 | } 27 | 28 | & "$PSScriptRoot/../tools/artifacts/_stage_all.ps1" -ArtifactNameSuffix $ArtifactNameSuffix -AvoidSymbolicLinks:$AvoidSymbolicLinks |% { 29 | # Set a variable which will out-live this script so that a subsequent attempt to collect and upload artifacts 30 | # will skip this one from a check in the _all.ps1 script. 31 | Set-PipelineVariable "ARTIFACTSTAGED_$($_.Name.ToUpper())" 'true' 32 | Write-Host "Staged artifact $($_.Name) to $($_.Path)" 33 | 34 | if (!$StageOnly) { 35 | if (Test-ArtifactUploaded $_.Name) { 36 | Write-Host "Skipping $($_.Name) because it has already been uploaded." -ForegroundColor DarkGray 37 | } else { 38 | Write-Host "##vso[artifact.upload containerfolder=$($_.Name);artifactname=$($_.Name);]$($_.Path)" 39 | 40 | # Set a variable which will out-live this script so that a subsequent attempt to collect and upload artifacts 41 | # will skip this one from a check in the _all.ps1 script. 42 | Set-PipelineVariable "ARTIFACTUPLOADED_$($_.Name.ToUpper())" 'true' 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /azure-pipelines/release-deployment-prep.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - download: CI 3 | artifact: Variables-Windows 4 | displayName: 🔻 Download Variables-Windows artifact 5 | - powershell: $(Pipeline.Workspace)/CI/Variables-Windows/_define.ps1 6 | displayName: ⚙️ Set pipeline variables based on artifacts 7 | -------------------------------------------------------------------------------- /azure-pipelines/release.yml: -------------------------------------------------------------------------------- 1 | trigger: none # We only want to trigger manually or based on resources 2 | pr: none 3 | 4 | resources: 5 | repositories: 6 | - repository: MicroBuildTemplate 7 | type: git 8 | name: 1ESPipelineTemplates/MicroBuildTemplate 9 | ref: refs/tags/release 10 | pipelines: 11 | - pipeline: CI 12 | source: vs-Validation 13 | trigger: 14 | tags: 15 | - auto-release 16 | 17 | variables: 18 | - template: GlobalVariables.yml 19 | 20 | extends: 21 | template: azure-pipelines/MicroBuild.1ES.Official.yml@MicroBuildTemplate 22 | parameters: 23 | sdl: 24 | sourceAnalysisPool: VSEngSS-MicroBuild2022-1ES 25 | 26 | stages: 27 | - stage: release 28 | jobs: 29 | - job: release 30 | pool: 31 | name: AzurePipelines-EO 32 | demands: 33 | - ImageOverride -equals 1ESPT-Ubuntu22.04 34 | os: Linux 35 | templateContext: 36 | outputs: 37 | - output: nuget 38 | displayName: 📦 Push packages to nuget.org 39 | packagesToPush: '$(Pipeline.Workspace)/CI/deployables-Windows/NuGet/*.nupkg' 40 | packageParentPath: $(Pipeline.Workspace)/CI/deployables-Windows/NuGet 41 | allowPackageConflicts: true 42 | nuGetFeedType: external 43 | publishFeedCredentials: VisualStudioExtensibility (nuget.org) 44 | steps: 45 | - checkout: none 46 | - powershell: | 47 | Write-Host "##vso[build.updatebuildnumber]$(resources.pipeline.CI.runName)" 48 | if ('$(resources.pipeline.CI.runName)'.Contains('-')) { 49 | Write-Host "##vso[task.setvariable variable=IsPrerelease]true" 50 | } else { 51 | Write-Host "##vso[task.setvariable variable=IsPrerelease]false" 52 | } 53 | displayName: ⚙ Set up pipeline 54 | - download: CI 55 | artifact: deployables-Windows 56 | displayName: 🔻 Download deployables-Windows artifact 57 | patterns: 'NuGet/*' 58 | - task: GitHubRelease@1 59 | displayName: 📢 GitHub release (create) 60 | inputs: 61 | gitHubConnection: AArnott 62 | repositoryName: $(Build.Repository.Name) 63 | target: $(resources.pipeline.CI.sourceCommit) 64 | tagSource: userSpecifiedTag 65 | tag: v$(resources.pipeline.CI.runName) 66 | title: v$(resources.pipeline.CI.runName) 67 | isDraft: true # After running this step, visit the new draft release, edit, and publish. 68 | isPreRelease: $(IsPrerelease) 69 | assets: $(Pipeline.Workspace)/CI/deployables-Windows/NuGet/*.nupkg 70 | changeLogCompareToRelease: lastNonDraftRelease 71 | changeLogType: issueBased 72 | changeLogLabels: | 73 | [ 74 | { "label" : "breaking change", "displayName" : "Breaking changes", "state" : "closed" }, 75 | { "label" : "bug", "displayName" : "Fixes", "state" : "closed" }, 76 | { "label" : "enhancement", "displayName": "Enhancements", "state" : "closed" } 77 | ] 78 | -------------------------------------------------------------------------------- /azure-pipelines/schedule-only-steps.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - powershell: echo "##vso[build.addbuildtag]auto-insertion" 3 | displayName: Tag for auto-insertion 4 | -------------------------------------------------------------------------------- /azure-pipelines/unofficial.yml: -------------------------------------------------------------------------------- 1 | trigger: 2 | batch: true 3 | branches: 4 | include: 5 | - main 6 | - 'validate/*' 7 | paths: 8 | exclude: 9 | - doc/ 10 | - '*.md' 11 | - .vscode/ 12 | - azure-pipelines/release.yml 13 | - azure-pipelines/vs-insertion.yml 14 | 15 | parameters: 16 | # As an entrypoint pipeline yml file, all parameters here show up in the Queue Run dialog. 17 | # If any paramaters should NOT be queue-time options, they should be removed from here 18 | # and references to them in this file replaced with hard-coded values. 19 | # - name: ShouldSkipOptimize # Uncomment this and references to it below when setting EnableOptProf to true in build.yml. 20 | # displayName: Skip OptProf optimization 21 | # type: boolean 22 | # default: false 23 | - name: EnableMacOSBuild 24 | displayName: Build on macOS 25 | type: boolean 26 | default: false # macOS is often bogged down in Azure Pipelines 27 | - name: RunTests 28 | displayName: Run tests 29 | type: boolean 30 | default: true 31 | - name: EnableAPIScan 32 | displayName: Include APIScan with compliance tools 33 | type: boolean 34 | default: false 35 | - name: EnableProductionSDL 36 | displayName: Enable Production SDL 37 | type: boolean 38 | default: false 39 | - name: PublishCodeCoverage 40 | displayName: Publish code coverage 41 | type: boolean 42 | default: true 43 | 44 | resources: 45 | repositories: 46 | - repository: MicroBuildTemplate 47 | type: git 48 | name: 1ESPipelineTemplates/MicroBuildTemplate 49 | ref: refs/tags/release 50 | 51 | variables: 52 | - template: GlobalVariables.yml 53 | 54 | extends: 55 | template: azure-pipelines/MicroBuild.1ES.Unofficial.yml@MicroBuildTemplate 56 | parameters: 57 | sdl: 58 | sourceAnalysisPool: VSEngSS-MicroBuild2022-1ES 59 | suppression: 60 | suppressionFile: $(System.DefaultWorkingDirectory)\azure-pipelines\falsepositives.gdnsuppress 61 | enableProductionSDL: ${{ parameters.EnableProductionSDL }} 62 | codeSignValidation: 63 | enabled: ${{ parameters.EnableProductionSDL }} 64 | break: true 65 | additionalTargetsGlobPattern: -|Variables-*\*.ps1;-|APIScanInputs-*\**;-|test_symbols-*\**;-|MicroBuild\** 66 | policyFile: $(MBSIGN_APPFOLDER)\CSVTestSignPolicy.xml 67 | policheck: 68 | enabled: ${{ parameters.EnableProductionSDL }} 69 | exclusionsFile: $(System.DefaultWorkingDirectory)\azure-pipelines\PoliCheckExclusions.xml 70 | sbom: 71 | enabled: false # Skip 1ES SBOM because microbuild has our own sbom system 72 | stages: 73 | - stage: Build 74 | variables: 75 | - template: /azure-pipelines/BuildStageVariables.yml@self 76 | jobs: 77 | - template: /azure-pipelines/build.yml@self 78 | parameters: 79 | Is1ESPT: true 80 | RealSign: false 81 | # ShouldSkipOptimize: ${{ parameters.ShouldSkipOptimize }} 82 | EnableAPIScan: ${{ parameters.EnableAPIScan }} 83 | windowsPool: VSEngSS-MicroBuild2022-1ES 84 | linuxPool: 85 | name: AzurePipelines-EO 86 | demands: 87 | - ImageOverride -equals 1ESPT-Ubuntu22.04 88 | os: Linux 89 | macOSPool: 90 | name: Azure Pipelines 91 | vmImage: macOS-14 92 | os: macOS 93 | EnableMacOSBuild: ${{ parameters.EnableMacOSBuild }} 94 | RunTests: ${{ parameters.RunTests }} 95 | PublishCodeCoverage: ${{ parameters.PublishCodeCoverage }} 96 | -------------------------------------------------------------------------------- /azure-pipelines/vs-insertion.yml: -------------------------------------------------------------------------------- 1 | trigger: none # We only want to trigger manually or based on resources 2 | pr: none 3 | 4 | resources: 5 | repositories: 6 | - repository: MicroBuildTemplate 7 | type: git 8 | name: 1ESPipelineTemplates/MicroBuildTemplate 9 | ref: refs/tags/release 10 | pipelines: 11 | - pipeline: CI 12 | source: vs-Validation 13 | tags: 14 | - Real signed 15 | trigger: 16 | tags: 17 | - Real signed 18 | - auto-insertion 19 | 20 | variables: 21 | - template: GlobalVariables.yml 22 | 23 | extends: 24 | template: azure-pipelines/MicroBuild.1ES.Official.yml@MicroBuildTemplate 25 | parameters: 26 | sdl: 27 | sourceAnalysisPool: VSEngSS-MicroBuild2022-1ES 28 | 29 | stages: 30 | - stage: insertion 31 | jobs: 32 | - job: insertion 33 | displayName: VS insertion 34 | pool: VSEngSS-MicroBuild2022-1ES 35 | templateContext: 36 | outputParentDirectory: $(Pipeline.Workspace)/CI 37 | steps: 38 | - checkout: none 39 | - powershell: Write-Host "##vso[build.updatebuildnumber]$(resources.pipeline.CI.runName)" 40 | displayName: ⚙️ Set pipeline name 41 | - template: azure-pipelines/release-deployment-prep.yml@self 42 | - download: CI 43 | artifact: VSInsertion-Windows 44 | displayName: 🔻 Download VSInsertion-Windows artifact 45 | - ${{ if eq(variables['ContainsVsix'], 'true') }}: 46 | - task: 1ES.MicroBuildVstsDrop@1 47 | displayName: 🔺 Upload VSTS Drop 48 | inputs: 49 | dropFolder: $(Pipeline.Workspace)/CI/VSInsertion-windows/Vsix 50 | dropName: $(VstsDropNames) 51 | accessToken: $(System.AccessToken) 52 | - task: 1ES.PublishNuget@1 53 | displayName: 📦 Push VS-repo packages to VS feed 54 | inputs: 55 | packagesToPush: '$(Pipeline.Workspace)/CI/VSInsertion-Windows/*.nupkg' 56 | packageParentPath: $(Pipeline.Workspace)/CI/VSInsertion-Windows 57 | allowPackageConflicts: true 58 | publishVstsFeed: VS 59 | - task: MicroBuildInsertVsPayload@5 60 | displayName: 🏭 Insert VS Payload 61 | inputs: 62 | TeamName: $(TeamName) 63 | TeamEmail: $(TeamEmail) 64 | InsertionPayloadName: $(Build.Repository.Name) $(Build.BuildNumber) 65 | InsertionBuildPolicies: Request Perf DDRITs 66 | InsertionReviewers: $(Build.RequestedFor),Andrew Arnott 67 | AutoCompletePR: true 68 | AutoCompleteMergeStrategy: Squash 69 | ShallowClone: true 70 | - powershell: | 71 | $contentType = 'application/json'; 72 | $headers = @{ Authorization = 'Bearer $(System.AccessToken)' }; 73 | $rawRequest = @{ daysValid = 365 * 2; definitionId = $(resources.pipeline.CI.pipelineID); ownerId = 'User:$(Build.RequestedForId)'; protectPipeline = $false; runId = $(resources.pipeline.CI.runId) }; 74 | $request = ConvertTo-Json @($rawRequest); 75 | Write-Host $request 76 | $uri = "$(System.CollectionUri)$(System.TeamProject)/_apis/build/retention/leases?api-version=6.0-preview.1"; 77 | Invoke-RestMethod -uri $uri -method POST -Headers $headers -ContentType $contentType -Body $request; 78 | displayName: 🗻 Retain inserted builds 79 | -------------------------------------------------------------------------------- /azure-pipelines/vs-validation.yml: -------------------------------------------------------------------------------- 1 | # This is a top-level pipeline file, which is designed to be added as an optional PR build policy 2 | # so that a VS insertion and all the validation that entails can be done before ever merging the PR 3 | # in its original repo. 4 | 5 | trigger: none # We only want to trigger manually or based on resources 6 | pr: none 7 | 8 | # parameters: 9 | # - name: ShouldSkipOptimize # Uncomment this and references to it below when setting EnableOptProf to true in build.yml. 10 | # displayName: Skip OptProf optimization 11 | # type: boolean 12 | # default: false 13 | 14 | resources: 15 | repositories: 16 | - repository: MicroBuildTemplate 17 | type: git 18 | name: 1ESPipelineTemplates/MicroBuildTemplate 19 | ref: refs/tags/release 20 | 21 | variables: 22 | - template: GlobalVariables.yml 23 | - name: MicroBuild_NuPkgSigningEnabled 24 | value: false # test-signed nuget packages fail to restore in the VS insertion PR validations. Just don't sign them *at all*. 25 | 26 | extends: 27 | template: azure-pipelines/MicroBuild.1ES.Unofficial.yml@MicroBuildTemplate 28 | parameters: 29 | sdl: 30 | sourceAnalysisPool: VSEngSS-MicroBuild2022-1ES 31 | 32 | stages: 33 | - stage: Build 34 | variables: 35 | - template: /azure-pipelines/BuildStageVariables.yml@self 36 | - name: SkipCodesignVerify 37 | value: true 38 | 39 | jobs: 40 | - template: /azure-pipelines/build.yml@self 41 | parameters: 42 | Is1ESPT: true 43 | RealSign: false 44 | # ShouldSkipOptimize: ${{ parameters.ShouldSkipOptimize }} 45 | windowsPool: VSEngSS-MicroBuild2022-1ES 46 | linuxPool: 47 | name: AzurePipelines-EO 48 | demands: 49 | - ImageOverride -equals 1ESPT-Ubuntu22.04 50 | os: Linux 51 | macOSPool: 52 | name: Azure Pipelines 53 | vmImage: macOS-14 54 | os: macOS 55 | EnableMacOSBuild: false 56 | RunTests: false 57 | SkipCodesignVerify: true 58 | 59 | - template: /azure-pipelines/prepare-insertion-stages.yml@self 60 | parameters: 61 | ArchiveSymbols: false 62 | RealSign: false 63 | 64 | - stage: insertion 65 | displayName: VS insertion 66 | jobs: 67 | - job: insertion 68 | displayName: VS insertion 69 | pool: VSEngSS-MicroBuild2022-1ES 70 | steps: 71 | - checkout: self 72 | clean: true 73 | fetchDepth: 1 74 | - download: current 75 | artifact: Variables-Windows 76 | displayName: 🔻 Download Variables-Windows artifact 77 | - powershell: $(Pipeline.Workspace)/Variables-Windows/_define.ps1 78 | displayName: ⚙️ Set pipeline variables based on artifacts 79 | - download: current 80 | artifact: VSInsertion-Windows 81 | displayName: 🔻 Download VSInsertion-Windows artifact 82 | - ${{ if eq(variables['ContainsVsix'], 'true') }}: 83 | - task: 1ES.MicroBuildVstsDrop@1 84 | displayName: 🔺 Upload VSTS Drop 85 | inputs: 86 | dropFolder: $(Pipeline.Workspace)/VSInsertion-windows/Vsix 87 | dropName: $(VstsDropNames) 88 | accessToken: $(System.AccessToken) 89 | - task: 1ES.PublishNuget@1 90 | displayName: 📦 Push VS-repo packages to VS feed 91 | inputs: 92 | packagesToPush: '$(Pipeline.Workspace)/VSInsertion-Windows/*.nupkg' 93 | packageParentPath: $(Pipeline.Workspace)/VSInsertion-Windows 94 | allowPackageConflicts: true 95 | publishVstsFeed: VS 96 | - task: MicroBuildInsertVsPayload@5 97 | displayName: 🏭 Insert VS Payload 98 | inputs: 99 | TeamName: $(TeamName) 100 | TeamEmail: $(TeamEmail) 101 | InsertionPayloadName: $(Build.Repository.Name) VALIDATION BUILD $(Build.BuildNumber) ($(Build.SourceBranch)) [Skip-SymbolCheck] [Skip-HashCheck] [Skip-SignCheck] 102 | InsertionDescription: | 103 | This PR is for **validation purposes only** for !$(System.PullRequest.PullRequestId). **Do not complete**. 104 | CustomScriptExecutionCommand: src/VSSDK/NuGet/AllowUnstablePackages.ps1 105 | InsertionBuildPolicies: Request Perf DDRITs 106 | InsertionReviewers: $(Build.RequestedFor) 107 | DraftPR: false # set to true and update InsertionBuildPolicy when we can specify all the validations we want to run (https://dev.azure.com/devdiv/DevDiv/_workitems/edit/2224288) 108 | AutoCompletePR: false 109 | ShallowClone: true 110 | - powershell: | 111 | $insertionPRId = azure-pipelines/Get-InsertionPRId.ps1 112 | $Markdown = @" 113 | Validation insertion pull request created: !$insertionPRId 114 | Please check status there before proceeding to merge this PR. 115 | Remember to Abandon and (if allowed) to Delete Source Branch on that insertion PR when validation is complete. 116 | "@ 117 | azure-pipelines/PostPRMessage.ps1 -AccessToken '$(System.AccessToken)' -Markdown $Markdown -Verbose 118 | displayName: ✏️ Comment on pull request 119 | condition: and(succeeded(), eq(variables['Build.Reason'], 'PullRequest')) 120 | -------------------------------------------------------------------------------- /azurepipelines-coverage.yml: -------------------------------------------------------------------------------- 1 | # https://learn.microsoft.com/azure/devops/pipelines/test/codecoverage-for-pullrequests?view=azure-devops 2 | coverage: 3 | status: 4 | comments: on # add comment to PRs reporting diff in coverage of modified files 5 | diff: # diff coverage is code coverage only for the lines changed in a pull request. 6 | target: 70% # set this to a desired %. Default is 70% 7 | -------------------------------------------------------------------------------- /docfx/.gitignore: -------------------------------------------------------------------------------- 1 | _site/ 2 | api/ 3 | -------------------------------------------------------------------------------- /docfx/docfx.json: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": [ 3 | { 4 | "src": [ 5 | { 6 | "src": "../src/Microsoft.VisualStudio.Validation", 7 | "files": [ 8 | "**/*.csproj" 9 | ] 10 | } 11 | ], 12 | "dest": "api" 13 | } 14 | ], 15 | "build": { 16 | "content": [ 17 | { 18 | "files": [ 19 | "**/*.{md,yml}" 20 | ], 21 | "exclude": [ 22 | "_site/**" 23 | ] 24 | } 25 | ], 26 | "resource": [ 27 | { 28 | "files": [ 29 | "images/**" 30 | ] 31 | } 32 | ], 33 | "xref": [ 34 | "https://learn.microsoft.com/en-us/dotnet/.xrefmap.json" 35 | ], 36 | "output": "_site", 37 | "template": [ 38 | "default", 39 | "modern" 40 | ], 41 | "globalMetadata": { 42 | "_appName": "Microsoft.VisualStudio.Validation", 43 | "_appTitle": "Microsoft.VisualStudio.Validation", 44 | "_enableSearch": true, 45 | "pdf": false 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /docfx/docs/features.md: -------------------------------------------------------------------------------- 1 | # Features 2 | 3 | TODO 4 | -------------------------------------------------------------------------------- /docfx/docs/getting-started.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | ## Installation 4 | 5 | Consume this library via its NuGet Package. 6 | Click on the badge to find its latest version and the instructions for consuming it that best apply to your project. 7 | 8 | [![NuGet package](https://img.shields.io/nuget/v/Microsoft.VisualStudio.Validation.svg)](https://nuget.org/packages/Microsoft.VisualStudio.Validation) 9 | 10 | ## Usage 11 | 12 | TODO 13 | -------------------------------------------------------------------------------- /docfx/docs/toc.yml: -------------------------------------------------------------------------------- 1 | items: 2 | - name: Features 3 | href: features.md 4 | - name: Getting Started 5 | href: getting-started.md 6 | -------------------------------------------------------------------------------- /docfx/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | _layout: landing 3 | --- 4 | 5 | # Overview 6 | 7 | This is your docfx landing page. 8 | 9 | Click "Docs" across the top to get started. 10 | -------------------------------------------------------------------------------- /docfx/toc.yml: -------------------------------------------------------------------------------- 1 | items: 2 | - name: Docs 3 | href: docs/ 4 | - name: API 5 | href: api/ 6 | -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "9.0.300", 4 | "rollForward": "patch", 5 | "allowPrerelease": false 6 | }, 7 | "msbuild-sdks": { 8 | "Microsoft.Build.NoTargets": "3.7.56" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /init.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | SETLOCAL 3 | set PS1UnderCmd=1 4 | 5 | :: Get the datetime in a format that can go in a filename. 6 | set _my_datetime=%date%_%time% 7 | set _my_datetime=%_my_datetime: =_% 8 | set _my_datetime=%_my_datetime::=% 9 | set _my_datetime=%_my_datetime:/=_% 10 | set _my_datetime=%_my_datetime:.=_% 11 | set CmdEnvScriptPath=%temp%\envvarscript_%_my_datetime%.cmd 12 | 13 | powershell.exe -NoProfile -NoLogo -ExecutionPolicy bypass -Command "try { & '%~dpn0.ps1' %*; exit $LASTEXITCODE } catch { write-host $_; exit 1 }" 14 | 15 | :: Set environment variables in the parent cmd.exe process. 16 | IF EXIST "%CmdEnvScriptPath%" ( 17 | ENDLOCAL 18 | CALL "%CmdEnvScriptPath%" 19 | DEL "%CmdEnvScriptPath%" 20 | ) 21 | -------------------------------------------------------------------------------- /loc/lci/Microsoft.VisualStudio.Validation.dll.lci: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /nuget.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /settings.VisualStudio.json: -------------------------------------------------------------------------------- 1 | { 2 | "textEditor.codeCleanup.profile": "profile1" 3 | } 4 | -------------------------------------------------------------------------------- /src/.editorconfig: -------------------------------------------------------------------------------- 1 | [*.cs] 2 | 3 | # SA1611: Element parameters should be documented 4 | dotnet_diagnostic.SA1611.severity = suggestion 5 | -------------------------------------------------------------------------------- /src/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.Runtime.InteropServices; 5 | 6 | [assembly: DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)] 7 | -------------------------------------------------------------------------------- /src/AssemblyInfo.vb: -------------------------------------------------------------------------------- 1 | ' Copyright (c) Microsoft Corporation. All rights reserved. 2 | ' Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | Imports System.Runtime.InteropServices 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | README.md 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | https://github.com/Microsoft/vs-validation 15 | PackageIcon.png 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/Directory.Build.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Validation/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.Resources; 5 | using System.Security; 6 | 7 | [assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.MainAssembly)] 8 | [assembly: AllowPartiallyTrustedCallers] 9 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Validation/Assumes.InternalErrorException.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.Diagnostics; 5 | using System.Diagnostics.CodeAnalysis; 6 | using System.Runtime.Serialization; 7 | 8 | namespace Microsoft; 9 | 10 | /// 11 | /// Contains the inner exception thrown by Assumes. 12 | /// 13 | public partial class Assumes 14 | { 15 | /// 16 | /// The exception that is thrown when an internal assumption failed. 17 | /// 18 | [Serializable] 19 | private sealed class InternalErrorException : Exception 20 | { 21 | /// 22 | /// Initializes a new instance of the class. 23 | /// 24 | [DebuggerStepThrough] 25 | public InternalErrorException(string? message = null) 26 | : base(message ?? Strings.InternalExceptionMessage) 27 | { 28 | } 29 | 30 | /// 31 | /// Initializes a new instance of the class. 32 | /// 33 | [DebuggerStepThrough] 34 | public InternalErrorException(string? message, Exception? innerException) 35 | : base(message ?? Strings.InternalExceptionMessage, innerException) 36 | { 37 | } 38 | 39 | /// 40 | /// Initializes a new instance of the class. 41 | /// 42 | [DebuggerStepThrough] 43 | [Obsolete] 44 | private InternalErrorException(SerializationInfo info, StreamingContext context) 45 | : base(info, context) 46 | { 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Validation/EventHandlerExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.Diagnostics.CodeAnalysis; 5 | 6 | namespace Microsoft; 7 | 8 | /// 9 | /// Extension methods to make it easier to safely invoke events. 10 | /// 11 | public static class EventHandlerExtensions 12 | { 13 | /// 14 | /// Invokes any event handlers that are hooked to the specified event. 15 | /// 16 | /// The event. Null is allowed. 17 | /// The value to pass as the sender of the event. 18 | /// Event arguments to include. 19 | public static void Raise(this Delegate? handler, object sender, EventArgs e) 20 | { 21 | Requires.NotNull(sender, nameof(sender)); 22 | Requires.NotNull(e, nameof(e)); 23 | 24 | handler?.DynamicInvoke(sender, e); 25 | } 26 | 27 | /// 28 | /// Invokes any event handlers that are hooked to the specified event. 29 | /// 30 | /// The event. Null is allowed. 31 | /// The value to pass as the sender of the event. 32 | /// Event arguments to include. 33 | public static void Raise(this EventHandler? handler, object sender, EventArgs e) 34 | { 35 | Requires.NotNull(sender, nameof(sender)); 36 | Requires.NotNull(e, nameof(e)); 37 | 38 | handler?.Invoke(sender, e); 39 | } 40 | 41 | /// 42 | /// Invokes any event handlers that are hooked to the specified event. 43 | /// 44 | /// The type of EventArgs. 45 | /// The event. Null is allowed. 46 | /// The value to pass as the sender of the event. 47 | /// Event arguments to include. 48 | public static void Raise(this EventHandler? handler, object sender, T e) 49 | { 50 | Requires.NotNull(sender, nameof(sender)); 51 | Requires.NotNullAllowStructs(e, nameof(e)); 52 | 53 | handler?.Invoke(sender, e); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Validation/ExceptionExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | namespace Microsoft; 5 | 6 | /// 7 | /// Extension methods for exceptions. 8 | /// 9 | public static class ExceptionExtensions 10 | { 11 | /// 12 | /// Adds data to the Data member of before returning the modified exception. 13 | /// 14 | /// The type of exception being modified. 15 | /// The exception to add data to. 16 | /// The key to use for the added data. 17 | /// The values to add with the given . 18 | /// A reference to the same . 19 | /// 20 | /// This method should be used to add context (beyond the message and callstack we normally get) to the exception 21 | /// that would be useful when debugging Watson crashes. 22 | /// Do not use this method when you expect the exception to be handled. 23 | /// 24 | public static T AddData(this T exception, string key, params object[]? values) 25 | where T : Exception 26 | { 27 | Requires.NotNull(exception, nameof(exception)); 28 | 29 | if (values?.Length > 0) 30 | { 31 | exception.Data.Add(key, values); 32 | } 33 | 34 | return exception; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Validation/IDisposableObservable.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | namespace Microsoft; 5 | 6 | /// 7 | /// A disposable object that also provides a safe way to query its disposed status. 8 | /// 9 | public interface IDisposableObservable : IDisposable 10 | { 11 | /// 12 | /// Gets a value indicating whether this instance has been disposed. 13 | /// 14 | /// if this instance has been disposed. 15 | bool IsDisposed { get; } 16 | } 17 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Validation/Microsoft.VisualStudio.Validation.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | netstandard2.0;net8.0 4 | Microsoft 5 | 6 | Common input validation and state verification utility methods. 7 | InputValidation IntegrityCheck 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Validation/Polyfill.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | #if !NET6_0_OR_GREATER 5 | 6 | #pragma warning disable SA1402 // File may only contain a single type 7 | #pragma warning disable SA1649 // File name should match first type name 8 | #pragma warning disable SA1600 // Elements should be documented 9 | 10 | namespace System.Runtime.CompilerServices 11 | { 12 | [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] 13 | internal sealed class CallerArgumentExpressionAttribute : Attribute 14 | { 15 | internal CallerArgumentExpressionAttribute(string parameterName) 16 | { 17 | } 18 | } 19 | 20 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false, Inherited = false)] 21 | internal sealed class InterpolatedStringHandlerAttribute : Attribute 22 | { 23 | public InterpolatedStringHandlerAttribute() 24 | { 25 | } 26 | } 27 | 28 | [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] 29 | internal sealed class InterpolatedStringHandlerArgumentAttribute : Attribute 30 | { 31 | public InterpolatedStringHandlerArgumentAttribute(string argument) => this.Arguments = new string[] { argument }; 32 | 33 | public InterpolatedStringHandlerArgumentAttribute(params string[] arguments) => this.Arguments = arguments; 34 | 35 | public string[] Arguments { get; } 36 | } 37 | } 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Validation/PrivateErrorHelpers.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.Globalization; 5 | using System.Reflection; 6 | 7 | namespace Microsoft; 8 | 9 | /// 10 | /// Common utility methods used by the various error detection and reporting classes. 11 | /// 12 | internal static class PrivateErrorHelpers 13 | { 14 | /// 15 | /// Trims away a given surrounding type, returning just the generic type argument, 16 | /// if the given type is in fact a generic type with just one type argument and 17 | /// the generic type matches a given wrapper type. Otherwise, it returns the original type. 18 | /// 19 | /// The type to trim, or return unmodified. 20 | /// The SomeType<> generic type definition to trim away from if it is present. 21 | /// , if it is not a generic type instance of ; otherwise the type argument. 22 | internal static Type TrimGenericWrapper(Type type, Type? wrapper) 23 | { 24 | Type[] typeArgs; 25 | if (type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == wrapper && (typeArgs = type.GenericTypeArguments).Length == 1) 26 | { 27 | return typeArgs[0]; 28 | } 29 | else 30 | { 31 | return type; 32 | } 33 | } 34 | 35 | /// 36 | /// Helper method that formats string arguments. 37 | /// 38 | /// The formatted string. 39 | internal static string Format(string format, params object?[] arguments) 40 | { 41 | return string.Format(CultureInfo.CurrentCulture, format, arguments); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Validation/Report.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | // Enable calling the Debug class even in Release builds, 5 | // and be able to call other methods in this same class. 6 | #define DEBUG 7 | 8 | using System.ComponentModel; 9 | using System.Diagnostics; 10 | using System.Runtime.CompilerServices; 11 | 12 | namespace Microsoft; 13 | 14 | /// 15 | /// Common runtime checks that trace messages and invoke an assertion failure, 16 | /// but does *not* throw exceptions. 17 | /// 18 | public static class Report 19 | { 20 | /// 21 | /// Verifies that a value is not null, and reports an error about a missing MEF component otherwise. 22 | /// 23 | /// The interface of the imported part. 24 | [Conditional("DEBUG")] 25 | public static void IfNotPresent(T part) 26 | { 27 | if (part is null) 28 | { 29 | Type coreType = PrivateErrorHelpers.TrimGenericWrapper(typeof(T), typeof(Lazy<>)); 30 | Fail(Strings.FormatServiceMissing(coreType.FullName)); 31 | } 32 | } 33 | 34 | /// 35 | /// Reports an error if a condition evaluates to true. 36 | /// 37 | [Conditional("DEBUG")] 38 | public static void If(bool condition, [Localizable(false)] string? message = null) 39 | { 40 | if (condition) 41 | { 42 | Fail(message); 43 | } 44 | } 45 | 46 | /// 47 | /// Reports an error if a condition does not evaluate to true. 48 | /// 49 | [Conditional("DEBUG")] 50 | public static void IfNot(bool condition, [Localizable(false)] string? message = null) 51 | { 52 | if (!condition) 53 | { 54 | Fail(message); 55 | } 56 | } 57 | 58 | /// 59 | /// Reports an error if a condition does not evaluate to true. 60 | /// 61 | [Conditional("DEBUG")] 62 | public static void IfNot(bool condition, [Localizable(false)] string message, object? arg1) 63 | { 64 | if (!condition) 65 | { 66 | Fail(PrivateErrorHelpers.Format(message, arg1)); 67 | } 68 | } 69 | 70 | /// 71 | /// Reports an error if a condition does not evaluate to true. 72 | /// 73 | [Conditional("DEBUG")] 74 | public static void IfNot(bool condition, [Localizable(false)] string message, object? arg1, object? arg2) 75 | { 76 | if (!condition) 77 | { 78 | Fail(PrivateErrorHelpers.Format(message, arg1, arg2)); 79 | } 80 | } 81 | 82 | /// 83 | /// Reports an error if a condition does not evaluate to true. 84 | /// 85 | [Conditional("DEBUG")] 86 | public static void IfNot(bool condition, [Localizable(false)] string message, params object?[] args) 87 | { 88 | if (!condition) 89 | { 90 | Fail(PrivateErrorHelpers.Format(message, args)); 91 | } 92 | } 93 | 94 | /// 95 | /// Reports an error if a condition does not evaluate to true. 96 | /// 97 | [Conditional("DEBUG")] 98 | public static void IfNot(bool condition, [InterpolatedStringHandlerArgument("condition")] ref ValidationInterpolatedStringHandler message) 99 | { 100 | if (!condition) 101 | { 102 | Fail(message.ToStringAndClear()); 103 | } 104 | } 105 | 106 | /// 107 | /// Reports a certain failure. 108 | /// 109 | [Conditional("DEBUG")] 110 | public static void Fail([Localizable(false)] string? message = null) 111 | { 112 | if (message is null) 113 | { 114 | message = "A recoverable error has been detected."; 115 | } 116 | 117 | Debug.WriteLine(message); 118 | Debug.Assert(false, message); 119 | } 120 | 121 | /// 122 | /// Reports a certain failure. 123 | /// 124 | [Conditional("DEBUG")] 125 | public static void Fail([Localizable(false)] string message, params object?[] args) 126 | { 127 | Fail(PrivateErrorHelpers.Format(message, args)); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/Microsoft.VisualStudio.Validation/ValidatedNotNullAttribute.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.Diagnostics.CodeAnalysis; 5 | 6 | namespace Microsoft; 7 | 8 | /// 9 | /// Indicates to Code Analysis that a method validates a particular parameter. 10 | /// 11 | [ExcludeFromCodeCoverage] 12 | [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] 13 | public sealed class ValidatedNotNullAttribute : Attribute 14 | { 15 | /// 16 | /// Initializes a new instance of the class. 17 | /// 18 | public ValidatedNotNullAttribute() 19 | { 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/OptProf.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | IBC 5 | Common7\IDE\PrivateAssemblies\$(TargetFileName) 6 | /ExeConfig:"%VisualStudio.InstallationUnderTest.Path%\Common7\IDE\vsn.exe" 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/PackageIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vs-validation/28c82310309d02f6f9bdbceb8b393296d6d8e2ee/src/PackageIcon.png -------------------------------------------------------------------------------- /src/VSInsertionMetadata/Microsoft.VisualStudio.Validation.VSInsertionMetadata.proj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netstandard2.0 4 | true 5 | $(RepoRootPath)bin\Packages\$(Configuration)\VSRepo\ 6 | false 7 | false 8 | Contains metadata for insertion into VS. 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/VSInsertionMetadata/ProfilingInputs.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/VSInsertionMetadata/VSInsertionMetadata.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | $(TargetsForTfmSpecificContentInPackage); 27 | SubstituteProfilingInputsMacro; 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 43 | 44 | 47 | 49 | 50 | 51 | 52 | 53 | 54 | 57 | 64 | 65 | $(PackageVersion).$(Build_BuildId) 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /stylecop.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", 3 | "settings": { 4 | "documentationRules": { 5 | "companyName": "Microsoft", 6 | "copyrightText": "Copyright (c) {companyName}. All rights reserved.\nLicensed under the {licenseName} license. See {licenseFile} file in the project root for full license information.", 7 | "variables": { 8 | "licenseName": "MIT", 9 | "licenseFile": "LICENSE" 10 | }, 11 | "fileNamingConvention": "stylecop", 12 | "xmlHeader": false 13 | }, 14 | "orderingRules": { 15 | "usingDirectivesPlacement": "outsideNamespace" 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/.editorconfig: -------------------------------------------------------------------------------- 1 | [*.cs] 2 | 3 | # SA1600: Elements should be documented 4 | dotnet_diagnostic.SA1600.severity = silent 5 | 6 | # SA1601: Partial elements should be documented 7 | dotnet_diagnostic.SA1601.severity = silent 8 | 9 | # SA1602: Enumeration items should be documented 10 | dotnet_diagnostic.SA1602.severity = silent 11 | 12 | # SA1615: Element return value should be documented 13 | dotnet_diagnostic.SA1615.severity = silent 14 | 15 | # VSTHRD103: Call async methods when in an async method 16 | dotnet_diagnostic.VSTHRD103.severity = silent 17 | 18 | # VSTHRD111: Use .ConfigureAwait(bool) 19 | dotnet_diagnostic.VSTHRD111.severity = none 20 | 21 | # VSTHRD200: Use Async suffix for async methods 22 | dotnet_diagnostic.VSTHRD200.severity = silent 23 | 24 | # CA1014: Mark assemblies with CLSCompliant 25 | dotnet_diagnostic.CA1014.severity = none 26 | 27 | # CA1050: Declare types in namespaces 28 | dotnet_diagnostic.CA1050.severity = none 29 | 30 | # CA1303: Do not pass literals as localized parameters 31 | dotnet_diagnostic.CA1303.severity = none 32 | 33 | # CS1591: Missing XML comment for publicly visible type or member 34 | dotnet_diagnostic.CS1591.severity = silent 35 | 36 | # CA1707: Identifiers should not contain underscores 37 | dotnet_diagnostic.CA1707.severity = silent 38 | 39 | # CA1062: Validate arguments of public methods 40 | dotnet_diagnostic.CA1062.severity = suggestion 41 | 42 | # CA1063: Implement IDisposable Correctly 43 | dotnet_diagnostic.CA1063.severity = silent 44 | 45 | # CA1816: Dispose methods should call SuppressFinalize 46 | dotnet_diagnostic.CA1816.severity = silent 47 | 48 | # CA2007: Consider calling ConfigureAwait on the awaited task 49 | dotnet_diagnostic.CA2007.severity = none 50 | 51 | # SA1401: Fields should be private 52 | dotnet_diagnostic.SA1401.severity = silent 53 | 54 | # SA1133: Do not combine attributes 55 | dotnet_diagnostic.SA1133.severity = silent 56 | -------------------------------------------------------------------------------- /test/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | false 7 | true 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /test/Directory.Build.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /test/Microsoft.VisualStudio.Validation.Tests/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using Xunit; 5 | 6 | // Suppress xunit parallelizing tests since we manipulate statics (TraceListeners) 7 | [assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly)] 8 | -------------------------------------------------------------------------------- /test/Microsoft.VisualStudio.Validation.Tests/AssertDialogSuppression.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.Diagnostics; 5 | 6 | /// 7 | /// Suppresses the managed Assertion Failure dialog box, and continues 8 | /// to log assertion failures to the debug output. 9 | /// 10 | /// 11 | /// Inspired by Matt Ellis' post at: 12 | /// . 13 | /// 14 | internal class AssertDialogSuppression : IDisposable 15 | { 16 | /// 17 | /// Stores the original popup-ability of the assertion dialog. 18 | /// 19 | private bool? originalAssertUiSetting = false; 20 | 21 | /// 22 | /// Initializes a new instance of the class, 23 | /// and immediately begins suppressing assertion dialog popups. 24 | /// 25 | public AssertDialogSuppression() 26 | { 27 | #if NETCOREAPP 28 | Trace.Listeners.Clear(); 29 | #else 30 | // We disable the assertion dialog so it doesn't block tests, as we expect some tests to test failure cases. 31 | if (Trace.Listeners["Default"] is DefaultTraceListener assertDialogListener) 32 | { 33 | this.originalAssertUiSetting = assertDialogListener.AssertUiEnabled; 34 | assertDialogListener.AssertUiEnabled = false; 35 | } 36 | 37 | // Xunit.v3 v2 also adds a TraceListener that throws on failure, so remove that too. 38 | // See also https://github.com/xunit/xunit/issues/3317. 39 | // My mechanism for removing the listener is designed to work before and after that issue is resolved. 40 | if (Trace.Listeners.OfType().FirstOrDefault() is { } listener) 41 | { 42 | Trace.Listeners.Remove(listener); 43 | } 44 | #endif 45 | } 46 | 47 | /// 48 | /// Stops suppressing the assertion dialog and restores its popup-ability to whatever it was 49 | /// (either on or off) when this object was instantiated. 50 | /// 51 | public void Dispose() 52 | { 53 | if (this.originalAssertUiSetting.HasValue) 54 | { 55 | if (Trace.Listeners["Default"] is DefaultTraceListener assertDialogListener) 56 | { 57 | assertDialogListener.AssertUiEnabled = this.originalAssertUiSetting.Value; 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /test/Microsoft.VisualStudio.Validation.Tests/DisposableValue{T}.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | internal class DisposableValue : IDisposable 5 | { 6 | private Action? disposeAction; 7 | 8 | internal DisposableValue(T value, Action disposeAction) 9 | { 10 | this.Value = value; 11 | this.disposeAction = disposeAction; 12 | } 13 | 14 | internal T Value { get; } 15 | 16 | public void Dispose() 17 | { 18 | this.disposeAction?.Invoke(); 19 | this.disposeAction = null; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /test/Microsoft.VisualStudio.Validation.Tests/EventHandlerExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using Microsoft; 5 | using Xunit; 6 | 7 | public class EventHandlerExtensionsTests 8 | { 9 | private static readonly EventArgs Args = new EventArgs(); 10 | 11 | [Fact] 12 | public void Raise_EventHandlerOfT() 13 | { 14 | bool invoked = false; 15 | EventHandler? handler = (s, e) => 16 | { 17 | Assert.Same(this, s); 18 | Assert.Same(Args, e); 19 | invoked = true; 20 | }; 21 | handler.Raise(this, Args); 22 | Assert.True(invoked); 23 | 24 | Assert.Throws(() => handler.Raise(null!, EventArgs.Empty)); 25 | Assert.Throws(() => handler.Raise(this, null!)); 26 | 27 | handler = null; 28 | handler.Raise(this, Args); 29 | Assert.Throws(() => handler.Raise(null!, EventArgs.Empty)); 30 | Assert.Throws(() => handler.Raise(this, null!)); 31 | } 32 | 33 | [Fact] 34 | public void Raise_EventHandler() 35 | { 36 | bool invoked = false; 37 | EventHandler? handler = (s, e) => 38 | { 39 | Assert.Same(this, s); 40 | Assert.Same(Args, e); 41 | invoked = true; 42 | }; 43 | handler.Raise(this, Args); 44 | Assert.True(invoked); 45 | 46 | Assert.Throws(() => handler.Raise(null!, EventArgs.Empty)); 47 | Assert.Throws(() => handler.Raise(this, null!)); 48 | 49 | handler = null; 50 | handler.Raise(this, Args); 51 | Assert.Throws(() => handler.Raise(null!, EventArgs.Empty)); 52 | Assert.Throws(() => handler.Raise(this, null!)); 53 | } 54 | 55 | [Fact] 56 | public void Raise_Delegate() 57 | { 58 | bool invoked = false; 59 | Delegate? handler = new EventHandler((s, e) => 60 | { 61 | Assert.Same(this, s); 62 | Assert.Same(Args, e); 63 | invoked = true; 64 | }); 65 | handler.Raise(this, Args); 66 | Assert.True(invoked); 67 | 68 | Assert.Throws(() => handler.Raise(null!, EventArgs.Empty)); 69 | Assert.Throws(() => handler.Raise(this, null!)); 70 | 71 | handler = null; 72 | handler.Raise(this, Args); 73 | Assert.Throws(() => handler.Raise(null!, EventArgs.Empty)); 74 | Assert.Throws(() => handler.Raise(this, null!)); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /test/Microsoft.VisualStudio.Validation.Tests/ExceptionExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using Microsoft; 5 | using Xunit; 6 | 7 | public class ExceptionExtensionsTests 8 | { 9 | [Fact] 10 | public void AddData_NullException() 11 | { 12 | Assert.Throws(() => ExceptionExtensions.AddData(null!, "a", 2)); 13 | } 14 | 15 | [Fact] 16 | public void AddData_NullKey() 17 | { 18 | ArgumentNullException ex = Assert.Throws(() => new InvalidOperationException().AddData(null!, "a")); 19 | Assert.Equal("key", ex.ParamName); 20 | } 21 | 22 | [Fact] 23 | public void AddData_NullValuesArray() 24 | { 25 | InvalidOperationException ex = new InvalidOperationException().AddData("hi", null); 26 | Assert.False(ex.Data.Contains("hi")); 27 | } 28 | 29 | [Fact] 30 | public void AddData_EmptyValuesArray() 31 | { 32 | InvalidOperationException ex = new InvalidOperationException().AddData("hi"); 33 | Assert.False(ex.Data.Contains("hi")); 34 | } 35 | 36 | [Fact] 37 | public void AddData_One() 38 | { 39 | InvalidOperationException ex = new InvalidOperationException().AddData("hi", "1"); 40 | Assert.Equal(new object[] { "1" }, ex.Data["hi"]); 41 | } 42 | 43 | [Fact] 44 | public void AddData_TwoWithSameKey() 45 | { 46 | InvalidOperationException ex = new InvalidOperationException().AddData("hi", "1", "2"); 47 | Assert.Equal(new object[] { "1", "2" }, ex.Data["hi"]); 48 | } 49 | 50 | [Fact] 51 | public void AddData_TwoKeys() 52 | { 53 | InvalidOperationException ex = new InvalidOperationException() 54 | .AddData("hi", "1", "2") 55 | .AddData("bye", "3"); 56 | Assert.Equal(new object[] { "1", "2" }, ex.Data["hi"]); 57 | Assert.Equal(new object[] { "3" }, ex.Data["bye"]); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /test/Microsoft.VisualStudio.Validation.Tests/Microsoft.VisualStudio.Validation.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net8.0 4 | $(TargetFrameworks);net472 5 | Exe 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /test/Microsoft.VisualStudio.Validation.Tests/OverrideCulture.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.Globalization; 5 | 6 | /// 7 | /// Sets a specific culture for a duration, restoring the prior culture upon disposal. 8 | /// 9 | internal sealed class OverrideCulture : IDisposable 10 | { 11 | private readonly CultureInfo priorCulture; 12 | private readonly CultureInfo priorUICulture; 13 | 14 | public OverrideCulture(CultureInfo culture) 15 | { 16 | this.priorCulture = CultureInfo.CurrentCulture; 17 | this.priorUICulture = CultureInfo.CurrentUICulture; 18 | 19 | CultureInfo.CurrentCulture = culture; 20 | CultureInfo.CurrentUICulture = culture; 21 | } 22 | 23 | public void Dispose() 24 | { 25 | CultureInfo.CurrentCulture = this.priorCulture; 26 | CultureInfo.CurrentUICulture = this.priorUICulture; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /test/Microsoft.VisualStudio.Validation.Tests/ReportTests.Release.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | // Ensure the tests defined in this file always emulate a client compiled for Release 5 | #undef DEBUG 6 | 7 | using System.Diagnostics; 8 | using System.Diagnostics.CodeAnalysis; 9 | using Microsoft; 10 | using Moq; 11 | using Xunit; 12 | 13 | /// 14 | /// Verify that the message does NOT propagate to the trace listeners when 15 | /// the test project compiles without DEBUG. 16 | /// 17 | [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1649:File name must match first type name", Justification = "By design")] 18 | public class ReportReleaseTests : IDisposable 19 | { 20 | private const string FailureMessage = "failure"; 21 | 22 | private AssertDialogSuppression suppressAssertUi = new AssertDialogSuppression(); 23 | 24 | public void Dispose() 25 | { 26 | this.suppressAssertUi.Dispose(); 27 | } 28 | 29 | [Fact] 30 | public void If() 31 | { 32 | using (DisposableValue> listener = Listen()) 33 | { 34 | Report.If(false, FailureMessage); 35 | Report.If(true, FailureMessage); 36 | } 37 | } 38 | 39 | [Fact] 40 | public void IfNot() 41 | { 42 | using (DisposableValue> listener = Listen()) 43 | { 44 | Report.IfNot(true, FailureMessage); 45 | Report.IfNot(false, FailureMessage); 46 | } 47 | } 48 | 49 | [Fact] 50 | public void IfNot_Format1Arg() 51 | { 52 | using (DisposableValue> listener = Listen()) 53 | { 54 | Report.IfNot(true, "a{0}c", "b"); 55 | Report.IfNot(false, "a{0}c", "b"); 56 | } 57 | } 58 | 59 | [Fact] 60 | public void IfNot_Format2Arg() 61 | { 62 | using (DisposableValue> listener = Listen()) 63 | { 64 | Report.IfNot(true, "a{0}{1}d", "b", "c"); 65 | Report.IfNot(false, "a{0}{1}d", "b", "c"); 66 | } 67 | } 68 | 69 | [Fact] 70 | public void IfNot_FormatNArg() 71 | { 72 | using (DisposableValue> listener = Listen()) 73 | { 74 | Report.IfNot(true, "a{0}{1}{2}e", "b", "c", "d"); 75 | Report.IfNot(false, "a{0}{1}{2}e", "b", "c", "d"); 76 | } 77 | } 78 | 79 | [Fact] 80 | public void IfNot_InterpolatedString() 81 | { 82 | int formatCount = 0; 83 | string FormattingMethod() 84 | { 85 | formatCount++; 86 | return "b"; 87 | } 88 | 89 | using (DisposableValue> listener = Listen()) 90 | { 91 | Report.IfNot(true, $"a{FormattingMethod()}c"); 92 | Assert.Equal(0, formatCount); 93 | Report.IfNot(false, $"a{FormattingMethod()}c"); 94 | Assert.Equal(0, formatCount); 95 | } 96 | } 97 | 98 | [Fact] 99 | public void IfNotPresent() 100 | { 101 | using (DisposableValue> listener = Listen()) 102 | { 103 | string? possiblyPresent = "not missing"; 104 | var missingTypeName = possiblyPresent.GetType().FullName; 105 | Report.IfNotPresent(possiblyPresent); 106 | possiblyPresent = null; 107 | Report.IfNotPresent(possiblyPresent); 108 | } 109 | } 110 | 111 | [Fact] 112 | public void Fail() 113 | { 114 | using (DisposableValue> listener = Listen()) 115 | { 116 | Report.Fail(FailureMessage); 117 | } 118 | } 119 | 120 | [Fact] 121 | public void Fail_DefaultMessage() 122 | { 123 | using (DisposableValue> listener = Listen()) 124 | { 125 | Report.Fail(); 126 | } 127 | } 128 | 129 | private static DisposableValue> Listen() 130 | { 131 | var mockListener = new Mock(MockBehavior.Strict); 132 | Trace.Listeners.Add(mockListener.Object); 133 | return new DisposableValue>( 134 | mockListener, 135 | () => 136 | { 137 | Trace.Listeners.Remove(mockListener.Object); 138 | mockListener.Verify(); 139 | }); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /test/Microsoft.VisualStudio.Validation.Tests/VerifyTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.Runtime.InteropServices; 5 | using Microsoft; 6 | using Microsoft.VisualStudio.Validation.Tests; 7 | using Xunit; 8 | 9 | public class VerifyTests 10 | { 11 | [Fact] 12 | public void Operation() 13 | { 14 | Verify.Operation(true, "Should not throw"); 15 | Verify.Operation(true, "Should not throw", "arg1"); 16 | Verify.Operation(true, "Should not throw", "arg1", "arg2"); 17 | Verify.Operation(true, "Should not throw", "arg1", "arg2", "arg3"); 18 | 19 | Assert.Throws(() => Verify.Operation(false, "throw")); 20 | Assert.Throws(() => Verify.Operation(false, "throw", "arg1")); 21 | Assert.Throws(() => Verify.Operation(false, "throw", "arg1", "arg2")); 22 | Assert.Throws(() => Verify.Operation(false, "throw", "arg1", "arg2", "arg3")); 23 | } 24 | 25 | [Fact] 26 | public void Operation_InterpolatedString() 27 | { 28 | int formatCount = 0; 29 | string FormattingMethod() 30 | { 31 | formatCount++; 32 | return "generated string"; 33 | } 34 | 35 | Verify.Operation(true, $"Some {FormattingMethod()} method."); 36 | Assert.Equal(0, formatCount); 37 | 38 | InvalidOperationException ex = Assert.Throws(() => Verify.Operation(false, $"Some {FormattingMethod()} method.")); 39 | Assert.Equal(1, formatCount); 40 | Assert.StartsWith("Some generated string method.", ex.Message); 41 | } 42 | 43 | [Fact] 44 | public void Operation_ResourceManager() 45 | { 46 | AssertThrows(TestStrings.GetResourceString(TestStrings.SomeError), c => Verify.Operation(c, TestStrings.ResourceManager, TestStrings.SomeError)); 47 | AssertThrows(TestStrings.FormatSomeError1Arg("A"), c => Verify.Operation(c, TestStrings.ResourceManager, TestStrings.SomeError1Arg, "A")); 48 | AssertThrows(TestStrings.FormatSomeError2Args("A", "B"), c => Verify.Operation(c, TestStrings.ResourceManager, TestStrings.SomeError2Args, "A", "B")); 49 | AssertThrows(TestStrings.FormatSomeError3Args("A", "B", "C"), c => Verify.Operation(c, TestStrings.ResourceManager, TestStrings.SomeError3Args, "A", "B", "C")); 50 | 51 | static void AssertThrows(string? expectedMessage, Action action) 52 | { 53 | action(true); 54 | 55 | InvalidOperationException actual = Assert.Throws(() => action(false)); 56 | Assert.Equal(expectedMessage, actual.Message); 57 | } 58 | } 59 | 60 | [Fact] 61 | public void OperationWithHelp() 62 | { 63 | Verify.OperationWithHelp(true, "message", "helpLink"); 64 | InvalidOperationException ex = Assert.Throws(() => Verify.OperationWithHelp(false, "message", "helpLink")); 65 | Assert.Equal("message", ex.Message); 66 | Assert.Equal("helpLink", ex.HelpLink); 67 | } 68 | 69 | [Fact] 70 | public void NotDisposed() 71 | { 72 | Verify.NotDisposed(true, "message"); 73 | ObjectDisposedException actualException = Assert.Throws(() => Verify.NotDisposed(false, "message")); 74 | Assert.Equal(string.Empty, actualException.ObjectName); 75 | Assert.Equal("message", actualException.Message); 76 | 77 | Assert.Throws(() => Verify.NotDisposed(false, null)); 78 | Assert.Throws(() => Verify.NotDisposed(false, (object?)null)); 79 | 80 | actualException = Assert.Throws(() => Verify.NotDisposed(false, "hi", "message")); 81 | string expectedObjectName = typeof(string).FullName!; 82 | Assert.Equal(expectedObjectName, actualException.ObjectName); 83 | Assert.Equal(new ObjectDisposedException(expectedObjectName, "message").Message, actualException.Message); 84 | 85 | actualException = Assert.Throws(() => Verify.NotDisposed(false, new object())); 86 | Assert.Equal(typeof(object).FullName, actualException.ObjectName); 87 | } 88 | 89 | [Fact] 90 | public void NotDisposed_Observable() 91 | { 92 | var observable = new Disposable(); 93 | Verify.NotDisposed(observable); 94 | observable.Dispose(); 95 | Assert.Throws(() => Verify.NotDisposed(observable)); 96 | Assert.Throws(() => Verify.NotDisposed(observable, "message")); 97 | } 98 | 99 | [Fact] 100 | public void FailOperation_ParamsFormattingArgs() 101 | { 102 | InvalidOperationException ex = Assert.Throws(() => Verify.FailOperation("a{0}c", "b")); 103 | Assert.Equal("abc", ex.Message); 104 | } 105 | 106 | [Fact] 107 | public void FailOperation_String() 108 | { 109 | InvalidOperationException ex = Assert.Throws(() => Verify.FailOperation("a{0}c")); 110 | Assert.Equal("a{0}c", ex.Message); 111 | } 112 | 113 | [Fact] 114 | public void HResult() 115 | { 116 | const int E_INVALIDARG = unchecked((int)0x80070057); 117 | const int E_FAIL = unchecked((int)0x80004005); 118 | Verify.HResult(0); 119 | Assert.Throws(() => Verify.HResult(E_INVALIDARG)); 120 | Assert.Throws(() => Verify.HResult(E_FAIL)); 121 | Assert.Throws(() => Verify.HResult(E_INVALIDARG, ignorePreviousComCalls: true)); 122 | Assert.Throws(() => Verify.HResult(E_FAIL, ignorePreviousComCalls: true)); 123 | } 124 | 125 | private class Disposable : IDisposableObservable 126 | { 127 | public bool IsDisposed { get; private set; } 128 | 129 | public void Dispose() 130 | { 131 | this.IsDisposed = true; 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /test/Microsoft.VisualStudio.Validation.Tests/xunit.runner.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", 3 | "shadowCopy": false 4 | } 5 | -------------------------------------------------------------------------------- /tools/Check-DotNetRuntime.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Checks whether a given .NET Core runtime is installed. 4 | #> 5 | [CmdletBinding()] 6 | Param ( 7 | [Parameter()] 8 | [ValidateSet('Microsoft.AspNetCore.App','Microsoft.NETCore.App')] 9 | [string]$Runtime='Microsoft.NETCore.App', 10 | [Parameter(Mandatory=$true)] 11 | [Version]$Version 12 | ) 13 | 14 | $dotnet = Get-Command dotnet -ErrorAction SilentlyContinue 15 | if (!$dotnet) { 16 | # Nothing is installed. 17 | Write-Output $false 18 | exit 1 19 | } 20 | 21 | Function IsVersionMatch { 22 | Param( 23 | [Parameter()] 24 | $actualVersion 25 | ) 26 | return $actualVersion -and 27 | $Version.Major -eq $actualVersion.Major -and 28 | $Version.Minor -eq $actualVersion.Minor -and 29 | (($Version.Build -eq -1) -or ($Version.Build -eq $actualVersion.Build)) -and 30 | (($Version.Revision -eq -1) -or ($Version.Revision -eq $actualVersion.Revision)) 31 | } 32 | 33 | $installedRuntimes = dotnet --list-runtimes |? { $_.Split()[0] -ieq $Runtime } |% { $v = $null; [Version]::tryparse($_.Split()[1], [ref] $v); $v } 34 | $matchingRuntimes = $installedRuntimes |? { IsVersionMatch -actualVersion $_ } 35 | if (!$matchingRuntimes) { 36 | Write-Output $false 37 | exit 1 38 | } 39 | 40 | Write-Output $true 41 | exit 0 42 | -------------------------------------------------------------------------------- /tools/Check-DotNetSdk.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Checks whether the .NET Core SDK required by this repo is installed. 4 | #> 5 | [CmdletBinding()] 6 | Param ( 7 | ) 8 | 9 | $dotnet = Get-Command dotnet -ErrorAction SilentlyContinue 10 | if (!$dotnet) { 11 | # Nothing is installed. 12 | Write-Output $false 13 | exit 1 14 | } 15 | 16 | # We need to set the current directory so dotnet considers the SDK required by our global.json file. 17 | Push-Location "$PSScriptRoot\.." 18 | try { 19 | dotnet -h 2>&1 | Out-Null 20 | if (($LASTEXITCODE -eq 129) -or # On Linux 21 | ($LASTEXITCODE -eq -2147450751) # On Windows 22 | ) { 23 | # These exit codes indicate no matching SDK exists. 24 | Write-Output $false 25 | exit 2 26 | } 27 | 28 | # The required SDK is already installed! 29 | Write-Output $true 30 | exit 0 31 | } catch { 32 | # I don't know why, but on some build agents (e.g. MicroBuild), an exception is thrown from the `dotnet` invocation when a match is not found. 33 | Write-Output $false 34 | exit 3 35 | } finally { 36 | Pop-Location 37 | } 38 | -------------------------------------------------------------------------------- /tools/Convert-PDB.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Converts between Windows PDB and Portable PDB formats. 4 | .PARAMETER DllPath 5 | The path to the DLL whose PDB is to be converted. 6 | .PARAMETER PdbPath 7 | The path to the PDB to convert. May be omitted if the DLL was compiled on this machine and the PDB is still at its original path. 8 | .PARAMETER OutputPath 9 | The path of the output PDB to write. 10 | #> 11 | [CmdletBinding()] 12 | Param( 13 | [Parameter(Mandatory=$true,Position=0)] 14 | [string]$DllPath, 15 | [Parameter()] 16 | [string]$PdbPath, 17 | [Parameter(Mandatory=$true,Position=1)] 18 | [string]$OutputPath 19 | ) 20 | 21 | if ($IsMacOS -or $IsLinux) { 22 | Write-Error "This script only works on Windows" 23 | return 24 | } 25 | 26 | $version = '1.1.0-beta2-21101-01' 27 | $baseDir = "$PSScriptRoot/../obj/tools" 28 | $pdb2pdbpath = "$baseDir/Microsoft.DiaSymReader.Pdb2Pdb.$version/tools/Pdb2Pdb.exe" 29 | if (-not (Test-Path $pdb2pdbpath)) { 30 | if (-not (Test-Path $baseDir)) { New-Item -Type Directory -Path $baseDir | Out-Null } 31 | $baseDir = (Resolve-Path $baseDir).Path # Normalize it 32 | 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" 33 | & (& $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 34 | } 35 | 36 | $args = $DllPath,'/out',$OutputPath,'/nowarn','0021' 37 | if ($PdbPath) { 38 | $args += '/pdb',$PdbPath 39 | } 40 | 41 | Write-Verbose "$pdb2pdbpath $args" 42 | & $pdb2pdbpath $args 43 | -------------------------------------------------------------------------------- /tools/Get-3rdPartySymbolFiles.ps1: -------------------------------------------------------------------------------- 1 | Function Get-FileFromWeb([Uri]$Uri, $OutFile) { 2 | $OutDir = Split-Path $OutFile 3 | if (!(Test-Path $OutFile)) { 4 | Write-Verbose "Downloading $Uri..." 5 | if (!(Test-Path $OutDir)) { New-Item -ItemType Directory -Path $OutDir | Out-Null } 6 | try { 7 | (New-Object System.Net.WebClient).DownloadFile($Uri, $OutFile) 8 | } 9 | finally { 10 | # This try/finally causes the script to abort 11 | } 12 | } 13 | } 14 | 15 | Function Unzip($Path, $OutDir) { 16 | $OutDir = (New-Item -ItemType Directory -Path $OutDir -Force).FullName 17 | Add-Type -AssemblyName System.IO.Compression.FileSystem 18 | 19 | # Start by extracting to a temporary directory so that there are no file conflicts. 20 | [System.IO.Compression.ZipFile]::ExtractToDirectory($Path, "$OutDir.out") 21 | 22 | # Now move all files from the temp directory to $OutDir, overwriting any files. 23 | Get-ChildItem -Path "$OutDir.out" -Recurse -File | ForEach-Object { 24 | $destinationPath = Join-Path -Path $OutDir -ChildPath $_.FullName.Substring("$OutDir.out".Length).TrimStart([io.path]::DirectorySeparatorChar, [io.path]::AltDirectorySeparatorChar) 25 | if (!(Test-Path -Path (Split-Path -Path $destinationPath -Parent))) { 26 | New-Item -ItemType Directory -Path (Split-Path -Path $destinationPath -Parent) | Out-Null 27 | } 28 | Move-Item -Path $_.FullName -Destination $destinationPath -Force 29 | } 30 | Remove-Item -Path "$OutDir.out" -Recurse -Force 31 | } 32 | 33 | Function Get-SymbolsFromPackage($id, $version) { 34 | $symbolPackagesPath = "$PSScriptRoot/../obj/SymbolsPackages" 35 | New-Item -ItemType Directory -Path $symbolPackagesPath -Force | Out-Null 36 | $nupkgPath = Join-Path $symbolPackagesPath "$id.$version.nupkg" 37 | $snupkgPath = Join-Path $symbolPackagesPath "$id.$version.snupkg" 38 | $unzippedPkgPath = Join-Path $symbolPackagesPath "$id.$version" 39 | Get-FileFromWeb -Uri "https://www.nuget.org/api/v2/package/$id/$version" -OutFile $nupkgPath 40 | Get-FileFromWeb -Uri "https://www.nuget.org/api/v2/symbolpackage/$id/$version" -OutFile $snupkgPath 41 | 42 | Unzip -Path $nupkgPath -OutDir $unzippedPkgPath 43 | Unzip -Path $snupkgPath -OutDir $unzippedPkgPath 44 | 45 | Get-ChildItem -Recurse -LiteralPath $unzippedPkgPath -Filter *.pdb | % { 46 | # Collect the DLLs/EXEs as well. 47 | $rootName = Join-Path $_.Directory $_.BaseName 48 | if ($rootName.EndsWith('.ni')) { 49 | $rootName = $rootName.Substring(0, $rootName.Length - 3) 50 | } 51 | 52 | $dllPath = "$rootName.dll" 53 | $exePath = "$rootName.exe" 54 | if (Test-Path $dllPath) { 55 | $BinaryImagePath = $dllPath 56 | } 57 | elseif (Test-Path $exePath) { 58 | $BinaryImagePath = $exePath 59 | } 60 | else { 61 | Write-Warning "`"$_`" found with no matching binary file." 62 | $BinaryImagePath = $null 63 | } 64 | 65 | if ($BinaryImagePath) { 66 | Write-Output $BinaryImagePath 67 | Write-Output $_.FullName 68 | } 69 | } 70 | } 71 | 72 | Function Get-PackageVersion($id) { 73 | $versionProps = [xml](Get-Content -LiteralPath $PSScriptRoot\..\Directory.Packages.props) 74 | $version = $versionProps.Project.ItemGroup.PackageVersion | ? { $_.Include -eq $id } | % { $_.Version } 75 | if (!$version) { 76 | Write-Error "No package version found in Directory.Packages.props for the package '$id'" 77 | } 78 | 79 | $version 80 | } 81 | 82 | # All 3rd party packages for which symbols packages are expected should be listed here. 83 | # These must all be sourced from nuget.org, as it is the only feed that supports symbol packages. 84 | $3rdPartyPackageIds = @() 85 | 86 | $3rdPartyPackageIds | % { 87 | $version = Get-PackageVersion $_ 88 | if ($version) { 89 | Get-SymbolsFromPackage -id $_ -version $version 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /tools/Get-ArtifactsStagingDirectory.ps1: -------------------------------------------------------------------------------- 1 | Param( 2 | [switch]$CleanIfLocal 3 | ) 4 | if ($env:BUILD_ARTIFACTSTAGINGDIRECTORY) { 5 | $ArtifactStagingFolder = $env:BUILD_ARTIFACTSTAGINGDIRECTORY 6 | } elseif ($env:RUNNER_TEMP) { 7 | $ArtifactStagingFolder = Join-Path $env:RUNNER_TEMP _artifacts 8 | } else { 9 | $ArtifactStagingFolder = [System.IO.Path]::GetFullPath("$PSScriptRoot/../obj/_artifacts") 10 | if ($CleanIfLocal -and (Test-Path $ArtifactStagingFolder)) { 11 | Remove-Item $ArtifactStagingFolder -Recurse -Force 12 | } 13 | } 14 | 15 | $ArtifactStagingFolder 16 | -------------------------------------------------------------------------------- /tools/Get-CodeCovTool.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Downloads the CodeCov.io uploader tool and returns the path to it. 4 | .PARAMETER AllowSkipVerify 5 | Allows skipping signature verification of the downloaded tool if gpg is not installed. 6 | #> 7 | [CmdletBinding()] 8 | Param( 9 | [switch]$AllowSkipVerify 10 | ) 11 | 12 | if ($IsMacOS) { 13 | $codeCovUrl = "https://uploader.codecov.io/latest/macos/codecov" 14 | $toolName = 'codecov' 15 | } 16 | elseif ($IsLinux) { 17 | $codeCovUrl = "https://uploader.codecov.io/latest/linux/codecov" 18 | $toolName = 'codecov' 19 | } 20 | else { 21 | $codeCovUrl = "https://uploader.codecov.io/latest/windows/codecov.exe" 22 | $toolName = 'codecov.exe' 23 | } 24 | 25 | $shaSuffix = ".SHA256SUM" 26 | $sigSuffix = $shaSuffix + ".sig" 27 | 28 | Function Get-FileFromWeb([Uri]$Uri, $OutDir) { 29 | $OutFile = Join-Path $OutDir $Uri.Segments[-1] 30 | if (!(Test-Path $OutFile)) { 31 | Write-Verbose "Downloading $Uri..." 32 | if (!(Test-Path $OutDir)) { New-Item -ItemType Directory -Path $OutDir | Out-Null } 33 | try { 34 | (New-Object System.Net.WebClient).DownloadFile($Uri, $OutFile) 35 | } finally { 36 | # This try/finally causes the script to abort 37 | } 38 | } 39 | 40 | $OutFile 41 | } 42 | 43 | $toolsPath = & "$PSScriptRoot\Get-TempToolsPath.ps1" 44 | $binaryToolsPath = Join-Path $toolsPath codecov 45 | $testingPath = Join-Path $binaryToolsPath unverified 46 | $finalToolPath = Join-Path $binaryToolsPath $toolName 47 | 48 | if (!(Test-Path $finalToolPath)) { 49 | if (Test-Path $testingPath) { 50 | Remove-Item -Recurse -Force $testingPath # ensure we download all matching files 51 | } 52 | $tool = Get-FileFromWeb $codeCovUrl $testingPath 53 | $sha = Get-FileFromWeb "$codeCovUrl$shaSuffix" $testingPath 54 | $sig = Get-FileFromWeb "$codeCovUrl$sigSuffix" $testingPath 55 | $key = Get-FileFromWeb https://keybase.io/codecovsecurity/pgp_keys.asc $testingPath 56 | 57 | if ((Get-Command gpg -ErrorAction SilentlyContinue)) { 58 | Write-Host "Importing codecov key" -ForegroundColor Yellow 59 | gpg --import $key 60 | Write-Host "Verifying signature on codecov hash" -ForegroundColor Yellow 61 | gpg --verify $sig $sha 62 | } else { 63 | if ($AllowSkipVerify) { 64 | Write-Warning "gpg not found. Unable to verify hash signature." 65 | } else { 66 | throw "gpg not found. Unable to verify hash signature. Install gpg or add -AllowSkipVerify to override." 67 | } 68 | } 69 | 70 | Write-Host "Verifying hash on downloaded tool" -ForegroundColor Yellow 71 | $actualHash = (Get-FileHash -LiteralPath $tool -Algorithm SHA256).Hash 72 | $expectedHash = (Get-Content $sha).Split()[0] 73 | if ($actualHash -ne $expectedHash) { 74 | # Validation failed. Delete the tool so we can't execute it. 75 | #Remove-Item $codeCovPath 76 | throw "codecov uploader tool failed signature validation." 77 | } 78 | 79 | Copy-Item $tool $finalToolPath 80 | 81 | if ($IsMacOS -or $IsLinux) { 82 | chmod u+x $finalToolPath 83 | } 84 | } 85 | 86 | return $finalToolPath 87 | -------------------------------------------------------------------------------- /tools/Get-LibTemplateBasis.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Returns the name of the well-known branch in the Library.Template repository upon which HEAD is based. 4 | #> 5 | [CmdletBinding(SupportsShouldProcess = $true)] 6 | Param( 7 | [switch]$ErrorIfNotRelated 8 | ) 9 | 10 | # This list should be sorted in order of decreasing specificity. 11 | $branchMarkers = @( 12 | @{ commit = 'fd0a7b25ccf030bbd16880cca6efe009d5b1fffc'; branch = 'microbuild' }; 13 | @{ commit = '05f49ce799c1f9cc696d53eea89699d80f59f833'; branch = 'main' }; 14 | ) 15 | 16 | foreach ($entry in $branchMarkers) { 17 | if (git rev-list HEAD | Select-String -Pattern $entry.commit) { 18 | return $entry.branch 19 | } 20 | } 21 | 22 | if ($ErrorIfNotRelated) { 23 | Write-Error "Library.Template has not been previously merged with this repo. Please review https://github.com/AArnott/Library.Template/tree/main?tab=readme-ov-file#readme for instructions." 24 | exit 1 25 | } 26 | -------------------------------------------------------------------------------- /tools/Get-NuGetTool.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Downloads the NuGet.exe tool and returns the path to it. 4 | .PARAMETER NuGetVersion 5 | The version of the NuGet tool to acquire. 6 | #> 7 | Param( 8 | [Parameter()] 9 | [string]$NuGetVersion='6.12.2' 10 | ) 11 | 12 | $toolsPath = & "$PSScriptRoot\Get-TempToolsPath.ps1" 13 | $binaryToolsPath = Join-Path $toolsPath $NuGetVersion 14 | if (!(Test-Path $binaryToolsPath)) { $null = mkdir $binaryToolsPath } 15 | $nugetPath = Join-Path $binaryToolsPath nuget.exe 16 | 17 | if (!(Test-Path $nugetPath)) { 18 | Write-Host "Downloading nuget.exe $NuGetVersion..." -ForegroundColor Yellow 19 | (New-Object System.Net.WebClient).DownloadFile("https://dist.nuget.org/win-x86-commandline/v$NuGetVersion/NuGet.exe", $nugetPath) 20 | } 21 | 22 | return (Resolve-Path $nugetPath).Path 23 | -------------------------------------------------------------------------------- /tools/Get-ProcDump.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Downloads 32-bit and 64-bit procdump executables and returns the path to where they were installed. 4 | #> 5 | $version = '0.0.1' 6 | $baseDir = "$PSScriptRoot\..\obj\tools" 7 | $procDumpToolPath = "$baseDir\procdump.$version\bin" 8 | if (-not (Test-Path $procDumpToolPath)) { 9 | if (-not (Test-Path $baseDir)) { New-Item -Type Directory -Path $baseDir | Out-Null } 10 | $baseDir = (Resolve-Path $baseDir).Path # Normalize it 11 | & (& $PSScriptRoot\Get-NuGetTool.ps1) install procdump -version $version -PackageSaveMode nuspec -OutputDirectory $baseDir -Source https://api.nuget.org/v3/index.json | Out-Null 12 | } 13 | 14 | (Resolve-Path $procDumpToolPath).Path 15 | -------------------------------------------------------------------------------- /tools/Get-SymbolFiles.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Collect the list of PDBs built in this repo. 4 | .PARAMETER Path 5 | The directory to recursively search for PDBs. 6 | .PARAMETER Tests 7 | A switch indicating to find PDBs only for test binaries instead of only for shipping shipping binaries. 8 | #> 9 | [CmdletBinding()] 10 | param ( 11 | [parameter(Mandatory=$true)] 12 | [string]$Path, 13 | [switch]$Tests 14 | ) 15 | 16 | $ActivityName = "Collecting symbols from $Path" 17 | Write-Progress -Activity $ActivityName -CurrentOperation "Discovery PDB files" 18 | $PDBs = Get-ChildItem -rec "$Path/*.pdb" 19 | 20 | # Filter PDBs to product OR test related. 21 | $testregex = "unittest|tests|\.test\." 22 | 23 | Write-Progress -Activity $ActivityName -CurrentOperation "De-duplicating symbols" 24 | $PDBsByHash = @{} 25 | $i = 0 26 | $PDBs |% { 27 | Write-Progress -Activity $ActivityName -CurrentOperation "De-duplicating symbols" -PercentComplete (100 * $i / $PDBs.Length) 28 | $hash = Get-FileHash $_ 29 | $i++ 30 | Add-Member -InputObject $_ -MemberType NoteProperty -Name Hash -Value $hash.Hash 31 | Write-Output $_ 32 | } | Sort-Object CreationTime |% { 33 | # De-dupe based on hash. Prefer the first match so we take the first built copy. 34 | if (-not $PDBsByHash.ContainsKey($_.Hash)) { 35 | $PDBsByHash.Add($_.Hash, $_.FullName) 36 | Write-Output $_ 37 | } 38 | } |? { 39 | if ($Tests) { 40 | $_.FullName -match $testregex 41 | } else { 42 | $_.FullName -notmatch $testregex 43 | } 44 | } |% { 45 | # Collect the DLLs/EXEs as well. 46 | $rootName = Join-Path $_.Directory $_.BaseName 47 | if ($rootName.EndsWith('.ni')) { 48 | $rootName = $rootName.Substring(0, $rootName.Length - 3) 49 | } 50 | 51 | $dllPath = "$rootName.dll" 52 | $exePath = "$rootName.exe" 53 | if (Test-Path $dllPath) { 54 | $BinaryImagePath = $dllPath 55 | } elseif (Test-Path $exePath) { 56 | $BinaryImagePath = $exePath 57 | } else { 58 | Write-Warning "`"$_`" found with no matching binary file." 59 | $BinaryImagePath = $null 60 | } 61 | 62 | if ($BinaryImagePath) { 63 | Write-Output $BinaryImagePath 64 | Write-Output $_.FullName 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /tools/Get-TempToolsPath.ps1: -------------------------------------------------------------------------------- 1 | if ($env:AGENT_TEMPDIRECTORY) { 2 | $path = "$env:AGENT_TEMPDIRECTORY\$env:BUILD_BUILDID" 3 | } elseif ($env:localappdata) { 4 | $path = "$env:localappdata\gitrepos\tools" 5 | } else { 6 | $path = "$PSScriptRoot\..\obj\tools" 7 | } 8 | 9 | if (!(Test-Path $path)) { 10 | New-Item -ItemType Directory -Path $Path | Out-Null 11 | } 12 | 13 | (Resolve-Path $path).Path 14 | -------------------------------------------------------------------------------- /tools/Install-NuGetCredProvider.ps1: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env pwsh 2 | 3 | <# 4 | .SYNOPSIS 5 | Downloads and installs the Microsoft Artifacts Credential Provider 6 | from https://github.com/microsoft/artifacts-credprovider 7 | to assist in authenticating to Azure Artifact feeds in interactive development 8 | or unattended build agents. 9 | .PARAMETER Force 10 | Forces install of the CredProvider plugin even if one already exists. This is useful to upgrade an older version. 11 | .PARAMETER AccessToken 12 | An optional access token for authenticating to Azure Artifacts authenticated feeds. 13 | #> 14 | [CmdletBinding()] 15 | Param ( 16 | [Parameter()] 17 | [switch]$Force, 18 | [Parameter()] 19 | [string]$AccessToken 20 | ) 21 | 22 | $envVars = @{} 23 | 24 | $toolsPath = & "$PSScriptRoot\Get-TempToolsPath.ps1" 25 | 26 | if ($IsMacOS -or $IsLinux) { 27 | $installerScript = "installcredprovider.sh" 28 | $sourceUrl = "https://raw.githubusercontent.com/microsoft/artifacts-credprovider/master/helpers/installcredprovider.sh" 29 | } else { 30 | $installerScript = "installcredprovider.ps1" 31 | $sourceUrl = "https://raw.githubusercontent.com/microsoft/artifacts-credprovider/master/helpers/installcredprovider.ps1" 32 | } 33 | 34 | $installerScript = Join-Path $toolsPath $installerScript 35 | 36 | if (!(Test-Path $installerScript) -or $Force) { 37 | Invoke-WebRequest $sourceUrl -OutFile $installerScript 38 | } 39 | 40 | $installerScript = (Resolve-Path $installerScript).Path 41 | 42 | if ($IsMacOS -or $IsLinux) { 43 | chmod u+x $installerScript 44 | } 45 | 46 | & $installerScript -Force:$Force -AddNetfx -InstallNet8 47 | 48 | if ($AccessToken) { 49 | $endpoints = @() 50 | 51 | $endpointURIs = @() 52 | Get-ChildItem "$PSScriptRoot\..\nuget.config" -Recurse |% { 53 | $nugetConfig = [xml](Get-Content -LiteralPath $_) 54 | 55 | $nugetConfig.configuration.packageSources.add |? { ($_.value -match '^https://pkgs\.dev\.azure\.com/') -or ($_.value -match '^https://[\w\-]+\.pkgs\.visualstudio\.com/') } |% { 56 | if ($endpointURIs -notcontains $_.Value) { 57 | $endpointURIs += $_.Value 58 | $endpoint = New-Object -TypeName PSObject 59 | Add-Member -InputObject $endpoint -MemberType NoteProperty -Name endpoint -Value $_.value 60 | Add-Member -InputObject $endpoint -MemberType NoteProperty -Name username -Value ado 61 | Add-Member -InputObject $endpoint -MemberType NoteProperty -Name password -Value $AccessToken 62 | $endpoints += $endpoint 63 | } 64 | } 65 | } 66 | 67 | $auth = New-Object -TypeName PSObject 68 | Add-Member -InputObject $auth -MemberType NoteProperty -Name endpointCredentials -Value $endpoints 69 | 70 | $authJson = ConvertTo-Json -InputObject $auth 71 | $envVars += @{ 72 | 'VSS_NUGET_EXTERNAL_FEED_ENDPOINTS'=$authJson; 73 | } 74 | } 75 | 76 | & "$PSScriptRoot/Set-EnvVars.ps1" -Variables $envVars | Out-Null 77 | -------------------------------------------------------------------------------- /tools/MergeFrom-Template.ps1: -------------------------------------------------------------------------------- 1 | 2 | <# 3 | .SYNOPSIS 4 | Merges the latest changes from Library.Template into HEAD of this repo. 5 | .PARAMETER LocalBranch 6 | The name of the local branch to create at HEAD and use to merge into from Library.Template. 7 | #> 8 | [CmdletBinding(SupportsShouldProcess = $true)] 9 | Param( 10 | [string]$LocalBranch = "dev/$($env:USERNAME)/libtemplateUpdate" 11 | ) 12 | 13 | Function Spawn-Tool($command, $commandArgs, $workingDirectory, $allowFailures) { 14 | if ($workingDirectory) { 15 | Push-Location $workingDirectory 16 | } 17 | try { 18 | if ($env:TF_BUILD) { 19 | Write-Host "$pwd >" 20 | Write-Host "##[command]$command $commandArgs" 21 | } 22 | else { 23 | Write-Host "$command $commandArgs" -ForegroundColor Yellow 24 | } 25 | if ($commandArgs) { 26 | & $command @commandArgs 27 | } else { 28 | Invoke-Expression $command 29 | } 30 | if ((!$allowFailures) -and ($LASTEXITCODE -ne 0)) { exit $LASTEXITCODE } 31 | } 32 | finally { 33 | if ($workingDirectory) { 34 | Pop-Location 35 | } 36 | } 37 | } 38 | 39 | $remoteBranch = & $PSScriptRoot\Get-LibTemplateBasis.ps1 -ErrorIfNotRelated 40 | if ($LASTEXITCODE -ne 0) { 41 | exit $LASTEXITCODE 42 | } 43 | 44 | $LibTemplateUrl = 'https://github.com/aarnott/Library.Template' 45 | Spawn-Tool 'git' ('fetch', $LibTemplateUrl, $remoteBranch) 46 | $SourceCommit = Spawn-Tool 'git' ('rev-parse', 'FETCH_HEAD') 47 | $BaseBranch = Spawn-Tool 'git' ('branch', '--show-current') 48 | $SourceCommitUrl = "$LibTemplateUrl/commit/$SourceCommit" 49 | 50 | # To reduce the odds of merge conflicts at this stage, we always move HEAD to the last successful merge. 51 | $basis = Spawn-Tool 'git' ('rev-parse', 'HEAD') # TODO: consider improving this later 52 | 53 | Write-Host "Merging the $remoteBranch branch of Library.Template ($SourceCommit) into local repo $basis" -ForegroundColor Green 54 | 55 | Spawn-Tool 'git' ('checkout', '-b', $LocalBranch, $basis) $null $true 56 | if ($LASTEXITCODE -eq 128) { 57 | Spawn-Tool 'git' ('checkout', $LocalBranch) 58 | Spawn-Tool 'git' ('merge', $basis) 59 | } 60 | 61 | Spawn-Tool 'git' ('merge', 'FETCH_HEAD', '--no-ff', '-m', "Merge the $remoteBranch branch from $LibTemplateUrl`n`nSpecifically, this merges [$SourceCommit from that repo]($SourceCommitUrl).") 62 | if ($LASTEXITCODE -eq 1) { 63 | Write-Error "Merge conflict detected. Manual resolution required." 64 | exit 1 65 | } 66 | elseif ($LASTEXITCODE -ne 0) { 67 | Write-Error "Merge failed with exit code $LASTEXITCODE." 68 | exit $LASTEXITCODE 69 | } 70 | 71 | $result = New-Object PSObject -Property @{ 72 | BaseBranch = $BaseBranch # The original branch that was checked out when the script ran. 73 | LocalBranch = $LocalBranch # The name of the local branch that was created before the merge. 74 | SourceCommit = $SourceCommit # The commit from Library.Template that was merged in. 75 | SourceBranch = $remoteBranch # The branch from Library.Template that was merged in. 76 | } 77 | 78 | Write-Host $result 79 | Write-Output $result 80 | -------------------------------------------------------------------------------- /tools/Prepare-Legacy-Symbols.ps1: -------------------------------------------------------------------------------- 1 | Param( 2 | [string]$Path 3 | ) 4 | 5 | $ArtifactStagingFolder = & "$PSScriptRoot/Get-ArtifactsStagingDirectory.ps1" 6 | $ArtifactStagingFolder += '/symbols-legacy' 7 | robocopy $Path $ArtifactStagingFolder /mir /njh /njs /ndl /nfl 8 | $WindowsPdbSubDirName = 'symstore' 9 | 10 | Get-ChildItem "$ArtifactStagingFolder\*.pdb" -Recurse |% { 11 | $dllPath = "$($_.Directory)/$($_.BaseName).dll" 12 | $exePath = "$($_.Directory)/$($_.BaseName).exe" 13 | if (Test-Path $dllPath) { 14 | $BinaryImagePath = $dllPath 15 | } elseif (Test-Path $exePath) { 16 | $BinaryImagePath = $exePath 17 | } else { 18 | Write-Warning "`"$_`" found with no matching binary file." 19 | $BinaryImagePath = $null 20 | } 21 | 22 | if ($BinaryImagePath) { 23 | # Convert the PDB to legacy Windows PDBs 24 | Write-Host "Converting PDB for $_" -ForegroundColor DarkGray 25 | $WindowsPdbDir = "$($_.Directory.FullName)\$WindowsPdbSubDirName" 26 | if (!(Test-Path $WindowsPdbDir)) { mkdir $WindowsPdbDir | Out-Null } 27 | $legacyPdbPath = "$WindowsPdbDir\$($_.BaseName).pdb" 28 | & "$PSScriptRoot\Convert-PDB.ps1" -DllPath $BinaryImagePath -PdbPath $_ -OutputPath $legacyPdbPath 29 | if ($LASTEXITCODE -ne 0) { 30 | Write-Warning "PDB conversion of `"$_`" failed." 31 | } 32 | 33 | Move-Item $legacyPdbPath $_ -Force 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tools/Set-EnvVars.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Set environment variables in the environment. 4 | Azure Pipeline and CMD environments are considered. 5 | .PARAMETER Variables 6 | A hashtable of variables to be set. 7 | .PARAMETER PrependPath 8 | A set of paths to prepend to the PATH environment variable. 9 | .OUTPUTS 10 | A boolean indicating whether the environment variables can be expected to propagate to the caller's environment. 11 | .DESCRIPTION 12 | The CmdEnvScriptPath environment variable may be optionally set to a path to a cmd shell script to be created (or appended to if it already exists) that will set the environment variables in cmd.exe that are set within the PowerShell environment. 13 | This is used by init.cmd in order to reapply any new environment variables to the parent cmd.exe process that were set in the powershell child process. 14 | #> 15 | [CmdletBinding(SupportsShouldProcess=$true)] 16 | Param( 17 | [Parameter(Mandatory=$true, Position=1)] 18 | $Variables, 19 | [string[]]$PrependPath 20 | ) 21 | 22 | if ($Variables.Count -eq 0) { 23 | return $true 24 | } 25 | 26 | $cmdInstructions = !$env:TF_BUILD -and !$env:GITHUB_ACTIONS -and !$env:CmdEnvScriptPath -and ($env:PS1UnderCmd -eq '1') 27 | if ($cmdInstructions) { 28 | Write-Warning "Environment variables have been set that will be lost because you're running under cmd.exe" 29 | Write-Host "Environment variables that must be set manually:" -ForegroundColor Blue 30 | } else { 31 | Write-Host "Environment variables set:" -ForegroundColor Blue 32 | Write-Host ($Variables | Out-String) 33 | if ($PrependPath) { 34 | Write-Host "Paths prepended to PATH: $PrependPath" 35 | } 36 | } 37 | 38 | if ($env:TF_BUILD) { 39 | Write-Host "Azure Pipelines detected. Logging commands will be used to propagate environment variables and prepend path." 40 | } 41 | 42 | if ($env:GITHUB_ACTIONS) { 43 | Write-Host "GitHub Actions detected. Logging commands will be used to propagate environment variables and prepend path." 44 | } 45 | 46 | $CmdEnvScript = '' 47 | $Variables.GetEnumerator() |% { 48 | Set-Item -LiteralPath env:$($_.Key) -Value $_.Value 49 | 50 | # If we're running in a cloud CI, set these environment variables so they propagate. 51 | if ($env:TF_BUILD) { 52 | Write-Host "##vso[task.setvariable variable=$($_.Key);]$($_.Value)" 53 | } 54 | if ($env:GITHUB_ACTIONS) { 55 | Add-Content -LiteralPath $env:GITHUB_ENV -Value "$($_.Key)=$($_.Value)" 56 | } 57 | 58 | if ($cmdInstructions) { 59 | Write-Host "SET $($_.Key)=$($_.Value)" 60 | } 61 | 62 | $CmdEnvScript += "SET $($_.Key)=$($_.Value)`r`n" 63 | } 64 | 65 | $pathDelimiter = ';' 66 | if ($IsMacOS -or $IsLinux) { 67 | $pathDelimiter = ':' 68 | } 69 | 70 | if ($PrependPath) { 71 | $PrependPath |% { 72 | $newPathValue = "$_$pathDelimiter$env:PATH" 73 | Set-Item -LiteralPath env:PATH -Value $newPathValue 74 | if ($cmdInstructions) { 75 | Write-Host "SET PATH=$newPathValue" 76 | } 77 | 78 | if ($env:TF_BUILD) { 79 | Write-Host "##vso[task.prependpath]$_" 80 | } 81 | if ($env:GITHUB_ACTIONS) { 82 | Add-Content -LiteralPath $env:GITHUB_PATH -Value $_ 83 | } 84 | 85 | $CmdEnvScript += "SET PATH=$_$pathDelimiter%PATH%" 86 | } 87 | } 88 | 89 | if ($env:CmdEnvScriptPath) { 90 | if (Test-Path $env:CmdEnvScriptPath) { 91 | $CmdEnvScript = (Get-Content -LiteralPath $env:CmdEnvScriptPath) + $CmdEnvScript 92 | } 93 | 94 | Set-Content -LiteralPath $env:CmdEnvScriptPath -Value $CmdEnvScript 95 | } 96 | 97 | return !$cmdInstructions 98 | -------------------------------------------------------------------------------- /tools/artifacts/APIScanInputs.ps1: -------------------------------------------------------------------------------- 1 | $inputs = & "$PSScriptRoot/symbols.ps1" 2 | 3 | if (!$inputs) { return } 4 | 5 | # Filter out specific files that target OS's that are not subject to APIScan. 6 | # Files that are subject but are not supported must be scanned and an SEL exception filed. 7 | $outputs = @{} 8 | $forbiddenSubPaths = @( 9 | , 'linux-*' 10 | , 'osx*' 11 | ) 12 | 13 | $inputs.GetEnumerator() | % { 14 | $list = $_.Value | ? { 15 | $path = $_.Replace('\', '/') 16 | return !($forbiddenSubPaths | ? { $path -like "*/$_/*" }) 17 | } 18 | $outputs[$_.Key] = $list 19 | } 20 | 21 | 22 | $outputs 23 | -------------------------------------------------------------------------------- /tools/artifacts/LocBin.ps1: -------------------------------------------------------------------------------- 1 | # Identify LCE files and the binary files they describe 2 | $BinRoot = [System.IO.Path]::GetFullPath("$PSScriptRoot\..\..\bin") 3 | if (!(Test-Path $BinRoot)) { return } 4 | 5 | $FilesToCopy = @() 6 | $FilesToCopy += Get-ChildItem -Recurse -File -Path $BinRoot |? { $_.FullName -match '\\Localize\\' } 7 | 8 | Get-ChildItem -rec "$BinRoot\*.lce" -File | % { 9 | $FilesToCopy += $_ 10 | $FilesToCopy += $_.FullName.SubString(0, $_.FullName.Length - 4) 11 | } 12 | 13 | $FilesToCopy += Get-ChildItem -rec "$BinRoot\*.lcg" -File | % { [xml](Get-Content $_) } | % { $_.lcx.name } 14 | 15 | @{ 16 | "$BinRoot" = $FilesToCopy; 17 | } 18 | -------------------------------------------------------------------------------- /tools/artifacts/VSInsertion.ps1: -------------------------------------------------------------------------------- 1 | # This artifact captures everything needed to insert into VS (NuGet packages, insertion metadata, etc.) 2 | 3 | [CmdletBinding()] 4 | Param ( 5 | ) 6 | 7 | if ($IsMacOS -or $IsLinux) { 8 | # We only package up for insertions on Windows agents since they are where optprof can happen. 9 | Write-Verbose "Skipping VSInsertion artifact since we're not on Windows." 10 | return @{} 11 | } 12 | 13 | $RepoRoot = [System.IO.Path]::GetFullPath("$PSScriptRoot\..\..") 14 | $BuildConfiguration = $env:BUILDCONFIGURATION 15 | if (!$BuildConfiguration) { 16 | $BuildConfiguration = 'Debug' 17 | } 18 | 19 | $PackagesRoot = "$RepoRoot/bin/Packages/$BuildConfiguration" 20 | $NuGetPackages = "$PackagesRoot/NuGet" 21 | $VsixPackages = "$PackagesRoot/Vsix" 22 | 23 | if (!(Test-Path $NuGetPackages)) { 24 | Write-Warning "Skipping because NuGet packages haven't been built yet." 25 | return @{} 26 | } 27 | 28 | $result = @{ 29 | "$NuGetPackages" = (Get-ChildItem $NuGetPackages -Recurse) 30 | } 31 | 32 | if (Test-Path $VsixPackages) { 33 | $result["$PackagesRoot"] += Get-ChildItem $VsixPackages -Recurse 34 | } 35 | 36 | if ($env:IsOptProf) { 37 | $VSRepoPackages = "$PackagesRoot/VSRepo" 38 | $result["$VSRepoPackages"] = (Get-ChildItem "$VSRepoPackages\*.VSInsertionMetadata.*.nupkg"); 39 | } 40 | 41 | $result 42 | -------------------------------------------------------------------------------- /tools/artifacts/Variables.ps1: -------------------------------------------------------------------------------- 1 | # This artifact captures all variables defined in the ..\variables folder. 2 | # It "snaps" the values of these variables where we can compute them during the build, 3 | # and otherwise captures the scripts to run later during an Azure Pipelines environment release. 4 | 5 | $RepoRoot = [System.IO.Path]::GetFullPath("$PSScriptRoot/../..") 6 | $ArtifactBasePath = "$RepoRoot/obj/_artifacts" 7 | $VariablesArtifactPath = Join-Path $ArtifactBasePath variables 8 | if (-not (Test-Path $VariablesArtifactPath)) { New-Item -ItemType Directory -Path $VariablesArtifactPath | Out-Null } 9 | 10 | # Copy variables, either by value if the value is calculable now, or by script 11 | Get-ChildItem "$PSScriptRoot/../variables" |% { 12 | $value = $null 13 | if (-not $_.BaseName.StartsWith('_')) { # Skip trying to interpret special scripts 14 | # First check the environment variables in case the variable was set in a queued build 15 | # Always use all caps for env var access because Azure Pipelines converts variables to upper-case for env vars, 16 | # and on non-Windows env vars are case sensitive. 17 | $envVarName = $_.BaseName.ToUpper() 18 | if (Test-Path env:$envVarName) { 19 | $value = Get-Content "env:$envVarName" 20 | } 21 | 22 | # If that didn't give us anything, try executing the script right now from its original position 23 | if (-not $value) { 24 | $value = & $_.FullName 25 | } 26 | 27 | if ($value) { 28 | # We got something, so wrap it with quotes so it's treated like a literal value. 29 | $value = "'$value'" 30 | } 31 | } 32 | 33 | # If that didn't get us anything, just copy the script itself 34 | if (-not $value) { 35 | $value = Get-Content -LiteralPath $_.FullName 36 | } 37 | 38 | Set-Content -LiteralPath "$VariablesArtifactPath/$($_.Name)" -Value $value 39 | } 40 | 41 | @{ 42 | "$VariablesArtifactPath" = (Get-ChildItem $VariablesArtifactPath -Recurse); 43 | } 44 | -------------------------------------------------------------------------------- /tools/artifacts/_all.ps1: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env pwsh 2 | 3 | <# 4 | .SYNOPSIS 5 | This script returns all the artifacts that should be collected after a build. 6 | Each powershell artifact is expressed as an object with these properties: 7 | Source - the full path to the source file 8 | ArtifactName - the name of the artifact to upload to 9 | ContainerFolder - the relative path within the artifact in which the file should appear 10 | Each artifact aggregating .ps1 script should return a hashtable: 11 | Key = path to the directory from which relative paths within the artifact should be calculated 12 | Value = an array of paths (absolute or relative to the BaseDirectory) to files to include in the artifact. 13 | FileInfo objects are also allowed. 14 | .PARAMETER Force 15 | Executes artifact scripts even if they have already been staged. 16 | #> 17 | 18 | [CmdletBinding(SupportsShouldProcess = $true)] 19 | param ( 20 | [string]$ArtifactNameSuffix, 21 | [switch]$Force 22 | ) 23 | 24 | Function EnsureTrailingSlash($path) { 25 | if ($path.length -gt 0 -and !$path.EndsWith('\') -and !$path.EndsWith('/')) { 26 | $path = $path + [IO.Path]::DirectorySeparatorChar 27 | } 28 | 29 | $path.Replace('\', [IO.Path]::DirectorySeparatorChar) 30 | } 31 | 32 | Function Test-ArtifactStaged($artifactName) { 33 | $varName = "ARTIFACTSTAGED_$($artifactName.ToUpper())" 34 | Test-Path "env:$varName" 35 | } 36 | 37 | Get-ChildItem "$PSScriptRoot\*.ps1" -Exclude "_*" -Recurse | % { 38 | $ArtifactName = $_.BaseName 39 | if ($Force -or !(Test-ArtifactStaged($ArtifactName + $ArtifactNameSuffix))) { 40 | $totalFileCount = 0 41 | Write-Verbose "Collecting file list for artifact $($_.BaseName)" 42 | $fileGroups = & $_ 43 | if ($fileGroups) { 44 | $fileGroups.GetEnumerator() | % { 45 | $BaseDirectory = New-Object Uri ((EnsureTrailingSlash $_.Key.ToString()), [UriKind]::Absolute) 46 | $_.Value | ? { $_ } | % { 47 | if ($_.GetType() -eq [IO.FileInfo] -or $_.GetType() -eq [IO.DirectoryInfo]) { 48 | $_ = $_.FullName 49 | } 50 | 51 | $artifact = New-Object -TypeName PSObject 52 | Add-Member -InputObject $artifact -MemberType NoteProperty -Name ArtifactName -Value $ArtifactName 53 | 54 | $SourceFullPath = New-Object Uri ($BaseDirectory, $_) 55 | Add-Member -InputObject $artifact -MemberType NoteProperty -Name Source -Value $SourceFullPath.LocalPath 56 | 57 | $RelativePath = [Uri]::UnescapeDataString($BaseDirectory.MakeRelative($SourceFullPath)) 58 | Add-Member -InputObject $artifact -MemberType NoteProperty -Name ContainerFolder -Value (Split-Path $RelativePath) 59 | 60 | Write-Output $artifact 61 | $totalFileCount += 1 62 | } 63 | } 64 | } 65 | 66 | if ($totalFileCount -eq 0) { 67 | Write-Warning "No files found for the `"$ArtifactName`" artifact." 68 | } 69 | } else { 70 | Write-Host "Skipping $ArtifactName because it has already been staged." -ForegroundColor DarkGray 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /tools/artifacts/_stage_all.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | This script links all the artifacts described by _all.ps1 4 | into a staging directory, reading for uploading to a cloud build artifact store. 5 | It returns a sequence of objects with Name and Path properties. 6 | #> 7 | 8 | [CmdletBinding()] 9 | param ( 10 | [string]$ArtifactNameSuffix, 11 | [switch]$AvoidSymbolicLinks 12 | ) 13 | 14 | $ArtifactStagingFolder = & "$PSScriptRoot/../Get-ArtifactsStagingDirectory.ps1" -CleanIfLocal 15 | 16 | function Create-SymbolicLink { 17 | param ( 18 | $Link, 19 | $Target 20 | ) 21 | 22 | if ($Link -eq $Target) { 23 | return 24 | } 25 | 26 | if (Test-Path $Link) { Remove-Item $Link } 27 | $LinkContainer = Split-Path $Link -Parent 28 | if (!(Test-Path $LinkContainer)) { mkdir $LinkContainer } 29 | if ($IsMacOS -or $IsLinux) { 30 | ln $Target $Link | Out-Null 31 | } else { 32 | cmd /c "mklink `"$Link`" `"$Target`"" | Out-Null 33 | } 34 | 35 | if ($LASTEXITCODE -ne 0) { 36 | # Windows requires admin privileges to create symbolic links 37 | # unless Developer Mode has been enabled. 38 | throw "Failed to create symbolic link at $Link that points to $Target" 39 | } 40 | } 41 | 42 | # Stage all artifacts 43 | $Artifacts = & "$PSScriptRoot\_all.ps1" -ArtifactNameSuffix $ArtifactNameSuffix 44 | $Artifacts |% { 45 | $DestinationFolder = [System.IO.Path]::GetFullPath("$ArtifactStagingFolder/$($_.ArtifactName)$ArtifactNameSuffix/$($_.ContainerFolder)").TrimEnd('\') 46 | $Name = "$(Split-Path $_.Source -Leaf)" 47 | 48 | #Write-Host "$($_.Source) -> $($_.ArtifactName)\$($_.ContainerFolder)" -ForegroundColor Yellow 49 | 50 | if (-not (Test-Path $DestinationFolder)) { New-Item -ItemType Directory -Path $DestinationFolder | Out-Null } 51 | if (Test-Path -PathType Leaf $_.Source) { # skip folders 52 | $TargetPath = Join-Path $DestinationFolder $Name 53 | if ($AvoidSymbolicLinks) { 54 | Copy-Item -LiteralPath $_.Source -Destination $TargetPath 55 | } else { 56 | Create-SymbolicLink -Link $TargetPath -Target $_.Source 57 | } 58 | } 59 | } 60 | 61 | $ArtifactNames = $Artifacts |% { "$($_.ArtifactName)$ArtifactNameSuffix" } 62 | $ArtifactNames += Get-ChildItem env:ARTIFACTSTAGED_* |% { 63 | # Return from ALLCAPS to the actual capitalization used for the artifact. 64 | $artifactNameAllCaps = "$($_.Name.Substring('ARTIFACTSTAGED_'.Length))" 65 | (Get-ChildItem $ArtifactStagingFolder\$artifactNameAllCaps* -Filter $artifactNameAllCaps).Name 66 | } 67 | $ArtifactNames | Get-Unique |% { 68 | $artifact = New-Object -TypeName PSObject 69 | Add-Member -InputObject $artifact -MemberType NoteProperty -Name Name -Value $_ 70 | Add-Member -InputObject $artifact -MemberType NoteProperty -Name Path -Value (Join-Path $ArtifactStagingFolder $_) 71 | Write-Output $artifact 72 | } 73 | -------------------------------------------------------------------------------- /tools/artifacts/build_logs.ps1: -------------------------------------------------------------------------------- 1 | $ArtifactStagingFolder = & "$PSScriptRoot/../Get-ArtifactsStagingDirectory.ps1" 2 | 3 | if (!(Test-Path $ArtifactStagingFolder/build_logs)) { return } 4 | 5 | @{ 6 | "$ArtifactStagingFolder/build_logs" = (Get-ChildItem -Recurse "$ArtifactStagingFolder/build_logs") 7 | } 8 | -------------------------------------------------------------------------------- /tools/artifacts/coverageResults.ps1: -------------------------------------------------------------------------------- 1 | $RepoRoot = [System.IO.Path]::GetFullPath("$PSScriptRoot\..\..") 2 | 3 | $coverageFiles = @(Get-ChildItem "$RepoRoot/test/*.cobertura.xml" -Recurse | Where {$_.FullName -notlike "*/In/*" -and $_.FullName -notlike "*\In\*" }) 4 | 5 | # Prepare code coverage reports for merging on another machine 6 | $repoRoot = $env:SYSTEM_DEFAULTWORKINGDIRECTORY 7 | if (!$repoRoot) { $repoRoot = $env:GITHUB_WORKSPACE } 8 | if ($repoRoot) { 9 | Write-Host "Substituting $repoRoot with `"{reporoot}`"" 10 | $coverageFiles |% { 11 | $content = Get-Content -LiteralPath $_ |% { $_ -Replace [regex]::Escape($repoRoot), "{reporoot}" } 12 | Set-Content -LiteralPath $_ -Value $content -Encoding UTF8 13 | } 14 | } else { 15 | Write-Warning "coverageResults: Cloud build not detected. Machine-neutral token replacement skipped." 16 | } 17 | 18 | if (!((Test-Path $RepoRoot\bin) -and (Test-Path $RepoRoot\obj))) { return } 19 | 20 | @{ 21 | $RepoRoot = ( 22 | $coverageFiles + 23 | (Get-ChildItem "$RepoRoot\obj\*.cs" -Recurse) 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /tools/artifacts/deployables.ps1: -------------------------------------------------------------------------------- 1 | $RepoRoot = [System.IO.Path]::GetFullPath("$PSScriptRoot\..\..") 2 | $BuildConfiguration = $env:BUILDCONFIGURATION 3 | if (!$BuildConfiguration) { 4 | $BuildConfiguration = 'Debug' 5 | } 6 | 7 | $PackagesRoot = "$RepoRoot/bin/Packages/$BuildConfiguration" 8 | 9 | if (!(Test-Path $PackagesRoot)) { return } 10 | 11 | @{ 12 | "$PackagesRoot" = (Get-ChildItem $PackagesRoot -Recurse) 13 | } 14 | -------------------------------------------------------------------------------- /tools/artifacts/projectAssetsJson.ps1: -------------------------------------------------------------------------------- 1 | $ObjRoot = [System.IO.Path]::GetFullPath("$PSScriptRoot\..\..\obj") 2 | 3 | if (!(Test-Path $ObjRoot)) { return } 4 | 5 | @{ 6 | "$ObjRoot" = ( 7 | (Get-ChildItem "$ObjRoot\project.assets.json" -Recurse) 8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /tools/artifacts/symbols.ps1: -------------------------------------------------------------------------------- 1 | $BinPath = [System.IO.Path]::GetFullPath("$PSScriptRoot/../../bin") 2 | $3rdPartyPath = [System.IO.Path]::GetFullPath("$PSScriptRoot/../../obj/SymbolsPackages") 3 | if (!(Test-Path $BinPath)) { return } 4 | $symbolfiles = & "$PSScriptRoot/../Get-SymbolFiles.ps1" -Path $BinPath | Get-Unique 5 | $3rdPartyFiles = & "$PSScriptRoot/../Get-3rdPartySymbolFiles.ps1" 6 | 7 | @{ 8 | "$BinPath" = $SymbolFiles; 9 | "$3rdPartyPath" = $3rdPartyFiles; 10 | } 11 | -------------------------------------------------------------------------------- /tools/artifacts/testResults.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | Param( 3 | ) 4 | 5 | $result = @{} 6 | 7 | $testRoot = Resolve-Path "$PSScriptRoot\..\..\test" 8 | $result[$testRoot] = (Get-ChildItem "$testRoot\TestResults" -Recurse -Directory | Get-ChildItem -Recurse -File) 9 | 10 | $artifactStaging = & "$PSScriptRoot/../Get-ArtifactsStagingDirectory.ps1" 11 | $testlogsPath = Join-Path $artifactStaging "test_logs" 12 | if (Test-Path $testlogsPath) { 13 | $result[$testlogsPath] = Get-ChildItem $testlogsPath -Recurse; 14 | } 15 | 16 | $result 17 | -------------------------------------------------------------------------------- /tools/artifacts/test_symbols.ps1: -------------------------------------------------------------------------------- 1 | $BinPath = [System.IO.Path]::GetFullPath("$PSScriptRoot/../../bin") 2 | if (!(Test-Path $BinPath)) { return } 3 | $symbolfiles = & "$PSScriptRoot/../Get-SymbolFiles.ps1" -Path $BinPath -Tests | Get-Unique 4 | 5 | @{ 6 | "$BinPath" = $SymbolFiles; 7 | } 8 | -------------------------------------------------------------------------------- /tools/dotnet-test-cloud.ps1: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env pwsh 2 | 3 | <# 4 | .SYNOPSIS 5 | Runs tests as they are run in cloud test runs. 6 | .PARAMETER Configuration 7 | The configuration within which to run tests 8 | .PARAMETER Agent 9 | The name of the agent. This is used in preparing test run titles. 10 | .PARAMETER PublishResults 11 | A switch to publish results to Azure Pipelines. 12 | .PARAMETER x86 13 | A switch to run the tests in an x86 process. 14 | .PARAMETER dotnet32 15 | The path to a 32-bit dotnet executable to use. 16 | #> 17 | [CmdletBinding()] 18 | Param( 19 | [string]$Configuration='Debug', 20 | [string]$Agent='Local', 21 | [switch]$PublishResults, 22 | [switch]$x86, 23 | [string]$dotnet32 24 | ) 25 | 26 | $RepoRoot = (Resolve-Path "$PSScriptRoot/..").Path 27 | $ArtifactStagingFolder = & "$PSScriptRoot/Get-ArtifactsStagingDirectory.ps1" 28 | 29 | $dotnet = 'dotnet' 30 | if ($x86) { 31 | $x86RunTitleSuffix = ", x86" 32 | if ($dotnet32) { 33 | $dotnet = $dotnet32 34 | } else { 35 | $dotnet32Possibilities = "$PSScriptRoot\../obj/tools/x86/.dotnet/dotnet.exe", "$env:AGENT_TOOLSDIRECTORY/x86/dotnet/dotnet.exe", "${env:ProgramFiles(x86)}\dotnet\dotnet.exe" 36 | $dotnet32Matches = $dotnet32Possibilities |? { Test-Path $_ } 37 | if ($dotnet32Matches) { 38 | $dotnet = Resolve-Path @($dotnet32Matches)[0] 39 | Write-Host "Running tests using `"$dotnet`"" -ForegroundColor DarkGray 40 | } else { 41 | Write-Error "Unable to find 32-bit dotnet.exe" 42 | return 1 43 | } 44 | } 45 | } 46 | 47 | $testBinLog = Join-Path $ArtifactStagingFolder (Join-Path build_logs test.binlog) 48 | $testDiagLog = Join-Path $ArtifactStagingFolder (Join-Path test_logs diag.log) 49 | 50 | & $dotnet test $RepoRoot ` 51 | --no-build ` 52 | -c $Configuration ` 53 | --filter "TestCategory!=FailsInCloudTest" ` 54 | --collect "Code Coverage;Format=cobertura" ` 55 | --settings "$PSScriptRoot/test.runsettings" ` 56 | --blame-hang-timeout 60s ` 57 | --blame-crash ` 58 | -bl:"$testBinLog" ` 59 | --diag "$testDiagLog;TraceLevel=info" ` 60 | --logger trx ` 61 | 62 | $unknownCounter = 0 63 | Get-ChildItem -Recurse -Path $RepoRoot\test\*.trx |% { 64 | Copy-Item $_ -Destination $ArtifactStagingFolder/test_logs/ 65 | 66 | if ($PublishResults) { 67 | $x = [xml](Get-Content -LiteralPath $_) 68 | $runTitle = $null 69 | if ($x.TestRun.TestDefinitions -and $x.TestRun.TestDefinitions.GetElementsByTagName('UnitTest')) { 70 | $storage = $x.TestRun.TestDefinitions.GetElementsByTagName('UnitTest')[0].storage -replace '\\','/' 71 | if ($storage -match '/(?net[^/]+)/(?:(?[^/]+)/)?(?[^/]+)\.(dll|exe)$') { 72 | if ($matches.rid) { 73 | $runTitle = "$($matches.lib) ($($matches.tfm), $($matches.rid), $Agent)" 74 | } else { 75 | $runTitle = "$($matches.lib) ($($matches.tfm)$x86RunTitleSuffix, $Agent)" 76 | } 77 | } 78 | } 79 | if (!$runTitle) { 80 | $unknownCounter += 1; 81 | $runTitle = "unknown$unknownCounter ($Agent$x86RunTitleSuffix)"; 82 | } 83 | 84 | Write-Host "##vso[results.publish type=VSTest;runTitle=$runTitle;publishRunAttachments=true;resultFiles=$_;failTaskOnFailedTests=true;testRunSystem=VSTS - PTR;]" 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /tools/publish-CodeCov.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Uploads code coverage to codecov.io 4 | .PARAMETER CodeCovToken 5 | Code coverage token to use 6 | .PARAMETER PathToCodeCoverage 7 | Path to root of code coverage files 8 | .PARAMETER Name 9 | Name to upload with codecoverge 10 | .PARAMETER Flags 11 | Flags to upload with codecoverge 12 | #> 13 | [CmdletBinding()] 14 | Param ( 15 | [Parameter(Mandatory=$true)] 16 | [string]$CodeCovToken, 17 | [Parameter(Mandatory=$true)] 18 | [string]$PathToCodeCoverage, 19 | [string]$Name, 20 | [string]$Flags 21 | ) 22 | 23 | $RepoRoot = (Resolve-Path "$PSScriptRoot/..").Path 24 | 25 | Get-ChildItem -Recurse -LiteralPath $PathToCodeCoverage -Filter "*.cobertura.xml" | % { 26 | $relativeFilePath = Resolve-Path -relative $_.FullName 27 | 28 | Write-Host "Uploading: $relativeFilePath" -ForegroundColor Yellow 29 | & (& "$PSScriptRoot/Get-CodeCovTool.ps1") -t $CodeCovToken -f $relativeFilePath -R $RepoRoot -F $Flags -n $Name 30 | } 31 | -------------------------------------------------------------------------------- /tools/test.runsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | \.dll$ 10 | \.exe$ 11 | 12 | 13 | xunit\..* 14 | 15 | 16 | 17 | 18 | ^System\.Diagnostics\.DebuggerHiddenAttribute$ 19 | ^System\.Diagnostics\.DebuggerNonUserCodeAttribute$ 20 | ^System\.CodeDom\.Compiler\.GeneratedCodeAttribute$ 21 | ^System\.Diagnostics\.CodeAnalysis\.ExcludeFromCodeCoverageAttribute$ 22 | 23 | 24 | 25 | 26 | True 27 | 28 | True 29 | 30 | True 31 | 32 | False 33 | 34 | False 35 | 36 | False 37 | 38 | True 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /tools/variables/BusinessGroupName.ps1: -------------------------------------------------------------------------------- 1 | 'Visual Studio - VS Core' 2 | -------------------------------------------------------------------------------- /tools/variables/DotNetSdkVersion.ps1: -------------------------------------------------------------------------------- 1 | $globalJson = Get-Content -LiteralPath "$PSScriptRoot\..\..\global.json" | ConvertFrom-Json 2 | $globalJson.sdk.version 3 | -------------------------------------------------------------------------------- /tools/variables/InsertJsonValues.ps1: -------------------------------------------------------------------------------- 1 | $vstsDropNames = & "$PSScriptRoot\VstsDropNames.ps1" 2 | $BuildConfiguration = $env:BUILDCONFIGURATION 3 | if (!$BuildConfiguration) { 4 | $BuildConfiguration = 'Debug' 5 | } 6 | 7 | $BasePath = "$PSScriptRoot\..\..\bin\Packages\$BuildConfiguration\Vsix" 8 | 9 | if (Test-Path $BasePath) { 10 | $vsmanFiles = @() 11 | Get-ChildItem $BasePath *.vsman -Recurse -File |% { 12 | $version = (Get-Content $_.FullName | ConvertFrom-Json).info.buildVersion 13 | $fn = $_.Name 14 | $vsmanFiles += "$fn{$version}=https://vsdrop.corp.microsoft.com/file/v1/$vstsDropNames;$fn" 15 | } 16 | 17 | [string]::join(',',$vsmanFiles) 18 | } 19 | -------------------------------------------------------------------------------- /tools/variables/InsertPropsValues.ps1: -------------------------------------------------------------------------------- 1 | $InsertedPkgs = (& "$PSScriptRoot\..\artifacts\VSInsertion.ps1") 2 | 3 | $icv=@() 4 | foreach ($kvp in $InsertedPkgs.GetEnumerator()) { 5 | $kvp.Value |% { 6 | if ($_.Name -match "^(.*?)\.(\d+\.\d+\.\d+(?:\.\d+)?(?:-.*?)?)(?:\.symbols)?\.nupkg$") { 7 | $id = $Matches[1] 8 | $version = $Matches[2] 9 | $icv += "$id=$version" 10 | } 11 | } 12 | } 13 | 14 | Write-Output ([string]::join(',',$icv)) 15 | -------------------------------------------------------------------------------- /tools/variables/InsertTargetBranch.ps1: -------------------------------------------------------------------------------- 1 | # This is the default branch of the VS repo that we will use to insert into VS. 2 | 'main' 3 | -------------------------------------------------------------------------------- /tools/variables/InsertVersionsValues.ps1: -------------------------------------------------------------------------------- 1 | $MacroName = 'MicrosoftVisualStudioValidationVersion' 2 | $SampleProject = "$PSScriptRoot\..\..\src\Microsoft.VisualStudio.Validation" 3 | [string]::join(',',(@{ 4 | ($MacroName) = & { (dotnet nbgv get-version --project $SampleProject --format json | ConvertFrom-Json).AssemblyVersion }; 5 | }.GetEnumerator() |% { "$($_.key)=$($_.value)" })) 6 | -------------------------------------------------------------------------------- /tools/variables/LocLanguages.ps1: -------------------------------------------------------------------------------- 1 | ## For faster PR/CI builds localize only for 2 languages, ENU and JPN provide good enough coverage 2 | if ($env:BUILD_REASON -eq 'PullRequest') { 3 | 'ENU,JPN' 4 | } else { 5 | 'VS' 6 | } 7 | -------------------------------------------------------------------------------- /tools/variables/ProfilingInputsDropName.ps1: -------------------------------------------------------------------------------- 1 | if ($env:SYSTEM_TEAMPROJECT) { 2 | "ProfilingInputs/$env:SYSTEM_TEAMPROJECT/$env:BUILD_REPOSITORY_NAME/$env:BUILD_SOURCEBRANCHNAME/$env:BUILD_BUILDID" 3 | } else { 4 | Write-Warning "No Azure Pipelines build detected. No Azure Pipelines drop name will be computed." 5 | } 6 | -------------------------------------------------------------------------------- /tools/variables/SymbolsFeatureName.ps1: -------------------------------------------------------------------------------- 1 | 'vs-validation' 2 | -------------------------------------------------------------------------------- /tools/variables/VstsDropNames.ps1: -------------------------------------------------------------------------------- 1 | "Products/$env:SYSTEM_TEAMPROJECT/$env:BUILD_REPOSITORY_NAME/$env:BUILD_SOURCEBRANCHNAME/$env:BUILD_BUILDID" 2 | -------------------------------------------------------------------------------- /tools/variables/_all.ps1: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env pwsh 2 | 3 | <# 4 | .SYNOPSIS 5 | This script returns a hashtable of build variables that should be set 6 | at the start of a build or release definition's execution. 7 | #> 8 | 9 | [CmdletBinding(SupportsShouldProcess = $true)] 10 | param ( 11 | ) 12 | 13 | $vars = @{} 14 | 15 | Get-ChildItem "$PSScriptRoot\*.ps1" -Exclude "_*" |% { 16 | Write-Host "Computing $($_.BaseName) variable" 17 | $vars[$_.BaseName] = & $_ 18 | } 19 | 20 | $vars 21 | -------------------------------------------------------------------------------- /tools/variables/_define.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | This script translates the variables returned by the _all.ps1 script 4 | into commands that instruct Azure Pipelines to actually set those variables for other pipeline tasks to consume. 5 | 6 | The build or release definition may have set these variables to override 7 | what the build would do. So only set them if they have not already been set. 8 | #> 9 | 10 | [CmdletBinding()] 11 | param ( 12 | ) 13 | 14 | (& "$PSScriptRoot\_all.ps1").GetEnumerator() |% { 15 | # Always use ALL CAPS for env var names since Azure Pipelines converts variable names to all caps and on non-Windows OS, env vars are case sensitive. 16 | $keyCaps = $_.Key.ToUpper() 17 | if ((Test-Path "env:$keyCaps") -and (Get-Content "env:$keyCaps")) { 18 | Write-Host "Skipping setting $keyCaps because variable is already set to '$(Get-Content env:$keyCaps)'." -ForegroundColor Cyan 19 | } else { 20 | Write-Host "$keyCaps=$($_.Value)" -ForegroundColor Yellow 21 | if ($env:TF_BUILD) { 22 | # Create two variables: the first that can be used by its simple name and accessible only within this job. 23 | Write-Host "##vso[task.setvariable variable=$keyCaps]$($_.Value)" 24 | # and the second that works across jobs and stages but must be fully qualified when referenced. 25 | Write-Host "##vso[task.setvariable variable=$keyCaps;isOutput=true]$($_.Value)" 26 | } elseif ($env:GITHUB_ACTIONS) { 27 | Add-Content -LiteralPath $env:GITHUB_ENV -Value "$keyCaps=$($_.Value)" 28 | } 29 | Set-Item -LiteralPath "env:$keyCaps" -Value $_.Value 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /version.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", 3 | "version": "17.13", 4 | "publicReleaseRefSpec": [ 5 | "^refs/heads/main$", 6 | "^refs/heads/v\\d+(?:\\.\\d+)?$" 7 | ] 8 | } 9 | --------------------------------------------------------------------------------