devs-common 2.0.3__tar.gz → 2.0.4__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-2.0.3 → devs_common-2.0.4}/PKG-INFO +1 -1
- {devs_common-2.0.3 → devs_common-2.0.4}/devs_common/core/container.py +139 -40
- {devs_common-2.0.3 → devs_common-2.0.4}/devs_common/templates/Dockerfile +2 -2
- {devs_common-2.0.3 → devs_common-2.0.4}/devs_common/templates/devcontainer.json +2 -1
- {devs_common-2.0.3 → devs_common-2.0.4}/devs_common.egg-info/PKG-INFO +1 -1
- {devs_common-2.0.3 → devs_common-2.0.4}/pyproject.toml +1 -1
- {devs_common-2.0.3 → devs_common-2.0.4}/LICENSE +0 -0
- {devs_common-2.0.3 → devs_common-2.0.4}/README.md +0 -0
- {devs_common-2.0.3 → devs_common-2.0.4}/devs_common/__init__.py +0 -0
- {devs_common-2.0.3 → devs_common-2.0.4}/devs_common/config.py +0 -0
- {devs_common-2.0.3 → devs_common-2.0.4}/devs_common/core/__init__.py +0 -0
- {devs_common-2.0.3 → devs_common-2.0.4}/devs_common/core/project.py +0 -0
- {devs_common-2.0.3 → devs_common-2.0.4}/devs_common/core/workspace.py +0 -0
- {devs_common-2.0.3 → devs_common-2.0.4}/devs_common/devs_config.py +0 -0
- {devs_common-2.0.3 → devs_common-2.0.4}/devs_common/exceptions.py +0 -0
- {devs_common-2.0.3 → devs_common-2.0.4}/devs_common/templates/scripts/copy-ssh.sh +0 -0
- {devs_common-2.0.3 → devs_common-2.0.4}/devs_common/templates/scripts/post-create-wrapper.sh +0 -0
- {devs_common-2.0.3 → devs_common-2.0.4}/devs_common/templates/scripts/python-venv-integration.sh +0 -0
- {devs_common-2.0.3 → devs_common-2.0.4}/devs_common/templates/scripts/screenshot-webapp.sh +0 -0
- {devs_common-2.0.3 → devs_common-2.0.4}/devs_common/templates/scripts/setup-workspace.sh +0 -0
- {devs_common-2.0.3 → devs_common-2.0.4}/devs_common/templates/scripts/start-tunnel.sh +0 -0
- {devs_common-2.0.3 → devs_common-2.0.4}/devs_common/templates/scripts/tunnel-status.sh +0 -0
- {devs_common-2.0.3 → devs_common-2.0.4}/devs_common/templates/scripts/webapp-screenshot.py +0 -0
- {devs_common-2.0.3 → devs_common-2.0.4}/devs_common/templates/sudo-scripts/init-firewall.sh +0 -0
- {devs_common-2.0.3 → devs_common-2.0.4}/devs_common/templates/sudo-scripts/setup-devs-env.sh +0 -0
- {devs_common-2.0.3 → devs_common-2.0.4}/devs_common/templates/sudo-scripts/setup-vscode-cache.sh +0 -0
- {devs_common-2.0.3 → devs_common-2.0.4}/devs_common/templates/sudo-scripts/start-services.sh +0 -0
- {devs_common-2.0.3 → devs_common-2.0.4}/devs_common/templates/vscode-settings-reference.md +0 -0
- {devs_common-2.0.3 → devs_common-2.0.4}/devs_common/utils/__init__.py +0 -0
- {devs_common-2.0.3 → devs_common-2.0.4}/devs_common/utils/config_hash.py +0 -0
- {devs_common-2.0.3 → devs_common-2.0.4}/devs_common/utils/console.py +0 -0
- {devs_common-2.0.3 → devs_common-2.0.4}/devs_common/utils/devcontainer.py +0 -0
- {devs_common-2.0.3 → devs_common-2.0.4}/devs_common/utils/devcontainer_template.py +0 -0
- {devs_common-2.0.3 → devs_common-2.0.4}/devs_common/utils/docker_client.py +0 -0
- {devs_common-2.0.3 → devs_common-2.0.4}/devs_common/utils/file_utils.py +0 -0
- {devs_common-2.0.3 → devs_common-2.0.4}/devs_common/utils/git_utils.py +0 -0
- {devs_common-2.0.3 → devs_common-2.0.4}/devs_common.egg-info/SOURCES.txt +0 -0
- {devs_common-2.0.3 → devs_common-2.0.4}/devs_common.egg-info/dependency_links.txt +0 -0
- {devs_common-2.0.3 → devs_common-2.0.4}/devs_common.egg-info/requires.txt +0 -0
- {devs_common-2.0.3 → devs_common-2.0.4}/devs_common.egg-info/top_level.txt +0 -0
- {devs_common-2.0.3 → devs_common-2.0.4}/setup.cfg +0 -0
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
4
|
import re
|
|
5
|
+
import time
|
|
5
6
|
from datetime import datetime, timezone
|
|
6
7
|
from pathlib import Path
|
|
7
8
|
from typing import Dict, List, Optional, Tuple
|
|
@@ -817,14 +818,34 @@ class ContainerManager:
|
|
|
817
818
|
extra_env=extra_env
|
|
818
819
|
)
|
|
819
820
|
|
|
820
|
-
def
|
|
821
|
-
"""
|
|
821
|
+
def _get_tunnel_name(self, dev_name: str) -> str:
|
|
822
|
+
"""Generate a VS Code tunnel name, truncated to 20 chars.
|
|
823
|
+
|
|
824
|
+
VS Code tunnel names have a 20 character limit. We keep the dev name
|
|
825
|
+
suffix and shorten the project portion if needed.
|
|
826
|
+
"""
|
|
827
|
+
project_prefix = self.config.project_prefix if self.config else "dev"
|
|
828
|
+
tunnel_name = f"{project_prefix}-{self.project.info.name}-{dev_name}"
|
|
829
|
+
# Sanitize: tunnel names can only contain alphanumeric, dash, underscore
|
|
830
|
+
tunnel_name = tunnel_name.replace(".", "-").replace("_", "-")
|
|
831
|
+
|
|
832
|
+
if len(tunnel_name) > 20:
|
|
833
|
+
suffix = f"-{dev_name}"
|
|
834
|
+
prefix_budget = 20 - len(suffix)
|
|
835
|
+
if prefix_budget >= 3:
|
|
836
|
+
project_part = f"{project_prefix}-{self.project.info.name}"
|
|
837
|
+
project_part = project_part[:prefix_budget].rstrip("-")
|
|
838
|
+
tunnel_name = f"{project_part}{suffix}"
|
|
839
|
+
else:
|
|
840
|
+
tunnel_name = tunnel_name[:20]
|
|
841
|
+
|
|
842
|
+
return tunnel_name
|
|
822
843
|
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
and can be stopped with Ctrl+C.
|
|
844
|
+
def tunnel_auth(self, dev_name: str, workspace_dir: Path, debug: bool = False, live: bool = False, extra_env: Optional[Dict[str, str]] = None) -> None:
|
|
845
|
+
"""Authenticate VS Code tunnel (interactive, one-time setup).
|
|
826
846
|
|
|
827
|
-
|
|
847
|
+
Runs the device code flow interactively. Auth is stored in a shared
|
|
848
|
+
Docker volume so it persists across containers and recreations.
|
|
828
849
|
|
|
829
850
|
Args:
|
|
830
851
|
dev_name: Development environment name
|
|
@@ -834,62 +855,140 @@ class ContainerManager:
|
|
|
834
855
|
extra_env: Additional environment variables to pass to container
|
|
835
856
|
|
|
836
857
|
Raises:
|
|
837
|
-
ContainerError: If
|
|
858
|
+
ContainerError: If authentication fails
|
|
838
859
|
"""
|
|
839
860
|
try:
|
|
840
|
-
# Prepare container for execution
|
|
841
861
|
container_name, container_workspace_dir = self._prepare_container_exec(
|
|
842
862
|
dev_name, workspace_dir, debug=debug, live=live, extra_env=extra_env
|
|
843
863
|
)
|
|
844
864
|
|
|
845
|
-
|
|
846
|
-
# This will be the name that appears in VS Code Remote Explorer
|
|
847
|
-
# VS Code tunnel names have a 20 character limit
|
|
848
|
-
project_prefix = self.config.project_prefix if self.config else "dev"
|
|
849
|
-
tunnel_name = f"{project_prefix}-{self.project.info.name}-{dev_name}"
|
|
850
|
-
# Sanitize: tunnel names can only contain alphanumeric, dash, underscore
|
|
851
|
-
tunnel_name = tunnel_name.replace(".", "-").replace("_", "-")
|
|
852
|
-
|
|
853
|
-
# Truncate to 20 chars if needed (VS Code tunnel limit)
|
|
854
|
-
if len(tunnel_name) > 20:
|
|
855
|
-
# Keep the dev name suffix, shorten the project part
|
|
856
|
-
suffix = f"-{dev_name}"
|
|
857
|
-
prefix_budget = 20 - len(suffix)
|
|
858
|
-
if prefix_budget >= 3:
|
|
859
|
-
project_part = f"{project_prefix}-{self.project.info.name}"
|
|
860
|
-
project_part = project_part[:prefix_budget].rstrip("-")
|
|
861
|
-
tunnel_name = f"{project_part}{suffix}"
|
|
862
|
-
else:
|
|
863
|
-
# Dev name itself is very long, just truncate the whole thing
|
|
864
|
-
tunnel_name = tunnel_name[:20]
|
|
865
|
+
tunnel_name = self._get_tunnel_name(dev_name)
|
|
865
866
|
|
|
866
|
-
console.print(
|
|
867
|
+
console.print("[bold cyan]Setting up VS Code tunnel authentication...[/bold cyan]")
|
|
867
868
|
console.print(f" Container: {container_name}")
|
|
868
|
-
console.print(f" Tunnel name: {tunnel_name}")
|
|
869
869
|
console.print("")
|
|
870
|
-
console.print("[yellow]
|
|
870
|
+
console.print("[yellow]Follow the prompts to authenticate:[/yellow]")
|
|
871
871
|
console.print(" 1. A URL and code will be displayed")
|
|
872
872
|
console.print(" 2. Open the URL in your browser")
|
|
873
873
|
console.print(" 3. Enter the code to authenticate with GitHub/Microsoft")
|
|
874
874
|
console.print("")
|
|
875
|
-
console.print("[dim]Press Ctrl+C to stop the tunnel[/dim]")
|
|
876
|
-
console.print("")
|
|
877
875
|
|
|
878
|
-
# Start the tunnel interactively
|
|
879
|
-
# The code CLI binary is installed at /usr/local/bin/code in the container
|
|
880
876
|
cmd = [
|
|
881
877
|
'docker', 'exec', '-it',
|
|
882
878
|
container_name,
|
|
883
|
-
'/usr/local/bin/code', 'tunnel',
|
|
884
|
-
'--
|
|
885
|
-
'--name', tunnel_name
|
|
879
|
+
'/usr/local/bin/code', 'tunnel', 'user', 'login',
|
|
880
|
+
'--provider', 'github'
|
|
886
881
|
]
|
|
887
882
|
|
|
888
883
|
if debug:
|
|
889
884
|
console.print(f"[dim]Running: {' '.join(cmd)}[/dim]")
|
|
890
885
|
|
|
891
|
-
|
|
892
|
-
|
|
886
|
+
result = subprocess.run(cmd, check=False)
|
|
887
|
+
|
|
888
|
+
if result.returncode == 0:
|
|
889
|
+
console.print("")
|
|
890
|
+
console.print("[bold green]Authentication successful![/bold green]")
|
|
891
|
+
console.print(" Auth is shared across all containers on this machine.")
|
|
892
|
+
console.print("")
|
|
893
|
+
console.print(f" Start a tunnel: devs tunnel {dev_name}")
|
|
894
|
+
console.print(f" Then connect: code --remote tunnel+{tunnel_name} {container_workspace_dir}")
|
|
895
|
+
else:
|
|
896
|
+
console.print("")
|
|
897
|
+
console.print("[yellow]Authentication was cancelled or failed.[/yellow]")
|
|
898
|
+
|
|
899
|
+
except (DockerError, subprocess.SubprocessError) as e:
|
|
900
|
+
raise ContainerError(f"Failed to authenticate tunnel in {dev_name}: {e}")
|
|
901
|
+
|
|
902
|
+
def start_tunnel(self, dev_name: str, workspace_dir: Path, debug: bool = False, live: bool = False, extra_env: Optional[Dict[str, str]] = None) -> None:
|
|
903
|
+
"""Start a VS Code tunnel in the background.
|
|
904
|
+
|
|
905
|
+
Requires prior authentication via tunnel_auth(). The tunnel runs
|
|
906
|
+
detached inside the container.
|
|
907
|
+
|
|
908
|
+
Args:
|
|
909
|
+
dev_name: Development environment name
|
|
910
|
+
workspace_dir: Workspace directory path
|
|
911
|
+
debug: Show debug output for devcontainer operations
|
|
912
|
+
live: Whether the container is in live mode
|
|
913
|
+
extra_env: Additional environment variables to pass to container
|
|
914
|
+
|
|
915
|
+
Raises:
|
|
916
|
+
ContainerError: If tunnel startup fails
|
|
917
|
+
"""
|
|
918
|
+
try:
|
|
919
|
+
container_name, container_workspace_dir = self._prepare_container_exec(
|
|
920
|
+
dev_name, workspace_dir, debug=debug, live=live, extra_env=extra_env
|
|
921
|
+
)
|
|
922
|
+
|
|
923
|
+
tunnel_name = self._get_tunnel_name(dev_name)
|
|
924
|
+
log_file = '/tmp/vscode-tunnel.log'
|
|
925
|
+
tunnel_cmd = f'/usr/local/bin/code tunnel --accept-server-license-terms --name {tunnel_name}'
|
|
926
|
+
vscode_cmd = f"code --remote tunnel+{tunnel_name} {container_workspace_dir}"
|
|
927
|
+
|
|
928
|
+
console.print(f"[bold cyan]Starting VS Code tunnel for: {dev_name}[/bold cyan]")
|
|
929
|
+
console.print(f" Container: {container_name}")
|
|
930
|
+
console.print(f" Tunnel name: {tunnel_name}")
|
|
931
|
+
console.print("")
|
|
932
|
+
|
|
933
|
+
# Start detached
|
|
934
|
+
cmd = [
|
|
935
|
+
'docker', 'exec', '-d',
|
|
936
|
+
container_name,
|
|
937
|
+
'/bin/sh', '-c',
|
|
938
|
+
f'{tunnel_cmd} > {log_file} 2>&1'
|
|
939
|
+
]
|
|
940
|
+
|
|
941
|
+
if debug:
|
|
942
|
+
console.print(f"[dim]Running: {' '.join(cmd)}[/dim]")
|
|
943
|
+
|
|
944
|
+
subprocess.run(cmd, check=True)
|
|
945
|
+
|
|
946
|
+
# Poll the log to confirm startup and show the URL
|
|
947
|
+
console.print("[dim]Waiting for tunnel to start...[/dim]")
|
|
948
|
+
for i in range(15):
|
|
949
|
+
time.sleep(1)
|
|
950
|
+
result = subprocess.run(
|
|
951
|
+
['docker', 'exec', container_name, 'cat', log_file],
|
|
952
|
+
capture_output=True, text=True
|
|
953
|
+
)
|
|
954
|
+
output = result.stdout
|
|
955
|
+
|
|
956
|
+
if 'Open this link' in output or 'vscode.dev/tunnel' in output:
|
|
957
|
+
console.print(f"[bold green]Tunnel running![/bold green]")
|
|
958
|
+
console.print("")
|
|
959
|
+
console.print("[bold]Connect from your local machine:[/bold]")
|
|
960
|
+
console.print(f" {vscode_cmd}")
|
|
961
|
+
console.print("")
|
|
962
|
+
for line in output.split('\n'):
|
|
963
|
+
if 'vscode.dev/tunnel' in line:
|
|
964
|
+
console.print(f"[dim]Or open in browser: {line.strip()}[/dim]")
|
|
965
|
+
console.print(f"[dim]Use 'devs tunnel {dev_name} --status' to check status[/dim]")
|
|
966
|
+
console.print(f"[dim]Use 'devs tunnel {dev_name} --kill' to stop[/dim]")
|
|
967
|
+
return
|
|
968
|
+
|
|
969
|
+
if 'log in' in output.lower() or 'device' in output.lower() or 'invalid' in output.lower():
|
|
970
|
+
console.print("[yellow]Authentication required.[/yellow]")
|
|
971
|
+
console.print(f" Run: devs tunnel {dev_name} --auth")
|
|
972
|
+
subprocess.run(
|
|
973
|
+
['docker', 'exec', container_name, '/usr/local/bin/code', 'tunnel', 'kill'],
|
|
974
|
+
capture_output=True
|
|
975
|
+
)
|
|
976
|
+
return
|
|
977
|
+
|
|
978
|
+
if debug and output.strip():
|
|
979
|
+
console.print(f"[dim]Log ({i+1}s): {output.strip()[-100:]}[/dim]")
|
|
980
|
+
|
|
981
|
+
# Timeout - show what we have
|
|
982
|
+
result = subprocess.run(
|
|
983
|
+
['docker', 'exec', container_name, 'cat', log_file],
|
|
984
|
+
capture_output=True, text=True
|
|
985
|
+
)
|
|
986
|
+
if result.stdout.strip():
|
|
987
|
+
console.print("[yellow]Tunnel may still be starting. Log output:[/yellow]")
|
|
988
|
+
console.print(result.stdout.strip())
|
|
989
|
+
else:
|
|
990
|
+
console.print("[yellow]Tunnel started but no output yet. Check status with:[/yellow]")
|
|
991
|
+
console.print(f" devs tunnel {dev_name} --status")
|
|
893
992
|
|
|
894
993
|
except (DockerError, subprocess.SubprocessError) as e:
|
|
895
994
|
raise ContainerError(f"Failed to start tunnel in {dev_name}: {e}")
|
|
@@ -87,9 +87,9 @@ RUN SNIPPET="export PROMPT_COMMAND='history -a' && export HISTFILE=/commandhisto
|
|
|
87
87
|
ENV DEVCONTAINER=true
|
|
88
88
|
|
|
89
89
|
# Create workspace and config directories and set permissions
|
|
90
|
-
RUN mkdir -p /workspaces /home/node/claudeconfig /home/node/codexconfig /home/node/.vscode-server/extensions && \
|
|
90
|
+
RUN mkdir -p /workspaces /home/node/claudeconfig /home/node/codexconfig /home/node/.vscode-server/extensions /home/node/.vscode/cli && \
|
|
91
91
|
echo '[]' > /home/node/.vscode-server/extensions/extensions.json && \
|
|
92
|
-
chown -R node:node /workspaces /home/node /home/node/.vscode-server && \
|
|
92
|
+
chown -R node:node /workspaces /home/node /home/node/.vscode-server /home/node/.vscode && \
|
|
93
93
|
chmod 755 /workspaces
|
|
94
94
|
|
|
95
95
|
WORKDIR /workspaces
|
|
@@ -83,7 +83,8 @@
|
|
|
83
83
|
// "source=${localEnv:WORKSPACE_FOLDER_NAME}-vscode-extensions,target=/home/node/.vscode-server/extensions,type=volume",
|
|
84
84
|
// .vscode-server includes extension state which we don't want to keep
|
|
85
85
|
// "source=${localEnv:WORKSPACE_FOLDER_NAME}-vscode-data,target=/home/node/.vscode-server,type=volume",
|
|
86
|
-
"source=${localEnv:WORKSPACE_FOLDER_NAME}-vscode-downloads,target=/home/node/.vscode-extensions-cache-volume,type=volume"
|
|
86
|
+
"source=${localEnv:WORKSPACE_FOLDER_NAME}-vscode-downloads,target=/home/node/.vscode-extensions-cache-volume,type=volume",
|
|
87
|
+
"source=devs-vscode-cli-tunnel,target=/home/node/.vscode/cli,type=volume"
|
|
87
88
|
],
|
|
88
89
|
"remoteEnv": {
|
|
89
90
|
"NODE_OPTIONS": "--max-old-space-size=4096",
|
|
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-2.0.3 → devs_common-2.0.4}/devs_common/templates/scripts/post-create-wrapper.sh
RENAMED
|
File without changes
|
{devs_common-2.0.3 → devs_common-2.0.4}/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-2.0.3 → devs_common-2.0.4}/devs_common/templates/sudo-scripts/setup-devs-env.sh
RENAMED
|
File without changes
|
{devs_common-2.0.3 → devs_common-2.0.4}/devs_common/templates/sudo-scripts/setup-vscode-cache.sh
RENAMED
|
File without changes
|
{devs_common-2.0.3 → devs_common-2.0.4}/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
|