public async Task <bool> ExecuteAsync() { try { var tasks = AssetManifestPaths .Select(manifestParam => WhichPublishingTask(manifestParam.ItemSpec)) .ToArray(); // Check that was possible to construct a publishing task for all manifests if (tasks.Any(t => t == null)) { return(false); } // Process all manifests in parallel var results = await Task.WhenAll( tasks.Select(t => t.ExecuteAsync()) ); // Check that all tasks returned true if (results.All(t => t)) { // Currently a build can produce several build manifests and publish them independently. // It's also possible that somehow we have manifests using different versions of the publishing infra. // // The V3 infra, once all assets have been published, promotes the build to the target channels informed. // Since we can have multiple manifests (perhaps using different versions), things // get a bit more complicated. For now, we are going to just promote the build to the target // channels if it creates at least one V3 manifest. // // There is an issue to merge all build manifests into a single one before publishing: // https://github.com/dotnet/arcade/issues/5489 if (PublishedV3Manifest) { IMaestroApi client = ApiFactory.GetAuthenticated(MaestroApiEndpoint, BuildAssetRegistryToken); Maestro.Client.Models.Build buildInformation = await client.Builds.GetBuildAsync(BARBuildId); var targetChannelsIds = TargetChannels.Split('-').Select(ci => int.Parse(ci)); foreach (var targetChannelId in targetChannelsIds) { await client.Channels.AddBuildToChannelAsync(BARBuildId, targetChannelId); } } return(true); } return(false); } catch (Exception e) { Log.LogErrorFromException(e, true); return(false); } }
public override async Task <bool> ExecuteAsync() { if (AnyMissingRequiredProperty()) { Log.LogError("Missing required properties. Aborting execution."); return(false); } try { List <int> targetChannelsIds = new List <int>(); foreach (var channelIdStr in TargetChannels.Split('-')) { if (!int.TryParse(channelIdStr, out var channelId)) { Log.LogError( $"Value '{channelIdStr}' isn't recognized as a valid Maestro++ channel ID. To add a channel refer to https://github.com/dotnet/arcade/blob/master/Documentation/CorePackages/Publishing.md#how-to-add-a-new-channel-to-use-v3-publishing."); continue; } targetChannelsIds.Add(channelId); } if (Log.HasLoggedErrors) { Log.LogError( $"Could not parse the target channels list '{TargetChannels}'. It should be a comma separated list of integers."); return(false); } SplitArtifactsInCategories(BuildModel); if (Log.HasLoggedErrors) { return(false); } // Fetch Maestro record of the build. We're going to use it to get the BAR ID // of the assets being published so we can add a new location for them. IMaestroApi client = ApiFactory.GetAuthenticated(MaestroApiEndpoint, BuildAssetRegistryToken); Maestro.Client.Models.Build buildInformation = await client.Builds.GetBuildAsync(BARBuildId); Dictionary <string, HashSet <Asset> > buildAssets = CreateBuildAssetDictionary(buildInformation); foreach (var targetChannelId in targetChannelsIds) { TargetChannelConfig targetChannelConfig = PublishingConstants.ChannelInfos .Where(ci => ci.Id == targetChannelId && (ci.PublishingInfraVersion == PublishingInfraVersion.All || ci.PublishingInfraVersion == PublishingInfraVersion.Next)) .FirstOrDefault(); // Invalid channel ID was supplied if (targetChannelConfig.Equals(default(TargetChannelConfig))) { Log.LogError($"Channel with ID '{targetChannelId}' is not configured to be published to."); return(false); } if (await client.Channels.GetChannelAsync(targetChannelId) == null) { Log.LogError($"Channel with ID '{targetChannelId}' does not exist in BAR."); return(false); } Log.LogMessage(MessageImportance.High, $"Publishing to this target channel: {targetChannelConfig}"); string shortLinkUrl = string.IsNullOrEmpty(targetChannelConfig.AkaMSChannelName) ? $"dotnet/" : $"dotnet/{targetChannelConfig.AkaMSChannelName}/{BuildQuality}"; var targetFeedsSetup = new SetupTargetFeedConfigV3( targetChannelConfig.IsInternal, BuildModel.Identity.IsStable, BuildModel.Identity.Name, BuildModel.Identity.Commit, AzureStorageTargetFeedKey, PublishInstallersAndChecksums, GetFeed(targetChannelConfig.InstallersFeed, InstallersFeedOverride), targetChannelConfig.IsInternal ? InternalInstallersFeedKey : InstallersFeedKey, GetFeed(targetChannelConfig.ChecksumsFeed, ChecksumsFeedOverride), targetChannelConfig.IsInternal ? InternalCheckSumsFeedKey : CheckSumsFeedKey, GetFeed(targetChannelConfig.ShippingFeed, ShippingFeedOverride), GetFeed(targetChannelConfig.TransportFeed, TransportFeedOverride), GetFeed(targetChannelConfig.SymbolsFeed, SymbolsFeedOverride), shortLinkUrl, AzureDevOpsFeedsKey, BuildEngine = this.BuildEngine, targetChannelConfig.SymbolTargetType, azureDevOpsPublicStaticSymbolsFeed: GetFeed(null, PublicSymbolsFeedOverride), filesToExclude: targetChannelConfig.FilenamesToExclude, flatten: targetChannelConfig.Flatten); var targetFeedConfigs = targetFeedsSetup.Setup(); // No target feeds to publish to, very likely this is an error if (targetFeedConfigs.Count() == 0) { Log.LogError($"No target feeds were found to publish the assets to."); return(false); } foreach (var feedConfig in targetFeedConfigs) { Log.LogMessage(MessageImportance.High, $"Target feed config: {feedConfig}"); TargetFeedContentType categoryKey = feedConfig.ContentType; if (!FeedConfigs.TryGetValue(categoryKey, out _)) { FeedConfigs[categoryKey] = new HashSet <TargetFeedConfig>(); } FeedConfigs[categoryKey].Add(feedConfig); } } CheckForStableAssetsInNonIsolatedFeeds(); if (Log.HasLoggedErrors) { return(false); } string temporarySymbolsLocation = ""; if (!UseStreamingPublishing) { temporarySymbolsLocation = Path.GetFullPath(Path.Combine(BlobAssetsBasePath, @"..\", "tempSymbols")); EnsureTemporaryDirectoryExists(temporarySymbolsLocation); DeleteTemporaryFiles(temporarySymbolsLocation); // Copying symbol files to temporary location is required because the symUploader API needs read/write access to the files, // since we publish blobs and symbols in parallel this will cause IO exceptions. CopySymbolFilesToTemporaryLocation(BuildModel, temporarySymbolsLocation); } using var clientThrottle = new SemaphoreSlim(MaxClients, MaxClients); await Task.WhenAll(new Task[] { HandlePackagePublishingAsync(buildAssets, clientThrottle), HandleBlobPublishingAsync(buildAssets, clientThrottle), HandleSymbolPublishingAsync( PdbArtifactsBasePath, MsdlToken, SymWebToken, SymbolPublishingExclusionsFile, PublishSpecialClrFiles, buildAssets, clientThrottle, temporarySymbolsLocation) }); DeleteTemporaryFiles(temporarySymbolsLocation); DeleteTemporaryDirectory(temporarySymbolsLocation); await PersistPendingAssetLocationAsync(client); } catch (Exception e) { Log.LogErrorFromException(e, true); } if (!Log.HasLoggedErrors) { Log.LogMessage(MessageImportance.High, "Publishing finished with success."); } return(!Log.HasLoggedErrors); }
public override async Task<bool> ExecuteAsync() { if (AnyMissingRequiredProperty()) { Log.LogError("Missing required properties. Aborting execution."); return false; } try { List<int> targetChannelsIds = new List<int>(); foreach (var channelIdStr in TargetChannels.Split(',')) { if (!int.TryParse(channelIdStr, out var channelId)) { Log.LogError($"Value '{channelIdStr}' isn't recognized as a valid Maestro++ channel ID."); continue; } targetChannelsIds.Add(channelId); } if (Log.HasLoggedErrors) { Log.LogError($"Could not parse the target channels list '{TargetChannels}'. It should be a comma separated list of integers."); return false; } SplitArtifactsInCategories(BuildModel); if (Log.HasLoggedErrors) { return false; } // Fetch Maestro record of the build. We're going to use it to get the BAR ID // of the assets being published so we can add a new location for them. IMaestroApi client = ApiFactory.GetAuthenticated(MaestroApiEndpoint, BuildAssetRegistryToken); Maestro.Client.Models.Build buildInformation = await client.Builds.GetBuildAsync(BARBuildId); Dictionary<string, HashSet<Asset>> buildAssets = CreateBuildAssetDictionary(buildInformation); foreach (var targetChannelId in targetChannelsIds) { TargetChannelConfig targetChannelConfig = PublishingConstants.ChannelInfos .Where(ci => ci.Id == targetChannelId && (ci.PublishingInfraVersion == PublishingInfraVersion.All || ci.PublishingInfraVersion == PublishingInfraVersion.Next)) .FirstOrDefault(); // Invalid channel ID was supplied if (targetChannelConfig.Equals(default(TargetChannelConfig))) { Log.LogError($"Channel with ID '{targetChannelId}' is not configured to be published to."); return false; } if (await client.Channels.GetChannelAsync(targetChannelId) == null) { Log.LogError($"Channel with ID '{targetChannelId}' does not exist in BAR."); return false; } Log.LogMessage(MessageImportance.High, $"Publishing to this target channel: {targetChannelConfig}"); var targetFeedsSetup = new SetupTargetFeedConfigV3( InternalBuild, BuildModel.Identity.IsStable.Equals("true", System.StringComparison.OrdinalIgnoreCase), BuildModel.Identity.Name, BuildModel.Identity.Commit, AzureStorageTargetFeedKey, PublishInstallersAndChecksums, targetChannelConfig.InstallersFeed, InstallersFeedKey, targetChannelConfig.ChecksumsFeed, CheckSumsFeedKey, targetChannelConfig.ShippingFeed, targetChannelConfig.TransportFeed, targetChannelConfig.SymbolsFeed, $"dotnet/{targetChannelConfig.AkaMSChannelName}", AzureDevOpsFeedsKey, BuildEngine = this.BuildEngine); var targetFeedConfigs = targetFeedsSetup.Setup(); // No target feeds to publish to, very likely this is an error if (targetFeedConfigs.Count() == 0) { Log.LogError($"No target feeds were found to publish the assets to."); return false; } foreach (var feedConfig in targetFeedConfigs) { Log.LogMessage(MessageImportance.High, $"Target feed config: {feedConfig}"); TargetFeedContentType categoryKey = feedConfig.ContentType; if (!FeedConfigs.TryGetValue(categoryKey, out _)) { FeedConfigs[categoryKey] = new HashSet<TargetFeedConfig>(); } FeedConfigs[categoryKey].Add(feedConfig); } } CheckForStableAssetsInNonIsolatedFeeds(); if (Log.HasLoggedErrors) { return false; } await Task.WhenAll(new Task[] { HandlePackagePublishingAsync(buildAssets), HandleBlobPublishingAsync(buildAssets) }); await PersistPendingAssetLocationAsync(client); } catch (Exception e) { Log.LogErrorFromException(e, true); } if (!Log.HasLoggedErrors) { Log.LogMessage(MessageImportance.High, "Publishing finished with success."); } return !Log.HasLoggedErrors; }