devs-common 3.3.0__tar.gz → 3.3.1__tar.gz
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.
- {devs_common-3.3.0 → devs_common-3.3.1}/PKG-INFO +1 -1
- {devs_common-3.3.0 → devs_common-3.3.1}/devs_common/core/container.py +109 -21
- {devs_common-3.3.0 → devs_common-3.3.1}/devs_common.egg-info/PKG-INFO +1 -1
- {devs_common-3.3.0 → devs_common-3.3.1}/pyproject.toml +1 -1
- {devs_common-3.3.0 → devs_common-3.3.1}/LICENSE +0 -0
- {devs_common-3.3.0 → devs_common-3.3.1}/README.md +0 -0
- {devs_common-3.3.0 → devs_common-3.3.1}/devs_common/__init__.py +0 -0
- {devs_common-3.3.0 → devs_common-3.3.1}/devs_common/config.py +0 -0
- {devs_common-3.3.0 → devs_common-3.3.1}/devs_common/core/__init__.py +0 -0
- {devs_common-3.3.0 → devs_common-3.3.1}/devs_common/core/project.py +0 -0
- {devs_common-3.3.0 → devs_common-3.3.1}/devs_common/core/workspace.py +0 -0
- {devs_common-3.3.0 → devs_common-3.3.1}/devs_common/devs_config.py +0 -0
- {devs_common-3.3.0 → devs_common-3.3.1}/devs_common/exceptions.py +0 -0
- {devs_common-3.3.0 → devs_common-3.3.1}/devs_common/templates/Dockerfile +0 -0
- {devs_common-3.3.0 → devs_common-3.3.1}/devs_common/templates/devcontainer.json +0 -0
- {devs_common-3.3.0 → devs_common-3.3.1}/devs_common/templates/scripts/copy-ssh.sh +0 -0
- {devs_common-3.3.0 → devs_common-3.3.1}/devs_common/templates/scripts/post-create-wrapper.sh +0 -0
- {devs_common-3.3.0 → devs_common-3.3.1}/devs_common/templates/scripts/python-venv-integration.sh +0 -0
- {devs_common-3.3.0 → devs_common-3.3.1}/devs_common/templates/scripts/screenshot-webapp.sh +0 -0
- {devs_common-3.3.0 → devs_common-3.3.1}/devs_common/templates/scripts/setup-workspace.sh +0 -0
- {devs_common-3.3.0 → devs_common-3.3.1}/devs_common/templates/scripts/start-tunnel.sh +0 -0
- {devs_common-3.3.0 → devs_common-3.3.1}/devs_common/templates/scripts/tunnel-status.sh +0 -0
- {devs_common-3.3.0 → devs_common-3.3.1}/devs_common/templates/scripts/webapp-screenshot.py +0 -0
- {devs_common-3.3.0 → devs_common-3.3.1}/devs_common/templates/sudo-scripts/init-firewall.sh +0 -0
- {devs_common-3.3.0 → devs_common-3.3.1}/devs_common/templates/sudo-scripts/setup-devs-env.sh +0 -0
- {devs_common-3.3.0 → devs_common-3.3.1}/devs_common/templates/sudo-scripts/setup-vscode-cache.sh +0 -0
- {devs_common-3.3.0 → devs_common-3.3.1}/devs_common/templates/sudo-scripts/start-services.sh +0 -0
- {devs_common-3.3.0 → devs_common-3.3.1}/devs_common/templates/vscode-settings-reference.md +0 -0
- {devs_common-3.3.0 → devs_common-3.3.1}/devs_common/utils/__init__.py +0 -0
- {devs_common-3.3.0 → devs_common-3.3.1}/devs_common/utils/config_hash.py +0 -0
- {devs_common-3.3.0 → devs_common-3.3.1}/devs_common/utils/console.py +0 -0
- {devs_common-3.3.0 → devs_common-3.3.1}/devs_common/utils/devcontainer.py +0 -0
- {devs_common-3.3.0 → devs_common-3.3.1}/devs_common/utils/devcontainer_template.py +0 -0
- {devs_common-3.3.0 → devs_common-3.3.1}/devs_common/utils/docker_client.py +0 -0
- {devs_common-3.3.0 → devs_common-3.3.1}/devs_common/utils/file_utils.py +0 -0
- {devs_common-3.3.0 → devs_common-3.3.1}/devs_common/utils/git_utils.py +0 -0
- {devs_common-3.3.0 → devs_common-3.3.1}/devs_common/utils/repo_cache.py +0 -0
- {devs_common-3.3.0 → devs_common-3.3.1}/devs_common.egg-info/SOURCES.txt +0 -0
- {devs_common-3.3.0 → devs_common-3.3.1}/devs_common.egg-info/dependency_links.txt +0 -0
- {devs_common-3.3.0 → devs_common-3.3.1}/devs_common.egg-info/requires.txt +0 -0
- {devs_common-3.3.0 → devs_common-3.3.1}/devs_common.egg-info/top_level.txt +0 -0
- {devs_common-3.3.0 → devs_common-3.3.1}/setup.cfg +0 -0
|
@@ -908,6 +908,22 @@ class ContainerManager:
|
|
|
908
908
|
console.print("[yellow]Authentication was cancelled or failed.[/yellow]")
|
|
909
909
|
return False
|
|
910
910
|
|
|
911
|
+
def _kill_tunnel_processes(self, container_name: str) -> None:
|
|
912
|
+
"""Kill all tunnel processes in the container.
|
|
913
|
+
|
|
914
|
+
Uses pkill to ensure detached shell wrappers and tunnel processes
|
|
915
|
+
are fully terminated, not just the service-level shutdown that
|
|
916
|
+
'code tunnel kill' provides.
|
|
917
|
+
"""
|
|
918
|
+
subprocess.run(
|
|
919
|
+
['docker', 'exec', container_name, '/usr/local/bin/code', 'tunnel', 'kill'],
|
|
920
|
+
capture_output=True
|
|
921
|
+
)
|
|
922
|
+
subprocess.run(
|
|
923
|
+
['docker', 'exec', container_name, 'pkill', '-f', 'code tunnel'],
|
|
924
|
+
capture_output=True
|
|
925
|
+
)
|
|
926
|
+
|
|
911
927
|
def _start_and_poll_tunnel(self, container_name: str, dev_name: str, tunnel_cmd: str,
|
|
912
928
|
log_file: str, vscode_cmd: str, web_url: str, debug: bool = False) -> bool:
|
|
913
929
|
"""Start a detached tunnel and poll the log for startup confirmation.
|
|
@@ -915,6 +931,14 @@ class ContainerManager:
|
|
|
915
931
|
Returns:
|
|
916
932
|
True if tunnel started (or timed out), False if auth is needed.
|
|
917
933
|
"""
|
|
934
|
+
# Kill any stale tunnel processes and clear the log before starting
|
|
935
|
+
self._kill_tunnel_processes(container_name)
|
|
936
|
+
subprocess.run(
|
|
937
|
+
['docker', 'exec', container_name, '/bin/sh', '-c', f'> {log_file}'],
|
|
938
|
+
capture_output=True
|
|
939
|
+
)
|
|
940
|
+
time.sleep(1)
|
|
941
|
+
|
|
918
942
|
subprocess.run(
|
|
919
943
|
['docker', 'exec', '-d', container_name,
|
|
920
944
|
'/bin/sh', '-c', f'{tunnel_cmd} > {log_file} 2>&1'],
|
|
@@ -942,10 +966,7 @@ class ContainerManager:
|
|
|
942
966
|
return True
|
|
943
967
|
|
|
944
968
|
if 'log in' in output.lower() or 'device' in output.lower() or 'invalid' in output.lower():
|
|
945
|
-
|
|
946
|
-
['docker', 'exec', container_name, '/usr/local/bin/code', 'tunnel', 'kill'],
|
|
947
|
-
capture_output=True
|
|
948
|
-
)
|
|
969
|
+
self._kill_tunnel_processes(container_name)
|
|
949
970
|
return False
|
|
950
971
|
|
|
951
972
|
if debug and output.strip():
|
|
@@ -1078,14 +1099,92 @@ class ContainerManager:
|
|
|
1078
1099
|
|
|
1079
1100
|
result = subprocess.run(cmd, capture_output=True, text=True)
|
|
1080
1101
|
|
|
1102
|
+
# Get auth info (logged in user/provider)
|
|
1103
|
+
user_result = subprocess.run(
|
|
1104
|
+
['docker', 'exec', container_name,
|
|
1105
|
+
'/usr/local/bin/code', 'tunnel', 'user', 'show'],
|
|
1106
|
+
capture_output=True, text=True
|
|
1107
|
+
)
|
|
1108
|
+
auth_info = user_result.stdout.strip() if user_result.returncode == 0 else None
|
|
1109
|
+
|
|
1081
1110
|
if result.returncode == 0:
|
|
1082
|
-
|
|
1111
|
+
raw = result.stdout.strip()
|
|
1112
|
+
return True, self._format_tunnel_status(raw, dev_name, auth_info)
|
|
1083
1113
|
else:
|
|
1084
1114
|
return False, result.stderr.strip() if result.stderr else "No tunnel running"
|
|
1085
1115
|
|
|
1086
1116
|
except (DockerError, subprocess.SubprocessError) as e:
|
|
1087
1117
|
raise ContainerError(f"Failed to get tunnel status in {dev_name}: {e}")
|
|
1088
1118
|
|
|
1119
|
+
def _format_tunnel_status(self, raw_json: str, dev_name: str, auth_info: Optional[str] = None) -> str:
|
|
1120
|
+
"""Format tunnel status JSON into human-readable output."""
|
|
1121
|
+
import json
|
|
1122
|
+
try:
|
|
1123
|
+
data = json.loads(raw_json)
|
|
1124
|
+
except json.JSONDecodeError:
|
|
1125
|
+
return raw_json
|
|
1126
|
+
|
|
1127
|
+
tunnel = data.get("tunnel")
|
|
1128
|
+
if not tunnel:
|
|
1129
|
+
lines = ["[dim]● No tunnel running[/dim]"]
|
|
1130
|
+
if auth_info:
|
|
1131
|
+
lines.append(f" Auth: {auth_info}")
|
|
1132
|
+
return "\n".join(lines)
|
|
1133
|
+
name = tunnel.get("name") or "unknown"
|
|
1134
|
+
state = tunnel.get("tunnel", "Unknown")
|
|
1135
|
+
started_at = tunnel.get("started_at")
|
|
1136
|
+
connected_at = tunnel.get("last_connected_at")
|
|
1137
|
+
disconnected_at = tunnel.get("last_disconnected_at")
|
|
1138
|
+
fail_reason = tunnel.get("last_fail_reason")
|
|
1139
|
+
|
|
1140
|
+
lines = []
|
|
1141
|
+
|
|
1142
|
+
# Status line with color
|
|
1143
|
+
if state == "Connected":
|
|
1144
|
+
lines.append(f"[bold green]● Connected[/bold green] ({name})")
|
|
1145
|
+
elif state == "Disconnected":
|
|
1146
|
+
lines.append(f"[bold red]● Disconnected[/bold red] ({name})")
|
|
1147
|
+
else:
|
|
1148
|
+
lines.append(f"[bold yellow]● {state}[/bold yellow] ({name})")
|
|
1149
|
+
|
|
1150
|
+
# Timestamps
|
|
1151
|
+
if started_at:
|
|
1152
|
+
lines.append(f" Started: {self._format_timestamp(started_at)}")
|
|
1153
|
+
if connected_at:
|
|
1154
|
+
lines.append(f" Connected: {self._format_timestamp(connected_at)}")
|
|
1155
|
+
if disconnected_at:
|
|
1156
|
+
lines.append(f" Disconnected: {self._format_timestamp(disconnected_at)}")
|
|
1157
|
+
if fail_reason:
|
|
1158
|
+
lines.append(f" Fail reason: {fail_reason}")
|
|
1159
|
+
if auth_info:
|
|
1160
|
+
lines.append(f" Auth: {auth_info}")
|
|
1161
|
+
|
|
1162
|
+
# Connection info when connected
|
|
1163
|
+
if state == "Connected":
|
|
1164
|
+
tunnel_name = self._get_tunnel_name(dev_name)
|
|
1165
|
+
lines.append("")
|
|
1166
|
+
lines.append("[bold]Connect:[/bold]")
|
|
1167
|
+
lines.append(f" Desktop: code --remote tunnel+{tunnel_name}")
|
|
1168
|
+
lines.append(f" Browser: https://vscode.dev/tunnel/{tunnel_name}")
|
|
1169
|
+
|
|
1170
|
+
return "\n".join(lines)
|
|
1171
|
+
|
|
1172
|
+
@staticmethod
|
|
1173
|
+
def _format_timestamp(ts: str) -> str:
|
|
1174
|
+
"""Format an ISO timestamp into a friendly local time string."""
|
|
1175
|
+
from datetime import datetime
|
|
1176
|
+
try:
|
|
1177
|
+
# Handle nanosecond precision by truncating to microseconds
|
|
1178
|
+
if "." in ts:
|
|
1179
|
+
base, frac = ts.split(".")
|
|
1180
|
+
frac = frac.rstrip("Z")[:6]
|
|
1181
|
+
ts = f"{base}.{frac}Z"
|
|
1182
|
+
dt = datetime.fromisoformat(ts.replace("Z", "+00:00"))
|
|
1183
|
+
local_dt = dt.astimezone()
|
|
1184
|
+
return local_dt.strftime("%H:%M:%S %Z")
|
|
1185
|
+
except (ValueError, TypeError):
|
|
1186
|
+
return ts
|
|
1187
|
+
|
|
1089
1188
|
def kill_tunnel(self, dev_name: str, workspace_dir: Path, debug: bool = False, live: bool = False, extra_env: Optional[Dict[str, str]] = None) -> bool:
|
|
1090
1189
|
"""Kill a running VS Code tunnel in the container.
|
|
1091
1190
|
|
|
@@ -1108,24 +1207,13 @@ class ContainerManager:
|
|
|
1108
1207
|
dev_name, workspace_dir, debug=debug, live=live, extra_env=extra_env, reuse_existing=True
|
|
1109
1208
|
)
|
|
1110
1209
|
|
|
1111
|
-
# Kill the tunnel
|
|
1112
|
-
cmd = [
|
|
1113
|
-
'docker', 'exec',
|
|
1114
|
-
container_name,
|
|
1115
|
-
'/usr/local/bin/code', 'tunnel', 'kill'
|
|
1116
|
-
]
|
|
1117
|
-
|
|
1210
|
+
# Kill the tunnel (both service-level and process-level)
|
|
1118
1211
|
if debug:
|
|
1119
|
-
console.print(f"[dim]
|
|
1120
|
-
|
|
1121
|
-
result = subprocess.run(cmd, capture_output=True, text=True)
|
|
1212
|
+
console.print(f"[dim]Killing tunnel processes in {container_name}[/dim]")
|
|
1122
1213
|
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
else:
|
|
1127
|
-
console.print(f" No tunnel to kill in: {dev_name}")
|
|
1128
|
-
return False
|
|
1214
|
+
self._kill_tunnel_processes(container_name)
|
|
1215
|
+
console.print(f" Tunnel killed in: {dev_name}")
|
|
1216
|
+
return True
|
|
1129
1217
|
|
|
1130
1218
|
except (DockerError, subprocess.SubprocessError) as e:
|
|
1131
1219
|
raise ContainerError(f"Failed to kill tunnel in {dev_name}: {e}")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{devs_common-3.3.0 → devs_common-3.3.1}/devs_common/templates/scripts/post-create-wrapper.sh
RENAMED
|
File without changes
|
{devs_common-3.3.0 → devs_common-3.3.1}/devs_common/templates/scripts/python-venv-integration.sh
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{devs_common-3.3.0 → devs_common-3.3.1}/devs_common/templates/sudo-scripts/setup-devs-env.sh
RENAMED
|
File without changes
|
{devs_common-3.3.0 → devs_common-3.3.1}/devs_common/templates/sudo-scripts/setup-vscode-cache.sh
RENAMED
|
File without changes
|
{devs_common-3.3.0 → devs_common-3.3.1}/devs_common/templates/sudo-scripts/start-services.sh
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|