diff --git a/.azure-pipelines-shared.yml b/.azure-pipelines-shared.yml new file mode 100644 index 0000000..c1e701c --- /dev/null +++ b/.azure-pipelines-shared.yml @@ -0,0 +1,31 @@ +steps: +- task: DownloadBuildArtifacts@0 + displayName: Download Build Artifacts + inputs: + artifactName: nuget + downloadPath: $(Build.ArtifactStagingDirectory) +- bash: | + set -e + export PATH="$PATH:/$HOME/.dotnet/tools" + + mkdir test-$(command)/ + cd test-$(command) + + echo "" > NuGet.config + version=$(cat $(Build.ArtifactStagingDirectory)/nuget/version.txt) + + # Install the dotnet-$(command) tool + dotnet tool install --global dotnet-$(command) --version $version --add-source $(Build.ArtifactStagingDirectory)/nuget + + # Create a new console application + dotnet new console + + # Install and use dotnet $(command) + dotnet $(command) install + dotnet $(command) -o $(Build.ArtifactStagingDirectory)/packages/$(container) + workingDirectory: $(Pipeline.Workspace) + displayName: Run dotnet $(command) +- task: PublishBuildArtifacts@1 + inputs: + pathToPublish: $(Build.ArtifactStagingDirectory)/packages/ + artifactName: packages \ No newline at end of file diff --git a/.azure-pipelines.yml b/.azure-pipelines.yml index 81a7e45..352644f 100644 --- a/.azure-pipelines.yml +++ b/.azure-pipelines.yml @@ -1,3 +1,6 @@ +trigger: +- master + resources: containers: - container: 2.2-bionic @@ -60,36 +63,7 @@ stages: container: $[ variables['container'] ] steps: - - task: DownloadBuildArtifacts@0 - displayName: Download Build Artifacts - inputs: - artifactName: nuget - downloadPath: $(Build.ArtifactStagingDirectory) - - bash: | - set -e - export PATH="$PATH:/$HOME/.dotnet/tools" - - mkdir test-$(command)/ - cd test-$(command) - - echo "" > NuGet.config - version=$(cat $(Build.ArtifactStagingDirectory)/nuget/version.txt) - - # Install the dotnet-$(command) tool - dotnet tool install --global dotnet-$(command) --version $version --add-source $(Build.ArtifactStagingDirectory)/nuget - - # Create a new console application - dotnet new console - - # Install and use dotnet $(command) - dotnet $(command) install - dotnet $(command) -o $(Build.ArtifactStagingDirectory)/packages/$(container) - workingDirectory: $(Pipeline.Workspace) - displayName: Run dotnet $(command) - - task: PublishBuildArtifacts@1 - inputs: - pathToPublish: $(Build.ArtifactStagingDirectory)/packages/ - artifactName: packages + - template: .azure-pipelines-shared.yml - job: molecule pool: @@ -151,3 +125,42 @@ stages: searchFolder: '$(Build.SourcesDirectory)/molecule/$(suite)/molecule/default/' condition: always() displayName: Publish test results + + - job: build_macos + pool: + vmImage: 'macOS-10.14' + variables: + container: macos + command: tarball + steps: + - task: UseDotNet@2 + displayName: 'Use .NET Core 3.0' + inputs: + packageType: sdk + version: 3.0.100 + - template: .azure-pipelines-shared.yml + + - job: test_macos + pool: + vmImage: 'macOS-10.14' + dependsOn: build_macos + steps: + - task: DownloadBuildArtifacts@0 + displayName: Download Build Artifacts + inputs: + artifactName: packages + downloadPath: $(Build.ArtifactStagingDirectory)/ + - task: UseDotNet@2 + displayName: 'Use .NET Core 3.0' + inputs: + packageType: sdk + version: 3.0.100 + - bash: | + set -e + tar xvzf $(Build.ArtifactStagingDirectory)/packages/macos/test-tarball.1.0.0.tar.gz + displayName: Extract tarball + - bash: | + set -e + chmod +x ./test-tarball + ./test-tarball + displayName: Execute program \ No newline at end of file diff --git a/Packaging.Targets.Tests/Deb/DebTaskTests.cs b/Packaging.Targets.Tests/Deb/DebTaskTests.cs index 47deb64..956745e 100644 --- a/Packaging.Targets.Tests/Deb/DebTaskTests.cs +++ b/Packaging.Targets.Tests/Deb/DebTaskTests.cs @@ -1,4 +1,8 @@ -using Xunit; +using Packaging.Targets.IO; +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; namespace Packaging.Targets.Tests.Deb { @@ -32,5 +36,70 @@ namespace Packaging.Targets.Tests.Deb { Assert.Equal(packageAchitecture, DebTask.GetPackageArchitecture(runtimeIdentifier)); } + + [Fact] + public void EnsureDirectoriesTest() + { + List archiveEntries = new List(); + archiveEntries.Add( + new ArchiveEntry() + { + Mode = LinuxFileMode.S_IROTH | LinuxFileMode.S_IRGRP | LinuxFileMode.S_IRUSR | LinuxFileMode.S_IFREG, + TargetPath = "./ConsoleApp1.deps.json", + }); + + DebTask.EnsureDirectories(archiveEntries, includeRoot: false); + + // This example contains one entry in the current directory, so no new directory entries should + // have been created + Assert.Single(archiveEntries); + } + + [Fact] + public void EnsureDirectoriesTest2() + { + List archiveEntries = new List(); + archiveEntries.Add( + new ArchiveEntry() + { + Mode = LinuxFileMode.S_IROTH | LinuxFileMode.S_IRGRP | LinuxFileMode.S_IRUSR | LinuxFileMode.S_IFREG, + TargetPath = "/usr/local/share/consoleapp/ConsoleApp1.deps.json", + }); + + DebTask.EnsureDirectories(archiveEntries, includeRoot: true); + + archiveEntries = archiveEntries + .OrderBy(e => e.TargetPathWithFinalSlash, StringComparer.Ordinal) + .ToList(); + + // This example contains one entry in the current directory, so no new directory entries should + // have been created + Assert.Collection( + archiveEntries, + (e) => Assert.Equal("/", e.TargetPath), + (e) => Assert.Equal("/usr", e.TargetPath), + (e) => Assert.Equal("/usr/local", e.TargetPath), + (e) => Assert.Equal("/usr/local/share", e.TargetPath), + (e) => Assert.Equal("/usr/local/share/consoleapp", e.TargetPath), + (e) => Assert.Equal("/usr/local/share/consoleapp/ConsoleApp1.deps.json", e.TargetPath)); + } + + [Fact] + public void EnsureDirectoriesTest3() + { + List archiveEntries = new List(); + archiveEntries.Add( + new ArchiveEntry() + { + Mode = LinuxFileMode.S_IROTH | LinuxFileMode.S_IRGRP | LinuxFileMode.S_IRUSR | LinuxFileMode.S_IFREG, + TargetPath = "ConsoleApp1.deps.json", + }); + + DebTask.EnsureDirectories(archiveEntries, includeRoot: false); + + // This example contains one entry in the current directory, so no new directory entries should + // have been created + Assert.Single(archiveEntries); + } } } diff --git a/Packaging.Targets/ArchiveBuilder.cs b/Packaging.Targets/ArchiveBuilder.cs index 5d72d7c..09120ba 100644 --- a/Packaging.Targets/ArchiveBuilder.cs +++ b/Packaging.Targets/ArchiveBuilder.cs @@ -293,7 +293,14 @@ namespace Packaging.Targets if (name == null) { - name = prefix + "/" + fileName; + if (!string.IsNullOrEmpty(prefix)) + { + name = prefix + "/" + fileName; + } + else + { + name = fileName; + } } string linkTo = string.Empty; diff --git a/Packaging.Targets/DebTask.cs b/Packaging.Targets/DebTask.cs index a870007..b660d4d 100644 --- a/Packaging.Targets/DebTask.cs +++ b/Packaging.Targets/DebTask.cs @@ -192,7 +192,7 @@ namespace Packaging.Targets this.Content); archiveEntries.AddRange(archiveBuilder.FromLinuxFolders(this.LinuxFolders)); - this.EnsureDirectories(archiveEntries); + EnsureDirectories(archiveEntries); archiveEntries = archiveEntries .OrderBy(e => e.TargetPathWithFinalSlash, StringComparer.Ordinal) @@ -268,7 +268,7 @@ namespace Packaging.Targets } } - private void EnsureDirectories(List entries) + internal static void EnsureDirectories(List entries, bool includeRoot = true) { var dirs = new HashSet(entries.Where(x => x.Mode.HasFlag(LinuxFileMode.S_IFDIR)) .Select(d => d.TargetPathWithFinalSlash)); @@ -285,7 +285,7 @@ namespace Packaging.Targets if (!path.Contains("/")) { - return "/"; + return string.Empty; } return path.Substring(0, path.LastIndexOf('/')); @@ -293,7 +293,7 @@ namespace Packaging.Targets void EnsureDir(string dirPath) { - if (dirPath == string.Empty) + if (dirPath == string.Empty || dirPath == ".") { return; } @@ -325,7 +325,11 @@ namespace Packaging.Targets EnsureDir(GetDirPath(entry.TargetPathWithFinalSlash)); } - EnsureDir("/"); + if (includeRoot) + { + EnsureDir("/"); + } + entries.AddRange(toAdd); } } diff --git a/Packaging.Targets/IO/TarFileCreator.cs b/Packaging.Targets/IO/TarFileCreator.cs index ca57f92..cadd6d8 100644 --- a/Packaging.Targets/IO/TarFileCreator.cs +++ b/Packaging.Targets/IO/TarFileCreator.cs @@ -31,7 +31,14 @@ namespace Packaging.Targets.IO public static void WriteTrailer(Stream stream) { - Align(stream); + // The stream should already be aligned; as it is aligned after every entry. + // As a safety measure, for streams which can report on .Position, align the + // stream again. + if (stream.CanSeek) + { + Align(stream); + } + var trailer = new byte[1024]; stream.Write(trailer, 0, trailer.Length); } @@ -39,10 +46,10 @@ namespace Packaging.Targets.IO public static void WriteEntry(Stream stream, TarHeader header, Stream data) { header.Checksum = header.ComputeChecksum(); - stream.WriteStruct(header); - Align(stream); + int written = stream.WriteStruct(header); + Align(stream, written); data.CopyTo(stream); - Align(stream); + Align(stream, data.Length); } public static void WriteEntry(Stream stream, ArchiveEntry entry, Stream data = null) @@ -151,7 +158,12 @@ namespace Packaging.Targets.IO private static void Align(Stream stream) { - var spos = stream.Position % 512; + Align(stream, stream.Position); + } + + private static void Align(Stream stream, long position) + { + var spos = position % 512; if (spos == 0) { return; diff --git a/Packaging.Targets/Packaging.Targets.csproj b/Packaging.Targets/Packaging.Targets.csproj index 267ae54..3dbaa89 100644 --- a/Packaging.Targets/Packaging.Targets.csproj +++ b/Packaging.Targets/Packaging.Targets.csproj @@ -30,15 +30,12 @@ all - + all all - - all - @@ -46,9 +43,9 @@ true build\ - + - + true tools\netstandard2.0\ diff --git a/Packaging.Targets/StreamExtensions.cs b/Packaging.Targets/StreamExtensions.cs index 848468e..5fa2e4d 100644 --- a/Packaging.Targets/StreamExtensions.cs +++ b/Packaging.Targets/StreamExtensions.cs @@ -81,7 +81,7 @@ namespace Packaging.Targets /// /// The struct to write to the stram. /// - public static void WriteStruct(this Stream stream, T data) + public static int WriteStruct(this Stream stream, T data) where T : struct { if (stream == null) @@ -105,6 +105,7 @@ namespace Packaging.Targets RespectEndianness(bytes); stream.Write(bytes, 0, bytes.Length); + return bytes.Length; } /// diff --git a/Packaging.Targets/TarballTask.cs b/Packaging.Targets/TarballTask.cs index 1c4f966..a027ef1 100644 --- a/Packaging.Targets/TarballTask.cs +++ b/Packaging.Targets/TarballTask.cs @@ -1,11 +1,10 @@ -using ICSharpCode.SharpZipLib.GZip; -using ICSharpCode.SharpZipLib.Tar; -using ICSharpCode.SharpZipLib.Zip; -using Microsoft.Build.Framework; +using Microsoft.Build.Framework; using Microsoft.Build.Utilities; +using Packaging.Targets.IO; using System; -using System.Diagnostics; using System.IO; +using System.IO.Compression; +using System.Linq; namespace Packaging.Targets { @@ -13,17 +12,18 @@ namespace Packaging.Targets { [Required] public string PublishDir - { - get; - set; - } + { get; set; } [Required] public string TarballPath - { - get; - set; - } + { get; set; } + + [Required] + public ITaskItem[] Content + { get; set; } + + public string Prefix + { get; set; } public override bool Execute() { @@ -37,14 +37,22 @@ namespace Packaging.Targets private void CreateLinuxTarball() { - using (var stream = File.Create(this.TarballPath)) - using (var gzipStream = new GZipOutputStream(stream)) - using (var archive = TarArchive.CreateOutputTarArchive(gzipStream)) - { - archive.RootPath = this.PublishDir.Replace("\\", "/"); - var entry = TarEntry.CreateEntryFromFile(this.PublishDir); + ArchiveBuilder archiveBuilder = new ArchiveBuilder(); + var archiveEntries = archiveBuilder.FromDirectory( + this.PublishDir, + this.Prefix, + this.Content); - archive.WriteEntry(entry, true); + DebTask.EnsureDirectories(archiveEntries, includeRoot: false); + + archiveEntries = archiveEntries + .OrderBy(e => e.TargetPathWithFinalSlash, StringComparer.Ordinal) + .ToList(); + + using (var stream = File.Create(this.TarballPath)) + using (var gzipStream = new GZipStream(stream, CompressionMode.Compress)) + { + TarFileCreator.FromArchiveEntries(archiveEntries, gzipStream); } } } diff --git a/Packaging.Targets/build/Packaging.Targets.targets b/Packaging.Targets/build/Packaging.Targets.targets index 5463bc5..3353874 100644 --- a/Packaging.Targets/build/Packaging.Targets.targets +++ b/Packaging.Targets/build/Packaging.Targets.targets @@ -182,6 +182,7 @@ $(PackagePath).tar.gz + . @@ -189,7 +190,9 @@ + TarballPath="$(TarballPath)" + Prefix="$(TarballPrefix)" + Content="@(Content)" />