portacode 1.4.15.dev10__py3-none-any.whl → 1.4.15.dev11__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.dev10'
32
- __version_tuple__ = version_tuple = (1, 4, 15, 'dev10')
31
+ __version__ = version = '1.4.15.dev11'
32
+ __version_tuple__ = version_tuple = (1, 4, 15, 'dev11')
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
- steps = [
483
- {
484
- "name": "apt_update",
485
- "cmd": "apt-get update -y",
486
- "retries": 4,
487
- "retry_delay_s": 5,
488
- "retry_on": [
489
- "Temporary failure resolving",
490
- "Could not resolve",
491
- "Failed to fetch",
492
- ],
493
- },
494
- {
495
- "name": "install_deps",
496
- "cmd": "apt-get install -y python3 python3-pip sudo --fix-missing",
497
- "retries": 5,
498
- "retry_delay_s": 5,
499
- "retry_on": [
500
- "lock-frontend",
501
- "Unable to acquire the dpkg frontend lock",
502
- "Temporary failure resolving",
503
- "Could not resolve",
504
- "Failed to fetch",
505
- ],
506
- },
507
- {"name": "user_exists", "cmd": f"id -u {user} >/dev/null 2>&1 || adduser --disabled-password --gecos '' {user}", "retries": 0},
508
- {"name": "add_sudo", "cmd": f"usermod -aG sudo {user}", "retries": 0},
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
- "name": "add_ssh_key",
515
- "cmd": f"install -d -m 700 /home/{user}/.ssh && echo '{ssh_key}' >> /home/{user}/.ssh/authorized_keys && chown -R {user}:{user} /home/{user}/.ssh",
516
- "retries": 0,
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,27 @@ 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
+
529
621
  def _get_storage_type(storages: Iterable[Dict[str, Any]], storage_name: str) -> str:
530
622
  for entry in storages:
531
623
  if entry.get("storage") == storage_name:
@@ -1189,12 +1281,17 @@ class CreateProxmoxContainerHandler(SyncHandler):
1189
1281
  device_public_key = (message.get("device_public_key") or "").strip()
1190
1282
  device_private_key = (message.get("device_private_key") or "").strip()
1191
1283
  has_device_keypair = bool(device_public_key and device_private_key)
1284
+ config_guess = _load_config()
1285
+ template_candidates = config_guess.get("templates") or []
1286
+ template_hint = (message.get("template") or (template_candidates[0] if template_candidates else "")).strip()
1287
+ package_manager = _guess_package_manager_from_template(template_hint)
1192
1288
  bootstrap_user, bootstrap_password, bootstrap_ssh_key = _get_provisioning_user_info(message)
1193
1289
  bootstrap_steps = _build_bootstrap_steps(
1194
1290
  bootstrap_user,
1195
1291
  bootstrap_password,
1196
1292
  bootstrap_ssh_key,
1197
1293
  include_portacode_connect=not has_device_keypair,
1294
+ package_manager=package_manager,
1198
1295
  )
1199
1296
  total_steps = 3 + len(bootstrap_steps) + 2
1200
1297
  current_step_index = 1
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: portacode
3
- Version: 1.4.15.dev10
3
+ Version: 1.4.15.dev11
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=OsU3LmSVwSb16YuRZZoOGhx4fmAppA2W-vOQo60j6hQ,721
4
+ portacode/_version.py,sha256=xmWnbzztE5Rc80b-QMEet19V4hm80qIDp1evukvT-mY,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=5DAHhzbegCI0fKdekP8Or6jrGZptmthLdq-RMtOPZDc,63843
25
+ portacode/connection/handlers/proxmox_infra.py,sha256=GX2IE1J0YX5GHnqqn__e_wpag_Lsa-cWVRQ82MZnbjU,67267
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.dev10.dist-info/licenses/LICENSE,sha256=2FGbCnUDgRYuQTkB1O1dUUpu5CVAjK1j4_p6ack9Z54,1066
68
+ portacode-1.4.15.dev11.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.dev10.dist-info/METADATA,sha256=FZkeltpJrx27P2fnujDDrYsinIFjnm1VsnnqTSVBk1g,13052
95
- portacode-1.4.15.dev10.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
96
- portacode-1.4.15.dev10.dist-info/entry_points.txt,sha256=lLUUL-BM6_wwe44Xv0__5NQ1BnAz6jWjSMFvZdWW3zU,48
97
- portacode-1.4.15.dev10.dist-info/top_level.txt,sha256=TGhTYUxfW8SyVZc_zGgzjzc24gGT7nSw8Qf73liVRKM,41
98
- portacode-1.4.15.dev10.dist-info/RECORD,,
94
+ portacode-1.4.15.dev11.dist-info/METADATA,sha256=YjU_cRf0I5P6zz-KmGOslg3APj4CL8IgOHbP13Z6T1U,13052
95
+ portacode-1.4.15.dev11.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
96
+ portacode-1.4.15.dev11.dist-info/entry_points.txt,sha256=lLUUL-BM6_wwe44Xv0__5NQ1BnAz6jWjSMFvZdWW3zU,48
97
+ portacode-1.4.15.dev11.dist-info/top_level.txt,sha256=TGhTYUxfW8SyVZc_zGgzjzc24gGT7nSw8Qf73liVRKM,41
98
+ portacode-1.4.15.dev11.dist-info/RECORD,,