/// <summary> /// 获取插件加载信息 /// </summary> /// <param name="metadata">数据元素</param> /// <returns>ActivatorData</returns> private static ActivatorData GetActivatorData(AddinMetadata metadata) { ActivatorData activator = new ActivatorData(); activator.Policy = metadata.Immediate ? ActivatorPolicy.Immediate : ActivatorPolicy.Lazy; return(activator); }
internal AddinRecord(AddinMetadata metadata) { _addinMetadata = metadata; _addinHeader = new AddinHeaderRecord(); _addinFilePack = new AddinFilePack(); //OperationStatus = AddinOperationStatus.Unaffected; }
/// <summary> /// Get Runtime Data For RuntimeData /// </summary> /// <returns></returns> private static RuntimeData GetRuntimeDataForRuntimeData(AddinMetadata metadata) { RuntimeData result = new RuntimeData(); GetDependencyForRuntimeData(result, metadata); GetAssemblyDataForRuntimeData(result, metadata); return(result); }
internal AddinRecord(AddinHeaderRecord addinHeader, AddinFilePack addinFilePack, AddinActivatorRecord addinActivator) { _addinFilePack = addinFilePack; _addinHeader = addinHeader; _addinActivator = addinActivator; //OperationStatus = AddinOperationStatus.Unaffected; _addinMetadata = new AddinMetadata(); }
/// <summary> /// 获取组件信息 /// </summary> /// <param name="metadata">数据元素</param> /// <returns>BundleInfoData</returns> private static BundleInfoData GetBundleInfoData(AddinMetadata metadata) { BundleInfoData bundle = new BundleInfoData(); bundle.AssemblyVersion = metadata.AssemblyVersion; bundle.Company = metadata.Company; bundle.Copyright = metadata.Copyright; bundle.ContactAddress = metadata.Path; bundle.Description = metadata.Description; bundle.Title = metadata.Title; return(bundle); }
/// <summary> /// Get Assembly Data For RuntimeData /// </summary> /// <returns></returns> private static void GetAssemblyDataForRuntimeData(RuntimeData runtimeData, AddinMetadata metadata) { if (null != metadata.Runtime.Items && metadata.Runtime.Items.Length > 0) { foreach (ImportInfo de in metadata.Runtime.Items) { AssemblyData dd = new AssemblyData(); dd.AssemblyPatch = de.assembly; dd.IsWeb = de.isweb; runtimeData.SetAssembly(dd); } } }
/// <summary> /// Get Dependency For RuntimeData /// </summary> /// <returns></returns> private static void GetDependencyForRuntimeData(RuntimeData runtimeData, AddinMetadata metadata) { if (null != metadata.Runtime.Items1 && metadata.Runtime.Items1.Length > 0) { foreach (Dependency de in metadata.Runtime.Items1) { DependencyData dd = new DependencyData(); dd.AssemblyName = de.AssemblyName; dd.BundleSymbolicName = de.BundleSymbolicName; runtimeData.AddDependency(dd); } } }
private IconAnalysisResult AnalyzeIcon(AddinMetadata addin, byte[] recommendedIcon, IEnumerable <KeyValuePair <AddinType, byte[]> > fancyIcons) { if (addin.EmbeddedIcon != null) { return(AnalyzeEmbeddedIcon(addin.EmbeddedIcon, addin.Type, recommendedIcon, fancyIcons)); } else if (addin.IconUrl != null) { return(AnalyzeLinkedIcon(addin.IconUrl)); } else { return(IconAnalysisResult.Unspecified); } }
/// <summary> /// 获取XML扩张数据信息 /// </summary> /// <param name="metadata">The metadata.</param> /// <returns>ExtensionData</returns> private static ExtensionData GetXmlForExtensionData(AddinMetadata metadata) { ExtensionData dd = new ExtensionData(); //获取配置信息 //将配置信息加入组件信息中 if (null != metadata.ApplicationMenu) { var menuItem = metadata.ApplicationMenu.Items.Where(o => o.Language == BundleRuntime.CultureInfoLanguage); var enumerable = menuItem as ExtendsMenu[] ?? menuItem.ToArray(); if (enumerable.Any()) { dd.ApplicationExtends.ApplicationIco = metadata.ApplicationMenu.ApplicationIco; foreach (var extendsMenu in enumerable) { dd.ApplicationExtends.ApplicationName = extendsMenu.ApplicationName; dd.ApplicationExtends.Language = extendsMenu.Language; if (extendsMenu.Items.Any()) { foreach (var de in extendsMenu.Items) { List <LoadOtherContainerPanles> loadOther = null; if (de.Items != null) { loadOther = de.Items.ToList(); } dd.ApplicationExtends.MenuList.Add(new ExtendionMenu() { Caption = de.Caption, ClassForm = de.ClassForm, Group = de.Group, MenuIco = de.MenuIco, ToolTip = de.ToolTip, PluginDockStyle = de.DockType, Width = de.Width, High = de.High, Sort = de.Sort, LoadOtherContainer = loadOther }); } } } } } return(dd); }
/// <summary> /// Get Xml For BundleData /// </summary> /// <param name="dirName">Name of the dir.</param> /// <returns>BundleData.</returns> internal static BundleData GetXmlForBundleData(string dirName) { BundleData result = new BundleData(); //获取配置信息 AddinMetadata metadata = SearchXml(dirName); //将配置信息加入组件信息中 if (null != metadata) { try { result.Name = metadata.Name; result.SymbolicName = metadata.Name; result.Path = metadata.Path; result.Runtime = GetRuntimeDataForRuntimeData(metadata); result.Extensions = GetXmlForExtensionData(metadata); result.PageSerivce = GetXmlForPageServiceData(metadata); result.Enable = metadata.Enabled; result.Immediate = metadata.Immediate; result.Description = metadata.Description; result.Activator = GetActivatorData(metadata); result.BundleInfo = GetBundleInfoData(metadata); result.StartLevel = metadata.StartLevel; result.Company = metadata.Company; result.Copyright = metadata.Copyright; result.Product = metadata.Product; result.AppSettings = metadata.AppSettings; if (metadata.AssemblyVersion == null) { metadata.AssemblyVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString(); } result.Version = metadata.AssemblyVersion; if (metadata.ApplicationMenu != null && !string.IsNullOrEmpty(metadata.ApplicationMenu.ApplicationIco)) { result.ApplicationIco = metadata.ApplicationMenu.ApplicationIco; } } catch (ArgumentNullException ex) { FileLogUtility.Error(dirName + ex.Message); } } return(result); }
/// <summary> /// Gets the XML for page service data. /// </summary> /// <param name="metadata">The metadata.</param> /// <returns>PageServiceData.</returns> private static PageServiceData GetXmlForPageServiceData(AddinMetadata metadata) { PageServiceData page = new PageServiceData(); if (null != metadata.PageService) { page.PageServicePoint = metadata.PageService.ServicePoint; foreach (var pageServiceData in metadata.PageService.Items) { page.PageNodeList.Add(new PageNode() { PageName = pageServiceData.PageName, PagePlguinClassValue = pageServiceData.PagePlguinClassValue, PageType = pageServiceData.PageType }); } } return(page); }
private async Task DownloadSymbolsPackage(DiscoveryContext context, AddinMetadata package) { var packageFileName = Path.Combine(context.PackagesFolder, $"{package.Name}.{package.NuGetPackageVersion}.snupkg"); if (!File.Exists(packageFileName)) { // Delete prior versions of this package foreach (var f in Directory.EnumerateFiles(context.PackagesFolder, $"{package.Name}.*.snupkg")) { var expectedSplitLength = package.Name.Split('.').Length + package.NuGetPackageVersion.Split('.').Length; var fileName = Path.GetFileNameWithoutExtension(f); if (fileName.Split('.').Length == expectedSplitLength) { File.Delete(f); } } // Download the latest version of the symbols package try { var response = await context.HttpClient .GetAsync($"https://www.nuget.org/api/v2/symbolpackage/{package.Name}/{package.NuGetPackageVersion}") .ConfigureAwait(false); if (response.IsSuccessStatusCode) { await using var getStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); await using var fileStream = File.OpenWrite(packageFileName); await getStream.CopyToAsync(fileStream).ConfigureAwait(false); } } catch (Exception e) { throw new Exception($"An error occured while attempting to download symbol package for '{package.Name} {package.NuGetPackageVersion}'", e); } } }
static void ParseAddinConfig(Addin addin) { Log <PluginManager> .Debug("Processing config file for \"{0}\".", addin.Name); Assembly addinAssembly = Assembly.LoadFile(addin.AddinFile); string addinManifestName = addinAssembly.GetManifestResourceNames().FirstOrDefault(res => res.Contains("addin.xml")); if (string.IsNullOrEmpty(addinManifestName)) { Log <PluginManager> .Warn("Could not find addin manifest for '{0}'.", addin.AddinFile); return; } using (Stream s = addinAssembly.GetManifestResourceStream(addinManifestName)) { XmlDocument addinManifest = new XmlDocument(); addinManifest.Load(s); if (!AddinMetadata.ContainsKey(addin)) { AddinMetadata[addin] = new Dictionary <string, string> (); } foreach (XmlAttribute a in addinManifest.SelectSingleNode("/Addin").Attributes) { AddinMetadata [addin] [a.Name] = a.Value; } } AddinMetadata [addin] ["AssemblyFullName"] = addinAssembly.FullName; if (AddinMetadata [addin].ContainsKey("icon") && AddinMetadata [addin] ["icon"].EndsWith("@")) { AddinMetadata [addin] ["icon"] = string.Format("{0}{1}", AddinMetadata [addin] ["icon"], addinAssembly.FullName); } }
private async Task DownloadNugetPackage(DownloadResource nugetClient, DiscoveryContext context, AddinMetadata package) { var packageFileName = Path.Combine(context.PackagesFolder, $"{package.Name}.{package.NuGetPackageVersion}.nupkg"); if (!File.Exists(packageFileName)) { // Delete prior versions of this package foreach (var f in Directory.EnumerateFiles(context.PackagesFolder, $"{package.Name}.*.nupkg")) { var expectedSplitLength = package.Name.Split('.').Length + package.NuGetPackageVersion.Split('.').Length; var fileName = Path.GetFileNameWithoutExtension(f); if (fileName.Split('.').Length == expectedSplitLength) { File.Delete(f); } } // Download the latest version of the package using var sourceCacheContext = new SourceCacheContext() { NoCache = true }; var downloadContext = new PackageDownloadContext(sourceCacheContext, Path.GetTempPath(), true); var packageIdentity = new PackageIdentity(package.Name, new NuGet.Versioning.NuGetVersion(package.NuGetPackageVersion)); using var result = await nugetClient.GetDownloadResourceResultAsync(packageIdentity, downloadContext, string.Empty, NullLogger.Instance, CancellationToken.None).ConfigureAwait(false); switch (result.Status) { case DownloadResourceResultStatus.Cancelled: throw new OperationCanceledException(); case DownloadResourceResultStatus.NotFound: throw new Exception($"Package '{package.Name} {package.NuGetPackageVersion}' not found"); default: { await using var fileStream = File.OpenWrite(packageFileName); await result.PackageStream.CopyToAsync(fileStream).ConfigureAwait(false); break; } } } }
public async Task ExecuteAsync(DiscoveryContext context, TextWriter log) { var nugetPackageMetadataClient = await context.NugetRepository.GetResourceAsync <PackageMetadataResource>().ConfigureAwait(false); var addinPackages = new List <IPackageSearchMetadata>(); //-------------------------------------------------- // Get the metadata from NuGet.org if (!string.IsNullOrEmpty(context.Options.AddinName)) { // Get metadata for one specific package var packageMetadata = await FetchPackageMetadata(nugetPackageMetadataClient, context.Options.AddinName).ConfigureAwait(false); if (packageMetadata != null) { addinPackages.AddRange(packageMetadata); } } else { // Get the most recent "stable" version of packages matching the naming convention. await foreach (var packageMetadata in SearchForPackages(context.NugetRepository, "Cake", false, CancellationToken.None)) { addinPackages.Add(packageMetadata); } // Get the most recent version of packages matching the naming convention (regardless of the stable/prerelease status) await foreach (var packageMetadata in SearchForPackages(context.NugetRepository, "Cake", true, CancellationToken.None)) { addinPackages.Add(packageMetadata); } // Get metadata for the packages we specifically want to include foreach (var additionalPackageName in context.IncludedAddins) { var packageMetadata = await FetchPackageMetadata(nugetPackageMetadataClient, additionalPackageName).ConfigureAwait(false); if (packageMetadata != null) { addinPackages.AddRange(packageMetadata); } } } //-------------------------------------------------- // Select the most recent "stable" release of each addin. // If an addin does not have any stable release, select the most recent, regardless of its stable/prerelease status var uniqueAddinPackages = addinPackages .GroupBy(p => p.Identity.Id) .Select(g => g .OrderBy(p => p.Identity.Version.IsPrerelease ? 1 : 0) // Stable versions are sorted first, prerelease versions sorted second .ThenByDescending(p => p.Published) .First()) .ToArray(); //-------------------------------------------------- // Convert metadata from nuget into our own metadata context.Addins = await uniqueAddinPackages .ForEachAsync( async package => { // As of June 2019, the 'Owners' metadata value returned from NuGet is always null. // This code is just in case they add this information to the metadata and don't let us know. // See feature request: https://github.com/NuGet/NuGetGallery/issues/5647 var packageOwners = package.Owners? .Split(',', StringSplitOptions.RemoveEmptyEntries) .Select(owner => owner.Trim()) .ToArray() ?? Array.Empty <string>(); var tags = package.Tags? .Split(new[] { ' ', ',' }, StringSplitOptions.RemoveEmptyEntries) .Select(tag => tag.Trim()) .ToArray() ?? Array.Empty <string>(); var addinMetadata = new AddinMetadata() { AnalysisResult = new AddinAnalysisResult() { CakeRecipeIsUsed = false, CakeRecipeVersion = null, CakeRecipeIsPrerelease = false, CakeRecipeIsLatest = false }, Maintainer = package.Authors, Description = package.Description, ProjectUrl = package.ProjectUrl, IconUrl = package.IconUrl, Name = package.Identity.Id, NuGetPackageUrl = new Uri($"https://www.nuget.org/packages/{package.Identity.Id}/"), NuGetPackageOwners = packageOwners, NuGetPackageVersion = package.Identity.Version.ToNormalizedString(), IsDeprecated = false, IsPrerelease = package.Identity.Version.IsPrerelease, HasPrereleaseDependencies = false, Tags = tags, Type = AddinType.Unknown, PublishedOn = Constants.UtcMinDateTime, RepoContent = ImmutableDictionary <string, Stream> .Empty }; if (package.Title.Contains("[DEPRECATED]", StringComparison.OrdinalIgnoreCase)) { addinMetadata.IsDeprecated = true; addinMetadata.AnalysisResult.Notes = package.Description; } else { // The metadata returned by nugetSearchClient.SearchAsync is minimal. // That's why we need to invoke nugetPackageMetadataClient.GetMetadataAsync to get more detailed metadata. var packageMetadata = await nugetPackageMetadataClient.GetMetadataAsync(package.Identity.Id, true, false, new SourceCacheContext(), NullLogger.Instance, CancellationToken.None).ConfigureAwait(false); var detailedPackageMetadata = packageMetadata.SingleOrDefault(m => m.Identity.Equals(package.Identity)); if (detailedPackageMetadata != null) { addinMetadata.PublishedOn = detailedPackageMetadata.Published.Value; } // We need to look at the most recent version (even if that's not the version that would otherwise be analyzed) // to determine if the package has been deprecated var mostRecentPackageMetadata = packageMetadata.OrderByDescending(p => p.Published).FirstOrDefault(); var deprecationMetadata = mostRecentPackageMetadata != null ? await mostRecentPackageMetadata.GetDeprecationMetadataAsync().ConfigureAwait(false) : null; if (deprecationMetadata != null) { // Derive a message based on the 'Reasons' enumeration in case an actual message has not been provided var deprecationReasonMessage = default(string); if (deprecationMetadata.Reasons == null || !deprecationMetadata.Reasons.Any()) { deprecationReasonMessage = "This package has been deprecated but the author has not provided a reason."; } else if (deprecationMetadata.Reasons.Count() == 1) { deprecationReasonMessage = "This package has been deprecated for the following reason: " + deprecationMetadata.Reasons.First(); } else { deprecationReasonMessage = "This package has been deprecated for the following reasons: " + string.Join(", ", deprecationMetadata.Reasons); } addinMetadata.IsDeprecated = true; addinMetadata.AnalysisResult.Notes = deprecationMetadata.Message ?? deprecationReasonMessage; } } return(addinMetadata); }, Constants.MAX_NUGET_CONCURENCY) .ConfigureAwait(false); }
private async Task <IssueComment> AddCommentAsync(bool debugging, DiscoveryContext context, AddinMetadata addin) { var comment = new StringBuilder(); comment.AppendLine($"We performed a follow up automated audit of your Cake addin and found that some (or all) issues previously identified have not been resolved.{Environment.NewLine}"); comment.AppendLine($"We strongly encourage you to make the modifications previously highlighted.{Environment.NewLine}"); if (addin.AnalysisResult.Icon == IconAnalysisResult.RawgitUrl) { comment.AppendLine($"In particular would would like to highlight the fact that you use the rawgit CDN to serve your addin's icon. On October 8 2018 the maintainer of rawgit made the [announcement](https://rawgit.com/) that rawgit would shutdown in October 2019. Therefore it's **urgent** that you change your addin's icon URL to the new recommended URL: `{Constants.NEW_CAKE_CONTRIB_ICON_URL}`.{Environment.NewLine}"); } if (addin.AnalysisResult.Icon != IconAnalysisResult.EmbeddedCakeContrib && addin.AnalysisResult.Icon != IconAnalysisResult.EmbeddedFancyCakeContrib) { comment.AppendLine($"Please also note that the recommendation changed following .netcore3.0's release: you should now embedded the icon in your Nuget package. Read more about embedded icons in the [.nuspec reference](https://docs.microsoft.com/en-us/nuget/reference/nuspec#icon).{Environment.NewLine}"); } comment.AppendLine($"{Environment.NewLine}This comment was created by a tool: Cake.AddinDiscoverer version {context.Version}{Environment.NewLine}"); IssueComment issueComment = null; try { if (debugging) { await File.WriteAllTextAsync(Path.Combine(context.TempFolder, $"Comment_{addin.Name}.txt"), comment.ToString()).ConfigureAwait(false); } else { issueComment = await context.GithubClient.Issue.Comment.Create(addin.RepositoryOwner, addin.RepositoryName, addin.AuditIssue.Number, comment.ToString()).ConfigureAwait(false); } } catch (ApiException e) when(e.ApiError.Message.EqualsIgnoreCase("Issues are disabled for this repo")) { // There's a NuGet package with a project URL that points to a fork which doesn't allow issue. // Therefore it's safe to ignore this error. } catch (ApiException e) when(e.StatusCode == System.Net.HttpStatusCode.NotFound) { // I know of at least one case where the URL in the NuGet metadata points to a repo that has been deleted. // Therefore it's safe to ignore this error. } #pragma warning disable CS0168 // Variable is declared but never used catch (Exception e) #pragma warning restore CS0168 // Variable is declared but never used { Debugger.Break(); throw; } finally { // This delay is important to avoid triggering GitHub's abuse protection await Misc.RandomGithubDelayAsync().ConfigureAwait(false); } return(issueComment); }
private async Task <Issue> CreateIssueAsync(bool debugging, DiscoveryContext context, AddinMetadata addin, CakeVersion recommendedCakeVersion) { var issuesDescription = new StringBuilder(); if (addin.AnalysisResult.CakeCoreVersion == Constants.UNKNOWN_VERSION) { issuesDescription.AppendLine($"- [ ] We were unable to determine what version of Cake.Core your addin is referencing. Please make sure you are referencing {recommendedCakeVersion.Version}"); } else if (!addin.AnalysisResult.CakeCoreVersion.IsUpToDate(recommendedCakeVersion.Version)) { issuesDescription.AppendLine($"- [ ] You are currently referencing Cake.Core {addin.AnalysisResult.CakeCoreVersion}. Please upgrade to {recommendedCakeVersion.Version}"); } if (addin.AnalysisResult.CakeCommonVersion == Constants.UNKNOWN_VERSION) { issuesDescription.AppendLine($"- [ ] We were unable to determine what version of Cake.Common your addin is referencing. Please make sure you are referencing {recommendedCakeVersion.Version}"); } else if (!addin.AnalysisResult.CakeCommonVersion.IsUpToDate(recommendedCakeVersion.Version)) { issuesDescription.AppendLine($"- [ ] You are currently referencing Cake.Common {addin.AnalysisResult.CakeCommonVersion}. Please upgrade to {recommendedCakeVersion.Version}"); } if (!addin.AnalysisResult.CakeCoreIsPrivate) { issuesDescription.AppendLine($"- [ ] The Cake.Core reference should be private. Specifically, your addin's `.csproj` should have a line similar to this: `<PackageReference Include=\"Cake.Core\" Version=\"{recommendedCakeVersion.Version}\" PrivateAssets=\"All\" />`"); } if (!addin.AnalysisResult.CakeCommonIsPrivate) { issuesDescription.AppendLine($"- [ ] The Cake.Common reference should be private. Specifically, your addin's `.csproj` should have a line similar to this: `<PackageReference Include=\"Cake.Common\" Version=\"{recommendedCakeVersion.Version}\" PrivateAssets=\"All\" />`"); } if (!Misc.IsFrameworkUpToDate(addin.Frameworks, recommendedCakeVersion)) { issuesDescription.AppendLine($"- [ ] Your addin should target {recommendedCakeVersion.RequiredFramework} at a minimum. Optionally, your addin can also multi-target {string.Join(" or ", recommendedCakeVersion.OptionalFrameworks)}."); } switch (addin.AnalysisResult.Icon) { case IconAnalysisResult.Unspecified: case IconAnalysisResult.RawgitUrl: case IconAnalysisResult.CustomUrl: issuesDescription.AppendLine("- [ ] The nuget package for your addin should embed the cake-contrib icon. Specifically, your addin's `.csproj` should have a line like this: `<PackageIcon>path/to/icon.png</PackageIcon>`."); break; case IconAnalysisResult.EmbeddedCustom: issuesDescription.AppendLine("- [ ] The icon embedded in your nuget package doesn't appear to be the cake-contrib icon. We strongly recommend that you use the icon [available here](https://github.com/cake-contrib/graphics/blob/master/png/cake-contrib-medium.png)."); break; } if (issuesDescription.Length == 0) { return(null); } // Create a new issue var issueBody = $"We performed an automated audit of your Cake addin and found that it does not follow all the best practices.{Environment.NewLine}{Environment.NewLine}"; issueBody += $"We encourage you to make the following modifications:{Environment.NewLine}{Environment.NewLine}"; issueBody += issuesDescription.ToString(); issueBody += $"{Environment.NewLine}{Environment.NewLine}{Environment.NewLine}"; issueBody += $"Apologies if this is already being worked on, or if there are existing open issues, this issue was created based on what is currently published for this package on NuGet.{Environment.NewLine}"; issueBody += $"{Environment.NewLine}This issue was created by a tool: Cake.AddinDiscoverer version {context.Version}{Environment.NewLine}"; var newIssue = new NewIssue(Constants.ISSUE_TITLE) { Body = issueBody.ToString() }; Issue issue = null; try { if (debugging) { await File.WriteAllTextAsync(Path.Combine(context.TempFolder, $"Issue_{addin.Name}.txt"), issueBody.ToString()).ConfigureAwait(false); } else { issue = await context.GithubClient.Issue.Create(addin.RepositoryOwner, addin.RepositoryName, newIssue).ConfigureAwait(false); } } catch (ApiException e) when(e.ApiError.Message.EqualsIgnoreCase("Issues are disabled for this repo")) { // There's a NuGet package with a project URL that points to a fork which doesn't allow issues. // Therefore it's safe to ignore this error. } catch (ApiException e) when(e.StatusCode == System.Net.HttpStatusCode.NotFound) { // I know of at least one case where the URL in the NuGet metadata points to a repo that has been deleted. // Therefore it's safe to ignore this error. } #pragma warning disable CS0168 // Variable is declared but never used catch (Exception e) #pragma warning restore CS0168 // Variable is declared but never used { Debugger.Break(); throw; } finally { // This delay is important to avoid triggering GitHub's abuse protection await Misc.RandomGithubDelayAsync().ConfigureAwait(false); } return(issue); }
private async Task FixNuspec(DiscoveryContext context, AddinMetadata addin, CakeVersion cakeVersion, IList <(string CommitMessage, IEnumerable <string> FilesToDelete, IEnumerable <(EncodingType Encoding, string Path, string Content)> FilesToUpsert)> commits)