diff --git a/azure-pipelines-official.yml b/azure-pipelines-official.yml index 91df7e50c4dd0..cfe38a4a3fa3f 100644 --- a/azure-pipelines-official.yml +++ b/azure-pipelines-official.yml @@ -9,7 +9,6 @@ resources: # SkipTests: false # SkipApplyOptimizationData: false # IbcDrop: 'default' -# PRNumber: 'default' # The variables `_DotNetArtifactsCategory` and `_DotNetValidationArtifactsCategory` are required for proper publishing of build artifacts. See https://github.com/dotnet/roslyn/pull/38259 variables: @@ -20,25 +19,17 @@ variables: - group: DotNet-Roslyn-SDLValidation-Params # To retrieve OptProf data we need to authenticate to the VS drop storage. - # If the pipeline is running in DevDiv, the account has access to the VS drop storage. - # Get $AccessToken-dotnet-build-bot-public-repo from DotNet-GitHub-Versions-Repo-Write - - ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: - - group: DotNet-GitHub-Versions-Repo-Write - - name: _DevDivDropAccessToken - value: $(System.AccessToken) - # If the pipeline is running in dnceng: # Get access token with $dn-bot-devdiv-drop-rw-code-rw and dn-bot-dnceng-build-rw-code-rw from DotNet-VSTS-Infra-Access # Get $dotnetfeed-storage-access-key-1 from DotNet-Blob-Feed # Get $microsoft-symbol-server-pat and $symweb-symbol-server-pat from DotNet-Symbol-Server-Pats # Get $AccessToken-dotnet-build-bot-public-repo from DotNet-Versions-Publish - - ${{ if eq(variables['System.TeamProject'], 'internal') }}: - - group: DotNet-Blob-Feed - - group: DotNet-Symbol-Server-Pats - - group: DotNet-Versions-Publish - - group: DotNet-VSTS-Infra-Access - - group: DotNet-DevDiv-Insertion-Workflow-Variables - - name: _DevDivDropAccessToken - value: $(dn-bot-devdiv-drop-rw-code-rw) + - group: DotNet-Blob-Feed + - group: DotNet-Symbol-Server-Pats + - group: DotNet-Versions-Publish + - group: DotNet-VSTS-Infra-Access + - group: DotNet-DevDiv-Insertion-Workflow-Variables + - name: _DevDivDropAccessToken + value: $(dn-bot-devdiv-drop-rw-code-rw) stages: - stage: build @@ -54,31 +45,11 @@ stages: - job: OfficialBuild displayName: Official Build timeoutInMinutes: 360 - # Conditionally set build pool so we can share this YAML when building with different pipeline (devdiv vs dnceng) pool: - ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: - name: VSEngSS-MicroBuild2017 - demands: - - msbuild - - visualstudio - - DotNetFramework - ${{ if eq(variables['System.TeamProject'], 'internal') }}: - name: NetCoreInternal-Pool - queue: BuildPool.Server.Amd64.VS2017 + name: NetCoreInternal-Pool + queue: BuildPool.Server.Amd64.VS2017 steps: - # Make sure our two pipelines generate builds with distinct build numbers to avoid confliction. - # i.e. DevDiv builds will have even rev# whereas dnceng builds will be odd. - - task: PowerShell@2 - displayName: Update BuildNumber - inputs: - filePath: 'eng\update-build-number.ps1' - ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: - arguments: '-buildNumber $(Build.BuildNumber) -oddOrEven even' - ${{ if eq(variables['System.TeamProject'], 'internal') }}: - arguments: '-buildNumber $(Build.BuildNumber) -oddOrEven odd' - condition: eq(variables['System.JobAttempt'], '1') - - powershell: Write-Host "##vso[task.setvariable variable=SourceBranchName]$('$(Build.SourceBranch)'.Substring('refs/heads/'.Length))" displayName: Setting SourceBranchName variable condition: succeeded() @@ -88,23 +59,7 @@ stages: inputs: type: 'Build' tags: 'OfficialBuild' - condition: and(succeeded(), endsWith(variables['SourceBranchName'], '-vs-deps'), eq(variables['PRNumber'], 'default')) - - - task: tagBuildOrRelease@0 - displayName: Tag PR validation build - inputs: - type: 'Build' - tags: | - PRValidationBuild - PRNumber:$(PRNumber) - condition: and(succeeded(), ne(variables['PRNumber'], 'default')) - - - task: PowerShell@2 - displayName: Setup branch for insertion validation - inputs: - filePath: 'eng\setup-pr-validation.ps1' - arguments: '-sourceBranchName $(SourceBranchName) -prNumber $(PRNumber)' - condition: and(succeeded(), ne(variables['PRNumber'], 'default')) + condition: and(succeeded(), endsWith(variables['SourceBranchName'], '-vs-deps')) - task: tagBuildOrRelease@0 displayName: Tag main validation build @@ -118,8 +73,7 @@ stages: displayName: Merge main-vs-deps into source branch inputs: filePath: 'scripts\merge-vs-deps.ps1' - ${{ if eq(variables['System.TeamProject'], 'internal') }}: - arguments: '-accessToken $(dn-bot-dnceng-build-rw-code-rw)' + arguments: '-accessToken $(dn-bot-dnceng-build-rw-code-rw)' condition: and(succeeded(), eq(variables['SourceBranchName'], 'main')) - powershell: Write-Host "##vso[task.setvariable variable=VisualStudio.DropName]Products/$(System.TeamProject)/$(Build.Repository.Name)/$(SourceBranchName)/$(Build.BuildNumber)" @@ -132,20 +86,15 @@ stages: # Authenticate with service connections to be able to publish packages to external nuget feeds. - task: NuGetAuthenticate@0 inputs: - ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: - nuGetServiceConnections: azure-public/vs-impl, azure-public/vssdk - ${{ if eq(variables['System.TeamProject'], 'internal') }}: - nuGetServiceConnections: azure-public/vs-impl, azure-public/vssdk, devdiv/engineering + nuGetServiceConnections: azure-public/vs-impl, azure-public/vssdk, devdiv/engineering - # Needed because the dnceng image we build on fails the next task without it + # Needed because the build fails the NuGet Tools restore without it - task: UseDotNet@2 displayName: 'Use .NET Core sdk' inputs: packageType: sdk useGlobalJson: true workingDirectory: '$(Build.SourcesDirectory)' - installationPath: '$(Build.SourcesDirectory)/.dotnet' - condition: eq(variables['System.TeamProject'], 'internal') # Needed to restore the Microsoft.DevDiv.Optimization.Data.PowerShell package - task: NuGetCommand@2 @@ -161,9 +110,7 @@ stages: inputs: signType: $(SignType) zipSources: false - # If running in dnceng, we need to use a feed different from default - ${{ if eq(variables['System.TeamProject'], 'internal') }}: - feedSource: https://dnceng.pkgs.visualstudio.com/_packaging/MicroBuildToolset/nuget/v3/index.json + feedSource: https://dnceng.pkgs.visualstudio.com/_packaging/MicroBuildToolset/nuget/v3/index.json condition: and(succeeded(), in(variables['SignType'], 'test', 'real')) - task: PowerShell@2 @@ -202,7 +149,7 @@ stages: inputs: filePath: 'eng\publish-assets.ps1' arguments: '-configuration $(BuildConfiguration) -branchName "$(SourceBranchName)"' - condition: and(succeeded(), eq(variables['PRNumber'], 'default')) + condition: succeeded() # Publish OptProf configuration files # The env variable is required to enable cross account access using PAT (dnceng -> devdiv) @@ -272,12 +219,8 @@ stages: command: push packagesToPush: '$(Build.SourcesDirectory)\artifacts\VSSetup\$(BuildConfiguration)\DevDivPackages\**\*.nupkg' allowPackageConflicts: true - ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: - feedsToUse: config - publishVstsFeed: '97a41293-2972-4f48-8c0e-05493ae82010' - ${{ if eq(variables['System.TeamProject'], 'internal') }}: - nuGetFeedType: external - publishFeedCredentials: 'DevDiv - VS package feed' + nuGetFeedType: external + publishFeedCredentials: 'DevDiv - VS package feed' condition: succeeded() # Publish an artifact that the RoslynInsertionTool is able to find by its name. @@ -315,25 +258,8 @@ stages: publishUsingPipelines: true dependsOn: - OfficialBuild - ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: - queue: - name: Hosted VS2017 - ${{ if eq(variables['System.TeamProject'], 'internal') }}: - pool: - vmImage: vs2017-win2016 - -# We need to skip post-build stages for PR validation build, but it can only be identified by -# the runtime variable 'PRNumber', thus this dummy stage. Also the dummy job is required -# otherwise AzDO would just repeat jobs from previous stage. -- stage: SetValidateDependency - displayName: Setup the dependency for post-build stages - condition: and(succeeded(), eq(variables['PRNumber'], 'default')) - jobs: - - job: Log - displayName: Log - steps: - - powershell: Write-Host "Setup the dependency for post-build stages." - displayName: Log + pool: + vmImage: vs2017-win2016 - ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - template: eng\common\templates\post-build\post-build.yml @@ -343,12 +269,6 @@ stages: # https://github.com/dotnet/arcade/issues/2871 is resolved. enableSymbolValidation: false enableSourceLinkValidation: false - # It's important that post-build stages are depends on 'SetValidateDependency' stage instead of 'build', - # since we don't want to publish validation build. - validateDependsOn: - - SetValidateDependency - dependsOn: - - SetValidateDependency # Enable SDL validation, passing through values from the 'DotNet-Roslyn-SDLValidation-Params' group. SDLValidationParameters: enable: true diff --git a/azure-pipelines-pr-validation.yml b/azure-pipelines-pr-validation.yml new file mode 100644 index 0000000000000..0f40733d53ef4 --- /dev/null +++ b/azure-pipelines-pr-validation.yml @@ -0,0 +1,199 @@ +resources: +- repo: self + clean: true + +# Variables defined in yml cannot be overridden at queue time instead overrideable variables must be defined in the web gui. +# Commenting out until AzDO supports something like the runtime parameters outlined here: https://github.com/Microsoft/azure-pipelines-yaml/pull/129 +#variables: +# SignType: test +# SkipTests: true +# SkipApplyOptimizationData: false +# IbcDrop: 'default' +# PRNumber: 'REQUIRED' +# CommitSHA: 'REQUIRED' + +# The variables `_DotNetArtifactsCategory` and `_DotNetValidationArtifactsCategory` are required for proper publishing of build artifacts. See https://github.com/dotnet/roslyn/pull/38259 +variables: + - name: _DotNetArtifactsCategory + value: .NETCore + - name: _DotNetValidationArtifactsCategory + value: .NETCoreValidation + - group: DotNet-Roslyn-SDLValidation-Params + + # To retrieve OptProf data we need to authenticate to the VS drop storage. + # If the pipeline is running in DevDiv, the account has access to the VS drop storage. + # Get $AccessToken-dotnet-build-bot-public-repo from DotNet-GitHub-Versions-Repo-Write + - group: DotNet-GitHub-Versions-Repo-Write + - name: _DevDivDropAccessToken + value: $(System.AccessToken) + +stages: +- stage: build + displayName: Build and Test + + jobs: + + - job: OfficialTestBuild + displayName: Official Test Build + timeoutInMinutes: 360 + # Conditionally set build pool so we can share this YAML when building with different pipeline + pool: + name: VSEngSS-MicroBuild2017 + demands: + - msbuild + - visualstudio + - DotNetFramework + + steps: + - powershell: Write-Host "##vso[task.setvariable variable=SourceBranchName]$('$(Build.SourceBranch)'.Substring('refs/heads/'.Length))" + displayName: Setting SourceBranchName variable + condition: succeeded() + + - task: tagBuildOrRelease@0 + displayName: Tag PR validation build + inputs: + type: 'Build' + tags: | + PRValidationBuild + PRNumber:$(PRNumber) + CommitSHA:$(CommitSHA) + condition: succeeded() + + - task: PowerShell@2 + displayName: Setup branch for insertion validation + inputs: + filePath: 'eng\setup-pr-validation.ps1' + arguments: '-sourceBranchName $(SourceBranchName) -prNumber $(PRNumber) -commitSHA $(CommitSHA)' + condition: succeeded() + + - powershell: Write-Host "##vso[task.setvariable variable=VisualStudio.DropName]Products/$(System.TeamProject)/$(Build.Repository.Name)/$(SourceBranchName)/$(Build.BuildNumber)" + displayName: Setting VisualStudio.DropName variable + + - task: NuGetToolInstaller@0 + inputs: + versionSpec: '4.9.2' + + # Authenticate with service connections to be able to publish packages to external nuget feeds. + - task: NuGetAuthenticate@0 + inputs: + nuGetServiceConnections: azure-public/vs-impl, azure-public/vssdk + + # Needed to restore the Microsoft.DevDiv.Optimization.Data.PowerShell package + - task: NuGetCommand@2 + displayName: Restore internal tools + inputs: + command: restore + feedsToUse: config + restoreSolution: 'eng\common\internal\Tools.csproj' + nugetConfigPath: 'NuGet.config' + restoreDirectory: '$(Build.SourcesDirectory)\.packages' + + - task: MicroBuildSigningPlugin@2 + inputs: + signType: $(SignType) + zipSources: false + condition: and(succeeded(), in(variables['SignType'], 'test', 'real')) + + - task: PowerShell@2 + displayName: Build + inputs: + filePath: eng/build.ps1 + arguments: -ci + -restore + -build + -pack + -sign + -publish + -binaryLog + -configuration $(BuildConfiguration) + -officialBuildId $(Build.BuildNumber) + -officialSkipTests $(SkipTests) + -officialSkipApplyOptimizationData $(SkipApplyOptimizationData) + -officialSourceBranchName $(SourceBranchName) + -officialIbcDrop $(IbcDrop) + -officialVisualStudioDropAccessToken $(_DevDivDropAccessToken) + /p:RepositoryName=$(Build.Repository.Name) + /p:VisualStudioDropName=$(VisualStudio.DropName) + /p:DotNetSignType=$(SignType) + /p:DotNetPublishToBlobFeed=false + /p:PublishToSymbolServer=false + /p:DotNetArtifactsCategory=$(_DotNetArtifactsCategory) + /p:DotnetPublishUsingPipelines=false + condition: succeeded() + + # Publish OptProf generated JSON files as a build artifact. This allows for easy inspection from + # a build execution. + - task: PublishBuildArtifacts@1 + displayName: Publish OptProf Data Files + inputs: + PathtoPublish: '$(Build.SourcesDirectory)\artifacts\OptProf\$(BuildConfiguration)\Data' + ArtifactName: 'OptProf Data Files' + condition: succeeded() + + - task: PublishBuildArtifacts@1 + displayName: Publish Logs + inputs: + PathtoPublish: '$(Build.SourcesDirectory)\artifacts\log\$(BuildConfiguration)' + ArtifactName: 'Build Diagnostic Files' + publishLocation: Container + continueOnError: true + condition: succeededOrFailed() + + - task: PublishBuildArtifacts@1 + displayName: Publish Ngen Logs + inputs: + PathtoPublish: '$(Build.SourcesDirectory)\artifacts\log\$(BuildConfiguration)\ngen' + ArtifactName: 'NGen Logs' + publishLocation: Container + continueOnError: true + condition: succeeded() + + - task: PublishTestResults@2 + displayName: Publish xUnit Test Results + inputs: + testRunner: XUnit + testResultsFiles: '$(Build.SourcesDirectory)\artifacts\TestResults\$(BuildConfiguration)\*.xml' + mergeTestResults: true + testRunTitle: 'Unit Tests' + condition: and(succeededOrFailed(), ne(variables['SkipTests'], 'true')) + + # Publishes setup VSIXes to a drop. + # Note: The insertion tool looks for the display name of this task in the logs. + - task: ms-vseng.MicroBuildTasks.4305a8de-ba66-4d8b-b2d1-0dc4ecbbf5e8.MicroBuildUploadVstsDropFolder@1 + displayName: Upload VSTS Drop + inputs: + DropName: $(VisualStudio.DropName) + DropFolder: 'artifacts\VSSetup\$(BuildConfiguration)\Insertion' + AccessToken: $(_DevDivDropAccessToken) + condition: succeeded() + + # Publish insertion packages to CoreXT store. + - task: NuGetCommand@2 + displayName: Publish CoreXT Packages + inputs: + command: push + packagesToPush: '$(Build.SourcesDirectory)\artifacts\VSSetup\$(BuildConfiguration)\DevDivPackages\**\*.nupkg' + allowPackageConflicts: true + feedsToUse: config + publishVstsFeed: '97a41293-2972-4f48-8c0e-05493ae82010' + condition: succeeded() + + # Publish an artifact that the RoslynInsertionTool is able to find by its name. + - task: PublishBuildArtifacts@1 + displayName: Publish Artifact VSSetup + inputs: + PathtoPublish: 'artifacts\VSSetup\$(BuildConfiguration)' + ArtifactName: 'VSSetup' + condition: succeeded() + + # Publish Asset Manifests for Build Asset Registry job + - task: PublishBuildArtifacts@1 + displayName: Publish Asset Manifests + inputs: + PathtoPublish: '$(Build.SourcesDirectory)/artifacts/log/$(BuildConfiguration)/AssetManifest' + ArtifactName: AssetManifests + condition: succeeded() + + - task: ms-vseng.MicroBuildTasks.521a94ea-9e68-468a-8167-6dcf361ea776.MicroBuildCleanup@1 + displayName: Perform Cleanup Tasks + condition: succeededOrFailed() diff --git a/eng/setup-pr-validation.ps1 b/eng/setup-pr-validation.ps1 index f65ac65de526e..44f3e7d07f6c8 100644 --- a/eng/setup-pr-validation.ps1 +++ b/eng/setup-pr-validation.ps1 @@ -1,24 +1,40 @@ [CmdletBinding(PositionalBinding=$false)] param ( [string]$sourceBranchName, - [string]$prNumber) + [string]$prNumber, + [string]$commitSHA) -try { - # name and email are only used for merge commit, it doesn't really matter what we put in there. +try { + # name and email are only used for merge commit, it doesn't really matter what we put in there. git config user.name "RoslynValidation" - git config user.email "validation@roslyn.net" - + git config user.email "validation@roslyn.net" + if ($sourceBranchName -notlike '*-vs-deps') { Write-Host "##vso[task.LogIssue type=warning;]The base branch for insertion validation is $sourceBranchName, which is not a vs-deps branch." } + + Write-Host "Validating the PR head matches the specified commit SHA ($commitSHA)..." + if ($commitSHA.Length -lt 7) { + Write-Host "##vso[task.LogIssue type=error;]The PR Commit SHA must be at least 7 characters long." + exit 1 + } + + Write-Host "Getting the hash of refs/pull/$prNumber/head..." + $remoteRef = git ls-remote origin refs/pull/$prNumber/head + Write-Host ($remoteRef | Out-String) + + $prHeadSHA = $remoteRef.Split()[0] + if (!$prHeadSHA.StartsWith($commitSHA)) { + Write-Host "##vso[task.LogIssue type=error;]The PR's Head SHA ($prHeadSHA) does not begin with the specified commit SHA ($commitSHA). Unreviewed changes may have been pushed to the PR." + exit 1 + } + Write-Host "Setting up the build for PR validation by merging refs/pull/$prNumber/merge into $sourceBranchName..." git pull origin refs/pull/$prNumber/merge if (!$?) { Write-Host "##vso[task.LogIssue type=error;]Merging branch refs/pull/$prNumber/merge failed." exit 1 } - Write-Host "Getting the hash of refs/pull/$prNumber/head..." - Write-Host ((git ls-remote origin refs/pull/$prNumber/head) | Out-String) } catch { Write-Host $_ diff --git a/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/InheritanceGlyphFactory.cs b/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/InheritanceGlyphFactory.cs index e7d73fe45f3c2..54d8952451302 100644 --- a/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/InheritanceGlyphFactory.cs +++ b/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/InheritanceGlyphFactory.cs @@ -44,9 +44,6 @@ public InheritanceGlyphFactory( var membersOnLine = inheritanceMarginTag.MembersOnLine; Contract.ThrowIfTrue(membersOnLine.IsEmpty); - // ZoomLevel of textView is percentage based. (e.g. 20 -> 400 means 20% -> 400%) - // and the scaleFactor of CrispImage is 1 based. (e.g 1 means 100%) - var scaleFactor = _textView.ZoomLevel / 100; return new MarginGlyph.InheritanceMargin( _threadingContext, _streamingFindUsagesPresenter, @@ -54,7 +51,7 @@ public InheritanceGlyphFactory( _classificationFormatMap, _waitIndicator, inheritanceMarginTag, - scaleFactor); + _textView); } return null; diff --git a/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/MarginGlyph/InheritanceMargin.xaml.cs b/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/MarginGlyph/InheritanceMargin.xaml.cs index 8b9c5a7687b6e..92d6b1fffb1b4 100644 --- a/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/MarginGlyph/InheritanceMargin.xaml.cs +++ b/src/VisualStudio/Core/Def/Implementation/InheritanceMargin/MarginGlyph/InheritanceMargin.xaml.cs @@ -16,6 +16,7 @@ using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Text.Classification; +using Microsoft.VisualStudio.Text.Editor; namespace Microsoft.VisualStudio.LanguageServices.Implementation.InheritanceMargin.MarginGlyph { @@ -25,6 +26,7 @@ internal partial class InheritanceMargin private readonly IStreamingFindUsagesPresenter _streamingFindUsagesPresenter; private readonly IWaitIndicator _waitIndicator; private readonly Workspace _workspace; + private readonly IWpfTextView _textView; public InheritanceMargin( IThreadingContext threadingContext, @@ -33,14 +35,18 @@ public InheritanceMargin( IClassificationFormatMap classificationFormatMap, IWaitIndicator waitIndicator, InheritanceMarginTag tag, - double scaleFactor) + IWpfTextView textView) { _threadingContext = threadingContext; _streamingFindUsagesPresenter = streamingFindUsagesPresenter; _workspace = tag.Workspace; _waitIndicator = waitIndicator; + _textView = textView; InitializeComponent(); + // ZoomLevel of textView is percentage based. (e.g. 20 -> 400 means 20% -> 400%) + // and the scaleFactor of CrispImage is 1 based. (e.g 1 means 100%) + var scaleFactor = textView.ZoomLevel; var viewModel = InheritanceMarginViewModel.Create(classificationTypeMap, classificationFormatMap, tag, scaleFactor); DataContext = viewModel; ContextMenu.DataContext = viewModel; @@ -99,6 +105,9 @@ private void InheritanceMargin_OnMouseLeave(object sender, MouseEventArgs e) private void ContextMenu_OnClose(object sender, RoutedEventArgs e) { ResetBorderToInitialColor(); + // Move the focus back to textView when the context menu is closed. + // It ensures the focus won't be left at the margin + ResetFocus(); } private void ContextMenu_OnOpen(object sender, RoutedEventArgs e) @@ -131,5 +140,17 @@ private void ResetBorderToInitialColor() this.Background = Brushes.Transparent; this.BorderBrush = Brushes.Transparent; } + + private void ResetFocus() + { + if (!_textView.HasAggregateFocus) + { + var visualElement = _textView.VisualElement; + if (visualElement.Focusable) + { + Keyboard.Focus(visualElement); + } + } + } } }