proxcli 0.16.1__tar.gz → 0.16.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. {proxcli-0.16.1 → proxcli-0.16.2}/CHANGELOG.md +9 -0
  2. {proxcli-0.16.1 → proxcli-0.16.2}/PKG-INFO +9 -1
  3. {proxcli-0.16.1 → proxcli-0.16.2}/README.md +8 -0
  4. {proxcli-0.16.1 → proxcli-0.16.2}/TODO.md +1 -0
  5. {proxcli-0.16.1 → proxcli-0.16.2}/proxmox/cli/main.py +4 -2
  6. proxcli-0.16.2/proxmox/cli/update.py +154 -0
  7. {proxcli-0.16.1 → proxcli-0.16.2}/pyproject.toml +1 -1
  8. {proxcli-0.16.1 → proxcli-0.16.2}/uv.lock +1 -1
  9. {proxcli-0.16.1 → proxcli-0.16.2}/.env.example +0 -0
  10. {proxcli-0.16.1 → proxcli-0.16.2}/.github/workflows/ci.yml +0 -0
  11. {proxcli-0.16.1 → proxcli-0.16.2}/.gitignore +0 -0
  12. {proxcli-0.16.1 → proxcli-0.16.2}/.python-version +0 -0
  13. {proxcli-0.16.1 → proxcli-0.16.2}/AGENTS.md +0 -0
  14. {proxcli-0.16.1 → proxcli-0.16.2}/PLAN.md +0 -0
  15. {proxcli-0.16.1 → proxcli-0.16.2}/PROJECT.md +0 -0
  16. {proxcli-0.16.1 → proxcli-0.16.2}/PROMPT.md +0 -0
  17. {proxcli-0.16.1 → proxcli-0.16.2}/docs/DESIGN.md +0 -0
  18. {proxcli-0.16.1 → proxcli-0.16.2}/docs/api-coverage.md +0 -0
  19. {proxcli-0.16.1 → proxcli-0.16.2}/docs/api-permissions.md +0 -0
  20. {proxcli-0.16.1 → proxcli-0.16.2}/docs/assets/index-Bzx5-fXr.css +0 -0
  21. {proxcli-0.16.1 → proxcli-0.16.2}/docs/assets/index-CXpOjuxt.css +0 -0
  22. {proxcli-0.16.1 → proxcli-0.16.2}/docs/assets/index-DLDs0H6j.js +0 -0
  23. {proxcli-0.16.1 → proxcli-0.16.2}/docs/assets/index-DYQPMK8x.js +0 -0
  24. {proxcli-0.16.1 → proxcli-0.16.2}/docs/cloud-init.md +0 -0
  25. {proxcli-0.16.1 → proxcli-0.16.2}/docs/coding-agents.md +0 -0
  26. {proxcli-0.16.1 → proxcli-0.16.2}/docs/coverage.json +0 -0
  27. {proxcli-0.16.1 → proxcli-0.16.2}/docs/demos/cluster.gif +0 -0
  28. {proxcli-0.16.1 → proxcli-0.16.2}/docs/demos/node-list.gif +0 -0
  29. {proxcli-0.16.1 → proxcli-0.16.2}/docs/demos/vm-list.gif +0 -0
  30. {proxcli-0.16.1 → proxcli-0.16.2}/docs/demos/vm-show.gif +0 -0
  31. {proxcli-0.16.1 → proxcli-0.16.2}/docs/demos/yaml-spec.gif +0 -0
  32. {proxcli-0.16.1 → proxcli-0.16.2}/docs/favicon.svg +0 -0
  33. {proxcli-0.16.1 → proxcli-0.16.2}/docs/icons.svg +0 -0
  34. {proxcli-0.16.1 → proxcli-0.16.2}/docs/index.html +0 -0
  35. {proxcli-0.16.1 → proxcli-0.16.2}/docs/production-automation.md +0 -0
  36. {proxcli-0.16.1 → proxcli-0.16.2}/docs/quickstart.md +0 -0
  37. {proxcli-0.16.1 → proxcli-0.16.2}/docs/website/.gitignore +0 -0
  38. {proxcli-0.16.1 → proxcli-0.16.2}/docs/website/README.md +0 -0
  39. {proxcli-0.16.1 → proxcli-0.16.2}/docs/website/build-all.sh +0 -0
  40. {proxcli-0.16.1 → proxcli-0.16.2}/docs/website/eslint.config.js +0 -0
  41. {proxcli-0.16.1 → proxcli-0.16.2}/docs/website/index.html +0 -0
  42. {proxcli-0.16.1 → proxcli-0.16.2}/docs/website/package-lock.json +0 -0
  43. {proxcli-0.16.1 → proxcli-0.16.2}/docs/website/package.json +0 -0
  44. {proxcli-0.16.1 → proxcli-0.16.2}/docs/website/public/coverage.json +0 -0
  45. {proxcli-0.16.1 → proxcli-0.16.2}/docs/website/public/demos/cluster.gif +0 -0
  46. {proxcli-0.16.1 → proxcli-0.16.2}/docs/website/public/demos/node-list.gif +0 -0
  47. {proxcli-0.16.1 → proxcli-0.16.2}/docs/website/public/demos/vm-list.gif +0 -0
  48. {proxcli-0.16.1 → proxcli-0.16.2}/docs/website/public/demos/vm-show.gif +0 -0
  49. {proxcli-0.16.1 → proxcli-0.16.2}/docs/website/public/demos/yaml-spec.gif +0 -0
  50. {proxcli-0.16.1 → proxcli-0.16.2}/docs/website/public/favicon.svg +0 -0
  51. {proxcli-0.16.1 → proxcli-0.16.2}/docs/website/public/icons.svg +0 -0
  52. {proxcli-0.16.1 → proxcli-0.16.2}/docs/website/remotion-demos/.gitignore +0 -0
  53. {proxcli-0.16.1 → proxcli-0.16.2}/docs/website/remotion-demos/.prettierrc +0 -0
  54. {proxcli-0.16.1 → proxcli-0.16.2}/docs/website/remotion-demos/README.md +0 -0
  55. {proxcli-0.16.1 → proxcli-0.16.2}/docs/website/remotion-demos/eslint.config.mjs +0 -0
  56. {proxcli-0.16.1 → proxcli-0.16.2}/docs/website/remotion-demos/package-lock.json +0 -0
  57. {proxcli-0.16.1 → proxcli-0.16.2}/docs/website/remotion-demos/package.json +0 -0
  58. {proxcli-0.16.1 → proxcli-0.16.2}/docs/website/remotion-demos/public/cluster-real.txt +0 -0
  59. {proxcli-0.16.1 → proxcli-0.16.2}/docs/website/remotion-demos/public/node-list-real.txt +0 -0
  60. {proxcli-0.16.1 → proxcli-0.16.2}/docs/website/remotion-demos/public/vm-list-real.txt +0 -0
  61. {proxcli-0.16.1 → proxcli-0.16.2}/docs/website/remotion-demos/public/vm-show-real.txt +0 -0
  62. {proxcli-0.16.1 → proxcli-0.16.2}/docs/website/remotion-demos/remotion.config.ts +0 -0
  63. {proxcli-0.16.1 → proxcli-0.16.2}/docs/website/remotion-demos/src/Composition.tsx +0 -0
  64. {proxcli-0.16.1 → proxcli-0.16.2}/docs/website/remotion-demos/src/Root.tsx +0 -0
  65. {proxcli-0.16.1 → proxcli-0.16.2}/docs/website/remotion-demos/src/index.css +0 -0
  66. {proxcli-0.16.1 → proxcli-0.16.2}/docs/website/remotion-demos/src/index.ts +0 -0
  67. {proxcli-0.16.1 → proxcli-0.16.2}/docs/website/remotion-demos/tsconfig.json +0 -0
  68. {proxcli-0.16.1 → proxcli-0.16.2}/docs/website/src/App.jsx +0 -0
  69. {proxcli-0.16.1 → proxcli-0.16.2}/docs/website/src/components/CoverageGrid.jsx +0 -0
  70. {proxcli-0.16.1 → proxcli-0.16.2}/docs/website/src/components/SplitFlapAgent.jsx +0 -0
  71. {proxcli-0.16.1 → proxcli-0.16.2}/docs/website/src/index.css +0 -0
  72. {proxcli-0.16.1 → proxcli-0.16.2}/docs/website/src/main.jsx +0 -0
  73. {proxcli-0.16.1 → proxcli-0.16.2}/docs/website/src/pages/Docs.jsx +0 -0
  74. {proxcli-0.16.1 → proxcli-0.16.2}/docs/website/src/pages/Landing.jsx +0 -0
  75. {proxcli-0.16.1 → proxcli-0.16.2}/docs/website/vite.config.js +0 -0
  76. {proxcli-0.16.1 → proxcli-0.16.2}/proxmox/__init__.py +0 -0
  77. {proxcli-0.16.1 → proxcli-0.16.2}/proxmox/cli/__init__.py +0 -0
  78. {proxcli-0.16.1 → proxcli-0.16.2}/proxmox/cli/acl.py +0 -0
  79. {proxcli-0.16.1 → proxcli-0.16.2}/proxmox/cli/api.py +0 -0
  80. {proxcli-0.16.1 → proxcli-0.16.2}/proxmox/cli/auth.py +0 -0
  81. {proxcli-0.16.1 → proxcli-0.16.2}/proxmox/cli/backup.py +0 -0
  82. {proxcli-0.16.1 → proxcli-0.16.2}/proxmox/cli/ceph.py +0 -0
  83. {proxcli-0.16.1 → proxcli-0.16.2}/proxmox/cli/cluster.py +0 -0
  84. {proxcli-0.16.1 → proxcli-0.16.2}/proxmox/cli/completion.py +0 -0
  85. {proxcli-0.16.1 → proxcli-0.16.2}/proxmox/cli/container.py +0 -0
  86. {proxcli-0.16.1 → proxcli-0.16.2}/proxmox/cli/firewall_helpers.py +0 -0
  87. {proxcli-0.16.1 → proxcli-0.16.2}/proxmox/cli/network.py +0 -0
  88. {proxcli-0.16.1 → proxcli-0.16.2}/proxmox/cli/node.py +0 -0
  89. {proxcli-0.16.1 → proxcli-0.16.2}/proxmox/cli/pool.py +0 -0
  90. {proxcli-0.16.1 → proxcli-0.16.2}/proxmox/cli/role.py +0 -0
  91. {proxcli-0.16.1 → proxcli-0.16.2}/proxmox/cli/storage.py +0 -0
  92. {proxcli-0.16.1 → proxcli-0.16.2}/proxmox/cli/tasks.py +0 -0
  93. {proxcli-0.16.1 → proxcli-0.16.2}/proxmox/cli/user.py +0 -0
  94. {proxcli-0.16.1 → proxcli-0.16.2}/proxmox/cli/vm.py +0 -0
  95. {proxcli-0.16.1 → proxcli-0.16.2}/proxmox/cli/vm_spec.py +0 -0
  96. {proxcli-0.16.1 → proxcli-0.16.2}/proxmox/client/__init__.py +0 -0
  97. {proxcli-0.16.1 → proxcli-0.16.2}/proxmox/client/auth.py +0 -0
  98. {proxcli-0.16.1 → proxcli-0.16.2}/proxmox/client/client.py +0 -0
  99. {proxcli-0.16.1 → proxcli-0.16.2}/proxmox/client/exceptions.py +0 -0
  100. {proxcli-0.16.1 → proxcli-0.16.2}/proxmox/config/__init__.py +0 -0
  101. {proxcli-0.16.1 → proxcli-0.16.2}/proxmox/config/config.py +0 -0
  102. {proxcli-0.16.1 → proxcli-0.16.2}/proxmox/config/models.py +0 -0
  103. {proxcli-0.16.1 → proxcli-0.16.2}/proxmox/output/__init__.py +0 -0
  104. {proxcli-0.16.1 → proxcli-0.16.2}/proxmox/output/formatter.py +0 -0
  105. {proxcli-0.16.1 → proxcli-0.16.2}/proxmox/output/json_fmt.py +0 -0
  106. {proxcli-0.16.1 → proxcli-0.16.2}/proxmox/output/log_fmt.py +0 -0
  107. {proxcli-0.16.1 → proxcli-0.16.2}/proxmox/output/table_fmt.py +0 -0
  108. {proxcli-0.16.1 → proxcli-0.16.2}/proxmox/output/yaml_fmt.py +0 -0
  109. {proxcli-0.16.1 → proxcli-0.16.2}/proxmox/utils/__init__.py +0 -0
  110. {proxcli-0.16.1 → proxcli-0.16.2}/proxmox/utils/helpers.py +0 -0
  111. {proxcli-0.16.1 → proxcli-0.16.2}/proxmox/utils/logging.py +0 -0
  112. {proxcli-0.16.1 → proxcli-0.16.2}/tests/__init__.py +0 -0
  113. {proxcli-0.16.1 → proxcli-0.16.2}/tests/conftest.py +0 -0
  114. {proxcli-0.16.1 → proxcli-0.16.2}/tests/test_auth.py +0 -0
  115. {proxcli-0.16.1 → proxcli-0.16.2}/tests/test_cli/__init__.py +0 -0
  116. {proxcli-0.16.1 → proxcli-0.16.2}/tests/test_cli/test_api.py +0 -0
  117. {proxcli-0.16.1 → proxcli-0.16.2}/tests/test_cli/test_backup.py +0 -0
  118. {proxcli-0.16.1 → proxcli-0.16.2}/tests/test_cli/test_backup_restore.py +0 -0
  119. {proxcli-0.16.1 → proxcli-0.16.2}/tests/test_cli/test_ceph.py +0 -0
  120. {proxcli-0.16.1 → proxcli-0.16.2}/tests/test_cli/test_container_ip.py +0 -0
  121. {proxcli-0.16.1 → proxcli-0.16.2}/tests/test_cli/test_main.py +0 -0
  122. {proxcli-0.16.1 → proxcli-0.16.2}/tests/test_cli/test_network.py +0 -0
  123. {proxcli-0.16.1 → proxcli-0.16.2}/tests/test_cli/test_node_system.py +0 -0
  124. {proxcli-0.16.1 → proxcli-0.16.2}/tests/test_cli/test_role_acl.py +0 -0
  125. {proxcli-0.16.1 → proxcli-0.16.2}/tests/test_cli/test_task_wait.py +0 -0
  126. {proxcli-0.16.1 → proxcli-0.16.2}/tests/test_cli/test_user.py +0 -0
  127. {proxcli-0.16.1 → proxcli-0.16.2}/tests/test_cli/test_vm_agent.py +0 -0
  128. {proxcli-0.16.1 → proxcli-0.16.2}/tests/test_cli/test_vm_clone.py +0 -0
  129. {proxcli-0.16.1 → proxcli-0.16.2}/tests/test_cli/test_vm_disk.py +0 -0
  130. {proxcli-0.16.1 → proxcli-0.16.2}/tests/test_cli/test_vm_ip.py +0 -0
  131. {proxcli-0.16.1 → proxcli-0.16.2}/tests/test_cli/test_vm_iso.py +0 -0
  132. {proxcli-0.16.1 → proxcli-0.16.2}/tests/test_cli/test_vm_migrate.py +0 -0
  133. {proxcli-0.16.1 → proxcli-0.16.2}/tests/test_cli/test_vm_set.py +0 -0
  134. {proxcli-0.16.1 → proxcli-0.16.2}/tests/test_cli/test_vm_template.py +0 -0
  135. {proxcli-0.16.1 → proxcli-0.16.2}/tests/test_client.py +0 -0
  136. {proxcli-0.16.1 → proxcli-0.16.2}/tests/test_config.py +0 -0
  137. {proxcli-0.16.1 → proxcli-0.16.2}/tests/test_integration/__init__.py +0 -0
  138. {proxcli-0.16.1 → proxcli-0.16.2}/tests/test_output/__init__.py +0 -0
  139. {proxcli-0.16.1 → proxcli-0.16.2}/tests/test_output/test_formatter.py +0 -0
@@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.16.2] - 2026-06-30
9
+
10
+ ### Added
11
+ - **``proxmox update``**: self-upgrade proxcli from PyPI. Queries the PyPI JSON
12
+ API to check for new versions and uses ``uv tool install proxcli --reinstall``
13
+ to upgrade in-place. ``--check`` flag reports available updates without
14
+ installing. ``--pre`` includes pre-release versions.
15
+
8
16
  ## [0.16.1] - 2026-06-30
9
17
 
10
18
  ### Added
@@ -355,6 +363,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
355
363
  - CSRF ticket auto-refresh on 401.
356
364
  - AI-agent-friendly: default JSON output, strict exit codes, `--dry-run` mode.
357
365
 
366
+ [0.16.2]: https://github.com/xezpeleta/proxcli/releases/tag/v0.16.2
358
367
  [0.16.1]: https://github.com/xezpeleta/proxcli/releases/tag/v0.16.1
359
368
  [0.16.0]: https://github.com/xezpeleta/proxcli/releases/tag/v0.16.0
360
369
  [0.15.0]: https://github.com/xezpeleta/proxcli/releases/tag/v0.15.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: proxcli
3
- Version: 0.16.1
3
+ Version: 0.16.2
4
4
  Summary: A CLI tool to interact with Proxmox VE nodes and clusters via the REST API
5
5
  Author-email: Xabi Ezpeleta <xezpeleta@gmail.com>
6
6
  License: MIT
@@ -192,6 +192,14 @@ proxmox auth setup # Create recommended roles + ACLs (needs Administ
192
192
  proxmox auth check # Live permission test table (39 checks)
193
193
  ```
194
194
 
195
+ ### Self-update
196
+
197
+ ```bash
198
+ proxmox update # Check for updates and install the latest version
199
+ proxmox update --check # Only check (no install)
200
+ proxmox update --pre # Include pre-release versions
201
+ ```
202
+
195
203
  ### Completion
196
204
 
197
205
  ```bash
@@ -169,6 +169,14 @@ proxmox auth setup # Create recommended roles + ACLs (needs Administ
169
169
  proxmox auth check # Live permission test table (39 checks)
170
170
  ```
171
171
 
172
+ ### Self-update
173
+
174
+ ```bash
175
+ proxmox update # Check for updates and install the latest version
176
+ proxmox update --check # Only check (no install)
177
+ proxmox update --pre # Include pre-release versions
178
+ ```
179
+
172
180
  ### Completion
173
181
 
174
182
  ```bash
@@ -29,6 +29,7 @@ Completed items are marked with a check. Implementation notes are preserved for
29
29
  - [x] **API coverage doc** — `docs/api-coverage.md` tracking all implemented and remaining endpoints.
30
30
  - [x] **VM disk import from URL** — `vm disk import --url` downloads, uploads, and imports a disk image from a remote URL in one command. `--image` for local PVE node paths.
31
31
  - [x] **Cloud-init auto serial console** — `vm create` with cloud-init flags now auto-adds `serial0=socket` and `vga=serial0`, required by Debian generic cloud images.
32
+ - [x] **Self-update** — `proxmox update` checks PyPI for newer versions and upgrades via `uv tool install --reinstall`. `--check` for dry-run, `--pre` for pre-releases.
32
33
 
33
34
  ## v1.1 — Polish & Usability
34
35
 
@@ -74,6 +74,7 @@ def build_root_parser() -> argparse.ArgumentParser:
74
74
  from proxmox.cli.role import register_role_parser
75
75
  from proxmox.cli.storage import register_storage_parser
76
76
  from proxmox.cli.tasks import register_task_parser
77
+ from proxmox.cli.update import register_update_parser
77
78
  from proxmox.cli.user import register_user_parser
78
79
  from proxmox.cli.vm import register_vm_parser
79
80
 
@@ -93,6 +94,7 @@ def build_root_parser() -> argparse.ArgumentParser:
93
94
  register_task_parser(subparsers)
94
95
  register_role_parser(subparsers)
95
96
  register_user_parser(subparsers)
97
+ register_update_parser(subparsers)
96
98
 
97
99
  return parser
98
100
 
@@ -224,7 +226,7 @@ GLOBAL_FLAGS_WITH_VALUE = {"--url", "--username", "--password", "--api-token", "
224
226
  def _hint_global_flags_order(argv: list[str]) -> None:
225
227
  """If user placed global flags after the resource, show a helpful hint."""
226
228
  resource_pos = -1
227
- resources = {"acl", "api", "auth", "backup", "ceph", "vm", "node", "pool", "container", "storage", "cluster", "completion", "task", "user", "role", "network"}
229
+ resources = {"acl", "api", "auth", "backup", "ceph", "vm", "node", "pool", "container", "storage", "cluster", "completion", "task", "user", "role", "network", "update"}
228
230
  for i, arg in enumerate(argv):
229
231
  if arg in resources:
230
232
  resource_pos = i
@@ -297,7 +299,7 @@ def main(argv: list[str] | None = None) -> None:
297
299
 
298
300
  try:
299
301
  # auth status (without --permissions) and completion don't need a client
300
- if args.resource == "completion" or (
302
+ if args.resource == "completion" or args.resource == "update" or (
301
303
  args.resource == "auth"
302
304
  and args.action == "status"
303
305
  and not getattr(args, "permissions", False)
@@ -0,0 +1,154 @@
1
+ """proxmox update — self-upgrade proxcli from PyPI."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import json
7
+ import subprocess
8
+ import sys
9
+ import urllib.request
10
+ from importlib.metadata import version
11
+
12
+ from proxmox.utils.logging import log_error
13
+
14
+
15
+ def register_update_parser(subparsers: argparse._SubParsersAction) -> None:
16
+ update_parser = subparsers.add_parser("update", help="Check for and install proxcli updates")
17
+ update_parser.add_argument(
18
+ "--check",
19
+ action="store_true",
20
+ help="Only check if an update is available (do not install)",
21
+ )
22
+ update_parser.add_argument(
23
+ "--pre",
24
+ action="store_true",
25
+ help="Include pre-release versions when checking",
26
+ )
27
+ update_parser.set_defaults(func=_update, output_format="table")
28
+
29
+
30
+ def _get_latest_version(include_pre: bool = False) -> str:
31
+ """Fetch the latest version tag from PyPI."""
32
+ url = "https://pypi.org/pypi/proxcli/json"
33
+ try:
34
+ with urllib.request.urlopen(url, timeout=10) as resp:
35
+ data = json.loads(resp.read().decode())
36
+ except Exception as exc:
37
+ raise RuntimeError(f"Failed to query PyPI: {exc}") from exc
38
+
39
+ versions = list(data.get("releases", {}).keys())
40
+
41
+ if not versions:
42
+ raise RuntimeError("No releases found on PyPI")
43
+
44
+ if not include_pre:
45
+ versions = [v for v in versions if _is_stable(v)]
46
+ if not versions:
47
+ raise RuntimeError("No stable releases found on PyPI")
48
+
49
+ return max(versions, key=_version_key)
50
+
51
+
52
+ def _is_stable(ver: str) -> bool:
53
+ """Return True if version looks like a stable release (no dev/pre markers)."""
54
+ # Strip epoch if present
55
+ v = ver
56
+ # Simple check: no a/b/rc/dev/post markers
57
+ for marker in ("a", "b", "rc", "dev", "post"):
58
+ if marker in v:
59
+ return False
60
+ return True
61
+
62
+
63
+ def _version_key(ver: str) -> tuple:
64
+ """Parse a version string into a comparable tuple."""
65
+ parts = ver.split(".")
66
+ result = []
67
+ for p in parts:
68
+ # Try int first
69
+ try:
70
+ result.append(int(p))
71
+ except ValueError:
72
+ result.append(p)
73
+ return tuple(result)
74
+
75
+
76
+ def _update(args: argparse.Namespace, client: object | None = None) -> dict | str:
77
+ """Check for updates and optionally install them.
78
+
79
+ Returns a dict for the output formatter.
80
+ """
81
+ current = version("proxcli")
82
+
83
+ try:
84
+ latest = _get_latest_version(include_pre=args.pre)
85
+ except RuntimeError as exc:
86
+ return {"error": str(exc)}
87
+
88
+ up_to_date = _version_key(current) >= _version_key(latest)
89
+
90
+ if args.check:
91
+ if up_to_date:
92
+ return {
93
+ "current": current,
94
+ "latest": latest,
95
+ "status": "up-to-date",
96
+ "message": f"proxcli {current} is the latest version.",
97
+ }
98
+ else:
99
+ return {
100
+ "current": current,
101
+ "latest": latest,
102
+ "status": "update-available",
103
+ "message": f"proxcli {latest} is available (you have {current}). Run 'proxmox update' to upgrade.",
104
+ }
105
+
106
+ if up_to_date:
107
+ return {
108
+ "current": current,
109
+ "latest": latest,
110
+ "status": "up-to-date",
111
+ "message": f"proxcli {current} is already the latest version.",
112
+ }
113
+
114
+ # Run uv tool install
115
+ log_error(f"Upgrading proxcli from {current} to {latest}...", file=sys.stderr)
116
+ try:
117
+ result = subprocess.run(
118
+ ["uv", "tool", "install", "proxcli", "--reinstall"],
119
+ capture_output=True,
120
+ text=True,
121
+ timeout=120,
122
+ )
123
+ if result.returncode != 0:
124
+ error_msg = result.stderr.strip() or result.stdout.strip() or f"exit code {result.returncode}"
125
+ log_error(error_msg)
126
+ return {
127
+ "current": current,
128
+ "latest": latest,
129
+ "status": "error",
130
+ "message": f"Upgrade failed: {error_msg}",
131
+ }
132
+ except FileNotFoundError:
133
+ return {
134
+ "current": current,
135
+ "latest": latest,
136
+ "status": "error",
137
+ "message": "uv not found in PATH. Install uv (https://docs.astral.sh/uv/) or upgrade manually with 'uv tool install proxcli --reinstall'.",
138
+ }
139
+ except subprocess.TimeoutExpired:
140
+ return {
141
+ "current": current,
142
+ "latest": latest,
143
+ "status": "error",
144
+ "message": "Upgrade timed out. Try again or upgrade manually.",
145
+ }
146
+
147
+ new_version = version("proxcli")
148
+ return {
149
+ "current": new_version,
150
+ "previous": current,
151
+ "latest": latest,
152
+ "status": "upgraded",
153
+ "message": f"proxcli upgraded from {current} to {new_version}.",
154
+ }
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "proxcli"
3
- version = "0.16.1"
3
+ version = "0.16.2"
4
4
  description = "A CLI tool to interact with Proxmox VE nodes and clusters via the REST API"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -254,7 +254,7 @@ wheels = [
254
254
 
255
255
  [[package]]
256
256
  name = "proxcli"
257
- version = "0.16.1"
257
+ version = "0.16.2"
258
258
  source = { editable = "." }
259
259
  dependencies = [
260
260
  { name = "httpx" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes