portacode 1.4.15.dev15__py3-none-any.whl → 1.4.15.dev23__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 +2 -2
- portacode/connection/handlers/proxmox_infra.py +59 -12
- {portacode-1.4.15.dev15.dist-info → portacode-1.4.15.dev23.dist-info}/METADATA +1 -1
- {portacode-1.4.15.dev15.dist-info → portacode-1.4.15.dev23.dist-info}/RECORD +8 -8
- {portacode-1.4.15.dev15.dist-info → portacode-1.4.15.dev23.dist-info}/WHEEL +0 -0
- {portacode-1.4.15.dev15.dist-info → portacode-1.4.15.dev23.dist-info}/entry_points.txt +0 -0
- {portacode-1.4.15.dev15.dist-info → portacode-1.4.15.dev23.dist-info}/licenses/LICENSE +0 -0
- {portacode-1.4.15.dev15.dist-info → portacode-1.4.15.dev23.dist-info}/top_level.txt +0 -0
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.15.
|
|
32
|
-
__version_tuple__ = version_tuple = (1, 4, 15, '
|
|
31
|
+
__version__ = version = '1.4.15.dev23'
|
|
32
|
+
__version_tuple__ = version_tuple = (1, 4, 15, 'dev23')
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
|
@@ -496,6 +496,16 @@ def _friendly_step_label(step_name: str) -> str:
|
|
|
496
496
|
return normalized.capitalize()
|
|
497
497
|
|
|
498
498
|
|
|
499
|
+
_NETWORK_WAIT_CMD = (
|
|
500
|
+
"count=0; "
|
|
501
|
+
"while [ \"$count\" -lt 20 ]; do "
|
|
502
|
+
" if command -v ip >/dev/null 2>&1 && ip route get 1.1.1.1 >/dev/null 2>&1; then break; fi; "
|
|
503
|
+
" if [ -f /proc/net/route ] && grep -q '^00000000' /proc/net/route >/dev/null 2>&1; then break; fi; "
|
|
504
|
+
" sleep 1; "
|
|
505
|
+
" count=$((count+1)); "
|
|
506
|
+
"done"
|
|
507
|
+
)
|
|
508
|
+
|
|
499
509
|
_PACKAGE_MANAGER_PROFILES: Dict[str, Dict[str, Any]] = {
|
|
500
510
|
"apt": {
|
|
501
511
|
"update_cmd": "apt-get update -y",
|
|
@@ -524,7 +534,7 @@ _PACKAGE_MANAGER_PROFILES: Dict[str, Dict[str, Any]] = {
|
|
|
524
534
|
"apk": {
|
|
525
535
|
"update_cmd": "apk update",
|
|
526
536
|
"update_step_name": "apk_update",
|
|
527
|
-
"install_cmd": "apk add --no-cache python3 py3-pip sudo",
|
|
537
|
+
"install_cmd": "apk add --no-cache python3 py3-pip sudo shadow",
|
|
528
538
|
"install_step_name": "install_deps",
|
|
529
539
|
"update_retries": 3,
|
|
530
540
|
"install_retries": 5,
|
|
@@ -570,7 +580,9 @@ def _build_bootstrap_steps(
|
|
|
570
580
|
package_manager: str = "apt",
|
|
571
581
|
) -> List[Dict[str, Any]]:
|
|
572
582
|
profile = _PACKAGE_MANAGER_PROFILES.get(package_manager, _PACKAGE_MANAGER_PROFILES["apt"])
|
|
573
|
-
steps: List[Dict[str, Any]] = [
|
|
583
|
+
steps: List[Dict[str, Any]] = [
|
|
584
|
+
{"name": "wait_for_network", "cmd": _NETWORK_WAIT_CMD, "retries": 0},
|
|
585
|
+
]
|
|
574
586
|
update_cmd = profile.get("update_cmd")
|
|
575
587
|
if update_cmd:
|
|
576
588
|
steps.append(
|
|
@@ -596,7 +608,36 @@ def _build_bootstrap_steps(
|
|
|
596
608
|
steps.extend(
|
|
597
609
|
[
|
|
598
610
|
{"name": "user_exists", "cmd": f"id -u {user} >/dev/null 2>&1 || adduser --disabled-password --gecos '' {user}", "retries": 0},
|
|
599
|
-
{
|
|
611
|
+
{
|
|
612
|
+
"name": "add_sudo",
|
|
613
|
+
"cmd": (
|
|
614
|
+
f"if command -v usermod >/dev/null 2>&1; then "
|
|
615
|
+
" if ! getent group sudo >/dev/null 2>&1; then "
|
|
616
|
+
" if command -v groupadd >/dev/null 2>&1; then "
|
|
617
|
+
" groupadd sudo >/dev/null 2>&1 || true; "
|
|
618
|
+
" fi; "
|
|
619
|
+
" fi; "
|
|
620
|
+
f" usermod -aG sudo {user}; "
|
|
621
|
+
"else "
|
|
622
|
+
" for grp in wheel sudo; do "
|
|
623
|
+
" if ! getent group \"$grp\" >/dev/null 2>&1 && command -v groupadd >/dev/null 2>&1; then "
|
|
624
|
+
" groupadd \"$grp\" >/dev/null 2>&1 || true; "
|
|
625
|
+
" fi; "
|
|
626
|
+
" addgroup \"$grp\" >/dev/null 2>&1 || true; "
|
|
627
|
+
f" addgroup {user} \"$grp\" >/dev/null 2>&1 || true; "
|
|
628
|
+
" done; "
|
|
629
|
+
"fi"
|
|
630
|
+
),
|
|
631
|
+
"retries": 0,
|
|
632
|
+
},
|
|
633
|
+
{
|
|
634
|
+
"name": "add_sudoers",
|
|
635
|
+
"cmd": (
|
|
636
|
+
f"printf '%s ALL=(ALL) NOPASSWD:ALL\n' {shlex.quote(user)} >/etc/sudoers.d/portacode && "
|
|
637
|
+
"chmod 0440 /etc/sudoers.d/portacode"
|
|
638
|
+
),
|
|
639
|
+
"retries": 0,
|
|
640
|
+
},
|
|
600
641
|
]
|
|
601
642
|
)
|
|
602
643
|
if password:
|
|
@@ -846,7 +887,8 @@ def _connect_proxmox(config: Dict[str, Any]) -> Any:
|
|
|
846
887
|
|
|
847
888
|
|
|
848
889
|
def _run_pct(vmid: int, cmd: str, input_text: Optional[str] = None) -> Dict[str, Any]:
|
|
849
|
-
|
|
890
|
+
shell = "/bin/sh"
|
|
891
|
+
full = ["pct", "exec", str(vmid), "--", shell, "-c", cmd]
|
|
850
892
|
start = time.time()
|
|
851
893
|
proc = subprocess.run(full, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, input=input_text)
|
|
852
894
|
return {
|
|
@@ -858,6 +900,10 @@ def _run_pct(vmid: int, cmd: str, input_text: Optional[str] = None) -> Dict[str,
|
|
|
858
900
|
}
|
|
859
901
|
|
|
860
902
|
|
|
903
|
+
def _su_command(user: str, command: str) -> str:
|
|
904
|
+
return f"su - {user} -s /bin/sh -c {shlex.quote(command)}"
|
|
905
|
+
|
|
906
|
+
|
|
861
907
|
def _run_pct_check(vmid: int, cmd: str) -> Dict[str, Any]:
|
|
862
908
|
res = _run_pct(vmid, cmd)
|
|
863
909
|
if res["returncode"] != 0:
|
|
@@ -917,7 +963,7 @@ def _push_bytes_to_container(
|
|
|
917
963
|
|
|
918
964
|
|
|
919
965
|
def _resolve_portacode_key_dir(vmid: int, user: str) -> str:
|
|
920
|
-
data_dir_cmd =
|
|
966
|
+
data_dir_cmd = _su_command(user, "echo -n ${XDG_DATA_HOME:-$HOME/.local/share}")
|
|
921
967
|
data_home = _run_pct_check(vmid, data_dir_cmd)["stdout"].strip()
|
|
922
968
|
portacode_dir = f"{data_home}/portacode"
|
|
923
969
|
_run_pct_exec_check(vmid, ["mkdir", "-p", portacode_dir])
|
|
@@ -934,18 +980,19 @@ def _deploy_device_keypair(vmid: int, user: str, private_key: str, public_key: s
|
|
|
934
980
|
|
|
935
981
|
|
|
936
982
|
def _portacode_connect_and_read_key(vmid: int, user: str, timeout_s: int = 10) -> Dict[str, Any]:
|
|
937
|
-
|
|
983
|
+
su_connect_cmd = _su_command(user, "portacode connect")
|
|
984
|
+
cmd = ["pct", "exec", str(vmid), "--", "/bin/sh", "-c", su_connect_cmd]
|
|
938
985
|
proc = subprocess.Popen(cmd, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
939
986
|
start = time.time()
|
|
940
987
|
|
|
941
|
-
data_dir_cmd =
|
|
988
|
+
data_dir_cmd = _su_command(user, "echo -n ${XDG_DATA_HOME:-$HOME/.local/share}")
|
|
942
989
|
data_dir = _run_pct_check(vmid, data_dir_cmd)["stdout"].strip()
|
|
943
990
|
key_dir = f"{data_dir}/portacode/keys"
|
|
944
991
|
pub_path = f"{key_dir}/id_portacode.pub"
|
|
945
992
|
priv_path = f"{key_dir}/id_portacode"
|
|
946
993
|
|
|
947
994
|
def file_size(path: str) -> Optional[int]:
|
|
948
|
-
stat_cmd = f"
|
|
995
|
+
stat_cmd = _su_command(user, f"test -s {path} && stat -c %s {path}")
|
|
949
996
|
res = _run_pct(vmid, stat_cmd)
|
|
950
997
|
if res["returncode"] != 0:
|
|
951
998
|
return None
|
|
@@ -1003,7 +1050,7 @@ def _portacode_connect_and_read_key(vmid: int, user: str, timeout_s: int = 10) -
|
|
|
1003
1050
|
final_pub = file_size(pub_path)
|
|
1004
1051
|
final_priv = file_size(priv_path)
|
|
1005
1052
|
if final_pub and final_priv:
|
|
1006
|
-
key_res = _run_pct(vmid, f"
|
|
1053
|
+
key_res = _run_pct(vmid, _su_command(user, f"cat {pub_path}"))
|
|
1007
1054
|
if not process_exited:
|
|
1008
1055
|
proc.terminate()
|
|
1009
1056
|
try:
|
|
@@ -1043,7 +1090,7 @@ def _portacode_connect_and_read_key(vmid: int, user: str, timeout_s: int = 10) -
|
|
|
1043
1090
|
except subprocess.TimeoutExpired:
|
|
1044
1091
|
proc.kill()
|
|
1045
1092
|
|
|
1046
|
-
key_res = _run_pct(vmid, f"
|
|
1093
|
+
key_res = _run_pct(vmid, _su_command(user, f"cat {pub_path}"))
|
|
1047
1094
|
return {
|
|
1048
1095
|
"ok": True,
|
|
1049
1096
|
"public_key": key_res["stdout"].strip(),
|
|
@@ -1586,7 +1633,7 @@ class CreateProxmoxContainerHandler(SyncHandler):
|
|
|
1586
1633
|
on_behalf_of_device=device_id,
|
|
1587
1634
|
)
|
|
1588
1635
|
|
|
1589
|
-
cmd =
|
|
1636
|
+
cmd = _su_command(payload["username"], "sudo -S portacode service install")
|
|
1590
1637
|
res = _run_pct(vmid, cmd, input_text=payload["password"] + "\n")
|
|
1591
1638
|
|
|
1592
1639
|
if res["returncode"] != 0:
|
|
@@ -1718,7 +1765,7 @@ class StartPortacodeServiceHandler(SyncHandler):
|
|
|
1718
1765
|
on_behalf_of_device=on_behalf_of_device,
|
|
1719
1766
|
)
|
|
1720
1767
|
|
|
1721
|
-
cmd =
|
|
1768
|
+
cmd = _su_command(user, "sudo -S portacode service install")
|
|
1722
1769
|
res = _run_pct(vmid, cmd, input_text=password + "\n")
|
|
1723
1770
|
|
|
1724
1771
|
if res["returncode"] != 0:
|
|
@@ -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=
|
|
4
|
+
portacode/_version.py,sha256=8YGMPC5_rqCpJnjJF3sLM5flS3H0d8do1siDivvmwVs,721
|
|
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=
|
|
25
|
+
portacode/connection/handlers/proxmox_infra.py,sha256=cAueQb_D3COmPI84LfI1bqHOcgt02BOgho_pCRGeLWE,71132
|
|
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=fr12QpOr_Z8KYGUU-AYrTQwRPAcrLK85hvj3SEq1Kw8,14757
|
|
@@ -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.15.
|
|
68
|
+
portacode-1.4.15.dev23.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.15.
|
|
95
|
-
portacode-1.4.15.
|
|
96
|
-
portacode-1.4.15.
|
|
97
|
-
portacode-1.4.15.
|
|
98
|
-
portacode-1.4.15.
|
|
94
|
+
portacode-1.4.15.dev23.dist-info/METADATA,sha256=1pp2pJiX9YjuOBghWO4mYN7fkpK0dqJSv6CdFMgbwOQ,13052
|
|
95
|
+
portacode-1.4.15.dev23.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
96
|
+
portacode-1.4.15.dev23.dist-info/entry_points.txt,sha256=lLUUL-BM6_wwe44Xv0__5NQ1BnAz6jWjSMFvZdWW3zU,48
|
|
97
|
+
portacode-1.4.15.dev23.dist-info/top_level.txt,sha256=TGhTYUxfW8SyVZc_zGgzjzc24gGT7nSw8Qf73liVRKM,41
|
|
98
|
+
portacode-1.4.15.dev23.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|