Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Correct installers get picked #46

Merged
merged 2 commits into from
Apr 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/src/WingetIntune.Cli/bin/Debug/net8.0/WingetIntune.Cli.dll",
"args": [
"update",
"list",
"--username", "[email protected]"
"package",
"Microsoft.SQLServerManagementStudio",
"--package-folder", "C:\\tools\\packages"
],
"cwd": "${workspaceFolder}",
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
Expand Down
8 changes: 5 additions & 3 deletions src/WingetIntune/Implementations/DefaultFileManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,14 @@ public async Task DownloadFileAsync(string url, string path, string? expectedHas
}
result.EnsureSuccessStatusCode();

if (result.Content.Headers.ContentLength > 100 * 1024 * 1024)
bool largeFile = result.Content.Headers.ContentLength > 50 * 1024 * 1024;

if (largeFile)
{
logger.LogWarning("Downloading large file {url} to {path} with size {size}", url, path, result.Content.Headers.ContentLength);
logger.LogWarning("Downloading large file {url} to {path} with size {size}MB", url, path, (result.Content.Headers.ContentLength / 1024 / 1024));
}

using (var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, bufferSize: 4096, useAsync: true))
using (var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, bufferSize: largeFile ? 81920 : 4096, useAsync: true))
{
await result.Content.CopyToAsync(fileStream, cancellationToken);
await fileStream.FlushAsync(cancellationToken);
Expand Down
47 changes: 40 additions & 7 deletions src/WingetIntune/Intune/IntuneManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,8 @@ private void LoadMsiDetails(string installerPath, ref PackageInfo packageInfo)
private void ComputeInstallerDetails(ref PackageInfo package, PackageOptions packageOptions)
{
var installer = package.GetBestFit(packageOptions.Architecture, packageOptions.InstallerContext)
?? package.GetBestFit(Architecture.Neutral, InstallerContext.Unknown)
?? package.GetBestFit(Architecture.Neutral, packageOptions.InstallerContext)
?? package.GetBestFit(packageOptions.Architecture, InstallerContext.Unknown);
if (installer == null && packageOptions.Architecture == Architecture.X64)
{
Expand All @@ -488,8 +490,14 @@ private void ComputeInstallerDetails(ref PackageInfo package, PackageOptions pac

package.InstallerUrl = new Uri(installer.InstallerUrl!);
package.InstallerFilename = Path.GetFileName(package.InstallerUrl.LocalPath.Replace(" ", ""));

if (string.IsNullOrEmpty(package.InstallerFilename))
{
package.InstallerFilename = $"{package.PackageIdentifier}_{package.Version}.{GuessInstallerExtension(installer.ParseInstallerType())}";
}

// Maybe this should be done for other installers as well?
if (installer.InstallerType!.Equals("exe", StringComparison.OrdinalIgnoreCase) && package.InstallerFilename!.EndsWith(".exe", StringComparison.OrdinalIgnoreCase) == false)
if ((installer.InstallerType!.Equals("exe", StringComparison.OrdinalIgnoreCase) || installer.InstallerType!.Equals("burn", StringComparison.OrdinalIgnoreCase)) && package.InstallerFilename!.EndsWith(".exe", StringComparison.OrdinalIgnoreCase) == false)
{
package.InstallerFilename += ".exe";
}
Expand All @@ -498,19 +506,44 @@ private void ComputeInstallerDetails(ref PackageInfo package, PackageOptions pac
package.InstallerContext = installer.ParseInstallerContext() == InstallerContext.Unknown ? (package.InstallerContext ?? packageOptions.InstallerContext) : installer.ParseInstallerContext();
package.InstallerType = installer.ParseInstallerType();
package.Installer = installer;
if (package.InstallerType.IsMsi())
{
package.MsiVersion ??= installer.AppsAndFeaturesEntries?.FirstOrDefault()?.DisplayVersion;
package.MsiProductCode ??= installer.ProductCode;
}
else
if (!package.InstallerType.IsMsi())
{
ComputeInstallerCommands(ref package, packageOptions);
}

package.MsiVersion ??= installer.AppsAndFeaturesEntries?.FirstOrDefault()?.DisplayVersion;
package.MsiProductCode ??= installer.ProductCode ?? installer.AppsAndFeaturesEntries?.FirstOrDefault()?.ProductCode;

}

private static readonly InstallerType[] SupportedInstallers = new[] { InstallerType.Inno, InstallerType.Msi, InstallerType.Burn, InstallerType.Wix, InstallerType.Nullsoft, InstallerType.Exe };
private static string GuessInstallerName(InstallerType installerType) => installerType switch
{
InstallerType.Inno => "setup.exe",
InstallerType.Msi => "setup.msi",
InstallerType.Msix => "setup.msix",
InstallerType.Appx => "setup.appx",
InstallerType.Burn => "setup.exe",
InstallerType.Wix => "setup.msi",
InstallerType.Nullsoft => "setup.exe",
InstallerType.Exe => "setup.exe",
InstallerType.Zip => "setup.zip",
_ => throw new ArgumentException("Unknown installer type", nameof(installerType))
};

private static string GuessInstallerExtension(InstallerType installerType) => installerType switch
{
InstallerType.Inno => "exe",
InstallerType.Msi => "msi",
InstallerType.Msix => "msix",
InstallerType.Appx => "appx",
InstallerType.Burn => "exe",
InstallerType.Wix => "msi",
InstallerType.Nullsoft => "exe",
InstallerType.Exe => "exe",
InstallerType.Zip => "zip",
_ => throw new ArgumentException("Unknown installer type", nameof(installerType))
};
private static readonly Dictionary<InstallerType, string> DefaultInstallerSwitches = new()
{
{ InstallerType.Inno, "/VERYSILENT /SUPPRESSMSGBOXES /NORESTART /SP-" },
Expand Down
2 changes: 2 additions & 0 deletions src/WingetIntune/Models/Enums.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public enum Architecture
X86,
X64,
Arm64,
Neutral,
}

internal static class EnumParsers
Expand Down Expand Up @@ -85,6 +86,7 @@ public static Architecture ParseArchitecture(string? input)
"x86" => Architecture.X86,
"x64" => Architecture.X64,
"arm64" => Architecture.Arm64,
"neutral" => Architecture.Neutral,
_ => Architecture.Unknown,
};
}
Expand Down
11 changes: 10 additions & 1 deletion src/WingetIntune/Models/Manifest/WingetInstallerExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,21 @@ internal static class WingetInstallerExtensions
public static Winget.CommunityRepository.Models.WingetInstaller? SingleOrDefault(this IList<Winget.CommunityRepository.Models.WingetInstaller>? installers, InstallerType installerType, Architecture architecture, InstallerContext installerContext)
{
if (installers is null || !installers.Any()) { return null; }
return installers.singleOrDefault(installerType, architecture, installerContext, "en-US")
?? installers.singleOrDefault(installerType, architecture, installerContext);
}

private static Winget.CommunityRepository.Models.WingetInstaller? singleOrDefault(this IList<Winget.CommunityRepository.Models.WingetInstaller> installers, InstallerType installerType, Architecture architecture, InstallerContext installerContext, string? locale = null)
{
return installers.FirstOrDefault(i =>
(i.ParseInstallerType() == installerType || installerType == InstallerType.Unknown)
&& (i.InstallerArchitecture() == architecture || architecture == Architecture.Unknown)
&& (i.ParseInstallerContext() == installerContext || installerContext == InstallerContext.Unknown));
&& (i.ParseInstallerContext() == installerContext || installerContext == InstallerContext.Unknown)
&& (string.IsNullOrWhiteSpace(locale) || i.InstallerLocale == locale));
}



public static InstallerContext ParseInstallerContext(this Winget.CommunityRepository.Models.WingetInstaller installer)
{
return EnumParsers.ParseInstallerContext(installer.Scope);
Expand Down
22 changes: 13 additions & 9 deletions src/WingetIntune/Models/Mapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,18 @@ public Win32LobApp ToWin32LobApp(PackageInfo packageInfo)

app.ApplicableArchitectures = ToGraphArchitecture(packageInfo.Architecture);

if (packageInfo.InstallerType.IsMsi())
if (packageInfo.MsiProductCode is not null && packageInfo.MsiVersion is not null) // packageInfo.InstallerType.IsMsi()
{
app.MsiInformation = new Win32LobAppMsiInformation
if (packageInfo.InstallerType.IsMsi())
{
ProductCode = packageInfo.MsiProductCode!,
ProductVersion = packageInfo.MsiVersion!,
Publisher = packageInfo.Publisher,
ProductName = packageInfo.DisplayName
};
app.MsiInformation = new Win32LobAppMsiInformation
{
ProductCode = packageInfo.MsiProductCode,
ProductVersion = packageInfo.MsiVersion,
Publisher = packageInfo.Publisher,
ProductName = packageInfo.DisplayName
};
}

app.DetectionRules = new List<Win32LobAppDetection>
{
Expand All @@ -83,7 +86,7 @@ public Win32LobApp ToWin32LobApp(PackageInfo packageInfo)
app.Developer = packageInfo.Publisher;
app.FileName = Path.GetFileNameWithoutExtension(packageInfo.InstallerFilename) + ".intunewin";
app.SetupFilePath = packageInfo.InstallerFilename ?? "install.ps1";
app.Notes = $"Generated by {nameof(WingetIntune)} at {DateTimeOffset.UtcNow} [WingetIntune|{packageInfo.Source}|{packageInfo.PackageIdentifier}]";
app.Notes = $"Generated by {nameof(WingetIntune)} at {DateTimeOffset.UtcNow} [WinTuner|{packageInfo.Source}|{packageInfo.PackageIdentifier}]";
return app;
}

Expand All @@ -94,7 +97,8 @@ public Win32LobApp ToWin32LobApp(PackageInfo packageInfo)
Architecture.Arm64 => WindowsArchitecture.Arm64,
Architecture.X86 => WindowsArchitecture.X86 | WindowsArchitecture.X64,
Architecture.X64 => WindowsArchitecture.X64,
_ => WindowsArchitecture.Neutral
Architecture.Neutral => WindowsArchitecture.Neutral | WindowsArchitecture.X86 | WindowsArchitecture.X64 | WindowsArchitecture.Arm64,
_ => WindowsArchitecture.None
};

public WinGetApp ToWinGetApp(MicrosoftStoreManifest storeManifest)
Expand Down
2 changes: 1 addition & 1 deletion src/WingetIntune/WingetServiceCollectionExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public static IServiceCollection AddWingetServices(this IServiceCollection servi
client.Timeout = TimeSpan.FromSeconds(180);
// Set buffer size to 500MB
// TODO: fix this in some other way, by not loading the whole file into memory.
//client.MaxResponseContentBufferSize = 1024L * 1024 * 512;
client.MaxResponseContentBufferSize = 1024L * 1024 * 50;
});
});
services.AddTransient<IFileManager, Os.DefaultFileManager>();
Expand Down