portacode 1.4.15.dev8__py3-none-any.whl → 1.4.15.dev9__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.15.dev8'
32
- __version_tuple__ = version_tuple = (1, 4, 15, 'dev8')
31
+ __version__ = version = '1.4.15.dev9'
32
+ __version_tuple__ = version_tuple = (1, 4, 15, 'dev9')
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -504,48 +504,119 @@ def _friendly_step_label(step_name: str) -> str:
504
504
  return normalized.capitalize()
505
505
 
506
506
 
507
+ _PACKAGE_MANAGER_PROFILES: Dict[str, Dict[str, Any]] = {
508
+ "apt": {
509
+ "update_cmd": "apt-get update -y",
510
+ "update_step_name": "apt_update",
511
+ "install_cmd": "apt-get install -y python3 python3-pip sudo --fix-missing",
512
+ "install_step_name": "install_deps",
513
+ "update_retries": 4,
514
+ "install_retries": 5,
515
+ },
516
+ "dnf": {
517
+ "update_cmd": "dnf check-update || true",
518
+ "update_step_name": "dnf_update",
519
+ "install_cmd": "dnf install -y python3 python3-pip sudo",
520
+ "install_step_name": "install_deps",
521
+ "update_retries": 3,
522
+ "install_retries": 5,
523
+ },
524
+ "yum": {
525
+ "update_cmd": "yum makecache",
526
+ "update_step_name": "yum_update",
527
+ "install_cmd": "yum install -y python3 python3-pip sudo",
528
+ "install_step_name": "install_deps",
529
+ "update_retries": 3,
530
+ "install_retries": 5,
531
+ },
532
+ "apk": {
533
+ "update_cmd": "apk update",
534
+ "update_step_name": "apk_update",
535
+ "install_cmd": "apk add --no-cache python3 py3-pip sudo",
536
+ "install_step_name": "install_deps",
537
+ "update_retries": 3,
538
+ "install_retries": 5,
539
+ },
540
+ "pacman": {
541
+ "update_cmd": "pacman -Sy --noconfirm",
542
+ "update_step_name": "pacman_update",
543
+ "install_cmd": "pacman -S --noconfirm python python-pip sudo",
544
+ "install_step_name": "install_deps",
545
+ "update_retries": 3,
546
+ "install_retries": 5,
547
+ },
548
+ "zypper": {
549
+ "update_cmd": "zypper refresh",
550
+ "update_step_name": "zypper_update",
551
+ "install_cmd": "zypper install -y python3 python3-pip sudo",
552
+ "install_step_name": "install_deps",
553
+ "update_retries": 3,
554
+ "install_retries": 5,
555
+ },
556
+ }
557
+
558
+ _UPDATE_RETRY_ON = [
559
+ "Temporary failure resolving",
560
+ "Could not resolve",
561
+ "Failed to fetch",
562
+ ]
563
+
564
+ _INSTALL_RETRY_ON = [
565
+ "lock-frontend",
566
+ "Unable to acquire the dpkg frontend lock",
567
+ "Temporary failure resolving",
568
+ "Could not resolve",
569
+ "Failed to fetch",
570
+ ]
571
+
572
+
507
573
  def _build_bootstrap_steps(
508
574
  user: str,
509
575
  password: str,
510
576
  ssh_key: str,
511
577
  include_portacode_connect: bool = True,
578
+ package_manager: str = "apt",
512
579
  ) -> List[Dict[str, Any]]:
513
- steps = [
514
- {
515
- "name": "apt_update",
516
- "cmd": "apt-get update -y",
517
- "retries": 4,
518
- "retry_delay_s": 5,
519
- "retry_on": [
520
- "Temporary failure resolving",
521
- "Could not resolve",
522
- "Failed to fetch",
523
- ],
524
- },
525
- {
526
- "name": "install_deps",
527
- "cmd": "apt-get install -y python3 python3-pip sudo --fix-missing",
528
- "retries": 5,
529
- "retry_delay_s": 5,
530
- "retry_on": [
531
- "lock-frontend",
532
- "Unable to acquire the dpkg frontend lock",
533
- "Temporary failure resolving",
534
- "Could not resolve",
535
- "Failed to fetch",
536
- ],
537
- },
538
- {"name": "user_exists", "cmd": f"id -u {user} >/dev/null 2>&1 || adduser --disabled-password --gecos '' {user}", "retries": 0},
539
- {"name": "add_sudo", "cmd": f"usermod -aG sudo {user}", "retries": 0},
540
- ]
580
+ profile = _PACKAGE_MANAGER_PROFILES.get(package_manager, _PACKAGE_MANAGER_PROFILES["apt"])
581
+ steps: List[Dict[str, Any]] = []
582
+ update_cmd = profile.get("update_cmd")
583
+ if update_cmd:
584
+ steps.append(
585
+ {
586
+ "name": profile.get("update_step_name", "package_update"),
587
+ "cmd": update_cmd,
588
+ "retries": profile.get("update_retries", 3),
589
+ "retry_delay_s": 5,
590
+ "retry_on": _UPDATE_RETRY_ON,
591
+ }
592
+ )
593
+ install_cmd = profile.get("install_cmd")
594
+ if install_cmd:
595
+ steps.append(
596
+ {
597
+ "name": profile.get("install_step_name", "install_deps"),
598
+ "cmd": install_cmd,
599
+ "retries": profile.get("install_retries", 5),
600
+ "retry_delay_s": 5,
601
+ "retry_on": _INSTALL_RETRY_ON,
602
+ }
603
+ )
604
+ steps.extend(
605
+ [
606
+ {"name": "user_exists", "cmd": f"id -u {user} >/dev/null 2>&1 || adduser --disabled-password --gecos '' {user}", "retries": 0},
607
+ {"name": "add_sudo", "cmd": f"usermod -aG sudo {user}", "retries": 0},
608
+ ]
609
+ )
541
610
  if password:
542
611
  steps.append({"name": "set_password", "cmd": f"echo '{user}:{password}' | chpasswd", "retries": 0})
543
612
  if ssh_key:
544
- steps.append({
545
- "name": "add_ssh_key",
546
- "cmd": f"install -d -m 700 /home/{user}/.ssh && echo '{ssh_key}' >> /home/{user}/.ssh/authorized_keys && chown -R {user}:{user} /home/{user}/.ssh",
547
- "retries": 0,
548
- })
613
+ steps.append(
614
+ {
615
+ "name": "add_ssh_key",
616
+ "cmd": f"install -d -m 700 /home/{user}/.ssh && echo '{ssh_key}' >> /home/{user}/.ssh/authorized_keys && chown -R {user}:{user} /home/{user}/.ssh",
617
+ "retries": 0,
618
+ }
619
+ )
549
620
  steps.extend(
550
621
  [
551
622
  {"name": "pip_upgrade", "cmd": "python3 -m pip install --upgrade pip", "retries": 0},
@@ -557,6 +628,27 @@ def _build_bootstrap_steps(
557
628
  return steps
558
629
 
559
630
 
631
+ def _guess_package_manager_from_template(template: str) -> str:
632
+ normalized = (template or "").lower()
633
+ if "alpine" in normalized:
634
+ return "apk"
635
+ if "archlinux" in normalized:
636
+ return "pacman"
637
+ if "centos-7" in normalized:
638
+ return "yum"
639
+ if any(keyword in normalized for keyword in ("centos-8", "centos-9", "centos-9-stream", "centos-8-stream")):
640
+ return "dnf"
641
+ if any(keyword in normalized for keyword in ("rockylinux", "almalinux", "fedora")):
642
+ return "dnf"
643
+ if "opensuse" in normalized or "suse" in normalized:
644
+ return "zypper"
645
+ if any(keyword in normalized for keyword in ("debian", "ubuntu", "devuan", "turnkeylinux")):
646
+ return "apt"
647
+ if normalized.startswith("system/") and "linux" in normalized:
648
+ return "apt"
649
+ return "apt"
650
+
651
+
560
652
  def _get_storage_type(storages: Iterable[Dict[str, Any]], storage_name: str) -> str:
561
653
  for entry in storages:
562
654
  if entry.get("storage") == storage_name:
@@ -1221,12 +1313,17 @@ class CreateProxmoxContainerHandler(SyncHandler):
1221
1313
  device_public_key = (message.get("device_public_key") or "").strip()
1222
1314
  device_private_key = (message.get("device_private_key") or "").strip()
1223
1315
  has_device_keypair = bool(device_public_key and device_private_key)
1316
+ config_guess = _load_config()
1317
+ template_candidates = _get_startup_templates(config_guess)
1318
+ template_hint = (message.get("template") or (template_candidates[0] if template_candidates else "")).strip()
1319
+ package_manager = _guess_package_manager_from_template(template_hint)
1224
1320
  bootstrap_user, bootstrap_password, bootstrap_ssh_key = _get_provisioning_user_info(message)
1225
1321
  bootstrap_steps = _build_bootstrap_steps(
1226
1322
  bootstrap_user,
1227
1323
  bootstrap_password,
1228
1324
  bootstrap_ssh_key,
1229
1325
  include_portacode_connect=not has_device_keypair,
1326
+ package_manager=package_manager,
1230
1327
  )
1231
1328
  total_steps = 3 + len(bootstrap_steps) + 2
1232
1329
  current_step_index = 1
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: portacode
3
- Version: 1.4.15.dev8
3
+ Version: 1.4.15.dev9
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=wz7cM9xrg98SOlEfHeMTG7xaJCZ3Le5OiMq1M5iYGqc,719
4
+ portacode/_version.py,sha256=YbH9Im7cDkYWkWFiH-JGJ5BzJGjRVMg1TyCC8DAh1fE,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=QALeGkuvNdhpLidrEzZOVgowAXh3L2vk4YbdssGIHKk,64781
25
+ portacode/connection/handlers/proxmox_infra.py,sha256=g3AXQ_SQ5-WDH0ixDMtowqPtod6ruTrPd7_6FkMp1ts,68206
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.dev8.dist-info/licenses/LICENSE,sha256=2FGbCnUDgRYuQTkB1O1dUUpu5CVAjK1j4_p6ack9Z54,1066
68
+ portacode-1.4.15.dev9.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.dev8.dist-info/METADATA,sha256=KL7PnPejNsSu4P3ATAsmYLVmFpw8kGml7RjJypiLvHQ,13051
95
- portacode-1.4.15.dev8.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
96
- portacode-1.4.15.dev8.dist-info/entry_points.txt,sha256=lLUUL-BM6_wwe44Xv0__5NQ1BnAz6jWjSMFvZdWW3zU,48
97
- portacode-1.4.15.dev8.dist-info/top_level.txt,sha256=TGhTYUxfW8SyVZc_zGgzjzc24gGT7nSw8Qf73liVRKM,41
98
- portacode-1.4.15.dev8.dist-info/RECORD,,
94
+ portacode-1.4.15.dev9.dist-info/METADATA,sha256=6lnCia50lWblHLbiGKsxzaOVohX8yzayZgrAB8Hz-CY,13051
95
+ portacode-1.4.15.dev9.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
96
+ portacode-1.4.15.dev9.dist-info/entry_points.txt,sha256=lLUUL-BM6_wwe44Xv0__5NQ1BnAz6jWjSMFvZdWW3zU,48
97
+ portacode-1.4.15.dev9.dist-info/top_level.txt,sha256=TGhTYUxfW8SyVZc_zGgzjzc24gGT7nSw8Qf73liVRKM,41
98
+ portacode-1.4.15.dev9.dist-info/RECORD,,