diff --git a/.azuredevops/dependabot.yml b/.azuredevops/dependabot.yml new file mode 100644 index 00000000..4d848fb5 --- /dev/null +++ b/.azuredevops/dependabot.yml @@ -0,0 +1,9 @@ +# Please see the documentation for all configuration options: +# https://eng.ms/docs/products/dependabot/configuration/version_updates + +version: 2 +updates: +- package-ecosystem: nuget + directory: / + schedule: + interval: monthly diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json new file mode 100644 index 00000000..a333e70b --- /dev/null +++ b/.config/dotnet-tools.json @@ -0,0 +1,34 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "powershell": { + "version": "7.4.6", + "commands": [ + "pwsh" + ], + "rollForward": false + }, + "dotnet-coverage": { + "version": "17.12.6", + "commands": [ + "dotnet-coverage" + ], + "rollForward": false + }, + "nbgv": { + "version": "3.6.146", + "commands": [ + "nbgv" + ], + "rollForward": false + }, + "docfx": { + "version": "2.77.0", + "commands": [ + "docfx" + ], + "rollForward": false + } + } +} \ No newline at end of file diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 00000000..9626b31b --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,14 @@ +# Refer to https://hub.docker.com/_/microsoft-dotnet-sdk for available versions +FROM mcr.microsoft.com/dotnet/sdk:8.0.402-jammy + +# Installing mono makes `dotnet test` work without errors even for net472. +# But installing it takes a long time, so it's excluded by default. +#RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF +#RUN echo "deb https://download.mono-project.com/repo/ubuntu stable-bionic main" | tee /etc/apt/sources.list.d/mono-official-stable.list +#RUN apt-get update +#RUN DEBIAN_FRONTEND=noninteractive apt-get install -y mono-devel + +# Clear the NUGET_XMLDOC_MODE env var so xml api doc files get unpacked, allowing a rich experience in Intellisense. +# See https://github.com/dotnet/dotnet-docker/issues/2790 for a discussion on this, where the prioritized use case +# was *not* devcontainers, sadly. +ENV NUGET_XMLDOC_MODE= diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000..f4e3b31a --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,20 @@ +{ + "name": "Dev space", + "dockerFile": "Dockerfile", + "settings": { + "terminal.integrated.shell.linux": "/usr/bin/pwsh" + }, + "postCreateCommand": "./init.ps1 -InstallLocality machine", + "extensions": [ + "ms-azure-devops.azure-pipelines", + "ms-dotnettools.csharp", + "k--kato.docomment", + "editorconfig.editorconfig", + "pflannery.vscode-versionlens", + "davidanson.vscode-markdownlint", + "dotjoshjohnson.xml", + "ms-vscode-remote.remote-containers", + "ms-azuretools.vscode-docker", + "ms-vscode.powershell" + ] +} diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..b268b5ee --- /dev/null +++ b/.editorconfig @@ -0,0 +1,192 @@ +# EditorConfig is awesome:http://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Don't use tabs for indentation. +[*] +indent_style = space + +# (Please don't specify an indent_size here; that has too many unintended consequences.) + +[*.yml] +indent_size = 2 +indent_style = space + +# Code files +[*.{cs,csx,vb,vbx,h,cpp,idl}] +indent_size = 4 +insert_final_newline = true +trim_trailing_whitespace = true + +# MSBuild project files +[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj,msbuildproj,props,targets}] +indent_size = 2 + +# Xml config files +[*.{ruleset,config,nuspec,resx,vsixmanifest,vsct,runsettings}] +indent_size = 2 +indent_style = space + +# JSON files +[*.json] +indent_size = 2 +indent_style = space + +[*.ps1] +indent_style = space +indent_size = 4 + +# Dotnet code style settings: +[*.{cs,vb}] +# Sort using and Import directives with System.* appearing first +dotnet_sort_system_directives_first = true +dotnet_separate_import_directive_groups = false +dotnet_style_qualification_for_field = true:warning +dotnet_style_qualification_for_property = true:warning +dotnet_style_qualification_for_method = true:warning +dotnet_style_qualification_for_event = true:warning + +# Use language keywords instead of framework type names for type references +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion +dotnet_style_predefined_type_for_member_access = true:suggestion + +# Suggest more modern language features when available +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion + +# Non-private static fields are PascalCase +dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.symbols = non_private_static_fields +dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.style = non_private_static_field_style + +dotnet_naming_symbols.non_private_static_fields.applicable_kinds = field +dotnet_naming_symbols.non_private_static_fields.applicable_accessibilities = public, protected, internal, protected internal, private protected +dotnet_naming_symbols.non_private_static_fields.required_modifiers = static + +dotnet_naming_style.non_private_static_field_style.capitalization = pascal_case + +# Constants are PascalCase +dotnet_naming_rule.constants_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.constants_should_be_pascal_case.symbols = constants +dotnet_naming_rule.constants_should_be_pascal_case.style = constant_style + +dotnet_naming_symbols.constants.applicable_kinds = field, local +dotnet_naming_symbols.constants.required_modifiers = const + +dotnet_naming_style.constant_style.capitalization = pascal_case + +# Static fields are camelCase +dotnet_naming_rule.static_fields_should_be_camel_case.severity = suggestion +dotnet_naming_rule.static_fields_should_be_camel_case.symbols = static_fields +dotnet_naming_rule.static_fields_should_be_camel_case.style = static_field_style + +dotnet_naming_symbols.static_fields.applicable_kinds = field +dotnet_naming_symbols.static_fields.required_modifiers = static + +dotnet_naming_style.static_field_style.capitalization = camel_case + +# Instance fields are camelCase +dotnet_naming_rule.instance_fields_should_be_camel_case.severity = suggestion +dotnet_naming_rule.instance_fields_should_be_camel_case.symbols = instance_fields +dotnet_naming_rule.instance_fields_should_be_camel_case.style = instance_field_style + +dotnet_naming_symbols.instance_fields.applicable_kinds = field + +dotnet_naming_style.instance_field_style.capitalization = camel_case + +# Locals and parameters are camelCase +dotnet_naming_rule.locals_should_be_camel_case.severity = suggestion +dotnet_naming_rule.locals_should_be_camel_case.symbols = locals_and_parameters +dotnet_naming_rule.locals_should_be_camel_case.style = camel_case_style + +dotnet_naming_symbols.locals_and_parameters.applicable_kinds = parameter, local + +dotnet_naming_style.camel_case_style.capitalization = camel_case + +# Local functions are PascalCase +dotnet_naming_rule.local_functions_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.local_functions_should_be_pascal_case.symbols = local_functions +dotnet_naming_rule.local_functions_should_be_pascal_case.style = local_function_style + +dotnet_naming_symbols.local_functions.applicable_kinds = local_function + +dotnet_naming_style.local_function_style.capitalization = pascal_case + +# By default, name items with PascalCase +dotnet_naming_rule.members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.members_should_be_pascal_case.symbols = all_members +dotnet_naming_rule.members_should_be_pascal_case.style = pascal_case_style + +dotnet_naming_symbols.all_members.applicable_kinds = * + +dotnet_naming_style.pascal_case_style.capitalization = pascal_case + +# CSharp code style settings: +[*.cs] +# Indentation preferences +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_switch_labels = true +csharp_indent_labels = flush_left + +# Prefer "var" everywhere +csharp_style_var_for_built_in_types = false +csharp_style_var_when_type_is_apparent = true:suggestion +csharp_style_var_elsewhere = false:warning + +# Prefer method-like constructs to have a block body +csharp_style_expression_bodied_methods = false:none +csharp_style_expression_bodied_constructors = false:none +csharp_style_expression_bodied_operators = false:none + +# Prefer property-like constructs to have an expression-body +csharp_style_expression_bodied_properties = true:none +csharp_style_expression_bodied_indexers = true:none +csharp_style_expression_bodied_accessors = true:none + +# Suggest more modern language features when available +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +csharp_style_throw_expression = true:suggestion +csharp_style_conditional_delegate_call = true:suggestion + +# Newline settings +csharp_new_line_before_open_brace = all +csharp_new_line_before_else = true +csharp_new_line_before_catch = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_members_in_anonymous_types = true + +# Blocks are allowed +csharp_prefer_braces = true:silent + +# SA1130: Use lambda syntax +dotnet_diagnostic.SA1130.severity = silent + +# IDE1006: Naming Styles - StyleCop handles these for us +dotnet_diagnostic.IDE1006.severity = none + +dotnet_diagnostic.DOC100.severity = silent +dotnet_diagnostic.DOC104.severity = warning +dotnet_diagnostic.DOC105.severity = warning +dotnet_diagnostic.DOC106.severity = warning +dotnet_diagnostic.DOC107.severity = warning +dotnet_diagnostic.DOC108.severity = warning +dotnet_diagnostic.DOC200.severity = warning +dotnet_diagnostic.DOC202.severity = warning + +# CA1062: Validate arguments of public methods +dotnet_diagnostic.CA1062.severity = warning + +# CA2016: Forward the CancellationToken parameter +dotnet_diagnostic.CA2016.severity = warning + +[*.sln] +indent_style = tab diff --git a/.gitattributes b/.gitattributes index 1ff0c423..1f35e683 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3,6 +3,13 @@ ############################################################################### * text=auto +# Ensure shell scripts use LF line endings (linux only accepts LF) +*.sh eol=lf +*.ps1 eol=lf + +# The macOS codesign tool is extremely picky, and requires LF line endings. +*.plist eol=lf + ############################################################################### # Set default behavior for command prompt diff. # @@ -17,7 +24,7 @@ # # Merging from the command prompt will add diff markers to the files if there # are conflicts (Merging from VS is not affected by the settings below, in VS -# the diff markers are never inserted). Diff markers may cause the following +# the diff markers are never inserted). Diff markers may cause the following # file extensions to fail to load in VS. An alternative would be to treat # these files as binary and thus will always conflict and require user # intervention with every merge. To do so, just uncomment the entries below @@ -46,9 +53,9 @@ ############################################################################### # diff behavior for common document formats -# +# # Convert binary document formats to text before diffing them. This feature -# is only available from the command line. Turn it on by uncommenting the +# is only available from the command line. Turn it on by uncommenting the # entries below. ############################################################################### #*.doc diff=astextplain diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..63e3e890 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,9 @@ +# Please see the documentation for all configuration options: +# https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file + +version: 2 +updates: +- package-ecosystem: nuget + directory: / + schedule: + interval: weekly diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..4ccf1187 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,118 @@ +name: CI + +on: + push: + branches: + - main + - microbuild + - validate/* + pull_request: + +env: + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true + BUILDCONFIGURATION: Release + # codecov_token: 4dc9e7e2-6b01-4932-a180-847b52b43d35 # Get a new one from https://codecov.io/ + NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages/ + +jobs: + build: + + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: + - windows-2022 + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # avoid shallow clone so nbgv can do its work. + - name: โš™ Install prerequisites + run: | + ./init.ps1 -UpgradePrerequisites + dotnet --info + + # Print mono version if it is present. + if (Get-Command mono -ErrorAction SilentlyContinue) { + mono --version + } + shell: pwsh + - name: โš™๏ธ Set pipeline variables based on source + run: azure-pipelines/variables/_pipelines.ps1 + shell: pwsh + - name: ๐Ÿ›  build + run: dotnet build src -t:build,pack --no-restore -c ${{ env.BUILDCONFIGURATION }} /bl:"${{ runner.temp }}/_artifacts/build_logs/build.binlog" + - name: ๐Ÿงช test + run: azure-pipelines/dotnet-test-cloud.ps1 -Configuration ${{ env.BUILDCONFIGURATION }} -Agent ${{ runner.os }} + shell: pwsh + - name: ๐Ÿ’…๐Ÿป Verify formatted code + run: dotnet format --verify-no-changes --no-restore + shell: pwsh + if: runner.os == 'Linux' + - name: โš™ Update pipeline variables based on build outputs + run: azure-pipelines/variables/_pipelines.ps1 + shell: pwsh + - name: ๐Ÿ“ฅ Collect artifacts + run: azure-pipelines/artifacts/_stage_all.ps1 + shell: pwsh + if: always() + - name: ๐Ÿ“ข Upload project.assets.json files + if: always() + uses: actions/upload-artifact@v4 + with: + name: projectAssetsJson-${{ runner.os }} + path: ${{ runner.temp }}/_artifacts/projectAssetsJson + continue-on-error: true + - name: ๐Ÿ“ข Upload variables + uses: actions/upload-artifact@v4 + with: + name: variables-${{ runner.os }} + path: ${{ runner.temp }}/_artifacts/Variables + continue-on-error: true + - name: ๐Ÿ“ข Upload build_logs + if: always() + uses: actions/upload-artifact@v4 + with: + name: build_logs-${{ runner.os }} + path: ${{ runner.temp }}/_artifacts/build_logs + continue-on-error: true + - name: ๐Ÿ“ข Upload test_logs + if: always() + uses: actions/upload-artifact@v4 + with: + name: test_logs-${{ runner.os }} + path: ${{ runner.temp }}/_artifacts/test_logs + continue-on-error: true + - name: ๐Ÿ“ข Upload testResults + if: always() + uses: actions/upload-artifact@v4 + with: + name: testResults-${{ runner.os }} + path: ${{ runner.temp }}/_artifacts/testResults + continue-on-error: true + - name: ๐Ÿ“ข Upload coverageResults + if: always() + uses: actions/upload-artifact@v4 + with: + name: coverageResults-${{ runner.os }} + path: ${{ runner.temp }}/_artifacts/coverageResults + continue-on-error: true + - name: ๐Ÿ“ข Upload symbols + uses: actions/upload-artifact@v4 + with: + name: symbols-${{ runner.os }} + path: ${{ runner.temp }}/_artifacts/symbols + continue-on-error: true + - name: ๐Ÿ“ข Upload deployables + uses: actions/upload-artifact@v4 + with: + name: deployables-${{ runner.os }} + path: ${{ runner.temp }}/_artifacts/deployables + if: always() + - name: ๐Ÿ“ข Publish code coverage results to codecov.io + run: ./azure-pipelines/publish-CodeCov.ps1 -CodeCovToken "${{ env.codecov_token }}" -PathToCodeCoverage "${{ runner.temp }}/_artifacts/coverageResults" -Name "${{ runner.os }} Coverage Results" -Flags "${{ runner.os }}" + shell: pwsh + timeout-minutes: 3 + continue-on-error: true + if: env.codecov_token != '' diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 00000000..6e6e64a0 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,41 @@ +name: ๐Ÿ“š Docs + +on: + push: + branches: + - main + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + actions: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: pages + cancel-in-progress: false + +jobs: + publish-docs: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: โš™ Install prerequisites + run: ./init.ps1 -UpgradePrerequisites + + - run: dotnet docfx docfx/docfx.json + name: ๐Ÿ“š Generate documentation + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: docfx/_site + + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/libtemplate-update.yml b/.github/workflows/libtemplate-update.yml new file mode 100644 index 00000000..0831ced6 --- /dev/null +++ b/.github/workflows/libtemplate-update.yml @@ -0,0 +1,98 @@ +name: Library.Template update + +# PREREQUISITE: This workflow requires the repo to be configured to allow workflows to create pull requests. +# Visit https://github.com/USER/REPO/settings/actions +# Under "Workflow permissions" check "Allow GitHub Actions to create ...pull requests" +# Click Save. + +on: + schedule: + - cron: "0 3 * * Mon" # Sun @ 8 or 9 PM Mountain Time (depending on DST) + workflow_dispatch: + +jobs: + merge: + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # avoid shallow clone so nbgv can do its work. + + - name: merge + id: merge + shell: pwsh + run: | + $LibTemplateBranch = & ./azure-pipelines/Get-LibTemplateBasis.ps1 -ErrorIfNotRelated + if ($LASTEXITCODE -ne 0) { + exit $LASTEXITCODE + } + + git fetch https://github.com/aarnott/Library.Template $LibTemplateBranch + if ($LASTEXITCODE -ne 0) { + exit $LASTEXITCODE + } + $LibTemplateCommit = git rev-parse FETCH_HEAD + git diff --stat ...FETCH_HEAD + + if ((git rev-list FETCH_HEAD ^HEAD --count) -eq 0) { + Write-Host "There are no Library.Template updates to merge." + echo "uptodate=true" >> $env:GITHUB_OUTPUT + exit 0 + } + + # Pushing commits that add or change files under .github/workflows will cause our workflow to fail. + # But it usually isn't necessary because the target branch already has (or doesn't have) these changes. + # So if the merged doesn't bring in any changes to these files, try the merge locally and push that + # to keep github happy. + if ((git rev-list FETCH_HEAD ^HEAD --count -- .github/workflows) -eq 0) { + # Indeed there are no changes in that area. So merge locally to try to appease GitHub. + git checkout -b auto/libtemplateUpdate + git config user.name "Andrew Arnott" + git config user.email "andrewarnott@live.com" + git merge FETCH_HEAD + if ($LASTEXITCODE -ne 0) { + Write-Host "Merge conflicts prevent creating the pull request. Please run tools/MergeFrom-Template.ps1 locally and push the result as a pull request." + exit 2 + } + + git -c http.extraheader="AUTHORIZATION: bearer $env:GH_TOKEN" push origin -u HEAD + } else { + 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." + exit 1 + } + - name: pull request + shell: pwsh + if: success() && steps.merge.outputs.uptodate != 'true' + run: | + # If there is already an active pull request, don't create a new one. + $existingPR = gh pr list -H auto/libtemplateUpdate --json url | ConvertFrom-Json + if ($existingPR) { + Write-Host "::warning::Skipping pull request creation because one already exists at $($existingPR[0].url)" + exit 0 + } + + $prTitle = "Merge latest Library.Template" + $prBody = "This merges the latest features and fixes from [Library.Template's branch](https://github.com/AArnott/Library.Template/tree/). + + โš ๏ธ Do **not** squash this pull request when completing it. You must *merge* it." + +
+ Merge conflicts? + Resolve merge conflicts locally by carrying out these steps: + + ``` + git fetch + git checkout auto/libtemplateUpdate + git merge origin/main + # resolve conflicts + git commit + git push + ``` +
+ + gh pr create -H auto/libtemplateUpdate -b $prBody -t $prTitle + env: + GH_TOKEN: ${{ github.token }} diff --git a/.gitignore b/.gitignore index f1e3d20e..cc2b1247 100644 --- a/.gitignore +++ b/.gitignore @@ -1,15 +1,23 @@ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore # User-specific files +*.rsuser *.suo *.user *.userosscache *.sln.docstates +*.lutconfig +launchSettings.json # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs +# Mono auto generated files +mono_crash.* + # Build results [Dd]ebug/ [Dd]ebugPublic/ @@ -17,50 +25,66 @@ [Rr]eleases/ x64/ x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ bld/ [Bb]in/ [Oo]bj/ [Ll]og/ -# Visual Studio 2015 cache/options directory +# Visual Studio 2015/2017 cache/options directory .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ +# Jetbrains Rider cache directory +.idea/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* -# NUNIT +# NUnit *.VisualState.xml TestResult.xml +nunit-*.xml # Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c -# DNX -project.lock.json -artifacts/ +# Benchmark Results +BenchmarkDotNet.Artifacts/ +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio *_i.c *_p.c -*_i.h +*_h.h *.ilk *.meta *.obj +*.iobj *.pch *.pdb +*.ipdb *.pgc *.pgd *.rsp +!Directory.Build.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj +*_wpftmp.csproj *.log *.vspscc *.vssscc @@ -89,6 +113,9 @@ ipch/ *.vspx *.sap +# Visual Studio Trace Files +*.e2e + # TFS 2012 Local Workspace $tf/ @@ -109,6 +136,15 @@ _TeamCity* # DotCover is a Code Coverage Tool *.dotCover +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml +/coveragereport/ + # NCrunch _NCrunch_* .*crunch*.local.xml @@ -140,7 +176,7 @@ publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml -# TODO: Comment the next line if you want to checkin your web deploy settings +# Note: Comment the next line if you want to checkin your web deploy settings, # but database connection strings (with potential passwords) will be unencrypted *.pubxml *.publishproj @@ -152,13 +188,15 @@ PublishScripts/ # NuGet Packages *.nupkg +# NuGet Symbol Packages +*.snupkg # The packages folder can be ignored because of Package Restore -**/packages/* +**/[Pp]ackages/* # except build/, which is used as an MSBuild target. -!**/packages/build/ +!**/[Pp]ackages/build/ # Uncomment if necessary however generally it will be regenerated when needed -#!**/packages/repositories.config -# NuGet v3's project.json files produces more ignoreable files +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files *.nuget.props *.nuget.targets @@ -175,12 +213,15 @@ AppPackages/ BundleArtifacts/ Package.StoreAssociation.xml _pkginfo.txt +*.appx +*.appxbundle +*.appxupload # Visual Studio cache files # files ending in .cache can be ignored *.[Cc]ache # but keep track of directories ending in .cache -!*.[Cc]ache/ +!?*.[Cc]ache/ # Others ClientBin/ @@ -188,11 +229,15 @@ ClientBin/ *~ *.dbmdl *.dbproj.schemaview +*.jfm *.pfx *.publishsettings -node_modules/ orleans.codegen.cs +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + # Since there are multiple workflows, uncomment next line to ignore bower_components # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) #bower_components/ @@ -207,15 +252,22 @@ _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak # SQL Server files *.mdf *.ldf +*.ndf # Business Intelligence projects *.rdl.data *.bim.layout *.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl # Microsoft Fakes FakesAssemblies/ @@ -225,6 +277,7 @@ FakesAssemblies/ # Node.js Tools for Visual Studio .ntvs_analysis.dat +node_modules/ # Visual Studio 6 build log *.plg @@ -232,6 +285,9 @@ FakesAssemblies/ # Visual Studio 6 workspace options file *.opt +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + # Visual Studio LightSwitch build output **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts @@ -247,6 +303,58 @@ paket-files/ # FAKE - F# Make .fake/ -# JetBrains Rider -.idea/ -*.sln.iml +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# dotnet tool local install directory +.store/ + +# mac-created file to track user view preferences for a directory +.DS_Store + +# Analysis results +*.sarif diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000..acaf0213 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,20 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. + // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp + // List of extensions which should be recommended for users of this workspace. + "recommendations": [ + "ms-azure-devops.azure-pipelines", + "ms-dotnettools.csharp", + "k--kato.docomment", + "editorconfig.editorconfig", + "esbenp.prettier-vscode", + "pflannery.vscode-versionlens", + "davidanson.vscode-markdownlint", + "dotjoshjohnson.xml", + "ms-vscode-remote.remote-containers", + "ms-azuretools.vscode-docker", + "tintoy.msbuild-project-tools" + ], + // List of extensions recommended by VS Code that should not be recommended for users of this workspace. + "unwantedRecommendations": [] +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..a6e4859c --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,14 @@ +{ + // Use IntelliSense to find out which attributes exist for C# debugging + // Use hover for the description of the existing attributes + // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md + "version": "0.2.0", + "configurations": [ + { + "name": ".NET Core Attach", + "type": "coreclr", + "request": "attach", + "processId": "${command:pickProcess}" + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..aa4ef023 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,24 @@ +{ + "files.trimTrailingWhitespace": true, + "files.insertFinalNewline": true, + "files.trimFinalNewlines": true, + "azure-pipelines.1ESPipelineTemplatesSchemaFile": true, + "omnisharp.enableEditorConfigSupport": true, + "omnisharp.enableRoslynAnalyzers": true, + "dotnet.completion.showCompletionItemsFromUnimportedNamespaces": true, + "editor.formatOnSave": true, + "[xml]": { + "editor.wordWrap": "off" + }, + // Treat these files as Azure Pipelines files + "files.associations": { + "**/azure-pipelines/**/*.yml": "azure-pipelines", + "azure-pipelines.yml": "azure-pipelines" + }, + // Use Prettier as the default formatter for Azure Pipelines files. + // Needs to be explicitly configured: https://github.com/Microsoft/azure-pipelines-vscode#document-formatting + "[azure-pipelines]": { + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnSave": false // enable this when they conform + }, +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 00000000..67b06180 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,17 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + } + ] +} diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..f9ba8cf6 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,9 @@ +# Microsoft Open Source Code of Conduct + +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). + +Resources: + +- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) +- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) +- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 00000000..a094cfdf --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,59 @@ + + + + Debug + $(MSBuildThisFileDirectory) + $(RepoRootPath)obj\$([MSBuild]::MakeRelative($(RepoRootPath), $(MSBuildProjectDirectory)))\ + $(RepoRootPath)bin\$(MSBuildProjectName)\ + $(RepoRootPath)bin\Packages\$(Configuration)\NuGet\ + $(RepoRootPath)bin\Packages\$(Configuration)\Vsix\$(Platform)\ + enable + enable + latest + true + true + true + + + true + + + + false + + + $(MSBuildThisFileDirectory) + + + embedded + + https://github.com/microsoft/json-document-transforms + Microsoft + Microsoft + ยฉ Microsoft Corporation. All rights reserved. + MIT + true + true + true + snupkg + + + + + + + + + + + + + + + + + + $(RepositoryUrl)/releases/tag/v$(Version) + + + diff --git a/Directory.Build.rsp b/Directory.Build.rsp new file mode 100644 index 00000000..9a833a03 --- /dev/null +++ b/Directory.Build.rsp @@ -0,0 +1,16 @@ +#------------------------------------------------------------------------------ +# This file contains command-line options that MSBuild will process as part of +# every build, unless the "/noautoresponse" switch is specified. +# +# MSBuild processes the options in this file first, before processing the +# options on the command line. As a result, options on the command line can +# override the options in this file. However, depending on the options being +# set, the overriding can also result in conflicts. +# +# NOTE: The "/noautoresponse" switch cannot be specified in this file, nor in +# any response file that is referenced by this file. +#------------------------------------------------------------------------------ +/nr:false +/m +/verbosity:minimal +/clp:Summary;ForceNoAlign diff --git a/Directory.Build.targets b/Directory.Build.targets new file mode 100644 index 00000000..27b0fad1 --- /dev/null +++ b/Directory.Build.targets @@ -0,0 +1,13 @@ + + + + 13 + 16.9 + + + + + + + + diff --git a/Directory.Packages.props b/Directory.Packages.props new file mode 100644 index 00000000..62383d30 --- /dev/null +++ b/Directory.Packages.props @@ -0,0 +1,26 @@ + + + + + true + true + 2.0.171 + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/LICENSE b/LICENSE index fdf12460..85c615ef 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,5 @@ JSON Document Transforms - + MIT License Copyright (c) Microsoft Corporation. All rights reserved. diff --git a/SECURITY.md b/SECURITY.md index 869fdfe2..0dc4b6a7 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,20 +1,20 @@ - + ## Security Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). -If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. +If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/previous-versions/tn-archive/cc751383(v=technet.10)), please report it to us as described below. ## Reporting Security Issues **Please do not report security vulnerabilities through public GitHub issues.** -Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). +Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report). -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://aka.ms/opensource/security/pgpkey). +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). -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://aka.ms/opensource/security/msrc). +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). 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: @@ -28,7 +28,7 @@ Please include the requested information listed below (as much as you can provid This information will help us triage your report more quickly. -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://aka.ms/opensource/security/bounty) page for more details about our active programs. +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. ## Preferred Languages @@ -36,6 +36,6 @@ We prefer all communications to be in English. ## Policy -Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). +Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/msrc/cvd). diff --git a/SUPPORT.md b/SUPPORT.md new file mode 100644 index 00000000..9bf7fd97 --- /dev/null +++ b/SUPPORT.md @@ -0,0 +1,13 @@ +# Support + +## How to file issues and get help + +This project uses GitHub Issues to track bugs and feature requests. Please search the existing +issues before filing new issues to avoid duplicates. For new issues, file your bug or +feature request as a new Issue. + +For help and questions about using this project, please create an issue. + +## Microsoft Support Policy + +Support for this **json-document-transforms** is limited to the resources listed above. diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 549bf520..79f69c22 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1,252 +1,34 @@ trigger: + batch: true branches: include: - main + - microbuild + - 'validate/*' paths: exclude: - - .github - - doc + - doc/ - '*.md' -schedules: -- cron: "0 10 * * Sun" - displayName: Weekly api-scan - always: true - branches: - include: - - main + - .vscode/ + - .github/ + - azure-pipelines/release.yml + parameters: -- name: RunApiScanTools - displayName: Run API Scan? +- name: EnableMacOSBuild + displayName: Build on macOS type: boolean - default: false -variables: - NugetSecurityAnalysisWarningLevel: none - ${{ if or(eq(parameters.RunApiScanTools, 'true'), eq(variables['Build.CronSchedule.DisplayName'], 'Weekly api-scan')) }}: - RunAPIScan: true - ${{ else }}: - RunAPIScan: false - Codeql.Enabled: true - Codeql.TSAEnabled: true - Codeql.TSAOptionsPath: $(Build.SourcesDirectory)\azure-pipelines\TSAOptions.json + default: false # macOS is often bogged down in Azure Pipelines +- name: RunTests + displayName: Run tests + type: boolean + default: true -resources: - repositories: - - repository: MicroBuildTemplate - type: git - name: 1ESPipelineTemplates/MicroBuildTemplate - ref: refs/tags/release +variables: +- template: /azure-pipelines/BuildStageVariables.yml@self -extends: - template: azure-pipelines/MicroBuild.1ES.Official.yml@MicroBuildTemplate +jobs: +- template: azure-pipelines/build.yml parameters: - sdl: - sourceAnalysisPool: - name: VSEngSS-MicroBuild2022-1ES - image: server2022-microbuildVS2022-1es - - pool: - name: VSEngSS-MicroBuild2022-1ES - image: server2022-microbuildVS2022-1es - os: windows - customBuildTags: - - ES365AIMigrationTooling - stages: - - stage: stage - jobs: - - job: job - templateContext: - mb: - signing: - enabled: true - signType: $(SignType) - zipSources: false - feedSource: 'https://pkgs.dev.azure.com/devdiv/_packaging/MicroBuildToolset/nuget/v3/index.json' # Optional parameter: Artifact feed for outside DevDiv. - azureSubscription: 'MicroBuild Signing Task (DevDiv)' # Optional parameter: Microbuild Service Connection. - outputs: - # - output: pipelineArtifact - # displayName: 'Publish Artifact: ESRP signing logs' - # condition: eq(variables['SignType'], 'real') - # targetPath: $(Build.ArtifactStagingDirectory)/MicroBuild/ESRPClient - # artifactName: esrpclient_logs - # artifactType: Container - - output: pipelineArtifact - displayName: 'Publish Artifact: build logs' - condition: succeededOrFailed() - targetPath: $(Build.ArtifactStagingDirectory)/build_logs - artifactName: build_logs - artifactType: Container - - output: pipelineArtifact - displayName: 'Publish Artifact: symbols' - condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) - targetPath: '$(Build.ArtifactStagingDirectory)/symbols' - artifactName: symbols - publishLocation: Container - - output: pipelineArtifact - displayName: 'Publish packages' - condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) - targetPath: $(Build.ArtifactStagingDirectory)/packages - artifactName: packages - artifactType: Container - - output: nuget - displayName: 'Publish Sdk NuGet packages to VSTS feeds' - condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) - packageParentPath: '$(Build.ArtifactStagingDirectory)' - searchPatternPush: 'bin/$(BuildConfiguration)/packages/*.nupkg' - publishVstsFeed: $(feedGuid) - allowPackageConflicts: true - steps: - - checkout: self - fetchDepth: 0 - - task: ComponentGovernanceComponentDetection@0 - inputs: - scanType: 'Register' - verbosity: 'Verbose' - alertWarningLevel: 'High' - - task: PowerShell@2 - displayName: Set VSTS variables - inputs: - targetType: inline - script: | - if ($env:SignType -eq 'Real') { - $feedGuid = '09d8d03c-1ac8-456e-9274-4d2364527d99' ## VSIDE-RealSigned-Release - } else { - $feedGuid = 'da484c78-f942-44ef-b197-99e2a1bef53c' ## VSIDE-TestSigned-Release - } - Write-Host "##vso[task.setvariable variable=feedGuid]$feedGuid" - $SkipPublishingNetworkArtifacts = 'true' ## Network artifacts not allowed on Scale Set Pool - Write-Host "##vso[task.setvariable variable=SkipPublishingNetworkArtifacts]$SkipPublishingNetworkArtifacts" - if ($env:ComputerName.StartsWith('factoryvm', [StringComparison]::OrdinalIgnoreCase)) { - Write-Host "Running on hosted queue" - Write-Host "##vso[task.setvariable variable=Hosted]true" - } - - task: CmdLine@2 - inputs: - script: | - del /s /q "bin" - displayName: Purge bin - - task: NuGetToolInstaller@0 - displayName: Pin nuget.exe version - inputs: - versionSpec: 6.4.0 - - task: NuGetAuthenticate@1 - displayName: 'NuGet Authenticate' - inputs: - forceReinstallCredentialProvider: true - - task: VSBuild@1 - inputs: - vsVersion: 15.0 - solution: 'src\jdt.sln' - msbuildArgs: /t:Restore - platform: $(BuildPlatform) - configuration: $(BuildConfiguration) - displayName: Restore jdt solution - - task: VSBuild@1 - inputs: - vsVersion: 15.0 - solution: 'src\jdt.sln' - msbuildArgs: '/bl:"$(Build.ArtifactStagingDirectory)/build_logs/jdt.binlog"' - platform: $(BuildPlatform) - configuration: $(BuildConfiguration) - displayName: Build jdt solution - - task: MicroBuildCodesignVerify@3 - inputs: - TargetFolders: | - $(Build.SourcesDirectory)\bin\$(BuildConfiguration)\packages - ApprovalListPathForCerts: $(Build.SourcesDirectory)\src\build\no_authenticode.txt - ApprovalListPathForSigs: $(Build.SourcesDirectory)\src\build\no_strongname.txt - displayName: Verify code signing - condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) - - task: VSTest@2 - inputs: - testFiltercriteria: TestCategory!=FailsInCloudTest - searchFolder: $(System.DefaultWorkingDirectory)\bin\ - testAssemblyVer2: | - $(BuildConfiguration)\**\net472\*test*.dll - !**\obj\** - platform: $(BuildPlatform) - configuration: $(BuildConfiguration) - diagnosticsEnabled: true - displayName: Run Tests - condition: and(succeeded(), ne(variables['SignType'], 'real')) - - task: AntiMalware@4 - displayName: 'Run MpCmdRun.exe' - inputs: - InputType: Basic - ScanType: CustomScan - FileDirPath: '$(Build.StagingDirectory)' - DisableRemediation: false - - task: PoliCheck@2 - displayName: 'Run PoliCheck' - inputs: - targetType: F - condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) - - task: ManifestGeneratorTask@0 - inputs: - BuildDropPath: $(Build.ArtifactStagingDirectory)/build_logs - - task: BinSkim@4 - displayName: Run BinSkim - inputs: - InputType: 'Basic' - Function: 'analyze' - TargetPattern: 'guardianGlob' - AnalyzeTargetGlob: 'bin/$(BuildConfiguration)/net472/Microsoft.VisualStudio.Jdt*.dll;' - - task: CopyFiles@2 - displayName: 'Copy Files for APIScan' - inputs: - SourceFolder: 'bin/$(BuildConfiguration)/net472/' - Contents: | - **/Microsoft.VisualStudio.Jdt*.dll - **/Microsoft.VisualStudio.Jdt*.pdb - TargetFolder: $(Agent.TempDirectory)\APIScanFiles - condition: and(succeeded(), eq(variables['RunApiScan'], 'true')) - - task: APIScan@2 - displayName: Run APIScan - inputs: - softwareFolder: $(Agent.TempDirectory)\APIScanFiles - softwareName: 'json-document-transform' - softwareVersionNum: '$(Build.BuildId)' - isLargeApp: false - toolVersion: 'Latest' - condition: and(succeeded(), eq(variables['RunApiScan'], 'true')) - env: - AzureServicesAuthConnectionString: RunAs=App;AppId=$(ApiScanClientId) - - task: PublishSecurityAnalysisLogs@3 - displayName: 'Publish Guardian Artifacts' - inputs: - ArtifactName: CodeAnalysisLogs - ArtifactType: Container - PublishProcessedResults: false - AllTools: true - - task: TSAUpload@2 - displayName: 'Create bugs for APIScan' - inputs: - GdnPublishTsaOnboard: true - GdnPublishTsaConfigFile: '$(Build.SourcesDirectory)\azure-pipelines\TSAOptions.json' - condition: eq(variables['RunApiScan'], 'true') - - task: CopyFiles@1 - displayName: Collecting symbols artifacts - inputs: - SourceFolder: bin/$(BuildConfiguration)/net472 - Contents: | - **/Microsoft.VisualStudio.Jdt?(*.dll|*.pdb|*.xml) - !**/*Test* - TargetFolder: $(Build.ArtifactStagingDirectory)/symbols - condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) - - - task: MicroBuildArchiveSymbols@5 - displayName: ๐Ÿ”ฃ Archive symbols to Symweb - inputs: - SymbolsFeatureName: $(SymbolsFeatureName) - SymbolsProject: VS - condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest'), eq(variables['SignType'], 'real')) - - - - task: CopyFiles@1 - displayName: Collecting packages - inputs: - SourceFolder: bin/$(BuildConfiguration)/packages - Contents: | - *.nupkg - TargetFolder: $(Build.ArtifactStagingDirectory)/packages - flattenFolders: false - condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) \ No newline at end of file + Is1ESPT: false + EnableMacOSBuild: ${{ parameters.EnableMacOSBuild }} + RunTests: ${{ parameters.RunTests }} diff --git a/azure-pipelines/Archive-SourceCode.ps1 b/azure-pipelines/Archive-SourceCode.ps1 new file mode 100644 index 00000000..0360a14f --- /dev/null +++ b/azure-pipelines/Archive-SourceCode.ps1 @@ -0,0 +1,234 @@ +#Requires -PSEdition Core -Version 7 +<# +.SYNOPSIS + Submits a source archival request for this repo. +.PARAMETER Requester + The alias for the user requesting this backup. +.PARAMETER ManagerAlias + The alias of the manager that owns the repo. +.PARAMETER TeamAlias + The alias of the team that owns the repo. +.PARAMETER BusinessGroupName + A human-readable title for your team or business group. +.PARAMETER ProductionType +.PARAMETER ReleaseType + The type of release being backed up. +.PARAMETER ReleaseDate + The date of the release of your software. Defaults to today. +.PARAMETER OwnerAlias + The alias of the owner. +.PARAMETER OS +.PARAMETER ProductLanguage + One or more languages. +.PARAMETER Notes + Any notes to record with the backup. +.PARAMETER FileCollection + One or more collections to archive. +.PARAMETER ProductName + The name of the product. This will default to the repository name. +.PARAMETER RepoUrl + The URL to the repository. This will default to the repository containing this script. +.PARAMETER BackupType + The kind of backup to be performed. +.PARAMETER ServerPath + The UNC path to the server to be backed up (if applicable). +.PARAMETER SourceCodeArchivalUri + The URI to POST the source code archival request to. + This value will typically come automatically by a variable group associated with your pipeline. + You can also look it up at https://dpsopsrequestforms.azurewebsites.net/#/help -> SCA Request Help -> SCA API Help -> Description +#> +[CmdletBinding(SupportsShouldProcess = $true, PositionalBinding = $false)] +param ( + [Parameter()] + [string]$Requester, + [Parameter(Mandatory = $true)] + [string]$ManagerAlias, + [Parameter(Mandatory = $true)] + [string]$TeamAlias, + [Parameter(Mandatory = $true)] + [string]$BusinessGroupName, + [Parameter()] + [string]$ProductionType = 'Visual Studio', + [Parameter()] + [string]$ReleaseType = 'RTW', + [Parameter()] + [DateTime]$ReleaseDate = [DateTime]::Today, + [Parameter()] + [string]$OwnerAlias, + [Parameter()] + [ValidateSet('64-Bit Win', '32-Bit Win', 'Linux', 'Mac', '64-Bit ARM', '32-Bit ARM')] + [string[]]$OS = @('64-Bit Win'), + [Parameter(Mandatory = $true)] + [ValidateSet('English', 'Chinese Simplified', 'Chinese Traditional', 'Czech', 'French', 'German', 'Italian', 'Japanese', 'Korean', 'Polish', 'Portuguese', 'Russian', 'Spanish', 'Turkish')] + [string[]]$ProductLanguage, + [Parameter()] + [string]$Notes = '', + [Parameter()] + [ValidateSet('Binaries', 'Localization', 'Source Code')] + [string[]]$FileCollection = @('Source Code'), + [Parameter()] + [string]$ProductName, + [Parameter()] + [Uri]$RepoUrl, + [Parameter()] + [ValidateSet('Server Path', 'Code Repo(Git URL/AzureDevOps)', 'Git', 'Azure Storage Account')] + [string]$BackupType = 'Code Repo(Git URL/AzureDevOps)', + [Parameter()] + [string]$ServerPath = '', + [Parameter()] + [Uri]$SourceCodeArchivalUri = $env:SOURCECODEARCHIVALURI, + [Parameter(Mandatory = $true)] + [string]$AccessToken +) + +function Invoke-Git() { + # Make sure we invoke git from within the repo. + Push-Location $PSScriptRoot + try { + return (git $args) + } + finally { + Pop-Location + } +} + +if (!$ProductName) { + if ($env:BUILD_REPOSITORY_NAME) { + Write-Verbose 'Using $env:BUILD_REPOSITORY_NAME for ProductName.' # single quotes are intentional so user sees the name of env var. + $ProductName = $env:BUILD_REPOSITORY_NAME + } + else { + $originUrl = [Uri](Invoke-Git remote get-url origin) + if ($originUrl) { + $lastPathSegment = $originUrl.Segments[$originUrl.Segments.Length - 1] + if ($lastPathSegment.EndsWith('.git')) { + $lastPathSegment = $lastPathSegment.Substring(0, $lastPathSegment.Length - '.git'.Length) + } + Write-Verbose 'Using origin remote URL to derive ProductName.' + $ProductName = $lastPathSegment + } + } + + if (!$ProductName) { + Write-Error "Unable to determine default value for -ProductName." + } +} + +if (!$OwnerAlias) { + if ($env:BUILD_REQUESTEDFOREMAIL) { + Write-Verbose 'Using $env:BUILD_REQUESTEDFOREMAIL and slicing to just the alias for OwnerAlias.' + $OwnerAlias = ($env:BUILD_REQUESTEDFOREMAIL -split '@')[0] + } else { + $OwnerAlias = $TeamAlias + } + + if (!$OwnerAlias) { + Write-Error "Unable to determine default value for -OwnerAlias." + } +} + +if (!$Requester) { + if ($env:BUILD_REQUESTEDFOREMAIL) { + Write-Verbose 'Using $env:BUILD_REQUESTEDFOREMAIL and slicing to just the alias for Requester.' + $Requester = ($env:BUILD_REQUESTEDFOREMAIL -split '@')[0] + } + else { + Write-Verbose 'Using $env:USERNAME for Requester.' + $Requester = $env:USERNAME + } + if (!$Requester) { + $Requester = $OwnerAlias + } +} + +if (!$RepoUrl) { + $RepoUrl = $env:BUILD_REPOSITORY_URI + if (!$RepoUrl) { + $originUrl = [Uri](Invoke-Git remote get-url origin) + if ($originUrl) { + Write-Verbose 'Using git origin remote url for GitURL.' + $RepoUrl = $originUrl + } + + if (!$RepoUrl) { + Write-Error "Unable to determine default value for -RepoUrl." + } + } +} + +Push-Location $PSScriptRoot +$versionsObj = dotnet nbgv get-version -f json | ConvertFrom-Json +Pop-Location + +$ReleaseDateString = $ReleaseDate.ToShortDateString() +$Version = $versionsObj.Version + +$BackupSize = Get-ChildItem $PSScriptRoot\..\.git -Recurse -File | Measure-Object -Property Length -Sum +$DataSizeMB = [int]($BackupSize.Sum / 1mb) +$FileCount = $BackupSize.Count + +$Request = @{ + "Requester" = $Requester + "Manager" = $ManagerAlias + "TeamAlias" = $TeamAlias + "AdditionalContacts" = $AdditionalContacts + "BusinessGroupName" = $BusinessGroupName + "ProductName" = $ProductName + "Version" = $Version + "ProductionType" = $ProductionType + "ReleaseType" = $ReleaseType + "ReleaseDateString" = $ReleaseDateString + "OS" = [string]::Join(',', $OS) + "ProductLanguage" = [string]::Join(',', $ProductLanguage) + "FileCollection" = [string]::Join(',', $FileCollection) + "OwnerAlias" = $OwnerAlias + "Notes" = $Notes.Trim() + "CustomerProvidedDataSizeMB" = $DataSizeMB + "CustomerProvidedFileCount" = $FileCount + "BackupType" = $BackupType + "ServerPath" = $ServerPath + "AzureStorageAccount" = $AzureStorageAccount + "AzureStorageContainer" = $AzureStorageContainer + "GitURL" = $RepoUrl +} + +$RequestJson = ConvertTo-Json $Request +Write-Host "SCA request:`n$RequestJson" + +if ($PSCmdlet.ShouldProcess('source archival request', 'post')) { + if (!$SourceCodeArchivalUri) { + Write-Error "Unable to post request without -SourceCodeArchivalUri parameter." + exit 1 + } + + $headers = @{ + 'Authorization' = "Bearer $AccessToken" + } + + [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 + + $Response = Invoke-WebRequest -Uri $SourceCodeArchivalUri -Method POST -Headers $headers -Body $RequestJson -ContentType "application/json" -UseBasicParsing -SkipHttpErrorCheck + Write-Host "Status Code : " -NoNewline + if ($Response.StatusCode -eq 200) { + Write-Host $Response.StatusCode -ForegroundColor Green + Write-Host "Ticket ID : " -NoNewline + $responseContent = ConvertFrom-Json ($Response.Content) + Write-Host $responseContent + } + else { + Write-Host $Response.StatusCode -ForegroundColor Red + try { + $responseContent = ConvertFrom-Json $Response.Content + Write-Host "Message : $($responseContent.message)" + } + catch { + Write-Host "JSON Parse Error: $($_.Exception.Message)" + Write-Host "Raw response content:" + Write-Host $Response.Content + } + + exit 2 + } +} elseif ($SourceCodeArchivalUri) { + Write-Host "Would have posted to $SourceCodeArchivalUri" +} diff --git a/azure-pipelines/BuildStageVariables.yml b/azure-pipelines/BuildStageVariables.yml new file mode 100644 index 00000000..2a683569 --- /dev/null +++ b/azure-pipelines/BuildStageVariables.yml @@ -0,0 +1,5 @@ +variables: + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true + BuildConfiguration: Release + NUGET_PACKAGES: $(Agent.TempDirectory)/.nuget/packages/ + # codecov_token: 4dc9e7e2-6b01-4932-a180-847b52b43d35 # Get a new one from https://codecov.io/ diff --git a/azure-pipelines/Convert-PDB.ps1 b/azure-pipelines/Convert-PDB.ps1 new file mode 100644 index 00000000..f119a164 --- /dev/null +++ b/azure-pipelines/Convert-PDB.ps1 @@ -0,0 +1,42 @@ +<# +.SYNOPSIS + Converts between Windows PDB and Portable PDB formats. +.PARAMETER DllPath + The path to the DLL whose PDB is to be converted. +.PARAMETER PdbPath + 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. +.PARAMETER OutputPath + The path of the output PDB to write. +#> +[CmdletBinding()] +Param( + [Parameter(Mandatory=$true,Position=0)] + [string]$DllPath, + [Parameter()] + [string]$PdbPath, + [Parameter(Mandatory=$true,Position=1)] + [string]$OutputPath +) + +if ($IsMacOS -or $IsLinux) { + Write-Error "This script only works on Windows" + return +} + +$version = '1.1.0-beta2-21101-01' +$baseDir = "$PSScriptRoot/../obj/tools" +$pdb2pdbpath = "$baseDir/Microsoft.DiaSymReader.Pdb2Pdb.$version/tools/Pdb2Pdb.exe" +if (-not (Test-Path $pdb2pdbpath)) { + if (-not (Test-Path $baseDir)) { New-Item -Type Directory -Path $baseDir | Out-Null } + $baseDir = (Resolve-Path $baseDir).Path # Normalize it + Write-Verbose "& (& $PSScriptRoot/Get-NuGetTool.ps1) install Microsoft.DiaSymReader.Pdb2Pdb -version $version -PackageSaveMode nuspec -OutputDirectory $baseDir -Source https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json | Out-Null" + & (& $PSScriptRoot/Get-NuGetTool.ps1) install Microsoft.DiaSymReader.Pdb2Pdb -version $version -PackageSaveMode nuspec -OutputDirectory $baseDir -Source https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json | Out-Null +} + +$args = $DllPath,'/out',$OutputPath,'/nowarn','0021' +if ($PdbPath) { + $args += '/pdb',$PdbPath +} + +Write-Verbose "$pdb2pdbpath $args" +& $pdb2pdbpath $args diff --git a/azure-pipelines/Get-ArtifactsStagingDirectory.ps1 b/azure-pipelines/Get-ArtifactsStagingDirectory.ps1 new file mode 100644 index 00000000..391e5713 --- /dev/null +++ b/azure-pipelines/Get-ArtifactsStagingDirectory.ps1 @@ -0,0 +1,15 @@ +Param( + [switch]$CleanIfLocal +) +if ($env:BUILD_ARTIFACTSTAGINGDIRECTORY) { + $ArtifactStagingFolder = $env:BUILD_ARTIFACTSTAGINGDIRECTORY +} elseif ($env:RUNNER_TEMP) { + $ArtifactStagingFolder = "$env:RUNNER_TEMP\_artifacts" +} else { + $ArtifactStagingFolder = [System.IO.Path]::GetFullPath("$PSScriptRoot/../obj/_artifacts") + if ($CleanIfLocal -and (Test-Path $ArtifactStagingFolder)) { + Remove-Item $ArtifactStagingFolder -Recurse -Force + } +} + +$ArtifactStagingFolder diff --git a/azure-pipelines/Get-CodeCovTool.ps1 b/azure-pipelines/Get-CodeCovTool.ps1 new file mode 100644 index 00000000..ca580b4d --- /dev/null +++ b/azure-pipelines/Get-CodeCovTool.ps1 @@ -0,0 +1,86 @@ +<# +.SYNOPSIS + Downloads the CodeCov.io uploader tool and returns the path to it. +.PARAMETER AllowSkipVerify + Allows skipping signature verification of the downloaded tool if gpg is not installed. +#> +[CmdletBinding()] +Param( + [switch]$AllowSkipVerify +) + +if ($IsMacOS) { + $codeCovUrl = "https://uploader.codecov.io/latest/macos/codecov" + $toolName = 'codecov' +} +elseif ($IsLinux) { + $codeCovUrl = "https://uploader.codecov.io/latest/linux/codecov" + $toolName = 'codecov' +} +else { + $codeCovUrl = "https://uploader.codecov.io/latest/windows/codecov.exe" + $toolName = 'codecov.exe' +} + +$shaSuffix = ".SHA256SUM" +$sigSuffix = $shaSuffix + ".sig" + +Function Get-FileFromWeb([Uri]$Uri, $OutDir) { + $OutFile = Join-Path $OutDir $Uri.Segments[-1] + if (!(Test-Path $OutFile)) { + Write-Verbose "Downloading $Uri..." + if (!(Test-Path $OutDir)) { New-Item -ItemType Directory -Path $OutDir | Out-Null } + try { + (New-Object System.Net.WebClient).DownloadFile($Uri, $OutFile) + } finally { + # This try/finally causes the script to abort + } + } + + $OutFile +} + +$toolsPath = & "$PSScriptRoot\Get-TempToolsPath.ps1" +$binaryToolsPath = Join-Path $toolsPath codecov +$testingPath = Join-Path $binaryToolsPath unverified +$finalToolPath = Join-Path $binaryToolsPath $toolName + +if (!(Test-Path $finalToolPath)) { + if (Test-Path $testingPath) { + Remove-Item -Recurse -Force $testingPath # ensure we download all matching files + } + $tool = Get-FileFromWeb $codeCovUrl $testingPath + $sha = Get-FileFromWeb "$codeCovUrl$shaSuffix" $testingPath + $sig = Get-FileFromWeb "$codeCovUrl$sigSuffix" $testingPath + $key = Get-FileFromWeb https://keybase.io/codecovsecurity/pgp_keys.asc $testingPath + + if ((Get-Command gpg -ErrorAction SilentlyContinue)) { + Write-Host "Importing codecov key" -ForegroundColor Yellow + gpg --import $key + Write-Host "Verifying signature on codecov hash" -ForegroundColor Yellow + gpg --verify $sig $sha + } else { + if ($AllowSkipVerify) { + Write-Warning "gpg not found. Unable to verify hash signature." + } else { + throw "gpg not found. Unable to verify hash signature. Install gpg or add -AllowSkipVerify to override." + } + } + + Write-Host "Verifying hash on downloaded tool" -ForegroundColor Yellow + $actualHash = (Get-FileHash -Path $tool -Algorithm SHA256).Hash + $expectedHash = (Get-Content $sha).Split()[0] + if ($actualHash -ne $expectedHash) { + # Validation failed. Delete the tool so we can't execute it. + #Remove-Item $codeCovPath + throw "codecov uploader tool failed signature validation." + } + + Copy-Item $tool $finalToolPath + + if ($IsMacOS -or $IsLinux) { + chmod u+x $finalToolPath + } +} + +return $finalToolPath diff --git a/azure-pipelines/Get-InsertionPRId.ps1 b/azure-pipelines/Get-InsertionPRId.ps1 new file mode 100644 index 00000000..62cb30cd --- /dev/null +++ b/azure-pipelines/Get-InsertionPRId.ps1 @@ -0,0 +1,26 @@ +<# +.SYNOPSIS + Look up the pull request URL of the insertion PR. +#> +$stagingFolder = $env:BUILD_STAGINGDIRECTORY +if (!$stagingFolder) { + $stagingFolder = $env:SYSTEM_DEFAULTWORKINGDIRECTORY + if (!$stagingFolder) { + Write-Error "This script must be run in an Azure Pipeline." + exit 1 + } +} +$markdownFolder = Join-Path $stagingFolder (Join-Path 'MicroBuild' 'Output') +$markdownFile = Join-Path $markdownFolder 'PullRequestUrl.md' +if (!(Test-Path $markdownFile)) { + Write-Error "This script should be run after the MicroBuildInsertVsPayload task." + exit 2 +} + +$insertionPRUrl = Get-Content $markdownFile +if (!($insertionPRUrl -match 'https:.+?/pullrequest/(\d+)')) { + Write-Error "Failed to parse pull request URL: $insertionPRUrl" + exit 3 +} + +$Matches[1] diff --git a/azure-pipelines/Get-LibTemplateBasis.ps1 b/azure-pipelines/Get-LibTemplateBasis.ps1 new file mode 100644 index 00000000..2181c77b --- /dev/null +++ b/azure-pipelines/Get-LibTemplateBasis.ps1 @@ -0,0 +1,25 @@ +<# +.SYNOPSIS + Returns the name of the well-known branch in the Library.Template repository upon which HEAD is based. +#> +[CmdletBinding(SupportsShouldProcess = $true)] +Param( + [switch]$ErrorIfNotRelated +) + +# This list should be sorted in order of decreasing specificity. +$branchMarkers = @( + @{ commit = 'fd0a7b25ccf030bbd16880cca6efe009d5b1fffc'; branch = 'microbuild' }; + @{ commit = '05f49ce799c1f9cc696d53eea89699d80f59f833'; branch = 'main' }; +) + +foreach ($entry in $branchMarkers) { + if (git rev-list HEAD | Select-String -Pattern $entry.commit) { + return $entry.branch + } +} + +if ($ErrorIfNotRelated) { + 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." + exit 1 +} diff --git a/azure-pipelines/Get-NuGetTool.ps1 b/azure-pipelines/Get-NuGetTool.ps1 new file mode 100644 index 00000000..3097c873 --- /dev/null +++ b/azure-pipelines/Get-NuGetTool.ps1 @@ -0,0 +1,22 @@ +<# +.SYNOPSIS + Downloads the NuGet.exe tool and returns the path to it. +.PARAMETER NuGetVersion + The version of the NuGet tool to acquire. +#> +Param( + [Parameter()] + [string]$NuGetVersion='6.4.0' +) + +$toolsPath = & "$PSScriptRoot\Get-TempToolsPath.ps1" +$binaryToolsPath = Join-Path $toolsPath $NuGetVersion +if (!(Test-Path $binaryToolsPath)) { $null = mkdir $binaryToolsPath } +$nugetPath = Join-Path $binaryToolsPath nuget.exe + +if (!(Test-Path $nugetPath)) { + Write-Host "Downloading nuget.exe $NuGetVersion..." -ForegroundColor Yellow + (New-Object System.Net.WebClient).DownloadFile("https://dist.nuget.org/win-x86-commandline/v$NuGetVersion/NuGet.exe", $nugetPath) +} + +return (Resolve-Path $nugetPath).Path diff --git a/azure-pipelines/Get-ProcDump.ps1 b/azure-pipelines/Get-ProcDump.ps1 new file mode 100644 index 00000000..1493fe4b --- /dev/null +++ b/azure-pipelines/Get-ProcDump.ps1 @@ -0,0 +1,14 @@ +<# +.SYNOPSIS +Downloads 32-bit and 64-bit procdump executables and returns the path to where they were installed. +#> +$version = '0.0.1' +$baseDir = "$PSScriptRoot\..\obj\tools" +$procDumpToolPath = "$baseDir\procdump.$version\bin" +if (-not (Test-Path $procDumpToolPath)) { + if (-not (Test-Path $baseDir)) { New-Item -Type Directory -Path $baseDir | Out-Null } + $baseDir = (Resolve-Path $baseDir).Path # Normalize it + & (& $PSScriptRoot\Get-NuGetTool.ps1) install procdump -version $version -PackageSaveMode nuspec -OutputDirectory $baseDir -Source https://api.nuget.org/v3/index.json | Out-Null +} + +(Resolve-Path $procDumpToolPath).Path diff --git a/azure-pipelines/Get-SymbolFiles.ps1 b/azure-pipelines/Get-SymbolFiles.ps1 new file mode 100644 index 00000000..b5063cec --- /dev/null +++ b/azure-pipelines/Get-SymbolFiles.ps1 @@ -0,0 +1,66 @@ +<# +.SYNOPSIS + Collect the list of PDBs built in this repo. +.PARAMETER Path + The directory to recursively search for PDBs. +.PARAMETER Tests + A switch indicating to find PDBs only for test binaries instead of only for shipping shipping binaries. +#> +[CmdletBinding()] +param ( + [parameter(Mandatory=$true)] + [string]$Path, + [switch]$Tests +) + +$ActivityName = "Collecting symbols from $Path" +Write-Progress -Activity $ActivityName -CurrentOperation "Discovery PDB files" +$PDBs = Get-ChildItem -rec "$Path/*.pdb" + +# Filter PDBs to product OR test related. +$testregex = "unittest|tests|\.test\." + +Write-Progress -Activity $ActivityName -CurrentOperation "De-duplicating symbols" +$PDBsByHash = @{} +$i = 0 +$PDBs |% { + Write-Progress -Activity $ActivityName -CurrentOperation "De-duplicating symbols" -PercentComplete (100 * $i / $PDBs.Length) + $hash = Get-FileHash $_ + $i++ + Add-Member -InputObject $_ -MemberType NoteProperty -Name Hash -Value $hash.Hash + Write-Output $_ +} | Sort-Object CreationTime |% { + # De-dupe based on hash. Prefer the first match so we take the first built copy. + if (-not $PDBsByHash.ContainsKey($_.Hash)) { + $PDBsByHash.Add($_.Hash, $_.FullName) + Write-Output $_ + } +} |? { + if ($Tests) { + $_.FullName -match $testregex + } else { + $_.FullName -notmatch $testregex + } +} |% { + # Collect the DLLs/EXEs as well. + $rootName = "$($_.Directory)/$($_.BaseName)" + if ($rootName.EndsWith('.ni')) { + $rootName = $rootName.Substring(0, $rootName.Length - 3) + } + + $dllPath = "$rootName.dll" + $exePath = "$rootName.exe" + if (Test-Path $dllPath) { + $BinaryImagePath = $dllPath + } elseif (Test-Path $exePath) { + $BinaryImagePath = $exePath + } else { + Write-Warning "`"$_`" found with no matching binary file." + $BinaryImagePath = $null + } + + if ($BinaryImagePath) { + Write-Output $BinaryImagePath + Write-Output $_.FullName + } +} diff --git a/azure-pipelines/Get-TempToolsPath.ps1 b/azure-pipelines/Get-TempToolsPath.ps1 new file mode 100644 index 00000000..bb3da8e3 --- /dev/null +++ b/azure-pipelines/Get-TempToolsPath.ps1 @@ -0,0 +1,13 @@ +if ($env:AGENT_TEMPDIRECTORY) { + $path = "$env:AGENT_TEMPDIRECTORY\$env:BUILD_BUILDID" +} elseif ($env:localappdata) { + $path = "$env:localappdata\gitrepos\tools" +} else { + $path = "$PSScriptRoot\..\obj\tools" +} + +if (!(Test-Path $path)) { + New-Item -ItemType Directory -Path $Path | Out-Null +} + +(Resolve-Path $path).Path diff --git a/azure-pipelines/GlobalVariables.yml b/azure-pipelines/GlobalVariables.yml new file mode 100644 index 00000000..cee858b1 --- /dev/null +++ b/azure-pipelines/GlobalVariables.yml @@ -0,0 +1,6 @@ +variables: + # These variables are required for MicroBuild tasks + TeamName: VS IDE + TeamEmail: vsidemicrobuild@microsoft.com + # These variables influence insertion pipelines + ContainsVsix: false # This should be true when the repo builds a VSIX that should be inserted to VS. diff --git a/azure-pipelines/Install-NuGetPackage.ps1 b/azure-pipelines/Install-NuGetPackage.ps1 new file mode 100644 index 00000000..9afde055 --- /dev/null +++ b/azure-pipelines/Install-NuGetPackage.ps1 @@ -0,0 +1,55 @@ +<# +.SYNOPSIS + Installs a NuGet package. +.PARAMETER PackageID + The Package ID to install. +.PARAMETER Version + The version of the package to install. If unspecified, the latest stable release is installed. +.PARAMETER Source + The package source feed to find the package to install from. +.PARAMETER PackagesDir + The directory to install the package to. By default, it uses the Packages folder at the root of the repo. +.PARAMETER ConfigFile + The nuget.config file to use. By default, it uses :/nuget.config. +.OUTPUTS + System.String. The path to the installed package. +#> +[CmdletBinding(SupportsShouldProcess=$true,ConfirmImpact='Low')] +Param( + [Parameter(Position=1,Mandatory=$true)] + [string]$PackageId, + [Parameter()] + [string]$Version, + [Parameter()] + [string]$Source, + [Parameter()] + [switch]$Prerelease, + [Parameter()] + [string]$PackagesDir="$PSScriptRoot\..\packages", + [Parameter()] + [string]$ConfigFile="$PSScriptRoot\..\nuget.config", + [Parameter()] + [ValidateSet('Quiet','Normal','Detailed')] + [string]$Verbosity='normal' +) + +$nugetPath = & "$PSScriptRoot\Get-NuGetTool.ps1" + +try { + Write-Verbose "Installing $PackageId..." + $nugetArgs = "Install",$PackageId,"-OutputDirectory",$PackagesDir,'-ConfigFile',$ConfigFile + if ($Version) { $nugetArgs += "-Version",$Version } + if ($Source) { $nugetArgs += "-FallbackSource",$Source } + if ($Prerelease) { $nugetArgs += "-Prerelease" } + $nugetArgs += '-Verbosity',$Verbosity + + if ($PSCmdlet.ShouldProcess($PackageId, 'nuget install')) { + $p = Start-Process $nugetPath $nugetArgs -NoNewWindow -Wait -PassThru + if ($null -ne $p.ExitCode -and $p.ExitCode -ne 0) { throw } + } + + # Provide the path to the installed package directory to our caller. + Write-Output (Get-ChildItem "$PackagesDir\$PackageId.*")[0].FullName +} finally { + Pop-Location +} diff --git a/azure-pipelines/Merge-CodeCoverage.ps1 b/azure-pipelines/Merge-CodeCoverage.ps1 new file mode 100644 index 00000000..b126268c --- /dev/null +++ b/azure-pipelines/Merge-CodeCoverage.ps1 @@ -0,0 +1,51 @@ +#!/usr/bin/env pwsh + +<# +.SYNOPSIS + Merges code coverage reports. +.PARAMETER Path + The path(s) to search for Cobertura code coverage reports. +.PARAMETER Format + The format for the merged result. The default is Cobertura +.PARAMETER OutputDir + The directory the merged result will be written to. The default is `coveragereport` in the root of this repo. +#> +[CmdletBinding()] +Param( + [Parameter(Mandatory=$true)] + [string[]]$Path, + [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')] + [string]$Format='Cobertura', + [string]$OutputFile=("$PSScriptRoot/../coveragereport/merged.cobertura.xml") +) + +$RepoRoot = [string](Resolve-Path $PSScriptRoot/..) +Push-Location $RepoRoot +try { + Write-Verbose "Searching $Path for *.cobertura.xml files" + $reports = Get-ChildItem -Recurse $Path -Filter *.cobertura.xml + + if ($reports) { + $reports |% { $_.FullName } |% { + # 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. + $xml = [xml](Get-Content -Path $_) + $xml.coverage.packages.package.classes.class |? { $_.filename} |% { + $_.filename = $_.filename.Replace('{reporoot}', $RepoRoot).Replace([IO.Path]::AltDirectorySeparatorChar, [IO.Path]::DirectorySeparatorChar) + } + + $xml.Save($_) + } + + $Inputs = $reports |% { Resolve-Path -relative $_.FullName } + + if ((Split-Path $OutputFile) -and -not (Test-Path (Split-Path $OutputFile))) { + New-Item -Type Directory -Path (Split-Path $OutputFile) | Out-Null + } + + & dotnet dotnet-coverage merge $Inputs -o $OutputFile -f cobertura + } else { + Write-Error "No reports found to merge." + } +} finally { + Pop-Location +} diff --git a/azure-pipelines/NuGetSbom.targets b/azure-pipelines/NuGetSbom.targets new file mode 100644 index 00000000..a2599e88 --- /dev/null +++ b/azure-pipelines/NuGetSbom.targets @@ -0,0 +1,12 @@ + + + true + $(TargetsForTfmSpecificBuildOutput);IncludeSbomInNupkg + + + + + + + + diff --git a/azure-pipelines/OptProf.yml b/azure-pipelines/OptProf.yml new file mode 100644 index 00000000..4e309418 --- /dev/null +++ b/azure-pipelines/OptProf.yml @@ -0,0 +1,121 @@ +trigger: none +pr: none +schedules: +- cron: "0 3 * * Fri" # Thu @ 8 or 9 PM Mountain Time (depending on DST) + displayName: Weekly OptProf run + branches: + include: + - 'v*.*' + - main + always: true # we must keep data fresh since optimizationdata drops are purged after 30 days + +# Avoid errant CI builds: https://developercommunity.visualstudio.com/content/problem/1154409/azure-pipeline-is-triggering-due-to-events-that-ne.html +#resources: +# repositories: +# - repository: scripts +# type: git +# name: DeploymentScripts +# ref: refs/heads/test + +parameters: + - name: ShouldSkipOptimize + displayName: Skip OptProf optimization + type: boolean + 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. + +variables: +- template: GlobalVariables.yml +- name: PublicRelease + value: false # avoid using nice version since we're building a preliminary/unoptimized package +- name: IsOptProf + value: true +- name: MicroBuild_NuPkgSigningEnabled + value: false # test-signed nuget packages fail to restore in the VS insertion PR validations. Just don't sign them *at all*. + +stages: +- stage: Library + variables: + - name: OptProf + value: true + - template: BuildStageVariables.yml + jobs: + - template: build.yml + parameters: + Is1ESPT: false + RealSign: false + windowsPool: VSEngSS-MicroBuild2022-1ES + EnableMacOSBuild: false + ShouldSkipOptimize: ${{ parameters.ShouldSkipOptimize }} + IsOptProf: true + RunTests: false + SkipCodesignVerify: true +- stage: QueueVSBuild + jobs: + - job: QueueOptProf + pool: VSEngSS-MicroBuild2022-1ES + variables: + InsertPayloadName: LibraryName + InsertTopicBranch: team/VS-IDE/LibraryName-OptProf-run-$(Build.BuildId) + steps: + - checkout: none # We don't need source from our own repo + clean: true + + # Pipeline YAML does not yet support checking out other repos. So we'll do it by hand. +# - checkout: scripts # We DO need source from the DeploymentScripts repo +# clean: true +# path: $(Agent.TempDirectory)/DeploymentScripts +# fetchDepth: 1 + - script: 'git -c http.extraheader="AUTHORIZATION: bearer $(System.AccessToken)" clone https://devdiv.visualstudio.com/DevDiv/_git/DeploymentScripts --depth 1 --branch test "$(Agent.TempDirectory)/DeploymentScripts"' + displayName: Download DeploymentScripts repo + + - task: DownloadBuildArtifacts@0 + displayName: Download insertion artifacts + inputs: + artifactName: VSInsertion-Windows + downloadPath: $(Agent.TempDirectory) + - task: DownloadBuildArtifacts@0 + displayName: Download variables artifacts + inputs: + artifactName: Variables-Windows + downloadPath: $(Agent.TempDirectory) + - task: PowerShell@2 + displayName: Set pipeline variables based on artifacts + inputs: + targetType: filePath + filePath: $(Agent.TempDirectory)/Variables-Windows/_pipelines.ps1 + - task: NuGetCommand@2 + displayName: Push VS-repo packages to VS feed + inputs: + command: push + packagesToPush: $(Agent.TempDirectory)/VSInsertion-Windows/*.nupkg + publishVstsFeed: 97a41293-2972-4f48-8c0e-05493ae82010 # VS feed + allowPackageConflicts: true + - task: MicroBuildInsertVsPayload@4 + displayName: Insert VS Payload + inputs: + TeamName: $(TeamName) + TeamEmail: $(TeamEmail) + SkipCreatePR: true + CustomScriptExecutionCommand: src\VSSDK\NuGet\AllowUnstablePackages.ps1 + - task: benjhuser.tfs-extensions-build-tasks.trigger-build-task.TriggerBuild@3 + displayName: Trigger a new build of DD-CB-TestSignVS-devCI + inputs: + buildDefinition: DD-CB-TestSignVS-devCI + useSameBranch: false + branchToUse: $(InsertTopicBranch) + storeInEnvironmentVariable: true + queueBuildForUserThatTriggeredBuild: false + authenticationMethod: OAuth Token + password: $(System.AccessToken) + - task: PowerShell@2 + displayName: Associate InsertionOutputs artifacts with CloudBuild + inputs: + targetType: filePath + filePath: $(Agent.TempDirectory)/DeploymentScripts/Scripts/Insertion/WriteArtifact.ps1 + arguments: '-oldBuildID $(Build.BuildId) -newBuildID $(TriggeredBuildIds) -artifactName "InsertionOutputs" -accessToken $(System.AccessToken)' + - task: PowerShell@2 + displayName: Tag the build with LibraryName-insertion + inputs: + targetType: filePath + filePath: $(Agent.TempDirectory)/DeploymentScripts/Scripts/Insertion/TagBuild.ps1 + arguments: '-buildID $(TriggeredBuildIds) -tagName "LibraryName-insertion" -accessToken $(System.AccessToken)' diff --git a/azure-pipelines/OptProf_part2.yml b/azure-pipelines/OptProf_part2.yml new file mode 100644 index 00000000..c59d6999 --- /dev/null +++ b/azure-pipelines/OptProf_part2.yml @@ -0,0 +1,91 @@ +trigger: none +pr: none + +resources: + pipelines: + - pipeline: VisualStudioBuildUnderTest + source: DD-CB-TestSignVS-devCI + trigger: + tags: + - LibraryName-insertion + - pipeline: DartLab + source: DartLab + branch: main + - pipeline: DartLab.OptProf + source: DartLab.OptProf + branch: main + tags: + - production + repositories: + - repository: DartLabTemplates + type: git + name: DartLab.Templates + ref: refs/heads/main + - repository: DartLabOptProfTemplates + type: git + name: DartLab.OptProf + ref: refs/tags/Production + +parameters: + +# The prefix naming of the OptimizationInputs drop +- name: optimizationDropPrefix + type: string + default: OptimizationInputs/$(System.TeamProject)/$(Build.Repository.Name) + +stages: +- template: \templates\stages\visual-studio\single-runsettings.yml@DartLabOptProfTemplates + parameters: + ##### Required ##### + runSettingsURI: $(Pipeline.Workspace)\VisualStudioBuildUnderTest\BuildArtifacts\runsettings\LibraryName.OptProf.runsettings + visualStudioBootstrapperURI: https://vsdrop.corp.microsoft.com/file/v1/$(VisualStudio.BuildUnderTest.ProductsDropName);bootstrappers/Enterprise/vs_enterprise.exe + ##### Optional ##### + name: OptProfProfilingWorkflow + displayName: OptProf Profiling Workflow + optOptimizationInputsDropName: $(OptimizationInputsDropName) + previousOptimizationInputsDropName: $(PreviousOptimizationInputsDropName) + testLabPoolName: VS-Platform + ##### Step Hooks ##### + preTestMachineConfigurationStepList: + - download: VisualStudioBuildUnderTest + - task: PowerShell@2 + name: SetProductsDropName + displayName: Set 'VisualStudio.BuildUnderTest.ProductsDropName' + inputs: + filePath: $(DartLab.Path)\Scripts\VisualStudio\Build\Get-VisualStudioDropName.ps1 + arguments: -DropNamePrefix 'Products' -VstsDropUrlsJson '$(Pipeline.Workspace)\VisualStudioBuildUnderTest\BuildArtifacts\VstsDropUrls.json' -OutVariableName 'VisualStudio.BuildUnderTest.ProductsDropName' + preDeployAndRunTestsStepList: + - download: VisualStudioBuildUnderTest + prePublishOptimizationInputsDropStepList: + # Set parameter for PreviousOptimizationInputsDropName, MicroBuildCommitID, and OptimizationInputsDropName + - powershell: | + try { + $artifactName = 'InsertionOutputs' + $BuildID = $(resources.pipeline.VisualStudioBuildUnderTest.runID) + $artifact = Get-BuildArtifact -InstanceURL 'https://dev.azure.com/devdiv' -ProjectName 'DevDiv' -BuildID $BuildID -ArtifactName $artifactName -OAuthAccessToken (ConvertTo-SecureString '$(System.AccessToken)' -AsPlainText -Force) + $containerName = $artifact.Resource.Data -Split '/' | Select-Object -Last 1 + $fileName = Join-Path $containerName 'Metadata.json' + $jsonString = Read-BuildArtifactFile -InstanceURL 'https://dev.azure.com/devdiv' -ProjectName 'DevDiv' -BuildID $BuildID -ArtifactName $artifactName -FileName $fileName -OAuthAccessToken (ConvertTo-SecureString '$(System.AccessToken)' -AsPlainText -Force) + $json = $jsonString | ConvertFrom-Json + + Write-Host "The content of the metadata.json file was $json" + + $dropname = $json.OptimizationData + $commitID = $json.CommitID + $OptimizationInputsDropName = "${{parameters.optimizationDropPrefix}}/$($commitID)/$(Build.BuildId)/$(System.StageId)/$(System.StageAttempt)" + + Write-Host "PreviousOptimizationInputsDropName: $dropname" + Set-AzurePipelinesVariable 'PreviousOptimizationInputsDropName' $dropname + + Write-Host "MicroBuildCommitID: $commitID" + Set-AzurePipelinesVariable 'MicroBuildCommitID' $commitID + + Write-Host "OptimizationInputsDropName: $OptimizationInputsDropName" + Set-AzurePipelinesVariable 'OptimizationInputsDropName' $OptimizationInputsDropName + } + catch { + Write-Host $_ + Write-Error "Failed to set OptimizationInputsDropName pipeline variable" + throw + } + displayName: Set MicroBuildCommitID, PreviousOptimizationInputsDropName, and OptimizationInputsDropName diff --git a/azure-pipelines/PoliCheckExclusions.xml b/azure-pipelines/PoliCheckExclusions.xml new file mode 100644 index 00000000..5ae16710 --- /dev/null +++ b/azure-pipelines/PoliCheckExclusions.xml @@ -0,0 +1,10 @@ + + + NODE_MODULES|.STORE + + + + + + + diff --git a/azure-pipelines/PostPRMessage.ps1 b/azure-pipelines/PostPRMessage.ps1 new file mode 100644 index 00000000..4a2b7886 --- /dev/null +++ b/azure-pipelines/PostPRMessage.ps1 @@ -0,0 +1,57 @@ +[CmdletBinding(SupportsShouldProcess = $true)] +param( + [Parameter(Mandatory=$true)] + $AccessToken, + [Parameter(Mandatory=$true)] + $Markdown, + [ValidateSet('Active','ByDesign','Closed','Fixed','Pending','Unknown','WontFix')] + $CommentState='Active' +) + +# See https://docs.microsoft.com/en-us/dotnet/api/microsoft.teamfoundation.sourcecontrol.webapi.commentthreadstatus?view=azure-devops-dotnet +if ($CommentState -eq 'Active') { + $StatusCode = 1 +} elseif ($CommentState -eq 'ByDesign') { + $StatusCode = 5 +} elseif ($CommentState -eq 'Closed') { + $StatusCode = 4 +} elseif ($CommentState -eq 'Fixed') { + $StatusCode = 2 +} elseif ($CommentState -eq 'Pending') { + $StatusCode = 6 +} elseif ($CommentState -eq 'Unknown') { + $StatusCode = 0 +} elseif ($CommentState -eq 'WontFix') { + $StatusCode = 3 +} + +# Build the JSON body up +$body = ConvertTo-Json @{ + comments = @(@{ + parentCommentId = 0 + content = $Markdown + commentType = 1 + }) + status = $StatusCode +} + +Write-Verbose "Posting JSON payload: `n$Body" + +# Post the message to the Pull Request +# https://docs.microsoft.com/en-us/rest/api/azure/devops/git/pull%20request%20threads?view=azure-devops-rest-5.1 +$url = "$($env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI)$env:SYSTEM_TEAMPROJECTID/_apis/git/repositories/$($env:BUILD_REPOSITORY_NAME)/pullRequests/$($env:SYSTEM_PULLREQUEST_PULLREQUESTID)/threads?api-version=5.1" +if ($PSCmdlet.ShouldProcess($url, 'Post comment via REST call')) { + try { + if (!$env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI) { + Write-Error "Posting to the pull request requires that the script is running in an Azure Pipelines context." + exit 1 + } + Write-Host "Posting PR comment to: $url" + Invoke-RestMethod -Uri $url -Method POST -Headers @{Authorization = "Bearer $AccessToken"} -Body $Body -ContentType application/json + } + catch { + Write-Error $_ + Write-Error $_.Exception.Message + exit 2 + } +} diff --git a/azure-pipelines/Prepare-Legacy-Symbols.ps1 b/azure-pipelines/Prepare-Legacy-Symbols.ps1 new file mode 100644 index 00000000..ae0bc40c --- /dev/null +++ b/azure-pipelines/Prepare-Legacy-Symbols.ps1 @@ -0,0 +1,35 @@ +Param( + [string]$Path +) + +$ArtifactStagingFolder = & "$PSScriptRoot/Get-ArtifactsStagingDirectory.ps1" +$ArtifactStagingFolder += '/symbols-legacy' +robocopy $Path $ArtifactStagingFolder /mir /njh /njs /ndl /nfl +$WindowsPdbSubDirName = 'symstore' + +Get-ChildItem "$ArtifactStagingFolder\*.pdb" -Recurse |% { + $dllPath = "$($_.Directory)/$($_.BaseName).dll" + $exePath = "$($_.Directory)/$($_.BaseName).exe" + if (Test-Path $dllPath) { + $BinaryImagePath = $dllPath + } elseif (Test-Path $exePath) { + $BinaryImagePath = $exePath + } else { + Write-Warning "`"$_`" found with no matching binary file." + $BinaryImagePath = $null + } + + if ($BinaryImagePath) { + # Convert the PDB to legacy Windows PDBs + Write-Host "Converting PDB for $_" -ForegroundColor DarkGray + $WindowsPdbDir = "$($_.Directory.FullName)\$WindowsPdbSubDirName" + if (!(Test-Path $WindowsPdbDir)) { mkdir $WindowsPdbDir | Out-Null } + $legacyPdbPath = "$WindowsPdbDir\$($_.BaseName).pdb" + & "$PSScriptRoot\Convert-PDB.ps1" -DllPath $BinaryImagePath -PdbPath $_ -OutputPath $legacyPdbPath + if ($LASTEXITCODE -ne 0) { + Write-Warning "PDB conversion of `"$_`" failed." + } + + Move-Item $legacyPdbPath $_ -Force + } +} diff --git a/azure-pipelines/TSAOptions.json b/azure-pipelines/TSAOptions.json index 854ce6c9..5e67a50d 100644 --- a/azure-pipelines/TSAOptions.json +++ b/azure-pipelines/TSAOptions.json @@ -16,4 +16,4 @@ "CodeQL" ], "repositoryName": "json-document-transforms" -} \ No newline at end of file +} diff --git a/azure-pipelines/WIFtoPATauth.yml b/azure-pipelines/WIFtoPATauth.yml new file mode 100644 index 00000000..cb78f61f --- /dev/null +++ b/azure-pipelines/WIFtoPATauth.yml @@ -0,0 +1,22 @@ +parameters: +- name: deadPATServiceConnectionId # The GUID of the PAT-based service connection whose access token must be replaced. + type: string +- name: wifServiceConnectionName # The name of the WIF service connection to use to get the access token. + type: string +- name: resource # The scope for which the access token is requested. + type: string + default: 499b84ac-1321-427f-aa17-267ca6975798 # Azure Artifact feeds (any of them) + +steps: +- task: AzureCLI@2 + displayName: ๐Ÿ” Authenticate with WIF service connection + inputs: + azureSubscription: ${{ parameters.wifServiceConnectionName }} + scriptType: pscore + scriptLocation: inlineScript + inlineScript: | + $accessToken = az account get-access-token --query accessToken --resource '${{ parameters.resource }}' -o tsv + # Set the access token as a secret, so it doesn't get leaked in the logs + Write-Host "##vso[task.setsecret]$accessToken" + # Override the apitoken of the nuget service connection, for the duration of this stage + Write-Host "##vso[task.setendpoint id=${{ parameters.deadPATServiceConnectionId }};field=authParameter;key=apitoken]$accessToken" diff --git a/azure-pipelines/apiscan.yml b/azure-pipelines/apiscan.yml new file mode 100644 index 00000000..af78f15c --- /dev/null +++ b/azure-pipelines/apiscan.yml @@ -0,0 +1,53 @@ +parameters: +- name: windowsPool + type: object + +jobs: +- job: apiscan + displayName: APIScan + dependsOn: Windows + pool: ${{ parameters.windowsPool }} + timeoutInMinutes: 120 + templateContext: + outputs: + - output: pipelineArtifact + displayName: ๐Ÿ“ข collect apiscan artifact + targetPath: $(Pipeline.Workspace)/.gdn/.r/apiscan/001/Logs + artifactName: apiscan-logs + condition: succeededOrFailed() + variables: + - name: SymbolsFeatureName + value: $[ dependencies.Windows.outputs['SetPipelineVariables.SymbolsFeatureName'] ] + - name: NBGV_MajorMinorVersion + value: $[ dependencies.Windows.outputs['nbgv.NBGV_MajorMinorVersion'] ] + - ${{ if eq(variables['system.collectionId'], '011b8bdf-6d56-4f87-be0d-0092136884d9') }}: + # https://dev.azure.com/devdiv/DevDiv/_wiki/wikis/DevDiv.wiki/25351/APIScan-step-by-step-guide-to-setting-up-a-Pipeline + - group: VSEng sponsored APIScan # Expected to provide ApiScanClientId + steps: + # We need TSAOptions.json + - checkout: self + fetchDepth: 1 + + - download: current + artifact: APIScanInputs + displayName: ๐Ÿ”ป Download APIScanInputs artifact + + - task: APIScan@2 + displayName: ๐Ÿ” Run APIScan + inputs: + softwareFolder: $(Pipeline.Workspace)/APIScanInputs + softwareName: $(SymbolsFeatureName) + softwareVersionNum: $(NBGV_MajorMinorVersion) + isLargeApp: false + toolVersion: Latest + preserveLogsFolder: true + env: + AzureServicesAuthConnectionString: runAs=App;AppId=$(ApiScanClientId) + + # File bugs when APIScan finds issues + - task: TSAUpload@2 + displayName: ๐Ÿชณ TSA upload + inputs: + GdnPublishTsaOnboard: True + GdnPublishTsaConfigFile: $(Build.SourcesDirectory)\azure-pipelines\TSAOptions.json + condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main')) diff --git a/azure-pipelines/archive-sourcecode.yml b/azure-pipelines/archive-sourcecode.yml new file mode 100644 index 00000000..f5b4781e --- /dev/null +++ b/azure-pipelines/archive-sourcecode.yml @@ -0,0 +1,88 @@ +trigger: none # We only want to trigger manually or based on resources +pr: none + +# Source archival requirements come from a compliance tenet. Review a sample task here: https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1550985 +# 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. +# If your sources on GitHub are public open source project, then using GitHub Public Archive is sufficient. +schedules: +- cron: "13 13 13 */3 *" # Every three months + displayName: Periodic source archival + branches: + include: + - main + +resources: + repositories: + - repository: MicroBuildTemplate + type: git + name: 1ESPipelineTemplates/MicroBuildTemplate + ref: refs/tags/release + +parameters: +- name: notes + displayName: Notes to include in the SCA request + type: string + default: ' ' # optional parameters require a non-empty default. +- name: whatif + displayName: Only simulate the request + type: boolean + default: false + +variables: +- group: VS Core team # Expected to provide ManagerAlias, SourceCodeArchivalUri +- template: GlobalVariables.yml + +extends: + template: azure-pipelines/MicroBuild.1ES.Official.yml@MicroBuildTemplate + parameters: + sdl: + sourceAnalysisPool: VSEngSS-MicroBuild2022-1ES + + stages: + - stage: archive + jobs: + - job: archive + pool: + name: AzurePipelines-EO + demands: + - ImageOverride -equals 1ESPT-Ubuntu22.04 + os: Linux + + steps: + - checkout: self + clean: true + fetchDepth: 0 + - powershell: tools/Install-DotNetSdk.ps1 + displayName: โš™ Install .NET SDK + - task: NuGetAuthenticate@1 + displayName: ๐Ÿ” Authenticate NuGet feeds + inputs: + forceReinstallCredentialProvider: true + - script: dotnet tool restore + displayName: โš™๏ธ Restore CLI tools + - powershell: azure-pipelines/variables/_pipelines.ps1 + failOnStderr: true + displayName: โš™ Set pipeline variables based on source + - task: AzureCLI@2 + displayName: ๐Ÿ” Authenticate with WIF service connection + inputs: + azureSubscription: VS Core Source Code Archival + scriptType: pscore + scriptLocation: inlineScript + inlineScript: | + $accessToken = az account get-access-token --query accessToken --resource api://177cf50a-4bf5-4481-8b7e-f32900dfc8e6 -o tsv + Write-Host "##vso[task.setvariable variable=scaToken;issecret=true]$accessToken" + - pwsh: > + $TeamAlias = '$(TeamEmail)'.Substring(0, '$(TeamEmail)'.IndexOf('@')) + + azure-pipelines/Archive-SourceCode.ps1 + -ManagerAlias '$(ManagerAlias)' + -TeamAlias $TeamAlias + -BusinessGroupName '$(BusinessGroupName)' + -ProductName '$(SymbolsFeatureName)' + -ProductLanguage English + -Notes '${{ parameters.notes }}' + -AccessToken '$(scaToken)' + -Verbose + -WhatIf:$${{ parameters.whatif }} + displayName: ๐Ÿ—ƒ๏ธ Submit archival request diff --git a/azure-pipelines/artifacts/APIScanInputs.ps1 b/azure-pipelines/artifacts/APIScanInputs.ps1 new file mode 100644 index 00000000..b1550bfa --- /dev/null +++ b/azure-pipelines/artifacts/APIScanInputs.ps1 @@ -0,0 +1,22 @@ +$inputs = & "$PSScriptRoot/symbols.ps1" + +if (!$inputs) { return } + +# Filter out specific files that target OS's that are not subject to APIScan. +# Files that are subject but are not supported must be scanned and an SEL exception filed. +$outputs = @{} +$forbiddenSubPaths = @( + , 'linux-*' + , 'osx*' +) + +$inputs.GetEnumerator() | % { + $list = $_.Value | ? { + $path = $_.Replace('\', '/') + return !($forbiddenSubPaths | ? { $path -like "*/$_/*" }) + } + $outputs[$_.Key] = $list +} + + +$outputs diff --git a/azure-pipelines/artifacts/LocBin.ps1 b/azure-pipelines/artifacts/LocBin.ps1 new file mode 100644 index 00000000..3b6945f7 --- /dev/null +++ b/azure-pipelines/artifacts/LocBin.ps1 @@ -0,0 +1,17 @@ +# Identify LCE files and the binary files they describe +$BinRoot = [System.IO.Path]::GetFullPath("$PSScriptRoot\..\..\bin") +if (!(Test-Path $BinRoot)) { return } + +$FilesToCopy = @() +$FilesToCopy += Get-ChildItem -Recurse -File -Path $BinRoot |? { $_.FullName -match '\\Localize\\' } + +Get-ChildItem -rec "$BinRoot\*.lce" -File | % { + $FilesToCopy += $_ + $FilesToCopy += $_.FullName.SubString(0, $_.FullName.Length - 4) +} + +$FilesToCopy += Get-ChildItem -rec "$BinRoot\*.lcg" -File | % { [xml](Get-Content $_) } | % { $_.lcx.name } + +@{ + "$BinRoot" = $FilesToCopy; +} diff --git a/azure-pipelines/artifacts/VSInsertion.ps1 b/azure-pipelines/artifacts/VSInsertion.ps1 new file mode 100644 index 00000000..ffc7e29a --- /dev/null +++ b/azure-pipelines/artifacts/VSInsertion.ps1 @@ -0,0 +1,41 @@ +# This artifact captures everything needed to insert into VS (NuGet packages, insertion metadata, etc.) + +[CmdletBinding()] +Param ( +) + +if ($IsMacOS -or $IsLinux) { + # We only package up for insertions on Windows agents since they are where optprof can happen. + Write-Verbose "Skipping VSInsertion artifact since we're not on Windows." + return @{} +} + +$RepoRoot = [System.IO.Path]::GetFullPath("$PSScriptRoot\..\..") +$BuildConfiguration = $env:BUILDCONFIGURATION +if (!$BuildConfiguration) { + $BuildConfiguration = 'Debug' +} + +$PackagesRoot = "$RepoRoot/bin/Packages/$BuildConfiguration" +$NuGetPackages = "$PackagesRoot/NuGet" +$VsixPackages = "$PackagesRoot/Vsix" + +if (!(Test-Path $NuGetPackages)) { + Write-Warning "Skipping because NuGet packages haven't been built yet." + return @{} +} + +$result = @{ + "$NuGetPackages" = (Get-ChildItem $NuGetPackages -Recurse) +} + +if (Test-Path $VsixPackages) { + $result["$PackagesRoot"] += Get-ChildItem $VsixPackages -Recurse +} + +if ($env:IsOptProf) { + $VSRepoPackages = "$PackagesRoot/VSRepo" + $result["$VSRepoPackages"] = (Get-ChildItem "$VSRepoPackages\*.VSInsertionMetadata.*.nupkg"); +} + +$result diff --git a/azure-pipelines/artifacts/Variables.ps1 b/azure-pipelines/artifacts/Variables.ps1 new file mode 100644 index 00000000..4bc6d216 --- /dev/null +++ b/azure-pipelines/artifacts/Variables.ps1 @@ -0,0 +1,43 @@ +# This artifact captures all variables defined in the ..\variables folder. +# It "snaps" the values of these variables where we can compute them during the build, +# and otherwise captures the scripts to run later during an Azure Pipelines environment release. + +$RepoRoot = [System.IO.Path]::GetFullPath("$PSScriptRoot/../..") +$ArtifactBasePath = "$RepoRoot/obj/_artifacts" +$VariablesArtifactPath = Join-Path $ArtifactBasePath variables +if (-not (Test-Path $VariablesArtifactPath)) { New-Item -ItemType Directory -Path $VariablesArtifactPath | Out-Null } + +# Copy variables, either by value if the value is calculable now, or by script +Get-ChildItem "$PSScriptRoot/../variables" |% { + $value = $null + if (-not $_.BaseName.StartsWith('_')) { # Skip trying to interpret special scripts + # First check the environment variables in case the variable was set in a queued build + # Always use all caps for env var access because Azure Pipelines converts variables to upper-case for env vars, + # and on non-Windows env vars are case sensitive. + $envVarName = $_.BaseName.ToUpper() + if (Test-Path env:$envVarName) { + $value = Get-Content "env:$envVarName" + } + + # If that didn't give us anything, try executing the script right now from its original position + if (-not $value) { + $value = & $_.FullName + } + + if ($value) { + # We got something, so wrap it with quotes so it's treated like a literal value. + $value = "'$value'" + } + } + + # If that didn't get us anything, just copy the script itself + if (-not $value) { + $value = Get-Content -Path $_.FullName + } + + Set-Content -Path "$VariablesArtifactPath/$($_.Name)" -Value $value +} + +@{ + "$VariablesArtifactPath" = (Get-ChildItem $VariablesArtifactPath -Recurse); +} diff --git a/azure-pipelines/artifacts/_all.ps1 b/azure-pipelines/artifacts/_all.ps1 new file mode 100644 index 00000000..9a22a1d0 --- /dev/null +++ b/azure-pipelines/artifacts/_all.ps1 @@ -0,0 +1,72 @@ +#!/usr/bin/env pwsh + +<# +.SYNOPSIS + This script returns all the artifacts that should be collected after a build. + Each powershell artifact is expressed as an object with these properties: + Source - the full path to the source file + ArtifactName - the name of the artifact to upload to + ContainerFolder - the relative path within the artifact in which the file should appear + Each artifact aggregating .ps1 script should return a hashtable: + Key = path to the directory from which relative paths within the artifact should be calculated + Value = an array of paths (absolute or relative to the BaseDirectory) to files to include in the artifact. + FileInfo objects are also allowed. +.PARAMETER Force + Executes artifact scripts even if they have already been staged. +#> + +[CmdletBinding(SupportsShouldProcess = $true)] +param ( + [string]$ArtifactNameSuffix, + [switch]$Force +) + +Function EnsureTrailingSlash($path) { + if ($path.length -gt 0 -and !$path.EndsWith('\') -and !$path.EndsWith('/')) { + $path = $path + [IO.Path]::DirectorySeparatorChar + } + + $path.Replace('\', [IO.Path]::DirectorySeparatorChar) +} + +Function Test-ArtifactStaged($artifactName) { + $varName = "ARTIFACTSTAGED_$($artifactName.ToUpper())" + Test-Path "env:$varName" +} + +Get-ChildItem "$PSScriptRoot\*.ps1" -Exclude "_*" -Recurse | % { + $ArtifactName = $_.BaseName + if ($Force -or !(Test-ArtifactStaged($ArtifactName + $ArtifactNameSuffix))) { + $totalFileCount = 0 + Write-Verbose "Collecting file list for artifact $($_.BaseName)" + $fileGroups = & $_ + if ($fileGroups) { + $fileGroups.GetEnumerator() | % { + $BaseDirectory = New-Object Uri ((EnsureTrailingSlash $_.Key.ToString()), [UriKind]::Absolute) + $_.Value | ? { $_ } | % { + if ($_.GetType() -eq [IO.FileInfo] -or $_.GetType() -eq [IO.DirectoryInfo]) { + $_ = $_.FullName + } + + $artifact = New-Object -TypeName PSObject + Add-Member -InputObject $artifact -MemberType NoteProperty -Name ArtifactName -Value $ArtifactName + + $SourceFullPath = New-Object Uri ($BaseDirectory, $_) + Add-Member -InputObject $artifact -MemberType NoteProperty -Name Source -Value $SourceFullPath.LocalPath + + $RelativePath = [Uri]::UnescapeDataString($BaseDirectory.MakeRelative($SourceFullPath)) + Add-Member -InputObject $artifact -MemberType NoteProperty -Name ContainerFolder -Value (Split-Path $RelativePath) + + Write-Output $artifact + $totalFileCount += 1 + } + } + } + + if ($totalFileCount -eq 0) { + Write-Warning "No files found for the `"$ArtifactName`" artifact." + } + } else { + Write-Host "Skipping $ArtifactName because it has already been staged." -ForegroundColor DarkGray + } +} diff --git a/azure-pipelines/artifacts/_pipelines.ps1 b/azure-pipelines/artifacts/_pipelines.ps1 new file mode 100644 index 00000000..47321ed5 --- /dev/null +++ b/azure-pipelines/artifacts/_pipelines.ps1 @@ -0,0 +1,45 @@ +<# +.SYNOPSIS + This script translates all the artifacts described by _all.ps1 + into commands that instruct Azure Pipelines to actually collect those artifacts. +#> + +[CmdletBinding()] +param ( + [string]$ArtifactNameSuffix, + [switch]$StageOnly, + [switch]$AvoidSymbolicLinks +) + +Function Set-PipelineVariable($name, $value) { + if ((Test-Path "Env:\$name") -and (Get-Item "Env:\$name").Value -eq $value) { + return # already set + } + + #New-Item -Path "Env:\$name".ToUpper() -Value $value -Force | Out-Null + Write-Host "##vso[task.setvariable variable=$name]$value" +} + +Function Test-ArtifactUploaded($artifactName) { + $varName = "ARTIFACTUPLOADED_$($artifactName.ToUpper())" + Test-Path "env:$varName" +} + +& "$PSScriptRoot/_stage_all.ps1" -ArtifactNameSuffix $ArtifactNameSuffix -AvoidSymbolicLinks:$AvoidSymbolicLinks |% { + # Set a variable which will out-live this script so that a subsequent attempt to collect and upload artifacts + # will skip this one from a check in the _all.ps1 script. + Set-PipelineVariable "ARTIFACTSTAGED_$($_.Name.ToUpper())" 'true' + Write-Host "Staged artifact $($_.Name) to $($_.Path)" + + if (!$StageOnly) { + if (Test-ArtifactUploaded $_.Name) { + Write-Host "Skipping $($_.Name) because it has already been uploaded." -ForegroundColor DarkGray + } else { + Write-Host "##vso[artifact.upload containerfolder=$($_.Name);artifactname=$($_.Name);]$($_.Path)" + + # Set a variable which will out-live this script so that a subsequent attempt to collect and upload artifacts + # will skip this one from a check in the _all.ps1 script. + Set-PipelineVariable "ARTIFACTUPLOADED_$($_.Name.ToUpper())" 'true' + } + } +} diff --git a/azure-pipelines/artifacts/_stage_all.ps1 b/azure-pipelines/artifacts/_stage_all.ps1 new file mode 100644 index 00000000..74d7a38d --- /dev/null +++ b/azure-pipelines/artifacts/_stage_all.ps1 @@ -0,0 +1,72 @@ +<# +.SYNOPSIS + This script links all the artifacts described by _all.ps1 + into a staging directory, reading for uploading to a cloud build artifact store. + It returns a sequence of objects with Name and Path properties. +#> + +[CmdletBinding()] +param ( + [string]$ArtifactNameSuffix, + [switch]$AvoidSymbolicLinks +) + +$ArtifactStagingFolder = & "$PSScriptRoot/../Get-ArtifactsStagingDirectory.ps1" -CleanIfLocal + +function Create-SymbolicLink { + param ( + $Link, + $Target + ) + + if ($Link -eq $Target) { + return + } + + if (Test-Path $Link) { Remove-Item $Link } + $LinkContainer = Split-Path $Link -Parent + if (!(Test-Path $LinkContainer)) { mkdir $LinkContainer } + if ($IsMacOS -or $IsLinux) { + ln $Target $Link | Out-Null + } else { + cmd /c "mklink `"$Link`" `"$Target`"" | Out-Null + } + + if ($LASTEXITCODE -ne 0) { + # Windows requires admin privileges to create symbolic links + # unless Developer Mode has been enabled. + throw "Failed to create symbolic link at $Link that points to $Target" + } +} + +# Stage all artifacts +$Artifacts = & "$PSScriptRoot\_all.ps1" -ArtifactNameSuffix $ArtifactNameSuffix +$Artifacts |% { + $DestinationFolder = [System.IO.Path]::GetFullPath("$ArtifactStagingFolder/$($_.ArtifactName)$ArtifactNameSuffix/$($_.ContainerFolder)").TrimEnd('\') + $Name = "$(Split-Path $_.Source -Leaf)" + + #Write-Host "$($_.Source) -> $($_.ArtifactName)\$($_.ContainerFolder)" -ForegroundColor Yellow + + if (-not (Test-Path $DestinationFolder)) { New-Item -ItemType Directory -Path $DestinationFolder | Out-Null } + if (Test-Path -PathType Leaf $_.Source) { # skip folders + $TargetPath = Join-Path $DestinationFolder $Name + if ($AvoidSymbolicLinks) { + Copy-Item -Path $_.Source -Destination $TargetPath + } else { + Create-SymbolicLink -Link $TargetPath -Target $_.Source + } + } +} + +$ArtifactNames = $Artifacts |% { "$($_.ArtifactName)$ArtifactNameSuffix" } +$ArtifactNames += Get-ChildItem env:ARTIFACTSTAGED_* |% { + # Return from ALLCAPS to the actual capitalization used for the artifact. + $artifactNameAllCaps = "$($_.Name.Substring('ARTIFACTSTAGED_'.Length))" + (Get-ChildItem $ArtifactStagingFolder\$artifactNameAllCaps* -Filter $artifactNameAllCaps).Name +} +$ArtifactNames | Get-Unique |% { + $artifact = New-Object -TypeName PSObject + Add-Member -InputObject $artifact -MemberType NoteProperty -Name Name -Value $_ + Add-Member -InputObject $artifact -MemberType NoteProperty -Name Path -Value (Join-Path $ArtifactStagingFolder $_) + Write-Output $artifact +} diff --git a/azure-pipelines/artifacts/build_logs.ps1 b/azure-pipelines/artifacts/build_logs.ps1 new file mode 100644 index 00000000..f05358e0 --- /dev/null +++ b/azure-pipelines/artifacts/build_logs.ps1 @@ -0,0 +1,7 @@ +$ArtifactStagingFolder = & "$PSScriptRoot/../Get-ArtifactsStagingDirectory.ps1" + +if (!(Test-Path $ArtifactStagingFolder/build_logs)) { return } + +@{ + "$ArtifactStagingFolder/build_logs" = (Get-ChildItem -Recurse "$ArtifactStagingFolder/build_logs") +} diff --git a/azure-pipelines/artifacts/coverageResults.ps1 b/azure-pipelines/artifacts/coverageResults.ps1 new file mode 100644 index 00000000..280ff9ae --- /dev/null +++ b/azure-pipelines/artifacts/coverageResults.ps1 @@ -0,0 +1,23 @@ +$RepoRoot = [System.IO.Path]::GetFullPath("$PSScriptRoot\..\..") + +$coverageFiles = @(Get-ChildItem "$RepoRoot/test/*.cobertura.xml" -Recurse | Where {$_.FullName -notlike "*/In/*" -and $_.FullName -notlike "*\In\*" }) + +# Prepare code coverage reports for merging on another machine +if ($env:SYSTEM_DEFAULTWORKINGDIRECTORY) { + Write-Host "Substituting $env:SYSTEM_DEFAULTWORKINGDIRECTORY with `"{reporoot}`"" + $coverageFiles |% { + $content = Get-Content -Path $_ |% { $_ -Replace [regex]::Escape($env:SYSTEM_DEFAULTWORKINGDIRECTORY), "{reporoot}" } + Set-Content -Path $_ -Value $content -Encoding UTF8 + } +} else { + Write-Warning "coverageResults: Azure Pipelines not detected. Machine-neutral token replacement skipped." +} + +if (!((Test-Path $RepoRoot\bin) -and (Test-Path $RepoRoot\obj))) { return } + +@{ + $RepoRoot = ( + $coverageFiles + + (Get-ChildItem "$RepoRoot\obj\*.cs" -Recurse) + ); +} diff --git a/azure-pipelines/artifacts/deployables.ps1 b/azure-pipelines/artifacts/deployables.ps1 new file mode 100644 index 00000000..94c48cdd --- /dev/null +++ b/azure-pipelines/artifacts/deployables.ps1 @@ -0,0 +1,13 @@ +$RepoRoot = [System.IO.Path]::GetFullPath("$PSScriptRoot\..\..") +$BuildConfiguration = $env:BUILDCONFIGURATION +if (!$BuildConfiguration) { + $BuildConfiguration = 'Debug' +} + +$PackagesRoot = "$RepoRoot/bin/Packages/$BuildConfiguration" + +if (!(Test-Path $PackagesRoot)) { return } + +@{ + "$PackagesRoot" = (Get-ChildItem $PackagesRoot -Recurse) +} diff --git a/azure-pipelines/artifacts/projectAssetsJson.ps1 b/azure-pipelines/artifacts/projectAssetsJson.ps1 new file mode 100644 index 00000000..d2e85ffb --- /dev/null +++ b/azure-pipelines/artifacts/projectAssetsJson.ps1 @@ -0,0 +1,9 @@ +$ObjRoot = [System.IO.Path]::GetFullPath("$PSScriptRoot\..\..\obj") + +if (!(Test-Path $ObjRoot)) { return } + +@{ + "$ObjRoot" = ( + (Get-ChildItem "$ObjRoot\project.assets.json" -Recurse) + ); +} diff --git a/azure-pipelines/artifacts/symbols.ps1 b/azure-pipelines/artifacts/symbols.ps1 new file mode 100644 index 00000000..9e2c7bd5 --- /dev/null +++ b/azure-pipelines/artifacts/symbols.ps1 @@ -0,0 +1,7 @@ +$BinPath = [System.IO.Path]::GetFullPath("$PSScriptRoot/../../bin") +if (!(Test-Path $BinPath)) { return } +$symbolfiles = & "$PSScriptRoot/../Get-SymbolFiles.ps1" -Path $BinPath | Get-Unique + +@{ + "$BinPath" = $SymbolFiles; +} diff --git a/azure-pipelines/artifacts/testResults.ps1 b/azure-pipelines/artifacts/testResults.ps1 new file mode 100644 index 00000000..301a4376 --- /dev/null +++ b/azure-pipelines/artifacts/testResults.ps1 @@ -0,0 +1,15 @@ +[CmdletBinding()] +Param( +) + +$result = @{} + +$testRoot = Resolve-Path "$PSScriptRoot\..\..\test" +$result[$testRoot] = (Get-ChildItem "$testRoot\TestResults" -Recurse -Directory | Get-ChildItem -Recurse -File) + +$testlogsPath = "$env:BUILD_ARTIFACTSTAGINGDIRECTORY\test_logs" +if (Test-Path $testlogsPath) { + $result[$testlogsPath] = Get-ChildItem "$testlogsPath\*"; +} + +$result diff --git a/azure-pipelines/artifacts/test_symbols.ps1 b/azure-pipelines/artifacts/test_symbols.ps1 new file mode 100644 index 00000000..ce2b6481 --- /dev/null +++ b/azure-pipelines/artifacts/test_symbols.ps1 @@ -0,0 +1,7 @@ +$BinPath = [System.IO.Path]::GetFullPath("$PSScriptRoot/../../bin") +if (!(Test-Path $BinPath)) { return } +$symbolfiles = & "$PSScriptRoot/../Get-SymbolFiles.ps1" -Path $BinPath -Tests | Get-Unique + +@{ + "$BinPath" = $SymbolFiles; +} diff --git a/azure-pipelines/build.yml b/azure-pipelines/build.yml new file mode 100644 index 00000000..375c422e --- /dev/null +++ b/azure-pipelines/build.yml @@ -0,0 +1,307 @@ +parameters: +##### The following parameters are not set by other YAML files that import this one, +##### but we use parameters because they support rich types and defaults. +##### Feel free to adjust their default value as needed. + +# Whether this repo uses OptProf to optimize the built binaries. +# When enabling this, be sure to update these files: +# - OptProf.targets: InstallationPath and match TestCase selection with what's in the VS repo. +# - The project file(s) for the libraries to optimize must import OptProf.targets (for multi-targeted projects, only import it for ONE target). +# - OptProf.yml: Search for LibraryName (or your library's name) and verify that those names are appropriate. +# - OptProf_part2.yml: Search for LibraryName (or your library's name) and verify that those names are appropriate. +# and create pipelines for OptProf.yml, OptProf_part2.yml +- name: EnableOptProf + type: boolean + default: false +# Whether this repo is localized. +- name: EnableLocalization + type: boolean + default: false +# Whether to run `dotnet format` as part of the build to ensure code style consistency. +# This is just one of a a few mechanisms to enforce code style consistency. +- name: EnableDotNetFormatCheck + type: boolean + default: false +# This lists the names of the artifacts that will be published *from every OS build agent*. +# Any new azure-pipelines/artifacts/*.ps1 script needs to be added to this list. +# If an artifact is only generated or collected on one OS, it should NOT be listed here, +# but should be manually added to the `outputs:` field in the appropriate OS job. +- name: artifact_names + type: object + default: + - build_logs + - coverageResults + - deployables + - projectAssetsJson + - symbols + - testResults + - test_symbols + - Variables +# The Enable*Build parameters turn non-Windows agents on or off. +# Their default value should be based on whether the build and tests are expected/required to pass on that platform. +# Callers (e.g. Official.yml) *may* expose these parameters at queue-time in order to turn OFF optional agents. +- name: EnableLinuxBuild + type: boolean + default: false +- name: EnableMacOSBuild + type: boolean + default: false + +##### ๐Ÿ‘†๐Ÿผ You MAY change the defaults above. +##### ๐Ÿ‘‡๐Ÿผ You should NOT change the defaults below. + +##### The following parameters are expected to be set by other YAML files that import this one. +##### Those without defaults require explicit values to be provided by our importers. + +# Indicates whether the entrypoint file is 1ESPT compliant. Use this parameter to switch between publish tasks to fit 1ES or non-1ES needs. +- name: Is1ESPT + type: boolean + +- name: RealSign + type: boolean + default: false + +# Whether this particular run is an OptProf profiling run. +# This is used to skip unit tests and other non-essential work to improve reliability of the OptProf pipeline. +- name: IsOptProf + type: boolean + default: false + +- name: RunTests + type: boolean + default: true + +# Whether this is a special one-off build for inserting into VS for a validation insertion PR (that will never be merged). +- name: SkipCodesignVerify + type: boolean + default: false + +- name: EnableAPIScan + type: boolean + default: false + +# This parameter exists to provide a workaround to get a build out even when no OptProf profiling outputs can be found. +# Entrypoint yaml files like official.yml should expose this as a queue-time setting when EnableOptProf is true in this file. +# The OptProf.yml entrypoint sets this parameter to true so that collecting profile data isn't blocked by a prior lack of profile data. +- name: ShouldSkipOptimize + type: boolean + default: false + +# The pool parameters are set to defaults that work in the azure-public AzDO account. +# They are overridden by callers for the devdiv AzDO account to use 1ES compliant pools. +- name: windowsPool + type: object + default: + vmImage: windows-2022 +- name: linuxPool + type: object + default: + vmImage: ubuntu-22.04 +- name: macOSPool + type: object + default: + vmImage: macOS-14 + +jobs: +- job: Windows + pool: ${{ parameters.windowsPool }} + timeoutInMinutes: 180 # Give plenty of time due to real signing + ${{ if eq(variables['system.collectionId'], '011b8bdf-6d56-4f87-be0d-0092136884d9') }}: + templateContext: + mb: + signing: + enabled: true + zipSources: false + ${{ if parameters.RealSign }}: + signType: real + ${{ else }}: + signType: test + sbom: + enabled: true + localization: + enabled: ${{ parameters.EnableLocalization }} + ${{ if eq(variables['Build.Reason'], 'pullRequest') }}: + languages: ENU,JPN + optprof: + enabled: ${{ parameters.EnableOptProf }} + ProfilingInputsDropName: $(ProfilingInputsDropName) + OptimizationInputsLookupMethod: DropPrefix + DropNamePrefix: OptimizationInputs/$(System.TeamProject)/$(Build.Repository.Name) + ShouldSkipOptimize: ${{ parameters.ShouldSkipOptimize }} + AccessToken: $(System.AccessToken) + mbpresteps: + - checkout: self + fetchDepth: 0 # avoid shallow clone so nbgv can do its work. + clean: true + - ${{ if parameters.EnableOptProf }}: + - powershell: Write-Host "##vso[task.setvariable variable=PROFILINGINPUTSDROPNAME]$(azure-pipelines/variables/ProfilingInputsDropName.ps1)" + displayName: โš™ Set ProfilingInputsDropName for optprof + + outputParentDirectory: $(Build.ArtifactStagingDirectory) + outputs: + - ${{ each artifact_name in parameters.artifact_names }}: + - ${{ if or(ne(artifact_name, 'testResults'), parameters.RunTests) }}: + - output: pipelineArtifact + displayName: ๐Ÿ“ข Publish ${{ artifact_name }}-Windows + targetPath: $(Build.ArtifactStagingDirectory)/${{ artifact_name }}-Windows + artifactName: ${{ artifact_name }}-Windows + condition: succeededOrFailed() + - output: pipelineArtifact + displayName: ๐Ÿ“ข Publish VSInsertion-Windows + targetPath: $(Build.ArtifactStagingDirectory)/VSInsertion-Windows + artifactName: VSInsertion-Windows + - ${{ if parameters.EnableLocalization }}: + - output: pipelineArtifact + displayName: ๐Ÿ“ข Publish LocBin-Windows + targetPath: $(Build.ArtifactStagingDirectory)/LocBin-Windows + artifactName: LocBin-Windows + - ${{ if parameters.EnableAPIScan }}: + - output: pipelineArtifact + displayName: ๐Ÿ“ข Publish APIScanInputs + targetPath: $(Build.ArtifactStagingDirectory)/APIScanInputs-Windows + artifactName: APIScanInputs + steps: + - ${{ if not(parameters.Is1ESPT) }}: + - checkout: self + fetchDepth: 0 # avoid shallow clone so nbgv can do its work. + clean: true + - ${{ if parameters.EnableOptProf }}: + - powershell: Write-Host "##vso[task.setvariable variable=PROFILINGINPUTSDROPNAME]$(azure-pipelines/variables/ProfilingInputsDropName.ps1)" + displayName: โš™ Set ProfilingInputsDropName for optprof + + - ${{ if eq(variables['Build.Reason'], 'Schedule') }}: + - template: schedule-only-steps.yml + + - template: install-dependencies.yml + + - script: dotnet nbgv cloud -ca + displayName: โš™ Set build number + name: nbgv + + - ${{ if eq(variables['system.collectionId'], '011b8bdf-6d56-4f87-be0d-0092136884d9') }}: + - template: microbuild.before.yml + parameters: + EnableLocalization: ${{ parameters.EnableLocalization }} + EnableOptProf: ${{ parameters.EnableOptProf }} + IsOptProf: ${{ parameters.IsOptProf }} + ShouldSkipOptimize: ${{ parameters.ShouldSkipOptimize }} + RealSign: ${{ parameters.RealSign }} + + - template: dotnet.yml + parameters: + Is1ESPT: ${{ parameters.Is1ESPT }} + RunTests: ${{ parameters.RunTests }} + IsOptProf: ${{ parameters.IsOptProf }} + + - ${{ if and(parameters.EnableDotNetFormatCheck, not(parameters.EnableLinuxBuild)) }}: + - script: dotnet format --verify-no-changes --no-restore src + displayName: ๐Ÿ’… Verify formatted code + + - ${{ if eq(variables['system.collectionId'], '011b8bdf-6d56-4f87-be0d-0092136884d9') }}: + - template: microbuild.after.yml + parameters: + EnableOptProf: ${{ parameters.EnableOptProf }} + IsOptProf: ${{ parameters.IsOptProf }} + SkipCodesignVerify: ${{ parameters.SkipCodesignVerify }} + +- ${{ if not(parameters.IsOptProf) }}: + - ${{ if parameters.EnableLinuxBuild }}: + - job: Linux + pool: ${{ parameters.linuxPool }} + ${{ if eq(variables['system.collectionId'], '011b8bdf-6d56-4f87-be0d-0092136884d9') }}: + templateContext: + mb: + ${{ if parameters.RealSign }}: + signing: + enabled: false # enable when building unique artifacts on this agent that must be signed + signType: real + outputParentDirectory: $(Build.ArtifactStagingDirectory) + outputs: + - ${{ each artifact_name in parameters.artifact_names }}: + - ${{ if or(ne(artifact_name, 'testResults'), parameters.RunTests) }}: + - output: pipelineArtifact + displayName: ๐Ÿ“ข Publish ${{ artifact_name }}-Linux + targetPath: $(Build.ArtifactStagingDirectory)/${{ artifact_name }}-Linux + artifactName: ${{ artifact_name }}-Linux + condition: succeededOrFailed() + steps: + - checkout: self + fetchDepth: 0 # avoid shallow clone so nbgv can do its work. + clean: true + - template: install-dependencies.yml + - template: dotnet.yml + parameters: + Is1ESPT: ${{ parameters.Is1ESPT }} + RunTests: ${{ parameters.RunTests }} + - ${{ if parameters.EnableDotNetFormatCheck }}: + - script: dotnet format --verify-no-changes --no-restore src + displayName: ๐Ÿ’… Verify formatted code + + - ${{ if parameters.EnableMacOSBuild }}: + - job: macOS + pool: ${{ parameters.macOSPool }} + ${{ if eq(variables['system.collectionId'], '011b8bdf-6d56-4f87-be0d-0092136884d9') }}: + templateContext: + mb: + ${{ if parameters.RealSign }}: + signing: + enabled: false # enable when building unique artifacts on this agent that must be signed + signType: real + outputParentDirectory: $(Build.ArtifactStagingDirectory) + outputs: + - ${{ each artifact_name in parameters.artifact_names }}: + - ${{ if or(ne(artifact_name, 'testResults'), parameters.RunTests) }}: + - output: pipelineArtifact + displayName: ๐Ÿ“ข Publish ${{ artifact_name }}-macOS + targetPath: $(Build.ArtifactStagingDirectory)/${{ artifact_name }}-macOS + artifactName: ${{ artifact_name }}-macOS + condition: succeededOrFailed() + steps: + - checkout: self + fetchDepth: 0 # avoid shallow clone so nbgv can do its work. + clean: true + - template: install-dependencies.yml + - template: dotnet.yml + parameters: + Is1ESPT: ${{ parameters.Is1ESPT }} + RunTests: ${{ parameters.RunTests }} + + - job: WrapUp + dependsOn: + - Windows + - ${{ if parameters.EnableLinuxBuild }}: + - Linux + - ${{ if parameters.EnableMacOSBuild }}: + - macOS + pool: ${{ parameters.windowsPool }} # Use Windows agent because PublishSymbols task requires it (https://github.com/microsoft/azure-pipelines-tasks/issues/13821). + condition: succeededOrFailed() + ${{ if eq(variables['system.collectionId'], '011b8bdf-6d56-4f87-be0d-0092136884d9') }}: + templateContext: + outputParentDirectory: $(Build.ArtifactStagingDirectory) + outputs: + - output: pipelineArtifact + displayName: ๐Ÿ“ข Publish symbols-legacy + targetPath: $(Build.ArtifactStagingDirectory)/symbols-legacy + artifactName: symbols-legacy + condition: succeededOrFailed() + steps: + - checkout: self + fetchDepth: 0 # avoid shallow clone so nbgv can do its work. + clean: true + - template: install-dependencies.yml + parameters: + initArgs: -NoRestore + - template: publish-symbols.yml + parameters: + EnableLinuxBuild: ${{ parameters.EnableLinuxBuild }} + EnableMacOSBuild: ${{ parameters.EnableMacOSBuild }} + - ${{ if parameters.RunTests }}: + - template: publish-codecoverage.yml + parameters: + EnableLinuxBuild: ${{ parameters.EnableLinuxBuild }} + EnableMacOSBuild: ${{ parameters.EnableMacOSBuild }} + + - ${{ if parameters.EnableAPIScan }}: + - template: apiscan.yml + parameters: + windowsPool: ${{ parameters.windowsPool }} diff --git a/azure-pipelines/dotnet-test-cloud.ps1 b/azure-pipelines/dotnet-test-cloud.ps1 new file mode 100644 index 00000000..56b019e9 --- /dev/null +++ b/azure-pipelines/dotnet-test-cloud.ps1 @@ -0,0 +1,86 @@ +#!/usr/bin/env pwsh + +<# +.SYNOPSIS + Runs tests as they are run in cloud test runs. +.PARAMETER Configuration + The configuration within which to run tests +.PARAMETER Agent + The name of the agent. This is used in preparing test run titles. +.PARAMETER PublishResults + A switch to publish results to Azure Pipelines. +.PARAMETER x86 + A switch to run the tests in an x86 process. +.PARAMETER dotnet32 + The path to a 32-bit dotnet executable to use. +#> +[CmdletBinding()] +Param( + [string]$Configuration = 'Debug', + [string]$Agent = 'Local', + [switch]$PublishResults, + [switch]$x86, + [string]$dotnet32 +) + +$RepoRoot = (Resolve-Path "$PSScriptRoot/..").Path +$ArtifactStagingFolder = & "$PSScriptRoot/Get-ArtifactsStagingDirectory.ps1" + +$dotnet = 'dotnet' +if ($x86) { + $x86RunTitleSuffix = ", x86" + if ($dotnet32) { + $dotnet = $dotnet32 + } + else { + $dotnet32Possibilities = "$PSScriptRoot\../obj/tools/x86/.dotnet/dotnet.exe", "$env:AGENT_TOOLSDIRECTORY/x86/dotnet/dotnet.exe", "${env:ProgramFiles(x86)}\dotnet\dotnet.exe" + $dotnet32Matches = $dotnet32Possibilities | ? { Test-Path $_ } + if ($dotnet32Matches) { + $dotnet = Resolve-Path @($dotnet32Matches)[0] + Write-Host "Running tests using `"$dotnet`"" -ForegroundColor DarkGray + } + else { + Write-Error "Unable to find 32-bit dotnet.exe" + return 1 + } + } +} + +& $dotnet test "$RepoRoot\test\Microsoft.VisualStudio.Jdt.Tests"` + --no-build ` + -c $Configuration ` + --filter "TestCategory!=FailsInCloudTest" ` + --collect "Code Coverage;Format=cobertura" ` + --settings "$PSScriptRoot/test.runsettings" ` + --blame-hang-timeout 60s ` + --blame-crash ` + -bl:"$ArtifactStagingFolder/build_logs/test.binlog" ` + --diag "$ArtifactStagingFolder/test_logs/diag.log;TraceLevel=info" ` + --logger trx ` + +$unknownCounter = 0 +Get-ChildItem -Recurse -Path $RepoRoot\test\*.trx | % { + Copy-Item $_ -Destination $ArtifactStagingFolder/test_logs/ + + if ($PublishResults) { + $x = [xml](Get-Content -Path $_) + $runTitle = $null + if ($x.TestRun.TestDefinitions -and $x.TestRun.TestDefinitions.GetElementsByTagName('UnitTest')) { + $storage = $x.TestRun.TestDefinitions.GetElementsByTagName('UnitTest')[0].storage -replace '\\', '/' + if ($storage -match '/(?net[^/]+)/(?:(?[^/]+)/)?(?[^/]+)\.dll$') { + if ($matches.rid) { + $runTitle = "$($matches.lib) ($($matches.tfm), $($matches.rid), $Agent)" + } + else { + $runTitle = "$($matches.lib) ($($matches.tfm)$x86RunTitleSuffix, $Agent)" + } + } + } + if (!$runTitle) { + $unknownCounter += 1; + $runTitle = "unknown$unknownCounter ($Agent$x86RunTitleSuffix)"; + } + + Write-Host "##vso[results.publish type=VSTest;runTitle=$runTitle;publishRunAttachments=true;resultFiles=$_;failTaskOnFailedTests=true;testRunSystem=VSTS - PTR;]" + } +} diff --git a/azure-pipelines/dotnet.yml b/azure-pipelines/dotnet.yml new file mode 100644 index 00000000..638170d1 --- /dev/null +++ b/azure-pipelines/dotnet.yml @@ -0,0 +1,46 @@ +parameters: +- name: RunTests +- name: IsOptProf + type: boolean + default: false +- name: Is1ESPT + type: boolean + +steps: + +- script: dotnet build src -t:build,pack --no-restore -c $(BuildConfiguration) /bl:"$(Build.ArtifactStagingDirectory)/build_logs/build.binlog" + displayName: ๐Ÿ›  dotnet build + +- ${{ if not(parameters.IsOptProf) }}: + - powershell: azure-pipelines/dotnet-test-cloud.ps1 -Configuration $(BuildConfiguration) -Agent $(Agent.JobName) -PublishResults + displayName: ๐Ÿงช dotnet test + condition: and(succeeded(), ${{ parameters.RunTests }}) + +- ${{ if parameters.IsOptProf }}: + - script: dotnet pack src\VSInsertionMetadata -c $(BuildConfiguration) -warnaserror /bl:"$(Build.ArtifactStagingDirectory)/build_logs/VSInsertion-Pack.binlog" + displayName: ๐Ÿ”ง dotnet pack VSInsertionMetadata + +- powershell: azure-pipelines/variables/_pipelines.ps1 + failOnStderr: true + displayName: โš™ Update pipeline variables based on build outputs + condition: succeededOrFailed() + +- ${{ if parameters.Is1ESPT }}: + - powershell: azure-pipelines/artifacts/_pipelines.ps1 -StageOnly -AvoidSymbolicLinks -ArtifactNameSuffix "-$(Agent.JobName)" -Verbose + failOnStderr: true + displayName: ๐Ÿ“ข Stage artifacts + condition: succeededOrFailed() +- ${{ else }}: + - powershell: azure-pipelines/artifacts/_pipelines.ps1 -ArtifactNameSuffix "-$(Agent.JobName)" -Verbose + failOnStderr: true + displayName: ๐Ÿ“ข Publish artifacts + condition: succeededOrFailed() + +- ${{ if and(ne(variables['codecov_token'], ''), parameters.RunTests) }}: + - powershell: | + $ArtifactStagingFolder = & "azure-pipelines/Get-ArtifactsStagingDirectory.ps1" + $CoverageResultsFolder = Join-Path $ArtifactStagingFolder "coverageResults-$(Agent.JobName)" + azure-pipelines/publish-CodeCov.ps1 -CodeCovToken "$(codecov_token)" -PathToCodeCoverage "$CoverageResultsFolder" -Name "$(Agent.JobName) Coverage Results" -Flags "$(Agent.JobName)" + displayName: ๐Ÿ“ข Publish code coverage results to codecov.io + timeoutInMinutes: 3 + continueOnError: true diff --git a/azure-pipelines/falsepositives.gdnsuppress b/azure-pipelines/falsepositives.gdnsuppress new file mode 100644 index 00000000..1248172b --- /dev/null +++ b/azure-pipelines/falsepositives.gdnsuppress @@ -0,0 +1,12 @@ +{ + "version": "latest", + "suppressionSets": { + "falsepositives": { + "name": "falsepositives", + "createdDate": "2021-12-03 00:23:08Z", + "lastUpdatedDate": "2021-12-03 00:23:08Z" + } + }, + "results": { + } +} diff --git a/azure-pipelines/install-dependencies.yml b/azure-pipelines/install-dependencies.yml new file mode 100644 index 00000000..e5d58f41 --- /dev/null +++ b/azure-pipelines/install-dependencies.yml @@ -0,0 +1,36 @@ +parameters: +- name: initArgs + type: string + default: '' +- name: needsAzurePublicFeeds + type: boolean + default: true # If nuget.config pulls from the azure-public account, we need to authenticate when building on the devdiv account. + +steps: +- ${{ if and(parameters.needsAzurePublicFeeds, eq(variables['system.collectionId'], '011b8bdf-6d56-4f87-be0d-0092136884d9')) }}: + - template: WIFtoPATauth.yml + parameters: + wifServiceConnectionName: azure-public/vside package pull + deadPATServiceConnectionId: 0ae39abc-4d06-4436-a7b5-865833df49db # azure-public/msft_consumption + +- task: NuGetAuthenticate@1 + displayName: ๐Ÿ” Authenticate NuGet feeds + inputs: + ${{ if and(parameters.needsAzurePublicFeeds, eq(variables['system.collectionId'], '011b8bdf-6d56-4f87-be0d-0092136884d9')) }}: + nuGetServiceConnections: azure-public/msft_consumption + +- powershell: | + $AccessToken = '$(System.AccessToken)' # Avoid specifying the access token directly on the init.ps1 command line to avoid it showing up in errors + .\init.ps1 -AccessToken $AccessToken ${{ parameters['initArgs'] }} -UpgradePrerequisites -NoNuGetCredProvider + dotnet --info + + # Print mono version if it is present. + if (Get-Command mono -ErrorAction SilentlyContinue) { + mono --version + } + displayName: โš™ Install prerequisites + +- powershell: azure-pipelines/variables/_pipelines.ps1 + failOnStderr: true + displayName: โš™ Set pipeline variables based on source + name: SetPipelineVariables diff --git a/azure-pipelines/justnugetorg.nuget.config b/azure-pipelines/justnugetorg.nuget.config new file mode 100644 index 00000000..765346e5 --- /dev/null +++ b/azure-pipelines/justnugetorg.nuget.config @@ -0,0 +1,7 @@ + + + + + + + diff --git a/azure-pipelines/libtemplate-update.yml b/azure-pipelines/libtemplate-update.yml new file mode 100644 index 00000000..a67a4ece --- /dev/null +++ b/azure-pipelines/libtemplate-update.yml @@ -0,0 +1,168 @@ +# This pipeline schedules regular merges of Library.Template into a repo that is based on it. +# Only Azure Repos are supported. GitHub support comes via a GitHub Actions workflow. + +trigger: none +pr: none +schedules: +- cron: "0 3 * * Mon" # Sun @ 8 or 9 PM Mountain Time (depending on DST) + displayName: Weekly trigger + branches: + include: + - main + always: true + +resources: + repositories: + - repository: MicroBuildTemplate + type: git + name: 1ESPipelineTemplates/MicroBuildTemplate + ref: refs/tags/release + +parameters: +- name: AutoComplete + displayName: Auto-complete pull request + type: boolean + default: false + +variables: +- template: GlobalVariables.yml + +extends: + template: azure-pipelines/MicroBuild.1ES.Unofficial.yml@MicroBuildTemplate + parameters: + sdl: + sourceAnalysisPool: + name: AzurePipelines-EO + demands: + - ImageOverride -equals 1ESPT-Windows2022 + + stages: + - stage: Merge + jobs: + - job: merge + pool: + name: AzurePipelines-EO + demands: + - ImageOverride -equals 1ESPT-Ubuntu22.04 + os: Linux + steps: + - checkout: self + fetchDepth: 0 + clean: true + - pwsh: | + $LibTemplateBranch = & ./azure-pipelines/Get-LibTemplateBasis.ps1 -ErrorIfNotRelated + if ($LASTEXITCODE -ne 0) { + exit $LASTEXITCODE + } + + git fetch https://github.com/aarnott/Library.Template $LibTemplateBranch + if ($LASTEXITCODE -ne 0) { + exit $LASTEXITCODE + } + $LibTemplateCommit = git rev-parse FETCH_HEAD + + if ((git rev-list FETCH_HEAD ^HEAD --count) -eq 0) { + Write-Host "There are no Library.Template updates to merge." + exit 0 + } + + $UpdateBranchName = 'auto/libtemplateUpdate' + git -c http.extraheader="AUTHORIZATION: bearer $(System.AccessToken)" push origin -f FETCH_HEAD:refs/heads/$UpdateBranchName + + Write-Host "Creating pull request" + $contentType = 'application/json'; + $headers = @{ Authorization = 'Bearer $(System.AccessToken)' }; + $rawRequest = @{ + sourceRefName = "refs/heads/$UpdateBranchName"; + targetRefName = "refs/heads/main"; + title = 'Merge latest Library.Template'; + description = "This merges the latest features and fixes from [Library.Template's $LibTemplateBranch branch](https://github.com/AArnott/Library.Template/tree/$LibTemplateBranch)."; + } + $request = ConvertTo-Json $rawRequest + + $prApiBaseUri = '$(System.TeamFoundationCollectionUri)/$(System.TeamProject)/_apis/git/repositories/$(Build.Repository.ID)/pullrequests' + $prCreationUri = $prApiBaseUri + "?api-version=6.0" + Write-Host "POST $prCreationUri" + Write-Host $request + + $prCreationResult = Invoke-RestMethod -uri $prCreationUri -method POST -Headers $headers -ContentType $contentType -Body $request + $prUrl = "$($prCreationResult.repository.webUrl)/pullrequest/$($prCreationResult.pullRequestId)" + Write-Host "Pull request: $prUrl" + $prApiBaseUri += "/$($prCreationResult.pullRequestId)" + + $SummaryPath = Join-Path '$(Agent.TempDirectory)' 'summary.md' + Set-Content -Path $SummaryPath -Value "[Insertion pull request]($prUrl)" + Write-Host "##vso[task.uploadsummary]$SummaryPath" + + # Tag the PR + $tagUri = "$prApiBaseUri/labels?api-version=7.0" + $rawRequest = @{ + name = 'auto-template-merge'; + } + $request = ConvertTo-Json $rawRequest + Invoke-RestMethod -uri $tagUri -method POST -Headers $headers -ContentType $contentType -Body $request | Out-Null + + # Add properties to the PR that we can programatically parse later. + Function Set-PRProperties($properties) { + $rawRequest = $properties.GetEnumerator() |% { + @{ + op = 'add' + path = "/$($_.key)" + from = $null + value = $_.value + } + } + $request = ConvertTo-Json $rawRequest + $setPrPropertyUri = "$prApiBaseUri/properties?api-version=7.0" + Write-Debug "$request" + $setPrPropertyResult = Invoke-RestMethod -uri $setPrPropertyUri -method PATCH -Headers $headers -ContentType 'application/json-patch+json' -Body $request -StatusCodeVariable setPrPropertyStatus -SkipHttpErrorCheck + if ($setPrPropertyStatus -ne 200) { + Write-Host "##vso[task.logissue type=warning]Failed to set pull request properties. Result: $setPrPropertyStatus. $($setPrPropertyResult.message)" + } + } + Write-Host "Setting pull request properties" + Set-PRProperties @{ + 'AutomatedMerge.SourceBranch' = $LibTemplateBranch + 'AutomatedMerge.SourceCommit' = $LibTemplateCommit + } + + # Add an *active* PR comment to warn users to *merge* the pull request instead of squash it. + $request = ConvertTo-Json @{ + comments = @( + @{ + parentCommentId = 0 + content = "Do **not** squash this pull request when completing it. You must *merge* it." + commentType = 'system' + } + ) + status = 'active' + } + $result = Invoke-RestMethod -uri "$prApiBaseUri/threads?api-version=7.1" -method POST -Headers $headers -ContentType $contentType -Body $request -StatusCodeVariable addCommentStatus -SkipHttpErrorCheck + if ($addCommentStatus -ne 200) { + Write-Host "##vso[task.logissue type=warning]Failed to post comment on pull request. Result: $addCommentStatus. $($result.message)" + } + + # Set auto-complete on the PR + if ('${{ parameters.AutoComplete }}' -eq 'True') { + Write-Host "Setting auto-complete" + $mergeMessage = "Merged PR $($prCreationResult.pullRequestId): " + $commitMessage + $rawRequest = @{ + autoCompleteSetBy = @{ + id = $prCreationResult.createdBy.id + }; + completionOptions = @{ + deleteSourceBranch = $true; + mergeCommitMessage = $mergeMessage; + mergeStrategy = 'noFastForward'; + }; + } + $request = ConvertTo-Json $rawRequest + Write-Host $request + $uri = "$($prApiBaseUri)?api-version=6.0" + $result = Invoke-RestMethod -uri $uri -method PATCH -Headers $headers -ContentType $contentType -Body $request -StatusCodeVariable autoCompleteStatus -SkipHttpErrorCheck + if ($autoCompleteStatus -ne 200) { + Write-Host "##vso[task.logissue type=warning]Failed to set auto-complete on pull request. Result: $autoCompleteStatus. $($result.message)" + } + } + + displayName: Create pull request diff --git a/azure-pipelines/microbuild.after.yml b/azure-pipelines/microbuild.after.yml new file mode 100644 index 00000000..e2107433 --- /dev/null +++ b/azure-pipelines/microbuild.after.yml @@ -0,0 +1,38 @@ +parameters: +- name: EnableOptProf + type: boolean + default: false +- name: IsOptProf + type: boolean + default: false +- name: SkipCodesignVerify + type: boolean + +steps: +- ${{ if not(parameters.SkipCodesignVerify) }}: # skip CodesignVerify on validation builds because we don't even test-sign nupkg's. + - task: MicroBuildCodesignVerify@3 + displayName: ๐Ÿ” Verify Signed Files + inputs: + ApprovalListPathForSigs: $(Build.SourcesDirectory)\azure-pipelines\no_strongname.txt + ApprovalListPathForCerts: $(Build.SourcesDirectory)\azure-pipelines\no_authenticode.txt + TargetFolders: | + $(Build.SourcesDirectory)/bin/Packages/$(BuildConfiguration) + condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT')) + +- ${{ if parameters.IsOptProf }}: + - task: ms-vscs-artifact.build-tasks.artifactDropTask-1.artifactDropTask@0 + inputs: + dropServiceURI: https://devdiv.artifacts.visualstudio.com + buildNumber: $(ProfilingInputsDropName) + sourcePath: $(Build.ArtifactStagingDirectory)\OptProf\ProfilingInputs + toLowerCase: false + usePat: true + displayName: ๐Ÿ“ข Publish to Artifact Services - ProfilingInputs + condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) + + - task: PublishBuildArtifacts@1 + inputs: + PathtoPublish: $(Build.ArtifactStagingDirectory)/InsertionOutputs + ArtifactName: InsertionOutputs + ArtifactType: Container + displayName: ๐Ÿ“ข Publish InsertionOutputs as Azure DevOps artifacts diff --git a/azure-pipelines/microbuild.before.yml b/azure-pipelines/microbuild.before.yml new file mode 100644 index 00000000..05acd319 --- /dev/null +++ b/azure-pipelines/microbuild.before.yml @@ -0,0 +1,53 @@ +parameters: +- name: EnableLocalization + type: boolean + default: false +- name: EnableOptProf + type: boolean + default: false +- name: IsOptProf + type: boolean + default: false +- name: ShouldSkipOptimize + type: boolean + default: false +- name: RealSign + type: boolean + +steps: +- ${{ if and(not(parameters.IsOptProf), ne(variables['Build.Reason'], 'PullRequest')) }}: + # notice@0 requires CG detection to run first, and non-default branches don't inject it automatically. + - ${{ if ne(variables['Build.SourceBranch'], 'refs/heads/main') }}: + - task: ComponentGovernanceComponentDetection@0 + displayName: ๐Ÿ” Component Detection + + - task: notice@0 + displayName: ๐Ÿ› ๏ธ Generate NOTICE file + inputs: + outputfile: $(System.DefaultWorkingDirectory)/obj/NOTICE + outputformat: text + retryCountOnTaskFailure: 3 # fails when the cloud service is overloaded + continueOnError: ${{ not(parameters.RealSign) }} # Tolerate failures when we're not building something that may ship. + +- ${{ if parameters.IsOptProf }}: + # We have to install these plugins ourselves for Optprof runs because those pipelines haven't migrated to 1ES PT yet. + - task: MicroBuildOptProfPlugin@6 + inputs: + ProfilingInputsDropName: $(ProfilingInputsDropName) + OptimizationInputsLookupMethod: DropPrefix + DropNamePrefix: OptimizationInputs/$(System.TeamProject)/$(Build.Repository.Name) + ShouldSkipOptimize: ${{ parameters.ShouldSkipOptimize }} + AccessToken: $(System.AccessToken) + displayName: ๐Ÿ”ง Install OptProf Plugin + + - task: MicroBuildSigningPlugin@4 + inputs: + signType: Real + zipSources: false + displayName: ๐Ÿ”ง Install MicroBuild Signing Plugin + + - ${{ if parameters.EnableLocalization }}: + - task: MicroBuildLocalizationPlugin@4 + inputs: + languages: $(LocLanguages) + displayName: ๐Ÿ”ง Install MicroBuild Localization Plugin diff --git a/azure-pipelines/no_authenticode.txt b/azure-pipelines/no_authenticode.txt new file mode 100644 index 00000000..262625ac --- /dev/null +++ b/azure-pipelines/no_authenticode.txt @@ -0,0 +1,2 @@ +bin\packages\release\vsix\_manifest\manifest.cat,sbom signed +bin\packages\release\vsix\_manifest\spdx_2.2\manifest.cat,sbom signed diff --git a/azure-pipelines/no_strongname.txt b/azure-pipelines/no_strongname.txt new file mode 100644 index 00000000..e69de29b diff --git a/azure-pipelines/official.yml b/azure-pipelines/official.yml new file mode 100644 index 00000000..e01575fa --- /dev/null +++ b/azure-pipelines/official.yml @@ -0,0 +1,129 @@ +trigger: + batch: true + branches: + include: + - main + - microbuild + - 'validate/*' + paths: + exclude: + - doc/ + - '*.md' + - .vscode/ + - azure-pipelines/release.yml + - azure-pipelines/vs-insertion.yml +#schedules: +#- cron: "0 3 * * *" # Daily @ 8 PM PST +# displayName: Daily vs-insertion +# branches: +# include: +# - microbuild + +parameters: +# As an entrypoint pipeline yml file, all parameters here show up in the Queue Run dialog. +# If any paramaters should NOT be queue-time options, they should be removed from here +# and references to them in this file replaced with hard-coded values. +- name: ForceOfficialBuild + displayName: Official build (sign, compliance, etc.) + type: boolean + default: false # this should remain false so PR builds using this pipeline are unofficial +# - name: ShouldSkipOptimize # Uncomment this and references to it below when setting EnableOptProf to true in build.yml. +# displayName: Skip OptProf optimization +# type: boolean +# default: false +- name: EnableMacOSBuild + displayName: Build on macOS + type: boolean + default: false # macOS is often bogged down in Azure Pipelines +- name: RunTests + displayName: Run tests + type: boolean + default: true +- name: EnableAPIScan + displayName: Include APIScan with compliance tools + type: boolean + default: false # enable in individual repos only AFTER updating TSAOptions.json with your own values + +resources: + repositories: + - repository: MicroBuildTemplate + type: git + name: 1ESPipelineTemplates/MicroBuildTemplate + ref: refs/tags/release + +variables: +- template: GlobalVariables.yml + +extends: + ${{ if or(parameters.ForceOfficialBuild, eq(variables['Build.Reason'],'Schedule')) }}: + template: azure-pipelines/MicroBuild.1ES.Official.yml@MicroBuildTemplate + parameters: + sdl: + sourceAnalysisPool: VSEngSS-MicroBuild2022-1ES + codeSignValidation: + enabled: true + break: true + additionalTargetsGlobPattern: -|Variables-*\*.ps1;-|APIScanInputs-*\**;-|test_symbols-*\**;-|MicroBuild\** + policheck: + enabled: true + exclusionsFile: $(System.DefaultWorkingDirectory)\azure-pipelines\PoliCheckExclusions.xml + suppression: + suppressionFile: $(System.DefaultWorkingDirectory)\azure-pipelines\falsepositives.gdnsuppress + sbom: + enabled: true + stages: + - stage: Build + variables: + - template: /azure-pipelines/BuildStageVariables.yml@self + jobs: + - template: /azure-pipelines/build.yml@self + parameters: + Is1ESPT: true + RealSign: true + # ShouldSkipOptimize: ${{ parameters.ShouldSkipOptimize }} + EnableAPIScan: ${{ and(parameters.EnableAPIScan, ne(variables['Build.Reason'], 'pullRequest')) }} + windowsPool: VSEngSS-MicroBuild2022-1ES + linuxPool: + name: AzurePipelines-EO + demands: + - ImageOverride -equals 1ESPT-Ubuntu22.04 + os: Linux + macOSPool: + name: Azure Pipelines + vmImage: macOS-12 + os: macOS + EnableMacOSBuild: ${{ parameters.EnableMacOSBuild }} + RunTests: ${{ parameters.RunTests }} + - template: /azure-pipelines/prepare-insertion-stages.yml@self + parameters: + RealSign: true + ${{ else }}: + template: azure-pipelines/MicroBuild.1ES.Unofficial.yml@MicroBuildTemplate + parameters: + sdl: + sourceAnalysisPool: VSEngSS-MicroBuild2022-1ES + suppression: + suppressionFile: $(System.DefaultWorkingDirectory)\azure-pipelines\falsepositives.gdnsuppress + stages: + - stage: Build + variables: + - template: /azure-pipelines/BuildStageVariables.yml@self + jobs: + - template: /azure-pipelines/build.yml@self + parameters: + Is1ESPT: true + RealSign: false + # ShouldSkipOptimize: ${{ parameters.ShouldSkipOptimize }} + EnableAPIScan: false + windowsPool: VSEngSS-MicroBuild2022-1ES + linuxPool: + name: AzurePipelines-EO + demands: + - ImageOverride -equals 1ESPT-Ubuntu22.04 + os: Linux + macOSPool: + name: Azure Pipelines + vmImage: macOS-12 + os: macOS + EnableMacOSBuild: ${{ parameters.EnableMacOSBuild }} + RunTests: ${{ parameters.RunTests }} diff --git a/azure-pipelines/prepare-insertion-stages.yml b/azure-pipelines/prepare-insertion-stages.yml new file mode 100644 index 00000000..e838b424 --- /dev/null +++ b/azure-pipelines/prepare-insertion-stages.yml @@ -0,0 +1,78 @@ +parameters: +- name: ArchiveSymbols + type: boolean + default: true +- name: RealSign + displayName: Real sign? + type: boolean +- name: PackagePush + type: boolean + default: false # Switch to true to enable the push job below + +stages: +- ${{ if or(parameters.ArchiveSymbols, parameters.PackagePush) }}: + - stage: release + displayName: Publish + jobs: + - ${{ if parameters.ArchiveSymbols }}: + - job: symbol_archive + displayName: Archive symbols + pool: VSEngSS-MicroBuild2022-1ES + steps: + - checkout: none + - download: current + artifact: Variables-Windows + displayName: ๐Ÿ”ป Download Variables-Windows artifact + - powershell: $(Pipeline.Workspace)/Variables-Windows/_pipelines.ps1 + displayName: โš™๏ธ Set pipeline variables based on artifacts + - download: current + artifact: symbols-legacy + displayName: ๐Ÿ”ป Download symbols-legacy artifact + - task: MicroBuildArchiveSymbols@5 + displayName: ๐Ÿ”ฃ Archive symbols to Symweb + inputs: + SymbolsFeatureName: $(SymbolsFeatureName) + SymbolsProject: VS + SymbolsAgentPath: $(Pipeline.Workspace)/symbols-legacy + + - ${{ if parameters.PackagePush }}: + - job: push + ${{ if parameters.RealSign }}: + displayName: azure-public/vs-impl feed + ${{ else }}: + displayName: devdiv/vs-impl feed # Leave this as-is, since non-signed builds must not be pushed to public feeds. + ${{ if parameters.ArchiveSymbols }}: + dependsOn: symbol_archive + pool: + name: AzurePipelines-EO + demands: + - ImageOverride -equals 1ESPT-Ubuntu22.04 + os: Linux + templateContext: + outputs: + - output: nuget + displayName: ๐Ÿ“ฆ Push nuget packages + packagesToPush: '$(Pipeline.Workspace)/deployables-Windows/NuGet/*.nupkg' + packageParentPath: $(Pipeline.Workspace)/deployables-Windows/NuGet + allowPackageConflicts: true + ${{ if parameters.RealSign }}: + nuGetFeedType: external + publishFeedCredentials: azure-public/vs-impl + ${{ else }}: + nuGetFeedType: internal + publishVstsFeed: vs-impl # Leave this as-is, since non-signed builds must not be pushed to public feeds. + steps: + - checkout: none + - download: current + artifact: Variables-Windows + displayName: ๐Ÿ”ป Download Variables-Windows artifact + - powershell: $(Pipeline.Workspace)/Variables-Windows/_pipelines.ps1 + displayName: โš™๏ธ Set pipeline variables based on artifacts + - download: current + artifact: deployables-Windows + displayName: ๐Ÿ”ป Download deployables-Windows artifact + - ${{ if parameters.RealSign }}: + - template: WIFtoPATauth.yml + parameters: + wifServiceConnectionName: azure-public/vside package push + deadPATServiceConnectionId: 207efd62-fd0f-43e7-aeae-17c4febcc660 # azure-public/vs-impl diff --git a/azure-pipelines/publish-CodeCov.ps1 b/azure-pipelines/publish-CodeCov.ps1 new file mode 100644 index 00000000..9926f018 --- /dev/null +++ b/azure-pipelines/publish-CodeCov.ps1 @@ -0,0 +1,30 @@ +<# +.SYNOPSIS + Uploads code coverage to codecov.io +.PARAMETER CodeCovToken + Code coverage token to use +.PARAMETER PathToCodeCoverage + Path to root of code coverage files +.PARAMETER Name + Name to upload with codecoverge +.PARAMETER Flags + Flags to upload with codecoverge +#> +[CmdletBinding()] +Param ( + [Parameter(Mandatory=$true)] + [string]$CodeCovToken, + [Parameter(Mandatory=$true)] + [string]$PathToCodeCoverage, + [string]$Name, + [string]$Flags +) + +$RepoRoot = (Resolve-Path "$PSScriptRoot/..").Path + +Get-ChildItem -Recurse -Path $PathToCodeCoverage -Filter "*.cobertura.xml" | % { + $relativeFilePath = Resolve-Path -relative $_.FullName + + Write-Host "Uploading: $relativeFilePath" -ForegroundColor Yellow + & (& "$PSScriptRoot/Get-CodeCovTool.ps1") -t $CodeCovToken -f $relativeFilePath -R $RepoRoot -F $Flags -n $Name +} diff --git a/azure-pipelines/publish-codecoverage.yml b/azure-pipelines/publish-codecoverage.yml new file mode 100644 index 00000000..987b2fe2 --- /dev/null +++ b/azure-pipelines/publish-codecoverage.yml @@ -0,0 +1,28 @@ +parameters: +- name: EnableMacOSBuild + type: boolean +- name: EnableLinuxBuild + type: boolean + +steps: +- download: current + artifact: coverageResults-Windows + displayName: ๐Ÿ”ป Download Windows code coverage results + continueOnError: true +- ${{ if parameters.EnableLinuxBuild }}: + - download: current + artifact: coverageResults-Linux + displayName: ๐Ÿ”ป Download Linux code coverage results + continueOnError: true +- ${{ if parameters.EnableMacOSBuild }}: + - download: current + artifact: coverageResults-macOS + displayName: ๐Ÿ”ป Download macOS code coverage results + continueOnError: true +- powershell: azure-pipelines/Merge-CodeCoverage.ps1 -Path '$(Pipeline.Workspace)' -OutputFile coveragereport/merged.cobertura.xml -Format Cobertura -Verbose + displayName: โš™ Merge coverage +- task: PublishCodeCoverageResults@2 + displayName: ๐Ÿ“ข Publish code coverage results to Azure DevOps + inputs: + summaryFileLocation: coveragereport/merged.cobertura.xml + failIfCoverageEmpty: true diff --git a/azure-pipelines/publish-symbols.yml b/azure-pipelines/publish-symbols.yml new file mode 100644 index 00000000..9078ea25 --- /dev/null +++ b/azure-pipelines/publish-symbols.yml @@ -0,0 +1,67 @@ +parameters: +- name: EnableMacOSBuild + type: boolean +- name: EnableLinuxBuild + type: boolean + +steps: +- task: DownloadPipelineArtifact@2 + inputs: + artifact: symbols-Windows + path: $(Pipeline.Workspace)/symbols/Windows + displayName: ๐Ÿ”ป Download Windows symbols + continueOnError: true +- ${{ if parameters.EnableLinuxBuild }}: + - task: DownloadPipelineArtifact@2 + inputs: + artifact: symbols-Linux + path: $(Pipeline.Workspace)/symbols/Linux + displayName: ๐Ÿ”ป Download Linux symbols + continueOnError: true +- ${{ if parameters.EnableMacOSBuild }}: + - task: DownloadPipelineArtifact@2 + inputs: + artifact: symbols-macOS + path: $(Pipeline.Workspace)/symbols/macOS + displayName: ๐Ÿ”ป Download macOS symbols + continueOnError: true + +- task: DownloadPipelineArtifact@2 + inputs: + artifact: test_symbols-Windows + path: $(Pipeline.Workspace)/test_symbols/Windows + displayName: ๐Ÿ”ป Download Windows test symbols + continueOnError: true +- ${{ if parameters.EnableLinuxBuild }}: + - task: DownloadPipelineArtifact@2 + inputs: + artifact: test_symbols-Linux + path: $(Pipeline.Workspace)/test_symbols/Linux + displayName: ๐Ÿ”ป Download Linux test symbols + continueOnError: true +- ${{ if parameters.EnableMacOSBuild }}: + - task: DownloadPipelineArtifact@2 + inputs: + artifact: test_symbols-macOS + path: $(Pipeline.Workspace)/test_symbols/macOS + displayName: ๐Ÿ”ป Download macOS test symbols + continueOnError: true + +- task: PublishSymbols@2 + inputs: + SymbolsFolder: $(Pipeline.Workspace)/symbols + SearchPattern: '**/*.pdb' + IndexSources: false + SymbolServerType: TeamServices + displayName: ๐Ÿ“ข Publish symbols + +- task: PublishSymbols@2 + inputs: + SymbolsFolder: $(Pipeline.Workspace)/test_symbols + SearchPattern: '**/*.pdb' + IndexSources: false + SymbolServerType: TeamServices + displayName: ๐Ÿ“ข Publish test symbols + +- powershell: azure-pipelines/Prepare-Legacy-Symbols.ps1 -Path $(Pipeline.Workspace)/symbols/Windows + displayName: โš™ Prepare symbols for symbol archival diff --git a/azure-pipelines/release-deployment-prep.yml b/azure-pipelines/release-deployment-prep.yml new file mode 100644 index 00000000..d9a9ffd3 --- /dev/null +++ b/azure-pipelines/release-deployment-prep.yml @@ -0,0 +1,6 @@ +steps: +- download: CI + artifact: Variables-Windows + displayName: ๐Ÿ”ป Download Variables-Windows artifact +- powershell: $(Pipeline.Workspace)/CI/Variables-Windows/_pipelines.ps1 + displayName: โš™๏ธ Set pipeline variables based on artifacts diff --git a/azure-pipelines/release.yml b/azure-pipelines/release.yml new file mode 100644 index 00000000..673934c8 --- /dev/null +++ b/azure-pipelines/release.yml @@ -0,0 +1,77 @@ +trigger: none # We only want to trigger manually or based on resources +pr: none + +resources: + repositories: + - repository: MicroBuildTemplate + type: git + name: 1ESPipelineTemplates/MicroBuildTemplate + ref: refs/tags/release + pipelines: + - pipeline: CI + source: json-document-transforms-ci # TODO: This should match the name of your CI pipeline + trigger: + tags: + - auto-release + +variables: +- template: GlobalVariables.yml + +extends: + template: azure-pipelines/MicroBuild.1ES.Official.yml@MicroBuildTemplate + parameters: + sdl: + sourceAnalysisPool: VSEngSS-MicroBuild2022-1ES + + stages: + - stage: release + jobs: + - job: release + pool: + name: AzurePipelines-EO + demands: + - ImageOverride -equals 1ESPT-Ubuntu22.04 + os: Linux + templateContext: + outputs: + - output: nuget + displayName: ๐Ÿ“ฆ Push packages to nuget.org + packagesToPush: '$(Pipeline.Workspace)/CI/deployables-Windows/NuGet/*.nupkg' + packageParentPath: $(Pipeline.Workspace)/CI/deployables-Windows/NuGet + allowPackageConflicts: true + nuGetFeedType: external + publishFeedCredentials: VisualStudioExtensibility (nuget.org) + steps: + - checkout: none + - powershell: | + Write-Host "##vso[build.updatebuildnumber]$(resources.pipeline.CI.runName)" + if ('$(resources.pipeline.CI.runName)'.Contains('-')) { + Write-Host "##vso[task.setvariable variable=IsPrerelease]true" + } else { + Write-Host "##vso[task.setvariable variable=IsPrerelease]false" + } + displayName: โš™ Set up pipeline + - download: CI + artifact: deployables-Windows + displayName: ๐Ÿ”ป Download deployables-Windows artifact + patterns: 'NuGet/*' + - task: GitHubRelease@1 + displayName: ๐Ÿ“ข GitHub release (create) + inputs: + gitHubConnection: ttstanley # TODO: fill in service connection here + repositoryName: $(Build.Repository.Name) + target: $(resources.pipeline.CI.sourceCommit) + tagSource: userSpecifiedTag + tag: v$(resources.pipeline.CI.runName) + title: v$(resources.pipeline.CI.runName) + isDraft: true # After running this step, visit the new draft release, edit, and publish. + isPreRelease: $(IsPrerelease) + assets: $(Pipeline.Workspace)/CI/deployables-Windows/NuGet/*.nupkg + changeLogCompareToRelease: lastNonDraftRelease + changeLogType: issueBased + changeLogLabels: | + [ + { "label" : "breaking change", "displayName" : "Breaking changes", "state" : "closed" }, + { "label" : "bug", "displayName" : "Fixes", "state" : "closed" }, + { "label" : "enhancement", "displayName": "Enhancements", "state" : "closed" } + ] diff --git a/azure-pipelines/schedule-only-steps.yml b/azure-pipelines/schedule-only-steps.yml new file mode 100644 index 00000000..ad07a341 --- /dev/null +++ b/azure-pipelines/schedule-only-steps.yml @@ -0,0 +1,3 @@ +steps: +- powershell: echo "##vso[build.addbuildtag]auto-insertion" + displayName: Tag for auto-insertion diff --git a/azure-pipelines/test.runsettings b/azure-pipelines/test.runsettings new file mode 100644 index 00000000..4e24a0a6 --- /dev/null +++ b/azure-pipelines/test.runsettings @@ -0,0 +1,44 @@ + + + + + + + + + \.dll$ + \.exe$ + + + xunit\..* + + + + + ^System\.Diagnostics\.DebuggerHiddenAttribute$ + ^System\.Diagnostics\.DebuggerNonUserCodeAttribute$ + ^System\.CodeDom\.Compiler\.GeneratedCodeAttribute$ + ^System\.Diagnostics\.CodeAnalysis\.ExcludeFromCodeCoverageAttribute$ + + + + + True + + True + + True + + False + + False + + False + + True + + + + + + diff --git a/azure-pipelines/variables/BusinessGroupName.ps1 b/azure-pipelines/variables/BusinessGroupName.ps1 new file mode 100644 index 00000000..00824266 --- /dev/null +++ b/azure-pipelines/variables/BusinessGroupName.ps1 @@ -0,0 +1 @@ +'Visual Studio - VS Core' diff --git a/azure-pipelines/variables/DotNetSdkVersion.ps1 b/azure-pipelines/variables/DotNetSdkVersion.ps1 new file mode 100644 index 00000000..b213fbc2 --- /dev/null +++ b/azure-pipelines/variables/DotNetSdkVersion.ps1 @@ -0,0 +1,2 @@ +$globalJson = Get-Content -Path "$PSScriptRoot\..\..\global.json" | ConvertFrom-Json +$globalJson.sdk.version diff --git a/azure-pipelines/variables/InsertJsonValues.ps1 b/azure-pipelines/variables/InsertJsonValues.ps1 new file mode 100644 index 00000000..807ca1cb --- /dev/null +++ b/azure-pipelines/variables/InsertJsonValues.ps1 @@ -0,0 +1,18 @@ +$vstsDropNames = & "$PSScriptRoot\VstsDropNames.ps1" +$BuildConfiguration = $env:BUILDCONFIGURATION +if (!$BuildConfiguration) { + $BuildConfiguration = 'Debug' +} + +$BasePath = "$PSScriptRoot\..\..\bin\Packages\$BuildConfiguration\Vsix" + +if (Test-Path $BasePath) { + $vsmanFiles = @() + Get-ChildItem $BasePath *.vsman -Recurse -File |% { + $version = (Get-Content $_.FullName | ConvertFrom-Json).info.buildVersion + $fn = $_.Name + $vsmanFiles += "LibraryName.vsman{$version}=https://vsdrop.corp.microsoft.com/file/v1/$vstsDropNames;$fn" + } + + [string]::join(',',$vsmanFiles) +} diff --git a/azure-pipelines/variables/InsertPropsValues.ps1 b/azure-pipelines/variables/InsertPropsValues.ps1 new file mode 100644 index 00000000..3ae11de9 --- /dev/null +++ b/azure-pipelines/variables/InsertPropsValues.ps1 @@ -0,0 +1,14 @@ +$InsertedPkgs = (& "$PSScriptRoot\..\artifacts\VSInsertion.ps1") + +$icv=@() +foreach ($kvp in $InsertedPkgs.GetEnumerator()) { + $kvp.Value |% { + if ($_.Name -match "^(.*?)\.(\d+\.\d+\.\d+(?:\.\d+)?(?:-.*?)?)(?:\.symbols)?\.nupkg$") { + $id = $Matches[1] + $version = $Matches[2] + $icv += "$id=$version" + } + } +} + +Write-Output ([string]::join(',',$icv)) diff --git a/azure-pipelines/variables/InsertTargetBranch.ps1 b/azure-pipelines/variables/InsertTargetBranch.ps1 new file mode 100644 index 00000000..cdcc9194 --- /dev/null +++ b/azure-pipelines/variables/InsertTargetBranch.ps1 @@ -0,0 +1,2 @@ +# This is the default branch of the VS repo that we will use to insert into VS. +'main' diff --git a/azure-pipelines/variables/InsertVersionsValues.ps1 b/azure-pipelines/variables/InsertVersionsValues.ps1 new file mode 100644 index 00000000..7475f6be --- /dev/null +++ b/azure-pipelines/variables/InsertVersionsValues.ps1 @@ -0,0 +1,11 @@ +# When you need binding redirects in the VS repo updated to match +# assemblies that you build here, remove the "return" statement +# and update the hashtable below with the T4 macro you'll use for +# your libraries as defined in the src\ProductData\AssemblyVersions.tt file. +return + +$MacroName = 'LibraryNoDotsVersion' +$SampleProject = "$PSScriptRoot\..\..\src\LibraryName" +[string]::join(',',(@{ + ($MacroName) = & { (dotnet nbgv get-version --project $SampleProject --format json | ConvertFrom-Json).AssemblyVersion }; +}.GetEnumerator() |% { "$($_.key)=$($_.value)" })) diff --git a/azure-pipelines/variables/LocLanguages.ps1 b/azure-pipelines/variables/LocLanguages.ps1 new file mode 100644 index 00000000..f38e72e2 --- /dev/null +++ b/azure-pipelines/variables/LocLanguages.ps1 @@ -0,0 +1,6 @@ +## For faster PR/CI builds localize only for 2 languages, ENU and JPN provide good enough coverage +if ($env:BUILD_REASON -eq 'PullRequest') { + 'ENU,JPN' +} else { + 'VS' +} diff --git a/azure-pipelines/variables/ProfilingInputsDropName.ps1 b/azure-pipelines/variables/ProfilingInputsDropName.ps1 new file mode 100644 index 00000000..88d21f69 --- /dev/null +++ b/azure-pipelines/variables/ProfilingInputsDropName.ps1 @@ -0,0 +1,5 @@ +if ($env:SYSTEM_TEAMPROJECT) { + "ProfilingInputs/$env:SYSTEM_TEAMPROJECT/$env:BUILD_REPOSITORY_NAME/$env:BUILD_SOURCEBRANCHNAME/$env:BUILD_BUILDID" +} else { + Write-Warning "No Azure Pipelines build detected. No Azure Pipelines drop name will be computed." +} diff --git a/azure-pipelines/variables/SymbolsFeatureName.ps1 b/azure-pipelines/variables/SymbolsFeatureName.ps1 new file mode 100644 index 00000000..a6d08bb4 --- /dev/null +++ b/azure-pipelines/variables/SymbolsFeatureName.ps1 @@ -0,0 +1 @@ +'json-document-transforms' diff --git a/azure-pipelines/variables/VstsDropNames.ps1 b/azure-pipelines/variables/VstsDropNames.ps1 new file mode 100644 index 00000000..4ff36b2c --- /dev/null +++ b/azure-pipelines/variables/VstsDropNames.ps1 @@ -0,0 +1 @@ +"Products/$env:SYSTEM_TEAMPROJECT/$env:BUILD_REPOSITORY_NAME/$env:BUILD_SOURCEBRANCHNAME/$env:BUILD_BUILDID" diff --git a/azure-pipelines/variables/_all.ps1 b/azure-pipelines/variables/_all.ps1 new file mode 100644 index 00000000..cc6e8810 --- /dev/null +++ b/azure-pipelines/variables/_all.ps1 @@ -0,0 +1,20 @@ +#!/usr/bin/env pwsh + +<# +.SYNOPSIS + This script returns a hashtable of build variables that should be set + at the start of a build or release definition's execution. +#> + +[CmdletBinding(SupportsShouldProcess = $true)] +param ( +) + +$vars = @{} + +Get-ChildItem "$PSScriptRoot\*.ps1" -Exclude "_*" |% { + Write-Host "Computing $($_.BaseName) variable" + $vars[$_.BaseName] = & $_ +} + +$vars diff --git a/azure-pipelines/variables/_pipelines.ps1 b/azure-pipelines/variables/_pipelines.ps1 new file mode 100644 index 00000000..11748b81 --- /dev/null +++ b/azure-pipelines/variables/_pipelines.ps1 @@ -0,0 +1,31 @@ +<# +.SYNOPSIS + This script translates the variables returned by the _all.ps1 script + into commands that instruct Azure Pipelines to actually set those variables for other pipeline tasks to consume. + + The build or release definition may have set these variables to override + what the build would do. So only set them if they have not already been set. +#> + +[CmdletBinding()] +param ( +) + +(& "$PSScriptRoot\_all.ps1").GetEnumerator() |% { + # 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. + $keyCaps = $_.Key.ToUpper() + if ((Test-Path "env:$keyCaps") -and (Get-Content "env:$keyCaps")) { + Write-Host "Skipping setting $keyCaps because variable is already set to '$(Get-Content env:$keyCaps)'." -ForegroundColor Cyan + } else { + Write-Host "$keyCaps=$($_.Value)" -ForegroundColor Yellow + if ($env:TF_BUILD) { + # Create two variables: the first that can be used by its simple name and accessible only within this job. + Write-Host "##vso[task.setvariable variable=$keyCaps]$($_.Value)" + # and the second that works across jobs and stages but must be fully qualified when referenced. + Write-Host "##vso[task.setvariable variable=$keyCaps;isOutput=true]$($_.Value)" + } elseif ($env:GITHUB_ACTIONS) { + Add-Content -Path $env:GITHUB_ENV -Value "$keyCaps=$($_.Value)" + } + Set-Item -Path "env:$keyCaps" -Value $_.Value + } +} diff --git a/azure-pipelines/vs-insertion.yml b/azure-pipelines/vs-insertion.yml new file mode 100644 index 00000000..add241f0 --- /dev/null +++ b/azure-pipelines/vs-insertion.yml @@ -0,0 +1,76 @@ +trigger: none # We only want to trigger manually or based on resources +pr: none + +resources: + repositories: + - repository: MicroBuildTemplate + type: git + name: 1ESPipelineTemplates/MicroBuildTemplate + ref: refs/tags/release + pipelines: + - pipeline: CI + source: Library # TODO: This should match the name of your CI pipeline + tags: + - Real signed + trigger: + tags: + - Real signed + - auto-insertion + +variables: +- template: GlobalVariables.yml + +extends: + template: azure-pipelines/MicroBuild.1ES.Official.yml@MicroBuildTemplate + parameters: + sdl: + sourceAnalysisPool: VSEngSS-MicroBuild2022-1ES + + stages: + - stage: insertion + jobs: + - job: insertion + displayName: VS insertion + pool: VSEngSS-MicroBuild2022-1ES + steps: + - checkout: none + - powershell: Write-Host "##vso[build.updatebuildnumber]$(resources.pipeline.CI.runName)" + displayName: โš™๏ธ Set pipeline name + - template: azure-pipelines/release-deployment-prep.yml@self + - download: CI + artifact: VSInsertion-Windows + displayName: ๐Ÿ”ป Download VSInsertion-Windows artifact + - ${{ if eq(variables['ContainsVsix'], 'true') }}: + - task: 1ES.MicroBuildVstsDrop@1 + displayName: ๐Ÿ”บ Upload VSTS Drop + inputs: + dropFolder: $(Pipeline.Workspace)/CI/VSInsertion-windows/Vsix + dropName: $(VstsDropNames) + accessToken: $(System.AccessToken) + - task: 1ES.PublishNuget@1 + displayName: ๐Ÿ“ฆ Push VS-repo packages to VS feed + inputs: + packagesToPush: '$(Pipeline.Workspace)/CI/VSInsertion-Windows/*.nupkg' + packageParentPath: $(Pipeline.Workspace)/CI/VSInsertion-Windows + allowPackageConflicts: true + publishVstsFeed: VS + - task: MicroBuildInsertVsPayload@4 + displayName: ๐Ÿญ Insert VS Payload + inputs: + TeamName: $(TeamName) + TeamEmail: $(TeamEmail) + InsertionPayloadName: $(Build.Repository.Name) $(Build.BuildNumber) + InsertionBuildPolicy: Request Perf DDRITs + InsertionReviewers: $(Build.RequestedFor) # Append `,Your team name` (without quotes) + AutoCompletePR: true + AutoCompleteMergeStrategy: Squash + ShallowClone: true + - powershell: | + $contentType = 'application/json'; + $headers = @{ Authorization = 'Bearer $(System.AccessToken)' }; + $rawRequest = @{ daysValid = 365 * 2; definitionId = $(resources.pipeline.CI.pipelineID); ownerId = 'User:$(Build.RequestedForId)'; protectPipeline = $false; runId = $(resources.pipeline.CI.runId) }; + $request = ConvertTo-Json @($rawRequest); + Write-Host $request + $uri = "$(System.CollectionUri)$(System.TeamProject)/_apis/build/retention/leases?api-version=6.0-preview.1"; + Invoke-RestMethod -uri $uri -method POST -Headers $headers -ContentType $contentType -Body $request; + displayName: ๐Ÿ—ป Retain inserted builds diff --git a/azure-pipelines/vs-validation.yml b/azure-pipelines/vs-validation.yml new file mode 100644 index 00000000..1bb9f22e --- /dev/null +++ b/azure-pipelines/vs-validation.yml @@ -0,0 +1,113 @@ +# This is a top-level pipeline file, which is designed to be added as an optional PR build policy +# so that a VS insertion and all the validation that entails can be done before ever merging the PR +# in its original repo. + +trigger: none # We only want to trigger manually or based on resources +pr: none + +resources: + repositories: + - repository: MicroBuildTemplate + type: git + name: 1ESPipelineTemplates/MicroBuildTemplate + ref: refs/tags/release + +variables: +- template: GlobalVariables.yml +- name: MicroBuild_NuPkgSigningEnabled + value: false # test-signed nuget packages fail to restore in the VS insertion PR validations. Just don't sign them *at all*. + +extends: + template: azure-pipelines/MicroBuild.1ES.Unofficial.yml@MicroBuildTemplate + parameters: + sdl: + sourceAnalysisPool: VSEngSS-MicroBuild2022-1ES + + stages: + - stage: Build + variables: + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true + NUGET_PACKAGES: $(Agent.TempDirectory)/.nuget/packages/ + BuildConfiguration: Release + SkipCodesignVerify: true + + jobs: + - template: /azure-pipelines/build.yml@self + parameters: + Is1ESPT: true + RealSign: false + windowsPool: VSEngSS-MicroBuild2022-1ES + linuxPool: + name: AzurePipelines-EO + demands: + - ImageOverride -equals 1ESPT-Ubuntu22.04 + os: Linux + macOSPool: + name: Azure Pipelines + vmImage: macOS-12 + os: macOS + EnableMacOSBuild: false + RunTests: false + SkipCodesignVerify: true + + - template: /azure-pipelines/prepare-insertion-stages.yml@self + parameters: + ArchiveSymbols: false + RealSign: false + + - stage: insertion + displayName: VS insertion + jobs: + - job: insertion + displayName: VS insertion + pool: VSEngSS-MicroBuild2022-1ES + steps: + - checkout: self + clean: true + fetchDepth: 1 + - download: current + artifact: Variables-Windows + displayName: ๐Ÿ”ป Download Variables-Windows artifact + - powershell: $(Pipeline.Workspace)/Variables-Windows/_pipelines.ps1 + displayName: โš™๏ธ Set pipeline variables based on artifacts + - download: current + artifact: VSInsertion-Windows + displayName: ๐Ÿ”ป Download VSInsertion-Windows artifact + - ${{ if eq(variables['ContainsVsix'], 'true') }}: + - task: 1ES.MicroBuildVstsDrop@1 + displayName: ๐Ÿ”บ Upload VSTS Drop + inputs: + dropFolder: $(Pipeline.Workspace)/VSInsertion-windows/Vsix + dropName: $(VstsDropNames) + accessToken: $(System.AccessToken) + - task: 1ES.PublishNuget@1 + displayName: ๐Ÿ“ฆ Push VS-repo packages to VS feed + inputs: + packagesToPush: '$(Pipeline.Workspace)/VSInsertion-Windows/*.nupkg' + packageParentPath: $(Pipeline.Workspace)/VSInsertion-Windows + allowPackageConflicts: true + publishVstsFeed: VS + - task: MicroBuildInsertVsPayload@4 + displayName: ๐Ÿญ Insert VS Payload + inputs: + TeamName: $(TeamName) + TeamEmail: $(TeamEmail) + InsertionPayloadName: $(Build.Repository.Name) VALIDATION BUILD $(Build.BuildNumber) ($(Build.SourceBranch)) [Skip-SymbolCheck] [Skip-HashCheck] [Skip-SignCheck] + InsertionDescription: | + This PR is for **validation purposes only** for !$(System.PullRequest.PullRequestId). **Do not complete**. + CustomScriptExecutionCommand: src/VSSDK/NuGet/AllowUnstablePackages.ps1 + InsertionBuildPolicy: Request Perf DDRITs + InsertionReviewers: $(Build.RequestedFor) + 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) + AutoCompletePR: false + ShallowClone: true + - powershell: | + $insertionPRId = azure-pipelines/Get-InsertionPRId.ps1 + $Markdown = @" + Validation insertion pull request created: !$insertionPRId + Please check status there before proceeding to merge this PR. + Remember to Abandon and (if allowed) to Delete Source Branch on that insertion PR when validation is complete. + "@ + azure-pipelines/PostPRMessage.ps1 -AccessToken '$(System.AccessToken)' -Markdown $Markdown -Verbose + displayName: โœ๏ธ Comment on pull request + condition: and(succeeded(), eq(variables['Build.Reason'], 'PullRequest')) diff --git a/global.json b/global.json new file mode 100644 index 00000000..9b900542 --- /dev/null +++ b/global.json @@ -0,0 +1,10 @@ +{ + "sdk": { + "version": "9.0.100", + "rollForward": "patch", + "allowPrerelease": false + }, + "msbuild-sdks": { + "Microsoft.Build.NoTargets": "3.7.56" + } +} diff --git a/init.cmd b/init.cmd new file mode 100644 index 00000000..667efabb --- /dev/null +++ b/init.cmd @@ -0,0 +1,20 @@ +@echo off +SETLOCAL +set PS1UnderCmd=1 + +:: Get the datetime in a format that can go in a filename. +set _my_datetime=%date%_%time% +set _my_datetime=%_my_datetime: =_% +set _my_datetime=%_my_datetime::=% +set _my_datetime=%_my_datetime:/=_% +set _my_datetime=%_my_datetime:.=_% +set CmdEnvScriptPath=%temp%\envvarscript_%_my_datetime%.cmd + +powershell.exe -NoProfile -NoLogo -ExecutionPolicy bypass -Command "try { & '%~dpn0.ps1' %*; exit $LASTEXITCODE } catch { write-host $_; exit 1 }" + +:: Set environment variables in the parent cmd.exe process. +IF EXIST "%CmdEnvScriptPath%" ( + ENDLOCAL + CALL "%CmdEnvScriptPath%" + DEL "%CmdEnvScriptPath%" +) diff --git a/init.ps1 b/init.ps1 new file mode 100644 index 00000000..5a31bea7 --- /dev/null +++ b/init.ps1 @@ -0,0 +1,175 @@ +#!/usr/bin/env pwsh + +<# +.SYNOPSIS + Installs dependencies required to build and test the projects in this repository. +.DESCRIPTION + This MAY not require elevation, as the SDK and runtimes are installed to a per-user location, + unless the `-InstallLocality` switch is specified directing to a per-repo or per-machine location. + See detailed help on that switch for more information. + + 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. + 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. +.PARAMETER InstallLocality + A value indicating whether dependencies should be installed locally to the repo or at a per-user location. + Per-user allows sharing the installed dependencies across repositories and allows use of a shared expanded package cache. + Visual Studio will only notice and use these SDKs/runtimes if VS is launched from the environment that runs this script. + Per-repo allows for high isolation, allowing for a more precise recreation of the environment within an Azure Pipelines build. + When using 'repo', environment variables are set to cause the locally installed dotnet SDK to be used. + Per-repo can lead to file locking issues when dotnet.exe is left running as a build server and can be mitigated by running `dotnet build-server shutdown`. + Per-machine requires elevation and will download and install all SDKs and runtimes to machine-wide locations so all applications can find it. +.PARAMETER NoPrerequisites + Skips the installation of prerequisite software (e.g. SDKs, tools). +.PARAMETER NoNuGetCredProvider + Skips the installation of the NuGet credential provider. Useful in pipelines with the `NuGetAuthenticate` task, as a workaround for https://github.com/microsoft/artifacts-credprovider/issues/244. + This switch is ignored and installation is skipped when -NoPrerequisites is specified. +.PARAMETER UpgradePrerequisites + Takes time to install prerequisites even if they are already present in case they need to be upgraded. + No effect if -NoPrerequisites is specified. +.PARAMETER NoRestore + Skips the package restore step. +.PARAMETER NoToolRestore + Skips the dotnet tool restore step. +.PARAMETER Signing + Install the MicroBuild signing plugin for building test-signed builds on desktop machines. +.PARAMETER Localization + Install the MicroBuild localization plugin for building loc builds on desktop machines. + The environment is configured to build pseudo-loc for JPN only, but may be used to build + all languages with shipping-style loc by using the `/p:loctype=full,loclanguages=vs` + when building. +.PARAMETER Setup + Install the MicroBuild setup plugin for building VSIXv3 packages. +.PARAMETER OptProf + Install the MicroBuild OptProf plugin for building optimized assemblies on desktop machines. +.PARAMETER Sbom + Install the MicroBuild SBOM plugin. +.PARAMETER AccessToken + An optional access token for authenticating to Azure Artifacts authenticated feeds. +.PARAMETER Interactive + Runs NuGet restore in interactive mode. This can turn authentication failures into authentication challenges. +#> +[CmdletBinding(SupportsShouldProcess = $true)] +Param ( + [ValidateSet('repo', 'user', 'machine')] + [string]$InstallLocality = 'user', + [Parameter()] + [switch]$NoPrerequisites, + [Parameter()] + [switch]$NoNuGetCredProvider, + [Parameter()] + [switch]$UpgradePrerequisites, + [Parameter()] + [switch]$NoRestore, + [Parameter()] + [switch]$NoToolRestore, + [Parameter()] + [switch]$Signing, + [Parameter()] + [switch]$Localization, + [Parameter()] + [switch]$Setup, + [Parameter()] + [switch]$OptProf, + [Parameter()] + [switch]$SBOM, + [Parameter()] + [string]$AccessToken, + [Parameter()] + [switch]$Interactive +) + +$EnvVars = @{} +$PrependPath = @() + +if (!$NoPrerequisites) { + if (!$NoNuGetCredProvider) { + & "$PSScriptRoot\tools\Install-NuGetCredProvider.ps1" -AccessToken $AccessToken -Force:$UpgradePrerequisites + } + + & "$PSScriptRoot\tools\Install-DotNetSdk.ps1" -InstallLocality $InstallLocality + if ($LASTEXITCODE -eq 3010) { + Exit 3010 + } + + # The procdump tool and env var is required for dotnet test to collect hang/crash dumps of tests. + # But it only works on Windows. + if ($env:OS -eq 'Windows_NT') { + $EnvVars['PROCDUMP_PATH'] = & "$PSScriptRoot\azure-pipelines\Get-ProcDump.ps1" + } +} + +# Workaround nuget credential provider bug that causes very unreliable package restores on Azure Pipelines +$env:NUGET_PLUGIN_HANDSHAKE_TIMEOUT_IN_SECONDS = 20 +$env:NUGET_PLUGIN_REQUEST_TIMEOUT_IN_SECONDS = 20 + +Push-Location $PSScriptRoot +try { + $HeaderColor = 'Green' + + $RestoreArguments = @() + if ($Interactive) { + $RestoreArguments += '--interactive' + } + + if (!$NoRestore -and $PSCmdlet.ShouldProcess("NuGet packages", "Restore")) { + Write-Host "Restoring NuGet packages" -ForegroundColor $HeaderColor + dotnet restore @RestoreArguments src + if ($lastexitcode -ne 0) { + throw "Failure while restoring packages." + } + } + + if (!$NoToolRestore -and $PSCmdlet.ShouldProcess("dotnet tool", "restore")) { + dotnet tool restore @RestoreArguments + if ($lastexitcode -ne 0) { + throw "Failure while restoring dotnet CLI tools." + } + } + + $InstallNuGetPkgScriptPath = "$PSScriptRoot\azure-pipelines\Install-NuGetPackage.ps1" + $nugetVerbosity = 'quiet' + if ($Verbose) { $nugetVerbosity = 'normal' } + $MicroBuildPackageSource = 'https://pkgs.dev.azure.com/devdiv/_packaging/MicroBuildToolset%40Local/nuget/v3/index.json' + if ($Signing) { + Write-Host "Installing MicroBuild signing plugin" -ForegroundColor $HeaderColor + & $InstallNuGetPkgScriptPath MicroBuild.Plugins.Signing -source $MicroBuildPackageSource -Verbosity $nugetVerbosity + $EnvVars['SignType'] = "Test" + } + + if ($Setup) { + Write-Host "Installing MicroBuild SwixBuild plugin..." -ForegroundColor $HeaderColor + & $InstallNuGetPkgScriptPath Microsoft.VisualStudioEng.MicroBuild.Plugins.SwixBuild -source $MicroBuildPackageSource -Verbosity $nugetVerbosity + } + + if ($OptProf) { + Write-Host "Installing MicroBuild OptProf plugin" -ForegroundColor $HeaderColor + & $InstallNuGetPkgScriptPath MicroBuild.Plugins.OptProf -source $MicroBuildPackageSource -Verbosity $nugetVerbosity + $EnvVars['OptProfEnabled'] = '1' + } + + if ($Localization) { + Write-Host "Installing MicroBuild localization plugin" -ForegroundColor $HeaderColor + & $InstallNuGetPkgScriptPath MicroBuild.Plugins.Localization -source $MicroBuildPackageSource -Verbosity $nugetVerbosity + $EnvVars['LocType'] = "Pseudo" + $EnvVars['LocLanguages'] = "JPN" + } + + if ($SBOM) { + Write-Host "Installing MicroBuild SBOM plugin" -ForegroundColor $HeaderColor + & $InstallNuGetPkgScriptPath MicroBuild.Plugins.Sbom -source $MicroBuildPackageSource -Verbosity $nugetVerbosity + # The feed with the latest versions of the tool is at 'https://1essharedassets.pkgs.visualstudio.com/1esPkgs/_packaging/SBOMTool/nuget/v3/index.json', + # but we'll use the feed that the SBOM task itself uses to install the tool for consistency. + $PkgMicrosoft_ManifestTool_CrossPlatform = & $InstallNuGetPkgScriptPath Microsoft.ManifestTool.CrossPlatform -source $MicroBuildPackageSource -Verbosity $nugetVerbosity + $EnvVars['GenerateSBOM'] = "true" + $EnvVars['PkgMicrosoft_ManifestTool_CrossPlatform'] = $PkgMicrosoft_ManifestTool_CrossPlatform + } + + & "$PSScriptRoot/tools/Set-EnvVars.ps1" -Variables $EnvVars -PrependPath $PrependPath | Out-Null +} +catch { + Write-Error $error[0] + exit $lastexitcode +} +finally { + Pop-Location +} diff --git a/nuget.config b/nuget.config index 9eddc6be..efd1a949 100644 --- a/nuget.config +++ b/nuget.config @@ -1,4 +1,4 @@ - +๏ปฟ diff --git a/src/.editorconfig b/src/.editorconfig new file mode 100644 index 00000000..e69de29b diff --git a/src/AssemblyInfo.cs b/src/AssemblyInfo.cs new file mode 100644 index 00000000..9731a830 --- /dev/null +++ b/src/AssemblyInfo.cs @@ -0,0 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Runtime.InteropServices; + +[assembly: DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)] diff --git a/src/AssemblyInfo.vb b/src/AssemblyInfo.vb new file mode 100644 index 00000000..75fe6ea4 --- /dev/null +++ b/src/AssemblyInfo.vb @@ -0,0 +1,6 @@ +' Copyright (c) Microsoft Corporation. All rights reserved. +' Licensed under the MIT license. See LICENSE file in the project root for full license information. + +Imports System.Runtime.InteropServices + + diff --git a/src/Directory.Build.props b/src/Directory.Build.props index ca3652d2..9ba7818d 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,23 +1,12 @@ - - + + + - Debug - AnyCPU - $(MSBuildThisFileDirectory)..\bin\ - $(MSBuildThisFileDirectory)..\obj\$(MSBuildProjectName)\ - $(BaseIntermediateOutputPath)$(Configuration)\ - $(BaseOutputPath)$(Configuration)\ - $(OutputPath)packages\ - true + README.md - - - - - - - + + diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets new file mode 100644 index 00000000..654f5c6d --- /dev/null +++ b/src/Directory.Build.targets @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Microsoft.VisualStudio.Jdt.Tests.csproj b/src/Microsoft.VisualStudio.Jdt.Tests/Microsoft.VisualStudio.Jdt.Tests.csproj deleted file mode 100644 index 666def7b..00000000 --- a/src/Microsoft.VisualStudio.Jdt.Tests/Microsoft.VisualStudio.Jdt.Tests.csproj +++ /dev/null @@ -1,33 +0,0 @@ -๏ปฟ - - - net472;net5.0 - false - $(NoWarn);CS1591 - - - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - - - - - - - - - diff --git a/src/Microsoft.VisualStudio.Jdt/IJsonTransformationLogger.cs b/src/Microsoft.VisualStudio.Jdt/IJsonTransformationLogger.cs index 2d91942c..09a82c3a 100644 --- a/src/Microsoft.VisualStudio.Jdt/IJsonTransformationLogger.cs +++ b/src/Microsoft.VisualStudio.Jdt/IJsonTransformationLogger.cs @@ -1,5 +1,5 @@ ๏ปฟ// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Jdt { diff --git a/src/Microsoft.VisualStudio.Jdt/JdtException.cs b/src/Microsoft.VisualStudio.Jdt/JdtException.cs index 756c3807..658179c2 100644 --- a/src/Microsoft.VisualStudio.Jdt/JdtException.cs +++ b/src/Microsoft.VisualStudio.Jdt/JdtException.cs @@ -1,5 +1,5 @@ ๏ปฟ// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Jdt { @@ -12,17 +12,17 @@ namespace Microsoft.VisualStudio.Jdt public enum ErrorLocation { /// - /// Represents no location set + /// Represents no location set. /// None, /// - /// Represents the source file + /// Represents the source file. /// Source, /// - /// Represents the transform file + /// Represents the transform file. /// Transform, } diff --git a/src/Microsoft.VisualStudio.Jdt/JdtExtensions.cs b/src/Microsoft.VisualStudio.Jdt/JdtExtensions.cs index dd07bc41..7984e907 100644 --- a/src/Microsoft.VisualStudio.Jdt/JdtExtensions.cs +++ b/src/Microsoft.VisualStudio.Jdt/JdtExtensions.cs @@ -1,5 +1,5 @@ ๏ปฟ// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Jdt { @@ -58,7 +58,7 @@ internal static JObject CloneWithLineInfo(this JObject objectToClone) LineInfoHandling = JdtUtilities.GetLineInfoHandling(), }; - using (var objectReader = objectToClone.CreateReader()) + using (Newtonsoft.Json.JsonReader objectReader = objectToClone.CreateReader()) { return JObject.Load(objectReader, loadSettings); } diff --git a/src/Microsoft.VisualStudio.Jdt/JdtUtilities.cs b/src/Microsoft.VisualStudio.Jdt/JdtUtilities.cs index f0a45ade..b9f2e3d9 100644 --- a/src/Microsoft.VisualStudio.Jdt/JdtUtilities.cs +++ b/src/Microsoft.VisualStudio.Jdt/JdtUtilities.cs @@ -1,5 +1,5 @@ ๏ปฟ// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Jdt { diff --git a/src/Microsoft.VisualStudio.Jdt/JsonTransformation.cs b/src/Microsoft.VisualStudio.Jdt/JsonTransformation.cs index f661cb44..3339a34c 100644 --- a/src/Microsoft.VisualStudio.Jdt/JsonTransformation.cs +++ b/src/Microsoft.VisualStudio.Jdt/JsonTransformation.cs @@ -1,5 +1,5 @@ ๏ปฟ// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Jdt { diff --git a/src/Microsoft.VisualStudio.Jdt/JsonTransformationContextLogger.cs b/src/Microsoft.VisualStudio.Jdt/JsonTransformationContextLogger.cs index bc3841a8..ed46d45c 100644 --- a/src/Microsoft.VisualStudio.Jdt/JsonTransformationContextLogger.cs +++ b/src/Microsoft.VisualStudio.Jdt/JsonTransformationContextLogger.cs @@ -1,5 +1,5 @@ ๏ปฟ// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Jdt { diff --git a/src/Microsoft.VisualStudio.Jdt/Microsoft.VisualStudio.Jdt.csproj b/src/Microsoft.VisualStudio.Jdt/Microsoft.VisualStudio.Jdt.csproj index 64775cc7..3e4c39ba 100644 --- a/src/Microsoft.VisualStudio.Jdt/Microsoft.VisualStudio.Jdt.csproj +++ b/src/Microsoft.VisualStudio.Jdt/Microsoft.VisualStudio.Jdt.csproj @@ -2,7 +2,7 @@ library - netstandard1.5;net472 + netstandard2.0;net472 @@ -13,20 +13,24 @@ json transformation transforms file-transform jdt https://aka.ms/VsExtensibilityIcon true - https://raw.githubusercontent.com/Microsoft/json-document-transforms/master/LICENSE https://github.com/Microsoft/json-document-transforms https://github.com/Microsoft/json-document-transforms True - - + + - - - + + + + + + + + @@ -44,9 +48,4 @@ - - - - - diff --git a/src/Microsoft.VisualStudio.Jdt/Processors/Attributes/JdtAttributeExtensions.cs b/src/Microsoft.VisualStudio.Jdt/Processors/Attributes/JdtAttributeExtensions.cs index 91508afa..7d32c875 100644 --- a/src/Microsoft.VisualStudio.Jdt/Processors/Attributes/JdtAttributeExtensions.cs +++ b/src/Microsoft.VisualStudio.Jdt/Processors/Attributes/JdtAttributeExtensions.cs @@ -1,5 +1,5 @@ ๏ปฟ// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Jdt { diff --git a/src/Microsoft.VisualStudio.Jdt/Processors/Attributes/JdtAttributeValidator.cs b/src/Microsoft.VisualStudio.Jdt/Processors/Attributes/JdtAttributeValidator.cs index 0e8f6e06..bcb43a68 100644 --- a/src/Microsoft.VisualStudio.Jdt/Processors/Attributes/JdtAttributeValidator.cs +++ b/src/Microsoft.VisualStudio.Jdt/Processors/Attributes/JdtAttributeValidator.cs @@ -1,5 +1,5 @@ ๏ปฟ// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Jdt { @@ -14,17 +14,17 @@ namespace Microsoft.VisualStudio.Jdt internal enum JdtAttributes { /// - /// Represents an non existant attribute + /// Represents an non existant attribute. /// None = 0, /// - /// The JDT path attribute + /// The JDT path attribute. /// Path, /// - /// The JDT path attribute + /// The JDT path attribute. /// Value, } diff --git a/src/Microsoft.VisualStudio.Jdt/Processors/JdtArrayProcessor.cs b/src/Microsoft.VisualStudio.Jdt/Processors/JdtArrayProcessor.cs index 7561be80..e2e0a59b 100644 --- a/src/Microsoft.VisualStudio.Jdt/Processors/JdtArrayProcessor.cs +++ b/src/Microsoft.VisualStudio.Jdt/Processors/JdtArrayProcessor.cs @@ -1,5 +1,5 @@ ๏ปฟ// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Jdt { diff --git a/src/Microsoft.VisualStudio.Jdt/Processors/JdtDefault.cs b/src/Microsoft.VisualStudio.Jdt/Processors/JdtDefault.cs index 4da52d4a..e7c37876 100644 --- a/src/Microsoft.VisualStudio.Jdt/Processors/JdtDefault.cs +++ b/src/Microsoft.VisualStudio.Jdt/Processors/JdtDefault.cs @@ -1,5 +1,5 @@ ๏ปฟ// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Jdt { diff --git a/src/Microsoft.VisualStudio.Jdt/Processors/JdtMerge.cs b/src/Microsoft.VisualStudio.Jdt/Processors/JdtMerge.cs index 3a9d1d2d..f83ee2d6 100644 --- a/src/Microsoft.VisualStudio.Jdt/Processors/JdtMerge.cs +++ b/src/Microsoft.VisualStudio.Jdt/Processors/JdtMerge.cs @@ -1,5 +1,5 @@ ๏ปฟ// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Jdt { @@ -52,7 +52,7 @@ protected override bool ProcessCore(JObject source, JToken transformValue, JsonT private void MergeWithObject(JObject source, JObject mergeObject, JsonTransformationContextLogger logger) { - var attributes = this.attributeValidator.ValidateAndReturnAttributes(mergeObject); + Dictionary attributes = this.attributeValidator.ValidateAndReturnAttributes(mergeObject); // If there are attributes, handle them accordingly if (attributes.Any()) diff --git a/src/Microsoft.VisualStudio.Jdt/Processors/JdtProcessor.ProcessorChain.cs b/src/Microsoft.VisualStudio.Jdt/Processors/JdtProcessor.ProcessorChain.cs index 556d9c5a..e98e2e37 100644 --- a/src/Microsoft.VisualStudio.Jdt/Processors/JdtProcessor.ProcessorChain.cs +++ b/src/Microsoft.VisualStudio.Jdt/Processors/JdtProcessor.ProcessorChain.cs @@ -1,5 +1,5 @@ ๏ปฟ// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Jdt { @@ -37,9 +37,9 @@ public JdtProcessorChain() // The successor of each transform processor should be the next one on the list // The last processor defaults to the end of chain processor - var processorsEnumerator = this.processors.GetEnumerator(); + List.Enumerator processorsEnumerator = this.processors.GetEnumerator(); processorsEnumerator.MoveNext(); - foreach (var successor in this.processors.Skip(1)) + foreach (JdtProcessor? successor in this.processors.Skip(1)) { if (!string.IsNullOrEmpty(successor.Verb)) { diff --git a/src/Microsoft.VisualStudio.Jdt/Processors/JdtProcessor.cs b/src/Microsoft.VisualStudio.Jdt/Processors/JdtProcessor.cs index 96984e14..22c131db 100644 --- a/src/Microsoft.VisualStudio.Jdt/Processors/JdtProcessor.cs +++ b/src/Microsoft.VisualStudio.Jdt/Processors/JdtProcessor.cs @@ -1,5 +1,5 @@ ๏ปฟ// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Jdt { diff --git a/src/Microsoft.VisualStudio.Jdt/Processors/JdtRecurse.cs b/src/Microsoft.VisualStudio.Jdt/Processors/JdtRecurse.cs index 44e5dae7..7e48263c 100644 --- a/src/Microsoft.VisualStudio.Jdt/Processors/JdtRecurse.cs +++ b/src/Microsoft.VisualStudio.Jdt/Processors/JdtRecurse.cs @@ -1,5 +1,5 @@ ๏ปฟ// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Jdt { diff --git a/src/Microsoft.VisualStudio.Jdt/Processors/JdtRemove.cs b/src/Microsoft.VisualStudio.Jdt/Processors/JdtRemove.cs index fb5e8751..31c9cba2 100644 --- a/src/Microsoft.VisualStudio.Jdt/Processors/JdtRemove.cs +++ b/src/Microsoft.VisualStudio.Jdt/Processors/JdtRemove.cs @@ -1,5 +1,5 @@ ๏ปฟ// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Jdt { @@ -64,7 +64,7 @@ protected override bool ProcessCore(JObject source, JToken transformValue, JsonT private bool RemoveWithAttributes(JObject source, JObject removeObject, JsonTransformationContextLogger logger) { - var attributes = this.attributeValidator.ValidateAndReturnAttributes(removeObject); + Dictionary attributes = this.attributeValidator.ValidateAndReturnAttributes(removeObject); // The remove attribute only accepts objects if they have only the path attribute JToken pathToken; diff --git a/src/Microsoft.VisualStudio.Jdt/Processors/JdtRename.cs b/src/Microsoft.VisualStudio.Jdt/Processors/JdtRename.cs index 24476f72..d8ec2e89 100644 --- a/src/Microsoft.VisualStudio.Jdt/Processors/JdtRename.cs +++ b/src/Microsoft.VisualStudio.Jdt/Processors/JdtRename.cs @@ -1,5 +1,5 @@ ๏ปฟ// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Jdt { @@ -37,7 +37,7 @@ protected override bool ProcessCore(JObject source, JToken transformValue, JsonT { // Try and get attributes from the object var renameObject = (JObject)transformValue; - var attributes = this.attributeValidator.ValidateAndReturnAttributes(renameObject); + Dictionary attributes = this.attributeValidator.ValidateAndReturnAttributes(renameObject); // If there are attributes, handle them accordingly if (attributes.Any()) diff --git a/src/Microsoft.VisualStudio.Jdt/Processors/JdtReplace.cs b/src/Microsoft.VisualStudio.Jdt/Processors/JdtReplace.cs index 56025753..1a56613c 100644 --- a/src/Microsoft.VisualStudio.Jdt/Processors/JdtReplace.cs +++ b/src/Microsoft.VisualStudio.Jdt/Processors/JdtReplace.cs @@ -1,5 +1,5 @@ ๏ปฟ// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Jdt { @@ -50,7 +50,7 @@ protected override bool ProcessCore(JObject source, JToken transformValue, JsonT private bool ReplaceWithProperties(JObject source, JObject replaceObject, JsonTransformationContextLogger logger) { - var attributes = this.attributeValidator.ValidateAndReturnAttributes(replaceObject); + Dictionary attributes = this.attributeValidator.ValidateAndReturnAttributes(replaceObject); // If there are attributes, handle them accordingly if (attributes.Any()) diff --git a/src/Microsoft.VisualStudio.Jdt/Processors/JdtValidator.cs b/src/Microsoft.VisualStudio.Jdt/Processors/JdtValidator.cs index 3141a3b7..cb1971f1 100644 --- a/src/Microsoft.VisualStudio.Jdt/Processors/JdtValidator.cs +++ b/src/Microsoft.VisualStudio.Jdt/Processors/JdtValidator.cs @@ -1,5 +1,5 @@ ๏ปฟ// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Jdt { diff --git a/src/OptProf.targets b/src/OptProf.targets new file mode 100644 index 00000000..d0167d7c --- /dev/null +++ b/src/OptProf.targets @@ -0,0 +1,17 @@ + + + + IBC + Common7\IDE\PrivateAssemblies\$(TargetFileName) + /ExeConfig:"%VisualStudio.InstallationUnderTest.Path%\Common7\IDE\vsn.exe" + + + + + + + + + + + diff --git a/src/VSInsertionMetadata/Library.VSInsertionMetadata.proj b/src/VSInsertionMetadata/Library.VSInsertionMetadata.proj new file mode 100644 index 00000000..0caaedb9 --- /dev/null +++ b/src/VSInsertionMetadata/Library.VSInsertionMetadata.proj @@ -0,0 +1,11 @@ + + + netstandard2.0 + true + $(RepoRootPath)bin\Packages\$(Configuration)\VSRepo\ + false + false + Contains metadata for insertion into VS. + + + diff --git a/src/VSInsertionMetadata/ProfilingInputs.props b/src/VSInsertionMetadata/ProfilingInputs.props new file mode 100644 index 00000000..fb19d604 --- /dev/null +++ b/src/VSInsertionMetadata/ProfilingInputs.props @@ -0,0 +1,5 @@ + + + + + diff --git a/src/VSInsertionMetadata/VSInsertionMetadata.targets b/src/VSInsertionMetadata/VSInsertionMetadata.targets new file mode 100644 index 00000000..84ebb5a8 --- /dev/null +++ b/src/VSInsertionMetadata/VSInsertionMetadata.targets @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + $(TargetsForTfmSpecificContentInPackage); + SubstituteProfilingInputsMacro; + + + + + + + + + + + + + + + + + + + + + + $(PackageVersion).$(Build_BuildId) + + + + + + diff --git a/src/jdt.sln b/src/jdt.sln index 1043516d..69d4c554 100644 --- a/src/jdt.sln +++ b/src/jdt.sln @@ -1,18 +1,21 @@ ๏ปฟ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26403.7 +# Visual Studio Version 17 +VisualStudioVersion = 17.13.35505.181 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.VisualStudio.Jdt", "Microsoft.VisualStudio.Jdt\Microsoft.VisualStudio.Jdt.csproj", "{E4517B62-6E49-47A9-93A3-889B2E0A92B5}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.VisualStudio.Jdt.Tests", "Microsoft.VisualStudio.Jdt.Tests\Microsoft.VisualStudio.Jdt.Tests.csproj", "{A17BD5AD-EB38-4D71-90CE-25DAE7BFAAC0}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{386BAD62-57B8-4AEB-AEE5-D122A8330656}" ProjectSection(SolutionItems) = preProject + Directory.Build.props = Directory.Build.props + Directory.Build.targets = Directory.Build.targets + ..\Directory.Packages.props = ..\Directory.Packages.props stylecop.json = stylecop.json version.json = version.json EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.VisualStudio.Jdt.Tests", "..\test\Microsoft.VisualStudio.Jdt.Tests\Microsoft.VisualStudio.Jdt.Tests.csproj", "{DC4029BB-86FD-AC0B-8024-03ECD0301F86}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -23,12 +26,15 @@ Global {E4517B62-6E49-47A9-93A3-889B2E0A92B5}.Debug|Any CPU.Build.0 = Debug|Any CPU {E4517B62-6E49-47A9-93A3-889B2E0A92B5}.Release|Any CPU.ActiveCfg = Release|Any CPU {E4517B62-6E49-47A9-93A3-889B2E0A92B5}.Release|Any CPU.Build.0 = Release|Any CPU - {A17BD5AD-EB38-4D71-90CE-25DAE7BFAAC0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A17BD5AD-EB38-4D71-90CE-25DAE7BFAAC0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A17BD5AD-EB38-4D71-90CE-25DAE7BFAAC0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A17BD5AD-EB38-4D71-90CE-25DAE7BFAAC0}.Release|Any CPU.Build.0 = Release|Any CPU + {DC4029BB-86FD-AC0B-8024-03ECD0301F86}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DC4029BB-86FD-AC0B-8024-03ECD0301F86}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DC4029BB-86FD-AC0B-8024-03ECD0301F86}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DC4029BB-86FD-AC0B-8024-03ECD0301F86}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {F194D4D1-A28A-4C19-941E-BF828EA6DD8C} + EndGlobalSection EndGlobal diff --git a/src/stylecop.json b/src/stylecop.json index 5743660d..c125c72f 100644 --- a/src/stylecop.json +++ b/src/stylecop.json @@ -1,17 +1,15 @@ { // https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/EnableConfiguration.md - "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", "settings": { "documentationRules": { "companyName": "Microsoft Corporation", - "copyrightText": "Copyright (c) {companyName}. All rights reserved.\nLicensed under the {licenseName}. See {licenseFile} file in the project root for full license information.", + "copyrightText": "Copyright (c) {companyName}. All rights reserved.\nLicensed under the {licenseName} license. See {licenseFile} file in the project root for full license information.", "variables": { - "licenseName": "MIT License", + "licenseName": "MIT", "licenseFile": "LICENSE" }, "xmlHeader": false } } - -} \ No newline at end of file +} diff --git a/src/version.json b/src/version.json index 71368d09..8170c45d 100644 --- a/src/version.json +++ b/src/version.json @@ -2,7 +2,7 @@ "$schema": "https://raw.githubusercontent.com/AArnott/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", "version": "0.9", "publicReleaseRefSpec": [ - "^refs/heads/master$", // we release out of master + "^refs/heads/main$" // we release out of main ], "cloudBuild": { "buildNumber": { diff --git a/test/.editorconfig b/test/.editorconfig new file mode 100644 index 00000000..74dd4a1f --- /dev/null +++ b/test/.editorconfig @@ -0,0 +1,55 @@ +[*.cs] + +# SA1600: Elements should be documented +dotnet_diagnostic.SA1600.severity = silent + +# SA1601: Partial elements should be documented +dotnet_diagnostic.SA1601.severity = silent + +# SA1602: Enumeration items should be documented +dotnet_diagnostic.SA1602.severity = silent + +# SA1615: Element return value should be documented +dotnet_diagnostic.SA1615.severity = silent + +# VSTHRD103: Call async methods when in an async method +dotnet_diagnostic.VSTHRD103.severity = silent + +# VSTHRD111: Use .ConfigureAwait(bool) +dotnet_diagnostic.VSTHRD111.severity = none + +# VSTHRD200: Use Async suffix for async methods +dotnet_diagnostic.VSTHRD200.severity = silent + +# CA1014: Mark assemblies with CLSCompliant +dotnet_diagnostic.CA1014.severity = none + +# CA1050: Declare types in namespaces +dotnet_diagnostic.CA1050.severity = none + +# CA1303: Do not pass literals as localized parameters +dotnet_diagnostic.CA1303.severity = none + +# CS1591: Missing XML comment for publicly visible type or member +dotnet_diagnostic.CS1591.severity = silent + +# CA1707: Identifiers should not contain underscores +dotnet_diagnostic.CA1707.severity = silent + +# CA1062: Validate arguments of public methods +dotnet_diagnostic.CA1062.severity = suggestion + +# CA1063: Implement IDisposable Correctly +dotnet_diagnostic.CA1063.severity = silent + +# CA1816: Dispose methods should call SuppressFinalize +dotnet_diagnostic.CA1816.severity = silent + +# CA2007: Consider calling ConfigureAwait on the awaited task +dotnet_diagnostic.CA2007.severity = none + +# SA1401: Fields should be private +dotnet_diagnostic.SA1401.severity = silent + +# SA1133: Do not combine attributes +dotnet_diagnostic.SA1133.severity = silent diff --git a/test/Directory.Build.props b/test/Directory.Build.props new file mode 100644 index 00000000..395359dd --- /dev/null +++ b/test/Directory.Build.props @@ -0,0 +1,13 @@ + + + + + + false + true + + + + + + diff --git a/test/Directory.Build.targets b/test/Directory.Build.targets new file mode 100644 index 00000000..a6e0f4ac --- /dev/null +++ b/test/Directory.Build.targets @@ -0,0 +1,4 @@ + + + + diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/App.config b/test/Microsoft.VisualStudio.Jdt.Tests/App.config similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/App.config rename to test/Microsoft.VisualStudio.Jdt.Tests/App.config diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Array.Merge.Expected.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Array.Merge.Expected.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Array.Merge.Expected.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Array.Merge.Expected.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Array.Merge.Transform.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Array.Merge.Transform.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Array.Merge.Transform.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Array.Merge.Transform.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Array.Replace.Expected.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Array.Replace.Expected.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Array.Replace.Expected.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Array.Replace.Expected.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Array.Replace.Transform.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Array.Replace.Transform.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Array.Replace.Transform.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Array.Replace.Transform.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Array.Source.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Array.Source.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Array.Source.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Array.Source.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Object.Merge.Expected.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Object.Merge.Expected.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Object.Merge.Expected.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Object.Merge.Expected.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Object.Merge.Transform.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Object.Merge.Transform.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Object.Merge.Transform.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Object.Merge.Transform.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Object.Replace.Expected.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Object.Replace.Expected.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Object.Replace.Expected.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Object.Replace.Expected.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Object.Replace.Transform.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Object.Replace.Transform.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Object.Replace.Transform.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Object.Replace.Transform.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Object.Source.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Object.Source.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Object.Source.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Object.Source.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Primitive.AddArray.Expected.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Primitive.AddArray.Expected.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Primitive.AddArray.Expected.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Primitive.AddArray.Expected.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Primitive.AddArray.Transform.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Primitive.AddArray.Transform.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Primitive.AddArray.Transform.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Primitive.AddArray.Transform.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Primitive.AddObject.Expected.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Primitive.AddObject.Expected.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Primitive.AddObject.Expected.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Primitive.AddObject.Expected.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Primitive.AddObject.Transform.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Primitive.AddObject.Transform.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Primitive.AddObject.Transform.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Primitive.AddObject.Transform.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Primitive.AddPrimitive.Expected.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Primitive.AddPrimitive.Expected.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Primitive.AddPrimitive.Expected.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Primitive.AddPrimitive.Expected.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Primitive.AddPrimitive.Transform.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Primitive.AddPrimitive.Transform.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Primitive.AddPrimitive.Transform.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Primitive.AddPrimitive.Transform.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Primitive.Replace.Expected.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Primitive.Replace.Expected.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Primitive.Replace.Expected.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Primitive.Replace.Expected.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Primitive.Replace.Transform.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Primitive.Replace.Transform.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Primitive.Replace.Transform.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Primitive.Replace.Transform.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Primitive.Source.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Primitive.Source.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Primitive.Source.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Default/Primitive.Source.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Merge/Object.MergeObjects.Expected.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Merge/Object.MergeObjects.Expected.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Merge/Object.MergeObjects.Expected.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Merge/Object.MergeObjects.Expected.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Merge/Object.MergeObjects.Transform.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Merge/Object.MergeObjects.Transform.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Merge/Object.MergeObjects.Transform.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Merge/Object.MergeObjects.Transform.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Merge/Object.Source.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Merge/Object.Source.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Merge/Object.Source.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Merge/Object.Source.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Merge/Object.UsingDirectPath.Expected.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Merge/Object.UsingDirectPath.Expected.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Merge/Object.UsingDirectPath.Expected.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Merge/Object.UsingDirectPath.Expected.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Merge/Object.UsingDirectPath.Transform.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Merge/Object.UsingDirectPath.Transform.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Merge/Object.UsingDirectPath.Transform.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Merge/Object.UsingDirectPath.Transform.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Merge/Primitive.MergeObjects.Expected.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Merge/Primitive.MergeObjects.Expected.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Merge/Primitive.MergeObjects.Expected.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Merge/Primitive.MergeObjects.Expected.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Merge/Primitive.MergeObjects.Transform.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Merge/Primitive.MergeObjects.Transform.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Merge/Primitive.MergeObjects.Transform.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Merge/Primitive.MergeObjects.Transform.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Merge/Primitive.MergePrimitives.Expected.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Merge/Primitive.MergePrimitives.Expected.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Merge/Primitive.MergePrimitives.Expected.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Merge/Primitive.MergePrimitives.Expected.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Merge/Primitive.MergePrimitives.Transform.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Merge/Primitive.MergePrimitives.Transform.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Merge/Primitive.MergePrimitives.Transform.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Merge/Primitive.MergePrimitives.Transform.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Merge/Primitive.Source.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Merge/Primitive.Source.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Merge/Primitive.Source.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Merge/Primitive.Source.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Merge/Primitive.UsingSimplePath.Expected.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Merge/Primitive.UsingSimplePath.Expected.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Merge/Primitive.UsingSimplePath.Expected.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Merge/Primitive.UsingSimplePath.Expected.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Merge/Primitive.UsingSimplePath.Transform.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Merge/Primitive.UsingSimplePath.Transform.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Merge/Primitive.UsingSimplePath.Transform.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Merge/Primitive.UsingSimplePath.Transform.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Array.DirectPath.Expected.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Array.DirectPath.Expected.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Array.DirectPath.Expected.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Array.DirectPath.Expected.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Array.DirectPath.Transform.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Array.DirectPath.Transform.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Array.DirectPath.Transform.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Array.DirectPath.Transform.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Array.ScriptPath.Expected.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Array.ScriptPath.Expected.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Array.ScriptPath.Expected.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Array.ScriptPath.Expected.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Array.ScriptPath.Transform.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Array.ScriptPath.Transform.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Array.ScriptPath.Transform.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Array.ScriptPath.Transform.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Array.Source.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Array.Source.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Array.Source.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Array.Source.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Object.DirectPath.Expected.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Object.DirectPath.Expected.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Object.DirectPath.Expected.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Object.DirectPath.Expected.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Object.DirectPath.Transform.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Object.DirectPath.Transform.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Object.DirectPath.Transform.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Object.DirectPath.Transform.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Object.InObject.Expected.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Object.InObject.Expected.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Object.InObject.Expected.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Object.InObject.Expected.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Object.InObject.Transform.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Object.InObject.Transform.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Object.InObject.Transform.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Object.InObject.Transform.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Object.PathToItself.Expected.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Object.PathToItself.Expected.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Object.PathToItself.Expected.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Object.PathToItself.Expected.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Object.PathToItself.Transform.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Object.PathToItself.Transform.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Object.PathToItself.Transform.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Object.PathToItself.Transform.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Object.Source.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Object.Source.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Object.Source.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Object.Source.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Object.WithBool.Expected.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Object.WithBool.Expected.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Object.WithBool.Expected.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Object.WithBool.Expected.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Object.WithBool.Transform.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Object.WithBool.Transform.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Object.WithBool.Transform.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Object.WithBool.Transform.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Primitive.FromRoot.Expected.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Primitive.FromRoot.Expected.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Primitive.FromRoot.Expected.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Primitive.FromRoot.Expected.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Primitive.FromRoot.Transform.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Primitive.FromRoot.Transform.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Primitive.FromRoot.Transform.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Primitive.FromRoot.Transform.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Primitive.Source.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Primitive.Source.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Primitive.Source.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Primitive.Source.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Primitive.UsingPathArray.Expected.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Primitive.UsingPathArray.Expected.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Primitive.UsingPathArray.Expected.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Primitive.UsingPathArray.Expected.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Primitive.UsingPathArray.Transform.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Primitive.UsingPathArray.Transform.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Primitive.UsingPathArray.Transform.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Primitive.UsingPathArray.Transform.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Primitive.UsingSimpleArray.Expected.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Primitive.UsingSimpleArray.Expected.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Primitive.UsingSimpleArray.Expected.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Primitive.UsingSimpleArray.Expected.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Primitive.UsingSimpleArray.Transform.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Primitive.UsingSimpleArray.Transform.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Primitive.UsingSimpleArray.Transform.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Primitive.UsingSimpleArray.Transform.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Primitive.UsingSimplePath.Expected.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Primitive.UsingSimplePath.Expected.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Primitive.UsingSimplePath.Expected.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Primitive.UsingSimplePath.Expected.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Primitive.UsingSimplePath.Transform.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Primitive.UsingSimplePath.Transform.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Primitive.UsingSimplePath.Transform.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Primitive.UsingSimplePath.Transform.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Skipped/Object.ScriptPath.Expected.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Skipped/Object.ScriptPath.Expected.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Skipped/Object.ScriptPath.Expected.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Skipped/Object.ScriptPath.Expected.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Skipped/Object.ScriptPath.Transform.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Skipped/Object.ScriptPath.Transform.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Skipped/Object.ScriptPath.Transform.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Remove/Skipped/Object.ScriptPath.Transform.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Array.DirectPath.Expected.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Array.DirectPath.Expected.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Array.DirectPath.Expected.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Array.DirectPath.Expected.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Array.DirectPath.Transform.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Array.DirectPath.Transform.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Array.DirectPath.Transform.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Array.DirectPath.Transform.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Array.ScriptPath.Expected.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Array.ScriptPath.Expected.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Array.ScriptPath.Expected.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Array.ScriptPath.Expected.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Array.ScriptPath.Transform .json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Array.ScriptPath.Transform .json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Array.ScriptPath.Transform .json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Array.ScriptPath.Transform .json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Array.Source.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Array.Source.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Array.Source.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Array.Source.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Object.InObject.Expected.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Object.InObject.Expected.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Object.InObject.Expected.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Object.InObject.Expected.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Object.InObject.Transform.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Object.InObject.Transform.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Object.InObject.Transform.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Object.InObject.Transform.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Object.Source.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Object.Source.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Object.Source.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Object.Source.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Object.UsingSimplePath.Expected.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Object.UsingSimplePath.Expected.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Object.UsingSimplePath.Expected.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Object.UsingSimplePath.Expected.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Object.UsingSimplePath.Transform.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Object.UsingSimplePath.Transform.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Object.UsingSimplePath.Transform.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Object.UsingSimplePath.Transform.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Object.WithChangingNames.Expected.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Object.WithChangingNames.Expected.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Object.WithChangingNames.Expected.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Object.WithChangingNames.Expected.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Object.WithChangingNames.Transform.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Object.WithChangingNames.Transform.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Object.WithChangingNames.Transform.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Object.WithChangingNames.Transform.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Primitive.FromRoot.Expected.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Primitive.FromRoot.Expected.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Primitive.FromRoot.Expected.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Primitive.FromRoot.Expected.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Primitive.FromRoot.Transform.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Primitive.FromRoot.Transform.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Primitive.FromRoot.Transform.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Primitive.FromRoot.Transform.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Primitive.Source.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Primitive.Source.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Primitive.Source.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Primitive.Source.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Primitive.UsingSimpleArray.Expected.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Primitive.UsingSimpleArray.Expected.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Primitive.UsingSimpleArray.Expected.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Primitive.UsingSimpleArray.Expected.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Primitive.UsingSimpleArray.Transform.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Primitive.UsingSimpleArray.Transform.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Primitive.UsingSimpleArray.Transform.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Primitive.UsingSimpleArray.Transform.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Primitive.UsingSimplePath.Expected.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Primitive.UsingSimplePath.Expected.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Primitive.UsingSimplePath.Expected.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Primitive.UsingSimplePath.Expected.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Primitive.UsingSimplePath.Transform.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Primitive.UsingSimplePath.Transform.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Primitive.UsingSimplePath.Transform.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Rename/Primitive.UsingSimplePath.Transform.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Replace/Array.DirectPath.Expected.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Replace/Array.DirectPath.Expected.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Replace/Array.DirectPath.Expected.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Replace/Array.DirectPath.Expected.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Replace/Array.DirectPath.Transform.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Replace/Array.DirectPath.Transform.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Replace/Array.DirectPath.Transform.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Replace/Array.DirectPath.Transform.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Replace/Array.ScriptPath.Expected.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Replace/Array.ScriptPath.Expected.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Replace/Array.ScriptPath.Expected.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Replace/Array.ScriptPath.Expected.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Replace/Array.ScriptPath.Transform.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Replace/Array.ScriptPath.Transform.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Replace/Array.ScriptPath.Transform.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Replace/Array.ScriptPath.Transform.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Replace/Array.Source.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Replace/Array.Source.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Replace/Array.Source.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Replace/Array.Source.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Replace/Object.Source.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Replace/Object.Source.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Replace/Object.Source.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Replace/Object.Source.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Replace/Object.WithArray.Expected.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Replace/Object.WithArray.Expected.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Replace/Object.WithArray.Expected.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Replace/Object.WithArray.Expected.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Replace/Object.WithArray.Transform.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Replace/Object.WithArray.Transform.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Replace/Object.WithArray.Transform.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Replace/Object.WithArray.Transform.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Replace/Object.WithObject.Expected.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Replace/Object.WithObject.Expected.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Replace/Object.WithObject.Expected.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Replace/Object.WithObject.Expected.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Replace/Object.WithObject.Transform.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Replace/Object.WithObject.Transform.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Replace/Object.WithObject.Transform.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Replace/Object.WithObject.Transform.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Replace/Object.WithPrimitive.Expected.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Replace/Object.WithPrimitive.Expected.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Replace/Object.WithPrimitive.Expected.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Replace/Object.WithPrimitive.Expected.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Replace/Object.WithPrimitive.Transform.json b/test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Replace/Object.WithPrimitive.Transform.json similarity index 100% rename from src/Microsoft.VisualStudio.Jdt.Tests/Inputs/Replace/Object.WithPrimitive.Transform.json rename to test/Microsoft.VisualStudio.Jdt.Tests/Inputs/Replace/Object.WithPrimitive.Transform.json diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/JdtUtilitiesTest.cs b/test/Microsoft.VisualStudio.Jdt.Tests/JdtUtilitiesTest.cs similarity index 97% rename from src/Microsoft.VisualStudio.Jdt.Tests/JdtUtilitiesTest.cs rename to test/Microsoft.VisualStudio.Jdt.Tests/JdtUtilitiesTest.cs index 22d83e86..15e77086 100644 --- a/src/Microsoft.VisualStudio.Jdt.Tests/JdtUtilitiesTest.cs +++ b/test/Microsoft.VisualStudio.Jdt.Tests/JdtUtilitiesTest.cs @@ -1,5 +1,5 @@ ๏ปฟ// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Jdt.Tests { diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/JsonTransformationTest.cs b/test/Microsoft.VisualStudio.Jdt.Tests/JsonTransformationTest.cs similarity index 95% rename from src/Microsoft.VisualStudio.Jdt.Tests/JsonTransformationTest.cs rename to test/Microsoft.VisualStudio.Jdt.Tests/JsonTransformationTest.cs index 253366dc..5386d12c 100644 --- a/src/Microsoft.VisualStudio.Jdt.Tests/JsonTransformationTest.cs +++ b/test/Microsoft.VisualStudio.Jdt.Tests/JsonTransformationTest.cs @@ -1,5 +1,5 @@ ๏ปฟ// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Jdt.Tests { @@ -261,11 +261,11 @@ public void ThrowAndLogException() string transformString = @"{ '@jdt.invalid': false }"; - using (var transformStream = this.GetStreamFromString(transformString)) - using (var sourceStream = this.GetStreamFromString(SimpleSourceString)) + using (Stream transformStream = this.GetStreamFromString(transformString)) + using (Stream sourceStream = this.GetStreamFromString(SimpleSourceString)) { JsonTransformation transform = new JsonTransformation(transformStream, this.logger); - var exception = Record.Exception(() => transform.Apply(sourceStream)); + Exception exception = Record.Exception(() => transform.Apply(sourceStream)); Assert.NotNull(exception); Assert.IsType(exception); var jdtException = exception as JdtException; @@ -302,7 +302,7 @@ public void ReadOnly() private static void LogHasSingleEntry(List log, string fileName, int lineNumber, int linePosition, bool fromException) { Assert.Single(log); - var errorEntry = log.Single(); + JsonTransformationTestLogger.TestLogEntry errorEntry = log.Single(); Assert.Equal(fileName, errorEntry.FileName); Assert.Equal(lineNumber, errorEntry.LineNumber); Assert.Equal(linePosition, errorEntry.LinePosition); @@ -311,8 +311,8 @@ private static void LogHasSingleEntry(List applyTransformMethod, bool sho { Stream result = null; - var exception = Record.Exception(() => result = applyTransformMethod()); + Exception exception = Record.Exception(() => result = applyTransformMethod()); if (shouldTransformSucceed) { diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/JsonTransformationTestLogger.cs b/test/Microsoft.VisualStudio.Jdt.Tests/JsonTransformationTestLogger.cs similarity index 98% rename from src/Microsoft.VisualStudio.Jdt.Tests/JsonTransformationTestLogger.cs rename to test/Microsoft.VisualStudio.Jdt.Tests/JsonTransformationTestLogger.cs index 40843c70..e87f15e7 100644 --- a/src/Microsoft.VisualStudio.Jdt.Tests/JsonTransformationTestLogger.cs +++ b/test/Microsoft.VisualStudio.Jdt.Tests/JsonTransformationTestLogger.cs @@ -1,5 +1,5 @@ ๏ปฟ// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Jdt.Tests { diff --git a/test/Microsoft.VisualStudio.Jdt.Tests/Microsoft.VisualStudio.Jdt.Tests.csproj b/test/Microsoft.VisualStudio.Jdt.Tests/Microsoft.VisualStudio.Jdt.Tests.csproj new file mode 100644 index 00000000..b511263d --- /dev/null +++ b/test/Microsoft.VisualStudio.Jdt.Tests/Microsoft.VisualStudio.Jdt.Tests.csproj @@ -0,0 +1,33 @@ +๏ปฟ + + + net472;net5.0 + false + $(NoWarn);CS1591 + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/ReadOnlyTempFile.cs b/test/Microsoft.VisualStudio.Jdt.Tests/ReadOnlyTempFile.cs similarity index 96% rename from src/Microsoft.VisualStudio.Jdt.Tests/ReadOnlyTempFile.cs rename to test/Microsoft.VisualStudio.Jdt.Tests/ReadOnlyTempFile.cs index 3a75a0f3..3802b32b 100644 --- a/src/Microsoft.VisualStudio.Jdt.Tests/ReadOnlyTempFile.cs +++ b/test/Microsoft.VisualStudio.Jdt.Tests/ReadOnlyTempFile.cs @@ -1,5 +1,5 @@ ๏ปฟ// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Jdt.Tests { diff --git a/src/Microsoft.VisualStudio.Jdt.Tests/TransformTest.cs b/test/Microsoft.VisualStudio.Jdt.Tests/TransformTest.cs similarity index 97% rename from src/Microsoft.VisualStudio.Jdt.Tests/TransformTest.cs rename to test/Microsoft.VisualStudio.Jdt.Tests/TransformTest.cs index 317e7439..15b35848 100644 --- a/src/Microsoft.VisualStudio.Jdt.Tests/TransformTest.cs +++ b/test/Microsoft.VisualStudio.Jdt.Tests/TransformTest.cs @@ -1,5 +1,5 @@ ๏ปฟ// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace Microsoft.VisualStudio.Jdt.Tests { @@ -16,7 +16,7 @@ namespace Microsoft.VisualStudio.Jdt.Tests public class TransformTest { // Directory for test inputs, that are JSON files - private static readonly string TestInputDirectory = Directory.GetParent(Directory.GetCurrentDirectory()).Parent.Parent.FullName + "\\src\\Microsoft.VisualStudio.Jdt.Tests\\Inputs\\"; + private static readonly string TestInputDirectory = Directory.GetParent(Directory.GetCurrentDirectory()).Parent.Parent.Parent.FullName + "\\test\\Microsoft.VisualStudio.Jdt.Tests\\Inputs\\"; // Directory for Default transformation tests private static readonly string DefaultTestDirectory = TestInputDirectory + "Default\\"; diff --git a/tools/Check-DotNetRuntime.ps1 b/tools/Check-DotNetRuntime.ps1 new file mode 100644 index 00000000..9d012109 --- /dev/null +++ b/tools/Check-DotNetRuntime.ps1 @@ -0,0 +1,41 @@ +<# +.SYNOPSIS + Checks whether a given .NET Core runtime is installed. +#> +[CmdletBinding()] +Param ( + [Parameter()] + [ValidateSet('Microsoft.AspNetCore.App','Microsoft.NETCore.App')] + [string]$Runtime='Microsoft.NETCore.App', + [Parameter(Mandatory=$true)] + [Version]$Version +) + +$dotnet = Get-Command dotnet -ErrorAction SilentlyContinue +if (!$dotnet) { + # Nothing is installed. + Write-Output $false + exit 1 +} + +Function IsVersionMatch { + Param( + [Parameter()] + $actualVersion + ) + return $actualVersion -and + $Version.Major -eq $actualVersion.Major -and + $Version.Minor -eq $actualVersion.Minor -and + (($Version.Build -eq -1) -or ($Version.Build -eq $actualVersion.Build)) -and + (($Version.Revision -eq -1) -or ($Version.Revision -eq $actualVersion.Revision)) +} + +$installedRuntimes = dotnet --list-runtimes |? { $_.Split()[0] -ieq $Runtime } |% { $v = $null; [Version]::tryparse($_.Split()[1], [ref] $v); $v } +$matchingRuntimes = $installedRuntimes |? { IsVersionMatch -actualVersion $_ } +if (!$matchingRuntimes) { + Write-Output $false + exit 1 +} + +Write-Output $true +exit 0 diff --git a/tools/Check-DotNetSdk.ps1 b/tools/Check-DotNetSdk.ps1 new file mode 100644 index 00000000..6c9fa772 --- /dev/null +++ b/tools/Check-DotNetSdk.ps1 @@ -0,0 +1,37 @@ +<# +.SYNOPSIS + Checks whether the .NET Core SDK required by this repo is installed. +#> +[CmdletBinding()] +Param ( +) + +$dotnet = Get-Command dotnet -ErrorAction SilentlyContinue +if (!$dotnet) { + # Nothing is installed. + Write-Output $false + exit 1 +} + +# We need to set the current directory so dotnet considers the SDK required by our global.json file. +Push-Location "$PSScriptRoot\.." +try { + dotnet -h 2>&1 | Out-Null + if (($LASTEXITCODE -eq 129) -or # On Linux + ($LASTEXITCODE -eq -2147450751) # On Windows + ) { + # These exit codes indicate no matching SDK exists. + Write-Output $false + exit 2 + } + + # The required SDK is already installed! + Write-Output $true + exit 0 +} catch { + # 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. + Write-Output $false + exit 3 +} finally { + Pop-Location +} diff --git a/tools/Install-DotNetSdk.ps1 b/tools/Install-DotNetSdk.ps1 new file mode 100644 index 00000000..e190fcfb --- /dev/null +++ b/tools/Install-DotNetSdk.ps1 @@ -0,0 +1,421 @@ +#!/usr/bin/env pwsh + +<# +.SYNOPSIS + Installs the .NET SDK specified in the global.json file at the root of this repository, + along with supporting .NET runtimes used for testing. +.DESCRIPTION + This MAY not require elevation, as the SDK and runtimes are installed locally to this repo location, + unless `-InstallLocality machine` is specified. +.PARAMETER InstallLocality + A value indicating whether dependencies should be installed locally to the repo or at a per-user location. + Per-user allows sharing the installed dependencies across repositories and allows use of a shared expanded package cache. + Visual Studio will only notice and use these SDKs/runtimes if VS is launched from the environment that runs this script. + Per-repo allows for high isolation, allowing for a more precise recreation of the environment within an Azure Pipelines build. + When using 'repo', environment variables are set to cause the locally installed dotnet SDK to be used. + Per-repo can lead to file locking issues when dotnet.exe is left running as a build server and can be mitigated by running `dotnet build-server shutdown`. + Per-machine requires elevation and will download and install all SDKs and runtimes to machine-wide locations so all applications can find it. +.PARAMETER SdkOnly + Skips installing the runtime. +.PARAMETER IncludeX86 + Installs a x86 SDK and runtimes in addition to the x64 ones. Only supported on Windows. Ignored on others. +.PARAMETER IncludeAspNetCore + Installs the ASP.NET Core runtime along with the .NET runtime. +#> +[CmdletBinding(SupportsShouldProcess=$true,ConfirmImpact='Medium')] +Param ( + [ValidateSet('repo','user','machine')] + [string]$InstallLocality='user', + [switch]$SdkOnly, + [switch]$IncludeX86, + [switch]$IncludeAspNetCore +) + +$DotNetInstallScriptRoot = "$PSScriptRoot/../obj/tools" +if (!(Test-Path $DotNetInstallScriptRoot)) { New-Item -ItemType Directory -Path $DotNetInstallScriptRoot -WhatIf:$false | Out-Null } +$DotNetInstallScriptRoot = Resolve-Path $DotNetInstallScriptRoot + +# Look up actual required .NET SDK version from global.json +$sdkVersion = & "$PSScriptRoot/../azure-pipelines/variables/DotNetSdkVersion.ps1" + +If ($IncludeX86 -and ($IsMacOS -or $IsLinux)) { + Write-Verbose "Ignoring -IncludeX86 switch because 32-bit runtimes are only supported on Windows." + $IncludeX86 = $false +} + +$arch = [System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture +if (!$arch) { # Windows Powershell leaves this blank + $arch = 'x64' + if ($env:PROCESSOR_ARCHITECTURE -eq 'ARM64') { $arch = 'ARM64' } + if (${env:ProgramFiles(Arm)}) { $arch = 'ARM64' } +} + +# Search for all .NET runtime versions referenced from MSBuild projects and arrange to install them. +$runtimeVersions = @() +$windowsDesktopRuntimeVersions = @() +$aspnetRuntimeVersions = @() +if (!$SdkOnly) { + Get-ChildItem "$PSScriptRoot\..\src\*.*proj","$PSScriptRoot\..\test\*.*proj","$PSScriptRoot\..\Directory.Build.props" -Recurse |% { + $projXml = [xml](Get-Content -Path $_) + $pg = $projXml.Project.PropertyGroup + if ($pg) { + $targetFrameworks = @() + $tf = $pg.TargetFramework + $targetFrameworks += $tf + $tfs = $pg.TargetFrameworks + if ($tfs) { + $targetFrameworks = $tfs -Split ';' + } + } + $targetFrameworks |? { $_ -match 'net(?:coreapp)?(\d+\.\d+)' } |% { + $v = $Matches[1] + $runtimeVersions += $v + $aspnetRuntimeVersions += $v + if ($v -ge '3.0' -and -not ($IsMacOS -or $IsLinux)) { + $windowsDesktopRuntimeVersions += $v + } + } + + # Add target frameworks of the form: netXX + $targetFrameworks |? { $_ -match 'net(\d+\.\d+)' } |% { + $v = $Matches[1] + $runtimeVersions += $v + $aspnetRuntimeVersions += $v + if (-not ($IsMacOS -or $IsLinux)) { + $windowsDesktopRuntimeVersions += $v + } + } + } +} + +if (!$IncludeAspNetCore) { + $aspnetRuntimeVersions = @() +} + +Function Get-FileFromWeb([Uri]$Uri, $OutDir) { + $OutFile = Join-Path $OutDir $Uri.Segments[-1] + if (!(Test-Path $OutFile)) { + Write-Verbose "Downloading $Uri..." + if (!(Test-Path $OutDir)) { New-Item -ItemType Directory -Path $OutDir | Out-Null } + try { + (New-Object System.Net.WebClient).DownloadFile($Uri, $OutFile) + } finally { + # This try/finally causes the script to abort + } + } + + $OutFile +} + +Function Get-InstallerExe( + $Version, + $Architecture, + [ValidateSet('Sdk','Runtime','WindowsDesktop')] + [string]$sku +) { + # Get the latest/actual version for the specified one + $TypedVersion = $null + if (![Version]::TryParse($Version, [ref] $TypedVersion)) { + Write-Error "Unable to parse $Version into an a.b.c.d version. This version cannot be installed machine-wide." + exit 1 + } + + if ($TypedVersion.Build -eq -1) { + $versionInfo = -Split (Invoke-WebRequest -Uri "https://dotnetcli.blob.core.windows.net/dotnet/$sku/$Version/latest.version" -UseBasicParsing) + $Version = $versionInfo[-1] + } + + $majorMinor = "$($TypedVersion.Major).$($TypedVersion.Minor)" + $ReleasesFile = Join-Path $DotNetInstallScriptRoot "$majorMinor\releases.json" + if (!(Test-Path $ReleasesFile)) { + Get-FileFromWeb -Uri "https://dotnetcli.blob.core.windows.net/dotnet/release-metadata/$majorMinor/releases.json" -OutDir (Split-Path $ReleasesFile) | Out-Null + } + + $releases = Get-Content $ReleasesFile | ConvertFrom-Json + $url = $null + foreach ($release in $releases.releases) { + $filesElement = $null + if ($release.$sku.version -eq $Version) { + $filesElement = $release.$sku.files + } + if (!$filesElement -and ($sku -eq 'sdk') -and $release.sdks) { + foreach ($sdk in $release.sdks) { + if ($sdk.version -eq $Version) { + $filesElement = $sdk.files + break + } + } + } + + if ($filesElement) { + foreach ($file in $filesElement) { + if ($file.rid -eq "win-$Architecture") { + $url = $file.url + Break + } + } + + if ($url) { + Break + } + } + } + + if ($url) { + Get-FileFromWeb -Uri $url -OutDir $DotNetInstallScriptRoot + } else { + throw "Unable to find release of $sku v$Version" + } +} + +Function Install-DotNet($Version, $Architecture, [ValidateSet('Sdk','Runtime','WindowsDesktop','AspNetCore')][string]$sku = 'Sdk') { + Write-Host "Downloading .NET $sku $Version..." + $Installer = Get-InstallerExe -Version $Version -Architecture $Architecture -sku $sku + Write-Host "Installing .NET $sku $Version..." + cmd /c start /wait $Installer /install /passive /norestart + if ($LASTEXITCODE -eq 3010) { + Write-Verbose "Restart required" + } elseif ($LASTEXITCODE -ne 0) { + throw "Failure to install .NET SDK" + } +} + +$switches = @() +$envVars = @{ + # For locally installed dotnet, skip first time experience which takes a long time + 'DOTNET_SKIP_FIRST_TIME_EXPERIENCE' = 'true'; +} + +if ($InstallLocality -eq 'machine') { + if ($IsMacOS -or $IsLinux) { + $DotNetInstallDir = '/usr/share/dotnet' + } else { + $restartRequired = $false + if ($PSCmdlet.ShouldProcess(".NET SDK $sdkVersion", "Install")) { + Install-DotNet -Version $sdkVersion -Architecture $arch + $restartRequired = $restartRequired -or ($LASTEXITCODE -eq 3010) + + if ($IncludeX86) { + Install-DotNet -Version $sdkVersion -Architecture x86 + $restartRequired = $restartRequired -or ($LASTEXITCODE -eq 3010) + } + } + + $runtimeVersions | Sort-Object | Get-Unique |% { + if ($PSCmdlet.ShouldProcess(".NET runtime $_", "Install")) { + Install-DotNet -Version $_ -sku Runtime -Architecture $arch + $restartRequired = $restartRequired -or ($LASTEXITCODE -eq 3010) + + if ($IncludeX86) { + Install-DotNet -Version $_ -sku Runtime -Architecture x86 + $restartRequired = $restartRequired -or ($LASTEXITCODE -eq 3010) + } + } + } + + $windowsDesktopRuntimeVersions | Sort-Object | Get-Unique |% { + if ($PSCmdlet.ShouldProcess(".NET Windows Desktop $_", "Install")) { + Install-DotNet -Version $_ -sku WindowsDesktop -Architecture $arch + $restartRequired = $restartRequired -or ($LASTEXITCODE -eq 3010) + + if ($IncludeX86) { + Install-DotNet -Version $_ -sku WindowsDesktop -Architecture x86 + $restartRequired = $restartRequired -or ($LASTEXITCODE -eq 3010) + } + } + } + + $aspnetRuntimeVersions | Sort-Object | Get-Unique |% { + if ($PSCmdlet.ShouldProcess("ASP.NET Core $_", "Install")) { + Install-DotNet -Version $_ -sku AspNetCore -Architecture $arch + $restartRequired = $restartRequired -or ($LASTEXITCODE -eq 3010) + + if ($IncludeX86) { + Install-DotNet -Version $_ -sku AspNetCore -Architecture x86 + $restartRequired = $restartRequired -or ($LASTEXITCODE -eq 3010) + } + } + } + if ($restartRequired) { + Write-Host -ForegroundColor Yellow "System restart required" + Exit 3010 + } + + return + } +} elseif ($InstallLocality -eq 'repo') { + $DotNetInstallDir = "$DotNetInstallScriptRoot/.dotnet" + $DotNetX86InstallDir = "$DotNetInstallScriptRoot/x86/.dotnet" +} elseif ($env:AGENT_TOOLSDIRECTORY) { + $DotNetInstallDir = "$env:AGENT_TOOLSDIRECTORY/dotnet" + $DotNetX86InstallDir = "$env:AGENT_TOOLSDIRECTORY/x86/dotnet" +} else { + $DotNetInstallDir = Join-Path $HOME .dotnet +} + +if ($DotNetInstallDir) { + if (!(Test-Path $DotNetInstallDir)) { New-Item -ItemType Directory -Path $DotNetInstallDir } + $DotNetInstallDir = Resolve-Path $DotNetInstallDir + Write-Host "Installing .NET SDK and runtimes to $DotNetInstallDir" -ForegroundColor Blue + $envVars['DOTNET_MULTILEVEL_LOOKUP'] = '0' + $envVars['DOTNET_ROOT'] = $DotNetInstallDir +} + +if ($IncludeX86) { + if ($DotNetX86InstallDir) { + if (!(Test-Path $DotNetX86InstallDir)) { New-Item -ItemType Directory -Path $DotNetX86InstallDir } + $DotNetX86InstallDir = Resolve-Path $DotNetX86InstallDir + Write-Host "Installing x86 .NET SDK and runtimes to $DotNetX86InstallDir" -ForegroundColor Blue + } else { + # Only machine-wide or repo-wide installations can handle two unique dotnet.exe architectures. + Write-Error "The installation location or OS isn't supported for x86 installation. Try a different -InstallLocality value." + return 1 + } +} + +if ($IsMacOS -or $IsLinux) { + $DownloadUri = "https://raw.githubusercontent.com/dotnet/install-scripts/0b09de9bc136cacb5f849a6957ebd4062173c148/src/dotnet-install.sh" + $DotNetInstallScriptPath = "$DotNetInstallScriptRoot/dotnet-install.sh" +} else { + $DownloadUri = "https://raw.githubusercontent.com/dotnet/install-scripts/0b09de9bc136cacb5f849a6957ebd4062173c148/src/dotnet-install.ps1" + $DotNetInstallScriptPath = "$DotNetInstallScriptRoot/dotnet-install.ps1" +} + +if (-not (Test-Path $DotNetInstallScriptPath)) { + Invoke-WebRequest -Uri $DownloadUri -OutFile $DotNetInstallScriptPath -UseBasicParsing + if ($IsMacOS -or $IsLinux) { + chmod +x $DotNetInstallScriptPath + } +} + +# In case the script we invoke is in a directory with spaces, wrap it with single quotes. +# In case the path includes single quotes, escape them. +$DotNetInstallScriptPathExpression = $DotNetInstallScriptPath.Replace("'", "''") +$DotNetInstallScriptPathExpression = "& '$DotNetInstallScriptPathExpression'" + +$anythingInstalled = $false +$global:LASTEXITCODE = 0 + +if ($PSCmdlet.ShouldProcess(".NET SDK $sdkVersion", "Install")) { + $anythingInstalled = $true + Invoke-Expression -Command "$DotNetInstallScriptPathExpression -Version $sdkVersion -Architecture $arch -InstallDir $DotNetInstallDir $switches" + + if ($LASTEXITCODE -ne 0) { + Write-Error ".NET SDK installation failure: $LASTEXITCODE" + exit $LASTEXITCODE + } +} else { + Invoke-Expression -Command "$DotNetInstallScriptPathExpression -Version $sdkVersion -Architecture $arch -InstallDir $DotNetInstallDir $switches -DryRun" +} + +if ($IncludeX86) { + if ($PSCmdlet.ShouldProcess(".NET x86 SDK $sdkVersion", "Install")) { + $anythingInstalled = $true + Invoke-Expression -Command "$DotNetInstallScriptPathExpression -Version $sdkVersion -Architecture x86 -InstallDir $DotNetX86InstallDir $switches" + + if ($LASTEXITCODE -ne 0) { + Write-Error ".NET x86 SDK installation failure: $LASTEXITCODE" + exit $LASTEXITCODE + } + } else { + Invoke-Expression -Command "$DotNetInstallScriptPathExpression -Version $sdkVersion -Architecture x86 -InstallDir $DotNetX86InstallDir $switches -DryRun" + } +} + +$dotnetRuntimeSwitches = $switches + '-Runtime','dotnet' + +$runtimeVersions | Sort-Object -Unique |% { + if ($PSCmdlet.ShouldProcess(".NET $Arch runtime $_", "Install")) { + $anythingInstalled = $true + Invoke-Expression -Command "$DotNetInstallScriptPathExpression -Channel $_ -Architecture $arch -InstallDir $DotNetInstallDir $dotnetRuntimeSwitches" + + if ($LASTEXITCODE -ne 0) { + Write-Error ".NET SDK installation failure: $LASTEXITCODE" + exit $LASTEXITCODE + } + } else { + Invoke-Expression -Command "$DotNetInstallScriptPathExpression -Channel $_ -Architecture $arch -InstallDir $DotNetInstallDir $dotnetRuntimeSwitches -DryRun" + } + + if ($IncludeX86) { + if ($PSCmdlet.ShouldProcess(".NET x86 runtime $_", "Install")) { + $anythingInstalled = $true + Invoke-Expression -Command "$DotNetInstallScriptPathExpression -Channel $_ -Architecture x86 -InstallDir $DotNetX86InstallDir $dotnetRuntimeSwitches" + + if ($LASTEXITCODE -ne 0) { + Write-Error ".NET SDK installation failure: $LASTEXITCODE" + exit $LASTEXITCODE + } + } else { + Invoke-Expression -Command "$DotNetInstallScriptPathExpression -Channel $_ -Architecture x86 -InstallDir $DotNetX86InstallDir $dotnetRuntimeSwitches -DryRun" + } + } +} + +$windowsDesktopRuntimeSwitches = $switches + '-Runtime','windowsdesktop' + +$windowsDesktopRuntimeVersions | Sort-Object -Unique |% { + if ($PSCmdlet.ShouldProcess(".NET WindowsDesktop $arch runtime $_", "Install")) { + $anythingInstalled = $true + Invoke-Expression -Command "$DotNetInstallScriptPathExpression -Channel $_ -Architecture $arch -InstallDir $DotNetInstallDir $windowsDesktopRuntimeSwitches" + + if ($LASTEXITCODE -ne 0) { + Write-Error ".NET SDK installation failure: $LASTEXITCODE" + exit $LASTEXITCODE + } + } else { + Invoke-Expression -Command "$DotNetInstallScriptPathExpression -Channel $_ -Architecture $arch -InstallDir $DotNetInstallDir $windowsDesktopRuntimeSwitches -DryRun" + } + + if ($IncludeX86) { + if ($PSCmdlet.ShouldProcess(".NET WindowsDesktop x86 runtime $_", "Install")) { + $anythingInstalled = $true + Invoke-Expression -Command "$DotNetInstallScriptPathExpression -Channel $_ -Architecture x86 -InstallDir $DotNetX86InstallDir $windowsDesktopRuntimeSwitches" + + if ($LASTEXITCODE -ne 0) { + Write-Error ".NET SDK installation failure: $LASTEXITCODE" + exit $LASTEXITCODE + } + } else { + Invoke-Expression -Command "$DotNetInstallScriptPathExpression -Channel $_ -Architecture x86 -InstallDir $DotNetX86InstallDir $windowsDesktopRuntimeSwitches -DryRun" + } + } +} + +$aspnetRuntimeSwitches = $switches + '-Runtime','aspnetcore' + +$aspnetRuntimeVersions | Sort-Object -Unique |% { + if ($PSCmdlet.ShouldProcess(".NET ASP.NET Core $arch runtime $_", "Install")) { + $anythingInstalled = $true + Invoke-Expression -Command "$DotNetInstallScriptPathExpression -Channel $_ -Architecture $arch -InstallDir $DotNetInstallDir $aspnetRuntimeSwitches" + + if ($LASTEXITCODE -ne 0) { + Write-Error ".NET SDK installation failure: $LASTEXITCODE" + exit $LASTEXITCODE + } + } else { + Invoke-Expression -Command "$DotNetInstallScriptPathExpression -Channel $_ -Architecture $arch -InstallDir $DotNetInstallDir $aspnetRuntimeSwitches -DryRun" + } + + if ($IncludeX86) { + if ($PSCmdlet.ShouldProcess(".NET ASP.NET Core x86 runtime $_", "Install")) { + $anythingInstalled = $true + Invoke-Expression -Command "$DotNetInstallScriptPathExpression -Channel $_ -Architecture x86 -InstallDir $DotNetX86InstallDir $aspnetRuntimeSwitches" + + if ($LASTEXITCODE -ne 0) { + Write-Error ".NET SDK installation failure: $LASTEXITCODE" + exit $LASTEXITCODE + } + } else { + Invoke-Expression -Command "$DotNetInstallScriptPathExpression -Channel $_ -Architecture x86 -InstallDir $DotNetX86InstallDir $aspnetRuntimeSwitches -DryRun" + } + } +} + +if ($PSCmdlet.ShouldProcess("Set DOTNET environment variables to discover these installed runtimes?")) { + & "$PSScriptRoot/Set-EnvVars.ps1" -Variables $envVars -PrependPath $DotNetInstallDir | Out-Null +} + +if ($anythingInstalled -and ($InstallLocality -ne 'machine') -and !$env:TF_BUILD -and !$env:GITHUB_ACTIONS) { + Write-Warning ".NET runtimes or SDKs were installed to a non-machine location. Perform your builds or open Visual Studio from this same environment in order for tools to discover the location of these dependencies." +} diff --git a/tools/Install-NuGetCredProvider.ps1 b/tools/Install-NuGetCredProvider.ps1 new file mode 100644 index 00000000..857324b6 --- /dev/null +++ b/tools/Install-NuGetCredProvider.ps1 @@ -0,0 +1,76 @@ +#!/usr/bin/env pwsh + +<# +.SYNOPSIS + Downloads and installs the Microsoft Artifacts Credential Provider + from https://github.com/microsoft/artifacts-credprovider + to assist in authenticating to Azure Artifact feeds in interactive development + or unattended build agents. +.PARAMETER Force + Forces install of the CredProvider plugin even if one already exists. This is useful to upgrade an older version. +.PARAMETER AccessToken + An optional access token for authenticating to Azure Artifacts authenticated feeds. +#> +[CmdletBinding()] +Param ( + [Parameter()] + [switch]$Force, + [Parameter()] + [string]$AccessToken +) + +$envVars = @{} + +$toolsPath = & "$PSScriptRoot\..\azure-pipelines\Get-TempToolsPath.ps1" + +if ($IsMacOS -or $IsLinux) { + $installerScript = "installcredprovider.sh" + $sourceUrl = "https://raw.githubusercontent.com/microsoft/artifacts-credprovider/master/helpers/installcredprovider.sh" +} else { + $installerScript = "installcredprovider.ps1" + $sourceUrl = "https://raw.githubusercontent.com/microsoft/artifacts-credprovider/master/helpers/installcredprovider.ps1" +} + +$installerScript = Join-Path $toolsPath $installerScript + +if (!(Test-Path $installerScript) -or $Force) { + Invoke-WebRequest $sourceUrl -OutFile $installerScript +} + +$installerScript = (Resolve-Path $installerScript).Path + +if ($IsMacOS -or $IsLinux) { + chmod u+x $installerScript +} + +& $installerScript -Force:$Force -AddNetfx -InstallNet8 + +if ($AccessToken) { + $endpoints = @() + + $endpointURIs = @() + Get-ChildItem "$PSScriptRoot\..\nuget.config" -Recurse |% { + $nugetConfig = [xml](Get-Content -Path $_) + + $nugetConfig.configuration.packageSources.add |? { ($_.value -match '^https://pkgs\.dev\.azure\.com/') -or ($_.value -match '^https://[\w\-]+\.pkgs\.visualstudio\.com/') } |% { + if ($endpointURIs -notcontains $_.Value) { + $endpointURIs += $_.Value + $endpoint = New-Object -TypeName PSObject + Add-Member -InputObject $endpoint -MemberType NoteProperty -Name endpoint -Value $_.value + Add-Member -InputObject $endpoint -MemberType NoteProperty -Name username -Value ado + Add-Member -InputObject $endpoint -MemberType NoteProperty -Name password -Value $AccessToken + $endpoints += $endpoint + } + } + } + + $auth = New-Object -TypeName PSObject + Add-Member -InputObject $auth -MemberType NoteProperty -Name endpointCredentials -Value $endpoints + + $authJson = ConvertTo-Json -InputObject $auth + $envVars += @{ + 'VSS_NUGET_EXTERNAL_FEED_ENDPOINTS'=$authJson; + } +} + +& "$PSScriptRoot/Set-EnvVars.ps1" -Variables $envVars | Out-Null diff --git a/tools/MergeFrom-Template.ps1 b/tools/MergeFrom-Template.ps1 new file mode 100644 index 00000000..3f721c6a --- /dev/null +++ b/tools/MergeFrom-Template.ps1 @@ -0,0 +1,79 @@ + +<# +.SYNOPSIS + Merges the latest changes from Library.Template into HEAD of this repo. +.PARAMETER LocalBranch + The name of the local branch to create at HEAD and use to merge into from Library.Template. +#> +[CmdletBinding(SupportsShouldProcess = $true)] +Param( + [string]$LocalBranch = "dev/$($env:USERNAME)/libtemplateUpdate" +) + +Function Spawn-Tool($command, $commandArgs, $workingDirectory, $allowFailures) { + if ($workingDirectory) { + Push-Location $workingDirectory + } + try { + if ($env:TF_BUILD) { + Write-Host "$pwd >" + Write-Host "##[command]$command $commandArgs" + } + else { + Write-Host "$command $commandArgs" -ForegroundColor Yellow + } + if ($commandArgs) { + & $command @commandArgs + } else { + Invoke-Expression $command + } + if ((!$allowFailures) -and ($LASTEXITCODE -ne 0)) { exit $LASTEXITCODE } + } + finally { + if ($workingDirectory) { + Pop-Location + } + } +} + +$remoteBranch = & $PSScriptRoot\..\azure-pipelines\Get-LibTemplateBasis.ps1 -ErrorIfNotRelated +if ($LASTEXITCODE -ne 0) { + exit $LASTEXITCODE +} + +$LibTemplateUrl = 'https://github.com/aarnott/Library.Template' +Spawn-Tool 'git' ('fetch', $LibTemplateUrl, $remoteBranch) +$SourceCommit = Spawn-Tool 'git' ('rev-parse', 'FETCH_HEAD') +$BaseBranch = Spawn-Tool 'git' ('branch', '--show-current') +$SourceCommitUrl = "$LibTemplateUrl/commit/$SourceCommit" + +# To reduce the odds of merge conflicts at this stage, we always move HEAD to the last successful merge. +$basis = Spawn-Tool 'git' ('rev-parse', 'HEAD') # TODO: consider improving this later + +Write-Host "Merging the $remoteBranch branch of Library.Template ($SourceCommit) into local repo $basis" -ForegroundColor Green + +Spawn-Tool 'git' ('checkout', '-b', $LocalBranch, $basis) $null $true +if ($LASTEXITCODE -eq 128) { + Spawn-Tool 'git' ('checkout', $LocalBranch) + Spawn-Tool 'git' ('merge', $basis) +} + +Spawn-Tool 'git' ('merge', 'FETCH_HEAD', '--no-ff', '-m', "Merge the $remoteBranch branch from $LibTemplateUrl`n`nSpecifically, this merges [$SourceCommit from that repo]($SourceCommitUrl).") +if ($LASTEXITCODE -eq 1) { + Write-Error "Merge conflict detected. Manual resolution required." + exit 1 +} +elseif ($LASTEXITCODE -ne 0) { + Write-Error "Merge failed with exit code $LASTEXITCODE." + exit $LASTEXITCODE +} + +$result = New-Object PSObject -Property @{ + BaseBranch = $BaseBranch # The original branch that was checked out when the script ran. + LocalBranch = $LocalBranch # The name of the local branch that was created before the merge. + SourceCommit = $SourceCommit # The commit from Library.Template that was merged in. + SourceBranch = $remoteBranch # The branch from Library.Template that was merged in. +} + +Write-Host $result +Write-Output $result diff --git a/tools/Set-EnvVars.ps1 b/tools/Set-EnvVars.ps1 new file mode 100644 index 00000000..3f6f86ba --- /dev/null +++ b/tools/Set-EnvVars.ps1 @@ -0,0 +1,97 @@ +<# +.SYNOPSIS + Set environment variables in the environment. + Azure Pipeline and CMD environments are considered. +.PARAMETER Variables + A hashtable of variables to be set. +.PARAMETER PrependPath + A set of paths to prepend to the PATH environment variable. +.OUTPUTS + A boolean indicating whether the environment variables can be expected to propagate to the caller's environment. +.DESCRIPTION + 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. + 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. +#> +[CmdletBinding(SupportsShouldProcess=$true)] +Param( + [Parameter(Mandatory=$true, Position=1)] + $Variables, + [string[]]$PrependPath +) + +if ($Variables.Count -eq 0) { + return $true +} + +$cmdInstructions = !$env:TF_BUILD -and !$env:GITHUB_ACTIONS -and !$env:CmdEnvScriptPath -and ($env:PS1UnderCmd -eq '1') +if ($cmdInstructions) { + Write-Warning "Environment variables have been set that will be lost because you're running under cmd.exe" + Write-Host "Environment variables that must be set manually:" -ForegroundColor Blue +} else { + Write-Host "Environment variables set:" -ForegroundColor Blue + Write-Host ($Variables | Out-String) + if ($PrependPath) { + Write-Host "Paths prepended to PATH: $PrependPath" + } +} + +if ($env:TF_BUILD) { + Write-Host "Azure Pipelines detected. Logging commands will be used to propagate environment variables and prepend path." +} + +if ($env:GITHUB_ACTIONS) { + Write-Host "GitHub Actions detected. Logging commands will be used to propagate environment variables and prepend path." +} + +$CmdEnvScript = '' +$Variables.GetEnumerator() |% { + Set-Item -Path env:$($_.Key) -Value $_.Value + + # If we're running in a cloud CI, set these environment variables so they propagate. + if ($env:TF_BUILD) { + Write-Host "##vso[task.setvariable variable=$($_.Key);]$($_.Value)" + } + if ($env:GITHUB_ACTIONS) { + Add-Content -Path $env:GITHUB_ENV -Value "$($_.Key)=$($_.Value)" + } + + if ($cmdInstructions) { + Write-Host "SET $($_.Key)=$($_.Value)" + } + + $CmdEnvScript += "SET $($_.Key)=$($_.Value)`r`n" +} + +$pathDelimiter = ';' +if ($IsMacOS -or $IsLinux) { + $pathDelimiter = ':' +} + +if ($PrependPath) { + $PrependPath |% { + $newPathValue = "$_$pathDelimiter$env:PATH" + Set-Item -Path env:PATH -Value $newPathValue + if ($cmdInstructions) { + Write-Host "SET PATH=$newPathValue" + } + + if ($env:TF_BUILD) { + Write-Host "##vso[task.prependpath]$_" + } + if ($env:GITHUB_ACTIONS) { + Add-Content -Path $env:GITHUB_PATH -Value $_ + } + + $CmdEnvScript += "SET PATH=$_$pathDelimiter%PATH%" + } +} + +if ($env:CmdEnvScriptPath) { + if (Test-Path $env:CmdEnvScriptPath) { + $CmdEnvScript = (Get-Content -Path $env:CmdEnvScriptPath) + $CmdEnvScript + } + + Set-Content -Path $env:CmdEnvScriptPath -Value $CmdEnvScript +} + +return !$cmdInstructions