Skip to content

Commit

Permalink
Merge pull request #29 from svrooij/feature/list-updates
Browse files Browse the repository at this point in the history
Feature/list updates
  • Loading branch information
svrooij authored Jan 9, 2024
2 parents 6f15dab + 1830812 commit bc5a503
Show file tree
Hide file tree
Showing 18 changed files with 1,040 additions and 298 deletions.
25 changes: 25 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": ".NET Core Launch (console)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "dotnet: build-cli",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/src/WingetIntune.Cli/bin/Debug/net7.0/WingetIntune.Cli.dll",
"args": [
"update",
"list",
"--username", "[email protected]"
],
"cwd": "${workspaceFolder}",
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
"console": "internalConsole",
"stopAtEntry": false
}
]
}
15 changes: 15 additions & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "shell",
"label": "dotnet: build-cli",
"command": "dotnet build ${workspaceFolder}/src/WingetIntune.Cli/WingetIntune.Cli.csproj",
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": "$msCompile"
}
]
}
33 changes: 17 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
# Winget Intune packager CLI
# WinTuner CLI

[![GitHub issues](https://img.shields.io/github/issues/svrooij/wingetintune?style=for-the-badge)](https://github.com/svrooij/WingetIntune/issues)
[![Github sponsors](https://img.shields.io/github/sponsors/svrooij?style=for-the-badge&logo=github&logoColor=white)](https://github.com/sponsors/svrooij)

[Documentation](https://wintuner.app/)

Take any (just msi installers for now) app from winget and upload it to Intune in minutes.

- Downloading the installer and the logo
Expand All @@ -16,7 +18,6 @@ Some commands run the `winget` in the background and are thus Windows-only, make
The `package` and `publish` commands are cross-platform, and should work on any platform that supports dotnet 7. These commands no longer use the winget executable, which also means any other sources than `winget` are no longer supported.
The `msi` command is still windows only, as it uses the `Microsoft.Deployment.WindowsInstaller` package.


[![LinkedIn Profile][badge_linkedin]][link_linkedin]
[![Link Mastodon][badge_mastodon]][link_mastodon]
[![Follow on Twitter][badge_twitter]][link_twitter]
Expand Down Expand Up @@ -53,10 +54,10 @@ The CLI has several commands, try them out yourself.

```Shell
Description:
winget-intune by @svrooij allows you to package any winget app for Intune
wintuner by @svrooij allows you to package any winget app for Intune

Usage:
winget-intune [command] [options]
wintuner [command] [options]

Options:
--version Show version information
Expand All @@ -80,14 +81,14 @@ You can also expect a `detection.ps1` file, that you should configure to be used
It will also write a `app.json` file with all the information about the app, for automation purposes.
```Shell
winget-intune package {PackageId} [--version {version}] [--source winget] --package-folder {PackageFolder}
wintuner package {PackageId} [--version {version}] [--source winget] --package-folder {PackageFolder}
```
> The `packageId` ~~is case sensitive, so make sure you use the correct casing~~ will be matches against any package in the open source [index](https://github.com/svrooij/winget-pkgs-index). Tip: Copy it from the result of the `winget search {name}` command.
> The `packageId` ~~is case sensitive, so make sure you use the correct casing~~ will be matches against any package in the open source [index](https://wintuner.app/docs/related/winget-package-index). Tip: Copy it from the result of the `winget search {name}` command.
Upon downloading the installer, the SHA256 hash is checked against the one in the `winget` manifest, to make sure you won't package a tampered installer.

The packaging command uses an open-source & cross-platform [implementation](https://github.com/Svrooij/ContentPrep) of the Windows-only & closed source [content-prep-tool](https://github.com/Microsoft/Microsoft-Win32-Content-Prep-Tool), to allow cross-platform building of the packages.
The packaging command uses an open-source & cross-platform [implementation](https://wintuner.app/docs/related/content-prep-tool) of the Windows-only & closed source [content-prep-tool](https://github.com/Microsoft/Microsoft-Win32-Content-Prep-Tool), to allow cross-platform building of the packages.
This new implementation is available as a dotnet library and a PowerShell module, so if you're into Intune packaging, check it out.
### Publish
Expand All @@ -97,10 +98,10 @@ Not all packages will work for publishing, you can always try to manually upload

```Shell
# This app uses the built-in windows authentication, this will trigger a login prompt (or do sso).
winget-intune publish {PackageId} --package-folder {PackageFolder} --tenant {TenantId} --username {Username}
wintuner publish {PackageId} --package-folder {PackageFolder} --tenant {TenantId} --username {Username}
# You can also provide a token, this is useful for automation.
winget-intune publish {PackageId} --package-folder {PackageFolder} --token {Token}
wintuner publish {PackageId} --package-folder {PackageFolder} --token {Token}
```

#### Assignement and categories
Expand All @@ -109,22 +110,22 @@ You can also assign the app to a group, and set the categories.

```Shell
# Add --category "Productivity" --category "Utilities" to the command to set the categories (use the exact name!)
winget-intune publish {PackageId} ... --category "Productivity" --category "Utilities"
wintuner publish {PackageId} ... --category "Productivity" --category "Utilities"
# Add --available "group-guid" to make the app available to a group (use the guid of the group)
# Add --available "allusers" to make the app available to all users
# Instead of --available you can also use --required to make the app required for the group
# Or if you want to remove the app for that group, use --uninstall
winget-intune publish {PackageId}... --required "3bac8336-623f-46bf-bcab-b5c61e3e5b7a" --required "allusers"
winget-intune publish {PackageId}... --uninstall "3bac8336-623f-46bf-bcab-b5c61e3e5b7a" --uninstall "allusers"
wintuner publish {PackageId}... --required "3bac8336-623f-46bf-bcab-b5c61e3e5b7a" --required "allusers"
wintuner publish {PackageId}... --uninstall "3bac8336-623f-46bf-bcab-b5c61e3e5b7a" --uninstall "allusers"
```

#### Auto-package

You can also combine the `package` and `publish` command into one command, this will package the app and publish it to Intune. But this makes debugging harder, so when submitting issues, please don't use this option.
```Shell
winget-intune publish {PackageId}... --auto-package
wintuner publish {PackageId}... --auto-package
```
## Library (soon)
Expand All @@ -137,10 +138,10 @@ If you want to contribute to this project, please check out the [contributing](h

## Usefull information

- [WinTuner website](https://wintuner.app/)
- [Blog articles on Intune](https://svrooij.io/tags/intune/)
- Open-source [winget index](https://github.com/svrooij/winget-pkgs-index/)
- Open-source [PowerShell Content Prep](https://github.com/svrooij/contentprep)
- Closed source [Microsoft Win32 Content Prep tool](https://github.com/microsoft/Microsoft-Win32-Content-Prep-Tool)
- Open-source [winget index](https://wintuner.app/docs/related/winget-package-index)
- Open-source [PowerShell Content Prep](https://wintuner.app/docs/related/content-prep-tool)

[badge_blog]: https://img.shields.io/badge/blog-svrooij.io-blue?style=for-the-badge
[badge_linkedin]: https://img.shields.io/badge/LinkedIn-stephanvanrooij-blue?style=for-the-badge&logo=linkedin
Expand Down
5 changes: 3 additions & 2 deletions src/WingetIntune.Cli/Commands/AboutCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,14 @@ private Task HandleCommand(InvocationContext invocationContext)
Console.Write(header);
Console.WriteLine("#########################################################");
Console.WriteLine("#");
Console.WriteLine("# Winget-Intune");
Console.WriteLine("# WinTuner");
Console.WriteLine("# By Stephan van Rooij");
Console.WriteLine("#");
Console.WriteLine("# command: winget-intune");
Console.WriteLine("# command: wintuner");
Console.WriteLine("#");
Console.WriteLine("# version: {0}", Assembly.GetExecutingAssembly()?.GetName()?.Version?.ToString());
Console.WriteLine("# Repo: https://github.com/svrooij/wingetintune");
Console.WriteLine("# Docs: https://wintuner.app/docs/category/wintuner---cli");
Console.WriteLine("#");
Console.WriteLine("# dotnet tool update --global SvRooij.Winget-Intune.Cli");
Console.WriteLine("#");
Expand Down
39 changes: 39 additions & 0 deletions src/WingetIntune.Cli/Commands/UpdateCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using System.CommandLine;
using WingetIntune.Models;

namespace WingetIntune.Commands;

internal class UpdateCommand : Command
{
private const string name = "update";
private const string description = "Update a published app in Intune (cross platform)";

public UpdateCommand() : base(name, description)
{
AddCommand(new UpdateListCommand());
}
}

internal class UpdateCommandOptions : WinGetRootCommand.DefaultOptions
{
public string? PackageFolder { get; set; }
public string? Tenant { get; set; }
public string? Username { get; set; }
public string? Token { get; set; }

internal Intune.IntunePublishOptions GetPublishOptions()
{
return new Intune.IntunePublishOptions
{
Tenant = Tenant,
Username = Username,
Token = Token
};
}
}

internal class UpdateAbleIntuneApp : IntuneApp
{
public string? LatestVersion { get; set; }
public bool IsUpdateAvailable => LatestVersion != null && LatestVersion != Version;
}
72 changes: 72 additions & 0 deletions src/WingetIntune.Cli/Commands/UpdateListCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
using System.CommandLine;
using System.CommandLine.Hosting;
using System.CommandLine.Invocation;
using System.CommandLine.NamingConventionBinder;
using System.Text.Json;
using ConsoleTables;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using WingetIntune.Models;

namespace WingetIntune.Commands;

internal class UpdateListCommand : Command
{
private const string name = "list";
private const string description = "Show the list of published apps in Intune (cross platform)";

public UpdateListCommand() : base(name, description)
{
AddOption(PublishCommand.TenantOption);
AddOption(PublishCommand.UsernameOption);
AddOption(PublishCommand.TokenOption);
this.Handler = CommandHandler.Create(HandleCommandAsync);
}

private async Task<int> HandleCommandAsync(UpdateCommandOptions options, InvocationContext context)
{
var cancellationToken = context.GetCancellationToken();

var host = context.GetHost();
options.AdjustLogging(host);

var logger = host.Services.GetRequiredService<ILogger<UpdateListCommand>>();
logger.LogInformation("Getting list of published apps");
var repo = host.Services.GetRequiredService<Winget.CommunityRepository.WingetRepository>();
var intuneManager = host.Services.GetRequiredService<IntuneManager>();

var apps = await intuneManager.GetPublishedAppsAsync(options.GetPublishOptions(), cancellationToken);

var result = await GetUpdateAbleAppsAsync(apps, repo, cancellationToken);
if (options.Json)
{
Console.WriteLine(JsonSerializer.Serialize(result!));
return 0;
}
var table = new ConsoleTable("PackageId", "Version", "LatestVersion", "UpdateAvailable");
foreach (var app in result.OrderByDescending(a => a.IsUpdateAvailable).ThenBy(a => a.PackageId))
{
table.AddRow(app.PackageId, app.Version, app.LatestVersion, app.IsUpdateAvailable);
}
table.Write(Format.Minimal);
return 0;
}

private static async Task<IEnumerable<UpdateAbleIntuneApp>> GetUpdateAbleAppsAsync(IEnumerable<IntuneApp> apps, Winget.CommunityRepository.WingetRepository repo, CancellationToken cancellationToken)
{
var result = new List<UpdateAbleIntuneApp>();
foreach (var app in apps)
{
var latestVersion = await repo.GetLatestVersion(app.PackageId, cancellationToken);
result.Add(new UpdateAbleIntuneApp
{
GraphId = app.GraphId,
PackageId = app.PackageId,
Name = app.Name,
Version = app.Version,
LatestVersion = latestVersion,
});
}
return result;
}
}
3 changes: 2 additions & 1 deletion src/WingetIntune.Cli/Commands/WinGetRootCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@ internal class WinGetRootCommand : RootCommand

public WinGetRootCommand()
{
Description = "winget-intune by @svrooij allows you to package any winget app for Intune";
Description = "WinTuner by @svrooij allows you to package any winget app for Intune. Documentation: https://wintuner.app/docs/category/wintuner---cli";
// Cross platform commands
AddCommand(new PackageCommand());
AddCommand(new PublishCommand());
AddCommand(new AboutCommand());
AddCommand(new GenerateIndexCommand());
AddCommand(new UpdateCommand());

// Windows only command
AddCommand(new InstallOrUpgradeCommand());
Expand Down
9 changes: 5 additions & 4 deletions src/WingetIntune.Cli/WingetIntune.Cli.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,18 @@
<SelfContained>false</SelfContained>
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
<Authors>Stephan van Rooij</Authors>
<Copyright>2023</Copyright>
<Copyright>2024</Copyright>
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
<PackageLicenseFile>LICENSE.txt</PackageLicenseFile>
<PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryType>git</RepositoryType>
<PackageProjectUrl>https://github.com/svrooij/WingetIntune</PackageProjectUrl>
<PackageProjectUrl>https://wintuner.app/docs/category/wintuner---cli</PackageProjectUrl>
<RepositoryUrl>https://github.com/svrooij/WingetIntune.git</RepositoryUrl>
<PackageTags>Intune;Winget</PackageTags>
<PackageTags>Intune;Winget;WinTuner</PackageTags>
<DefaultNamespace>WingetIntune</DefaultNamespace>
<PackageId>SvRooij.Winget-Intune.Cli</PackageId>
<PackAsTool>true</PackAsTool>
<ToolCommandName>winget-intune</ToolCommandName>
<ToolCommandName>wintuner</ToolCommandName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<NoWarn>1701;1702;CS8618</NoWarn>
Expand All @@ -40,6 +40,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Azure.Identity" Version="1.10.4" />
<PackageReference Include="ConsoleTables" Version="2.4.2" />
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
<PackageReference Include="System.CommandLine.Hosting" Version="0.4.0-alpha.22272.1" />
Expand Down
Loading

0 comments on commit bc5a503

Please sign in to comment.