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..dfae4d2d --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,123 @@ +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: Add msbuild to PATH + uses: microsoft/setup-msbuild@v2 + + - name: 🛠 build + run: msbuild src/Slowcheetah.sln /t:build,pack /p:Configuration=Release /bl:"${{ runner.temp }}/_artifacts/build_logs/build.binlog" + # run: dotnet build -t:build,pack --no-restore -c ${{ env.BUILDCONFIGURATION }} -warnAsError -warnNotAsError:NU1901,NU1902,NU1903,NU1904 /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..564d4af2 --- /dev/null +++ b/.github/workflows/libtemplate-update.yml @@ -0,0 +1,72 @@ +name: Library.Template update + +# PREREQUISITE: This workflow requires the repo to be configured to allow workflows to push commits and create pull requests. +# Visit https://github.com/USER/REPO/settings/actions +# Under "Workflow permissions", select "Read and write permissions" and 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 + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # avoid shallow clone so nbgv can do its work. + + - name: 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 + + if ((git rev-list FETCH_HEAD ^HEAD --count) -eq 0) { + Write-Host "There are no Library.Template updates to merge." + exit 0 + } + + git -c http.extraheader="AUTHORIZATION: bearer $env:GH_TOKEN" push origin -u FETCH_HEAD:refs/heads/auto/libtemplateUpdate + - name: pull request + shell: pwsh + 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/). + +
+ 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 + ``` +
+ + ⚠️ Do **not** squash this pull request when completing it. You must *merge* it." + + gh pr create -H auto/libtemplateUpdate -b $prBody -t $prTitle + env: + GH_TOKEN: ${{ github.token }} diff --git a/.gitignore b/.gitignore index c6115846..cc2b1247 100644 --- a/.gitignore +++ b/.gitignore @@ -4,14 +4,20 @@ ## 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/ @@ -19,52 +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 -# .NET Core -project.lock.json -project.fragment.lock.json -artifacts/ -**/Properties/launchSettings.json +# 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 @@ -93,6 +113,9 @@ ipch/ *.vspx *.sap +# Visual Studio Trace Files +*.e2e + # TFS 2012 Local Workspace $tf/ @@ -113,9 +136,14 @@ _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_* @@ -148,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 @@ -160,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 @@ -183,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/ @@ -199,9 +232,12 @@ ClientBin/ *.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/ @@ -216,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/ @@ -234,6 +277,7 @@ FakesAssemblies/ # Node.js Tools for Visual Studio .ntvs_analysis.dat +node_modules/ # Visual Studio 6 build log *.plg @@ -259,12 +303,8 @@ paket-files/ # FAKE - F# Make .fake/ -# JetBrains Rider -.idea/ -*.sln.iml - -# CodeRush -.cr/ +# CodeRush personal settings +.cr/personal # Python Tools for Visual Studio (PTVS) __pycache__/ @@ -272,4 +312,49 @@ __pycache__/ # Cake - Uncomment if you are using it # tools/** -# !tools/packages.config \ No newline at end of file +# !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..38276086 --- /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/slow-cheetah + 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..ac2fd63d --- /dev/null +++ b/Directory.Build.targets @@ -0,0 +1,13 @@ + + + + 12 + 16.9 + + + + + + + + diff --git a/Directory.Packages.props b/Directory.Packages.props new file mode 100644 index 00000000..15db8d16 --- /dev/null +++ b/Directory.Packages.props @@ -0,0 +1,34 @@ + + + + + true + true + 2.0.171 + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/LICENSE b/LICENSE index a980532a..97992a97 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ Microsoft.VisualStudio.SlowCheetah Copyright (c) Microsoft Corporation -All rights reserved. +All rights reserved. MIT License @@ -20,4 +20,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file +SOFTWARE. diff --git a/README.md b/README.md index 49901a5d..095efa78 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,6 @@ SlowCheetah supports transformations for XML files, specified by [XDT](https://m Perform transformations of XML and JSON files on build per configuration and publish profiles. -Quickly add and preview transformations to a file in the project. +Quickly add and preview transformations to a file in the project. ## [How to Perform Transformations](doc/transforming_files.md) 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..ecbe64c0 --- /dev/null +++ b/SUPPORT.md @@ -0,0 +1,25 @@ +# TODO: The maintainer of this repo has not yet edited this file + +**REPO OWNER**: Do you want Customer Service & Support (CSS) support for this product/project? + +- **No CSS support:** Fill out this template with information about how to file issues and get help. +- **Yes CSS support:** Fill out an intake form at [aka.ms/spot](https://aka.ms/spot). CSS will work with/help you to determine next steps. More details also available at [aka.ms/onboardsupport](https://aka.ms/onboardsupport). +- **Not sure?** Fill out a SPOT intake as though the answer were "Yes". CSS will help you decide. + +*Then remove this first heading from this SUPPORT.MD file before publishing your repo.* + +# 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 **REPO MAINTAINER: INSERT INSTRUCTIONS HERE +FOR HOW TO ENGAGE REPO OWNERS OR COMMUNITY FOR HELP. COULD BE A STACK OVERFLOW TAG OR OTHER +CHANNEL. WHERE WILL YOU HELP PEOPLE?**. + +## Microsoft Support Policy + +Support for this **PROJECT or PRODUCT** is limited to the resources listed above. diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 00000000..79f69c22 --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,34 @@ +trigger: + batch: true + branches: + include: + - main + - microbuild + - 'validate/*' + paths: + exclude: + - doc/ + - '*.md' + - .vscode/ + - .github/ + - azure-pipelines/release.yml + +parameters: +- 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 + +variables: +- template: /azure-pipelines/BuildStageVariables.yml@self + +jobs: +- template: azure-pipelines/build.yml + parameters: + 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 d30a5edf..a781ebb6 100644 --- a/azure-pipelines/TSAOptions.json +++ b/azure-pipelines/TSAOptions.json @@ -1,19 +1,22 @@ { - "tsaVersion": "TsaV2", - "codebase": "NewOrUpdate", - "codebaseName": "SlowCheetah", - "tsaStamp": "DevDiv", - "tsaEnvironment": "PROD", - "notificationAliases": [ - "vsslnx@microsoft.com" - ], - "instanceUrl": "https://devdiv.visualstudio.com", - "projectName": "DevDiv", - "areaPath": "DevDiv\\VS Core\\Project\\SlowCheetah", - "iterationPath": "DevDiv", - "tools": [ - "APIScan", - "CodeQL" - ], - "repositoryName": "SlowCheetah" -} \ No newline at end of file + "tsaVersion": "TsaV2", + "codebase": "NewOrUpdate", + "codebaseName": "SlowCheetah", + "tsaStamp": "DevDiv", + "tsaEnvironment": "PROD", + "notificationAliases": [ + "vsslnx@microsoft.com" + ], + "codebaseAdmins": [ + "REDMOND\\tevinstanley" + ], + "instanceUrl": "https://devdiv.visualstudio.com", + "projectName": "DevDiv", + "areaPath": "DevDiv\\VS Core\\Project\\SlowCheetah", + "iterationPath": "DevDiv", + "tools": [ + "APIScan", + "CodeQL" + ], + "repositoryName": "SlowCheetah" +} 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..b712f14d --- /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" ` + --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..517923f3 --- /dev/null +++ b/azure-pipelines/dotnet.yml @@ -0,0 +1,54 @@ +parameters: +- name: RunTests +- name: IsOptProf + type: boolean + default: false +- name: Is1ESPT + type: boolean + +steps: + + +- task: VSBuild@1 + inputs: + solution: src/SlowCheetah.sln + msbuildArgs: /t:build,pack /bl:"$(Build.ArtifactStagingDirectory)/build_logs/build.binlog" + configuration: $(BuildConfiguration) + displayName: 🛠 Build +# Tevin: delete later +# - 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..b00e73db --- /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: true # 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..46585dd6 --- /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: slow-cheetah-ci + 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: # 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..1f702ba0 --- /dev/null +++ b/azure-pipelines/variables/SymbolsFeatureName.ps1 @@ -0,0 +1 @@ +'slow-cheetah' 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..2d466a84 --- /dev/null +++ b/global.json @@ -0,0 +1,11 @@ +{ + "sdk": { + "version": "8.0.402", + "rollForward": "patch", + "allowPrerelease": false + }, + "msbuild-sdks": { + "Microsoft.Build.NoTargets": "3.7.56", + "Microsoft.Build.Traversal": "3.1.6" + } +} 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 8f49d791..9ba7818d 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,17 +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.SlowCheetah.Tests/BaseTest.cs b/src/Microsoft.VisualStudio.SlowCheetah.Tests/BaseTest.cs index 73b05ec8..b5b5ff29 100644 --- a/src/Microsoft.VisualStudio.SlowCheetah.Tests/BaseTest.cs +++ b/src/Microsoft.VisualStudio.SlowCheetah.Tests/BaseTest.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 license. See LICENSE file in the project root for full license information. #pragma warning disable SA1512 // Single-line comments must not be followed by blank line diff --git a/src/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/ConfigTransformTestsBase.cs b/src/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/ConfigTransformTestsBase.cs index e35e2ffe..fdec22c5 100644 --- a/src/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/ConfigTransformTestsBase.cs +++ b/src/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/ConfigTransformTestsBase.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 license. See LICENSE file in the project root for full license information. #pragma warning disable SA1512 // Single-line comments must not be followed by blank line @@ -13,6 +13,7 @@ namespace Microsoft.VisualStudio.SlowCheetah.Tests.BuildTests using System.IO; using System.Linq; using System.Xml.Linq; + using Microsoft.Build.Utilities; using Xunit; /// @@ -25,7 +26,7 @@ public abstract class ConfigTransformTestsBase : IDisposable /// public string SolutionDir { - get { return Path.Combine(Environment.CurrentDirectory, @"..\..\..\src"); } + get { return Path.Combine(Environment.CurrentDirectory, @"..\..\..\..\test"); } } /// @@ -69,26 +70,50 @@ public void BuildProject(string projectName) { "OutputPath", this.OutputPath }, }; + var msbuildPath = ToolLocationHelper.GetPathToBuildToolsFile("msbuild.exe", ToolLocationHelper.CurrentToolsVersion); + // We use an external process to run msbuild, because XUnit test discovery breaks // when using . // MSBuild NuGet packages proved to be difficult in getting in-proc test builds to run. string projectPath = Path.Combine(this.TestProjectsDir, projectName, projectName + ".csproj"); - string msbuildPath = MSBuildExePath; + //string msbuildPath = MSBuildExePath; string properties = "/p:" + string.Join(",", globalProperties.Select(x => $"{x.Key}={x.Value}")); var startInfo = new System.Diagnostics.ProcessStartInfo() { FileName = msbuildPath, Arguments = $"{projectPath} {properties}", - CreateNoWindow = true, + CreateNoWindow = false, WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden, }; - using (var process = System.Diagnostics.Process.Start(startInfo)) + // Tevin: Delete later + string path = "C:\\src\\libtempslowcheetah\\test\\Microsoft.VisualStudio.SlowCheetah.Tests\\example.txt"; + + // Open the file for reading + using (FileStream fs = File.Open(path, FileMode.Open, FileAccess.Write)) + { + // Read from the file + using (StreamWriter writer = new StreamWriter(fs)) + { + writer.WriteLine($"Running msbuild.exe {startInfo.Arguments}"); + writer.WriteLine($"Running msbuild.exe filename {startInfo.FileName}"); + } + } + + try + { + + using (var process = System.Diagnostics.Process.Start(startInfo)) + { + process.WaitForExit(); + Assert.Equal(0, process.ExitCode); + process.Close(); + } + } + catch (Exception ex) { - process.WaitForExit(); - Assert.Equal(0, process.ExitCode); - process.Close(); + throw new Exception($"Error running msbuild: {ex.Message}", ex); } } diff --git a/src/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/ConsoleAppTests.cs b/src/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/ConsoleAppTests.cs index 92610027..3ce3bfe8 100644 --- a/src/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/ConsoleAppTests.cs +++ b/src/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/ConsoleAppTests.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 license. See LICENSE file in the project root for full license information. #pragma warning disable SA1512 // Single-line comments must not be followed by blank line diff --git a/src/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/WebApplication/Web.config b/src/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/WebApplication/Web.config index 37e083c5..31de11e4 100644 --- a/src/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/WebApplication/Web.config +++ b/src/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/WebApplication/Web.config @@ -1,21 +1,25 @@ - + + - - + + - + \ No newline at end of file diff --git a/src/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/WebApplication/WebApplication.csproj b/src/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/WebApplication/WebApplication.csproj index e1ab618c..85ecd30e 100644 --- a/src/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/WebApplication/WebApplication.csproj +++ b/src/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/WebApplication/WebApplication.csproj @@ -21,6 +21,7 @@ + True true @@ -58,6 +59,15 @@ + + + + + + + + + true diff --git a/src/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/WebAppTests.cs b/src/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/WebAppTests.cs index b2baef5e..431364e4 100644 --- a/src/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/WebAppTests.cs +++ b/src/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/WebAppTests.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 license. See LICENSE file in the project root for full license information. #pragma warning disable SA1512 // Single-line comments must not be followed by blank line diff --git a/src/Microsoft.VisualStudio.SlowCheetah.Tests/Microsoft.VisualStudio.SlowCheetah.Tests.csproj b/src/Microsoft.VisualStudio.SlowCheetah.Tests/Microsoft.VisualStudio.SlowCheetah.Tests.csproj index 093b52e7..2c45c5c2 100644 --- a/src/Microsoft.VisualStudio.SlowCheetah.Tests/Microsoft.VisualStudio.SlowCheetah.Tests.csproj +++ b/src/Microsoft.VisualStudio.SlowCheetah.Tests/Microsoft.VisualStudio.SlowCheetah.Tests.csproj @@ -12,25 +12,17 @@ - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - + + + + + + + - + diff --git a/src/Microsoft.VisualStudio.SlowCheetah.Tests/TestUtilities.cs b/src/Microsoft.VisualStudio.SlowCheetah.Tests/TestUtilities.cs index dbb76dea..9445c7a6 100644 --- a/src/Microsoft.VisualStudio.SlowCheetah.Tests/TestUtilities.cs +++ b/src/Microsoft.VisualStudio.SlowCheetah.Tests/TestUtilities.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 license. See LICENSE file in the project root for full license information. #pragma warning disable SA1512 // Single-line comments must not be followed by blank line diff --git a/src/Microsoft.VisualStudio.SlowCheetah.Tests/TransformTest.cs b/src/Microsoft.VisualStudio.SlowCheetah.Tests/TransformTest.cs index 85f88239..9ba19a01 100644 --- a/src/Microsoft.VisualStudio.SlowCheetah.Tests/TransformTest.cs +++ b/src/Microsoft.VisualStudio.SlowCheetah.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 license. See LICENSE file in the project root for full license information. #pragma warning disable SA1512 // Single-line comments must not be followed by blank line diff --git a/src/Microsoft.VisualStudio.SlowCheetah.Tests/example.txt b/src/Microsoft.VisualStudio.SlowCheetah.Tests/example.txt new file mode 100644 index 00000000..9e188f46 --- /dev/null +++ b/src/Microsoft.VisualStudio.SlowCheetah.Tests/example.txt @@ -0,0 +1,2 @@ +Running msbuild.exe C:\src\libtempslowcheetah\bin\Microsoft.VisualStudio.SlowCheetah.Tests\Debug\net472\..\..\..\..\test\Microsoft.VisualStudio.SlowCheetah.Tests\BuildTests\TestProjects\WebApplication\WebApplication.csproj /p:Configuration=Debug,OutputPath=C:\src\libtempslowcheetah\bin\Microsoft.VisualStudio.SlowCheetah.Tests\Debug\net472\ProjectOutput +Running msbuild.exe filename C:\Program Files\Microsoft Visual Studio\2022\IntPreview\MSBuild\Current\Bin\amd64\msbuild.exe diff --git a/src/Microsoft.VisualStudio.SlowCheetah.VS.Tests/Microsoft.VisualStudio.SlowCheetah.VS.Tests.csproj b/src/Microsoft.VisualStudio.SlowCheetah.VS.Tests/Microsoft.VisualStudio.SlowCheetah.VS.Tests.csproj index 43b54b10..df5cc548 100644 --- a/src/Microsoft.VisualStudio.SlowCheetah.VS.Tests/Microsoft.VisualStudio.SlowCheetah.VS.Tests.csproj +++ b/src/Microsoft.VisualStudio.SlowCheetah.VS.Tests/Microsoft.VisualStudio.SlowCheetah.VS.Tests.csproj @@ -6,21 +6,23 @@ - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - + + + + + + + + + + + + + diff --git a/src/Microsoft.VisualStudio.SlowCheetah.VS/Microsoft.VisualStudio.SlowCheetah.VS.csproj b/src/Microsoft.VisualStudio.SlowCheetah.VS/Microsoft.VisualStudio.SlowCheetah.VS.csproj index 27f99893..85691f4f 100644 --- a/src/Microsoft.VisualStudio.SlowCheetah.VS/Microsoft.VisualStudio.SlowCheetah.VS.csproj +++ b/src/Microsoft.VisualStudio.SlowCheetah.VS/Microsoft.VisualStudio.SlowCheetah.VS.csproj @@ -1,27 +1,27 @@  - + Library net472 false Copyright © Microsoft Corporation. All rights reserved. false + 10.0 + win - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - + + + + + + + + + + + @@ -70,6 +70,11 @@ True Resources.resx + + True + True + VSPackage.resx + @@ -89,6 +94,8 @@ true VSPackage Designer + ResXFileCodeGenerator + VSPackage.Designer.cs diff --git a/src/Microsoft.VisualStudio.SlowCheetah.VS/NugetHandler/PackageHandlers/BackgroundInstallationHandler.cs b/src/Microsoft.VisualStudio.SlowCheetah.VS/NugetHandler/PackageHandlers/BackgroundInstallationHandler.cs index d56e52da..4b25e036 100644 --- a/src/Microsoft.VisualStudio.SlowCheetah.VS/NugetHandler/PackageHandlers/BackgroundInstallationHandler.cs +++ b/src/Microsoft.VisualStudio.SlowCheetah.VS/NugetHandler/PackageHandlers/BackgroundInstallationHandler.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.SlowCheetah.VS { diff --git a/src/Microsoft.VisualStudio.SlowCheetah.VS/NugetHandler/PackageHandlers/BasePackageHandler.cs b/src/Microsoft.VisualStudio.SlowCheetah.VS/NugetHandler/PackageHandlers/BasePackageHandler.cs index 49a69fd7..f487026d 100644 --- a/src/Microsoft.VisualStudio.SlowCheetah.VS/NugetHandler/PackageHandlers/BasePackageHandler.cs +++ b/src/Microsoft.VisualStudio.SlowCheetah.VS/NugetHandler/PackageHandlers/BasePackageHandler.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.SlowCheetah.VS { diff --git a/src/Microsoft.VisualStudio.SlowCheetah.VS/NugetHandler/PackageHandlers/DialogInstallationHandler.cs b/src/Microsoft.VisualStudio.SlowCheetah.VS/NugetHandler/PackageHandlers/DialogInstallationHandler.cs index 600ff6e7..ea67ec25 100644 --- a/src/Microsoft.VisualStudio.SlowCheetah.VS/NugetHandler/PackageHandlers/DialogInstallationHandler.cs +++ b/src/Microsoft.VisualStudio.SlowCheetah.VS/NugetHandler/PackageHandlers/DialogInstallationHandler.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.SlowCheetah.VS { diff --git a/src/Microsoft.VisualStudio.SlowCheetah.VS/NugetHandler/PackageHandlers/EmptyHandler.cs b/src/Microsoft.VisualStudio.SlowCheetah.VS/NugetHandler/PackageHandlers/EmptyHandler.cs index 4e4dd64c..a3643cc8 100644 --- a/src/Microsoft.VisualStudio.SlowCheetah.VS/NugetHandler/PackageHandlers/EmptyHandler.cs +++ b/src/Microsoft.VisualStudio.SlowCheetah.VS/NugetHandler/PackageHandlers/EmptyHandler.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.SlowCheetah.VS { diff --git a/src/Microsoft.VisualStudio.SlowCheetah.VS/NugetHandler/PackageHandlers/IPackageHandler.cs b/src/Microsoft.VisualStudio.SlowCheetah.VS/NugetHandler/PackageHandlers/IPackageHandler.cs index ed3563c2..f867ad4f 100644 --- a/src/Microsoft.VisualStudio.SlowCheetah.VS/NugetHandler/PackageHandlers/IPackageHandler.cs +++ b/src/Microsoft.VisualStudio.SlowCheetah.VS/NugetHandler/PackageHandlers/IPackageHandler.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.SlowCheetah.VS { diff --git a/src/Microsoft.VisualStudio.SlowCheetah.VS/NugetHandler/PackageHandlers/NuGetUninstaller.cs b/src/Microsoft.VisualStudio.SlowCheetah.VS/NugetHandler/PackageHandlers/NuGetUninstaller.cs index d29c00d7..1007ee4e 100644 --- a/src/Microsoft.VisualStudio.SlowCheetah.VS/NugetHandler/PackageHandlers/NuGetUninstaller.cs +++ b/src/Microsoft.VisualStudio.SlowCheetah.VS/NugetHandler/PackageHandlers/NuGetUninstaller.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.SlowCheetah.VS { diff --git a/src/Microsoft.VisualStudio.SlowCheetah.VS/NugetHandler/PackageHandlers/NugetInstaller.cs b/src/Microsoft.VisualStudio.SlowCheetah.VS/NugetHandler/PackageHandlers/NugetInstaller.cs index f547edd5..a5336728 100644 --- a/src/Microsoft.VisualStudio.SlowCheetah.VS/NugetHandler/PackageHandlers/NugetInstaller.cs +++ b/src/Microsoft.VisualStudio.SlowCheetah.VS/NugetHandler/PackageHandlers/NugetInstaller.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.SlowCheetah.VS { diff --git a/src/Microsoft.VisualStudio.SlowCheetah.VS/NugetHandler/PackageHandlers/TargetsUninstaller.cs b/src/Microsoft.VisualStudio.SlowCheetah.VS/NugetHandler/PackageHandlers/TargetsUninstaller.cs index f4502ab8..c3384ecc 100644 --- a/src/Microsoft.VisualStudio.SlowCheetah.VS/NugetHandler/PackageHandlers/TargetsUninstaller.cs +++ b/src/Microsoft.VisualStudio.SlowCheetah.VS/NugetHandler/PackageHandlers/TargetsUninstaller.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.SlowCheetah.VS { diff --git a/src/Microsoft.VisualStudio.SlowCheetah.VS/NugetHandler/PackageHandlers/UserInstallationHandler.cs b/src/Microsoft.VisualStudio.SlowCheetah.VS/NugetHandler/PackageHandlers/UserInstallationHandler.cs index 66a74909..45715975 100644 --- a/src/Microsoft.VisualStudio.SlowCheetah.VS/NugetHandler/PackageHandlers/UserInstallationHandler.cs +++ b/src/Microsoft.VisualStudio.SlowCheetah.VS/NugetHandler/PackageHandlers/UserInstallationHandler.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.SlowCheetah.VS { diff --git a/src/Microsoft.VisualStudio.SlowCheetah.VS/NugetHandler/SlowCheetahNuGetManager.cs b/src/Microsoft.VisualStudio.SlowCheetah.VS/NugetHandler/SlowCheetahNuGetManager.cs index 6092510c..428dae86 100644 --- a/src/Microsoft.VisualStudio.SlowCheetah.VS/NugetHandler/SlowCheetahNuGetManager.cs +++ b/src/Microsoft.VisualStudio.SlowCheetah.VS/NugetHandler/SlowCheetahNuGetManager.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.SlowCheetah.VS { diff --git a/src/Microsoft.VisualStudio.SlowCheetah.VS/NugetHandler/VsProjectTypes.cs b/src/Microsoft.VisualStudio.SlowCheetah.VS/NugetHandler/VsProjectTypes.cs index 134e5c45..7d5e933d 100644 --- a/src/Microsoft.VisualStudio.SlowCheetah.VS/NugetHandler/VsProjectTypes.cs +++ b/src/Microsoft.VisualStudio.SlowCheetah.VS/NugetHandler/VsProjectTypes.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.SlowCheetah.VS { diff --git a/src/Microsoft.VisualStudio.SlowCheetah.VS/Options/AdvancedOptionsDialogPage.cs b/src/Microsoft.VisualStudio.SlowCheetah.VS/Options/AdvancedOptionsDialogPage.cs index ddb31ef6..2132f18f 100644 --- a/src/Microsoft.VisualStudio.SlowCheetah.VS/Options/AdvancedOptionsDialogPage.cs +++ b/src/Microsoft.VisualStudio.SlowCheetah.VS/Options/AdvancedOptionsDialogPage.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.SlowCheetah.VS { diff --git a/src/Microsoft.VisualStudio.SlowCheetah.VS/Options/AdvancedOptionsUserControl.cs b/src/Microsoft.VisualStudio.SlowCheetah.VS/Options/AdvancedOptionsUserControl.cs index 5c857cb5..ce239a63 100644 --- a/src/Microsoft.VisualStudio.SlowCheetah.VS/Options/AdvancedOptionsUserControl.cs +++ b/src/Microsoft.VisualStudio.SlowCheetah.VS/Options/AdvancedOptionsUserControl.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.SlowCheetah.VS { diff --git a/src/Microsoft.VisualStudio.SlowCheetah.VS/Options/BaseOptionsDialogPage.cs b/src/Microsoft.VisualStudio.SlowCheetah.VS/Options/BaseOptionsDialogPage.cs index a9d6e37f..d1a015db 100644 --- a/src/Microsoft.VisualStudio.SlowCheetah.VS/Options/BaseOptionsDialogPage.cs +++ b/src/Microsoft.VisualStudio.SlowCheetah.VS/Options/BaseOptionsDialogPage.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.SlowCheetah.VS { diff --git a/src/Microsoft.VisualStudio.SlowCheetah.VS/Options/OptionsDialogPage.cs b/src/Microsoft.VisualStudio.SlowCheetah.VS/Options/OptionsDialogPage.cs index d211b4c6..771eb4d8 100644 --- a/src/Microsoft.VisualStudio.SlowCheetah.VS/Options/OptionsDialogPage.cs +++ b/src/Microsoft.VisualStudio.SlowCheetah.VS/Options/OptionsDialogPage.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.SlowCheetah.VS { diff --git a/src/Microsoft.VisualStudio.SlowCheetah.VS/Options/OptionsUserControl.cs b/src/Microsoft.VisualStudio.SlowCheetah.VS/Options/OptionsUserControl.cs index 2d19c3ce..806133f2 100644 --- a/src/Microsoft.VisualStudio.SlowCheetah.VS/Options/OptionsUserControl.cs +++ b/src/Microsoft.VisualStudio.SlowCheetah.VS/Options/OptionsUserControl.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.SlowCheetah.VS { diff --git a/src/Microsoft.VisualStudio.SlowCheetah.VS/Package/AddTransformCommand.cs b/src/Microsoft.VisualStudio.SlowCheetah.VS/Package/AddTransformCommand.cs index f176c834..464c7305 100644 --- a/src/Microsoft.VisualStudio.SlowCheetah.VS/Package/AddTransformCommand.cs +++ b/src/Microsoft.VisualStudio.SlowCheetah.VS/Package/AddTransformCommand.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. #pragma warning disable SA1512 // Single-line comments must not be followed by blank line @@ -157,7 +157,7 @@ protected override void OnInvoke(object sender, EventArgs e) } // if it is a web project we should add publish profile specific transforms as well - var publishProfileTransforms = this.GetPublishProfileTransforms(hierarchy, projectFullPath); + IEnumerable publishProfileTransforms = this.GetPublishProfileTransforms(hierarchy, projectFullPath); if (publishProfileTransforms != null) { transformsToCreate.AddRange(publishProfileTransforms); diff --git a/src/Microsoft.VisualStudio.SlowCheetah.VS/Package/BaseCommand.cs b/src/Microsoft.VisualStudio.SlowCheetah.VS/Package/BaseCommand.cs index fa73a199..75eca39e 100644 --- a/src/Microsoft.VisualStudio.SlowCheetah.VS/Package/BaseCommand.cs +++ b/src/Microsoft.VisualStudio.SlowCheetah.VS/Package/BaseCommand.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.SlowCheetah.VS { diff --git a/src/Microsoft.VisualStudio.SlowCheetah.VS/Package/PackageSolutionEvents.cs b/src/Microsoft.VisualStudio.SlowCheetah.VS/Package/PackageSolutionEvents.cs index d2f18aa3..dd6599b2 100644 --- a/src/Microsoft.VisualStudio.SlowCheetah.VS/Package/PackageSolutionEvents.cs +++ b/src/Microsoft.VisualStudio.SlowCheetah.VS/Package/PackageSolutionEvents.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.SlowCheetah.VS { diff --git a/src/Microsoft.VisualStudio.SlowCheetah.VS/Package/PreviewTransformCommand.cs b/src/Microsoft.VisualStudio.SlowCheetah.VS/Package/PreviewTransformCommand.cs index e64a37dd..5d85839a 100644 --- a/src/Microsoft.VisualStudio.SlowCheetah.VS/Package/PreviewTransformCommand.cs +++ b/src/Microsoft.VisualStudio.SlowCheetah.VS/Package/PreviewTransformCommand.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. #pragma warning disable SA1512 // Single-line comments must not be followed by blank line @@ -261,7 +261,7 @@ private void PreviewTransform(IVsHierarchy hier, string sourceFile, string trans ProcessStartInfo psi = new ProcessStartInfo(advancedOptionsPage.PreviewToolExecutablePath, string.Format(CultureInfo.CurrentCulture, advancedOptionsPage.PreviewToolCommandLine, $"\"{sourceFile}\"", $"\"{destFile}\"")) { CreateNoWindow = true, - UseShellExecute = false + UseShellExecute = false, }; System.Diagnostics.Process.Start(psi); } diff --git a/src/Microsoft.VisualStudio.SlowCheetah.VS/Package/SlowCheetahPackageLogger.cs b/src/Microsoft.VisualStudio.SlowCheetah.VS/Package/SlowCheetahPackageLogger.cs index b85cc9ea..2400c13b 100644 --- a/src/Microsoft.VisualStudio.SlowCheetah.VS/Package/SlowCheetahPackageLogger.cs +++ b/src/Microsoft.VisualStudio.SlowCheetah.VS/Package/SlowCheetahPackageLogger.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.SlowCheetah.VS { diff --git a/src/Microsoft.VisualStudio.SlowCheetah.VS/Resources/Guids.cs b/src/Microsoft.VisualStudio.SlowCheetah.VS/Resources/Guids.cs index 16e104fc..99d7fc41 100644 --- a/src/Microsoft.VisualStudio.SlowCheetah.VS/Resources/Guids.cs +++ b/src/Microsoft.VisualStudio.SlowCheetah.VS/Resources/Guids.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. #pragma warning disable SA1512 // Single-line comments must not be followed by blank line diff --git a/src/Microsoft.VisualStudio.SlowCheetah.VS/Resources/PkgCmdID.cs b/src/Microsoft.VisualStudio.SlowCheetah.VS/Resources/PkgCmdID.cs index c0a5a3b3..bbeea178 100644 --- a/src/Microsoft.VisualStudio.SlowCheetah.VS/Resources/PkgCmdID.cs +++ b/src/Microsoft.VisualStudio.SlowCheetah.VS/Resources/PkgCmdID.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. #pragma warning disable SA1512 // Single-line comments must not be followed by blank line diff --git a/src/Microsoft.VisualStudio.SlowCheetah.VS/SlowCheetahPackage.cs b/src/Microsoft.VisualStudio.SlowCheetah.VS/SlowCheetahPackage.cs index 883331ee..152d3ff6 100644 --- a/src/Microsoft.VisualStudio.SlowCheetah.VS/SlowCheetahPackage.cs +++ b/src/Microsoft.VisualStudio.SlowCheetah.VS/SlowCheetahPackage.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. #pragma warning disable SA1512 // Single-line comments must not be followed by blank line diff --git a/src/Microsoft.VisualStudio.SlowCheetah.VS/TransformationPreviewLogger.cs b/src/Microsoft.VisualStudio.SlowCheetah.VS/TransformationPreviewLogger.cs index b1851628..920014d6 100644 --- a/src/Microsoft.VisualStudio.SlowCheetah.VS/TransformationPreviewLogger.cs +++ b/src/Microsoft.VisualStudio.SlowCheetah.VS/TransformationPreviewLogger.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. #pragma warning disable SA1512 // Single-line comments must not be followed by blank line diff --git a/src/Microsoft.VisualStudio.SlowCheetah.VS/Utilities/PackageUtilities.cs b/src/Microsoft.VisualStudio.SlowCheetah.VS/Utilities/PackageUtilities.cs index 221da9e0..e256c6b6 100644 --- a/src/Microsoft.VisualStudio.SlowCheetah.VS/Utilities/PackageUtilities.cs +++ b/src/Microsoft.VisualStudio.SlowCheetah.VS/Utilities/PackageUtilities.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. #pragma warning disable SA1512 // Single-line comments must not be followed by blank line diff --git a/src/Microsoft.VisualStudio.SlowCheetah.VS/Utilities/ProjectUtilities.cs b/src/Microsoft.VisualStudio.SlowCheetah.VS/Utilities/ProjectUtilities.cs index b19a1032..fb41a583 100644 --- a/src/Microsoft.VisualStudio.SlowCheetah.VS/Utilities/ProjectUtilities.cs +++ b/src/Microsoft.VisualStudio.SlowCheetah.VS/Utilities/ProjectUtilities.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. #pragma warning disable SA1512 // Single-line comments must not be followed by blank line diff --git a/src/Microsoft.VisualStudio.SlowCheetah.VS/VSPackage.Designer.cs b/src/Microsoft.VisualStudio.SlowCheetah.VS/VSPackage.Designer.cs new file mode 100644 index 00000000..ccacc044 --- /dev/null +++ b/src/Microsoft.VisualStudio.SlowCheetah.VS/VSPackage.Designer.cs @@ -0,0 +1,81 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Microsoft.VisualStudio.SlowCheetah.VS { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class VSPackage { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal VSPackage() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.VisualStudio.SlowCheetah.VS.VSPackage", typeof(VSPackage).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to SlowCheetah - XML config transforms. + /// + internal static string _110 { + get { + return ResourceManager.GetString("110", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This package enables you to transform your app.config or any other XML file based on the build configuration. It also adds additional tooling to help you create XML transforms.. + /// + internal static string _112 { + get { + return ResourceManager.GetString("112", resourceCulture); + } + } + } +} diff --git a/src/Microsoft.VisualStudio.SlowCheetah.Vsix/Microsoft.VisualStudio.SlowCheetah.Vsix.csproj b/src/Microsoft.VisualStudio.SlowCheetah.Vsix/Microsoft.VisualStudio.SlowCheetah.Vsix.csproj index 196ef619..162d1723 100644 --- a/src/Microsoft.VisualStudio.SlowCheetah.Vsix/Microsoft.VisualStudio.SlowCheetah.Vsix.csproj +++ b/src/Microsoft.VisualStudio.SlowCheetah.Vsix/Microsoft.VisualStudio.SlowCheetah.Vsix.csproj @@ -31,8 +31,9 @@ false false false - $(OutputPath)net472\ + $(BaseOutputPath)$(Configuration)\net472\ Microsoft.VisualStudio.SlowCheetah + win true @@ -59,7 +60,7 @@ CopyIfNewer - true + false Newtonsoft.Json.dll false @@ -79,16 +80,16 @@ - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - + + + {80A712EE-7B5C-44D3-A2AD-F918B893B6DF} @@ -102,7 +103,7 @@ - + - - - - - + + + @@ -33,12 +28,9 @@ - - - - - - + + + @@ -59,10 +51,27 @@ + + + 3PartySHA2 + None + + + + + + + + + + + + all + diff --git a/src/Microsoft.VisualStudio.SlowCheetah/Properties/AssemblyInfo.cs b/src/Microsoft.VisualStudio.SlowCheetah/Properties/AssemblyInfo.cs index be9d63d6..9b960036 100644 --- a/src/Microsoft.VisualStudio.SlowCheetah/Properties/AssemblyInfo.cs +++ b/src/Microsoft.VisualStudio.SlowCheetah/Properties/AssemblyInfo.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. using System.Resources; diff --git a/src/Microsoft.VisualStudio.SlowCheetah/Resources/Resources.Designer.cs b/src/Microsoft.VisualStudio.SlowCheetah/Resources/Resources.Designer.cs index 1faf678f..3857c6a2 100644 --- a/src/Microsoft.VisualStudio.SlowCheetah/Resources/Resources.Designer.cs +++ b/src/Microsoft.VisualStudio.SlowCheetah/Resources/Resources.Designer.cs @@ -19,7 +19,7 @@ namespace Microsoft.VisualStudio.SlowCheetah.Resources { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { diff --git a/src/Microsoft.VisualStudio.SlowCheetah/Transformer/ITransformer.cs b/src/Microsoft.VisualStudio.SlowCheetah/Transformer/ITransformer.cs index 26536cba..d00173df 100644 --- a/src/Microsoft.VisualStudio.SlowCheetah/Transformer/ITransformer.cs +++ b/src/Microsoft.VisualStudio.SlowCheetah/Transformer/ITransformer.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.SlowCheetah { diff --git a/src/Microsoft.VisualStudio.SlowCheetah/Transformer/JsonTransformer.cs b/src/Microsoft.VisualStudio.SlowCheetah/Transformer/JsonTransformer.cs index 0e703b2a..a779b495 100644 --- a/src/Microsoft.VisualStudio.SlowCheetah/Transformer/JsonTransformer.cs +++ b/src/Microsoft.VisualStudio.SlowCheetah/Transformer/JsonTransformer.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.SlowCheetah { @@ -59,7 +59,7 @@ public void CreateTransformFile(string sourcePath, string transformPath, bool ov // If the file should be overwritten or if it doesn't exist, we create it if (overwrite || !File.Exists(transformPath)) { - var encoding = TransformUtilities.GetEncoding(sourcePath); + System.Text.Encoding encoding = TransformUtilities.GetEncoding(sourcePath); File.WriteAllText(transformPath, Resources.Resources.JsonTransform_TransformFileContents, encoding); } } @@ -146,7 +146,7 @@ private bool TrySaveToFile(Stream result, string sourceFile, string destinationF try { string contents; - var encoding = TransformUtilities.GetEncoding(sourceFile); + System.Text.Encoding encoding = TransformUtilities.GetEncoding(sourceFile); using (StreamReader reader = new StreamReader(result, true)) { // Get the contents of the result stram diff --git a/src/Microsoft.VisualStudio.SlowCheetah/Transformer/TransformUtilities.cs b/src/Microsoft.VisualStudio.SlowCheetah/Transformer/TransformUtilities.cs index 6bcf4a9a..d8741fc3 100644 --- a/src/Microsoft.VisualStudio.SlowCheetah/Transformer/TransformUtilities.cs +++ b/src/Microsoft.VisualStudio.SlowCheetah/Transformer/TransformUtilities.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.SlowCheetah { diff --git a/src/Microsoft.VisualStudio.SlowCheetah/Transformer/TransformerFactory.cs b/src/Microsoft.VisualStudio.SlowCheetah/Transformer/TransformerFactory.cs index cded4f22..120a22f2 100644 --- a/src/Microsoft.VisualStudio.SlowCheetah/Transformer/TransformerFactory.cs +++ b/src/Microsoft.VisualStudio.SlowCheetah/Transformer/TransformerFactory.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.SlowCheetah { diff --git a/src/Microsoft.VisualStudio.SlowCheetah/Transformer/XmlTransformer.cs b/src/Microsoft.VisualStudio.SlowCheetah/Transformer/XmlTransformer.cs index fd13bde7..bc26105c 100644 --- a/src/Microsoft.VisualStudio.SlowCheetah/Transformer/XmlTransformer.cs +++ b/src/Microsoft.VisualStudio.SlowCheetah/Transformer/XmlTransformer.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.SlowCheetah { 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/SlowCheetah.sln b/src/SlowCheetah.sln index ef6eb1ad..a750bfe2 100644 --- a/src/SlowCheetah.sln +++ b/src/SlowCheetah.sln @@ -1,24 +1,28 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26430.6 +# Visual Studio Version 17 +VisualStudioVersion = 17.13.35505.181 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.SlowCheetah", "Microsoft.VisualStudio.SlowCheetah\Microsoft.VisualStudio.SlowCheetah.csproj", "{6354D859-E629-49FC-B154-FC0BA42D71B0}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.SlowCheetah.Tests", "Microsoft.VisualStudio.SlowCheetah.Tests\Microsoft.VisualStudio.SlowCheetah.Tests.csproj", "{8011779C-108E-46A7-B0E3-BE2DC023BDFE}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{16014DDD-6525-48EF-9D6A-E1067594E1E8}" ProjectSection(SolutionItems) = preProject + ..\Directory.Build.props = ..\Directory.Build.props + ..\Directory.Packages.props = ..\Directory.Packages.props + Microsoft.VisualStudio.SlowCheetah.Tests\Microsoft.VisualStudio.SlowCheetah.Tests.csproj = Microsoft.VisualStudio.SlowCheetah.Tests\Microsoft.VisualStudio.SlowCheetah.Tests.csproj + ..\test\Microsoft.VisualStudio.SlowCheetah.Tests\Microsoft.VisualStudio.SlowCheetah.Tests.csproj = ..\test\Microsoft.VisualStudio.SlowCheetah.Tests\Microsoft.VisualStudio.SlowCheetah.Tests.csproj stylecop.json = stylecop.json ..\version.json = ..\version.json EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.SlowCheetah.VS.Tests", "Microsoft.VisualStudio.SlowCheetah.VS.Tests\Microsoft.VisualStudio.SlowCheetah.VS.Tests.csproj", "{CF485485-48C5-4EEF-A552-7CE8A7A360E8}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.VisualStudio.SlowCheetah.Vsix", "Microsoft.VisualStudio.SlowCheetah.Vsix\Microsoft.VisualStudio.SlowCheetah.Vsix.csproj", "{CD2AF93D-5714-404B-9D42-61477BE8F3CF}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.SlowCheetah.VS", "Microsoft.VisualStudio.SlowCheetah.VS\Microsoft.VisualStudio.SlowCheetah.VS.csproj", "{80A712EE-7B5C-44D3-A2AD-F918B893B6DF}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.VisualStudio.SlowCheetah.VS.Tests", "..\test\Microsoft.VisualStudio.SlowCheetah.VS.Tests\Microsoft.VisualStudio.SlowCheetah.VS.Tests.csproj", "{7CAC125E-EE65-2E4D-57C6-25E4C869F409}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.VisualStudio.SlowCheetah.Tests", "..\test\Microsoft.VisualStudio.SlowCheetah.Tests\Microsoft.VisualStudio.SlowCheetah.Tests.csproj", "{2CDACD21-BB66-9B22-E564-3E5D0DFADE8F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -41,30 +45,6 @@ Global {6354D859-E629-49FC-B154-FC0BA42D71B0}.Release|x64.Build.0 = Release|Any CPU {6354D859-E629-49FC-B154-FC0BA42D71B0}.Release|x86.ActiveCfg = Release|Any CPU {6354D859-E629-49FC-B154-FC0BA42D71B0}.Release|x86.Build.0 = Release|Any CPU - {8011779C-108E-46A7-B0E3-BE2DC023BDFE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8011779C-108E-46A7-B0E3-BE2DC023BDFE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8011779C-108E-46A7-B0E3-BE2DC023BDFE}.Debug|x64.ActiveCfg = Debug|Any CPU - {8011779C-108E-46A7-B0E3-BE2DC023BDFE}.Debug|x64.Build.0 = Debug|Any CPU - {8011779C-108E-46A7-B0E3-BE2DC023BDFE}.Debug|x86.ActiveCfg = Debug|Any CPU - {8011779C-108E-46A7-B0E3-BE2DC023BDFE}.Debug|x86.Build.0 = Debug|Any CPU - {8011779C-108E-46A7-B0E3-BE2DC023BDFE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8011779C-108E-46A7-B0E3-BE2DC023BDFE}.Release|Any CPU.Build.0 = Release|Any CPU - {8011779C-108E-46A7-B0E3-BE2DC023BDFE}.Release|x64.ActiveCfg = Release|Any CPU - {8011779C-108E-46A7-B0E3-BE2DC023BDFE}.Release|x64.Build.0 = Release|Any CPU - {8011779C-108E-46A7-B0E3-BE2DC023BDFE}.Release|x86.ActiveCfg = Release|Any CPU - {8011779C-108E-46A7-B0E3-BE2DC023BDFE}.Release|x86.Build.0 = Release|Any CPU - {CF485485-48C5-4EEF-A552-7CE8A7A360E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CF485485-48C5-4EEF-A552-7CE8A7A360E8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CF485485-48C5-4EEF-A552-7CE8A7A360E8}.Debug|x64.ActiveCfg = Debug|Any CPU - {CF485485-48C5-4EEF-A552-7CE8A7A360E8}.Debug|x64.Build.0 = Debug|Any CPU - {CF485485-48C5-4EEF-A552-7CE8A7A360E8}.Debug|x86.ActiveCfg = Debug|Any CPU - {CF485485-48C5-4EEF-A552-7CE8A7A360E8}.Debug|x86.Build.0 = Debug|Any CPU - {CF485485-48C5-4EEF-A552-7CE8A7A360E8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CF485485-48C5-4EEF-A552-7CE8A7A360E8}.Release|Any CPU.Build.0 = Release|Any CPU - {CF485485-48C5-4EEF-A552-7CE8A7A360E8}.Release|x64.ActiveCfg = Release|Any CPU - {CF485485-48C5-4EEF-A552-7CE8A7A360E8}.Release|x64.Build.0 = Release|Any CPU - {CF485485-48C5-4EEF-A552-7CE8A7A360E8}.Release|x86.ActiveCfg = Release|Any CPU - {CF485485-48C5-4EEF-A552-7CE8A7A360E8}.Release|x86.Build.0 = Release|Any CPU {CD2AF93D-5714-404B-9D42-61477BE8F3CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CD2AF93D-5714-404B-9D42-61477BE8F3CF}.Debug|Any CPU.Build.0 = Debug|Any CPU {CD2AF93D-5714-404B-9D42-61477BE8F3CF}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -89,8 +69,35 @@ Global {80A712EE-7B5C-44D3-A2AD-F918B893B6DF}.Release|x64.Build.0 = Release|Any CPU {80A712EE-7B5C-44D3-A2AD-F918B893B6DF}.Release|x86.ActiveCfg = Release|Any CPU {80A712EE-7B5C-44D3-A2AD-F918B893B6DF}.Release|x86.Build.0 = Release|Any CPU + {7CAC125E-EE65-2E4D-57C6-25E4C869F409}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7CAC125E-EE65-2E4D-57C6-25E4C869F409}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7CAC125E-EE65-2E4D-57C6-25E4C869F409}.Debug|x64.ActiveCfg = Debug|Any CPU + {7CAC125E-EE65-2E4D-57C6-25E4C869F409}.Debug|x64.Build.0 = Debug|Any CPU + {7CAC125E-EE65-2E4D-57C6-25E4C869F409}.Debug|x86.ActiveCfg = Debug|Any CPU + {7CAC125E-EE65-2E4D-57C6-25E4C869F409}.Debug|x86.Build.0 = Debug|Any CPU + {7CAC125E-EE65-2E4D-57C6-25E4C869F409}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7CAC125E-EE65-2E4D-57C6-25E4C869F409}.Release|Any CPU.Build.0 = Release|Any CPU + {7CAC125E-EE65-2E4D-57C6-25E4C869F409}.Release|x64.ActiveCfg = Release|Any CPU + {7CAC125E-EE65-2E4D-57C6-25E4C869F409}.Release|x64.Build.0 = Release|Any CPU + {7CAC125E-EE65-2E4D-57C6-25E4C869F409}.Release|x86.ActiveCfg = Release|Any CPU + {7CAC125E-EE65-2E4D-57C6-25E4C869F409}.Release|x86.Build.0 = Release|Any CPU + {2CDACD21-BB66-9B22-E564-3E5D0DFADE8F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2CDACD21-BB66-9B22-E564-3E5D0DFADE8F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2CDACD21-BB66-9B22-E564-3E5D0DFADE8F}.Debug|x64.ActiveCfg = Debug|Any CPU + {2CDACD21-BB66-9B22-E564-3E5D0DFADE8F}.Debug|x64.Build.0 = Debug|Any CPU + {2CDACD21-BB66-9B22-E564-3E5D0DFADE8F}.Debug|x86.ActiveCfg = Debug|Any CPU + {2CDACD21-BB66-9B22-E564-3E5D0DFADE8F}.Debug|x86.Build.0 = Debug|Any CPU + {2CDACD21-BB66-9B22-E564-3E5D0DFADE8F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2CDACD21-BB66-9B22-E564-3E5D0DFADE8F}.Release|Any CPU.Build.0 = Release|Any CPU + {2CDACD21-BB66-9B22-E564-3E5D0DFADE8F}.Release|x64.ActiveCfg = Release|Any CPU + {2CDACD21-BB66-9B22-E564-3E5D0DFADE8F}.Release|x64.Build.0 = Release|Any CPU + {2CDACD21-BB66-9B22-E564-3E5D0DFADE8F}.Release|x86.ActiveCfg = Release|Any CPU + {2CDACD21-BB66-9B22-E564-3E5D0DFADE8F}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {AD9FD28F-3EAE-4C44-B1B1-E91933777300} + EndGlobalSection EndGlobal 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/stylecop.json b/stylecop.json similarity index 76% rename from src/stylecop.json rename to stylecop.json index 5743660d..c125c72f 100644 --- a/src/stylecop.json +++ b/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/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/test/Microsoft.VisualStudio.SlowCheetah.Tests/App.config b/test/Microsoft.VisualStudio.SlowCheetah.Tests/App.config new file mode 100644 index 00000000..7a556c88 --- /dev/null +++ b/test/Microsoft.VisualStudio.SlowCheetah.Tests/App.config @@ -0,0 +1,6 @@ + + + + + + diff --git a/test/Microsoft.VisualStudio.SlowCheetah.Tests/BaseTest.cs b/test/Microsoft.VisualStudio.SlowCheetah.Tests/BaseTest.cs new file mode 100644 index 00000000..42d06fc6 --- /dev/null +++ b/test/Microsoft.VisualStudio.SlowCheetah.Tests/BaseTest.cs @@ -0,0 +1,82 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#pragma warning disable SA1512 // Single-line comments must not be followed by blank line + +// Copyright (C) Sayed Ibrahim Hashimi +#pragma warning restore SA1512 // Single-line comments must not be followed by blank line + +namespace Microsoft.VisualStudio.SlowCheetah.Tests +{ + using System; + using System.Collections.Generic; + using System.IO; + + /// + /// Class that contains base initialization methods for all the unit tests, such as creating and deleting temporary files. + /// + public class BaseTest : IDisposable + { + /// + /// Gets the list of temporary files to delete after test. + /// + protected IList FilesToDeleteAfterTest { get; } = new List(); + + /// + /// At the end of tests, attempts to delete all the files generated during the test. + /// + public void Dispose() + { + foreach (string filename in this.FilesToDeleteAfterTest) + { + if (File.Exists(filename)) + { + try + { + File.Delete(filename); + } + catch (System.IO.IOException) + { + // some processes will hold onto the file until the AppDomain is unloaded + } + } + } + + this.FilesToDeleteAfterTest.Clear(); + } + + /// + /// Writes a string to a temporary file. + /// + /// Content to be written. + /// The path of the created file. + protected virtual string WriteTextToTempFile(string content) + { + if (string.IsNullOrEmpty(content)) + { + throw new ArgumentNullException(nameof(content)); + } + + string tempFile = this.GetTempFilename(true); + File.WriteAllText(tempFile, content); + return tempFile; + } + + /// + /// Creates a temporary file for testing. + /// + /// If it is ensured that a file with the same name doesn't already exist. + /// The path to the created file. + protected virtual string GetTempFilename(bool ensureFileDoesntExist) + { + string path = Path.GetTempFileName(); + if (ensureFileDoesntExist && File.Exists(path)) + { + File.Delete(path); + } + + this.FilesToDeleteAfterTest.Add(path); + return path; + } + } +} diff --git a/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/ConfigTransformTestsBase.cs b/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/ConfigTransformTestsBase.cs new file mode 100644 index 00000000..aa81c390 --- /dev/null +++ b/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/ConfigTransformTestsBase.cs @@ -0,0 +1,157 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#pragma warning disable SA1512 // Single-line comments must not be followed by blank line + +// Copyright (C) Sayed Ibrahim Hashimi +#pragma warning restore SA1512 // Single-line comments must not be followed by blank line + +namespace Microsoft.VisualStudio.SlowCheetah.Tests.BuildTests +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Xml.Linq; + using Microsoft.Build.Utilities; + using Xunit; + + /// + /// Base class for transformation tests. + /// + public abstract class ConfigTransformTestsBase : IDisposable + { + /// + /// Gets the test solution directory. + /// + public string SolutionDir + { + get { return Path.Combine(Environment.CurrentDirectory, @"..\..\..\..\test"); } + } + + /// + /// Gets the output path of the test project. + /// + public string OutputPath + { + get { return Path.Combine(Environment.CurrentDirectory, @"ProjectOutput"); } + } + + /// + /// Gets the test projects directory. + /// + public string TestProjectsDir + { + get { return Path.Combine(this.SolutionDir, @"Microsoft.VisualStudio.SlowCheetah.Tests\BuildTests\TestProjects"); } + } + + /// + /// Gets the msbuild exe path that was cached during build. + /// + private static string MSBuildExePath + { + get + { + string msbuildPathCache = Path.Combine(Environment.CurrentDirectory, "msbuildPath.txt"); + return Path.Combine(File.ReadAllLines(msbuildPathCache).First(), "msbuild.exe"); + } + } + + /// + /// Builds the project of the given name from the . + /// + /// Name of the project to be built. + /// Must correspond to a folder name in the test projects directory. + public void BuildProject(string projectName) + { + var globalProperties = new Dictionary() + { + { "Configuration", "Debug" }, + { "OutputPath", this.OutputPath }, + }; + + var msbuildPath = ToolLocationHelper.GetPathToBuildToolsFile("msbuild.exe", ToolLocationHelper.CurrentToolsVersion); + + // We use an external process to run msbuild, because XUnit test discovery breaks + // when using . + // MSBuild NuGet packages proved to be difficult in getting in-proc test builds to run. + string projectPath = Path.Combine(this.TestProjectsDir, projectName, projectName + ".csproj"); + string properties = "/p:" + string.Join(",", globalProperties.Select(x => $"{x.Key}={x.Value}")); + + var startInfo = new System.Diagnostics.ProcessStartInfo() + { + FileName = msbuildPath, + Arguments = $"{projectPath} {properties}", + CreateNoWindow = false, + WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden, + }; + + // Tevin: Delete later + //string path = "C:\\src\\libtempslowcheetah\\test\\Microsoft.VisualStudio.SlowCheetah.Tests\\example.txt"; + + //// Open the file for reading + //using (FileStream fs = File.Open(path, FileMode.Open, FileAccess.Write)) + //{ + // // Read from the file + // using (StreamWriter writer = new StreamWriter(fs)) + // { + // writer.WriteLine($"Running msbuild.exe {startInfo.Arguments}"); + // writer.WriteLine($"Running msbuild.exe filename {startInfo.FileName}"); + // } + //} + + try + { + + using (var process = System.Diagnostics.Process.Start(startInfo)) + { + process.WaitForExit(); + Assert.Equal(0, process.ExitCode); + process.Close(); + } + } + catch (Exception ex) + { + throw new Exception($"Error running msbuild: {ex.Message}", ex); + } + } + + /// + /// Gets a app setting from a configuration file. + /// + /// Path to the configuration file. + /// Setting key. + /// Value of the setting. + public string GetAppSettingValue(string configFilePath, string appSettingKey) + { + var configFile = XDocument.Load(configFilePath); + var testSetting = (from settingEl in configFile.Descendants("appSettings").Elements() + where settingEl.Attribute("key").Value == appSettingKey + select settingEl.Attribute("value").Value).Single(); + return testSetting; + } + + /// + /// Gets the value of a node within a configuration file. + /// + /// Path to the configuration file. + /// Name of the node. + /// Value of the node. + public string GetConfigNodeValue(string configFilePath, string nodeName) + { + var configFile = XDocument.Load(configFilePath); + return configFile.Descendants(nodeName).Single().Value; + } + + /// + /// At the end of tests, delete the output path for the tested projects. + /// + public void Dispose() + { + if (Directory.Exists(this.OutputPath)) + { + Directory.Delete(this.OutputPath, recursive: true); + } + } + } +} diff --git a/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/ConsoleAppTests.cs b/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/ConsoleAppTests.cs new file mode 100644 index 00000000..16cd29dc --- /dev/null +++ b/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/ConsoleAppTests.cs @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#pragma warning disable SA1512 // Single-line comments must not be followed by blank line + +// Copyright (C) Sayed Ibrahim Hashimi +#pragma warning restore SA1512 // Single-line comments must not be followed by blank line + +namespace Microsoft.VisualStudio.SlowCheetah.Tests.BuildTests +{ + using System.IO; + using Xunit; + + /// + /// Tests build time transformations for a test console app. + /// + [Collection("BuildTests")] + public class ConsoleAppTests : ConfigTransformTestsBase + { + /// + /// Tests if app.config is transformed on build. + /// + [Fact] + public void ConsoleApp_AppConfig_IsTransformed() + { + var projectName = "ConsoleApp"; + this.BuildProject(projectName); + + var configFilePath = Path.Combine(this.OutputPath, "ConsoleApp.exe.config"); + + var testSetting = this.GetAppSettingValue(configFilePath, "TestSetting"); + + Assert.Equal("Debug", testSetting); + } + + /// + /// Tests if other.config is transformed on build. + /// + [Fact] + public void ConsoleApp_OtherConfig_IsTransformed() + { + var projectName = "ConsoleApp"; + this.BuildProject(projectName); + + var configFilePath = Path.Combine(this.OutputPath, "Other.config"); + + var testNodeValue = this.GetConfigNodeValue(configFilePath, "TestNode"); + + Assert.Equal("Debug", testNodeValue); + } + } +} diff --git a/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/ConsoleApp/App.Debug.config b/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/ConsoleApp/App.Debug.config new file mode 100644 index 00000000..c582ae9a --- /dev/null +++ b/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/ConsoleApp/App.Debug.config @@ -0,0 +1,8 @@ + + + + + + + diff --git a/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/ConsoleApp/App.Release.config b/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/ConsoleApp/App.Release.config new file mode 100644 index 00000000..0d8dd466 --- /dev/null +++ b/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/ConsoleApp/App.Release.config @@ -0,0 +1,8 @@ + + + + + + + diff --git a/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/ConsoleApp/App.config b/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/ConsoleApp/App.config new file mode 100644 index 00000000..e6792411 --- /dev/null +++ b/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/ConsoleApp/App.config @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/ConsoleApp/ConsoleApp.csproj b/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/ConsoleApp/ConsoleApp.csproj new file mode 100644 index 00000000..2c786ed1 --- /dev/null +++ b/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/ConsoleApp/ConsoleApp.csproj @@ -0,0 +1,79 @@ + + + + + Debug + AnyCPU + {1C50F6B9-3E9C-48D1-87C3-783763D11A4C} + Exe + ConsoleApp + ConsoleApp + v4.5.2 + 512 + true + win + 9.0 + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + True + App.config + + + True + App.config + + + true + + + True + Other.config + + + True + Other.config + + + true + + + + + diff --git a/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/ConsoleApp/Other.Debug.config b/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/ConsoleApp/Other.Debug.config new file mode 100644 index 00000000..de6580ee --- /dev/null +++ b/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/ConsoleApp/Other.Debug.config @@ -0,0 +1,6 @@ + + + + Debug + diff --git a/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/ConsoleApp/Other.Release.config b/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/ConsoleApp/Other.Release.config new file mode 100644 index 00000000..b2f90247 --- /dev/null +++ b/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/ConsoleApp/Other.Release.config @@ -0,0 +1,6 @@ + + + + Release + diff --git a/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/ConsoleApp/Other.config b/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/ConsoleApp/Other.config new file mode 100644 index 00000000..28eeeac5 --- /dev/null +++ b/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/ConsoleApp/Other.config @@ -0,0 +1,4 @@ + + + default + diff --git a/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/ConsoleApp/Program.cs b/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/ConsoleApp/Program.cs new file mode 100644 index 00000000..0c76bf6f --- /dev/null +++ b/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/ConsoleApp/Program.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ConsoleApp +{ + class Program + { + static void Main(string[] args) + { + } + } +} diff --git a/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/ConsoleApp/Properties/AssemblyInfo.cs b/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/ConsoleApp/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..f8ba6d40 --- /dev/null +++ b/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/ConsoleApp/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("ConsoleApp")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("ConsoleApp")] +[assembly: AssemblyCopyright("Copyright © 2017")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("1c50f6b9-3e9c-48d1-87c3-783763d11a4c")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/Directory.Build.props b/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/Directory.Build.props new file mode 100644 index 00000000..42309161 --- /dev/null +++ b/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/Directory.Build.props @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/Directory.Build.targets b/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/Directory.Build.targets new file mode 100644 index 00000000..42309161 --- /dev/null +++ b/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/Directory.Build.targets @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/WebApplication/Other.Debug.config b/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/WebApplication/Other.Debug.config new file mode 100644 index 00000000..de6580ee --- /dev/null +++ b/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/WebApplication/Other.Debug.config @@ -0,0 +1,6 @@ + + + + Debug + diff --git a/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/WebApplication/Other.Release.config b/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/WebApplication/Other.Release.config new file mode 100644 index 00000000..b2f90247 --- /dev/null +++ b/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/WebApplication/Other.Release.config @@ -0,0 +1,6 @@ + + + + Release + diff --git a/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/WebApplication/Other.config b/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/WebApplication/Other.config new file mode 100644 index 00000000..28eeeac5 --- /dev/null +++ b/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/WebApplication/Other.config @@ -0,0 +1,4 @@ + + + default + diff --git a/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/WebApplication/Web.Debug.config b/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/WebApplication/Web.Debug.config new file mode 100644 index 00000000..ccf59884 --- /dev/null +++ b/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/WebApplication/Web.Debug.config @@ -0,0 +1,30 @@ + + + + + + + + + + diff --git a/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/WebApplication/Web.Release.config b/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/WebApplication/Web.Release.config new file mode 100644 index 00000000..9b90a9c2 --- /dev/null +++ b/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/WebApplication/Web.Release.config @@ -0,0 +1,31 @@ + + + + + + + + + + + diff --git a/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/WebApplication/Web.config b/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/WebApplication/Web.config new file mode 100644 index 00000000..31de11e4 --- /dev/null +++ b/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/WebApplication/Web.config @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/WebApplication/WebApplication.csproj b/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/WebApplication/WebApplication.csproj new file mode 100644 index 00000000..4ad12211 --- /dev/null +++ b/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/TestProjects/WebApplication/WebApplication.csproj @@ -0,0 +1,131 @@ + + + + Debug + AnyCPU + + + 2.0 + {FF416CD8-E3B3-4223-B8FD-E6B3B6720D71} + {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} + Library + Properties + WebApplication + WebApplication + v4.5.2 + win + true + + + + + + + + True + + + + true + full + false + bin\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + True + Other.config + + + True + Other.config + + + Web.config + + + Web.config + + + + + + + + + + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + + + + + + + True + True + 57677 + / + http://localhost:56732/ + False + False + + + False + + + + + + \ No newline at end of file diff --git a/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/WebAppTests.cs b/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/WebAppTests.cs new file mode 100644 index 00000000..8b0605ec --- /dev/null +++ b/test/Microsoft.VisualStudio.SlowCheetah.Tests/BuildTests/WebAppTests.cs @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#pragma warning disable SA1512 // Single-line comments must not be followed by blank line + +// Copyright (C) Sayed Ibrahim Hashimi +#pragma warning restore SA1512 // Single-line comments must not be followed by blank line + +namespace Microsoft.VisualStudio.SlowCheetah.Tests.BuildTests +{ + using System.IO; + using Xunit; + + /// + /// Tests build time transformations for a test web app. + /// + [Collection("BuildTests")] + public class WebAppTests : ConfigTransformTestsBase + { + /// + /// Tests if other.config is transformed on build. + /// + [Fact] + public void WebApp_OtherConfig_IsTransformed() + { + var projectName = "WebApplication"; + this.BuildProject(projectName); + + var configFilePath = Path.Combine(this.OutputPath, "Other.config"); + + var testNodeValue = this.GetConfigNodeValue(configFilePath, "TestNode"); + + Assert.Equal("Debug", testNodeValue); + } + } +} diff --git a/test/Microsoft.VisualStudio.SlowCheetah.Tests/Microsoft.VisualStudio.SlowCheetah.Tests.csproj b/test/Microsoft.VisualStudio.SlowCheetah.Tests/Microsoft.VisualStudio.SlowCheetah.Tests.csproj new file mode 100644 index 00000000..de73178b --- /dev/null +++ b/test/Microsoft.VisualStudio.SlowCheetah.Tests/Microsoft.VisualStudio.SlowCheetah.Tests.csproj @@ -0,0 +1,54 @@ + + + + net472 + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_CopyTools Include="@(_DependentAssemblies);%(_DependentAssemblies.RelativeDir)Microsoft.Web.XmlTransform.dll" /> + <_CopyTools Include="@(_DependentAssemblies);%(_DependentAssemblies.RelativeDir)Microsoft.VisualStudio.Jdt.dll" /> + <_CopyBuild Include="%(_DependentAssemblies.RelativeDir)Build\Microsoft.VisualStudio.SlowCheetah*.targets" /> + + + + + + + + + <_MSBuildPathLines Include="$(MSBuildToolsPath)" /> + + + + + diff --git a/test/Microsoft.VisualStudio.SlowCheetah.Tests/TestUtilities.cs b/test/Microsoft.VisualStudio.SlowCheetah.Tests/TestUtilities.cs new file mode 100644 index 00000000..027235cf --- /dev/null +++ b/test/Microsoft.VisualStudio.SlowCheetah.Tests/TestUtilities.cs @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#pragma warning disable SA1512 // Single-line comments must not be followed by blank line + +// Copyright (C) Sayed Ibrahim Hashimi +#pragma warning restore SA1512 // Single-line comments must not be followed by blank line + +namespace Microsoft.VisualStudio.SlowCheetah.Tests +{ + /// + /// Utilities class for SlowCheetah tests. + /// + public static class TestUtilities + { + /// + /// Example source file for transform testing. + /// + public const string Source01 = + @" + + + + + + "; + + /// + /// Example transform file for transform testing. + /// + public const string Transform01 = + @" + + + + + + "; + + /// + /// Example result file for transform testing. + /// + public const string Result01 = + @" + + + + + + "; + } +} diff --git a/test/Microsoft.VisualStudio.SlowCheetah.Tests/TransformTest.cs b/test/Microsoft.VisualStudio.SlowCheetah.Tests/TransformTest.cs new file mode 100644 index 00000000..7c07bbdd --- /dev/null +++ b/test/Microsoft.VisualStudio.SlowCheetah.Tests/TransformTest.cs @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#pragma warning disable SA1512 // Single-line comments must not be followed by blank line + +// Copyright (C) Sayed Ibrahim Hashimi +#pragma warning restore SA1512 // Single-line comments must not be followed by blank line + +namespace Microsoft.VisualStudio.SlowCheetah.Tests +{ + using System.IO; + using Xunit; + + /// + /// Tests for . + /// + public class TransformTest : BaseTest + { + /// + /// Tests for . + /// + [Fact] + public void TestXmlTransform() + { + string sourceFile = this.WriteTextToTempFile(TestUtilities.Source01); + string transformFile = this.WriteTextToTempFile(TestUtilities.Transform01); + string expectedResultFile = this.WriteTextToTempFile(TestUtilities.Result01); + + string destFile = this.GetTempFilename(true); + ITransformer transformer = new XmlTransformer(); + transformer.Transform(sourceFile, transformFile, destFile); + + Assert.True(File.Exists(sourceFile)); + Assert.True(File.Exists(transformFile)); + Assert.True(File.Exists(destFile)); + + string actualResult = File.ReadAllText(destFile); + string expectedResult = File.ReadAllText(expectedResultFile); + Assert.Equal(expectedResult.Trim(), actualResult.Trim()); + } + } +} diff --git a/test/Microsoft.VisualStudio.SlowCheetah.Tests/example.txt b/test/Microsoft.VisualStudio.SlowCheetah.Tests/example.txt new file mode 100644 index 00000000..9e188f46 --- /dev/null +++ b/test/Microsoft.VisualStudio.SlowCheetah.Tests/example.txt @@ -0,0 +1,2 @@ +Running msbuild.exe C:\src\libtempslowcheetah\bin\Microsoft.VisualStudio.SlowCheetah.Tests\Debug\net472\..\..\..\..\test\Microsoft.VisualStudio.SlowCheetah.Tests\BuildTests\TestProjects\WebApplication\WebApplication.csproj /p:Configuration=Debug,OutputPath=C:\src\libtempslowcheetah\bin\Microsoft.VisualStudio.SlowCheetah.Tests\Debug\net472\ProjectOutput +Running msbuild.exe filename C:\Program Files\Microsoft Visual Studio\2022\IntPreview\MSBuild\Current\Bin\amd64\msbuild.exe diff --git a/test/Microsoft.VisualStudio.SlowCheetah.VS.Tests/App.config b/test/Microsoft.VisualStudio.SlowCheetah.VS.Tests/App.config new file mode 100644 index 00000000..35252b96 --- /dev/null +++ b/test/Microsoft.VisualStudio.SlowCheetah.VS.Tests/App.config @@ -0,0 +1,6 @@ + + + + + + diff --git a/test/Microsoft.VisualStudio.SlowCheetah.VS.Tests/Microsoft.VisualStudio.SlowCheetah.VS.Tests.csproj b/test/Microsoft.VisualStudio.SlowCheetah.VS.Tests/Microsoft.VisualStudio.SlowCheetah.VS.Tests.csproj new file mode 100644 index 00000000..8c92893b --- /dev/null +++ b/test/Microsoft.VisualStudio.SlowCheetah.VS.Tests/Microsoft.VisualStudio.SlowCheetah.VS.Tests.csproj @@ -0,0 +1,25 @@ + + + + net472 + false + + + + + + + + + + + + + + + + + + + + diff --git a/test/Microsoft.VisualStudio.SlowCheetah.VS.Tests/PackageUtilitiesTest.cs b/test/Microsoft.VisualStudio.SlowCheetah.VS.Tests/PackageUtilitiesTest.cs new file mode 100644 index 00000000..583ef49b --- /dev/null +++ b/test/Microsoft.VisualStudio.SlowCheetah.VS.Tests/PackageUtilitiesTest.cs @@ -0,0 +1,130 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.SlowCheetah.VS.Tests +{ + using System.Collections.Generic; + using Xunit; + + /// + /// Test class for . + /// + public class PackageUtilitiesTest + { + private IEnumerable baseTestProjectConfigs = new List(new string[] { "Debug", "Release" }); + private IEnumerable testProjectConfigsWithDots = new List(new string[] { "Debug", "Debug.Test", "Release", "Test.Release", "Test.Rel" }); + + /// + /// Tests returns on arguments that are null or empty strings. + /// + /// Document name. + /// Transform file name. + [Theory] + [InlineData(null, null)] + [InlineData("", "")] + [InlineData("App.config", null)] + [InlineData("App.config", "")] + [InlineData(null, "App.Debug.config")] + [InlineData("", "App.Debug.config")] + public void IsFileTransformWithNullArguments(string docName, string trnName) + { + Assert.False(PackageUtilities.IsFileTransformForBuildConfiguration(docName, trnName, this.baseTestProjectConfigs)); + } + + /// + /// Tests with valid arguments normally found in projects. + /// + /// Document name. + /// Transform file name. + [Theory] + [InlineData("App.config", "App.Debug.config")] + [InlineData("App.config", "app.release.config")] + [InlineData("APP.config", "App.Debug.config")] + [InlineData("App.Test.config", "App.Test.Debug.config")] + public void IsFileTransformWithValidArguments(string docName, string trnName) + { + Assert.True(PackageUtilities.IsFileTransformForBuildConfiguration(docName, trnName, this.baseTestProjectConfigs)); + } + + /// + /// Tests with invalid arguments. + /// + /// Document name. + /// Transform file name. + [Theory] + [InlineData("App.config", "App.Test.Debug.config")] + [InlineData("App.Debug.config", "App.Debug.config")] + [InlineData("App.Debug.config", "App.Release.config")] + public void IsFileTransformWithInvalidArguments(string docName, string trnName) + { + Assert.False(PackageUtilities.IsFileTransformForBuildConfiguration(docName, trnName, this.baseTestProjectConfigs)); + } + + /// + /// Tests with project configurations containing dots + /// and file names with similar structures. Tests valid names. + /// + /// Document name. + /// Transform file name. + [Theory] + [InlineData("App.config", "App.Debug.Test.config")] + [InlineData("App.System.config", "App.System.Debug.Test.config")] + [InlineData("App.config", "App.Test.Release.config")] + [InlineData("App.Test.config", "App.Test.Release.config")] + [InlineData("App.Test.config", "App.Test.Test.Release.config")] + [InlineData("App.config", "App.Test.Rel.config")] + public void IsFileTransformWithDottedConfigsAndValidNames(string docName, string trnName) + { + Assert.True(PackageUtilities.IsFileTransformForBuildConfiguration(docName, trnName, this.testProjectConfigsWithDots)); + } + + /// + /// Tests with project configurations containing dots + /// and file names with similar structures. Tests invalid names. + /// + /// Document name. + /// Transform file name. + [Theory] + [InlineData("App.config", "App.Release.Test.config")] + [InlineData("App.config", "App.Rel.Test.config")] + [InlineData("App.Test.config", "App.Test.Rel.config")] + [InlineData("App.Test.config", "App.Test.Test.config")] + [InlineData("App.Test.config", "App.Debug.Test.config")] + [InlineData("App.config", "Test.Rel.config")] + [InlineData("App.Test.Rel.config", "App.Test.Rel.config")] + public void IsFileTransformWithDottedConfigsAndInvalidNames(string docName, string trnName) + { + Assert.False(PackageUtilities.IsFileTransformForBuildConfiguration(docName, trnName, this.testProjectConfigsWithDots)); + } + + /// + /// Tests with invalid arguments. + /// + /// Document name. + /// Transform file name. + [Theory] + [InlineData("App.config", "App.config")] + [InlineData("App.Debug.config", "App.Debug.config")] + [InlineData("App.config", "App..config")] + [InlineData("App.Debug.config", "App.config")] + [InlineData("App.config", "App.config.Debug")] + public void IsFileGenericTransformWithInvalidArguments(string docName, string trnName) + { + Assert.False(PackageUtilities.IsGenericFileTransform(docName, trnName)); + } + + /// + /// Tests with valid arguments. + /// + /// Document name. + /// Transform file name. + [Theory] + [InlineData("App.config", "App.Debug.config")] + [InlineData("App.config", "App.Test.Debug.config")] + [InlineData("App.Test.config", "App.Test.Debug.config")] + public void IsFileGenericTransformWithValidArguments(string docName, string trnName) + { + Assert.True(PackageUtilities.IsGenericFileTransform(docName, trnName)); + } + } +} diff --git a/test/dirs.proj b/test/dirs.proj new file mode 100644 index 00000000..2a5b1f05 --- /dev/null +++ b/test/dirs.proj @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file 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..496049a2 --- /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 -InstallNet6 + +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