├── .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 | [](https://www.nuget.org/packages/Microsoft.VisualStudio.Validation)
5 | [](https://dev.azure.com/azure-public/vside/_build/latest?definitionId=11&branchName=main)
6 | [](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 | [](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 |
--------------------------------------------------------------------------------