diff --git a/.gitignore b/.gitignore index 47b49dcd01..736c050e6a 100644 --- a/.gitignore +++ b/.gitignore @@ -499,6 +499,4 @@ local.settings.json /tools/ExtensionsMetadataGenerator/test/ExtensionsMetadataGeneratorTests/runtimeAssemblies.txt local.settings.json -msbuild.binlog -pp.xml BenchmarkDotNet.Artifacts/ \ No newline at end of file diff --git a/CODEOWNERS b/CODEOWNERS index 851ea8b2f1..389a41fedf 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -8,9 +8,9 @@ * @azure/azure-functions-core # Adding codeowner for Python specific files such that GitHub automatically adds python folks as a reviewer. -build/python.props @vrdmr @gavin-aguiar @YunchuWang @pdthummar @hallvictoria +eng/build/Workers.Python.props @vrdmr @gavin-aguiar @YunchuWang @pdthummar @hallvictoria # Deps.json validation file test/WebJobs.Script.Tests/Microsoft.Azure.WebJobs.Script.WebHost.deps.json @fabiocav @brettsam @mathewc -src/WebJobs.Script.WebHost/PreJIT/* @vrdmr @pragnagopa @eliaslopezgt @VpOfEngineering @azure/azure-functions-core +src/WebJobs.Script.WebHost/PreJIT/* @vrdmr @pragnagopa @eliaslopezgt @azure/azure-functions-core diff --git a/Directory.Build.props b/Directory.Build.props index 207f1b398d..a82d4d8799 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -5,12 +5,21 @@ $(MSBuildThisFileDirectory)out pub pkg + false $(MSBuildThisFileDirectory) $(RepoRoot)eng/ + $(EngRoot)build/ + + + + + NU1901;NU1902;NU1903;NU1904 + moderate + all diff --git a/Directory.Build.targets b/Directory.Build.targets index 9c9f2a6f8f..adc05e884c 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -1,5 +1,5 @@ - + diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 2a77f90a7c..5402cd4769 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -67,20 +67,13 @@ jobs: - ImageOverride -equals MMS2019TLS steps: - template: build/install-dotnet.yml - - task: PowerShell@2 - displayName: "Build artifacts $(minorVersionPrefix)" - inputs: - filePath: '$(Build.Repository.LocalPath)\build\build-extensions.ps1' - arguments: '-buildNumber "$(buildNumber)" -suffix "$(suffix)" -minorVersionPrefix "$(minorVersionPrefix)"' + + - template: eng/ci/templates/steps/build-site-ext.yml + - task: PowerShell@2 displayName: "Check for security vulnerabilities" inputs: filePath: '$(Build.Repository.LocalPath)\build\check-vulnerabilities.ps1' - - task: CopyFiles@2 - inputs: - SourceFolder: 'out/pub/WebJobs.Script.WebHost' - Contents: '**/*.zip' - TargetFolder: '$(Build.ArtifactStagingDirectory)' - task: DotNetCoreCLI@2 displayName: 'Build host packages' @@ -274,11 +267,11 @@ jobs: - task: ManifestGeneratorTask@0 displayName: 'SBOM Generation Task - Symbols' inputs: - BuildDropPath: '$(Build.ArtifactStagingDirectory)\Symbols' + BuildDropPath: '$(Build.ArtifactStagingDirectory)\SiteExtensionSymbols' Verbosity: 'Information' - - publish: $(Build.ArtifactStagingDirectory)\Symbols - artifact: _Symbols.net$(minorVersionPrefix) + - publish: $(Build.ArtifactStagingDirectory)\SiteExtensionSymbols + artifact: Symbols - task: ManifestGeneratorTask@0 displayName: 'SBOM Generation Task - NugetPackages' inputs: diff --git a/build/build-extensions.ps1 b/build/build-extensions.ps1 index 30b6118a34..479c6b3e0e 100644 --- a/build/build-extensions.ps1 +++ b/build/build-extensions.ps1 @@ -1,227 +1 @@ -param ( - [string]$buildNumber = "0", - [string]$suffix = "", - [ValidateSet("6", "8", "")][string]$minorVersionPrefix = "", - [string]$hashesForHardlinksFile = "hashesForHardlinks.txt" -) - -Import-Module "$PSScriptRoot\Get-AzureFunctionsVersion.psm1" -Force -$rootDir = Split-Path -Parent $PSScriptRoot -$outDir = "$rootDir\out" -$publishDir = "$outDir\pub\WebJobs.Script.WebHost" - -$extensionVersion = Get-AzureFunctionsVersion $buildNumber $suffix $minorVersionPrefix -Write-Host "Site extension version: $extensionVersion" - -# Based on the minorVersionPrefix value, set target framework to be used for building/publishing -if ($minorVersionPrefix -eq "8") { - $targetFramework = "net8.0" -} else { - $targetFramework = "net6.0" -} - -function ZipContent([string] $sourceDirectory, [string] $target) { - $stopwatch = [System.Diagnostics.Stopwatch]::StartNew() - - Write-Host "======================================" - Write-Host "Zipping $sourceDirectory into $target" - - if (Test-Path $target) { - Remove-Item $target - } - - Add-Type -assembly "system.io.compression.filesystem" - [IO.Compression.ZipFile]::CreateFromDirectory($sourceDirectory, $target) - - Write-Host "Done zipping $target. Elapsed: $($stopwatch.Elapsed)" - Write-Host "======================================" - Write-Host "" -} - -function BuildRuntime([string] $targetRid, [bool] $isSelfContained) { - $stopwatch = [System.Diagnostics.Stopwatch]::StartNew() - - $publishTarget = "$publishDir\release_$targetFramework" + "_$targetRid" - - $projectPath = "$rootDir\src\WebJobs.Script.WebHost\WebJobs.Script.WebHost.csproj" - if (-not (Test-Path $projectPath)) - { - throw "Project path '$projectPath' does not exist." - } - - $cmd = "publish", $projectPath , "-r", "$targetRid", "-f", "$targetFramework", "--self-contained", "$isSelfContained", "-v", "m", "-c", "Release", "-p:IsPackable=false", "-p:BuildNumber=$buildNumber", "-p:MinorVersionPrefix=$minorVersionPrefix" - - Write-Host "======================================" - Write-Host "Building $targetRid for target framework $targetFramework" - Write-Host " Self-Contained: $isSelfContained" - Write-Host " Publish Directory: $publishTarget" - Write-Host "" - Write-Host "dotnet $cmd" - Write-Host "" - - & dotnet $cmd - - if ($LASTEXITCODE -ne 0) - { - exit $LASTEXITCODE - } - - Write-Host "" - $symbols = Get-ChildItem -Path $publishTarget -Filter *.pdb - $symbols += Get-ChildItem -Path "$publishTarget\workers\dotnet-isolated\*" -Include "*.pdb", "*.dbg" -Recurse - Write-Host "Zipping symbols: $($symbols.Count) symbols found" - - $symbolsPath = "$publishDir\Symbols" - if (!(Test-Path -PathType Container $symbolsPath)) { - New-Item -ItemType Directory -Path $symbolsPath | Out-Null - } - - $symbols | Compress-Archive -DestinationPath "$symbolsPath\Functions.Symbols.$extensionVersion.$targetRid.zip" | Out-Null - $symbols | Remove-Item | Out-Null - - Write-Host "" - CleanOutput $publishTarget - Write-Host "" - Write-Host "Done building $targetRid for target framework $targetFramework. Elapsed: $($stopwatch.Elapsed)" - Write-Host "======================================" - Write-Host "" -} - -function GetFolderSizeInMb([string] $rootPath) { - return [math]::Round((Get-ChildItem $rootPath -Recurse | Measure-Object -Property Length -Sum -ErrorAction Stop).Sum / 1Mb, 2) -} - -function CleanOutput([string] $rootPath) { - Write-Host "Cleaning build output under $rootPath" - Write-Host " Current size: $(GetFolderSizeInMb $rootPath) Mb" - - Write-Host " Removing any linux and osx runtimes" - Remove-Item -Recurse -Force "$privateSiteExtensionPath\$bitness\runtimes\linux" -ErrorAction SilentlyContinue - Remove-Item -Recurse -Force "$privateSiteExtensionPath\$bitness\runtimes\osx" -ErrorAction SilentlyContinue - - Write-Host " Removing python worker" - Remove-Item -Recurse -Force "$rootPath\workers\python" -ErrorAction SilentlyContinue - - $keepRuntimes = @('win', 'win-x86', 'win10-x86', 'win-x64', 'win10-x64') - Write-Host " Removing all powershell runtimes except $keepRuntimes" - Get-ChildItem "$rootPath\workers\powershell" -Directory -ErrorAction SilentlyContinue | - ForEach-Object { Get-ChildItem "$($_.FullName)\runtimes" -Directory -Exclude $keepRuntimes } | - Remove-Item -Recurse -Force -ErrorAction SilentlyContinue - - Write-Host " Removing FunctionsNetHost(linux executable) and dependencies from dotnet-isolated worker" - $dotnetIsolatedBinPath = Join-Path $rootPath "workers\dotnet-isolated\bin" - if (Test-Path $dotnetIsolatedBinPath) { - Remove-Item -Path (Join-Path $dotnetIsolatedBinPath "FunctionsNetHost") -ErrorAction SilentlyContinue - Get-ChildItem -Path $dotnetIsolatedBinPath -Filter "*.so" | Remove-Item -ErrorAction SilentlyContinue - } - - Write-Host " Current size: $(GetFolderSizeInMb $rootPath) Mb" -} - -function CreateSiteExtensions() { - $stopwatch = [System.Diagnostics.Stopwatch]::StartNew() - $siteExtensionPath = "$publishDir\temp_extension" - - if (Test-Path $siteExtensionPath) { - Write-Host " Existing site extension path found. Deleting." - Remove-Item $siteExtensionPath -Recurse -Force | Out-Null - } - - # The official site extension needs to be nested inside a folder with its version. - # Not using the suffix (eg: '-ci') here as it may not work correctly in a private stamp - $officialSiteExtensionPath = "$siteExtensionPath\$extensionVersion" - - Write-Host "======================================" - Write-Host "Copying build to temp directory to prepare for zipping official site extension." - Copy-Item -Path "$publishDir\release_$targetFramework`_win-x86\" -Destination "$officialSiteExtensionPath\32bit" -Force -Recurse > $null - Copy-Item -Path "$publishDir\release_$targetFramework`_win-x64\" -Destination "$officialSiteExtensionPath\64bit" -Force -Recurse > $null - Copy-Item -Path $officialSiteExtensionPath\32bit\applicationHost.xdt -Destination $officialSiteExtensionPath -Force > $null - Write-Host " Deleting workers directory: $officialSiteExtensionPath\32bit\workers" - Remove-Item -Recurse -Force "$officialSiteExtensionPath\32bit\workers" -ErrorAction SilentlyContinue - Write-Host " Moving workers directory: $officialSiteExtensionPath\64bit\workers to $officialSiteExtensionPath\workers" - Move-Item -Path "$officialSiteExtensionPath\64bit\workers" -Destination "$officialSiteExtensionPath\workers" - - # This goes in the root dir - Copy-Item $rootDir\src\WebJobs.Script.WebHost\extension.xml $siteExtensionPath > $null - - Write-Host "Done copying. Elapsed: $($stopwatch.Elapsed)" - Write-Host "======================================" - Write-Host "" - - Write-Host "======================================" - Write-Host "Generating hashes for hard links" - WriteHashesFile $siteExtensionPath/$extensionVersion - Write-Host "Done generating hashes for hard links into $siteExtensionPath/$extensionVersion" - Write-Host "======================================" - Write-Host - - $zipOutput = "$publishDir\SiteExtension" - $hashesForHardLinksPath = "$siteExtensionPath\$extensionVersion\$hashesForHardlinksFile" - New-Item -Itemtype directory -path $zipOutput -Force > $null - if ($minorVersionPrefix -eq "") { - ZipContent $siteExtensionPath "$zipOutput\Functions.$extensionVersion.zip" - } elseif ($minorVersionPrefix -eq "8") { - Write-Host "======================================" - # Only the "Functions" site extension supports hard links - Write-Host "MinorVersionPrefix is '8'. Removing $hashesForHardLinksPath before zipping." - Remove-Item -Force "$hashesForHardLinksPath" -ErrorAction Stop - # The .NET 8 host doesn't require any workers. Doing this to save space. - Write-Host "Removing workers before zipping." - # The host requires that this folder exists and it cannot be empty - Remove-Item -Recurse -Force "$siteExtensionPath\$extensionVersion\workers" -ErrorAction Stop - New-Item -Path "$siteExtensionPath\$extensionVersion" -Name "workers" -ItemType Directory -ErrorAction Stop | Out-Null - Set-Content -Force -Path "$siteExtensionPath\$extensionVersion\workers\this_folder_intentionally_empty.txt" -Value ".NET 8 builds do not have workers. However, this folder must contain at least one file." -ErrorAction Stop - Write-Host "======================================" - Write-Host - ZipContent $siteExtensionPath "$zipOutput\FunctionsInProc8.$extensionVersion.zip" - } elseif ($minorVersionPrefix -eq "6") { - # Only the "Functions" site extension supports hard links - Write-Host "======================================" - Write-Host "MinorVersionPrefix is '6'. Removing $hashesForHardLinksPath before zipping." - Remove-Item -Force "$hashesForHardLinksPath" -ErrorAction Stop - Write-Host "======================================" - Write-Host - ZipContent $siteExtensionPath "$zipOutput\FunctionsInProc.$extensionVersion.zip" - } - - Remove-Item $siteExtensionPath -Recurse -Force > $null - - Write-Host "======================================" - $stopwatch.Reset() - Write-Host "Copying build to temp directory to prepare for zipping private site extension." - Copy-Item -Path $publishDir\release_$targetFramework`_win-x86\ -Destination $siteExtensionPath\SiteExtensions\Functions\32bit -Force -Recurse > $null - Copy-Item -Path $siteExtensionPath\SiteExtensions\Functions\32bit\applicationHost.xdt -Destination $siteExtensionPath\SiteExtensions\Functions -Force > $null - Write-Host "Done copying. Elapsed: $($stopwatch.Elapsed)" - Write-Host "======================================" - Write-Host "" - - $zipOutput = "$publishDir\PrivateSiteExtension" - New-Item -Itemtype directory -path $zipOutput -Force > $null - ZipContent $siteExtensionPath "$zipOutput\Functions.Private.$extensionVersion.win-x32.inproc.zip" - - Remove-Item $siteExtensionPath -Recurse -Force > $null -} - -function WriteHashesFile([string] $directoryPath) { - New-Item -Path "$directoryPath/../temp_hashes" -ItemType Directory | Out-Null - $temp_current = (Get-Location) - Set-Location $directoryPath - Get-ChildItem -Recurse $directoryPath | Where-Object { $_.PsIsContainer -eq $false } | Foreach-Object { "Hash:" + [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes((Get-FileHash -Algorithm MD5 $_.FullName).Hash)) + " FileName:" + (Resolve-Path -Relative -Path $_.FullName) } | Out-File -FilePath "$directoryPath\..\temp_hashes\$hashesForHardlinksFile" - Move-Item -Path "$directoryPath/../temp_hashes/$hashesForHardlinksFile" -Destination "$directoryPath" -Force - Set-Location $temp_current - Remove-Item "$directoryPath/../temp_hashes" -Recurse -Force > $null -} - -Write-Host "Output directory: $publishDir" -if (Test-Path $publishDir) { - Write-Host " Existing build output found. Deleting." - Remove-Item $publishDir -Recurse -Force -ErrorAction Stop -} - -Write-Host "Extensions version: $extensionVersion" -Write-Host "" - -BuildRuntime "win-x86" -BuildRuntime "win-x64" - -CreateSiteExtensions +Write-Error "This script is no longer used. Instead, publish src/WebJobs.Script.SiteExtension/WebJobs.Script.SiteExtension.proj directly." diff --git a/build/common.props b/build/common.props index 46363b9e31..80b1bcd39d 100644 --- a/build/common.props +++ b/build/common.props @@ -1,4 +1,5 @@ + latest @@ -7,12 +8,12 @@ 0 0 - + <_VersionSuffix Condition="'$(PreviewVersion)' != ''">-preview.$(PreviewVersion).$(BuildNumber) <_VersionSuffix Condition="'$(PreviewVersion)' == '' and '$(VersionSuffix)' != ''">-$(VersionSuffix) - + $(MajorVersion).$(MinorVersion).$(PatchVersion) $(VersionPrefix)$(_VersionSuffix) $(MajorVersion).$(MinorVersion).0.0 diff --git a/build/python.props b/build/python.props index a9038ec9e1..ebfd61c498 100644 --- a/build/python.props +++ b/build/python.props @@ -1,5 +1,6 @@ - - - + + + + diff --git a/build/update-hostreferences.ps1 b/build/update-hostreferences.ps1 deleted file mode 100644 index 07fedd72a0..0000000000 --- a/build/update-hostreferences.ps1 +++ /dev/null @@ -1,171 +0,0 @@ -function WriteLog -{ - param ( - [Parameter(Mandatory=$true)] - [ValidateNotNullOrEmpty()] - [System.String] - $Message, - - [Switch] - $Throw - ) - - $Message = (Get-Date -Format G) + " -- $Message" - - if ($Throw) - { - throw $Message - } - - Write-Host $Message -} - -Class PackageInfo { - [string]$Name - [string]$Version -} - -function NewPackageInfo -{ - param ( - [Parameter(Mandatory=$true)] - [ValidateNotNullOrEmpty()] - [System.String] - $PackageInformation - ) - - $parts = $PackageInformation.Split(" ") - - if ($parts.Count -gt 2) - { - WriteLog "Invalid package format. The string should only contain 'nameversion'. Current value: '$PackageInformation'" - } - - $packageInfo = [PackageInfo]::New() - $packageInfo.Name = $parts[0] - $packageInfo.Version = $parts[1] - - return $packageInfo -} - -function GetPackageInfo -{ - param ( - [Parameter(Mandatory=$true)] - [ValidateNotNullOrEmpty()] - [System.String] - $Name, - - [Parameter(Mandatory=$false)] - [System.String] - $MajorVersion - ) - - $result = $null - $includeAllVersion = if (-not [string]::IsNullOrWhiteSpace($MajorVersion)) { "-AllVersions" } else { "" } - - $packageInfo = & { NuGet list $Name -Source $SOURCE -PreRelease $includeAllVersion } - - if ($packageInfo -like "*No packages found*") - { - WriteLog "Package name $Name not found in $SOURCE." -Throw - } - - if (-not $MajorVersion) - { - $result = NewPackageInfo -PackageInformation $packageInfo - } - else - { - foreach ($thisPackage in $packageInfo.Split([System.Environment]::NewLine)) - { - $package = NewPackageInfo -PackageInformation $thisPackage - - if ($package.Version.StartsWith($MajorVersion)) - { - $result = $package - break - } - } - } - - return $result -} - -WriteLog "Script started." - -# Make sure the project path exits -$path = "$PSScriptRoot\..\src\WebJobs.Script" -if (-not (Test-Path $path)) -{ - WriteLog "Failed to find '$path' to update package references" -Throw -} - -$URL = "https://raw.githubusercontent.com/Azure/azure-functions-integration-tests/main/integrationTestsBuild/V4/HostBuild.json" -$SOURCE = "https://azfunc.pkgs.visualstudio.com/e6a70c92-4128-439f-8012-382fe78d6396/_packaging/AzureFunctionsPreRelease/nuget/v3/index.json" - -WriteLog "Get the list of packages to update" - -$packagesToUpdate = Invoke-RestMethod -Uri $URL -ErrorAction Stop -if ($packagesToUpdate.Count -eq 0) -{ - WriteLog "There are no packages to update in '$URL'" -Throw -} - -# Update packages references -WriteLog "Package references to update: $($packagesToUpdate.Count)" - -$currentDirectory = Get-Location -try -{ - set-location $path - - foreach ($package in $packagesToUpdate) - { - $packageInfo = GetPackageInfo -Name $package.Name -MajorVersion $package.MajorVersion - - if ($package.Name -eq "Microsoft.Azure.Functions.PythonWorker") - { - # The PythonWorker is not defined in the src/WebJobs.Script/WebJobs.Script.csproj. It is defined in build/python.props. - # To update the package version, the xml file build/python.props needs to be updated directly. - $pythonPropsFilePath = "$PSScriptRoot\python.props" - - if (-not (Test-Path $pythonPropsFilePath)) - { - WriteLog "Python Props file '$pythonPropsFilePath' does not exist." -Throw - } - - WriteLog "Set Python package version in '$pythonPropsFilePath' to '$($packageInfo.Version)'" - - # Read the xml file - [xml]$xml = Get-Content $pythonPropsFilePath -Raw -ErrorAction Stop - - # Replace the package version - $xml.Project.ItemGroup.PackageReference.Version = $packageInfo.Version - - # Save the file - $xml.Save($pythonPropsFilePath) - - if ($LASTEXITCODE -ne 0) - { - WriteLog "Failed to update Python Props file" -Throw - } - } - else - { - WriteLog "Adding '$($packageInfo.Name)' '$($packageInfo.Version)' to project" - & { dotnet add package $packageInfo.Name -v $packageInfo.Version -s $SOURCE --no-restore } - - if ($LASTEXITCODE -ne 0) - { - WriteLog "dotnet add package '$($packageInfo.Name)' -v '$($packageInfo.Version)' -s $SOURCE --no-restore failed" -Throw - } - } - } -} -finally -{ - Set-Location $currentDirectory -} - -WriteLog "Script completed." \ No newline at end of file diff --git a/eng/build/Engineering.targets b/eng/build/Engineering.targets index 2e4906c2e9..5f8423e5f3 100644 --- a/eng/build/Engineering.targets +++ b/eng/build/Engineering.targets @@ -1,5 +1,6 @@ + diff --git a/eng/build/Workers.Dotnet.props b/eng/build/Workers.Dotnet.props new file mode 100644 index 0000000000..2637018a77 --- /dev/null +++ b/eng/build/Workers.Dotnet.props @@ -0,0 +1,15 @@ + + + + + + + + + + <_DotnetWorkerFiles Include="@(None)" Condition="'%(None.DestinationSubDirectory)' == 'workers\dotnet-isolated\bin\'" /> + + + + + diff --git a/eng/build/Workers.Java.props b/eng/build/Workers.Java.props new file mode 100644 index 0000000000..1a2aeb9f19 --- /dev/null +++ b/eng/build/Workers.Java.props @@ -0,0 +1,7 @@ + + + + + + + diff --git a/eng/build/Workers.Node.props b/eng/build/Workers.Node.props new file mode 100644 index 0000000000..961f06b52e --- /dev/null +++ b/eng/build/Workers.Node.props @@ -0,0 +1,7 @@ + + + + + + + diff --git a/eng/build/Workers.Powershell.props b/eng/build/Workers.Powershell.props new file mode 100644 index 0000000000..cb60560979 --- /dev/null +++ b/eng/build/Workers.Powershell.props @@ -0,0 +1,29 @@ + + + + + + + + + + + <_KeepPowerShellRuntime Include="win;win-x86;win10-x86;win-x64;win10-x64" /> + + + + + <_PowershellRuntimesToKeepRegex>@(_KeepPowerShellRuntime->'%(Identity)(/|\\)', '|') + <_PowershellRuntimesToRemoveRegex>^workers(/|\\)powershell(/|\\).*(/|\\)runtimes(/|\\)(?!$(_PowershellRuntimesToKeepRegex)) + + + + <_PowershellRuntimeToRemove Include="@(None)" Condition="'%(None.TargetPath)' != '' AND $([System.Text.RegularExpressions.Regex]::IsMatch('%(None.TargetPath)', $(_PowershellRuntimesToRemoveRegex)))" /> + + + + diff --git a/eng/build/Workers.Python.props b/eng/build/Workers.Python.props new file mode 100644 index 0000000000..15ed2797fd --- /dev/null +++ b/eng/build/Workers.Python.props @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/eng/build/Workers.props b/eng/build/Workers.props new file mode 100644 index 0000000000..047a7a0860 --- /dev/null +++ b/eng/build/Workers.props @@ -0,0 +1,21 @@ + + + + + + + + + <_WorkerPublishFiles Include="@(ResolvedFileToPublish)" Condition="$([System.String]::new('%(ResolvedFileToPublish.TargetPath)').StartsWith('workers'))" /> + + + + + + + + + + + + diff --git a/eng/build/ZipPublish.targets b/eng/build/ZipPublish.targets new file mode 100644 index 0000000000..c89459077a --- /dev/null +++ b/eng/build/ZipPublish.targets @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + $(PackageOutputPath) + $([MSBuild]::EnsureTrailingSlash('$(ZipArtifactsPath)')) + <_RuntimeIdentifierWithPeriod Condition="'$(RuntimeIdentifier)' != ''">.$(RuntimeIdentifier.ToLowerInvariant()) + + + + + + + + + + + + + $([MSBuild]::EnsureTrailingSlash('$(ZipArtifactsPath)'))%(TargetName) + + + + + + <_ZipArtifactTargetPath Include="@(ZipArtifact->'%(TargetPath)')" /> + <_ZipArtifactTargetDirectories Include="@(_ZipArtifactTargetPath->'%(RootDir)%(Directory)'->Distinct())" /> + + + + + + + + + + diff --git a/eng/ci/integration-tests.yml b/eng/ci/integration-tests.yml index baeed4d535..3788853969 100644 --- a/eng/ci/integration-tests.yml +++ b/eng/ci/integration-tests.yml @@ -20,8 +20,10 @@ resources: ref: refs/tags/release variables: - - template: /eng/ci/templates/variables/build.yml@self - - template: /ci/variables/cfs.yml@eng +- template: /eng/ci/templates/variables/build.yml@self +- template: /ci/variables/cfs.yml@eng +- name: buildNumber + value: $[ counter('build', 0) ] extends: template: v1/1ES.Unofficial.PipelineTemplate.yml@1es diff --git a/eng/ci/public-build.yml b/eng/ci/public-build.yml index 459b5ab4f7..f1e3365996 100644 --- a/eng/ci/public-build.yml +++ b/eng/ci/public-build.yml @@ -25,7 +25,9 @@ resources: ref: refs/tags/release variables: - - template: /eng/ci/templates/variables/build.yml@self +- template: /eng/ci/templates/variables/build.yml@self +- name: buildNumber + value: $[ counter('build', 0) ] extends: template: v1/1ES.Unofficial.PipelineTemplate.yml@1es diff --git a/eng/ci/templates/official/jobs/build-artifacts-linux.yml b/eng/ci/templates/official/jobs/build-artifacts-linux.yml index 5ef1f39aa7..ef3d726708 100644 --- a/eng/ci/templates/official/jobs/build-artifacts-linux.yml +++ b/eng/ci/templates/official/jobs/build-artifacts-linux.yml @@ -6,18 +6,19 @@ jobs: project: src/WebJobs.Script.WebHost/WebJobs.Script.WebHost.csproj configuration: release runtime: linux-x64 - log_dir: $(Build.ArtifactStagingDirectory)/log - build_args: '-v m -c $(configuration) -r $(runtime) --self-contained true -p:BuildNumber=$(buildNumber) -p:IsPackable=false' - publish_zip_dir: $(Build.ArtifactStagingDirectory)/Linux + artifacts_path: $(Build.ArtifactStagingDirectory) + log_dir: $(artifacts_path)/log + zip_artifacts_path: $(artifacts_path)/Linux + build_args: '-v m -c $(configuration) -r $(runtime) --self-contained true -p:BuildNumber=$(buildNumber)' templateContext: - outputParentDirectory: $(Build.ArtifactStagingDirectory) + outputParentDirectory: $(artifacts_path) outputs: # TODO: onboard to Azure Artifacts Drops to allow accessing this from docker linux pipeline in msazure # https://eng.ms/docs/cloud-ai-platform/devdiv/one-engineering-system-1es/1es-docs/azure-artifacts/artifact-services-onboarding - output: pipelineArtifact displayName: Publish linux artifacts - path: $(publish_zip_dir) + path: $(zip_artifacts_path) artifact: Linux - output: pipelineArtifact displayName: Publish logs @@ -34,22 +35,6 @@ jobs: steps: - template: /eng/ci/templates/install-dotnet.yml@self - - task: DotNetCoreCLI@2 - displayName: Restore - inputs: - command: custom - custom: restore - projects: $(project) - arguments: '-v m -r $(runtime) -p:PublishReadyToRun=true -bl:$(log_dir)/restore.binlog' # add PublishReadyToRun=true for https://github.com/dotnet/sdk/issues/20701 - - - task: DotNetCoreCLI@2 - displayName: Build - inputs: - command: custom - custom: build - projects: $(project) - arguments: '$(build_args) --no-restore -bl:$(log_dir)/build.binlog' - - task: DotNetCoreCLI@2 displayName: Publish (net6.0) inputs: @@ -58,7 +43,7 @@ jobs: publishWebProjects: false # we use our own publish logic zipAfterPublish: false # we use our own zip logic projects: $(project) - arguments: '$(build_args) -f net6.0 --no-build -p:PublishZipDir=$(publish_zip_dir) -bl:$(log_dir)/publish.net6.binlog' + arguments: '$(build_args) -f net6.0 -p:MinorVersionPrefix=6 -p:ZipArtifactsPath=$(zip_artifacts_path) -bl:$(log_dir)/publish.net6.binlog' - task: DotNetCoreCLI@2 displayName: Publish (net8.0) @@ -68,4 +53,4 @@ jobs: publishWebProjects: false # we use our own publish logic zipAfterPublish: false # we use our own zip logic projects: $(project) - arguments: '$(build_args) -f net8.0 --no-build -p:PublishZipDir=$(publish_zip_dir) -bl:$(log_dir)/publish.net8.binlog' + arguments: '$(build_args) -f net8.0 -p:MinorVersionPrefix=8 -p:ZipArtifactsPath=$(zip_artifacts_path) -bl:$(log_dir)/publish.net8.binlog' diff --git a/eng/ci/templates/official/jobs/build-artifacts-windows.yml b/eng/ci/templates/official/jobs/build-artifacts-windows.yml index b00cc53fe8..01f32e6d27 100644 --- a/eng/ci/templates/official/jobs/build-artifacts-windows.yml +++ b/eng/ci/templates/official/jobs/build-artifacts-windows.yml @@ -2,6 +2,20 @@ jobs: - job: BuildArtifactsWindows displayName: Build Windows Artifacts + pool: + name: 1es-pool-azfunc + image: 1es-windows-2022 + os: windows + + variables: + ${{ if or( eq( variables['Build.Reason'], 'PullRequest' ), and( not( contains( variables['Build.SourceBranch'], 'release/inproc6/4.' ) ), not( contains( variables['Build.SourceBranch'], 'release/inproc8/4.' ) ), not( contains( variables['Build.SourceBranch'], 'release/4.' ) ), not( contains( variables['Build.SourceBranch'], 'release/ExtensionsMetadataGenerator/' ) ) ) ) }}: + packSuffixSwitchTemp: --version-suffix $(buildNumber) + emgSuffixSwitchTemp: --version-suffix ci$(buildNumber) + packSuffixSwitch: $[variables.packSuffixSwitchTemp] + emgSuffixSwitch: $[variables.emgSuffixSwitchTemp] + nuget_package_path: $(Build.ArtifactStagingDirectory)/NugetPackages + log_dir: $(Build.ArtifactStagingDirectory)/log + templateContext: outputParentDirectory: $(Build.ArtifactStagingDirectory) outputs: @@ -15,72 +29,35 @@ jobs: artifact: PrivateSiteExtension - output: pipelineArtifact displayName: Publish site extension symbols - path: $(Build.ArtifactStagingDirectory)/Symbols + path: $(Build.ArtifactStagingDirectory)/SiteExtensionSymbols artifact: Symbols - output: pipelineArtifact displayName: Publish nuget packages path: $(Build.ArtifactStagingDirectory)/NugetPackages artifact: NugetPackages - - pool: - name: 1es-pool-azfunc - image: 1es-windows-2022 - os: windows - - variables: - ${{ if or( eq( variables['Build.Reason'], 'PullRequest' ), and(not( contains( variables['Build.SourceBranch'], 'release/in-proc' ) ), not( contains( variables['Build.SourceBranch'], 'release/4.' ) ), not( contains( variables['Build.SourceBranch'], 'release/ExtensionsMetadataGenerator/' ) ) ) ) }}: - suffixTemp: $(buildNumber) - packSuffixSwitchTemp: --version-suffix $(buildNumber) - emgSuffixSwitchTemp: --version-suffix ci$(buildNumber) - suffix: $[variables.suffixTemp] # this resolves to an empty string if it is missing - packSuffixSwitch: $[variables.packSuffixSwitchTemp] - emgSuffixSwitch: $[variables.emgSuffixSwitchTemp] + - output: pipelineArtifact + displayName: Publish logs + path: $(log_dir) + artifact: Windows_Log + sbomEnabled: false + condition: always() steps: - template: /eng/ci/templates/install-dotnet.yml@self + - template: /eng/ci/templates/steps/build-site-ext.yml@self - - task: PowerShell@2 - displayName: Build artifacts - inputs: - filePath: build/build-extensions.ps1 - arguments: '-buildNumber "$(buildNumber)" -suffix "$(suffix)"' - - - task: CopyFiles@2 - inputs: - SourceFolder: out/pub/WebJobs.Script.WebHost - Contents: '**/*.zip' - TargetFolder: $(Build.ArtifactStagingDirectory) - - - task: PowerShell@2 - displayName: Build artifacts (.NET 6) - inputs: - filePath: build/build-extensions.ps1 - arguments: '-buildNumber "$(buildNumber)" -suffix "$(suffix)" -minorVersionPrefix "6"' - - - task: CopyFiles@2 - inputs: - SourceFolder: out/pub/WebJobs.Script.WebHost - Contents: '**/*.zip' - TargetFolder: $(Build.ArtifactStagingDirectory) - - - task: PowerShell@2 - displayName: Build artifacts (.NET 8) - inputs: - filePath: build/build-extensions.ps1 - arguments: '-buildNumber "$(buildNumber)" -suffix "$(suffix)" -minorVersionPrefix "8"' - - - task: CopyFiles@2 - inputs: - SourceFolder: out/pub/WebJobs.Script.WebHost - Contents: '**/*.zip' - TargetFolder: $(Build.ArtifactStagingDirectory) + # TODO: Keeping in-proc branch building OOP site extension for now, will remove eventually. + - template: /eng/ci/templates/steps/build-site-ext.yml@self + parameters: + extraBuildArgs: -f net6.0 -p:SuppressMinorVersionPrefix=true -p:SiteExtensionName=Functions -p:ShouldWriteHardLinkHashes=true + binlogPrefix: site_ext_oop - task: DotNetCoreCLI@2 displayName: Build host packages inputs: command: custom custom: pack - arguments: -p:BuildNumber=$(buildNumber) -c release $(packSuffixSwitch) + arguments: -p:BuildNumber=$(buildNumber) -c release $(packSuffixSwitch) -o $(nuget_package_path) projects: | **/WebJobs.Script.csproj **/WebJobs.Script.WebHost.csproj @@ -102,17 +79,12 @@ jobs: pattern: Microsoft.Azure.WebJobs.Script.Abstractions*.dll signType: dll - - task: DeleteFiles@1 - displayName: Delete CodeSignSummary files - inputs: - contents: '**/CodeSignSummary-*.md' - - task: DotNetCoreCLI@2 displayName: Pack Abstractions inputs: command: custom custom: pack - arguments: '--no-build -c release' + arguments: '--no-build -c release -o $(nuget_package_path)' projects: | **/WebJobs.Script.Abstractions.csproj @@ -123,24 +95,19 @@ jobs: pattern: Microsoft.Azure.WebJobs.Script.ExtensionsMetadataGenerator*.dll signType: dll-strong-name - - task: DeleteFiles@1 - displayName: Delete CodeSignSummary files - inputs: - contents: '**/CodeSignSummary-*.md' - - task: DotNetCoreCLI@2 displayName: Pack ExtensionsMetadataGenerator inputs: command: custom custom: pack - arguments: '--no-build -c release $(emgSuffixSwitch)' + arguments: '--no-build -c release $(emgSuffixSwitch) -o $(nuget_package_path)' projects: | **/ExtensionsMetadataGenerator.csproj - template: ci/sign-files.yml@eng parameters: displayName: Sign NugetPackages - folderPath: out/pkg/release + folderPath: $(nuget_package_path) pattern: '*.nupkg' signType: nuget @@ -149,8 +116,7 @@ jobs: inputs: contents: '**/CodeSignSummary-*.md' - - task: CopyFiles@2 + - task: DeleteFiles@1 + displayName: Delete CodeSignSummary files inputs: - SourceFolder: out/pkg/release - Contents: '**/*.nupkg' - TargetFolder: $(Build.ArtifactStagingDirectory)/NugetPackages + contents: '$(nuget_package_path)/**/CodeSignSummary-*.md' diff --git a/eng/ci/templates/steps/build-site-ext.yml b/eng/ci/templates/steps/build-site-ext.yml new file mode 100644 index 0000000000..6e3cb8fb30 --- /dev/null +++ b/eng/ci/templates/steps/build-site-ext.yml @@ -0,0 +1,31 @@ +parameters: + - name: project + type: string + default: src/WebJobs.Script.SiteExtension/WebJobs.Script.SiteExtension.csproj + - name: extraBuildArgs + type: string + default: '' + - name: binLogPrefix + type: string + default: site_ext + +steps: + - task: DotNetCoreCLI@2 + displayName: Publish site extension + inputs: + command: custom + custom: publish + publishWebProjects: false # we use our own publish logic + zipAfterPublish: false # we use our own zip logic + projects: ${{ parameters.project }} + arguments: '-v m -c release -f net6.0 -p:MinorVersionPrefix=6 -p:BuildNumber=$(buildNumber) -p:ZipArtifactsPath=$(Build.ArtifactStagingDirectory) -bl:$(log_dir)/${{ parameters.binLogPrefix }}.publish.binlog ${{ parameters.extraBuildArgs }}' + + - task: DotNetCoreCLI@2 + displayName: Publish site extension + inputs: + command: custom + custom: publish + publishWebProjects: false # we use our own publish logic + zipAfterPublish: false # we use our own zip logic + projects: ${{ parameters.project }} + arguments: '-v m -c release -f net8.0 -p:MinorVersionPrefix=8 -p:BuildNumber=$(buildNumber) -p:ZipArtifactsPath=$(Build.ArtifactStagingDirectory) -bl:$(log_dir)/${{ parameters.binLogPrefix }}.publish.binlog ${{ parameters.extraBuildArgs }}' diff --git a/src/Directory.Build.props b/src/Directory.Build.props new file mode 100644 index 0000000000..3fb289897c --- /dev/null +++ b/src/Directory.Build.props @@ -0,0 +1,10 @@ + + + + + + true + + + diff --git a/src/WebJobs.Script.SiteExtension/Publish.MultiTFM.targets b/src/WebJobs.Script.SiteExtension/Publish.MultiTFM.targets new file mode 100644 index 0000000000..e66bc0bc5d --- /dev/null +++ b/src/WebJobs.Script.SiteExtension/Publish.MultiTFM.targets @@ -0,0 +1,23 @@ + + + + + + + + + + + <_TargetFramework Include="$(TargetFrameworks)" /> + + <_TargetFrameworkNormalized Include="@(_TargetFramework->Trim()->Distinct())" /> + <_InnerBuildProjects Include="$(MSBuildProjectFile)"> + TargetFramework=%(_TargetFrameworkNormalized.Identity) + + + + + + diff --git a/src/WebJobs.Script.SiteExtension/Publish.SingleTFM.targets b/src/WebJobs.Script.SiteExtension/Publish.SingleTFM.targets new file mode 100644 index 0000000000..7137585c65 --- /dev/null +++ b/src/WebJobs.Script.SiteExtension/Publish.SingleTFM.targets @@ -0,0 +1,168 @@ + + + + + + + + + ValidatePublishSettings; + PublishProjectReferences; + PublishPrivateProjectReferences; + RemoveUnneededRuntimes; + MoveSymbols; + DeletePrivateSymbols; + WriteHardLinkHashes; + + ZipAfterPublish;ZipArtifactsPath + + + + + + + + + + + SiteExtension/$(Version)/ + $([MSBuild]::NormalizePath('$(PublishDir)$(SiteExtensionRelativeDir)')) + PrivateSiteExtension/SiteExtensions/Functions/ + $([MSBuild]::NormalizePath('$(PublishDir)$(PrivateSiteExtensionRelativeDir)')) + + + + + + + + + + + + + $(SiteExtensionRelativeDir)%(None.TargetPath) + + + + + + + <_ProjectReferenceWithRuntimes Include="@(ProjectReference)"> + + + RuntimeIdentifier=%(PublishRuntimeIdentifier.Identity); + SelfContained=%(PublishRuntimeIdentifier.SelfContained) + + %(PublishRuntimeIdentifier.Identity) + $(SiteExtensionDir)%(PublishRuntimeIdentifier.PublishDir)/ + $(PrivateSiteExtensionDir)%(PublishRuntimeIdentifier.PublishDir)/ + %(PublishRuntimeIdentifier.PrivateExtension) + false + + + + + + + + + + + + <_PublishProjectReferenceExistent Include="@(_MSBuildProjectReferenceExistent)"> + %(AdditionalProperties);PublishDir=%(PublishDir);_IsPublishing=true;PublishWorkers=false + + + + + + + + + + <_PublishPrivateProjectReferenceExistent Include="@(_MSBuildProjectReferenceExistent)" Condition="%(PublishPrivate)"> + %(AdditionalProperties);PublishDir=%(PublishPrivateDir);_IsPublishing=true + + + + + + + + + + + <_RuntimesToRemove Include="@(PublishRuntimeIdentifier->'$(SiteExtensionDir)%(PublishDir)/runtimes/linux')" /> + <_RuntimesToRemove Include="@(PublishRuntimeIdentifier->'$(SiteExtensionDir)%(PublishDir)/runtimes/osx')" /> + <_RuntimesToRemove Include="@(PublishRuntimeIdentifier->'$(PrivateSiteExtensionDir)%(PublishDir)/runtimes/linux')" Condition="%(PrivateExtension)" /> + <_RuntimesToRemove Include="@(PublishRuntimeIdentifier->'$(PrivateSiteExtensionDir)%(PublishDir)/runtimes/osx')" Condition="%(PrivateExtension)" /> + + + + + + + + + + + + + <_SymbolDirs Include="@(PublishRuntimeIdentifier->'$(SiteExtensionDir)%(PublishDir)')"> + $(PublishDir)Symbols/$(SiteExtensionName).Symbols.$(Version).%(Identity) + $(SiteExtensionName).Symbols.$(Version).%(Identity).zip + + <_WorkerSymbols Include="$(SiteExtensionDir)workers/**/*.pdb" Destination="$(PublishDir)Symbols/$(SiteExtensionName).Symbols.$(Version).%(PublishRuntimeIdentifier.Identity)/workers" /> + + + + + + + + + + + <_PrivateSymbolsToRemove Include="$(PrivateSiteExtensionDir)/**/*.pdb" /> + + + + + + + + <_FilesToHash Include="$(SiteExtensionDir)**" /> + + + + + + + + + + <_HashedFiles RelativePath=".$([System.IO.Path]::DirectorySeparatorChar)$([MSBuild]::MakeRelative('$(SiteExtensionDir)', '%(Identity)'))" /> + + + + + diff --git a/src/WebJobs.Script.SiteExtension/Tasks.targets b/src/WebJobs.Script.SiteExtension/Tasks.targets new file mode 100644 index 0000000000..67f3ef84c7 --- /dev/null +++ b/src/WebJobs.Script.SiteExtension/Tasks.targets @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/WebJobs.Script.SiteExtension/WebJobs.Script.SiteExtension.csproj b/src/WebJobs.Script.SiteExtension/WebJobs.Script.SiteExtension.csproj new file mode 100644 index 0000000000..0dcf7067b0 --- /dev/null +++ b/src/WebJobs.Script.SiteExtension/WebJobs.Script.SiteExtension.csproj @@ -0,0 +1,36 @@ + + + + + + + net6.0;net8.0 + win + $(MSBuildThisFileDirectory)Publish.SingleTFM.targets + $(MSBuildThisFileDirectory)Publish.MultiTFM.targets + false + false + + + + FunctionsInProc + + + + FunctionsInProc8 + + + + + + + + + + + + + + + + diff --git a/src/WebJobs.Script.SiteExtension/applicationHost.xdt b/src/WebJobs.Script.SiteExtension/applicationHost.xdt new file mode 100644 index 0000000000..1344402c99 --- /dev/null +++ b/src/WebJobs.Script.SiteExtension/applicationHost.xdt @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/WebJobs.Script.SiteExtension/extension.xml b/src/WebJobs.Script.SiteExtension/extension.xml new file mode 100644 index 0000000000..46338aa98a --- /dev/null +++ b/src/WebJobs.Script.SiteExtension/extension.xml @@ -0,0 +1,3 @@ + + disabled + diff --git a/src/WebJobs.Script.WebHost/WebJobs.Script.WebHost.csproj b/src/WebJobs.Script.WebHost/WebJobs.Script.WebHost.csproj index 3b60175c76..eb0a61a0d9 100644 --- a/src/WebJobs.Script.WebHost/WebJobs.Script.WebHost.csproj +++ b/src/WebJobs.Script.WebHost/WebJobs.Script.WebHost.csproj @@ -1,6 +1,8 @@  + - + + net8.0;net6.0 Microsoft.Azure.WebJobs.Script.WebHost @@ -12,6 +14,7 @@ NU5104 6.35.0 7.1.2 + win-x86;win-x64;linux-x64 true @@ -58,10 +61,6 @@ - - - - @@ -147,42 +146,4 @@ - - - - - - - - - - - - - - - - - - - - - - - $(PublishDir).. - $(PublishZipDir)\$(AssemblyName).$(Version).$(ArtifactsPivots).zip - - - - - - diff --git a/src/WebJobs.Script/WebJobs.Script.csproj b/src/WebJobs.Script/WebJobs.Script.csproj index 66bb89ed0e..1f93635960 100644 --- a/src/WebJobs.Script/WebJobs.Script.csproj +++ b/src/WebJobs.Script/WebJobs.Script.csproj @@ -54,26 +54,16 @@ - + - - - - - - - - - - - + diff --git a/test/WebJobs.Script.Abstractions/WebJobs.Script.Tests.Abstractions.csproj b/test/WebJobs.Script.Abstractions/WebJobs.Script.Tests.Abstractions.csproj index 9de7521cff..228cc25690 100644 --- a/test/WebJobs.Script.Abstractions/WebJobs.Script.Tests.Abstractions.csproj +++ b/test/WebJobs.Script.Abstractions/WebJobs.Script.Tests.Abstractions.csproj @@ -3,7 +3,6 @@ net6.0 true - false Microsoft.Azure.WebJobs.Script.Tests.Abstractions Microsoft.Azure.WebJobs.Script.Tests.Abstractions diff --git a/test/WebJobs.Script.Tests.Analyzers/WebJobs.Script.Tests.Analyzers.csproj b/test/WebJobs.Script.Tests.Analyzers/WebJobs.Script.Tests.Analyzers.csproj index b5c212cf3a..817bc56b79 100644 --- a/test/WebJobs.Script.Tests.Analyzers/WebJobs.Script.Tests.Analyzers.csproj +++ b/test/WebJobs.Script.Tests.Analyzers/WebJobs.Script.Tests.Analyzers.csproj @@ -2,11 +2,7 @@ net6.0 - - false - true - key.snk diff --git a/test/WebJobs.Script.Tests.Integration/WebJobs.Script.Tests.Integration.csproj b/test/WebJobs.Script.Tests.Integration/WebJobs.Script.Tests.Integration.csproj index 0cc05bbacf..e179a96800 100644 --- a/test/WebJobs.Script.Tests.Integration/WebJobs.Script.Tests.Integration.csproj +++ b/test/WebJobs.Script.Tests.Integration/WebJobs.Script.Tests.Integration.csproj @@ -1,9 +1,7 @@ - net8.0;net6.0 - false Microsoft.Azure.WebJobs.Script.Tests.Integration Microsoft.Azure.WebJobs.Script.Tests.Integration @@ -20,6 +18,9 @@ true + + + @@ -40,9 +41,7 @@ - - diff --git a/test/WebJobs.Script.Tests/WebJobs.Script.Tests.csproj b/test/WebJobs.Script.Tests/WebJobs.Script.Tests.csproj index d2679549ff..be07545a24 100644 --- a/test/WebJobs.Script.Tests/WebJobs.Script.Tests.csproj +++ b/test/WebJobs.Script.Tests/WebJobs.Script.Tests.csproj @@ -3,7 +3,6 @@ net8.0;net6.0 true - false Microsoft.Azure.WebJobs.Script.Tests Microsoft.Azure.WebJobs.Script.Tests preview