/// <summary> /// Given the nupkg file as a read-write stream with random access (e.g. MemoryStream), /// This will replace the .nuspec file with a new .nuspec generated from /// a) the old .nuspec file /// b) supplied edits /// /// This function leaves readWriteStream open. /// </summary> public static void RewriteNupkgManifest(Stream readWriteStream, IEnumerable <Action <ManifestEdit> > edits) { if (!readWriteStream.CanRead) { throw new ArgumentException("Must be a readable stream", "readWriteStream"); } if (!readWriteStream.CanWrite) { throw new ArgumentException("Must be a writeable stream", "readWriteStream"); } if (!readWriteStream.CanSeek) { throw new ArgumentException("Must be a seekable stream", "readWriteStream"); } using (var packageReader = new PackageReader(readWriteStream, leaveStreamOpen: true)) { var nuspecReader = packageReader.GetNuspecReader(); // Read <metadata> node from nuspec var metadataNode = nuspecReader.Xml.Root.Elements() .FirstOrDefault(e => StringComparer.Ordinal.Equals(e.Name.LocalName, "metadata")); if (metadataNode == null) { throw new PackagingException("The package manifest is missing the 'metadata' node."); } // Convert metadata into a ManifestEdit so that we can run it through the editing pipeline var editableManifestElements = new ManifestEdit { Title = ReadFromMetadata(metadataNode, "title"), Authors = ReadFromMetadata(metadataNode, "authors"), Copyright = ReadFromMetadata(metadataNode, "copyright"), Description = ReadFromMetadata(metadataNode, "description"), IconUrl = ReadFromMetadata(metadataNode, "iconUrl"), LicenseUrl = ReadFromMetadata(metadataNode, "licenseUrl"), ProjectUrl = ReadFromMetadata(metadataNode, "projectUrl"), ReleaseNotes = ReadFromMetadata(metadataNode, "releasenotes"), RequireLicenseAcceptance = ReadBoolFromMetadata(metadataNode, "requireLicenseAcceptance"), Summary = ReadFromMetadata(metadataNode, "summary"), Tags = ReadFromMetadata(metadataNode, "tags") }; // Perform edits foreach (var edit in edits) { edit.Invoke(editableManifestElements); } // Update the <metadata> node WriteToMetadata(metadataNode, "title", editableManifestElements.Title); WriteToMetadata(metadataNode, "authors", editableManifestElements.Authors); WriteToMetadata(metadataNode, "copyright", editableManifestElements.Copyright); WriteToMetadata(metadataNode, "description", editableManifestElements.Description); WriteToMetadata(metadataNode, "iconUrl", editableManifestElements.IconUrl); WriteToMetadata(metadataNode, "licenseUrl", editableManifestElements.LicenseUrl); WriteToMetadata(metadataNode, "projectUrl", editableManifestElements.ProjectUrl); WriteToMetadata(metadataNode, "releasenotes", editableManifestElements.ReleaseNotes); WriteToMetadata(metadataNode, "requireLicenseAcceptance", editableManifestElements.RequireLicenseAcceptance.ToString(CultureInfo.InvariantCulture).ToLowerInvariant()); WriteToMetadata(metadataNode, "summary", editableManifestElements.Summary); WriteToMetadata(metadataNode, "tags", editableManifestElements.Tags); // Update the package stream using (var newManifestStream = new MemoryStream()) { nuspecReader.Xml.Save(newManifestStream); using (var archive = new ZipArchive(readWriteStream, ZipArchiveMode.Update, leaveOpen: true)) { var manifestEntries = archive.Entries .Where(entry => entry.FullName.IndexOf("/", StringComparison.OrdinalIgnoreCase) == -1 && entry.Name.EndsWith(".nuspec", StringComparison.OrdinalIgnoreCase)).ToList(); if (manifestEntries.Count == 0) { throw new PackagingException("Nuspec file does not exist in package."); } if (manifestEntries.Count > 1) { throw new PackagingException("Package contains multiple nuspec files."); } var manifestEntry = manifestEntries[0]; using (var manifestOutputStream = manifestEntry.Open()) { manifestOutputStream.SetLength(0); newManifestStream.Position = 0; newManifestStream.CopyTo(manifestOutputStream); } } } } }
/// <summary> /// Given the nupkg file as a read-write stream with random access (e.g. MemoryStream), /// This will replace the .nuspec file with a new .nuspec generated from /// a) the old .nuspec file /// b) supplied edits /// /// This function leaves readWriteStream open. /// </summary> public static void RewriteNupkgManifest(Stream readWriteStream, IEnumerable <Action <ManifestEdit> > edits) { if (!readWriteStream.CanRead) { throw new ArgumentException(CoreStrings.StreamMustBeReadable, nameof(readWriteStream)); } if (!readWriteStream.CanWrite) { throw new ArgumentException(CoreStrings.StreamMustBeWriteable, nameof(readWriteStream)); } if (!readWriteStream.CanSeek) { throw new ArgumentException(CoreStrings.StreamMustBeSeekable, nameof(readWriteStream)); } using (var packageArchiveReader = new PackageArchiveReader(readWriteStream, leaveStreamOpen: true)) { var nuspecReader = packageArchiveReader.GetNuspecReader(); // Read <metadata> node from nuspec var metadataNode = nuspecReader.Xml.Root.Elements() .FirstOrDefault(e => StringComparer.Ordinal.Equals(e.Name.LocalName, PackageMetadataStrings.Metadata)); if (metadataNode == null) { throw new PackagingException($"The package manifest is missing the '{PackageMetadataStrings.Metadata}' node."); } // Convert metadata into a ManifestEdit so that we can run it through the editing pipeline var editableManifestElements = new ManifestEdit { Title = ReadFromMetadata(metadataNode, PackageMetadataStrings.Title), Authors = ReadFromMetadata(metadataNode, PackageMetadataStrings.Authors), Copyright = ReadFromMetadata(metadataNode, PackageMetadataStrings.Copyright), Description = ReadFromMetadata(metadataNode, PackageMetadataStrings.Description), IconUrl = ReadFromMetadata(metadataNode, PackageMetadataStrings.IconUrl), LicenseUrl = ReadFromMetadata(metadataNode, PackageMetadataStrings.LicenseUrl), ProjectUrl = ReadFromMetadata(metadataNode, PackageMetadataStrings.ProjectUrl), ReleaseNotes = ReadFromMetadata(metadataNode, PackageMetadataStrings.ReleaseNotes), RequireLicenseAcceptance = ReadBoolFromMetadata(metadataNode, PackageMetadataStrings.RequireLicenseAcceptance), Summary = ReadFromMetadata(metadataNode, PackageMetadataStrings.Summary), Tags = ReadFromMetadata(metadataNode, PackageMetadataStrings.Tags) }; var originalManifestElements = (ManifestEdit)editableManifestElements.Clone(); // Perform edits foreach (var edit in edits) { edit.Invoke(editableManifestElements); } // Update the <metadata> node // Modify metadata elements only if they are changed. // 1. Do not add empty/null elements to metadata // 2. Remove the empty/null elements from metadata after edit // Apart from Authors, Description, Id and Version all other elements are optional. // Defined by spec here: https://github.com/NuGet/NuGet.Client/blob/dev/src/NuGet.Core/NuGet.Packaging/compiler/resources/nuspec.xsd WriteToMetadata(metadataNode, PackageMetadataStrings.Title, originalManifestElements.Title, editableManifestElements.Title, canBeRemoved: true); WriteToMetadata(metadataNode, PackageMetadataStrings.Authors, originalManifestElements.Authors, editableManifestElements.Authors); WriteToMetadata(metadataNode, PackageMetadataStrings.Copyright, originalManifestElements.Copyright, editableManifestElements.Copyright, canBeRemoved: true); WriteToMetadata(metadataNode, PackageMetadataStrings.Description, originalManifestElements.Description, editableManifestElements.Description); WriteToMetadata(metadataNode, PackageMetadataStrings.IconUrl, originalManifestElements.IconUrl, editableManifestElements.IconUrl, canBeRemoved: true); WriteToMetadata(metadataNode, PackageMetadataStrings.LicenseUrl, originalManifestElements.LicenseUrl, editableManifestElements.LicenseUrl, canBeRemoved: true); WriteToMetadata(metadataNode, PackageMetadataStrings.ProjectUrl, originalManifestElements.ProjectUrl, editableManifestElements.ProjectUrl, canBeRemoved: true); WriteToMetadata(metadataNode, PackageMetadataStrings.ReleaseNotes, originalManifestElements.ReleaseNotes, editableManifestElements.ReleaseNotes, canBeRemoved: true); WriteToMetadata(metadataNode, PackageMetadataStrings.RequireLicenseAcceptance, originalManifestElements.RequireLicenseAcceptance.ToString(CultureInfo.InvariantCulture).ToLowerInvariant(), editableManifestElements.RequireLicenseAcceptance.ToString(CultureInfo.InvariantCulture).ToLowerInvariant()); WriteToMetadata(metadataNode, PackageMetadataStrings.Summary, originalManifestElements.Summary, editableManifestElements.Summary, canBeRemoved: true); WriteToMetadata(metadataNode, PackageMetadataStrings.Tags, originalManifestElements.Tags, editableManifestElements.Tags, canBeRemoved: true); // Update the package stream using (var newManifestStream = new MemoryStream()) { nuspecReader.Xml.Save(newManifestStream); using (var archive = new ZipArchive(readWriteStream, ZipArchiveMode.Update, leaveOpen: true)) { var manifestEntries = archive.Entries .Where(entry => entry.FullName.IndexOf("/", StringComparison.OrdinalIgnoreCase) == -1 && entry.Name.EndsWith(".nuspec", StringComparison.OrdinalIgnoreCase)).ToList(); if (manifestEntries.Count == 0) { throw new PackagingException("Nuspec file does not exist in package."); } if (manifestEntries.Count > 1) { throw new PackagingException("Package contains multiple nuspec files."); } var manifestEntry = manifestEntries[0]; using (var manifestOutputStream = manifestEntry.Open()) { manifestOutputStream.SetLength(0); newManifestStream.Position = 0; newManifestStream.CopyTo(manifestOutputStream); } } } } }
/// <summary> /// Given the nupkg file as a read-write stream with random access (e.g. MemoryStream), /// This will replace the .nuspec file with a new .nuspec generated from /// a) the old .nuspec file /// b) supplied edits /// /// This function leaves readWriteStream open. /// </summary> public static void RewriteNupkgManifest(Stream readWriteStream, IEnumerable<Action<ManifestEdit>> edits) { if (!readWriteStream.CanRead) { throw new ArgumentException(Strings.StreamMustBeReadable, nameof(readWriteStream)); } if (!readWriteStream.CanWrite) { throw new ArgumentException(Strings.StreamMustBeWriteable, nameof(readWriteStream)); } if (!readWriteStream.CanSeek) { throw new ArgumentException(Strings.StreamMustBeSeekable, nameof(readWriteStream)); } using (var packageArchiveReader = new PackageArchiveReader(readWriteStream, leaveStreamOpen: true)) { var nuspecReader = packageArchiveReader.GetNuspecReader(); // Read <metadata> node from nuspec var metadataNode = nuspecReader.Xml.Root.Elements() .FirstOrDefault(e => StringComparer.Ordinal.Equals(e.Name.LocalName, PackageMetadataStrings.Metadata)); if (metadataNode == null) { throw new PackagingException($"The package manifest is missing the '{PackageMetadataStrings.Metadata}' node."); } // Convert metadata into a ManifestEdit so that we can run it through the editing pipeline var editableManifestElements = new ManifestEdit { Title = ReadFromMetadata(metadataNode, PackageMetadataStrings.Title), Authors = ReadFromMetadata(metadataNode, PackageMetadataStrings.Authors), Copyright = ReadFromMetadata(metadataNode, PackageMetadataStrings.Copyright), Description = ReadFromMetadata(metadataNode, PackageMetadataStrings.Description), IconUrl = ReadFromMetadata(metadataNode, PackageMetadataStrings.IconUrl), LicenseUrl = ReadFromMetadata(metadataNode, PackageMetadataStrings.LicenseUrl), ProjectUrl = ReadFromMetadata(metadataNode, PackageMetadataStrings.ProjectUrl), ReleaseNotes = ReadFromMetadata(metadataNode, PackageMetadataStrings.ReleaseNotes), RequireLicenseAcceptance = ReadBoolFromMetadata(metadataNode, PackageMetadataStrings.RequireLicenseAcceptance), Summary = ReadFromMetadata(metadataNode, PackageMetadataStrings.Summary), Tags = ReadFromMetadata(metadataNode, PackageMetadataStrings.Tags) }; var originalManifestElements = (ManifestEdit)editableManifestElements.Clone(); // Perform edits foreach (var edit in edits) { edit.Invoke(editableManifestElements); } // Update the <metadata> node // Modify metadata elements only if they are changed. // 1. Do not add empty/null elements to metadata // 2. Remove the empty/null elements from metadata after edit // Apart from Authors, Description, Id and Version all other elements are optional. // Defined by spec here: https://github.com/NuGet/NuGet.Client/blob/dev/src/NuGet.Core/NuGet.Packaging/compiler/resources/nuspec.xsd WriteToMetadata(metadataNode, PackageMetadataStrings.Title, originalManifestElements.Title, editableManifestElements.Title, canBeRemoved: true); WriteToMetadata(metadataNode, PackageMetadataStrings.Authors, originalManifestElements.Authors, editableManifestElements.Authors); WriteToMetadata(metadataNode, PackageMetadataStrings.Copyright, originalManifestElements.Copyright, editableManifestElements.Copyright, canBeRemoved: true); WriteToMetadata(metadataNode, PackageMetadataStrings.Description, originalManifestElements.Description, editableManifestElements.Description); WriteToMetadata(metadataNode, PackageMetadataStrings.IconUrl, originalManifestElements.IconUrl, editableManifestElements.IconUrl, canBeRemoved: true); WriteToMetadata(metadataNode, PackageMetadataStrings.LicenseUrl, originalManifestElements.LicenseUrl, editableManifestElements.LicenseUrl, canBeRemoved: true); WriteToMetadata(metadataNode, PackageMetadataStrings.ProjectUrl, originalManifestElements.ProjectUrl, editableManifestElements.ProjectUrl, canBeRemoved: true); WriteToMetadata(metadataNode, PackageMetadataStrings.ReleaseNotes, originalManifestElements.ReleaseNotes, editableManifestElements.ReleaseNotes, canBeRemoved: true); WriteToMetadata(metadataNode, PackageMetadataStrings.RequireLicenseAcceptance, originalManifestElements.RequireLicenseAcceptance.ToString(CultureInfo.InvariantCulture).ToLowerInvariant(), editableManifestElements.RequireLicenseAcceptance.ToString(CultureInfo.InvariantCulture).ToLowerInvariant()); WriteToMetadata(metadataNode, PackageMetadataStrings.Summary, originalManifestElements.Summary, editableManifestElements.Summary, canBeRemoved: true); WriteToMetadata(metadataNode, PackageMetadataStrings.Tags, originalManifestElements.Tags, editableManifestElements.Tags, canBeRemoved: true); // Update the package stream using (var newManifestStream = new MemoryStream()) { nuspecReader.Xml.Save(newManifestStream); using (var archive = new ZipArchive(readWriteStream, ZipArchiveMode.Update, leaveOpen: true)) { var manifestEntries = archive.Entries .Where(entry => entry.FullName.IndexOf("/", StringComparison.OrdinalIgnoreCase) == -1 && entry.Name.EndsWith(".nuspec", StringComparison.OrdinalIgnoreCase)).ToList(); if (manifestEntries.Count == 0) { throw new PackagingException("Nuspec file does not exist in package."); } if (manifestEntries.Count > 1) { throw new PackagingException("Package contains multiple nuspec files."); } var manifestEntry = manifestEntries[0]; using (var manifestOutputStream = manifestEntry.Open()) { manifestOutputStream.SetLength(0); newManifestStream.Position = 0; newManifestStream.CopyTo(manifestOutputStream); } } } } }
/// <summary> /// Given the nupkg file as a read-write stream with random access (e.g. MemoryStream), /// This will replace the .nuspec file with a new .nuspec generated from /// a) the old .nuspec file /// b) supplied edits /// /// This function leaves readWriteStream open. /// </summary> public static void RewriteNupkgManifest(Stream readWriteStream, IEnumerable<Action<ManifestEdit>> edits) { if (!readWriteStream.CanRead) { throw new ArgumentException(Strings.StreamMustBeReadable, nameof(readWriteStream)); } if (!readWriteStream.CanWrite) { throw new ArgumentException(Strings.StreamMustBeWriteable, nameof(readWriteStream)); } if (!readWriteStream.CanSeek) { throw new ArgumentException(Strings.StreamMustBeSeekable, nameof(readWriteStream)); } using (var packageReader = new PackageReader(readWriteStream, leaveStreamOpen: true)) { var nuspecReader = packageReader.GetNuspecReader(); // Read <metadata> node from nuspec var metadataNode = nuspecReader.Xml.Root.Elements() .FirstOrDefault(e => StringComparer.Ordinal.Equals(e.Name.LocalName, "metadata")); if (metadataNode == null) { throw new PackagingException("The package manifest is missing the 'metadata' node."); } // Convert metadata into a ManifestEdit so that we can run it through the editing pipeline var editableManifestElements = new ManifestEdit { Title = ReadFromMetadata(metadataNode, "title"), Authors = ReadFromMetadata(metadataNode, "authors"), Copyright = ReadFromMetadata(metadataNode, "copyright"), Description = ReadFromMetadata(metadataNode, "description"), IconUrl = ReadFromMetadata(metadataNode, "iconUrl"), LicenseUrl = ReadFromMetadata(metadataNode, "licenseUrl"), ProjectUrl = ReadFromMetadata(metadataNode, "projectUrl"), ReleaseNotes = ReadFromMetadata(metadataNode, "releasenotes"), RequireLicenseAcceptance = ReadBoolFromMetadata(metadataNode, "requireLicenseAcceptance"), Summary = ReadFromMetadata(metadataNode, "summary"), Tags = ReadFromMetadata(metadataNode, "tags") }; // Perform edits foreach (var edit in edits) { edit.Invoke(editableManifestElements); } // Update the <metadata> node WriteToMetadata(metadataNode, "title", editableManifestElements.Title); WriteToMetadata(metadataNode, "authors", editableManifestElements.Authors); WriteToMetadata(metadataNode, "copyright", editableManifestElements.Copyright); WriteToMetadata(metadataNode, "description", editableManifestElements.Description); WriteToMetadata(metadataNode, "iconUrl", editableManifestElements.IconUrl); WriteToMetadata(metadataNode, "licenseUrl", editableManifestElements.LicenseUrl); WriteToMetadata(metadataNode, "projectUrl", editableManifestElements.ProjectUrl); WriteToMetadata(metadataNode, "releasenotes", editableManifestElements.ReleaseNotes); WriteToMetadata(metadataNode, "requireLicenseAcceptance", editableManifestElements.RequireLicenseAcceptance.ToString(CultureInfo.InvariantCulture).ToLowerInvariant()); WriteToMetadata(metadataNode, "summary", editableManifestElements.Summary); WriteToMetadata(metadataNode, "tags", editableManifestElements.Tags); // Update the package stream using (var newManifestStream = new MemoryStream()) { nuspecReader.Xml.Save(newManifestStream); using (var archive = new ZipArchive(readWriteStream, ZipArchiveMode.Update, leaveOpen: true)) { var manifestEntries = archive.Entries .Where(entry => entry.FullName.IndexOf("/", StringComparison.OrdinalIgnoreCase) == -1 && entry.Name.EndsWith(".nuspec", StringComparison.OrdinalIgnoreCase)).ToList(); if (manifestEntries.Count == 0) { throw new PackagingException("Nuspec file does not exist in package."); } if (manifestEntries.Count > 1) { throw new PackagingException("Package contains multiple nuspec files."); } var manifestEntry = manifestEntries[0]; using (var manifestOutputStream = manifestEntry.Open()) { manifestOutputStream.SetLength(0); newManifestStream.Position = 0; newManifestStream.CopyTo(manifestOutputStream); } } } } }