Skip to content

Commit 22f9fcf

Browse files
Unpack/Repack Pkg Tooling and Tests (#15205)
Co-authored-by: Matt Mitchell (.NET) <[email protected]>
1 parent 36dc6bd commit 22f9fcf

File tree

15 files changed

+710
-0
lines changed

15 files changed

+710
-0
lines changed

Arcade.sln

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.Internal.S
151151
EndProject
152152
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.ArcadeAzureIntegration", "src\Microsoft.DotNet.ArcadeAzureIntegration\Microsoft.DotNet.ArcadeAzureIntegration.csproj", "{CA159C84-CD7D-4364-9121-3842F97D4B60}"
153153
EndProject
154+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.DotNet.MacOsPkg", "src\Microsoft.DotNet.MacOsPkg\Microsoft.DotNet.MacOsPkg.csproj", "{CE0FAEB2-4B8A-4A37-840D-7FF88ECB42A0}"
155+
EndProject
156+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.DotNet.MacOsPkg.Tests", "src\Microsoft.DotNet.MacOsPkg.Tests\Microsoft.DotNet.MacOsPkg.Tests.csproj", "{1F5118A8-A5C5-4D18-AF34-FFB60FECCD45}"
157+
EndProject
154158
Global
155159
GlobalSection(SolutionConfigurationPlatforms) = preSolution
156160
Debug|Any CPU = Debug|Any CPU
@@ -949,6 +953,30 @@ Global
949953
{CA159C84-CD7D-4364-9121-3842F97D4B60}.Release|x64.Build.0 = Release|Any CPU
950954
{CA159C84-CD7D-4364-9121-3842F97D4B60}.Release|x86.ActiveCfg = Release|Any CPU
951955
{CA159C84-CD7D-4364-9121-3842F97D4B60}.Release|x86.Build.0 = Release|Any CPU
956+
{CE0FAEB2-4B8A-4A37-840D-7FF88ECB42A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
957+
{CE0FAEB2-4B8A-4A37-840D-7FF88ECB42A0}.Debug|Any CPU.Build.0 = Debug|Any CPU
958+
{CE0FAEB2-4B8A-4A37-840D-7FF88ECB42A0}.Debug|x64.ActiveCfg = Debug|Any CPU
959+
{CE0FAEB2-4B8A-4A37-840D-7FF88ECB42A0}.Debug|x64.Build.0 = Debug|Any CPU
960+
{CE0FAEB2-4B8A-4A37-840D-7FF88ECB42A0}.Debug|x86.ActiveCfg = Debug|Any CPU
961+
{CE0FAEB2-4B8A-4A37-840D-7FF88ECB42A0}.Debug|x86.Build.0 = Debug|Any CPU
962+
{CE0FAEB2-4B8A-4A37-840D-7FF88ECB42A0}.Release|Any CPU.ActiveCfg = Release|Any CPU
963+
{CE0FAEB2-4B8A-4A37-840D-7FF88ECB42A0}.Release|Any CPU.Build.0 = Release|Any CPU
964+
{CE0FAEB2-4B8A-4A37-840D-7FF88ECB42A0}.Release|x64.ActiveCfg = Release|Any CPU
965+
{CE0FAEB2-4B8A-4A37-840D-7FF88ECB42A0}.Release|x64.Build.0 = Release|Any CPU
966+
{CE0FAEB2-4B8A-4A37-840D-7FF88ECB42A0}.Release|x86.ActiveCfg = Release|Any CPU
967+
{CE0FAEB2-4B8A-4A37-840D-7FF88ECB42A0}.Release|x86.Build.0 = Release|Any CPU
968+
{1F5118A8-A5C5-4D18-AF34-FFB60FECCD45}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
969+
{1F5118A8-A5C5-4D18-AF34-FFB60FECCD45}.Debug|Any CPU.Build.0 = Debug|Any CPU
970+
{1F5118A8-A5C5-4D18-AF34-FFB60FECCD45}.Debug|x64.ActiveCfg = Debug|Any CPU
971+
{1F5118A8-A5C5-4D18-AF34-FFB60FECCD45}.Debug|x64.Build.0 = Debug|Any CPU
972+
{1F5118A8-A5C5-4D18-AF34-FFB60FECCD45}.Debug|x86.ActiveCfg = Debug|Any CPU
973+
{1F5118A8-A5C5-4D18-AF34-FFB60FECCD45}.Debug|x86.Build.0 = Debug|Any CPU
974+
{1F5118A8-A5C5-4D18-AF34-FFB60FECCD45}.Release|Any CPU.ActiveCfg = Release|Any CPU
975+
{1F5118A8-A5C5-4D18-AF34-FFB60FECCD45}.Release|Any CPU.Build.0 = Release|Any CPU
976+
{1F5118A8-A5C5-4D18-AF34-FFB60FECCD45}.Release|x64.ActiveCfg = Release|Any CPU
977+
{1F5118A8-A5C5-4D18-AF34-FFB60FECCD45}.Release|x64.Build.0 = Release|Any CPU
978+
{1F5118A8-A5C5-4D18-AF34-FFB60FECCD45}.Release|x86.ActiveCfg = Release|Any CPU
979+
{1F5118A8-A5C5-4D18-AF34-FFB60FECCD45}.Release|x86.Build.0 = Release|Any CPU
952980
EndGlobalSection
953981
GlobalSection(SolutionProperties) = preSolution
954982
HideSolutionNode = FALSE
@@ -986,6 +1014,8 @@ Global
9861014
{14462553-E4E1-4F67-B954-4BF24B1DAAFE} = {3C542789-2576-48C8-9772-C9D7575F7E42}
9871015
{650B7526-7B8A-45B5-B14E-C16D828891B2} = {C53DD924-C212-49EA-9BC4-1827421361EF}
9881016
{6BA81447-C61D-4F91-BF0F-5B17AF4CFFAC} = {C53DD924-C212-49EA-9BC4-1827421361EF}
1017+
{CE0FAEB2-4B8A-4A37-840D-7FF88ECB42A0} = {6DA9F58A-34D5-45A6-998E-5D2B8037C3FE}
1018+
{1F5118A8-A5C5-4D18-AF34-FFB60FECCD45} = {6DA9F58A-34D5-45A6-998E-5D2B8037C3FE}
9891019
EndGlobalSection
9901020
GlobalSection(ExtensibilityGlobals) = postSolution
9911021
SolutionGuid = {32B9C883-432E-4FC8-A1BF-090EB033DD5B}

src/Microsoft.DotNet.Arcade.Sdk/tools/DefaultVersions.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
<MicrosoftDotnetNuGetRepackTasksVersion Condition="'$(MicrosoftDotnetNuGetRepackTasksVersion)' == ''">$(ArcadeSdkVersion)</MicrosoftDotnetNuGetRepackTasksVersion>
8181
<MicrosoftDotNetSignToolVersion Condition="'$(MicrosoftDotNetSignToolVersion)' == ''">$(ArcadeSdkVersion)</MicrosoftDotNetSignToolVersion>
8282
<MicrosoftDotNetTarVersion Condition="'$(MicrosoftDotNetTarVersion)' == ''">$(ArcadeSdkVersion)</MicrosoftDotNetTarVersion>
83+
<MicrosoftDotNetMacOsPkgVersion Condition="'$(MicrosoftDotNetMacOsPkgVersion)' == ''">$(ArcadeSdkVersion)</MicrosoftDotNetMacOsPkgVersion>
8384
<MicrosoftTestPlatformVersion Condition="'$(MicrosoftTestPlatformVersion)' == ''">16.5.0</MicrosoftTestPlatformVersion>
8485

8586
<!-- Follow the instructions on how to update any of the below xunit versions: https://github.com/dotnet/arcade/blob/main/Documentation/update-xunit.md. -->

src/Microsoft.DotNet.Arcade.Sdk/tools/Tools.proj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
<PackageReference Include="Microsoft.Signed.Wix" Version="$(MicrosoftSignedWixVersion)" IsImplicitlyDefined="true" />
5757
<PackageReference Include="Microsoft.DotNet.SignTool" Version="$(MicrosoftDotNetSignToolVersion)" IsImplicitlyDefined="true" />
5858
<PackageReference Include="Microsoft.DotNet.Tar" Version="$(MicrosoftDotNetTarVersion)" IsImplicitlyDefined="true" />
59+
<PackageReference Include="Microsoft.DotNet.MacOsPkg" Version="$(MicrosoftDotNetMacOsPkgVersion)" IsImplicitlyDefined="true" />
5960
<PackageReference Include="Microsoft.SymbolUploader.Build.Task" Version="$(MicrosoftSymbolUploaderBuildTaskVersion)" Condition="'$(PublishToSymbolServer)' == 'true'" IsImplicitlyDefined="true" />
6061
<PackageReference Include="Microsoft.DotNet.Build.Tasks.VisualStudio" Version="$(MicrosoftDotNetBuildTasksVisualStudioVersion)" Condition="'$(UsingToolVSSDK)' == 'true'" IsImplicitlyDefined="true" />
6162
</ItemGroup>
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFrameworks>$(NetToolCurrent)</TargetFrameworks>
5+
<Nullable>enable</Nullable>
6+
</PropertyGroup>
7+
8+
<ItemGroup>
9+
<PackageReference Include="FluentAssertions" />
10+
<PackageReference Include="Microsoft.Build.Utilities.Core" />
11+
<PackageReference Include="Microsoft.Build.Framework" />
12+
</ItemGroup>
13+
14+
<ItemGroup>
15+
<Content Include="Resources\**"
16+
CopyToOutputDirectory="PreserveNewest" />
17+
</ItemGroup>
18+
19+
<ItemGroup>
20+
<ProjectReference Include="..\Common\Microsoft.Arcade.Test.Common\Microsoft.Arcade.Test.Common.csproj" />
21+
<ProjectReference Include="..\Microsoft.DotNet.XUnitExtensions\src\Microsoft.DotNet.XUnitExtensions.csproj" />
22+
23+
<ProjectReference Condition="$([MSBuild]::IsOSPlatform('osx'))"
24+
Include="..\Microsoft.DotNet.MacOsPkg\Microsoft.DotNet.MacOsPkg.csproj"
25+
ReferenceOutputAssembly="false"
26+
SetTargetFramework="TargetFramework=$(NetToolCurrent)"
27+
SkipGetTargetFrameworkProperties="true"
28+
Private="false"
29+
OutputItemType="_MacOsPkgToolPath" />
30+
</ItemGroup>
31+
32+
<Target Name="_CopyMacOsPkgTool" AfterTargets="ResolveProjectReferences" Condition="$([MSBuild]::IsOSPlatform('osx'))">
33+
<PropertyGroup>
34+
<_MacOSPkgToolPattern>@(_MacOsPkgToolPath->'%(RootDir)%(Directory)')**\*.*</_MacOSPkgToolPattern>
35+
</PropertyGroup>
36+
<ItemGroup>
37+
<_MacOSPkgToolFiles Include="$(_MacOsPkgToolPattern)"/>
38+
</ItemGroup>
39+
<ItemGroup>
40+
<Content Include="@(_MacOsPkgToolFiles)" CopyToOutputDirectory="PreserveNewest" Link="tools\macospkg\%(RecursiveDir)%(Filename)%(Extension)"/>
41+
</ItemGroup>
42+
</Target>
43+
44+
</Project>
1.63 KB
Binary file not shown.
Binary file not shown.
4.2 KB
Binary file not shown.
Binary file not shown.
Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using FluentAssertions;
5+
using System;
6+
using System.Collections.Generic;
7+
using System.Diagnostics;
8+
using System.IO;
9+
using System.Linq;
10+
using Xunit;
11+
using Xunit.Abstractions;
12+
13+
namespace Microsoft.DotNet.MacOsPkg.Tests
14+
{
15+
public class UnpackPackTests
16+
{
17+
private readonly ITestOutputHelper output;
18+
private static readonly string simplePkg = GetResourceFilePath("Simple.pkg");
19+
private static readonly string withAppPkg = GetResourceFilePath("WithApp.pkg");
20+
private static readonly string simpleInstallerPkg = GetResourceFilePath("SimpleInstaller.pkg");
21+
private static readonly string withAppInstallerPkg = GetResourceFilePath("WithAppInstaller.pkg");
22+
23+
private static readonly string pkgToolPath = Path.Combine(
24+
Path.GetDirectoryName(typeof(UnpackPackTests).Assembly.Location)!,
25+
"tools",
26+
"macospkg",
27+
"Microsoft.Dotnet.MacOsPkg.dll");
28+
29+
private static readonly string[] simplePkgFiles =
30+
[
31+
"Bom",
32+
"PackageInfo",
33+
Path.Combine("Payload", "Sample.txt")
34+
];
35+
36+
private static readonly string[] withAppPkgFiles =
37+
[
38+
"Bom",
39+
"PackageInfo",
40+
Path.Combine("Payload", "test.app")
41+
];
42+
43+
private static readonly string[] appFiles =
44+
[
45+
Path.Combine("Contents", "Info.plist"),
46+
Path.Combine("Contents", "MacOS", "main"),
47+
Path.Combine("Contents", "Resources", "libexample.dylib")
48+
];
49+
50+
private static readonly string[] simpleInstallerFiles =
51+
[
52+
"Distribution",
53+
"Simple.pkg"
54+
];
55+
56+
private static readonly string[] withAppInstallerFiles =
57+
[
58+
"Distribution",
59+
"WithApp.pkg"
60+
];
61+
62+
public UnpackPackTests(ITestOutputHelper output) => this.output = output;
63+
64+
[MacOSOnlyFact]
65+
public void UnpackPackSimplePkg()
66+
{
67+
string unpackPath = Path.GetTempFileName();
68+
string packPath = GetTempPkgPath();
69+
70+
ExecuteWithCleanup(() =>
71+
{
72+
Unpack(simplePkg, unpackPath, simplePkgFiles);
73+
Pack(unpackPath, packPath, simplePkgFiles);
74+
}, [ unpackPath, packPath ]);
75+
}
76+
77+
[MacOSOnlyFact]
78+
public void UnpackPackWithAppPkg()
79+
{
80+
string unpackPath = Path.GetTempFileName();
81+
string packPath = GetTempPkgPath();
82+
83+
ExecuteWithCleanup(() =>
84+
{
85+
Unpack(withAppPkg, unpackPath, withAppPkgFiles);
86+
Pack(unpackPath, packPath, withAppPkgFiles);
87+
}, [ unpackPath, packPath ]);
88+
}
89+
90+
[MacOSOnlyFact]
91+
public void UnpackPackAppBundle()
92+
{
93+
string unpackPkgPath = Path.GetTempFileName();
94+
string unpackAppPath = Path.GetTempFileName();
95+
string packAppPath = GetTempAppPath();
96+
97+
ExecuteWithCleanup(() =>
98+
{
99+
Unpack(withAppPkg, unpackPkgPath, withAppPkgFiles);
100+
Unpack(Path.Combine(unpackPkgPath, "Payload", "test.app"), unpackAppPath, appFiles);
101+
Pack(unpackAppPath, packAppPath, appFiles);
102+
}, [ unpackPkgPath, unpackAppPath ]);
103+
}
104+
105+
[MacOSOnlyFact]
106+
public void UnpackPackSimpleInstallerPkg()
107+
{
108+
string unpackPath = Path.GetTempFileName();
109+
string packPath = GetTempPkgPath();
110+
111+
ExecuteWithCleanup(() =>
112+
{
113+
Unpack(simpleInstallerPkg, unpackPath, simpleInstallerFiles);
114+
Pack(unpackPath, packPath, simpleInstallerFiles);
115+
}, [ unpackPath, packPath ]);
116+
}
117+
118+
[MacOSOnlyFact]
119+
public void UnpackPackSimplePkgInSimpleInstallerPkg()
120+
{
121+
string unpackInstallerPath = Path.GetTempFileName();
122+
string unpackComponentPath = Path.GetTempFileName();
123+
string packInstallerPath = GetTempPkgPath();
124+
125+
string componentPkgPath = Path.Combine(unpackInstallerPath, "Simple.pkg");
126+
127+
ExecuteWithCleanup(() =>
128+
{
129+
Unpack(simpleInstallerPkg, unpackInstallerPath, simpleInstallerFiles);
130+
Unpack(componentPkgPath, unpackComponentPath, simplePkgFiles);
131+
Pack(unpackComponentPath, componentPkgPath, simplePkgFiles);
132+
Pack(unpackInstallerPath, packInstallerPath, simpleInstallerFiles);
133+
}, [ unpackInstallerPath, unpackComponentPath, packInstallerPath ]);
134+
}
135+
136+
[MacOSOnlyFact]
137+
public void UnpackPackAppBundleAndWithAppPkgInWithAppInstallerPkg()
138+
{
139+
string unpackInstallerPath = Path.GetTempFileName();
140+
string unpackComponentPath = Path.GetTempFileName();
141+
string unpackAppPath = Path.GetTempFileName();
142+
string packInstallerPath = GetTempPkgPath();
143+
144+
string componentPkgPath = Path.Combine(unpackInstallerPath, "WithApp.pkg");
145+
string appPath = Path.Combine(unpackComponentPath, "Payload", "test.app");
146+
147+
ExecuteWithCleanup(() =>
148+
{
149+
Unpack(withAppInstallerPkg, unpackInstallerPath, withAppInstallerFiles);
150+
Unpack(componentPkgPath, unpackComponentPath, withAppPkgFiles);
151+
Unpack(appPath, unpackAppPath, appFiles);
152+
Pack(unpackAppPath, appPath, appFiles);
153+
Pack(unpackComponentPath, componentPkgPath, withAppPkgFiles);
154+
Pack(unpackInstallerPath, packInstallerPath, withAppInstallerFiles);
155+
}, [ unpackInstallerPath, unpackComponentPath, unpackAppPath, packInstallerPath ]);
156+
}
157+
158+
private static void ExecuteWithCleanup(Action action, List<string> cleanupPaths)
159+
{
160+
try
161+
{
162+
action();
163+
}
164+
finally
165+
{
166+
foreach (string path in cleanupPaths)
167+
{
168+
if (Directory.Exists(path))
169+
{
170+
Directory.Delete(path, true);
171+
}
172+
else if (File.Exists(path))
173+
{
174+
File.Delete(path);
175+
}
176+
}
177+
}
178+
}
179+
180+
private void Unpack(string srcPath, string dstPath, string[] expectedFiles)
181+
{
182+
RunPkgProcess(srcPath, dstPath, "unpack").Should().BeTrue();
183+
184+
Directory.Exists(dstPath).Should().BeTrue();
185+
186+
CompareContent(dstPath, expectedFiles);
187+
}
188+
189+
private void Pack(string srcPath, string dstPath, string[] expectedFiles)
190+
{
191+
RunPkgProcess(srcPath, dstPath, "pack").Should().BeTrue();
192+
193+
File.Exists(dstPath).Should().BeTrue();
194+
195+
// Unpack the packed pkg and verify the content
196+
string unpackPath = Path.GetTempFileName();
197+
ExecuteWithCleanup(() =>
198+
{
199+
Unpack(dstPath, unpackPath, expectedFiles);
200+
}, [ unpackPath ]);
201+
}
202+
203+
private bool RunPkgProcess(string inputPath, string outputPath, string action)
204+
{
205+
var process = Process.Start(new ProcessStartInfo()
206+
{
207+
FileName = "dotnet",
208+
Arguments = $@"exec ""{pkgToolPath}"" ""{inputPath}"" ""{outputPath}"" {action}",
209+
UseShellExecute = false,
210+
RedirectStandardError = true,
211+
});
212+
213+
process!.WaitForExit(60000); // 60 seconds
214+
bool success = process.ExitCode == 0;
215+
if (!success)
216+
{
217+
output.WriteLine($"Error: {process.StandardError.ReadToEnd()}");
218+
}
219+
return success;
220+
}
221+
222+
private static string GetResourceFilePath(string resourceName)
223+
{
224+
return Path.Combine(
225+
Path.GetDirectoryName(typeof(UnpackPackTests).Assembly.Location)!,
226+
"Resources",
227+
resourceName);
228+
}
229+
230+
private static string GetTempPkgPath() => $"{Path.GetTempFileName()}.pkg";
231+
232+
private static string GetTempAppPath() => $"{Path.GetTempFileName()}.app";
233+
234+
private static void CompareContent(string basePath, string[] expectedFiles)
235+
{
236+
string[] actualFiles = Directory.GetFiles(basePath, "*.*", SearchOption.AllDirectories)
237+
.Select(f => f.Substring(basePath.Length + 1))
238+
.ToArray();
239+
actualFiles.Should().BeEquivalentTo(expectedFiles);
240+
}
241+
}
242+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
namespace Microsoft.DotNet.MacOsPkg
5+
{
6+
internal static class AppBundle
7+
{
8+
internal static void Unpack(string inputPath, string outputPath)
9+
{
10+
string args = $"-V -xk {inputPath} {outputPath}";
11+
ExecuteHelper.Run("ditto", args);
12+
}
13+
14+
internal static void Pack(string inputPath, string outputPath)
15+
{
16+
string args = $"-c -k --sequesterRsrc {inputPath} {outputPath}";
17+
ExecuteHelper.Run("ditto", args);
18+
}
19+
}
20+
}

0 commit comments

Comments
 (0)