portacode 1.4.13.dev3__py3-none-any.whl → 1.4.13.dev5__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.dev3'
32
- __version_tuple__ = version_tuple = (1, 4, 13, 'dev3')
31
+ __version__ = version = '1.4.13.dev5'
32
+ __version_tuple__ = version_tuple = (1, 4, 13, 'dev5')
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -3,20 +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
15
16
  import time
16
17
  import threading
17
18
  from datetime import datetime
18
19
  from pathlib import Path
19
- from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple
20
+ from typing import Any, Callable, Dict, Iterable, List, Optional, Sequence, Tuple
20
21
 
21
22
  import platformdirs
22
23
 
@@ -688,34 +689,68 @@ def _run_pct_check(vmid: int, cmd: str) -> Dict[str, Any]:
688
689
  return res
689
690
 
690
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
+ parent = Path(path).parent
714
+ parent_str = parent.as_posix()
715
+ if parent_str not in {"", ".", "/"}:
716
+ _run_pct_exec_check(vmid, ["mkdir", "-p", parent_str])
717
+
718
+ with tempfile.NamedTemporaryFile(delete=False) as tmp:
719
+ tmp.write(data)
720
+ tmp.flush()
721
+ os.fsync(tmp.fileno())
722
+ tmp_path = tmp.name
723
+
724
+ push_res = _run_pct_push(vmid, tmp_path, path)
725
+ if push_res.returncode != 0:
726
+ raise RuntimeError(push_res.stderr or push_res.stdout or f"pct push returned {push_res.returncode}")
727
+
728
+ _run_pct_exec_check(vmid, ["chown", f"{user}:{user}", path])
729
+ _run_pct_exec_check(vmid, ["chmod", format(mode, "o"), path])
730
+ logger.debug("Successfully pushed %d bytes to vmid=%s path=%s", len(data), vmid, path)
731
+ except Exception as exc:
732
+ logger.error("Failed to write to container vmid=%s path=%s for user=%s: %s", vmid, path, user, exc)
733
+ raise
734
+ finally:
735
+ if tmp_path:
736
+ try:
737
+ os.remove(tmp_path)
738
+ except OSError as cleanup_exc:
739
+ logger.warning("Failed to remove temporary file %s: %s", tmp_path, cleanup_exc)
740
+
741
+
691
742
  def _resolve_portacode_key_dir(vmid: int, user: str) -> str:
692
743
  data_dir_cmd = f"su - {user} -c 'echo -n ${{XDG_DATA_HOME:-$HOME/.local/share}}'"
693
744
  data_home = _run_pct_check(vmid, data_dir_cmd)["stdout"].strip()
694
745
  return f"{data_home}/portacode/keys"
695
746
 
696
747
 
697
- def _write_bytes_as_user(vmid: int, user: str, path: str, data: bytes, mode: int = 0o600) -> None:
698
- encoded = base64.b64encode(data).decode()
699
- path_literal = json.dumps(path)
700
- script = (
701
- f"su - {user} -c 'python3 - <<\"PY\"\n"
702
- "import base64\n"
703
- "from pathlib import Path\n"
704
- f"path = Path({path_literal})\n"
705
- "path.parent.mkdir(parents=True, exist_ok=True)\n"
706
- f"path.write_bytes(base64.b64decode(\"{encoded}\"))\n"
707
- f"path.chmod({mode})\n"
708
- "PY'"
709
- )
710
- _run_pct_check(vmid, script)
711
-
712
-
713
748
  def _deploy_device_keypair(vmid: int, user: str, private_key: str, public_key: str) -> None:
714
749
  key_dir = _resolve_portacode_key_dir(vmid, user)
715
750
  priv_path = f"{key_dir}/id_portacode"
716
751
  pub_path = f"{key_dir}/id_portacode.pub"
717
- _write_bytes_as_user(vmid, user, priv_path, private_key.encode(), mode=0o600)
718
- _write_bytes_as_user(vmid, user, pub_path, public_key.encode(), mode=0o644)
752
+ _push_bytes_to_container(vmid, user, priv_path, private_key.encode(), mode=0o600)
753
+ _push_bytes_to_container(vmid, user, pub_path, public_key.encode(), mode=0o644)
719
754
 
720
755
 
721
756
  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.dev3
3
+ Version: 1.4.13.dev5
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=e6xwmilPep3lYl64LRXaySWQ_Gewk6WD90oO1hH3ckU,719
4
+ portacode/_version.py,sha256=-8qHEfgIuA1gX0IiXHXHG1Zfu4eNnZLDSsV3Lsp8upE,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=I9DzvhLDquUHGt_5e_AmmWSsIWka-O7kdWjMhonJ2b4,58984
25
+ portacode/connection/handlers/proxmox_infra.py,sha256=8-PTQqk-9abTY9DqWzUtabWDuMYpLNqw678vhjAQom4,60514
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.dev3.dist-info/licenses/LICENSE,sha256=2FGbCnUDgRYuQTkB1O1dUUpu5CVAjK1j4_p6ack9Z54,1066
68
+ portacode-1.4.13.dev5.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.dev3.dist-info/METADATA,sha256=vNRk53o0J8gYkSuPqsZO-3Aqhz86AAQwdRNEAf1s26U,13051
95
- portacode-1.4.13.dev3.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
96
- portacode-1.4.13.dev3.dist-info/entry_points.txt,sha256=lLUUL-BM6_wwe44Xv0__5NQ1BnAz6jWjSMFvZdWW3zU,48
97
- portacode-1.4.13.dev3.dist-info/top_level.txt,sha256=TGhTYUxfW8SyVZc_zGgzjzc24gGT7nSw8Qf73liVRKM,41
98
- portacode-1.4.13.dev3.dist-info/RECORD,,
94
+ portacode-1.4.13.dev5.dist-info/METADATA,sha256=2_UghpIjr0yiaPZRhlEMhiCy2Ii4hcA1JQl0jiVExy8,13051
95
+ portacode-1.4.13.dev5.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
96
+ portacode-1.4.13.dev5.dist-info/entry_points.txt,sha256=lLUUL-BM6_wwe44Xv0__5NQ1BnAz6jWjSMFvZdWW3zU,48
97
+ portacode-1.4.13.dev5.dist-info/top_level.txt,sha256=TGhTYUxfW8SyVZc_zGgzjzc24gGT7nSw8Qf73liVRKM,41
98
+ portacode-1.4.13.dev5.dist-info/RECORD,,