# Copyright Kevin Deldycke <kevin@deldycke.com> and contributors.
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from __future__ import annotations
from typing import Generator, Iterator
from extra_platforms import WINDOWS
from meta_package_manager.base import Package, PackageManager
[docs]
class WinGet(PackageManager):
homepage_url = "https://github.com/microsoft/winget-cli"
platforms = WINDOWS
requirement = "1.7"
post_args = ("--accept-source-agreements", "--disable-interactivity")
"""
``--accept-source-agreements``:
Used to accept the source license agreement, and avoid the following prompt:
.. code-block:: pwsh-session
PS C:\\Users\\kev> winget list
The "msstore' source requires that you view the following agreements before using.
Terms of Transaction: https://aka.ms/microsoft-store-terms-of-transaction
The source requires the current machine's 2-letter geographic region to be sent to the backend service to function prope rly (ex. "US").
Do you agree to all the source agreements terms?
[Y] Yes [N] No:
``--disable-interactivity``:
Disable interactive prompts.
"""
version_regex = r"v\s+(?P<version>\S+)"
"""
.. code-block:: pwsh-session
PS C:\\Users\\kev> winget --version
v1.7.11261
"""
def _parse_table(self, output: str) -> Iterator[Generator[str, None, None]]:
"""Parse a table from the output of a winget command and returns a generator of cells."""
# Extract table.
table_start = "Name "
assert table_start in output, (
f"Cannot find table starting with {table_start!r} in:\n{output}"
)
assert output.count(table_start) == 1, (
f"{table_start!r} not unique in:\n{output}"
)
table = output.split(table_start, 1)[1]
# Check table format.
lines = table.splitlines()
table_width = len(lines[0])
assert lines[1] == "-" * table_width, (
f"Table headers not followed by expected separator:\n{table}"
)
assert all(len(line) <= table_width for line in lines[2:]), (
f"Table lines with different width:\n{table}"
)
# Guess column positions.
headers = []
col_str = ""
for char in lines[0]:
if col_str and char != " " and " " in col_str:
headers.append(col_str)
col_str = ""
col_str += char
if col_str:
headers.append(col_str)
col_ranges = []
for header in headers:
start = lines[0].index(header)
end = start + len(header)
col_ranges.append((start, end))
for line in lines[2:]:
yield (line[start:end].strip() for start, end in col_ranges)
@property
def installed(self) -> Iterator[Package]:
"""Fetch installed packages.
.. code-block:: pwsh-session
PS C:\\Users\\kev> winget list --accept-source-agreements --disable-interactivity
The 'msstore' source requires that you view the following agreements before using.
Terms of Transaction: https://aka.ms/microsoft-store-terms-of-transaction
The source requires the current machine's 2-letter geographic region to be sent to the backend service to function properly (ex. "US").
Name Id Version Available Source
----------------------------------------------------------------------------------------------
CCleaner CCleaner 6.08
Git Git.Git 2.37.3 2.45.1 winget
Microsoft Edge Microsoft.Edge 109.0.1518.70 125.0.2535.51 winget
Microsoft Edge Update Microsoft Edge Update 1.3.187.37
App Installer Microsoft.AppInstaller 1.21.3482.0 winget
Microsoft.UI.Xaml.2.7 Microsoft.UI.Xaml.2.7 7.2208.15002.0 winget
Python Launcher Python.Launchez < 3.12.0 3.12.0 winget
Microsoft Visual C++ (x86)... Microsoft.VCRedist.2015+.X86 14.34.31931.0 14.38.33135.0 winget
"""
output = self.run_cli("list")
for name, package_id, installed_version, _, _ in self._parse_table(output):
# Strip the "<" comparison operator from the version.
if " " in installed_version:
installed_version = installed_version.split()[1]
yield self.package(
id=package_id, name=name, installed_version=installed_version
)
@property
def outdated(self) -> Iterator[Package]:
"""Fetch outdated packages.
.. code-block:: pwsh-session
PS C:\\Users\\kev> winget list --upgrade-available --accept-source-agreements --disable-interactivity
Name Id Version Available Source
---------------------------------------------------------------------------------------------
Git Git.Git 2.37.3 2.45.1 winget
Microsoft Edge Microsoft.Edge 109.0.1518.70 125.0.2535.51 winget
Python Launcher Python.Launchez < 3.12.0 3.12.0 winget
Microsoft Visual C++ (x86)... Microsoft.VCRedist.2015+.X86 14.34.31931.0 14.38.33135.0 winget
4 upgrades available.
"""
output = self.run_cli("list", "--upgrade-available").strip()
if output.endswith(" upgrades available."):
output = "\n".join(output.splitlines()[:-1])
for name, package_id, installed_version, latest_version, _ in self._parse_table(
output
):
# Strip the "<" comparison operator from the version.
if " " in installed_version:
installed_version = installed_version.split()[1]
yield self.package(
id=package_id,
name=name,
installed_version=installed_version,
latest_version=latest_version,
)
[docs]
def search(self, query: str, extended: bool, exact: bool) -> Iterator[Package]:
"""Fetch matching packages.
.. code-block:: pwsh-session
PS C:\\Users\\kev> winget search --query vscode --accept-source-agreements --disable-interactivity
Name Id Version Match Source
---------------------------------------------------------------------------------------------------------
Microsoft Visual Studio Code Microsoft.VisualStudioCode 1.89.1 Moniker: vscode winget
MrCode zokugun.MrCode 1.82.0.23253 Tag: vscode winget
VSCodium Insiders VSCodium.VSCodium.Insiders 1.88.0.24095 Tag: vscode winget
VSCodium VSCodium.VSCodium 1.89.1.24130 Tag: vscode winget
Upgit pluveto.Upgit 0.2.18 Tag: vscode winget
vscli michidk.vscli 0.3.0 Tag: vscode winget
Huawei QuickApp IDE Huawei.QuickAppIde 14.0.1 Tag: vscode winget
TheiaBlueprint EclipseFoundation.TheiaBlueprint 1.44.0 Tag: vscode winget
Codium Alex313031.Codium 1.86.2.24053 Tag: vscode winget
Cursor Editor CursorAI,Inc.Cursor latest Tag: vscode winget
Microsoft Visual Studio Code CLI Microsoft.VisualStudioCode.CLI 1.89.1 Moniker: vscode-cli winget
.. code-block:: pwsh-session
PS C:\\Users\\kev> winget search --query vscode --exact --accept-source-agreements --disable-interactivity
Name Id Version Match Source
-------------------------------------------------------------------------------------------------
Microsoft Visual Studio Code Microsoft.VisualStudioCode 1.89.1 Moniker: vscode winget
MrCode zokugun.MrCode 1.82.0.23253 Tag: vscode winget
VSCodium Insiders VSCodium.VSCodium.Insiders 1.88.0.24095 Tag: vscode winget
VSCodium VSCodium.VSCodium 1.89.1.24130 Tag: vscode winget
Upgit pluveto.Upgit 0.2.18 Tag: vscode winget
vscli michidk.vscli 0.3.0 Tag: vscode winget
Huawei QuickApp IDE Huawei.QuickAppIde 14.0.1 Tag: vscode winget
TheiaBlueprint EclipseFoundation.TheiaBlueprint 1.44.0 Tag: vscode winget
Codium Alex313031.Codium 1.86.2.24053 Tag: vscode winget
Cursor Editor CursorAI,Inc.Cursor latest Tag: vscode winget
.. code-block:: pwsh-session
PS C:\\Users\\kev> winget search --id VSCodium.VSCodium --accept-source-agreements --disable-interactivity
Name Id Version Source
----------------------------------------------------------------
VSCodium Insiders VSCodium.VSCodium.Insiders 1.88.0.24095 winget
VSCodium VSCodium.VSCodium 1.89.1.24130 winget
.. code-block:: pwsh-session
PS C:\\Users\\kev> winget search --name Codium --accept-source-agreements --disable-interactivity
Name Id Version Source
----------------------------------------------------------------
Codium Alex313031.Codium 1.86.2.24053 winget
VSCodium Insiders VSCodium.VSCodium.Insiders 1.88.0.24095 winget
VSCodium VSCodium.VSCodium 1.89.1.24130 winget
.. code-block:: pwsh-session
PS C:\\Users\\kev> winget search --id VSCodium.VSCodium --exact --accept-source-agreements --disable-interactivity
Name Id Version Source
----------------------------------------------
VSCodium VSCodium.VSCodium 1.89.1.24130 winget
.. code-block:: pwsh-session
PS C:\\Users\\kev> winget search --name Codium --exact --accept-source-agreements --disable-interactivity
Name Id Version Source
--------------------------------------------
Codium Alex313031.Codium 1.86.2.24053 winget
"""
# Default search is extended to all metadata: id, name, moniker and tag.
if extended:
args = ["search", "--query", query]
# Exact search deactivates substring search.
if exact:
args.append("--exact")
output = self.run_cli(args)
for name, package_id, version, _, _ in self._parse_table(output):
yield self.package(id=package_id, name=name, latest_version=version)
# For non-extended search, we need to perform 2 queries, one for id and
# one for name.
else:
for field in "--id", "--name":
output = self.run_cli(
"search", field, query, "--exact" if exact else None
)
for name, package_id, version, _ in self._parse_table(output):
yield self.package(id=package_id, name=name, latest_version=version)
[docs]
def install(self, package_id: str, version: str | None = None) -> str:
"""Install one package.
.. code-block:: pwsh-session
PS C:\\Users\\kev> winget install --id Microsoft.PowerToys --accept-package-agreements --version 0.15.2 --accept-source-agreements --disable-interactivity
Found Power Toys [Microsoft.PowerToys] Version 0.15.2
This application is licensed to you by its owner.
Microsoft is not responsible for, nor does it grant any licenses to, third-party packages.
Successfully verified installer hash
Starting package install...
ββββββββββββββββββββββββββββββ 100%
Successfully installed
"""
args = ["install", "--id", package_id, "--accept-package-agreements"]
if version:
args += ["--version", version]
return self.run_cli(args)
[docs]
def upgrade_all_cli(self) -> tuple[str, ...]:
"""Generates the CLI to upgrade all packages (default) or only the one provided
as parameter.
.. code-block:: pwsh-session
PS C:\\Users\\kev> winget upgrade --all --accept-package-agreements --accept-source-agreements --disable-interactivity
Name Id Version Available Source
------------------------------------------------------------------------------------------------
Microsoft Edge Microsoft.Edge 109.0.1518.70 125.0.2535.51 winget
Microsoft Edge WebView2 Runtime Microsoft.EdgeWebView2Runtime 109.0.1518.70 125.0.2535.51 winget
Python Launcher Python.Launchez < 3.12.0 3.12.0 winget
Microsoft Visual C++ (x86)... Microsoft.VCRedist.2015+.X86 14.34.31931.0 14.38.33135.0 winget
4 upgrades available.
Installing dependencies:
This package requires the following dependencies:
- Packages
Microsoft.UI.Xaml.2.8 [>= 8.2306.22001.0]
(1/3) Found Microsoft Edge WebView2 Runtime [Microsoft.EdgeWebView2Runtime] Version 125. 0.2535.51
This application is licensed to you by its owner.
Microsoft is not responsible for, nor does it grant any licenses to, third-party packages.
Downloading https://msedge.sf.dl.delivery.mp.microsoft.com/filestreamingservice/files/e5dd841e-17ff-43b7-a2c0-ff759f55c202/MicrosoftEdgeWebView2RuntimeInstallerARM64.exe
ββββββββββββββββββββββββββββββ 166 MB / 166 MB
Successfully verified installer hash
Starting package install...
Successfully installed
(...)
"""
return self.build_cli("update", "--all", "--accept-package-agreements")
[docs]
def upgrade_one_cli(
self, package_id: str, version: str | None = None
) -> tuple[str, ...]:
"""Generates the CLI to upgrade all packages (default) or only the one provided
as parameter.
.. code-block:: pwsh-session
PS C:\\Users\\kev> winget upgrade --id Git.Git --accept-package-agreements --accept-source-agreements --disable-interactivity
Found Git [Git.Git] Version 2.45.1
This application is licensed to you by its owner.
Microsoft is not responsible for, nor does it grant any licenses to, third-party packages.
Downloading https://github.com/git-for-windows/git/releases/download/v2.45.1.windows.1/Git-2.45.1-64-bit.exe
ββββββββββββββββββββββββββββββ 64.7 MB / 64.7 MB
Successfully verified installer hash
Starting package install...
Successfully installed
.. todo::
Automatically uninstall the package if the technology is different:
.. code-block:: pwsh-session
PS C:\\Users\\kev> winget upgrade --id Microsoft.Edge
A newer version was found, but the install technology is different from the current version installed. Please uninstall the package and install the newer version.
"""
args = ["install", "--id", package_id, "--accept-package-agreements"]
if version:
args += ["--version", version]
return self.build_cli(args)
[docs]
def remove(self, package_id: str) -> str:
"""Remove one package.
.. code-block:: pwsh-session
PS C:\\Users\\kev> winget uninstall --id Microsoft.PowerToys --source winget --accept-source-agreements --disable-interactivity
Found PowerToys (Preview) [Microsoft.PowerToys]
Starting package uninstall...
ββββββββββββββββββββββββββββββ 100%
Successfully uninstalled
"""
return self.run_cli("uninstall", "--id", package_id, "--source", "winget")