Add support for partial tarball extraction

This commit is contained in:
Nikola Milosavljevic 2023-03-10 23:06:01 +00:00
parent aeb53228be
commit 3b2ceb2072

View file

@ -5,8 +5,12 @@
using Microsoft.Build.Framework; using Microsoft.Build.Framework;
using Microsoft.Build.Utilities; using Microsoft.Build.Utilities;
using System; using System;
#if !NETFRAMEWORK
using System.Formats.Tar;
#endif
using System.IO; using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.Linq;
namespace Microsoft.DotNet.Build.Tasks namespace Microsoft.DotNet.Build.Tasks
{ {
@ -33,7 +37,7 @@ namespace Microsoft.DotNet.Build.Tasks
public bool CleanDestination { get; set; } public bool CleanDestination { get; set; }
/// <summary> /// <summary>
/// A list of directories, semicolon deliminated, relative to the root of the archive to include. If empty all directories will be copied. /// A list of directories, relative to the root of the archive to include. If empty all directories will be copied.
/// </summary> /// </summary>
public ITaskItem[] DirectoriesToCopy { get; set; } public ITaskItem[] DirectoriesToCopy { get; set; }
@ -71,22 +75,26 @@ namespace Microsoft.DotNet.Build.Tasks
public override bool Execute() public override bool Execute()
{ {
bool retVal = true; bool retVal = true;
bool isZipArchive = Path.GetExtension(SourceArchive).Equals(".zip", StringComparison.OrdinalIgnoreCase);
bool isTarballArchive = SourceArchive.EndsWith(".tar.gz", StringComparison.OrdinalIgnoreCase);
// Inherits from ToolTask in order to shell out to tar. // Inherits from ToolTask in order to shell out to tar for complete extraction
// If the file is a .zip, then don't call the base Execute method, just run as a normal task // If the file is a .zip, then don't call the base Execute method, just run as a normal task
if (Path.GetExtension(SourceArchive).Equals(".zip", StringComparison.OrdinalIgnoreCase)) // If the file is a .tar.gz, and DirectoriesToCopy isn't empty, also run a normal task.
if (isZipArchive || isTarballArchive)
{ {
if (ValidateParameters()) if (ValidateParameters())
{ {
if (DirectoriesToCopy != null && DirectoriesToCopy.Length != 0) if (DirectoriesToCopy != null && DirectoriesToCopy.Length != 0)
{ {
var zip = new ZipArchive(File.OpenRead(SourceArchive)); // Partial archive extraction
string loc = DestinationDirectory; if (isZipArchive)
foreach (var entry in zip.Entries)
{ {
foreach (var directory in DirectoriesToCopy) var zip = new ZipArchive(File.OpenRead(SourceArchive));
string loc = DestinationDirectory;
foreach (var entry in zip.Entries)
{ {
if (entry.FullName.StartsWith(directory.ItemSpec)) if (ShouldExtractItem(entry.FullName))
{ {
if (!Directory.Exists(Path.Combine(DestinationDirectory, Path.GetDirectoryName(entry.FullName)))) if (!Directory.Exists(Path.Combine(DestinationDirectory, Path.GetDirectoryName(entry.FullName))))
{ {
@ -98,16 +106,57 @@ namespace Microsoft.DotNet.Build.Tasks
} }
} }
} }
else
{
#if NETFRAMEWORK
// Run the base tool, which uses external 'tar' command
retVal = base.Execute();
#else
// Decompress GZip content
using FileStream compressedFileStream = File.Open(SourceArchive, FileMode.Open);
using var decompressor = new GZipStream(compressedFileStream, CompressionMode.Decompress);
using var decompressedStream = new MemoryStream();
decompressor.CopyTo(decompressedStream);
decompressedStream.Seek(0, SeekOrigin.Begin);
// Extract Tar content
using TarReader tr = new TarReader(decompressedStream);
while (tr.GetNextEntry() is TarEntry tarEntry)
{
if (tarEntry.EntryType != TarEntryType.Directory)
{
string entryName = tarEntry.Name;
entryName = entryName.StartsWith("./") ? entryName[2..] : entryName;
if (ShouldExtractItem(entryName))
{
Log.LogMessage(entryName);
string destinationPath = Path.Combine(DestinationDirectory, entryName);
Directory.CreateDirectory(Path.GetDirectoryName(destinationPath));
tarEntry.ExtractToFile(destinationPath, overwrite: true);
}
}
}
#endif
}
} }
else else
{ {
// Complete archive extraction
if (isZipArchive)
{
#if NETFRAMEWORK #if NETFRAMEWORK
// .NET Framework doesn't have overload to overwrite files // .NET Framework doesn't have overload to overwrite files
ZipFile.ExtractToDirectory(SourceArchive, DestinationDirectory); ZipFile.ExtractToDirectory(SourceArchive, DestinationDirectory);
#else #else
ZipFile.ExtractToDirectory(SourceArchive, DestinationDirectory, overwriteFiles: true); ZipFile.ExtractToDirectory(SourceArchive, DestinationDirectory, overwriteFiles: true);
#endif #endif
}
else
{
// Run the base tool, which uses external 'tar' command
retVal = base.Execute();
}
} }
} }
else else
@ -129,6 +178,17 @@ namespace Microsoft.DotNet.Build.Tasks
return retVal; return retVal;
} }
private bool ShouldExtractItem(string path)
{
if (DirectoriesToCopy != null)
{
return DirectoriesToCopy.Any(p => path.StartsWith(p.ItemSpec));
}
return false;
}
protected override string ToolName protected override string ToolName
{ {
get { return "tar"; } get { return "tar"; }