portacode 1.4.15.dev10__py3-none-any.whl → 1.4.15.dev12__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 +158 -34
- {portacode-1.4.15.dev10.dist-info → portacode-1.4.15.dev12.dist-info}/METADATA +1 -1
- {portacode-1.4.15.dev10.dist-info → portacode-1.4.15.dev12.dist-info}/RECORD +8 -8
- {portacode-1.4.15.dev10.dist-info → portacode-1.4.15.dev12.dist-info}/WHEEL +0 -0
- {portacode-1.4.15.dev10.dist-info → portacode-1.4.15.dev12.dist-info}/entry_points.txt +0 -0
- {portacode-1.4.15.dev10.dist-info → portacode-1.4.15.dev12.dist-info}/licenses/LICENSE +0 -0
- {portacode-1.4.15.dev10.dist-info → portacode-1.4.15.dev12.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.dev12'
|
|
32
|
+
__version_tuple__ = version_tuple = (1, 4, 15, 'dev12')
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
|
@@ -473,48 +473,119 @@ def _friendly_step_label(step_name: str) -> str:
|
|
|
473
473
|
return normalized.capitalize()
|
|
474
474
|
|
|
475
475
|
|
|
476
|
+
_PACKAGE_MANAGER_PROFILES: Dict[str, Dict[str, Any]] = {
|
|
477
|
+
"apt": {
|
|
478
|
+
"update_cmd": "apt-get update -y",
|
|
479
|
+
"update_step_name": "apt_update",
|
|
480
|
+
"install_cmd": "apt-get install -y python3 python3-pip sudo --fix-missing",
|
|
481
|
+
"install_step_name": "install_deps",
|
|
482
|
+
"update_retries": 4,
|
|
483
|
+
"install_retries": 5,
|
|
484
|
+
},
|
|
485
|
+
"dnf": {
|
|
486
|
+
"update_cmd": "dnf check-update || true",
|
|
487
|
+
"update_step_name": "dnf_update",
|
|
488
|
+
"install_cmd": "dnf install -y python3 python3-pip sudo",
|
|
489
|
+
"install_step_name": "install_deps",
|
|
490
|
+
"update_retries": 3,
|
|
491
|
+
"install_retries": 5,
|
|
492
|
+
},
|
|
493
|
+
"yum": {
|
|
494
|
+
"update_cmd": "yum makecache",
|
|
495
|
+
"update_step_name": "yum_update",
|
|
496
|
+
"install_cmd": "yum install -y python3 python3-pip sudo",
|
|
497
|
+
"install_step_name": "install_deps",
|
|
498
|
+
"update_retries": 3,
|
|
499
|
+
"install_retries": 5,
|
|
500
|
+
},
|
|
501
|
+
"apk": {
|
|
502
|
+
"update_cmd": "apk update",
|
|
503
|
+
"update_step_name": "apk_update",
|
|
504
|
+
"install_cmd": "apk add --no-cache python3 py3-pip sudo",
|
|
505
|
+
"install_step_name": "install_deps",
|
|
506
|
+
"update_retries": 3,
|
|
507
|
+
"install_retries": 5,
|
|
508
|
+
},
|
|
509
|
+
"pacman": {
|
|
510
|
+
"update_cmd": "pacman -Sy --noconfirm",
|
|
511
|
+
"update_step_name": "pacman_update",
|
|
512
|
+
"install_cmd": "pacman -S --noconfirm python python-pip sudo",
|
|
513
|
+
"install_step_name": "install_deps",
|
|
514
|
+
"update_retries": 3,
|
|
515
|
+
"install_retries": 5,
|
|
516
|
+
},
|
|
517
|
+
"zypper": {
|
|
518
|
+
"update_cmd": "zypper refresh",
|
|
519
|
+
"update_step_name": "zypper_update",
|
|
520
|
+
"install_cmd": "zypper install -y python3 python3-pip sudo",
|
|
521
|
+
"install_step_name": "install_deps",
|
|
522
|
+
"update_retries": 3,
|
|
523
|
+
"install_retries": 5,
|
|
524
|
+
},
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
_UPDATE_RETRY_ON = [
|
|
528
|
+
"Temporary failure resolving",
|
|
529
|
+
"Could not resolve",
|
|
530
|
+
"Failed to fetch",
|
|
531
|
+
]
|
|
532
|
+
|
|
533
|
+
_INSTALL_RETRY_ON = [
|
|
534
|
+
"lock-frontend",
|
|
535
|
+
"Unable to acquire the dpkg frontend lock",
|
|
536
|
+
"Temporary failure resolving",
|
|
537
|
+
"Could not resolve",
|
|
538
|
+
"Failed to fetch",
|
|
539
|
+
]
|
|
540
|
+
|
|
541
|
+
|
|
476
542
|
def _build_bootstrap_steps(
|
|
477
543
|
user: str,
|
|
478
544
|
password: str,
|
|
479
545
|
ssh_key: str,
|
|
480
546
|
include_portacode_connect: bool = True,
|
|
547
|
+
package_manager: str = "apt",
|
|
481
548
|
) -> List[Dict[str, Any]]:
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
"
|
|
490
|
-
"
|
|
491
|
-
"
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
"
|
|
501
|
-
"
|
|
502
|
-
"
|
|
503
|
-
"
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
549
|
+
profile = _PACKAGE_MANAGER_PROFILES.get(package_manager, _PACKAGE_MANAGER_PROFILES["apt"])
|
|
550
|
+
steps: List[Dict[str, Any]] = []
|
|
551
|
+
update_cmd = profile.get("update_cmd")
|
|
552
|
+
if update_cmd:
|
|
553
|
+
steps.append(
|
|
554
|
+
{
|
|
555
|
+
"name": profile.get("update_step_name", "package_update"),
|
|
556
|
+
"cmd": update_cmd,
|
|
557
|
+
"retries": profile.get("update_retries", 3),
|
|
558
|
+
"retry_delay_s": 5,
|
|
559
|
+
"retry_on": _UPDATE_RETRY_ON,
|
|
560
|
+
}
|
|
561
|
+
)
|
|
562
|
+
install_cmd = profile.get("install_cmd")
|
|
563
|
+
if install_cmd:
|
|
564
|
+
steps.append(
|
|
565
|
+
{
|
|
566
|
+
"name": profile.get("install_step_name", "install_deps"),
|
|
567
|
+
"cmd": install_cmd,
|
|
568
|
+
"retries": profile.get("install_retries", 5),
|
|
569
|
+
"retry_delay_s": 5,
|
|
570
|
+
"retry_on": _INSTALL_RETRY_ON,
|
|
571
|
+
}
|
|
572
|
+
)
|
|
573
|
+
steps.extend(
|
|
574
|
+
[
|
|
575
|
+
{"name": "user_exists", "cmd": f"id -u {user} >/dev/null 2>&1 || adduser --disabled-password --gecos '' {user}", "retries": 0},
|
|
576
|
+
{"name": "add_sudo", "cmd": f"usermod -aG sudo {user}", "retries": 0},
|
|
577
|
+
]
|
|
578
|
+
)
|
|
510
579
|
if password:
|
|
511
580
|
steps.append({"name": "set_password", "cmd": f"echo '{user}:{password}' | chpasswd", "retries": 0})
|
|
512
581
|
if ssh_key:
|
|
513
|
-
steps.append(
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
582
|
+
steps.append(
|
|
583
|
+
{
|
|
584
|
+
"name": "add_ssh_key",
|
|
585
|
+
"cmd": f"install -d -m 700 /home/{user}/.ssh && echo '{ssh_key}' >> /home/{user}/.ssh/authorized_keys && chown -R {user}:{user} /home/{user}/.ssh",
|
|
586
|
+
"retries": 0,
|
|
587
|
+
}
|
|
588
|
+
)
|
|
518
589
|
steps.extend(
|
|
519
590
|
[
|
|
520
591
|
{"name": "pip_upgrade", "cmd": "python3 -m pip install --upgrade pip", "retries": 0},
|
|
@@ -526,6 +597,45 @@ def _build_bootstrap_steps(
|
|
|
526
597
|
return steps
|
|
527
598
|
|
|
528
599
|
|
|
600
|
+
def _guess_package_manager_from_template(template: str) -> str:
|
|
601
|
+
normalized = (template or "").lower()
|
|
602
|
+
if "alpine" in normalized:
|
|
603
|
+
return "apk"
|
|
604
|
+
if "archlinux" in normalized:
|
|
605
|
+
return "pacman"
|
|
606
|
+
if "centos-7" in normalized:
|
|
607
|
+
return "yum"
|
|
608
|
+
if any(keyword in normalized for keyword in ("centos-8", "centos-9", "centos-9-stream", "centos-8-stream")):
|
|
609
|
+
return "dnf"
|
|
610
|
+
if any(keyword in normalized for keyword in ("rockylinux", "almalinux", "fedora")):
|
|
611
|
+
return "dnf"
|
|
612
|
+
if "opensuse" in normalized or "suse" in normalized:
|
|
613
|
+
return "zypper"
|
|
614
|
+
if any(keyword in normalized for keyword in ("debian", "ubuntu", "devuan", "turnkeylinux")):
|
|
615
|
+
return "apt"
|
|
616
|
+
if normalized.startswith("system/") and "linux" in normalized:
|
|
617
|
+
return "apt"
|
|
618
|
+
return "apt"
|
|
619
|
+
|
|
620
|
+
|
|
621
|
+
def _detect_package_manager(vmid: int) -> str:
|
|
622
|
+
candidates = [
|
|
623
|
+
("apt", "apt-get"),
|
|
624
|
+
("dnf", "dnf"),
|
|
625
|
+
("yum", "yum"),
|
|
626
|
+
("apk", "apk"),
|
|
627
|
+
("pacman", "pacman"),
|
|
628
|
+
("zypper", "zypper"),
|
|
629
|
+
]
|
|
630
|
+
for name, binary in candidates:
|
|
631
|
+
res = _run_pct(vmid, f"command -v {binary} >/dev/null 2>&1")
|
|
632
|
+
if res.get("returncode") == 0:
|
|
633
|
+
logger.debug("Detected package manager %s inside container %s", name, vmid)
|
|
634
|
+
return name
|
|
635
|
+
logger.warning("Unable to detect package manager inside container %s; defaulting to apt", vmid)
|
|
636
|
+
return "apt"
|
|
637
|
+
|
|
638
|
+
|
|
529
639
|
def _get_storage_type(storages: Iterable[Dict[str, Any]], storage_name: str) -> str:
|
|
530
640
|
for entry in storages:
|
|
531
641
|
if entry.get("storage") == storage_name:
|
|
@@ -1003,7 +1113,16 @@ def _bootstrap_portacode(
|
|
|
1003
1113
|
total_steps: Optional[int] = None,
|
|
1004
1114
|
default_public_key: Optional[str] = None,
|
|
1005
1115
|
) -> Tuple[str, List[Dict[str, Any]]]:
|
|
1006
|
-
|
|
1116
|
+
if steps is not None:
|
|
1117
|
+
actual_steps = steps
|
|
1118
|
+
else:
|
|
1119
|
+
detected_manager = _detect_package_manager(vmid)
|
|
1120
|
+
actual_steps = _build_bootstrap_steps(
|
|
1121
|
+
user,
|
|
1122
|
+
password,
|
|
1123
|
+
ssh_key,
|
|
1124
|
+
package_manager=detected_manager,
|
|
1125
|
+
)
|
|
1007
1126
|
results, ok = _run_setup_steps(
|
|
1008
1127
|
vmid,
|
|
1009
1128
|
actual_steps,
|
|
@@ -1189,12 +1308,17 @@ class CreateProxmoxContainerHandler(SyncHandler):
|
|
|
1189
1308
|
device_public_key = (message.get("device_public_key") or "").strip()
|
|
1190
1309
|
device_private_key = (message.get("device_private_key") or "").strip()
|
|
1191
1310
|
has_device_keypair = bool(device_public_key and device_private_key)
|
|
1311
|
+
config_guess = _load_config()
|
|
1312
|
+
template_candidates = config_guess.get("templates") or []
|
|
1313
|
+
template_hint = (message.get("template") or (template_candidates[0] if template_candidates else "")).strip()
|
|
1314
|
+
package_manager = _guess_package_manager_from_template(template_hint)
|
|
1192
1315
|
bootstrap_user, bootstrap_password, bootstrap_ssh_key = _get_provisioning_user_info(message)
|
|
1193
1316
|
bootstrap_steps = _build_bootstrap_steps(
|
|
1194
1317
|
bootstrap_user,
|
|
1195
1318
|
bootstrap_password,
|
|
1196
1319
|
bootstrap_ssh_key,
|
|
1197
1320
|
include_portacode_connect=not has_device_keypair,
|
|
1321
|
+
package_manager=package_manager,
|
|
1198
1322
|
)
|
|
1199
1323
|
total_steps = 3 + len(bootstrap_steps) + 2
|
|
1200
1324
|
current_step_index = 1
|
|
@@ -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=Onnf1SIY68AMKoWtMkpxq_B6Sw_RYyZQ86CKs_MVF84,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=aPVc55P8b0EqK4JGCw68EDZSscaBmYzH1DaxgRi3W0s,68061
|
|
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.dev12.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.dev12.dist-info/METADATA,sha256=dwNd_5apDmi-D2oDUKfEzBfEWsG7tzgjMSK0VwczFnE,13052
|
|
95
|
+
portacode-1.4.15.dev12.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
96
|
+
portacode-1.4.15.dev12.dist-info/entry_points.txt,sha256=lLUUL-BM6_wwe44Xv0__5NQ1BnAz6jWjSMFvZdWW3zU,48
|
|
97
|
+
portacode-1.4.15.dev12.dist-info/top_level.txt,sha256=TGhTYUxfW8SyVZc_zGgzjzc24gGT7nSw8Qf73liVRKM,41
|
|
98
|
+
portacode-1.4.15.dev12.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|