Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Publish artifacts and symbols one file at a time #7086

Merged
merged 39 commits into from
Apr 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
2ca72c8
Improve publishing performance
epananth Mar 10, 2021
8197834
remove unwanted param
epananth Mar 10, 2021
9d3a66e
some fixes
epananth Mar 10, 2021
271e5c2
more update
epananth Mar 11, 2021
23fd68d
minor fixes
epananth Mar 11, 2021
086bc12
Merge branch 'main' of https://github.com/dotnet/arcade into publishi…
epananth Mar 11, 2021
583c310
Merge branch 'main' of https://github.com/dotnet/arcade into publishi…
epananth Mar 11, 2021
b82c1c0
Disabled package download
epananth Mar 11, 2021
ff093e1
fix
epananth Mar 11, 2021
91f474a
Remove some logging
epananth Mar 12, 2021
68e8ea8
More fix
epananth Mar 16, 2021
7c1325a
Merge branch 'main' of https://github.com/dotnet/arcade into publishi…
epananth Mar 16, 2021
30e72f6
Merge branch 'main' of https://github.com/dotnet/arcade into publishi…
epananth Mar 18, 2021
037401b
test sdk
epananth Mar 19, 2021
6d73f0b
test
epananth Mar 18, 2021
ff239e8
review comments
epananth Mar 22, 2021
42edf24
merge conflict
epananth Mar 22, 2021
696ae33
test
epananth Mar 22, 2021
3cc26df
review comments:
epananth Mar 23, 2021
d2d4146
merge conflict
epananth Mar 23, 2021
cf46b2d
minor fix
epananth Mar 23, 2021
8b0a82b
minor fix
epananth Mar 23, 2021
16826fb
review:
epananth Mar 24, 2021
2a3eda7
review comments
epananth Mar 25, 2021
0cada85
Review comments
epananth Mar 30, 2021
99cbcaf
Merge branch 'main' of https://github.com/dotnet/arcade into publishi…
epananth Mar 30, 2021
5db58cc
more comments addressed
epananth Mar 31, 2021
53f99f8
Update comment
epananth Mar 31, 2021
32cc9b0
minor fix
epananth Mar 31, 2021
05ba152
moooore comments
epananth Apr 1, 2021
90bafde
minor changes
epananth Apr 1, 2021
41244b9
fix:
epananth Apr 1, 2021
d9f0469
remove error condition
epananth Apr 1, 2021
1da7ed1
last few comments
epananth Apr 2, 2021
21fc09f
Removed unwanted comment
epananth Apr 2, 2021
86215a9
review comment
epananth Apr 5, 2021
8de357d
last change
epananth Apr 6, 2021
8a5d431
IO exception fix
epananth Apr 8, 2021
07f63c4
test fix
epananth Apr 8, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions eng/publishing/v3/publish-assets.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,15 @@ jobs:
value: $[ dependencies.setupMaestroVars.outputs['setReleaseVars.AzDOPipelineId'] ]
- name: AzDOBuildId
value: $[ dependencies.setupMaestroVars.outputs['setReleaseVars.AzDOBuildId'] ]
- name: AzDOAccount
value: $[ dependencies.setupMaestroVars.outputs['setReleaseVars.AzDOBuildAccount'] ]
pool:
vmImage: 'windows-2019'
steps:
- task: DownloadBuildArtifacts@0
displayName: Download Build Assets
continueOnError: true
enabled: true
inputs:
buildType: specific
buildVersionToDownload: specific
Expand All @@ -36,12 +39,9 @@ jobs:
buildId: $(AzDOBuildId)
downloadType: 'specific'
itemPattern: |
PackageArtifacts/**
BlobArtifacts/**
AssetManifests/**
PdbArtifacts/**
downloadPath: '$(Build.ArtifactStagingDirectory)'
checkDownloadedFiles: true

- task: NuGetToolInstaller@1
displayName: 'Install NuGet.exe'
Expand Down Expand Up @@ -87,7 +87,12 @@ jobs:
/p:MsdlToken=$(microsoft-symbol-server-pat)
/p:SymWebToken=$(symweb-symbol-server-pat)
/p:BuildQuality='${{ parameters.buildQuality }}'

/p:AzdoApiToken='$(dn-bot-all-orgs-build-rw-code-rw)'
/p:ArtifactsBasePath='$(Build.ArtifactStagingDirectory)/'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is the extra slash at the end necessary?

Copy link
Member Author

@epananth epananth Mar 31, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually I copied the paths we already had in the yaml. But I checked without the / or with the / it will work.

/p:BuildId='$(AzDOBuildId)'
/p:AzureDevOpsOrg='$(AzDOAccount)'
/p:AzureProject='$(AzDOProjectName)'
/p:UseStreamingPublishing='true'
- template: /eng/common/templates/steps/publish-logs.yml
parameters:
StageLabel: '${{ parameters.stageName }}'
Expand Down
2 changes: 2 additions & 0 deletions eng/publishing/v3/setup-maestro-vars.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ jobs:
$AzureDevOpsProject = $buildInfo.azureDevOpsProject
$AzureDevOpsBuildDefinitionId = $buildInfo.azureDevOpsBuildDefinitionId
$AzureDevOpsBuildId = $buildInfo.azureDevOpsBuildId
$AzureDevOpsAccount = $buildInfo.azureDevOpsAccount

Write-Host "##vso[task.setvariable variable=BARBuildId;isOutput=true]$BarId"
Write-Host "##vso[task.setvariable variable=TargetChannels;isOutput=true]$Channels"
Expand All @@ -46,6 +47,7 @@ jobs:
Write-Host "##vso[task.setvariable variable=AzDOProjectName;isOutput=true]$AzureDevOpsProject"
Write-Host "##vso[task.setvariable variable=AzDOPipelineId;isOutput=true]$AzureDevOpsBuildDefinitionId"
Write-Host "##vso[task.setvariable variable=AzDOBuildId;isOutput=true]$AzureDevOpsBuildId"
Write-Host "##vso[task.setvariable variable=AzDOBuildAccount;isOutput=true]$AzureDevOpsAccount"
}
catch {
Write-Host $_
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@
- SymWebToken : Token used to publish symbol blobs, dll and pdb to Symweb
- SymbolPublishingExclusionsFile : Files that have to be excluded from symbol publishing
- PdbArtifactsBasePath : Path to dlls and pdbs
- AzdoApiToken : Token used to call the Azure Api to download artifacts and symbols
- ArtifactsBasePath : Staging directory
- UseStreamingPublishing : When set to true it will use Azure Api to download the artifacts and symbols using streaming,
else all the artifacts and symbols are downloaded before publishing.

Optional aka.ms link generation parameters:
- AkaMSClientId : Client ID for the aka.ms AD application
Expand Down Expand Up @@ -103,6 +107,8 @@
<ChecksumsFeedKey>$(ChecksumsAzureAccountKey)</ChecksumsFeedKey>
<InternalChecksumsFeedKey>$(InternalChecksumsAzureAccountKey)</InternalChecksumsFeedKey>
<AllowFeedOverrides Condition="'$(AllowFeedOverrides)' == ''">false</AllowFeedOverrides>
<UseStreamingPublishing Condition="'$(UseStreamingPublishing)' == ''">false</UseStreamingPublishing>
<ArtifactsBasePath Condition="'$(ArtifactsBasePath)' == ''">$(BlobBasePath)</ArtifactsBasePath>
</PropertyGroup>

<Error
Expand Down Expand Up @@ -147,7 +153,14 @@
ChecksumsFeedOverride="$(ChecksumsFeedOverride)"
TransportFeedOverride="$(TransportFeedOverride)"
ShippingFeedOverride="$(ShippingFeedOverride)"
SymbolsFeedOverride="$(SymbolsFeedOverride)" />
SymbolsFeedOverride="$(SymbolsFeedOverride)"
AzdoApiToken="$(AzdoApiToken)"
ArtifactsBasePath="$(ArtifactsBasePath)"
riarenas marked this conversation as resolved.
Show resolved Hide resolved
BuildId="$(BuildId)"
AzureDevOpsOrg="$(AzureDevOpsOrg)"
AzureProject="$(AzureProject)"
UseStreamingPublishing="$(UseStreamingPublishing)"/>
epananth marked this conversation as resolved.
Show resolved Hide resolved

</Target>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
using System.Collections.Generic;
using System.IO;
using Castle.DynamicProxy.Generators.Emitters.SimpleAST;
using Microsoft.Arcade.Test.Common;
using Microsoft.DotNet.Build.Tasks.Feed.Model;
using Microsoft.DotNet.Maestro.Client.Models;
using Xunit;

namespace Microsoft.DotNet.Build.Tasks.Feed.Tests
Expand Down Expand Up @@ -85,18 +85,23 @@ public void TemporarySymbolDirectoryDoesNotExists()
BuildEngine = buildEngine,
};
var path = TestInputs.GetFullPath("Symbol");
var publish = task.HandleSymbolPublishingAsync(path, MsdlToken, SymWebToken, "", path, false);
var buildAsset = new Dictionary<string, HashSet<Asset>>();
var publish = task.HandleSymbolPublishingAsync(path, MsdlToken, SymWebToken, "", false, buildAsset, path);
Assert.True(task.Log.HasLoggedErrors);
}

[Fact]
public void TemporarySymbolsDirectoryTest()
{
var publishTask = new PublishArtifactsInManifestV3();
var buildEngine = new MockBuildEngine();
var publishTask = new PublishArtifactsInManifestV3()
{
BuildEngine = buildEngine,
};
var path = TestInputs.GetFullPath("Test");
publishTask.EnsureTemporarySymbolDirectoryExists(path);
publishTask.EnsureTemporaryDirectoryExists(path);
Assert.True(Directory.Exists(path));
publishTask.DeleteSymbolTemporaryDirectory(path);
publishTask.DeleteTemporaryDirectory(path);
Assert.False(Directory.Exists(path));
}

Expand Down Expand Up @@ -134,6 +139,5 @@ public void PublishSymbolApiIsCalledTest()
false,
false).IsCompleted);
}

}
}
108 changes: 59 additions & 49 deletions src/Microsoft.DotNet.Build.Tasks.Feed/src/BlobFeedAction.cs
Original file line number Diff line number Diff line change
@@ -1,23 +1,21 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Build.Framework;
using Microsoft.DotNet.Build.CloudTestTasks;
using Microsoft.WindowsAzure.Storage;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using NuGet.Packaging;
using NuGet.Packaging.Core;
using Sleet;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using MSBuild = Microsoft.Build.Utilities;

namespace Microsoft.DotNet.Build.Tasks.Feed
Expand Down Expand Up @@ -124,26 +122,24 @@ public async Task<bool> PushItemsToFeedAsync(
return !Log.HasLoggedErrors;
}

public async Task PublishToFlatContainerAsync(IEnumerable<ITaskItem> taskItems, int maxClients, PushOptions pushOptions)
public async Task PublishToFlatContainerAsync(IEnumerable<ITaskItem> taskItems, int maxClients,
PushOptions pushOptions)
{
if (taskItems.Any())
{
using (var clientThrottle = new SemaphoreSlim(maxClients, maxClients))
{
await System.Threading.Tasks.Task.WhenAll(taskItems.Select(
item =>
{
return UploadAssetAsync(item, clientThrottle, pushOptions);
}
item => { return UploadAssetAsync(item, pushOptions, clientThrottle); }
));
}
}
}

public async Task UploadAssetAsync(
epananth marked this conversation as resolved.
Show resolved Hide resolved
ITaskItem item,
SemaphoreSlim clientThrottle,
PushOptions options)
PushOptions options,
SemaphoreSlim clientThrottle = null)
{
string relativeBlobPath = item.GetMetadata("RelativeBlobPath");

Expand All @@ -154,58 +150,72 @@ public async Task UploadAssetAsync(
relativeBlobPath = $"{recursiveDir}{fileName}";
michellemcdaniel marked this conversation as resolved.
Show resolved Hide resolved
}

relativeBlobPath = $"{RelativePath}{relativeBlobPath}".Replace("\\", "/");

if (relativeBlobPath.Contains("//"))
if (!string.IsNullOrEmpty(relativeBlobPath))
{
Log.LogError(
$"Item '{item.ItemSpec}' RelativeBlobPath contains virtual directory " +
$"without name (double forward slash): '{relativeBlobPath}'");
return;
}
relativeBlobPath = $"{RelativePath}{relativeBlobPath}".Replace("\\", "/");

Log.LogMessage($"Uploading {relativeBlobPath}");
if (relativeBlobPath.StartsWith("//"))
{
Log.LogError(
$"Item '{item.ItemSpec}' RelativeBlobPath contains virtual directory " +
$"without name (double forward slash): '{relativeBlobPath}'");
return;
}

await clientThrottle.WaitAsync();
Log.LogMessage($"Uploading {relativeBlobPath}");

try
{
AzureStorageUtils blobUtils = new AzureStorageUtils(AccountName, AccountKey, ContainerName);
if (clientThrottle != null)
{
await clientThrottle.WaitAsync();
}

if (!options.AllowOverwrite && await blobUtils.CheckIfBlobExistsAsync(relativeBlobPath))
try
{
if (options.PassIfExistingItemIdentical)
AzureStorageUtils blobUtils = new AzureStorageUtils(AccountName, AccountKey, ContainerName);

if (!options.AllowOverwrite && await blobUtils.CheckIfBlobExistsAsync(relativeBlobPath))
{
if (!await blobUtils.IsFileIdenticalToBlobAsync(item.ItemSpec, relativeBlobPath))
if (options.PassIfExistingItemIdentical)
{
Log.LogError(
$"Item '{item}' already exists with different contents " +
$"at '{relativeBlobPath}'");
if (!await blobUtils.IsFileIdenticalToBlobAsync(item.ItemSpec, relativeBlobPath))
{
Log.LogError(
$"Item '{item}' already exists with different contents " +
$"at '{relativeBlobPath}'");
}
}
else
{
Log.LogError($"Item '{item}' already exists at '{relativeBlobPath}'");
}
}
else
{
Log.LogError($"Item '{item}' already exists at '{relativeBlobPath}'");
using (FileStream stream =
new FileStream(item.ItemSpec, FileMode.Open, FileAccess.Read, FileShare.Read))
{
Log.LogMessage($"Uploading {item} to {relativeBlobPath}.");
await blobUtils.UploadBlockBlobAsync(item.ItemSpec, relativeBlobPath, stream);
}
}
}
else
catch (Exception exc)
{
using (FileStream stream =
new FileStream(item.ItemSpec, FileMode.Open, FileAccess.Read, FileShare.Read))
Log.LogError(
$"Unable to upload to {relativeBlobPath} in Azure Storage account {AccountName}/{ContainerName} due to {exc}.");
throw;
}
finally
{
if (clientThrottle != null)
{
Log.LogMessage($"Uploading {item} to {relativeBlobPath}.");
await blobUtils.UploadBlockBlobAsync(item.ItemSpec, relativeBlobPath, stream);
clientThrottle.Release();
}
}
}
catch (Exception exc)
{
Log.LogError($"Unable to upload to {relativeBlobPath} in Azure Storage account {AccountName}/{ContainerName} due to {exc}.");
throw;
}
finally
else
{
clientThrottle.Release();
Log.LogError($"Relative blob path is empty.");
}
}

Expand Down Expand Up @@ -332,7 +342,7 @@ private ISleetFileSystem GetAzureFileSystem(LocalCache fileCache)
}
}

private async Task<bool> PushAsync(IEnumerable<string> items, PushOptions options)
public async Task<bool> PushAsync(IEnumerable<string> items, PushOptions options)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like this method is basically wrapped by https://github.com/dotnet/arcade/blob/main/src/Microsoft.DotNet.Build.Tasks.Feed/src/BlobFeedAction.cs#L103, Is there a reason to make this public instead of using that?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am calling PushAsync in PublishArtifactsInManifestBase, before it was called only in BlobFeedAction

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, what I'm saying is that there's another method (the one I link in my comment) that is already public and calls into this private method that you're making public.

My question is whether that is intentional, or just that you didn't see the other one.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes other method which calls PushAsync which is PushToFeedAsync, does a sanity check to see if there are any duplicated in the ItemsList. Since I'm only pushing one item at a time. That part is not required. So I called PushAsync directly

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, looking at

public async Task<bool> PushItemsToFeedAsync(
IEnumerable<string> items,
PushOptions options)
{
Log.LogMessage(MessageImportance.Low, $"START pushing items to feed");
if (!items.Any())
{
Log.LogMessage("No items to push found in the items list.");
return true;
}
try
{
return await PushAsync(items, options);
}
catch (Exception e)
{
Log.LogErrorFromException(e);
}
return !Log.HasLoggedErrors;
}
it seems like it just checks that the items to push is not empty and includes some error handling, which I don't think is a bad thing. I think I'd prefer if we used it over changing the visibility of this one that doesn't have any parameter validation. When it was private that was mostly fine because the class controlled all callers, but by making it public you're now opening it so that anyone can call it.

Alternatively, you could add parameter validation and error handling, but then you'd end up with pretty much the same method as the one I'm saying to use.

{
LocalSettings settings = GetSettings();
SleetLogger log = new SleetLogger(Log, NuGet.Common.LogLevel.Verbose);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,21 @@ public string BuildQuality
get { return _buildQuality.GetDescription(); }
set { Enum.TryParse<PublishingConstants.BuildQuality>(value, true, out _buildQuality); }
}
public string AzdoApiToken {get; set;}

public string ArtifactsBasePath { get; set;}

public string BuildId { get; set; }

public string AzureProject { get; set; }

public string AzureDevOpsOrg { get; set; }

/// <summary>
/// If true, uses Azdo Api to download artifacts and symbols files one file at a time during publishing process.
/// If it is set to false, then artifacts and symbols are downloaded in PackageArtifacts and BlobArtifacts directory before publishing.
epananth marked this conversation as resolved.
Show resolved Hide resolved
/// </summary>
public bool UseStreamingPublishing { get; set; } = false;

/// <summary>
/// Just an internal flag to keep track whether we published assets via a V3 manifest or not.
Expand Down Expand Up @@ -372,7 +387,13 @@ internal PublishArtifactsInManifestBase ConstructPublishingV3Task(BuildModel bui
ChecksumsFeedOverride = this.ChecksumsFeedOverride,
ShippingFeedOverride = this.ShippingFeedOverride,
TransportFeedOverride = this.TransportFeedOverride,
SymbolsFeedOverride = this.SymbolsFeedOverride
SymbolsFeedOverride = this.SymbolsFeedOverride,
ArtifactsBasePath = this.ArtifactsBasePath,
AzdoApiToken = this.AzdoApiToken,
BuildId = this.BuildId,
AzureProject = this.AzureProject,
AzureDevOpsOrg = this.AzureDevOpsOrg,
UseStreamingPublishing = this.UseStreamingPublishing
};
}
}
Expand Down
Loading