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

Feature/list updates #29

Merged
merged 7 commits into from
Jan 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
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