Skip to content

Add daily update check and upgrade command to handle upgrading from CLI#424

Open
zateutsch wants to merge 6 commits intomainfrom
zt/392-upgrade-command
Open

Add daily update check and upgrade command to handle upgrading from CLI#424
zateutsch wants to merge 6 commits intomainfrom
zt/392-upgrade-command

Conversation

@zateutsch
Copy link
Copy Markdown
Contributor

@zateutsch zateutsch commented Apr 13, 2026

fixes #392

Copilot AI review requested due to automatic review settings April 13, 2026 23:40
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a daily “new version available” notification and a winapp upgrade command so the CLI can guide (and in some cases perform) self-upgrades based on install channel, addressing issue #392.

Changes:

  • Introduces ICliUpgradeService / CliUpgradeService and wires it into startup to check for updates once per day.
  • Adds a new upgrade command and registers it in the root command/help categories and CLI schema output.
  • Updates the npm-generated command wrappers (including run --symbols) and refreshes schema references.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
src/winapp-npm/src/winapp-commands.ts Regenerated npm command wrappers/docs; adds run --symbols and updates manifest wording.
src/winapp-CLI/WinApp.Cli/Services/ICliUpgradeService.cs New interface + InstallChannel enum for upgrade/update-check behavior.
src/winapp-CLI/WinApp.Cli/Services/CliUpgradeService.cs Implements daily update check + upgrade flows for MSIX/exe and instructions for npm/NuGet.
src/winapp-CLI/WinApp.Cli/Program.cs Invokes update check during startup (non-quiet, non-json/schema modes).
src/winapp-CLI/WinApp.Cli/NativeMethods.txt Adds GetCurrentPackageFullName for MSIX identity detection.
src/winapp-CLI/WinApp.Cli/Helpers/HostBuilderExtensions.cs Registers CliUpgradeService and hooks up UpgradeCommand handler.
src/winapp-CLI/WinApp.Cli/Commands/WinAppRootCommand.cs Adds upgrade to root command and “Setup” help category.
src/winapp-CLI/WinApp.Cli/Commands/UpgradeCommand.cs New winapp upgrade command delegating to ICliUpgradeService.
src/winapp-CLI/WinApp.Cli.Tests/CliUpgradeServiceTests.cs Adds tests for install channel detection, cache file behavior, and command registration.
docs/cli-schema.json Adds upgrade command metadata to the published CLI schema.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +105 to +109
var lines = await File.ReadAllLinesAsync(cacheFile.FullName, cancellationToken);
if (lines.Length >= 1
&& DateTime.TryParse(lines[0], out var lastCheck)
&& (DateTime.UtcNow - lastCheck).TotalHours < CheckIntervalHours)
{
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cache timestamp is written using the round-trip (O) format, but it’s parsed with DateTime.TryParse(...) which is culture-dependent and may interpret the value with an unexpected Kind. Consider parsing with CultureInfo.InvariantCulture + DateTimeStyles.RoundtripKind (or using DateTimeOffset) to ensure the 24h comparison is correct across locales.

Copilot uses AI. Check for mistakes.
Comment thread src/winapp-npm/src/winapp-commands.ts Outdated
Comment thread src/winapp-CLI/WinApp.Cli/Program.cs Outdated
if (!quiet)
{
var cliUpgradeService = serviceProvider.GetRequiredService<ICliUpgradeService>();
await cliUpgradeService.CheckAndNotifyAsync();
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CheckAndNotifyAsync() is awaited during startup without passing a cancellation token or enforcing a short timeout. If GitHub is slow/unreachable on first run (no cache yet), this can noticeably delay every CLI command. Consider linking a CancellationTokenSource with a small timeout inside the update check (or run it fire-and-forget) so the CLI remains responsive.

Suggested change
await cliUpgradeService.CheckAndNotifyAsync();
try
{
await cliUpgradeService.CheckAndNotifyAsync().WaitAsync(TimeSpan.FromSeconds(2));
}
catch (TimeoutException)
{
// Keep startup responsive if the update check is slow/unreachable.
}
catch (OperationCanceledException)
{
// Keep startup responsive if the timed wait is canceled.
}

Copilot uses AI. Check for mistakes.
Comment on lines +31 to +40
// Check caller env var (set by wrapper scripts via --caller option)
var caller = Environment.GetEnvironmentVariable("WINAPP_CLI_CALLER");
if (string.Equals(caller, "npm", StringComparison.OrdinalIgnoreCase))
{
return InstallChannel.Npm;
}
if (string.Equals(caller, "nuget-package", StringComparison.OrdinalIgnoreCase))
{
return InstallChannel.NuGet;
}
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DetectInstallChannel() checks WINAPP_CLI_CALLER for the literal value "npm", but the Node.js wrapper sets WINAPP_CLI_CALLER to "nodejs-package" (see src/winapp-npm/src/winapp-cli-utils.ts). This means the install channel can be misdetected when the exe path heuristics don't match. Consider accepting the actual wrapper values (e.g., "nodejs-package") or aligning the wrapper/CLI caller strings.

Copilot uses AI. Check for mistakes.
Comment on lines +369 to +372
var result = Windows.Win32.PInvoke.GetCurrentPackageFullName(ref length, null);
// ERROR_INSUFFICIENT_BUFFER (122) means the app has package identity
// APPMODEL_ERROR_NO_PACKAGE (15700) means it does not
return result == Windows.Win32.Foundation.WIN32_ERROR.ERROR_INSUFFICIENT_BUFFER;
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HasMsixPackageIdentity() calls Windows.Win32.PInvoke.GetCurrentPackageFullName(ref length, null) outside an unsafe context. CsWin32 typically generates pointer-based overloads for this API, which require an unsafe block even when passing null. Consider wrapping this call in unsafe { ... } (like LongPathHelper does for other P/Invokes), or using a safe overload (e.g., an empty Span<char>) if one is generated.

Suggested change
var result = Windows.Win32.PInvoke.GetCurrentPackageFullName(ref length, null);
// ERROR_INSUFFICIENT_BUFFER (122) means the app has package identity
// APPMODEL_ERROR_NO_PACKAGE (15700) means it does not
return result == Windows.Win32.Foundation.WIN32_ERROR.ERROR_INSUFFICIENT_BUFFER;
unsafe
{
var result = Windows.Win32.PInvoke.GetCurrentPackageFullName(ref length, null);
// ERROR_INSUFFICIENT_BUFFER (122) means the app has package identity
// APPMODEL_ERROR_NO_PACKAGE (15700) means it does not
return result == Windows.Win32.Foundation.WIN32_ERROR.ERROR_INSUFFICIENT_BUFFER;
}

Copilot uses AI. Check for mistakes.
Comment thread src/winapp-npm/src/winapp-commands.ts Outdated
/** Format output as JSON */
json?: boolean;
/** Path to the appxmanifest.xml (default: auto-detect from input folder or current directory) */
/** Path to the Package.appxmanifest (default: auto-detect from input folder or current directory) */
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The generated wrapper types/documentation now imply --manifest for run only accepts Package.appxmanifest, but the native CLI help still describes --manifest as appxmanifest.xml (and the implementation supports both via manifest auto-detection). To avoid misleading consumers, consider updating the schema generation/description to mention both appxmanifest.xml and Package.appxmanifest.

Suggested change
/** Path to the Package.appxmanifest (default: auto-detect from input folder or current directory) */
/** Path to the app manifest file, such as appxmanifest.xml or Package.appxmanifest (default: auto-detect from input folder or current directory) */

Copilot uses AI. Check for mistakes.
Comment thread src/winapp-npm/src/winapp-commands.ts Outdated
/** Format output as JSON */
json?: boolean;
/** Path to the appxmanifest.xml (default: auto-detect from current directory) */
/** Path to the Package.appxmanifest (default: auto-detect from current directory) */
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The generated wrapper types/documentation now imply --manifest for unregister only accepts Package.appxmanifest, but the native CLI help/implementation supports appxmanifest.xml as well. Consider updating the schema generation/description to mention both filenames to avoid breaking/misleading callers.

Suggested change
/** Path to the Package.appxmanifest (default: auto-detect from current directory) */
/** Path to the app manifest file, such as Package.appxmanifest or appxmanifest.xml (default: auto-detect from current directory) */

Copilot uses AI. Check for mistakes.
Comment on lines +91 to +96
// Check for CLI updates (at most once per day, silent on failure)
if (!quiet)
{
var cliUpgradeService = serviceProvider.GetRequiredService<ICliUpgradeService>();
await cliUpgradeService.CheckAndNotifyAsync();
}
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The update check runs before parsing args and before --caller is applied to WINAPP_CLI_CALLER (later at lines 116-121). That means install-channel detection during notifications can be wrong for wrapper scenarios that rely on --caller rather than path heuristics. Consider parsing args (or at least extracting --caller) before calling CheckAndNotifyAsync, or passing the caller into the upgrade service explicitly.

Copilot uses AI. Check for mistakes.
{
case InstallChannel.Npm:
logger.LogInformation("winapp was installed via npm. To upgrade, run:");
logger.LogInformation(" npm update -g @anthropic-ai/winappcli");
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The npm upgrade instructions use the package name @anthropic-ai/winappcli, but this repo’s npm package is @microsoft/winappcli (see src/winapp-npm/package.json). This will direct users to the wrong package when running winapp upgrade or reading the update notification.

Suggested change
logger.LogInformation(" npm update -g @anthropic-ai/winappcli");
logger.LogInformation(" npm update -g @microsoft/winappcli");

Copilot uses AI. Check for mistakes.
Comment on lines +327 to +334
var upgradeHint = channel switch
{
InstallChannel.Npm => "npm update -g @anthropic-ai/winappcli",
InstallChannel.NuGet => "update the Microsoft.Windows.SDK.BuildTools.WinApp NuGet package",
_ => "winapp upgrade"
};

AnsiConsole.MarkupLine($"[yellow]v{newVersion} is available. Run `{Markup.Escape(upgradeHint)}` to update.[/]");
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The update notification hint for npm installs also references @anthropic-ai/winappcli, but the npm package in this repo is @microsoft/winappcli. The hint should match the actual package name users have installed.

Copilot uses AI. Check for mistakes.
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 13, 2026

Build Metrics Report

Binary Sizes

Artifact Baseline Current Delta
CLI (ARM64) 30.52 MB 30.57 MB 📈 +46.5 KB (+0.15%)
CLI (x64) 30.89 MB 30.94 MB 📈 +44.0 KB (+0.14%)
MSIX (ARM64) 12.89 MB 12.89 MB 📈 +1.9 KB (+0.01%)
MSIX (x64) 13.68 MB 13.70 MB 📈 +22.1 KB (+0.16%)
NPM Package 26.79 MB 26.82 MB 📈 +34.9 KB (+0.13%)
NuGet Package 26.87 MB 26.90 MB 📈 +28.3 KB (+0.10%)

Test Results

739 passed out of 739 tests in 391.6s (+17 tests, +37.6s vs. baseline)

Test Coverage

20.6% line coverage, 34.7% branch coverage · ✅ no change vs. baseline

CLI Startup Time

36ms median (x64, winapp --version) · ✅ -5ms vs. baseline


Updated 2026-04-16 02:05:19 UTC · commit 2980c68 · workflow run

@zateutsch
Copy link
Copy Markdown
Contributor Author

@copilot apply changes based on the comments in this thread

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 14, 2026

@copilot apply changes based on the comments in this thread

Implemented the requested review-thread fixes in commit d62b629. This includes startup timeout + early caller parsing in Program, culture-safe update-cache timestamp parsing, nodejs-package caller detection, corrected npm upgrade package name, unsafe package-identity P/Invoke usage, network-isolated upgrade service tests, and regenerated schema/npm wrapper docs (including manifest wording alignment and the upgrade-options generator lint fix).

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • https://api.github.com/repos/microsoft/msstore-cli/releases/latest
    • Triggering command: /home/REDACTED/work/winappCli/winappCli/src/winapp-CLI/WinApp.Cli.Tests/bin/Debug/net10.0-windows10.0.19041.0/WinApp.Cli.Tests /home/REDACTED/work/winappCli/winappCli/src/winapp-CLI/WinApp.Cli.Tests/bin/Debug/net10.0-windows10.0.19041.0/WinApp.Cli.Tests --server dotnettestcli --dotnet-test-pipe /tmp/0842e64278ef4ec498d33984d795b83c (http block)
  • https://api.github.com/repos/microsoft/winappcli/releases/latest
    • Triggering command: /home/REDACTED/work/winappCli/winappCli/src/winapp-CLI/WinApp.Cli.Tests/bin/Debug/net10.0-windows10.0.19041.0/WinApp.Cli.Tests /home/REDACTED/work/winappCli/winappCli/src/winapp-CLI/WinApp.Cli.Tests/bin/Debug/net10.0-windows10.0.19041.0/WinApp.Cli.Tests --server dotnettestcli --dotnet-test-pipe /tmp/0842e64278ef4ec498d33984d795b83c (http block)

If you need me to access, download, or install something from one of these locations, you can either:

@zateutsch zateutsch requested a review from nmetulev April 16, 2026 02:21
@zateutsch
Copy link
Copy Markdown
Contributor Author

@copilot apply changes based on the comments in this thread

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 16, 2026

@copilot apply changes based on the comments in this thread

Applied the requested review-thread updates in commit 0ed5dfb. This re-aligns init wording with appxmanifest.xml, updates run/unregister --manifest descriptions to include both appxmanifest.xml and Package.appxmanifest, and regenerates docs/cli-schema.json, src/winapp-npm/src/winapp-commands.ts, and the affected generated setup skill docs so validate-docs stays in sync.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: CLI should check for updates

3 participants