diff --git a/.vscode/launch.json b/.vscode/launch.json index c970406..1878a7c 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -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", "admin@codingstephan.onmicrosoft.com" + "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 diff --git a/src/WingetIntune/Implementations/DefaultFileManager.cs b/src/WingetIntune/Implementations/DefaultFileManager.cs index abb145d..172dbb9 100644 --- a/src/WingetIntune/Implementations/DefaultFileManager.cs +++ b/src/WingetIntune/Implementations/DefaultFileManager.cs @@ -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); diff --git a/src/WingetIntune/Intune/IntuneManager.cs b/src/WingetIntune/Intune/IntuneManager.cs index 5ff5678..09b5856 100644 --- a/src/WingetIntune/Intune/IntuneManager.cs +++ b/src/WingetIntune/Intune/IntuneManager.cs @@ -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) { @@ -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"; } @@ -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 DefaultInstallerSwitches = new() { { InstallerType.Inno, "/VERYSILENT /SUPPRESSMSGBOXES /NORESTART /SP-" }, diff --git a/src/WingetIntune/Models/Enums.cs b/src/WingetIntune/Models/Enums.cs index 696e17e..78eb7d1 100644 --- a/src/WingetIntune/Models/Enums.cs +++ b/src/WingetIntune/Models/Enums.cs @@ -44,6 +44,7 @@ public enum Architecture X86, X64, Arm64, + Neutral, } internal static class EnumParsers @@ -85,6 +86,7 @@ public static Architecture ParseArchitecture(string? input) "x86" => Architecture.X86, "x64" => Architecture.X64, "arm64" => Architecture.Arm64, + "neutral" => Architecture.Neutral, _ => Architecture.Unknown, }; } diff --git a/src/WingetIntune/Models/Manifest/WingetInstallerExtensions.cs b/src/WingetIntune/Models/Manifest/WingetInstallerExtensions.cs index b4a5a22..47db880 100644 --- a/src/WingetIntune/Models/Manifest/WingetInstallerExtensions.cs +++ b/src/WingetIntune/Models/Manifest/WingetInstallerExtensions.cs @@ -5,12 +5,21 @@ internal static class WingetInstallerExtensions public static Winget.CommunityRepository.Models.WingetInstaller? SingleOrDefault(this IList? 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 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); diff --git a/src/WingetIntune/Models/Mapper.cs b/src/WingetIntune/Models/Mapper.cs index 840e6b1..f6fa783 100644 --- a/src/WingetIntune/Models/Mapper.cs +++ b/src/WingetIntune/Models/Mapper.cs @@ -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 { @@ -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; } @@ -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) diff --git a/src/WingetIntune/WingetServiceCollectionExtension.cs b/src/WingetIntune/WingetServiceCollectionExtension.cs index da7d498..b1e18a2 100644 --- a/src/WingetIntune/WingetServiceCollectionExtension.cs +++ b/src/WingetIntune/WingetServiceCollectionExtension.cs @@ -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();