portacode 1.4.13.dev2__py3-none-any.whl → 1.4.13.dev4__py3-none-any.whl

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.
portacode/_version.py CHANGED
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '1.4.13.dev2'
32
- __version_tuple__ = version_tuple = (1, 4, 13, 'dev2')
31
+ __version__ = version = '1.4.13.dev4'
32
+ __version_tuple__ = version_tuple = (1, 4, 13, 'dev4')
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -3,19 +3,21 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import asyncio
6
- import base64
7
6
  import json
8
7
  import logging
9
8
  import os
10
9
  import secrets
10
+ import shlex
11
11
  import shutil
12
12
  import stat
13
13
  import subprocess
14
14
  import sys
15
+ import tempfile
16
+ import time
15
17
  import threading
16
18
  from datetime import datetime
17
19
  from pathlib import Path
18
- from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple
20
+ from typing import Any, Callable, Dict, Iterable, List, Optional, Sequence, Tuple
19
21
 
20
22
  import platformdirs
21
23
 
@@ -687,34 +689,63 @@ def _run_pct_check(vmid: int, cmd: str) -> Dict[str, Any]:
687
689
  return res
688
690
 
689
691
 
692
+ def _run_pct_exec(vmid: int, command: Sequence[str]) -> subprocess.CompletedProcess[str]:
693
+ return _call_subprocess(["pct", "exec", str(vmid), "--", *command])
694
+
695
+
696
+ def _run_pct_exec_check(vmid: int, command: Sequence[str]) -> subprocess.CompletedProcess[str]:
697
+ res = _run_pct_exec(vmid, command)
698
+ if res.returncode != 0:
699
+ raise RuntimeError(res.stderr or res.stdout or f"pct exec {' '.join(command)} failed")
700
+ return res
701
+
702
+
703
+ def _run_pct_push(vmid: int, src: str, dest: str) -> subprocess.CompletedProcess[str]:
704
+ return _call_subprocess(["pct", "push", str(vmid), src, dest])
705
+
706
+
707
+ def _push_bytes_to_container(
708
+ vmid: int, user: str, path: str, data: bytes, mode: int = 0o600
709
+ ) -> None:
710
+ logger.debug("Preparing to push %d bytes to container vmid=%s path=%s for user=%s", len(data), vmid, path, user)
711
+ tmp_path: Optional[str] = None
712
+ try:
713
+ with tempfile.NamedTemporaryFile(delete=False) as tmp:
714
+ tmp.write(data)
715
+ tmp.flush()
716
+ os.fsync(tmp.fileno())
717
+ tmp_path = tmp.name
718
+
719
+ push_res = _run_pct_push(vmid, tmp_path, path)
720
+ if push_res.returncode != 0:
721
+ raise RuntimeError(push_res.stderr or push_res.stdout or f"pct push returned {push_res.returncode}")
722
+
723
+ _run_pct_exec_check(vmid, ["chown", f"{user}:{user}", path])
724
+ _run_pct_exec_check(vmid, ["chmod", format(mode, "o"), path])
725
+ logger.debug("Successfully pushed %d bytes to vmid=%s path=%s", len(data), vmid, path)
726
+ except Exception as exc:
727
+ logger.error("Failed to write to container vmid=%s path=%s for user=%s: %s", vmid, path, user, exc)
728
+ raise
729
+ finally:
730
+ if tmp_path:
731
+ try:
732
+ os.remove(tmp_path)
733
+ except OSError as cleanup_exc:
734
+ logger.warning("Failed to remove temporary file %s: %s", tmp_path, cleanup_exc)
735
+
736
+
690
737
  def _resolve_portacode_key_dir(vmid: int, user: str) -> str:
691
738
  data_dir_cmd = f"su - {user} -c 'echo -n ${{XDG_DATA_HOME:-$HOME/.local/share}}'"
692
739
  data_home = _run_pct_check(vmid, data_dir_cmd)["stdout"].strip()
693
740
  return f"{data_home}/portacode/keys"
694
741
 
695
742
 
696
- def _write_bytes_as_user(vmid: int, user: str, path: str, data: bytes, mode: int = 0o600) -> None:
697
- encoded = base64.b64encode(data).decode()
698
- path_literal = json.dumps(path)
699
- script = (
700
- f"su - {user} -c 'python3 - <<\"PY\"\n"
701
- "import base64\n"
702
- "from pathlib import Path\n"
703
- f"path = Path({path_literal})\n"
704
- "path.parent.mkdir(parents=True, exist_ok=True)\n"
705
- f"path.write_bytes(base64.b64decode(\"{encoded}\"))\n"
706
- f"path.chmod({mode})\n"
707
- "PY'"
708
- )
709
- _run_pct_check(vmid, script)
710
-
711
-
712
743
  def _deploy_device_keypair(vmid: int, user: str, private_key: str, public_key: str) -> None:
713
744
  key_dir = _resolve_portacode_key_dir(vmid, user)
714
745
  priv_path = f"{key_dir}/id_portacode"
715
746
  pub_path = f"{key_dir}/id_portacode.pub"
716
- _write_bytes_as_user(vmid, user, priv_path, private_key.encode(), mode=0o600)
717
- _write_bytes_as_user(vmid, user, pub_path, public_key.encode(), mode=0o644)
747
+ _push_bytes_to_container(vmid, user, priv_path, private_key.encode(), mode=0o600)
748
+ _push_bytes_to_container(vmid, user, pub_path, public_key.encode(), mode=0o644)
718
749
 
719
750
 
720
751
  def _portacode_connect_and_read_key(vmid: int, user: str, timeout_s: int = 10) -> Dict[str, Any]:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: portacode
3
- Version: 1.4.13.dev2
3
+ Version: 1.4.13.dev4
4
4
  Summary: Portacode CLI client and SDK
5
5
  Home-page: https://github.com/portacode/portacode
6
6
  Author: Meena Erian
@@ -1,7 +1,7 @@
1
1
  portacode/README.md,sha256=4dKtpvR8LNgZPVz37GmkQCMWIr_u25Ao63iW56s7Ke4,775
2
2
  portacode/__init__.py,sha256=oB3sV1wXr-um-RXio73UG8E5Xx6cF2ZVJveqjNmC-vQ,1086
3
3
  portacode/__main__.py,sha256=jmHTGC1hzmo9iKJLv-SSYe9BSIbPPZ2IOpecI03PlTs,296
4
- portacode/_version.py,sha256=DNXD9h6WnEeb90CTXVgfmtrpDU4zmIz1R3oJab1QQyY,719
4
+ portacode/_version.py,sha256=Fs9qvieZ__T0NcgTq_XxK_eFMBlTmdEvgXqq_uu2QV8,719
5
5
  portacode/cli.py,sha256=mGLKoZ-T2FBF7IA9wUq0zyG0X9__-A1ao7gajjcVRH8,21828
6
6
  portacode/data.py,sha256=5-s291bv8J354myaHm1Y7CQZTZyRzMU3TGe5U4hb-FA,1591
7
7
  portacode/keypair.py,sha256=0OO4vHDcF1XMxCDqce61xFTlFwlTcmqe5HyGsXFEt7s,5838
@@ -22,7 +22,7 @@ portacode/connection/handlers/diff_handlers.py,sha256=iYTIRCcpEQ03vIPKZCsMTE5aZb
22
22
  portacode/connection/handlers/file_handlers.py,sha256=nAJH8nXnX07xxD28ngLpgIUzcTuRwZBNpEGEKdRqohw,39507
23
23
  portacode/connection/handlers/project_aware_file_handlers.py,sha256=AqgMnDqX2893T2NsrvUSCwjN5VKj4Pb2TN0S_SuboOE,9803
24
24
  portacode/connection/handlers/project_state_handlers.py,sha256=v6ZefGW9i7n1aZLq2jOGumJIjYb6aHlPI4m1jkYewm8,1686
25
- portacode/connection/handlers/proxmox_infra.py,sha256=m9JKbJUBIpuv-7QGU1LBVqoZJXvPQzhNcx1MLatjNhs,58972
25
+ portacode/connection/handlers/proxmox_infra.py,sha256=1woStijF43ceynNZtt1jEVzb_AA9_UqltrU8cqsUEGw,60327
26
26
  portacode/connection/handlers/registry.py,sha256=qXGE60sYEWg6ZtVQzFcZ5YI2XWR6lMgw4hAL9x5qR1I,6181
27
27
  portacode/connection/handlers/session.py,sha256=uNGfiO_1B9-_yjJKkpvmbiJhIl6b-UXlT86UTfd6WYE,42219
28
28
  portacode/connection/handlers/system_handlers.py,sha256=AKh7IbwptlLYrbSw5f-DHigvlaKHsg9lDP-lkAUm8cE,10755
@@ -65,7 +65,7 @@ portacode/utils/__init__.py,sha256=NgBlWTuNJESfIYJzP_3adI1yJQJR0XJLRpSdVNaBAN0,3
65
65
  portacode/utils/diff_apply.py,sha256=4Oi7ft3VUCKmiUE4VM-OeqO7Gk6H7PF3WnN4WHXtjxI,15157
66
66
  portacode/utils/diff_renderer.py,sha256=S76StnQ2DLfsz4Gg0m07UwPfRp8270PuzbNaQq-rmYk,13850
67
67
  portacode/utils/ntp_clock.py,sha256=VqCnWCTehCufE43W23oB-WUdAZGeCcLxkmIOPwInYHc,2499
68
- portacode-1.4.13.dev2.dist-info/licenses/LICENSE,sha256=2FGbCnUDgRYuQTkB1O1dUUpu5CVAjK1j4_p6ack9Z54,1066
68
+ portacode-1.4.13.dev4.dist-info/licenses/LICENSE,sha256=2FGbCnUDgRYuQTkB1O1dUUpu5CVAjK1j4_p6ack9Z54,1066
69
69
  test_modules/README.md,sha256=Do_agkm9WhSzueXjRAkV_xEj6Emy5zB3N3VKY5Roce8,9274
70
70
  test_modules/__init__.py,sha256=1LcbHodIHsB0g-g4NGjSn6AMuCoGbymvXPYLOb6Z7F0,53
71
71
  test_modules/test_device_online.py,sha256=QtYq0Dq9vME8Gp2O4fGSheqVf8LUtpsSKosXXk56gGM,1654
@@ -91,8 +91,8 @@ testing_framework/core/playwright_manager.py,sha256=Tw46qwxIhOFkS48C2IWIQHHNpEe-
91
91
  testing_framework/core/runner.py,sha256=j2QwNJmAxVBmJvcbVS7DgPJUKPNzqfLmt_4NNdaKmZU,19297
92
92
  testing_framework/core/shared_cli_manager.py,sha256=BESSNtyQb7BOlaOvZmm04T8Uezjms4KCBs2MzTxvzYQ,8790
93
93
  testing_framework/core/test_discovery.py,sha256=2FZ9fJ8Dp5dloA-fkgXoJ_gCMC_nYPBnA3Hs2xlagzM,4928
94
- portacode-1.4.13.dev2.dist-info/METADATA,sha256=mVp7O5PlhPfvcaUdyshscyW9aH8bfYJ-FNIRkhunycE,13051
95
- portacode-1.4.13.dev2.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
96
- portacode-1.4.13.dev2.dist-info/entry_points.txt,sha256=lLUUL-BM6_wwe44Xv0__5NQ1BnAz6jWjSMFvZdWW3zU,48
97
- portacode-1.4.13.dev2.dist-info/top_level.txt,sha256=TGhTYUxfW8SyVZc_zGgzjzc24gGT7nSw8Qf73liVRKM,41
98
- portacode-1.4.13.dev2.dist-info/RECORD,,
94
+ portacode-1.4.13.dev4.dist-info/METADATA,sha256=JaS2exWbayOGy7ZrJu6Dzm6pJv3KhUWt7c6HkZAQ94E,13051
95
+ portacode-1.4.13.dev4.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
96
+ portacode-1.4.13.dev4.dist-info/entry_points.txt,sha256=lLUUL-BM6_wwe44Xv0__5NQ1BnAz6jWjSMFvZdWW3zU,48
97
+ portacode-1.4.13.dev4.dist-info/top_level.txt,sha256=TGhTYUxfW8SyVZc_zGgzjzc24gGT7nSw8Qf73liVRKM,41
98
+ portacode-1.4.13.dev4.dist-info/RECORD,,