Browse Source

[master] Update dependencies from 3 repositories (#11458)

* Update dependencies from https://github.com/aspnet/AspNetCore-Tooling build 20190621.2
- Microsoft.NET.Sdk.Razor - 3.0.0-preview7.19321.2
- Microsoft.CodeAnalysis.Razor - 3.0.0-preview7.19321.2
- Microsoft.AspNetCore.Razor.Language - 3.0.0-preview7.19321.2
- Microsoft.AspNetCore.Mvc.Razor.Extensions - 3.0.0-preview7.19321.2

* Update dependencies from https://github.com/aspnet/Blazor build 20190623.1
- Microsoft.AspNetCore.Blazor.Mono - 0.10.0-preview7.19323.1

* Update dependencies from https://github.com/dotnet/arcade build 20190623.4
- Microsoft.DotNet.Arcade.Sdk - 1.0.0-beta.19323.4
- Microsoft.DotNet.GenAPI - 1.0.0-beta.19323.4
- Microsoft.DotNet.Helix.Sdk - 2.0.0-beta.19323.4

* fix build (tests) - thanks @ajaybhargavb❕
dotnet-maestro[bot] 6 years ago
parent
commit
114f760d64

+ 16 - 16
eng/Version.Details.xml

@@ -9,25 +9,25 @@
 -->
 <Dependencies>
   <ProductDependencies>
-    <Dependency Name="Microsoft.AspNetCore.Blazor.Mono" Version="0.10.0-preview7.19317.1">
+    <Dependency Name="Microsoft.AspNetCore.Blazor.Mono" Version="0.10.0-preview7.19323.1">
       <Uri>https://github.com/aspnet/Blazor</Uri>
-      <Sha>03f3e4a1a88037d9bbdd21334706ff147d616148</Sha>
+      <Sha>8df334032840bda9221fe281890e88fb1de06845</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.AspNetCore.Razor.Language" Version="3.0.0-preview7.19320.3">
+    <Dependency Name="Microsoft.AspNetCore.Razor.Language" Version="3.0.0-preview7.19321.2">
       <Uri>https://github.com/aspnet/AspNetCore-Tooling</Uri>
-      <Sha>a399bd072e04f394de843ac100bd16fdd926a993</Sha>
+      <Sha>5e7c4e5e382a7e486d9416b711fcd65bae25808b</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.AspNetCore.Mvc.Razor.Extensions" Version="3.0.0-preview7.19320.3">
+    <Dependency Name="Microsoft.AspNetCore.Mvc.Razor.Extensions" Version="3.0.0-preview7.19321.2">
       <Uri>https://github.com/aspnet/AspNetCore-Tooling</Uri>
-      <Sha>a399bd072e04f394de843ac100bd16fdd926a993</Sha>
+      <Sha>5e7c4e5e382a7e486d9416b711fcd65bae25808b</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.CodeAnalysis.Razor" Version="3.0.0-preview7.19320.3">
+    <Dependency Name="Microsoft.CodeAnalysis.Razor" Version="3.0.0-preview7.19321.2">
       <Uri>https://github.com/aspnet/AspNetCore-Tooling</Uri>
-      <Sha>a399bd072e04f394de843ac100bd16fdd926a993</Sha>
+      <Sha>5e7c4e5e382a7e486d9416b711fcd65bae25808b</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.NET.Sdk.Razor" Version="3.0.0-preview7.19320.3">
+    <Dependency Name="Microsoft.NET.Sdk.Razor" Version="3.0.0-preview7.19321.2">
       <Uri>https://github.com/aspnet/AspNetCore-Tooling</Uri>
-      <Sha>a399bd072e04f394de843ac100bd16fdd926a993</Sha>
+      <Sha>5e7c4e5e382a7e486d9416b711fcd65bae25808b</Sha>
     </Dependency>
     <Dependency Name="dotnet-ef" Version="3.0.0-preview7.19313.2">
       <Uri>https://github.com/aspnet/EntityFrameworkCore</Uri>
@@ -404,17 +404,17 @@
       <Uri>https://github.com/aspnet/Extensions</Uri>
       <Sha>42b3a303ba27594637de04c78aaf9f1c7af6e303</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.DotNet.GenAPI" Version="1.0.0-beta.19312.4">
+    <Dependency Name="Microsoft.DotNet.GenAPI" Version="1.0.0-beta.19323.4">
       <Uri>https://github.com/dotnet/arcade</Uri>
-      <Sha>8b34615157193918f13821730cb2cac05c2eb1b1</Sha>
+      <Sha>9946534da4f73e6242ca105f6798ab58119c9ab0</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.DotNet.Arcade.Sdk" Version="1.0.0-beta.19312.4">
+    <Dependency Name="Microsoft.DotNet.Arcade.Sdk" Version="1.0.0-beta.19323.4">
       <Uri>https://github.com/dotnet/arcade</Uri>
-      <Sha>8b34615157193918f13821730cb2cac05c2eb1b1</Sha>
+      <Sha>9946534da4f73e6242ca105f6798ab58119c9ab0</Sha>
     </Dependency>
-    <Dependency Name="Microsoft.DotNet.Helix.Sdk" Version="2.0.0-beta.19312.4">
+    <Dependency Name="Microsoft.DotNet.Helix.Sdk" Version="2.0.0-beta.19323.4">
       <Uri>https://github.com/dotnet/arcade</Uri>
-      <Sha>8b34615157193918f13821730cb2cac05c2eb1b1</Sha>
+      <Sha>9946534da4f73e6242ca105f6798ab58119c9ab0</Sha>
     </Dependency>
     <Dependency Name="Microsoft.AspNetCore.Testing" Version="3.0.0-preview7.19312.4" CoherentParentDependency="Microsoft.EntityFrameworkCore">
       <Uri>https://github.com/aspnet/Extensions</Uri>

+ 6 - 6
eng/Versions.props

@@ -52,7 +52,7 @@
   -->
   <PropertyGroup Label="Automated">
     <!-- Packages from dotnet/arcade -->
-    <MicrosoftDotNetGenAPIPackageVersion>1.0.0-beta.19312.4</MicrosoftDotNetGenAPIPackageVersion>
+    <MicrosoftDotNetGenAPIPackageVersion>1.0.0-beta.19323.4</MicrosoftDotNetGenAPIPackageVersion>
     <!-- Packages from dotnet/core-setup -->
     <MicrosoftExtensionsDependencyModelPackageVersion>3.0.0-preview7-27812-08</MicrosoftExtensionsDependencyModelPackageVersion>
     <MicrosoftNETCoreAppRefPackageVersion>3.0.0-preview7-27812-08</MicrosoftNETCoreAppRefPackageVersion>
@@ -82,7 +82,7 @@
     <!-- Only listed explicitly to workaround https://github.com/dotnet/cli/issues/10528 -->
     <MicrosoftNETCorePlatformsPackageVersion>3.0.0-preview7.19312.3</MicrosoftNETCorePlatformsPackageVersion>
     <!-- Packages from aspnet/Blazor -->
-    <MicrosoftAspNetCoreBlazorMonoPackageVersion>0.10.0-preview7.19317.1</MicrosoftAspNetCoreBlazorMonoPackageVersion>
+    <MicrosoftAspNetCoreBlazorMonoPackageVersion>0.10.0-preview7.19323.1</MicrosoftAspNetCoreBlazorMonoPackageVersion>
     <!-- Packages from aspnet/Extensions -->
     <InternalAspNetCoreAnalyzersPackageVersion>3.0.0-preview7.19312.4</InternalAspNetCoreAnalyzersPackageVersion>
     <MicrosoftAspNetCoreAnalyzerTestingPackageVersion>3.0.0-preview7.19312.4</MicrosoftAspNetCoreAnalyzerTestingPackageVersion>
@@ -154,10 +154,10 @@
     <MicrosoftEntityFrameworkCoreToolsPackageVersion>3.0.0-preview7.19313.2</MicrosoftEntityFrameworkCoreToolsPackageVersion>
     <MicrosoftEntityFrameworkCorePackageVersion>3.0.0-preview7.19313.2</MicrosoftEntityFrameworkCorePackageVersion>
     <!-- Packages from aspnet/AspNetCore-Tooling -->
-    <MicrosoftAspNetCoreMvcRazorExtensionsPackageVersion>3.0.0-preview7.19320.3</MicrosoftAspNetCoreMvcRazorExtensionsPackageVersion>
-    <MicrosoftAspNetCoreRazorLanguagePackageVersion>3.0.0-preview7.19320.3</MicrosoftAspNetCoreRazorLanguagePackageVersion>
-    <MicrosoftCodeAnalysisRazorPackageVersion>3.0.0-preview7.19320.3</MicrosoftCodeAnalysisRazorPackageVersion>
-    <MicrosoftNETSdkRazorPackageVersion>3.0.0-preview7.19320.3</MicrosoftNETSdkRazorPackageVersion>
+    <MicrosoftAspNetCoreMvcRazorExtensionsPackageVersion>3.0.0-preview7.19321.2</MicrosoftAspNetCoreMvcRazorExtensionsPackageVersion>
+    <MicrosoftAspNetCoreRazorLanguagePackageVersion>3.0.0-preview7.19321.2</MicrosoftAspNetCoreRazorLanguagePackageVersion>
+    <MicrosoftCodeAnalysisRazorPackageVersion>3.0.0-preview7.19321.2</MicrosoftCodeAnalysisRazorPackageVersion>
+    <MicrosoftNETSdkRazorPackageVersion>3.0.0-preview7.19321.2</MicrosoftNETSdkRazorPackageVersion>
   </PropertyGroup>
   <!--
 

+ 11 - 0
eng/common/PSScriptAnalyzerSettings.psd1

@@ -0,0 +1,11 @@
+@{
+    IncludeRules=@('PSAvoidUsingCmdletAliases',
+                   'PSAvoidUsingWMICmdlet',
+                   'PSAvoidUsingPositionalParameters',
+                   'PSAvoidUsingInvokeExpression',
+                   'PSUseDeclaredVarsMoreThanAssignments',
+                   'PSUseCmdletCorrectly',
+                   'PSStandardDSCFunctionsInResource',
+                   'PSUseIdenticalMandatoryParametersForDSC',
+                   'PSUseIdenticalParametersForDSC')
+}

+ 1 - 1
eng/common/build.ps1

@@ -134,7 +134,7 @@ try {
 }
 catch {
   Write-Host $_.ScriptStackTrace
-  Write-PipelineTaskError -Message $_
+  Write-PipelineTelemetryError -Category "InitializeToolset" -Message $_
   ExitWithExitCode 1
 }
 

+ 1 - 0
eng/common/cross/build-rootfs.sh

@@ -203,6 +203,7 @@ if [[ "$__LinuxCodeName" == "alpine" ]]; then
       -X http://dl-cdn.alpinelinux.org/alpine/v$__AlpineVersion/main \
       -X http://dl-cdn.alpinelinux.org/alpine/v$__AlpineVersion/community \
       -X http://dl-cdn.alpinelinux.org/alpine/edge/testing \
+      -X http://dl-cdn.alpinelinux.org/alpine/edge/main \
       -U --allow-untrusted --root $__RootfsDir --arch $__AlpineArch --initdb \
       add $__AlpinePackages
     rm -r $__ApkToolsDir

+ 135 - 0
eng/common/internal-feed-operations.ps1

@@ -0,0 +1,135 @@
+param(
+  [Parameter(Mandatory=$true)][string] $Operation,
+  [string] $AuthToken,
+  [string] $CommitSha,
+  [string] $RepoName,
+  [switch] $IsFeedPrivate
+)
+
+$ErrorActionPreference = "Stop"
+Set-StrictMode -Version 2.0
+
+. $PSScriptRoot\tools.ps1
+
+# Sets VSS_NUGET_EXTERNAL_FEED_ENDPOINTS based on the "darc-int-*" feeds defined in NuGet.config. This is needed
+# in build agents by CredProvider to authenticate the restore requests to internal feeds as specified in
+# https://github.com/microsoft/artifacts-credprovider/blob/0f53327cd12fd893d8627d7b08a2171bf5852a41/README.md#environment-variables. This should ONLY be called from identified
+# internal builds
+function SetupCredProvider {
+  param(
+    [string] $AuthToken
+  )    
+
+  # Install the Cred Provider NuGet plugin
+  Write-Host "Setting up Cred Provider NuGet plugin in the agent..."
+  Write-Host "Getting 'installcredprovider.ps1' from 'https://github.com/microsoft/artifacts-credprovider'..."
+
+  $url = 'https://raw.githubusercontent.com/microsoft/artifacts-credprovider/master/helpers/installcredprovider.ps1'
+  
+  Write-Host "Writing the contents of 'installcredprovider.ps1' locally..."
+  Invoke-WebRequest $url -OutFile installcredprovider.ps1
+  
+  Write-Host "Installing plugin..."
+  .\installcredprovider.ps1 -Force
+  
+  Write-Host "Deleting local copy of 'installcredprovider.ps1'..."
+  Remove-Item .\installcredprovider.ps1
+
+  if (-Not("$env:USERPROFILE\.nuget\plugins\netcore")) {
+    Write-Host "CredProvider plugin was not installed correctly!"
+    ExitWithExitCode 1  
+  } 
+  else {
+    Write-Host "CredProvider plugin was installed correctly!"
+  }
+
+  # Then, we set the 'VSS_NUGET_EXTERNAL_FEED_ENDPOINTS' environment variable to restore from the stable 
+  # feeds successfully
+
+  $nugetConfigPath = "$RepoRoot\NuGet.config"
+
+  if (-Not (Test-Path -Path $nugetConfigPath)) {
+    Write-Host "NuGet.config file not found in repo's root!"
+    ExitWithExitCode 1  
+  }
+  
+  $endpoints = New-Object System.Collections.ArrayList
+  $nugetConfigPackageSources = Select-Xml -Path $nugetConfigPath -XPath "//packageSources/add[contains(@key, 'darc-int-')]/@value" | foreach{$_.Node.Value}
+  
+  if (($nugetConfigPackageSources | Measure-Object).Count -gt 0 ) {
+    foreach ($stableRestoreResource in $nugetConfigPackageSources) {
+      $trimmedResource = ([string]$stableRestoreResource).Trim()
+      [void]$endpoints.Add(@{endpoint="$trimmedResource"; password="$AuthToken"}) 
+    }
+  }
+
+  if (($endpoints | Measure-Object).Count -gt 0) {
+      # Create the JSON object. It should look like '{"endpointCredentials": [{"endpoint":"http://example.index.json", "username":"optional", "password":"accesstoken"}]}'
+      $endpointCredentials = @{endpointCredentials=$endpoints} | ConvertTo-Json -Compress
+
+     # Create the environment variables the AzDo way
+      Write-LoggingCommand -Area 'task' -Event 'setvariable' -Data $endpointCredentials -Properties @{
+        'variable' = 'VSS_NUGET_EXTERNAL_FEED_ENDPOINTS'
+        'issecret' = 'false'
+      } 
+
+      # We don't want sessions cached since we will be updating the endpoints quite frequently
+      Write-LoggingCommand -Area 'task' -Event 'setvariable' -Data 'False' -Properties @{
+        'variable' = 'NUGET_CREDENTIALPROVIDER_SESSIONTOKENCACHE_ENABLED'
+        'issecret' = 'false'
+      } 
+  }
+  else
+  {
+    Write-Host "No internal endpoints found in NuGet.config"
+  }
+}
+
+#Workaround for https://github.com/microsoft/msbuild/issues/4430
+function InstallDotNetSdkAndRestoreArcade {
+  $dotnetTempDir = "$RepoRoot\dotnet"
+  $dotnetSdkVersion="2.1.507" # After experimentation we know this version works when restoring the SDK (compared to 3.0.*)
+  $dotnet = "$dotnetTempDir\dotnet.exe"
+  $restoreProjPath = "$PSScriptRoot\restore.proj"
+  
+  Write-Host "Installing dotnet SDK version $dotnetSdkVersion to restore Arcade SDK..."
+  InstallDotNetSdk "$dotnetTempDir" "$dotnetSdkVersion"
+  
+  '<Project Sdk="Microsoft.DotNet.Arcade.Sdk"/>' | Out-File "$restoreProjPath"
+
+  & $dotnet restore $restoreProjPath
+
+  Write-Host "Arcade SDK restored!"
+
+  if (Test-Path -Path $restoreProjPath) {
+    Remove-Item $restoreProjPath
+  }
+
+  if (Test-Path -Path $dotnetTempDir) {
+    Remove-Item $dotnetTempDir -Recurse
+  }
+}
+
+try {
+  Push-Location $PSScriptRoot
+
+  if ($Operation -like "setup") {
+    SetupCredProvider $AuthToken
+  } 
+  elseif ($Operation -like "install-restore") {
+    InstallDotNetSdkAndRestoreArcade
+  }
+  else {
+    Write-Host "Unknown operation '$Operation'!"
+    ExitWithExitCode 1  
+  }
+} 
+catch {
+  Write-Host $_
+  Write-Host $_.Exception
+  Write-Host $_.ScriptStackTrace
+  ExitWithExitCode 1
+} 
+finally {
+    Pop-Location
+}

+ 142 - 0
eng/common/internal-feed-operations.sh

@@ -0,0 +1,142 @@
+#!/usr/bin/env bash
+
+set -e
+
+# Sets VSS_NUGET_EXTERNAL_FEED_ENDPOINTS based on the "darc-int-*" feeds defined in NuGet.config. This is needed
+# in build agents by CredProvider to authenticate the restore requests to internal feeds as specified in
+# https://github.com/microsoft/artifacts-credprovider/blob/0f53327cd12fd893d8627d7b08a2171bf5852a41/README.md#environment-variables. 
+# This should ONLY be called from identified internal builds
+function SetupCredProvider {
+  local authToken=$1
+  
+  # Install the Cred Provider NuGet plugin
+  echo "Setting up Cred Provider NuGet plugin in the agent..."...
+  echo "Getting 'installcredprovider.ps1' from 'https://github.com/microsoft/artifacts-credprovider'..."
+
+  local url="https://raw.githubusercontent.com/microsoft/artifacts-credprovider/master/helpers/installcredprovider.sh"  
+  
+  echo "Writing the contents of 'installcredprovider.ps1' locally..."
+  local installcredproviderPath="installcredprovider.sh"
+  if command -v curl > /dev/null; then
+    curl $url > "$installcredproviderPath"
+  else   
+    wget -q -O "$installcredproviderPath" "$url"
+  fi
+  
+  echo "Installing plugin..."
+  . "$installcredproviderPath"
+  
+  echo "Deleting local copy of 'installcredprovider.sh'..."
+  rm installcredprovider.sh
+
+  if [ ! -d "$HOME/.nuget/plugins" ]; then
+    echo "CredProvider plugin was not installed correctly!"
+    ExitWithExitCode 1  
+  else 
+    echo "CredProvider plugin was installed correctly!"
+  fi
+
+  # Then, we set the 'VSS_NUGET_EXTERNAL_FEED_ENDPOINTS' environment variable to restore from the stable 
+  # feeds successfully
+
+  local nugetConfigPath="$repo_root/NuGet.config"
+
+  if [ ! "$nugetConfigPath" ]; then
+    echo "NuGet.config file not found in repo's root!"
+    ExitWithExitCode 1  
+  fi
+  
+  local endpoints='['
+  local nugetConfigPackageValues=`cat "$nugetConfigPath" | grep "key=\"darc-int-"`
+  local pattern="value=\"(.*)\""
+
+  for value in $nugetConfigPackageValues 
+  do
+    if [[ $value =~ $pattern ]]; then
+      local endpoint="${BASH_REMATCH[1]}"  
+      endpoints+="{\"endpoint\": \"$endpoint\", \"password\": \"$authToken\"},"
+    fi
+  done
+  
+  endpoints=${endpoints%?}
+  endpoints+=']'
+
+  if [ ${#endpoints} -gt 2 ]; then 
+      # Create the JSON object. It should look like '{"endpointCredentials": [{"endpoint":"http://example.index.json", "username":"optional", "password":"accesstoken"}]}'
+      local endpointCredentials="{\"endpointCredentials\": "$endpoints"}"
+
+      echo "##vso[task.setvariable variable=VSS_NUGET_EXTERNAL_FEED_ENDPOINTS]$endpointCredentials"
+      echo "##vso[task.setvariable variable=NUGET_CREDENTIALPROVIDER_SESSIONTOKENCACHE_ENABLED]False"
+  else
+    echo "No internal endpoints found in NuGet.config"
+  fi
+} 
+
+# Workaround for https://github.com/microsoft/msbuild/issues/4430
+function InstallDotNetSdkAndRestoreArcade {
+  local dotnetTempDir="$repo_root/dotnet"
+  local dotnetSdkVersion="2.1.507" # After experimentation we know this version works when restoring the SDK (compared to 3.0.*)
+  local restoreProjPath="$repo_root/eng/common/restore.proj"
+  
+  echo "Installing dotnet SDK version $dotnetSdkVersion to restore Arcade SDK..."
+  echo "<Project Sdk=\"Microsoft.DotNet.Arcade.Sdk\"/>" > "$restoreProjPath"
+  
+  InstallDotNetSdk "$dotnetTempDir" "$dotnetSdkVersion"
+
+  local res=`$dotnetTempDir/dotnet restore $restoreProjPath`
+  echo "Arcade SDK restored!"
+
+  # Cleanup
+  if [ "$restoreProjPath" ]; then
+    rm "$restoreProjPath"
+  fi
+
+  if [ "$dotnetTempDir" ]; then
+    rm -r $dotnetTempDir
+  fi
+}
+
+source="${BASH_SOURCE[0]}"
+operation=''
+authToken=''
+repoName=''
+
+while [[ $# > 0 ]]; do
+  opt="$(echo "$1" | awk '{print tolower($0)}')"
+  case "$opt" in
+    --operation)
+      operation=$2
+      shift
+      ;;
+    --authtoken)
+      authToken=$2
+      shift
+      ;;
+    *)
+      echo "Invalid argument: $1"
+      usage
+      exit 1
+      ;;
+  esac
+
+  shift
+done
+
+while [[ -h "$source" ]]; do
+  scriptroot="$( cd -P "$( dirname "$source" )" && pwd )"
+  source="$(readlink "$source")"
+  # if $source was a relative symlink, we need to resolve it relative to the path where the
+  # symlink file was located
+  [[ $source != /* ]] && source="$scriptroot/$source"
+done
+scriptroot="$( cd -P "$( dirname "$source" )" && pwd )"
+
+. "$scriptroot/tools.sh"
+
+if [ "$operation" = "setup" ]; then
+  SetupCredProvider $authToken
+elif [ "$operation" = "install-restore" ]; then
+  InstallDotNetSdkAndRestoreArcade
+else
+  echo "Unknown operation '$operation'!"
+fi

+ 88 - 1
eng/common/LoggingCommandFunctions.ps1 → eng/common/pipeline-logging-functions.ps1

@@ -1,4 +1,4 @@
-# Source for this file was taken from https://github.com/microsoft/azure-pipelines-task-lib/blob/11c9439d4af17e6475d9fe058e6b2e03914d17e6/powershell/VstsTaskSdk/LoggingCommandFunctions.ps1
+# Source for this file was taken from https://github.com/microsoft/azure-pipelines-task-lib/blob/11c9439d4af17e6475d9fe058e6b2e03914d17e6/powershell/VstsTaskSdk/LoggingCommandFunctions.ps1 and modified.
 
 # NOTE: You should not be calling these method directly as they are likely to change.  Instead you should be calling the Write-Pipeline* functions defined in tools.ps1
 
@@ -12,6 +12,93 @@ $script:loggingCommandEscapeMappings = @( # TODO: WHAT ABOUT "="? WHAT ABOUT "%"
 # TODO: BUG: Escape % ???
 # TODO: Add test to verify don't need to escape "=".
 
+function Write-PipelineTelemetryError {
+    [CmdletBinding()]
+    param(
+        [Parameter(Mandatory = $true)]
+        [string]$Category,
+        [Parameter(Mandatory = $true)]
+        [string]$Message,
+        [Parameter(Mandatory = $false)]
+        [string]$Type = 'error',
+        [string]$ErrCode,
+        [string]$SourcePath,
+        [string]$LineNumber,
+        [string]$ColumnNumber,
+        [switch]$AsOutput)
+
+        $PSBoundParameters.Remove("Category") | Out-Null
+
+        $Message = "(NETCORE_ENGINEERING_TELEMETRY=$Category) $Message"
+        $PSBoundParameters.Remove("Message") | Out-Null
+        $PSBoundParameters.Add("Message", $Message)
+
+        Write-PipelineTaskError @PSBoundParameters
+}
+
+function Write-PipelineTaskError {
+    [CmdletBinding()]
+    param(
+      [Parameter(Mandatory = $true)]
+      [string]$Message,
+      [Parameter(Mandatory = $false)]
+      [string]$Type = 'error',
+      [string]$ErrCode,
+      [string]$SourcePath,
+      [string]$LineNumber,
+      [string]$ColumnNumber,
+      [switch]$AsOutput)
+  
+      if(!$ci) {
+        if($Type -eq 'error') {
+          Write-Host $Message -ForegroundColor Red
+          return
+        }
+        elseif ($Type -eq 'warning') {
+          Write-Host $Message -ForegroundColor Yellow
+          return
+        }
+      }
+  
+      if(($Type -ne 'error') -and ($Type -ne 'warning')) {
+        Write-Host $Message
+        return
+      }
+      if(-not $PSBoundParameters.ContainsKey('Type')) {
+        $PSBoundParameters.Add('Type', 'error')
+      }
+      Write-LogIssue @PSBoundParameters
+  }
+  
+  function Write-PipelineSetVariable {
+    [CmdletBinding()]
+    param(
+      [Parameter(Mandatory = $true)]
+      [string]$Name,
+      [string]$Value,
+      [switch]$Secret,
+      [switch]$AsOutput)
+  
+      if($ci) {
+        Write-LoggingCommand -Area 'task' -Event 'setvariable' -Data $Value -Properties @{
+          'variable' = $Name
+          'isSecret' = $Secret
+          'isOutput' = 'true'
+        } -AsOutput:$AsOutput
+      }
+  }
+  
+  function Write-PipelinePrependPath {
+    [CmdletBinding()]
+    param(
+      [Parameter(Mandatory=$true)]
+      [string]$Path,
+      [switch]$AsOutput)
+      if($ci) {
+        Write-LoggingCommand -Area 'task' -Event 'prependpath' -Data $Path -AsOutput:$AsOutput
+      }
+  }
+
 <########################################
 # Private functions.
 ########################################>

+ 102 - 0
eng/common/pipeline-logging-functions.sh

@@ -0,0 +1,102 @@
+#!/usr/bin/env bash
+
+function Write-PipelineTelemetryError {
+  local telemetry_category=''
+  local function_args=()
+  local message=''
+  while [[ $# -gt 0 ]]; do
+    opt="$(echo "${1/#--/-}" | awk '{print tolower($0)}')"
+    case "$opt" in
+      -category|-c)
+        telemetry_category=$2
+        shift
+        ;;
+      -*)
+        function_args+=("$1 $2")
+        shift
+        ;;
+      *)
+        message=$*
+        ;;
+    esac
+    shift
+  done
+
+  if [[ "$ci" != true ]]; then
+    echo "$message" >&2
+    return
+  fi
+
+  message="(NETCORE_ENGINEERING_TELEMETRY=$telemetry_category) $message"
+  function_args+=("$message")
+
+  Write-PipelineTaskError $function_args
+}
+
+function Write-PipelineTaskError {
+  if [[ "$ci" != true ]]; then
+    echo "$@" >&2
+    return
+  fi
+
+  message_type="error"
+  sourcepath=''
+  linenumber=''
+  columnnumber=''
+  error_code=''
+
+  while [[ $# -gt 0 ]]; do
+    opt="$(echo "${1/#--/-}" | awk '{print tolower($0)}')"
+    case "$opt" in
+      -type|-t)
+        message_type=$2
+        shift
+        ;;
+      -sourcepath|-s)
+        sourcepath=$2
+        shift
+        ;;
+      -linenumber|-ln)
+        linenumber=$2
+        shift
+        ;;
+      -columnnumber|-cn)
+        columnnumber=$2
+        shift
+        ;;
+      -errcode|-e)
+        error_code=$2
+        shift
+        ;;
+      *)
+        break
+        ;;
+    esac
+
+    shift
+  done
+
+  message="##vso[task.logissue"
+
+  message="$message type=$message_type"
+
+  if [ -n "$sourcepath" ]; then
+    message="$message;sourcepath=$sourcepath"
+  fi
+
+  if [ -n "$linenumber" ]; then
+    message="$message;linenumber=$linenumber"
+  fi
+
+  if [ -n "$columnnumber" ]; then
+    message="$message;columnnumber=$columnnumber"
+  fi
+
+  if [ -n "$error_code" ]; then
+    message="$message;code=$error_code"
+  fi
+
+  message="$message]$*"
+  echo "$message"
+}
+

+ 29 - 0
eng/common/post-build/dotnetsymbol-init.ps1

@@ -0,0 +1,29 @@
+param (
+  $dotnetsymbolVersion = $null
+)
+
+$ErrorActionPreference = "Stop"
+Set-StrictMode -Version 2.0
+
+. $PSScriptRoot\..\tools.ps1
+
+$verbosity = "minimal"
+
+function Installdotnetsymbol ($dotnetsymbolVersion) {
+  $dotnetsymbolPackageName = "dotnet-symbol"
+
+  $dotnetRoot = InitializeDotNetCli -install:$true
+  $dotnet = "$dotnetRoot\dotnet.exe"
+  $toolList = & "$dotnet" tool list --global
+
+  if (($toolList -like "*$dotnetsymbolPackageName*") -and ($toolList -like "*$dotnetsymbolVersion*")) {
+    Write-Host "dotnet-symbol version $dotnetsymbolVersion is already installed."
+  }
+  else {
+    Write-Host "Installing dotnet-symbol version $dotnetsymbolVersion..."
+    Write-Host "You may need to restart your command window if this is the first dotnet tool you have installed."
+    & "$dotnet" tool install $dotnetsymbolPackageName --version $dotnetsymbolVersion --verbosity $verbosity --global
+  }
+}
+
+Installdotnetsymbol $dotnetsymbolVersion

+ 29 - 0
eng/common/post-build/sourcelink-cli-init.ps1

@@ -0,0 +1,29 @@
+param (
+  $sourcelinkCliVersion = $null
+)
+
+$ErrorActionPreference = "Stop"
+Set-StrictMode -Version 2.0
+
+. $PSScriptRoot\..\tools.ps1
+
+$verbosity = "minimal"
+
+function InstallSourcelinkCli ($sourcelinkCliVersion) {
+  $sourcelinkCliPackageName = "sourcelink"
+
+  $dotnetRoot = InitializeDotNetCli -install:$true
+  $dotnet = "$dotnetRoot\dotnet.exe"
+  $toolList = & "$dotnet" tool list --global
+
+  if (($toolList -like "*$sourcelinkCliPackageName*") -and ($toolList -like "*$sourcelinkCliVersion*")) {
+    Write-Host "SourceLink CLI version $sourcelinkCliVersion is already installed."
+  }
+  else {
+    Write-Host "Installing SourceLink CLI version $sourcelinkCliVersion..."
+    Write-Host "You may need to restart your command window if this is the first dotnet tool you have installed."
+    & "$dotnet" tool install $sourcelinkCliPackageName --version $sourcelinkCliVersion --verbosity $verbosity --global 
+  }
+}
+
+InstallSourcelinkCli $sourcelinkCliVersion

+ 224 - 0
eng/common/post-build/sourcelink-validation.ps1

@@ -0,0 +1,224 @@
+param(
+  [Parameter(Mandatory=$true)][string] $InputPath,              # Full path to directory where Symbols.NuGet packages to be checked are stored
+  [Parameter(Mandatory=$true)][string] $ExtractPath,            # Full path to directory where the packages will be extracted during validation
+  [Parameter(Mandatory=$true)][string] $GHRepoName,             # GitHub name of the repo including the Org. E.g., dotnet/arcade
+  [Parameter(Mandatory=$true)][string] $GHCommit,               # GitHub commit SHA used to build the packages
+  [Parameter(Mandatory=$true)][string] $SourcelinkCliVersion    # Version of SourceLink CLI to use
+)
+
+$ErrorActionPreference = "Stop"
+Set-StrictMode -Version 2.0
+
+. $PSScriptRoot\..\tools.ps1
+
+# Cache/HashMap (File -> Exist flag) used to consult whether a file exist 
+# in the repository at a specific commit point. This is populated by inserting
+# all files present in the repo at a specific commit point.
+$global:RepoFiles = @{}
+
+$ValidatePackage = {
+  param( 
+    [string] $PackagePath                                 # Full path to a Symbols.NuGet package
+  )
+
+  . $using:PSScriptRoot\..\tools.ps1
+
+  # Ensure input file exist
+  if (!(Test-Path $PackagePath)) {
+    Write-PipelineTaskError "Input file does not exist: $PackagePath"
+    ExitWithExitCode 1
+  }
+
+  # Extensions for which we'll look for SourceLink information
+  # For now we'll only care about Portable & Embedded PDBs
+  $RelevantExtensions = @(".dll", ".exe", ".pdb")
+ 
+  Write-Host -NoNewLine "Validating" ([System.IO.Path]::GetFileName($PackagePath)) "... "
+
+  $PackageId = [System.IO.Path]::GetFileNameWithoutExtension($PackagePath)
+  $ExtractPath = Join-Path -Path $using:ExtractPath -ChildPath $PackageId
+  $FailedFiles = 0
+
+  Add-Type -AssemblyName System.IO.Compression.FileSystem
+
+  [System.IO.Directory]::CreateDirectory($ExtractPath);
+
+  try {
+    $zip = [System.IO.Compression.ZipFile]::OpenRead($PackagePath)
+
+    $zip.Entries | 
+      Where-Object {$RelevantExtensions -contains [System.IO.Path]::GetExtension($_.Name)} |
+        ForEach-Object {
+          $FileName = $_.FullName
+          $Extension = [System.IO.Path]::GetExtension($_.Name)
+          $FakeName = -Join((New-Guid), $Extension)
+          $TargetFile = Join-Path -Path $ExtractPath -ChildPath $FakeName 
+
+          # We ignore resource DLLs
+          if ($FileName.EndsWith(".resources.dll")) {
+            return
+          }
+
+          [System.IO.Compression.ZipFileExtensions]::ExtractToFile($_, $TargetFile, $true)
+
+          $ValidateFile = {
+            param( 
+              [string] $FullPath,                                # Full path to the module that has to be checked
+              [string] $RealPath,
+              [ref] $FailedFiles
+            )
+
+            $sourcelinkExe = "$env:USERPROFILE\.dotnet\tools"
+            $sourcelinkExe = Resolve-Path "$sourcelinkExe\sourcelink.exe"
+            $SourceLinkInfos = & $sourcelinkExe print-urls $FullPath | Out-String
+
+            if ($LASTEXITCODE -eq 0 -and -not ([string]::IsNullOrEmpty($SourceLinkInfos))) {
+              $NumFailedLinks = 0
+
+              # We only care about Http addresses
+              $Matches = (Select-String '(http[s]?)(:\/\/)([^\s,]+)' -Input $SourceLinkInfos -AllMatches).Matches
+
+              if ($Matches.Count -ne 0) {
+                $Matches.Value |
+                  ForEach-Object {
+                    $Link = $_
+                    $CommitUrl = "https://raw.githubusercontent.com/${using:GHRepoName}/${using:GHCommit}/"
+                    
+                    $FilePath = $Link.Replace($CommitUrl, "")
+                    $Status = 200
+                    $Cache = $using:RepoFiles
+
+                    if ( !($Cache.ContainsKey($FilePath)) ) {
+                      try {
+                        $Uri = $Link -as [System.URI]
+                      
+                        # Only GitHub links are valid
+                        if ($Uri.AbsoluteURI -ne $null -and ($Uri.Host -match "github" -or $Uri.Host -match "githubusercontent")) {
+                          $Status = (Invoke-WebRequest -Uri $Link -UseBasicParsing -Method HEAD -TimeoutSec 5).StatusCode
+                        }
+                        else {
+                          $Status = 0
+                        }
+                      }
+                      catch {
+                        write-host $_
+                        $Status = 0
+                      }
+                    }
+
+                    if ($Status -ne 200) {
+                      if ($NumFailedLinks -eq 0) {
+                        if ($FailedFiles.Value -eq 0) {
+                          Write-Host
+                        }
+
+                        Write-Host "`tFile $RealPath has broken links:"
+                      }
+
+                      Write-Host "`t`tFailed to retrieve $Link"
+
+                      $NumFailedLinks++
+                    }
+                  }
+              }
+
+              if ($NumFailedLinks -ne 0) {
+                $FailedFiles.value++
+                $global:LASTEXITCODE = 1
+              }
+            }
+          }
+        
+          &$ValidateFile $TargetFile $FileName ([ref]$FailedFiles)
+        }
+  }
+  catch {
+  
+  }
+  finally {
+    $zip.Dispose() 
+  }
+
+  if ($FailedFiles -eq 0) {
+    Write-Host "Passed."
+  }
+  else {
+    Write-PipelineTaskError "$PackagePath has broken SourceLink links."
+  }
+}
+
+function ValidateSourceLinkLinks {
+  if (!($GHRepoName -Match "^[^\s\/]+/[^\s\/]+$")) {
+    if (!($GHRepoName -Match "^[^\s-]+-[^\s]+$")) {
+      Write-PipelineTaskError "GHRepoName should be in the format <org>/<repo> or <org>-<repo>"
+      ExitWithExitCode 1
+    }
+    else {
+      $GHRepoName = $GHRepoName -replace '^([^\s-]+)-([^\s]+)$', '$1/$2';
+    }
+  }
+
+  if (!($GHCommit -Match "^[0-9a-fA-F]{40}$")) {
+    Write-PipelineTaskError "GHCommit should be a 40 chars hexadecimal string"
+    ExitWithExitCode 1
+  }
+
+  $RepoTreeURL = -Join("http://api.github.com/repos/", $GHRepoName, "/git/trees/", $GHCommit, "?recursive=1")
+  $CodeExtensions = @(".cs", ".vb", ".fs", ".fsi", ".fsx", ".fsscript")
+
+  try {
+    # Retrieve the list of files in the repo at that particular commit point and store them in the RepoFiles hash
+    $Data = Invoke-WebRequest $RepoTreeURL -UseBasicParsing | ConvertFrom-Json | Select-Object -ExpandProperty tree
+  
+    foreach ($file in $Data) {
+      $Extension = [System.IO.Path]::GetExtension($file.path)
+
+      if ($CodeExtensions.Contains($Extension)) {
+        $RepoFiles[$file.path] = 1
+      }
+    }
+  }
+  catch {
+    Write-PipelineTaskError "Problems downloading the list of files from the repo. Url used: $RepoTreeURL"
+    Write-Host $_
+    ExitWithExitCode 1
+  }
+  
+  if (Test-Path $ExtractPath) {
+    Remove-Item $ExtractPath -Force -Recurse -ErrorAction SilentlyContinue
+  }
+
+  # Process each NuGet package in parallel
+  $Jobs = @()
+  Get-ChildItem "$InputPath\*.symbols.nupkg" |
+    ForEach-Object {
+      $Jobs += Start-Job -ScriptBlock $ValidatePackage -ArgumentList $_.FullName
+    }
+
+  foreach ($Job in $Jobs) {
+    Wait-Job -Id $Job.Id | Receive-Job
+  }
+}
+
+function CheckExitCode ([string]$stage) {
+  $exitCode = $LASTEXITCODE
+  if ($exitCode -ne 0) {
+    Write-PipelineTaskError "Something failed while '$stage'. Check for errors above. Exiting now..."
+    ExitWithExitCode $exitCode
+  }
+}
+
+try {
+  Write-Host "Installing SourceLink CLI..."
+  Get-Location
+  . $PSScriptRoot\sourcelink-cli-init.ps1 -sourcelinkCliVersion $SourcelinkCliVersion
+  CheckExitCode "Running sourcelink-cli-init"
+
+  Measure-Command { ValidateSourceLinkLinks }
+}
+catch {
+  Write-Host $_
+  Write-Host $_.Exception
+  Write-Host $_.ScriptStackTrace
+  ExitWithExitCode 1
+}

+ 186 - 0
eng/common/post-build/symbols-validation.ps1

@@ -0,0 +1,186 @@
+param(
+  [Parameter(Mandatory=$true)][string] $InputPath,              # Full path to directory where NuGet packages to be checked are stored
+  [Parameter(Mandatory=$true)][string] $ExtractPath,            # Full path to directory where the packages will be extracted during validation
+  [Parameter(Mandatory=$true)][string] $DotnetSymbolVersion     # Version of dotnet symbol to use
+)
+
+$ErrorActionPreference = "Stop"
+Set-StrictMode -Version 2.0
+
+. $PSScriptRoot\..\tools.ps1
+
+Add-Type -AssemblyName System.IO.Compression.FileSystem
+
+function FirstMatchingSymbolDescriptionOrDefault {
+  param( 
+    [string] $FullPath,                  # Full path to the module that has to be checked
+    [string] $TargetServerParam,         # Parameter to pass to `Symbol Tool` indicating the server to lookup for symbols
+    [string] $SymbolsPath
+  )
+
+  $FileName = [System.IO.Path]::GetFileName($FullPath)
+  $Extension = [System.IO.Path]::GetExtension($FullPath)
+
+  # Those below are potential symbol files that the `dotnet symbol` might
+  # return. Which one will be returned depend on the type of file we are
+  # checking and which type of file was uploaded.
+
+  # The file itself is returned
+  $SymbolPath = $SymbolsPath + "\" + $FileName
+
+  # PDB file for the module
+  $PdbPath = $SymbolPath.Replace($Extension, ".pdb")
+
+  # PDB file for R2R module (created by crossgen)
+  $NGenPdb = $SymbolPath.Replace($Extension, ".ni.pdb")
+
+  # DBG file for a .so library
+  $SODbg = $SymbolPath.Replace($Extension, ".so.dbg")
+
+  # DWARF file for a .dylib
+  $DylibDwarf = $SymbolPath.Replace($Extension, ".dylib.dwarf")
+ 
+  $dotnetsymbolExe = "$env:USERPROFILE\.dotnet\tools"
+  $dotnetsymbolExe = Resolve-Path "$dotnetsymbolExe\dotnet-symbol.exe"
+
+  & $dotnetsymbolExe --symbols --modules --windows-pdbs $TargetServerParam $FullPath -o $SymbolsPath | Out-Null
+
+  if (Test-Path $PdbPath) {
+    return "PDB"
+  }
+  elseif (Test-Path $NGenPdb) {
+    return "NGen PDB"
+  }
+  elseif (Test-Path $SODbg) {
+    return "DBG for SO"
+  }  
+  elseif (Test-Path $DylibDwarf) {
+    return "Dwarf for Dylib"
+  }  
+  elseif (Test-Path $SymbolPath) {
+    return "Module"
+  }
+  else {
+    return $null
+  }
+}
+
+function CountMissingSymbols {
+  param( 
+    [string] $PackagePath          # Path to a NuGet package
+  )
+
+  # Ensure input file exist
+  if (!(Test-Path $PackagePath)) {
+    Write-PipelineTaskError "Input file does not exist: $PackagePath"
+    ExitWithExitCode 1
+  }
+  
+  # Extensions for which we'll look for symbols
+  $RelevantExtensions = @(".dll", ".exe", ".so", ".dylib")
+
+  # How many files are missing symbol information
+  $MissingSymbols = 0
+
+  $PackageId = [System.IO.Path]::GetFileNameWithoutExtension($PackagePath)
+  $PackageGuid = New-Guid
+  $ExtractPath = Join-Path -Path $ExtractPath -ChildPath $PackageGuid
+  $SymbolsPath = Join-Path -Path $ExtractPath -ChildPath "Symbols"
+  
+  [System.IO.Compression.ZipFile]::ExtractToDirectory($PackagePath, $ExtractPath)
+
+  Get-ChildItem -Recurse $ExtractPath |
+    Where-Object {$RelevantExtensions -contains $_.Extension} |
+    ForEach-Object {
+      if ($_.FullName -Match "\\ref\\") {
+        Write-Host "`t Ignoring reference assembly file" $_.FullName
+        return
+      }
+
+      $SymbolsOnMSDL = FirstMatchingSymbolDescriptionOrDefault $_.FullName "--microsoft-symbol-server" $SymbolsPath
+      $SymbolsOnSymWeb = FirstMatchingSymbolDescriptionOrDefault $_.FullName "--internal-server" $SymbolsPath
+
+      Write-Host -NoNewLine "`t Checking file" $_.FullName "... "
+  
+      if ($SymbolsOnMSDL -ne $null -and $SymbolsOnSymWeb -ne $null) {
+        Write-Host "Symbols found on MSDL (" $SymbolsOnMSDL ") and SymWeb (" $SymbolsOnSymWeb ")"
+      }
+      else {
+        $MissingSymbols++
+
+        if ($SymbolsOnMSDL -eq $null -and $SymbolsOnSymWeb -eq $null) {
+          Write-Host "No symbols found on MSDL or SymWeb!"
+        }
+        else {
+          if ($SymbolsOnMSDL -eq $null) {
+            Write-Host "No symbols found on MSDL!"
+          }
+          else {
+            Write-Host "No symbols found on SymWeb!"
+          }
+        }
+      }
+    }
+  
+  Pop-Location
+
+  return $MissingSymbols
+}
+
+function CheckSymbolsAvailable {
+  if (Test-Path $ExtractPath) {
+    Remove-Item $ExtractPath -Force  -Recurse -ErrorAction SilentlyContinue
+  }
+
+  Get-ChildItem "$InputPath\*.nupkg" |
+    ForEach-Object {
+      $FileName = $_.Name
+	  
+      # These packages from Arcade-Services include some native libraries that
+      # our current symbol uploader can't handle. Below is a workaround until
+      # we get issue: https://github.com/dotnet/arcade/issues/2457 sorted.
+      if ($FileName -Match "Microsoft\.DotNet\.Darc\.") {
+        Write-Host "Ignoring Arcade-services file: $FileName"
+        Write-Host
+        return
+      }
+      elseif ($FileName -Match "Microsoft\.DotNet\.Maestro\.Tasks\.") {
+        Write-Host "Ignoring Arcade-services file: $FileName"
+        Write-Host
+        return
+      }
+	  
+      Write-Host "Validating $FileName "
+      $Status = CountMissingSymbols "$InputPath\$FileName"
+  
+      if ($Status -ne 0) {
+	    Write-PipelineTaskError "Missing symbols for $Status modules in the package $FileName"
+		ExitWithExitCode $exitCode
+      }
+
+      Write-Host
+    }
+}
+
+function CheckExitCode ([string]$stage) {
+  $exitCode = $LASTEXITCODE
+  if ($exitCode -ne 0) {
+    Write-PipelineTaskError "Something failed while '$stage'. Check for errors above. Exiting now..."
+    ExitWithExitCode $exitCode
+  }
+}
+
+try {
+  Write-Host "Installing dotnet symbol ..."
+  Get-Location
+  . $PSScriptRoot\dotnetsymbol-init.ps1 -dotnetsymbolVersion $DotnetSymbolVersion
+  CheckExitCode "Running dotnetsymbol-init"
+
+  CheckSymbolsAvailable
+}
+catch {
+  Write-Host $_
+  Write-Host $_.Exception
+  Write-Host $_.ScriptStackTrace
+  ExitWithExitCode 1
+}

+ 28 - 28
eng/common/sdl/execute-all-sdl-tools.ps1

@@ -1,28 +1,28 @@
 Param(
-  [string] $GuardianPackageName,                        # Required: the name of guardian CLI pacakge (not needed if GuardianCliLocation is specified)
-  [string] $NugetPackageDirectory,                      # Required: directory where NuGet packages are installed (not needed if GuardianCliLocation is specified)
-  [string] $GuardianCliLocation,                        # Optional: Direct location of Guardian CLI executable if GuardianPackageName & NugetPackageDirectory are not specified
-  [string] $Repository,                                 # Required: the name of the repository (e.g. dotnet/arcade)
-  [string] $BranchName="master",                        # Optional: name of branch or version of gdn settings; defaults to master
-  [string] $SourceDirectory,                            # Required: the directory where source files are located
-  [string] $ArtifactsDirectory,                         # Required: the directory where build artifacts are located
-  [string] $DncEngAccessToken,                          # Required: access token for dnceng; should be provided via KeyVault
-  [string[]] $SourceToolsList,                          # Optional: list of SDL tools to run on source code
-  [string[]] $ArtifactToolsList,                        # Optional: list of SDL tools to run on built artifacts
-  [bool] $TsaPublish=$False,                            # Optional: true will publish results to TSA; only set to true after onboarding to TSA; TSA is the automated framework used to upload test results as bugs.
-  [string] $TsaBranchName=$env:BUILD_SOURCEBRANCHNAME,  # Optional: required for TSA publish; defaults to $(Build.SourceBranchName); TSA is the automated framework used to upload test results as bugs.
-  [string] $TsaRepositoryName,                          # Optional: TSA repository name; will be generated automatically if not submitted; TSA is the automated framework used to upload test results as bugs.
-  [string] $BuildNumber=$env:BUILD_BUILDNUMBER,         # Optional: required for TSA publish; defaults to $(Build.BuildNumber)
-  [bool] $UpdateBaseline=$False,                        # Optional: if true, will update the baseline in the repository; should only be run after fixing any issues which need to be fixed
-  [bool] $TsaOnboard=$False,                            # Optional: if true, will onboard the repository to TSA; should only be run once; TSA is the automated framework used to upload test results as bugs.
-  [string] $TsaInstanceUrl,                             # Optional: only needed if TsaOnboard or TsaPublish is true; the instance-url registered with TSA; TSA is the automated framework used to upload test results as bugs.
-  [string] $TsaCodebaseName,                            # Optional: only needed if TsaOnboard or TsaPublish is true; the name of the codebase registered with TSA; TSA is the automated framework used to upload test results as bugs.
-  [string] $TsaProjectName,                             # Optional: only needed if TsaOnboard or TsaPublish is true; the name of the project registered with TSA; TSA is the automated framework used to upload test results as bugs.
-  [string] $TsaNotificationEmail,                       # Optional: only needed if TsaOnboard is true; the email(s) which will receive notifications of TSA bug filings (e.g. [email protected]); TSA is the automated framework used to upload test results as bugs.
-  [string] $TsaCodebaseAdmin,                           # Optional: only needed if TsaOnboard is true; the aliases which are admins of the TSA codebase (e.g. DOMAIN\alias); TSA is the automated framework used to upload test results as bugs.
-  [string] $TsaBugAreaPath,                             # Optional: only needed if TsaOnboard is true; the area path where TSA will file bugs in AzDO; TSA is the automated framework used to upload test results as bugs.
-  [string] $TsaIterationPath,                           # Optional: only needed if TsaOnboard is true; the iteration path where TSA will file bugs in AzDO; TSA is the automated framework used to upload test results as bugs.
-  [string] $GuardianLoggerLevel="Standard"              # Optional: the logger level for the Guardian CLI; options are Trace, Verbose, Standard, Warning, and Error
+  [string] $GuardianPackageName,                                                           # Required: the name of guardian CLI package (not needed if GuardianCliLocation is specified)
+  [string] $NugetPackageDirectory,                                                         # Required: directory where NuGet packages are installed (not needed if GuardianCliLocation is specified)
+  [string] $GuardianCliLocation,                                                           # Optional: Direct location of Guardian CLI executable if GuardianPackageName & NugetPackageDirectory are not specified
+  [string] $Repository=$env:BUILD_REPOSITORY_NAME,                                         # Required: the name of the repository (e.g. dotnet/arcade)
+  [string] $BranchName=$env:BUILD_SOURCEBRANCH,                                            # Optional: name of branch or version of gdn settings; defaults to master
+  [string] $SourceDirectory=$env:BUILD_SOURCESDIRECTORY,                                   # Required: the directory where source files are located
+  [string] $ArtifactsDirectory = (Join-Path $env:BUILD_SOURCESDIRECTORY ("artifacts")),    # Required: the directory where build artifacts are located
+  [string] $AzureDevOpsAccessToken,                                                        # Required: access token for dnceng; should be provided via KeyVault
+  [string[]] $SourceToolsList,                                                             # Optional: list of SDL tools to run on source code
+  [string[]] $ArtifactToolsList,                                                           # Optional: list of SDL tools to run on built artifacts
+  [bool] $TsaPublish=$False,                                                               # Optional: true will publish results to TSA; only set to true after onboarding to TSA; TSA is the automated framework used to upload test results as bugs.
+  [string] $TsaBranchName=$env:BUILD_SOURCEBRANCH,                                         # Optional: required for TSA publish; defaults to $(Build.SourceBranchName); TSA is the automated framework used to upload test results as bugs.
+  [string] $TsaRepositoryName=$env:BUILD_REPOSITORY_NAME,                                  # Optional: TSA repository name; will be generated automatically if not submitted; TSA is the automated framework used to upload test results as bugs.
+  [string] $BuildNumber=$env:BUILD_BUILDNUMBER,                                            # Optional: required for TSA publish; defaults to $(Build.BuildNumber)
+  [bool] $UpdateBaseline=$False,                                                           # Optional: if true, will update the baseline in the repository; should only be run after fixing any issues which need to be fixed
+  [bool] $TsaOnboard=$False,                                                               # Optional: if true, will onboard the repository to TSA; should only be run once; TSA is the automated framework used to upload test results as bugs.
+  [string] $TsaInstanceUrl,                                                                # Optional: only needed if TsaOnboard or TsaPublish is true; the instance-url registered with TSA; TSA is the automated framework used to upload test results as bugs.
+  [string] $TsaCodebaseName,                                                               # Optional: only needed if TsaOnboard or TsaPublish is true; the name of the codebase registered with TSA; TSA is the automated framework used to upload test results as bugs.
+  [string] $TsaProjectName,                                                                # Optional: only needed if TsaOnboard or TsaPublish is true; the name of the project registered with TSA; TSA is the automated framework used to upload test results as bugs.
+  [string] $TsaNotificationEmail,                                                          # Optional: only needed if TsaOnboard is true; the email(s) which will receive notifications of TSA bug filings (e.g. [email protected]); TSA is the automated framework used to upload test results as bugs.
+  [string] $TsaCodebaseAdmin,                                                              # Optional: only needed if TsaOnboard is true; the aliases which are admins of the TSA codebase (e.g. DOMAIN\alias); TSA is the automated framework used to upload test results as bugs.
+  [string] $TsaBugAreaPath,                                                                # Optional: only needed if TsaOnboard is true; the area path where TSA will file bugs in AzDO; TSA is the automated framework used to upload test results as bugs.
+  [string] $TsaIterationPath,                                                              # Optional: only needed if TsaOnboard is true; the iteration path where TSA will file bugs in AzDO; TSA is the automated framework used to upload test results as bugs.
+  [string] $GuardianLoggerLevel="Standard"                                                 # Optional: the logger level for the Guardian CLI; options are Trace, Verbose, Standard, Warning, and Error
 )
 
 $ErrorActionPreference = "Stop"
@@ -51,7 +51,7 @@ if ($ValidPath -eq $False)
   exit 1
 }
 
-& $(Join-Path $PSScriptRoot "init-sdl.ps1") -GuardianCliLocation $guardianCliLocation -Repository $RepoName -BranchName $BranchName -WorkingDirectory $ArtifactsDirectory -DncEngAccessToken $DncEngAccessToken -GuardianLoggerLevel $GuardianLoggerLevel
+& $(Join-Path $PSScriptRoot "init-sdl.ps1") -GuardianCliLocation $guardianCliLocation -Repository $RepoName -BranchName $BranchName -WorkingDirectory $ArtifactsDirectory -AzureDevOpsAccessToken $AzureDevOpsAccessToken -GuardianLoggerLevel $GuardianLoggerLevel
 $gdnFolder = Join-Path $ArtifactsDirectory ".gdn"
 
 if ($TsaOnboard) {
@@ -69,14 +69,14 @@ if ($TsaOnboard) {
 }
 
 if ($ArtifactToolsList -and $ArtifactToolsList.Count -gt 0) {
-  & $(Join-Path $PSScriptRoot "run-sdl.ps1") -GuardianCliLocation $guardianCliLocation -WorkingDirectory $ArtifactsDirectory -TargetDirectory $ArtifactsDirectory -GdnFolder $gdnFolder -ToolsList $ArtifactToolsList -DncEngAccessToken $DncEngAccessToken -UpdateBaseline $UpdateBaseline -GuardianLoggerLevel $GuardianLoggerLevel
+  & $(Join-Path $PSScriptRoot "run-sdl.ps1") -GuardianCliLocation $guardianCliLocation -WorkingDirectory $ArtifactsDirectory -TargetDirectory $ArtifactsDirectory -GdnFolder $gdnFolder -ToolsList $ArtifactToolsList -AzureDevOpsAccessToken $AzureDevOpsAccessToken -UpdateBaseline $UpdateBaseline -GuardianLoggerLevel $GuardianLoggerLevel
 }
 if ($SourceToolsList -and $SourceToolsList.Count -gt 0) {
-  & $(Join-Path $PSScriptRoot "run-sdl.ps1") -GuardianCliLocation $guardianCliLocation -WorkingDirectory $ArtifactsDirectory -TargetDirectory $SourceDirectory -GdnFolder $gdnFolder -ToolsList $SourceToolsList -DncEngAccessToken $DncEngAccessToken -UpdateBaseline $UpdateBaseline -GuardianLoggerLevel $GuardianLoggerLevel
+  & $(Join-Path $PSScriptRoot "run-sdl.ps1") -GuardianCliLocation $guardianCliLocation -WorkingDirectory $ArtifactsDirectory -TargetDirectory $SourceDirectory -GdnFolder $gdnFolder -ToolsList $SourceToolsList -AzureDevOpsAccessToken $AzureDevOpsAccessToken -UpdateBaseline $UpdateBaseline -GuardianLoggerLevel $GuardianLoggerLevel
 }
 
 if ($UpdateBaseline) {
-  & (Join-Path $PSScriptRoot "push-gdn.ps1") -Repository $RepoName -BranchName $BranchName -GdnFolder $GdnFolder -DncEngAccessToken $DncEngAccessToken -PushReason "Update baseline"
+  & (Join-Path $PSScriptRoot "push-gdn.ps1") -Repository $RepoName -BranchName $BranchName -GdnFolder $GdnFolder -AzureDevOpsAccessToken $AzureDevOpsAccessToken -PushReason "Update baseline"
 }
 
 if ($TsaPublish) {

+ 3 - 3
eng/common/sdl/init-sdl.ps1

@@ -3,7 +3,7 @@ Param(
   [string] $Repository,
   [string] $BranchName="master",
   [string] $WorkingDirectory,
-  [string] $DncEngAccessToken,
+  [string] $AzureDevOpsAccessToken,
   [string] $GuardianLoggerLevel="Standard"
 )
 
@@ -12,7 +12,7 @@ Set-StrictMode -Version 2.0
 $LASTEXITCODE = 0
 
 # Construct basic auth from AzDO access token; construct URI to the repository's gdn folder stored in that repository; construct location of zip file
-$encodedPat = [Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$DncEngAccessToken"))
+$encodedPat = [Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$AzureDevOpsAccessToken"))
 $escapedRepository = [Uri]::EscapeDataString("/$Repository/$BranchName/.gdn")
 $uri = "https://dev.azure.com/dnceng/internal/_apis/git/repositories/sdl-tool-cfg/Items?path=$escapedRepository&versionDescriptor[versionOptions]=0&`$format=zip&api-version=5.0-preview.1"
 $zipFile = "$WorkingDirectory/gdn.zip"
@@ -44,5 +44,5 @@ Try
   if ($LASTEXITCODE -ne 0) {
     Write-Error "Guardian baseline failed with exit code $LASTEXITCODE."
   }
-  & $(Join-Path $PSScriptRoot "push-gdn.ps1") -Repository $Repository -BranchName $BranchName -GdnFolder $gdnFolder -DncEngAccessToken $DncEngAccessToken -PushReason "Initialize gdn folder"
+  & $(Join-Path $PSScriptRoot "push-gdn.ps1") -Repository $Repository -BranchName $BranchName -GdnFolder $gdnFolder -AzureDevOpsAccessToken $AzureDevOpsAccessToken -PushReason "Initialize gdn folder"
 }

+ 3 - 3
eng/common/sdl/push-gdn.ps1

@@ -2,7 +2,7 @@ Param(
   [string] $Repository,
   [string] $BranchName="master",
   [string] $GdnFolder,
-  [string] $DncEngAccessToken,
+  [string] $AzureDevOpsAccessToken,
   [string] $PushReason
 )
 
@@ -16,8 +16,8 @@ if (Test-Path $sdlDir) {
   Remove-Item -Force -Recurse $sdlDir
 }
 
-Write-Host "git clone https://dnceng:`$DncEng[email protected]/dnceng/internal/_git/sdl-tool-cfg $sdlDir"
-git clone https://dnceng:$DncEng[email protected]/dnceng/internal/_git/sdl-tool-cfg $sdlDir
+Write-Host "git clone https://dnceng:`$AzureDevOps[email protected]/dnceng/internal/_git/sdl-tool-cfg $sdlDir"
+git clone https://dnceng:$AzureDevOps[email protected]/dnceng/internal/_git/sdl-tool-cfg $sdlDir
 if ($LASTEXITCODE -ne 0) {
   Write-Error "Git clone failed with exit code $LASTEXITCODE."
 }

+ 44 - 0
eng/common/templates/job/execute-sdl.yml

@@ -0,0 +1,44 @@
+parameters:
+  overrideParameters: ''                                       # Optional: to override values for parameters.
+  additionalParameters: ''                                     # Optional: parameters that need user specific values eg: '-SourceToolsList @("abc","def") -ArtifactToolsList @("ghi","jkl")'
+  continueOnError: false                                       # optional: determines whether to continue the build if the step errors;
+  dependsOn: ''                                                # Optional: dependencies of the job
+
+jobs:
+- job: Run_SDL
+  dependsOn: ${{ parameters.dependsOn }}
+  displayName: Run SDL tool
+  variables:
+    - group: DotNet-VSTS-Bot
+  steps:
+  - checkout: self
+    clean: true
+  - task: DownloadBuildArtifacts@0
+    displayName: Download Build Artifacts
+    inputs:
+      buildType: current
+      downloadType: specific files
+      matchingPattern: "**"
+      downloadPath: $(Build.SourcesDirectory)\artifacts
+  - task: NuGetToolInstaller@1
+    displayName: 'Install NuGet.exe'
+  - task: NuGetCommand@2
+    displayName: 'Install Guardian'
+    inputs:
+      restoreSolution: $(Build.SourcesDirectory)\eng\common\sdl\packages.config
+      feedsToUse: config
+      nugetConfigPath: $(Build.SourcesDirectory)\eng\common\sdl\NuGet.config
+      externalFeedCredentials: GuardianConnect
+      restoreDirectory: $(Build.SourcesDirectory)\.packages
+  - ${{ if ne(parameters.overrideParameters, '') }}:
+    - powershell: eng/common/sdl/execute-all-sdl-tools.ps1 ${{ parameters.overrideParameters }}
+      displayName: Execute SDL
+      continueOnError: ${{ parameters.continueOnError }}
+  - ${{ if eq(parameters.overrideParameters, '') }}:
+    - powershell: eng/common/sdl/execute-all-sdl-tools.ps1
+        -GuardianPackageName Microsoft.Guardian.Cli.0.3.2
+        -NugetPackageDirectory $(Build.SourcesDirectory)\.packages
+        -AzureDevOpsAccessToken $(dn-bot-dotnet-build-rw-code-rw)
+        ${{ parameters.additionalParameters }}
+      displayName: Execute SDL
+      continueOnError: ${{ parameters.continueOnError }}

+ 15 - 0
eng/common/templates/job/publish-build-assets.yml

@@ -59,6 +59,21 @@ jobs:
           /p:Configuration=$(_BuildConfig)
       condition: ${{ parameters.condition }}
       continueOnError: ${{ parameters.continueOnError }}
+    - task: powershell@2
+      displayName: Create ReleaseConfigs Artifact
+      inputs:
+        targetType: inline
+        script: |
+          Add-Content -Path "$(Build.StagingDirectory)/ReleaseConfigs.txt" -Value $(BARBuildId)
+          Add-Content -Path "$(Build.StagingDirectory)/ReleaseConfigs.txt" -Value "$(DefaultChannels)"
+          Add-Content -Path "$(Build.StagingDirectory)/ReleaseConfigs.txt" -Value $(IsInternalBuild)
+          Add-Content -Path "$(Build.StagingDirectory)/ReleaseConfigs.txt" -Value $(IsStableBuild)
+    - task: PublishBuildArtifacts@1
+      displayName: Publish ReleaseConfigs Artifact
+      inputs:
+        PathtoPublish: '$(Build.StagingDirectory)/ReleaseConfigs.txt'
+        PublishLocation: Container
+        ArtifactName: ReleaseConfigs
     - ${{ if eq(parameters.enablePublishBuildArtifacts, 'true') }}:
       - task: PublishBuildArtifacts@1
         displayName: Publish Logs to VSTS

+ 145 - 0
eng/common/templates/post-build/channels/public-dev-release.yml

@@ -0,0 +1,145 @@
+parameters:
+  enableSymbolValidation: true
+
+stages:
+- stage: Publish
+  dependsOn: validate
+  variables:
+    - template: ../common-variables.yml
+  displayName: Developer Channel
+  jobs:
+  - template: ../setup-maestro-vars.yml
+
+  - job:
+    displayName: Symbol Publishing
+    dependsOn: setupMaestroVars
+    condition: contains(dependencies.setupMaestroVars.outputs['setReleaseVars.InitialChannels'], variables.PublicDevRelease_30_Channel_Id)
+    variables:
+      - group: DotNet-Symbol-Server-Pats
+    pool:
+      vmImage: 'windows-2019'
+    steps:
+      - task: DownloadBuildArtifacts@0
+        displayName: Download PDB Artifacts
+        inputs:
+          buildType: current
+          artifactName: PDBArtifacts
+        continueOnError: true
+
+      - task: DownloadBuildArtifacts@0
+        displayName: Download Blob Artifacts
+        inputs:
+          buildType: current
+          artifactName: BlobArtifacts
+
+      - task: PowerShell@2
+        displayName: Publish
+        inputs:
+          filePath: eng\common\sdk-task.ps1
+          arguments: -task PublishToSymbolServers -restore -msbuildEngine dotnet
+            /p:DotNetSymbolServerTokenMsdl=$(microsoft-symbol-server-pat) 
+            /p:DotNetSymbolServerTokenSymWeb=$(symweb-symbol-server-pat) 
+            /p:PDBArtifactsDirectory='$(Build.ArtifactStagingDirectory)/PDBArtifacts/'
+            /p:BlobBasePath='$(Build.ArtifactStagingDirectory)/BlobArtifacts/'
+            /p:Configuration=Release
+
+  - job:
+    displayName: Publish to Static Feed
+    dependsOn: setupMaestroVars
+    variables:
+      - group: DotNet-Blob-Feed
+      - group: Publish-Build-Assets
+      - name: BARBuildId
+        value: $[ dependencies.setupMaestroVars.outputs['setReleaseVars.BARBuildId'] ]
+    condition: contains(dependencies.setupMaestroVars.outputs['setReleaseVars.InitialChannels'], variables.PublicDevRelease_30_Channel_Id)
+    pool:
+      vmImage: 'windows-2019'
+    steps:
+      - task: DownloadBuildArtifacts@0
+        displayName: Download Package Artifacts
+        inputs:
+          buildType: current
+          artifactName: PackageArtifacts
+
+      - task: DownloadBuildArtifacts@0
+        displayName: Download Blob Artifacts
+        inputs:
+          buildType: current
+          artifactName: BlobArtifacts
+
+      - task: DownloadBuildArtifacts@0
+        displayName: Download Asset Manifests
+        inputs:
+          buildType: current
+          artifactName: AssetManifests
+
+      - task: PowerShell@2
+        displayName: Publish
+        inputs:
+          filePath: eng\common\sdk-task.ps1
+          arguments: -task PublishToPackageFeed -restore -msbuildEngine dotnet 
+            /p:AccountKeyToStaticFeed='$(dotnetfeed-storage-access-key-1)' 
+            /p:BARBuildId=$(BARBuildId) 
+            /p:MaestroApiEndpoint='https://maestro-prod.westus2.cloudapp.azure.com'
+            /p:BuildAssetRegistryToken='$(MaestroAccessToken)' 
+            /p:ManifestsBasePath='$(Build.ArtifactStagingDirectory)/AssetManifests/' 
+            /p:BlobBasePath='$(Build.ArtifactStagingDirectory)/BlobArtifacts/' 
+            /p:PackageBasePath='$(Build.ArtifactStagingDirectory)/PackageArtifacts/' 
+            /p:ArtifactsCategory='$(_DotNetArtifactsCategory)' 
+            /p:OverrideAssetsWithSameName=true 
+            /p:PassIfExistingItemIdentical=true 
+            /p:Configuration=Release 
+        
+
+- stage: PublishValidation
+  displayName: Publish Validation
+  variables:
+    - template: ../common-variables.yml  
+  jobs:
+  - template: ../setup-maestro-vars.yml
+
+  - ${{ if eq(parameters.enableSymbolValidation, 'true') }}:
+    - job:
+      displayName: Symbol Availability
+      dependsOn: setupMaestroVars
+      condition: contains(dependencies.setupMaestroVars.outputs['setReleaseVars.InitialChannels'], variables.PublicDevRelease_30_Channel_Id)
+      pool:
+        vmImage: 'windows-2019'
+      steps:
+        - task: DownloadBuildArtifacts@0
+          displayName: Download Package Artifacts
+          inputs:
+            buildType: current
+            artifactName: PackageArtifacts
+
+        - task: PowerShell@2
+          displayName: Check Symbol Availability
+          inputs:
+            filePath: $(Build.SourcesDirectory)/eng/common/post-build/symbols-validation.ps1
+            arguments: -InputPath $(Build.ArtifactStagingDirectory)/PackageArtifacts/ -ExtractPath $(Agent.BuildDirectory)/Temp/ -DotnetSymbolVersion $(SymbolToolVersion)
+
+  - job:
+    displayName: Gather Drop
+    dependsOn: setupMaestroVars
+    variables:
+      BARBuildId: $[ dependencies.setupMaestroVars.outputs['setReleaseVars.BARBuildId'] ]
+    condition: contains(dependencies.setupMaestroVars.outputs['setReleaseVars.InitialChannels'], variables.PublicDevRelease_30_Channel_Id)
+    pool:
+      vmImage: 'windows-2019'
+    steps:
+      - task: PowerShell@2
+        displayName: Setup Darc CLI
+        inputs:
+          targetType: filePath
+          filePath: '$(Build.SourcesDirectory)/eng/common/darc-init.ps1'
+
+      - task: PowerShell@2
+        displayName: Run Darc gather-drop
+        inputs:
+          targetType: inline
+          script: |
+            darc gather-drop --non-shipping --continue-on-error --id $(BARBuildId) --output-dir $(Agent.BuildDirectory)/Temp/Drop/ --bar-uri https://maestro-prod.westus2.cloudapp.azure.com/ --password $(MaestroAccessToken) --latest-location
+
+  - template: ../promote-build.yml
+    parameters:
+      ChannelId: ${{ variables.PublicDevRelease_30_Channel_Id }}

+ 91 - 0
eng/common/templates/post-build/channels/public-validation-release.yml

@@ -0,0 +1,91 @@
+stages:
+- stage: PVR_Publish
+  dependsOn: validate
+  variables:
+    - template: ../common-variables.yml
+  displayName: Validation Channel
+  jobs:
+  - template: ../setup-maestro-vars.yml
+
+  - job:
+    displayName: Publish to Static Feed
+    dependsOn: setupMaestroVars
+    condition: contains(dependencies.setupMaestroVars.outputs['setReleaseVars.InitialChannels'], variables.PublicValidationRelease_30_Channel_Id)
+    variables:
+      - group: DotNet-Blob-Feed
+      - group: Publish-Build-Assets
+      - name: BARBuildId
+        value: $[ dependencies.setupMaestroVars.outputs['setReleaseVars.BARBuildId'] ]
+    pool:
+      vmImage: 'windows-2019'
+    steps:
+      - task: DownloadBuildArtifacts@0
+        displayName: Download Package Artifacts
+        inputs:
+          buildType: current
+          artifactName: PackageArtifacts
+
+      - task: DownloadBuildArtifacts@0
+        displayName: Download Blob Artifacts
+        inputs:
+          buildType: current
+          artifactName: BlobArtifacts
+
+      - task: DownloadBuildArtifacts@0
+        displayName: Download Asset Manifests
+        inputs:
+          buildType: current
+          artifactName: AssetManifests
+
+      - task: PowerShell@2
+        displayName: Publish
+        inputs:
+          filePath: eng\common\sdk-task.ps1
+          arguments: -task PublishToPackageFeed -restore -msbuildEngine dotnet 
+            /p:AccountKeyToStaticFeed='$(dotnetfeed-storage-access-key-1)' 
+            /p:BARBuildId=$(BARBuildId) 
+            /p:MaestroApiEndpoint='https://maestro-prod.westus2.cloudapp.azure.com'
+            /p:BuildAssetRegistryToken='$(MaestroAccessToken)' 
+            /p:ManifestsBasePath='$(Build.ArtifactStagingDirectory)/AssetManifests/' 
+            /p:BlobBasePath='$(Build.ArtifactStagingDirectory)/BlobArtifacts/' 
+            /p:PackageBasePath='$(Build.ArtifactStagingDirectory)/PackageArtifacts/' 
+            /p:ArtifactsCategory='$(_DotNetArtifactsCategory)' 
+            /p:OverrideAssetsWithSameName=true 
+            /p:PassIfExistingItemIdentical=true 
+            /p:Configuration=Release 
+
+
+- stage: PVR_PublishValidation
+  displayName: Publish Validation
+  variables:
+    - template: ../common-variables.yml
+  jobs:
+  - template: ../setup-maestro-vars.yml
+
+  - job:
+    displayName: Gather Drop
+    dependsOn: setupMaestroVars
+    condition: contains(dependencies.setupMaestroVars.outputs['setReleaseVars.InitialChannels'], variables.PublicValidationRelease_30_Channel_Id)
+    variables:
+      - name: BARBuildId
+        value: $[ dependencies.setupMaestroVars.outputs['setReleaseVars.BARBuildId'] ]
+      - group: Publish-Build-Assets
+    pool:
+      vmImage: 'windows-2019'
+    steps:
+      - task: PowerShell@2
+        displayName: Setup Darc CLI
+        inputs:
+          targetType: filePath
+          filePath: '$(Build.SourcesDirectory)/eng/common/darc-init.ps1'
+
+      - task: PowerShell@2
+        displayName: Run Darc gather-drop
+        inputs:
+          targetType: inline
+          script: |
+            darc gather-drop --non-shipping --continue-on-error --id $(BARBuildId) --output-dir $(Agent.BuildDirectory)/Temp/Drop/ --bar-uri https://maestro-prod.westus2.cloudapp.azure.com --password $(MaestroAccessToken) --latest-location
+
+  - template: ../promote-build.yml
+    parameters:
+      ChannelId: ${{ variables.PublicValidationRelease_30_Channel_Id }}

+ 9 - 0
eng/common/templates/post-build/common-variables.yml

@@ -0,0 +1,9 @@
+variables:
+  # .NET Core 3 Dev
+  PublicDevRelease_30_Channel_Id: 3
+
+  # .NET Tools - Validation
+  PublicValidationRelease_30_Channel_Id: 9
+
+  SourceLinkCLIVersion: 3.0.0
+  SymbolToolVersion: 1.0.1

+ 67 - 0
eng/common/templates/post-build/post-build.yml

@@ -0,0 +1,67 @@
+parameters:
+  enableSourceLinkValidation: true
+  enableSigningValidation: true
+  enableSymbolValidation: true
+  SDLValidationParameters:
+    enable: false
+    params: ''
+
+stages:
+- stage: validate
+  dependsOn: build
+  displayName: Validate
+  jobs:
+  - ${{ if eq(parameters.enableSigningValidation, 'true') }}:
+    - job:
+      displayName: Signing Validation
+      pool:
+        vmImage: 'windows-2019'
+      steps:
+        - task: DownloadBuildArtifacts@0
+          displayName: Download Package Artifacts
+          inputs:
+            buildType: current
+            artifactName: PackageArtifacts
+
+        - task: PowerShell@2
+          displayName: Validate
+          inputs:
+            filePath: eng\common\sdk-task.ps1
+            arguments: -task SigningValidation -restore -msbuildEngine dotnet
+              /p:PackageBasePath='$(Build.ArtifactStagingDirectory)/PackageArtifacts'
+              /p:Configuration=Release
+
+  - ${{ if eq(parameters.enableSourceLinkValidation, 'true') }}:
+    - job:
+      displayName: SourceLink Validation
+      variables:
+        - template: common-variables.yml
+      pool:
+        vmImage: 'windows-2019'
+      steps:
+        - task: DownloadBuildArtifacts@0
+          displayName: Download Blob Artifacts
+          inputs:
+            buildType: current
+            artifactName: BlobArtifacts
+
+        - task: PowerShell@2
+          displayName: Validate
+          inputs:
+            filePath: $(Build.SourcesDirectory)/eng/common/post-build/sourcelink-validation.ps1
+            arguments: -InputPath $(Build.ArtifactStagingDirectory)/BlobArtifacts/ 
+              -ExtractPath $(Agent.BuildDirectory)/Extract/ 
+              -GHRepoName $(Build.Repository.Name) 
+              -GHCommit $(Build.SourceVersion)
+              -SourcelinkCliVersion $(SourceLinkCLIVersion)
+
+  - ${{ if eq(parameters.SDLValidationParameters.enable, 'true') }}:
+    - template: /eng/common/templates/job/execute-sdl.yml
+      parameters:
+        additionalParameters: ${{ parameters.SDLValidationParameters.params }}
+
+- template: \eng\common\templates\post-build\channels\public-dev-release.yml
+  parameters:
+    enableSymbolValidation: ${{ parameters.enableSymbolValidation }}
+
+- template: \eng\common\templates\post-build\channels\public-validation-release.yml

+ 28 - 0
eng/common/templates/post-build/promote-build.yml

@@ -0,0 +1,28 @@
+parameters:
+  ChannelId: 0
+
+jobs:
+- job:
+  displayName: Promote Build
+  dependsOn: setupMaestroVars
+  condition: contains(dependencies.setupMaestroVars.outputs['setReleaseVars.InitialChannels'], ${{ parameters.ChannelId }})
+  variables:
+    - name: BARBuildId
+      value: $[ dependencies.setupMaestroVars.outputs['setReleaseVars.BARBuildId'] ]
+    - name: ChannelId
+      value: ${{ parameters.ChannelId }}
+    - group: Publish-Build-Assets
+  pool:
+    vmImage: 'windows-2019'
+  steps:
+    - task: PowerShell@2
+      displayName: Add Build to Channel
+      inputs:
+        targetType: inline
+        script: |
+          $headers = @{
+            "Accept" = "application/json"
+            "Authorization" = "Bearer $(MaestroAccessToken)"
+          }
+          Invoke-RestMethod -Method Post -Headers $headers -Uri https://maestro-prod.westus2.cloudapp.azure.com/api/channels/$(ChannelId)/builds/$(BARBuildId)?api-version=2019-01-16
+      enabled: false

+ 37 - 0
eng/common/templates/post-build/setup-maestro-vars.yml

@@ -0,0 +1,37 @@
+jobs:
+- job: setupMaestroVars
+  displayName: Setup Maestro Vars
+  pool:
+    vmImage: 'windows-2019'
+  steps:
+    - task: DownloadBuildArtifacts@0
+      displayName: Download Release Configs
+      inputs:
+        buildType: current
+        artifactName: ReleaseConfigs
+
+    - task: PowerShell@2
+      name: setReleaseVars
+      displayName: Set Release Configs Vars
+      inputs:
+        targetType: inline
+        script: |
+          # This is needed to make Write-PipelineSetVariable works in this context
+          $ci = $true
+
+          . "$(Build.SourcesDirectory)/eng/common/tools.ps1"
+
+          $Content = Get-Content "$(Build.StagingDirectory)/ReleaseConfigs/ReleaseConfigs.txt"
+
+          $BarId = $Content | Select -Index 0
+
+          $Channels = ""            
+          $Content | Select -Index 1 | ForEach-Object { $Channels += "$_ ," }
+            
+          $IsInternalBuild = $Content | Select -Index 2
+          $IsStableBuild = $Content | Select -Index 3
+
+          Write-PipelineSetVariable -Name 'BARBuildId' -Value $BarId
+          Write-PipelineSetVariable -Name 'InitialChannels' -Value "$Channels"
+          Write-PipelineSetVariable -Name 'IsInternalBuild' -Value $IsInternalBuild
+          Write-PipelineSetVariable -Name 'IsStableBuild' -Value $IsStableBuild

+ 13 - 74
eng/common/tools.ps1

@@ -92,69 +92,6 @@ function Exec-Process([string]$command, [string]$commandArgs) {
   }
 }
 
-function Write-PipelineTaskError {
-  [CmdletBinding()]
-  param(
-    [Parameter(Mandatory = $true)]
-    [string]$Message,
-    [Parameter(Mandatory = $false)]
-    [string]$Type = 'error',
-    [string]$ErrCode,
-    [string]$SourcePath,
-    [string]$LineNumber,
-    [string]$ColumnNumber,
-    [switch]$AsOutput)
-
-    if(!$ci) {
-      if($Type -eq 'error') {
-        Write-Host $Message -ForegroundColor Red
-        return
-      }
-      elseif ($Type -eq 'warning') {
-        Write-Host $Message -ForegroundColor Yellow
-        return
-      }
-    }
-
-    if(($Type -ne 'error') -and ($Type -ne 'warning')) {
-      Write-Host $Message
-      return
-    }
-    if(-not $PSBoundParameters.ContainsKey('Type')) {
-      $PSBoundParameters.Add('Type', 'error')
-    }
-    Write-LogIssue @PSBoundParameters
-}
-
-function Write-PipelineSetVariable {
-  [CmdletBinding()]
-  param(
-    [Parameter(Mandatory = $true)]
-    [string]$Name,
-    [string]$Value,
-    [switch]$Secret,
-    [switch]$AsOutput)
-
-    if($ci) {
-      Write-LoggingCommand -Area 'task' -Event 'setvariable' -Data $Value -Properties @{
-        'variable' = $Name
-        'isSecret' = $Secret
-        'isOutput' = 'true'
-      } -AsOutput:$AsOutput
-    }
-}
-
-function Write-PipelinePrependPath {
-  [CmdletBinding()]
-  param(
-    [Parameter(Mandatory=$true)]
-    [string]$Path,
-    [switch]$AsOutput)
-    if($ci) {
-      Write-LoggingCommand -Area 'task' -Event 'prependpath' -Data $Path -AsOutput:$AsOutput
-    }
-}
-
 function InitializeDotNetCli([bool]$install) {
   if (Test-Path variable:global:_DotNetInstallDir) {
     return $global:_DotNetInstallDir
@@ -197,7 +134,7 @@ function InitializeDotNetCli([bool]$install) {
       if ($install) {
         InstallDotNetSdk $dotnetRoot $dotnetSdkVersion
       } else {
-        Write-PipelineTaskError "Unable to find dotnet with SDK version '$dotnetSdkVersion'"
+        Write-PipelineTelemetryError -Category "InitializeToolset" -Message "Unable to find dotnet with SDK version '$dotnetSdkVersion'"
         ExitWithExitCode 1
       }
     }
@@ -245,7 +182,7 @@ function InstallDotNet([string] $dotnetRoot, [string] $version, [string] $archit
 
   & $installScript @installParameters
   if ($lastExitCode -ne 0) {
-    Write-PipelineTaskError -Message "Failed to install dotnet cli (exit code '$lastExitCode')."
+    Write-PipelineTelemetryError -Category "InitializeToolset" -Message "Failed to install dotnet cli (exit code '$lastExitCode')."
     ExitWithExitCode $lastExitCode
   }
 }
@@ -419,7 +356,7 @@ function InitializeBuildTool() {
 
   if ($msbuildEngine -eq "dotnet") {
     if (!$dotnetRoot) {
-      Write-PipelineTaskError "/global.json must specify 'tools.dotnet'."
+      Write-PipelineTelemetryError -Category "InitializeToolset" -Message "/global.json must specify 'tools.dotnet'."
       ExitWithExitCode 1
     }
 
@@ -428,13 +365,13 @@ function InitializeBuildTool() {
     try {
       $msbuildPath = InitializeVisualStudioMSBuild -install:$restore
     } catch {
-      Write-PipelineTaskError $_
+      Write-PipelineTelemetryError -Category "InitializeToolset" -Message $_
       ExitWithExitCode 1
     }
 
     $buildTool = @{ Path = $msbuildPath; Command = ""; Tool = "vs"; Framework = "net472" }
   } else {
-    Write-PipelineTaskError "Unexpected value of -msbuildEngine: '$msbuildEngine'."
+    Write-PipelineTelemetryError -Category "InitializeToolset" -Message "Unexpected value of -msbuildEngine: '$msbuildEngine'."
     ExitWithExitCode 1
   }
 
@@ -451,7 +388,7 @@ function GetDefaultMSBuildEngine() {
     return "dotnet"
   }
 
-  Write-PipelineTaskError "-msbuildEngine must be specified, or /global.json must specify 'tools.dotnet' or 'tools.vs'."
+  Write-PipelineTelemetryError -Category "InitializeToolset" -Message "-msbuildEngine must be specified, or /global.json must specify 'tools.dotnet' or 'tools.vs'."
   ExitWithExitCode 1
 }
 
@@ -504,7 +441,7 @@ function InitializeToolset() {
   }
 
   if (-not $restore) {
-    Write-PipelineTaskError "Toolset version $toolsetVersion has not been restored."
+    Write-PipelineTelemetryError -Category "InitializeToolset" -Message "Toolset version $toolsetVersion has not been restored."
     ExitWithExitCode 1
   }
 
@@ -564,11 +501,13 @@ function MSBuild() {
 function MSBuild-Core() {
   if ($ci) {
     if (!$binaryLog) {
-      throw "Binary log must be enabled in CI build."
+      Write-PipelineTaskError -Message "Binary log must be enabled in CI build."
+      ExitWithExitCode 1
     }
 
     if ($nodeReuse) {
-      throw "Node reuse must be disabled in CI build."
+      Write-PipelineTaskError -Message "Node reuse must be disabled in CI build."
+      ExitWithExitCode 1
     }
   }
 
@@ -589,7 +528,7 @@ function MSBuild-Core() {
   $exitCode = Exec-Process $buildTool.Path $cmdArgs
 
   if ($exitCode -ne 0) {
-    Write-PipelineTaskError "Build failed."
+    Write-PipelineTaskError -Message "Build failed."
 
     $buildLog = GetMSBuildBinaryLogCommandLineArgument $args
     if ($buildLog -ne $null) {
@@ -617,7 +556,7 @@ function GetMSBuildBinaryLogCommandLineArgument($arguments) {
   return $null
 }
 
-. $PSScriptRoot\LoggingCommandFunctions.ps1
+. $PSScriptRoot\pipeline-logging-functions.ps1
 
 $RepoRoot = Resolve-Path (Join-Path $PSScriptRoot "..\..")
 $EngRoot = Resolve-Path (Join-Path $PSScriptRoot "..")

+ 10 - 80
eng/common/tools.sh

@@ -52,78 +52,6 @@ else
   use_global_nuget_cache=${use_global_nuget_cache:-true}
 fi
 
-function EmitError {
-  if [[ "$ci" != true ]]; then
-    echo "$@" >&2
-    return
-  fi
-
-  message_type="error"
-  sourcepath=''
-  linenumber=''
-  columnnumber=''
-  error_code=''
-
-  while [[ $# -gt 0 ]]; do
-    opt="$(echo "${1/#--/-}" | awk '{print tolower($0)}')"
-    case "$opt" in
-      -type|-t)
-        message_type=$2
-        shift
-        ;;
-      -sourcepath|-s)
-        sourcepath=$2
-        shift
-        ;;
-      -linenumber|-l)
-        linenumber=$2
-        shift
-        ;;
-      -columnnumber|-col)
-        columnnumber=$2
-        shift
-        ;;
-      -code|-c)
-        error_code=$2
-        shift
-        ;;
-      *)
-        break
-        ;;
-    esac
-
-    shift
-  done
-
-  message='##vso[task.logissue'
-
-  message="$message type=$message_type"
-
-  if [ -n "$sourcepath" ]; then
-    message="$message;sourcepath=$sourcepath"
-  else
-    message="$message;sourcepath=${BASH_SOURCE[1]}"
-  fi
-
-  if [ -n "$linenumber" ]; then
-    message="$message;linenumber=$linenumber"
-  else
-    message="$message;linenumber=${BASH_LINENO[0]}"
-  fi
-
-  if [ -n "$columnnumber" ]; then
-    message="$message;columnnumber=$columnnumber"
-  fi
-
-  if [ -n "$error_code" ]; then
-    message="$message;code=$error_code"
-  fi
-
-  message="$message]$*"
-
-  echo "$message"
-}
-
 # Resolve any symlinks in the given path.
 function ResolvePath {
   local path=$1
@@ -149,7 +77,7 @@ function ReadGlobalVersion {
   local pattern="\"$key\" *: *\"(.*)\""
 
   if [[ ! $line =~ $pattern ]]; then
-    EmitError "Error: Cannot find \"$key\" in $global_json_file"
+    Write-PipelineTelemetryError -category 'InitializeTools' "Error: Cannot find \"$key\" in $global_json_file"
     ExitWithExitCode 1
   fi
 
@@ -210,7 +138,7 @@ function InitializeDotNetCli {
       if [[ "$install" == true ]]; then
         InstallDotNetSdk "$dotnet_root" "$dotnet_sdk_version"
       else
-        EmitError "Unable to find dotnet with SDK version '$dotnet_sdk_version'"
+        Write-PipelineTelemetryError -category 'InitializeToolset' "Unable to find dotnet with SDK version '$dotnet_sdk_version'"
         ExitWithExitCode 1
       fi
     fi
@@ -263,7 +191,7 @@ function InstallDotNet {
   fi
   bash "$install_script" --version $version --install-dir "$root" $archArg $runtimeArg $skipNonVersionedFilesArg || {
     local exit_code=$?
-    EmitError "Failed to install dotnet SDK (exit code '$exit_code')."
+    Write-PipelineTelemetryError -category 'InitializeToolset' "Failed to install dotnet SDK (exit code '$exit_code')."
     ExitWithExitCode $exit_code
   }
 }
@@ -349,7 +277,7 @@ function InitializeToolset {
   fi
 
   if [[ "$restore" != true ]]; then
-    EmitError "Toolset version $toolset_version has not been restored."
+    Write-PipelineTelemetryError -category 'InitializeToolset' "Toolset version $toolset_version has not been restored."
     ExitWithExitCode 2
   fi
 
@@ -366,7 +294,7 @@ function InitializeToolset {
   local toolset_build_proj=`cat "$toolset_location_file"`
 
   if [[ ! -a "$toolset_build_proj" ]]; then
-    EmitError "Invalid toolset path: $toolset_build_proj"
+    Write-PipelineTelemetryError -category 'InitializeToolset' "Invalid toolset path: $toolset_build_proj"
     ExitWithExitCode 3
   fi
 
@@ -404,12 +332,12 @@ function MSBuild {
 function MSBuild-Core {
   if [[ "$ci" == true ]]; then
     if [[ "$binary_log" != true ]]; then
-      EmitError "Binary log must be enabled in CI build."
+      Write-PipelineTaskError "Binary log must be enabled in CI build."
       ExitWithExitCode 1
     fi
 
     if [[ "$node_reuse" == true ]]; then
-      EmitError "Node reuse must be disabled in CI build."
+      Write-PipelineTaskError "Node reuse must be disabled in CI build."
       ExitWithExitCode 1
     fi
   fi
@@ -423,7 +351,7 @@ function MSBuild-Core {
 
   "$_InitializeBuildTool" "$_InitializeBuildToolCommand" /m /nologo /clp:Summary /v:$verbosity /nr:$node_reuse $warnaserror_switch /p:TreatWarningsAsErrors=$warn_as_error /p:ContinuousIntegrationBuild=$ci "$@" || {
     local exit_code=$?
-    EmitError "Build failed (exit code '$exit_code')."
+    Write-PipelineTaskError "Build failed (exit code '$exit_code')."
     ExitWithExitCode $exit_code
   }
 }
@@ -431,6 +359,8 @@ function MSBuild-Core {
 ResolvePath "${BASH_SOURCE[0]}"
 _script_dir=`dirname "$_ResolvePath"`
 
+. "$_script_dir/pipeline-logging-functions.sh"
+
 eng_root=`cd -P "$_script_dir/.." && pwd`
 repo_root=`cd -P "$_script_dir/../.." && pwd`
 artifacts_dir="$repo_root/artifacts"

+ 2 - 2
global.json

@@ -24,7 +24,7 @@
   },
   "msbuild-sdks": {
     "Yarn.MSBuild": "1.15.2",
-    "Microsoft.DotNet.Arcade.Sdk": "1.0.0-beta.19312.4",
-    "Microsoft.DotNet.Helix.Sdk": "2.0.0-beta.19312.4"
+    "Microsoft.DotNet.Arcade.Sdk": "1.0.0-beta.19323.4",
+    "Microsoft.DotNet.Helix.Sdk": "2.0.0-beta.19323.4"
   }
 }

+ 2 - 3
src/Components/Blazor/Build/test/RenderingRazorIntegrationTest.cs

@@ -483,15 +483,14 @@ namespace Microsoft.AspNetCore.Blazor.Build.Test
         {
             // Arrange
             var component = CompileToComponent(@"
-<button @onclick=""function(){console.log('hello');};"" />");
+<button onclick=""function(){console.log('hello');};"" />");
 
             // Act
             var frames = GetRenderTree(component);
 
             // Assert
             Assert.Collection(frames,
-                frame => AssertFrame.Element(frame, "button", 2, 0),
-                frame => AssertFrame.Attribute(frame, "onclick", "function(){console.log('hello');};", 1));
+                frame => AssertFrame.Markup(frame, "<button onclick=\"function(){console.log('hello');};\"></button>", 0));
         }
 
         [Fact]