devs-common 2.0.11__tar.gz → 2.0.12__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.
Files changed (42) hide show
  1. {devs_common-2.0.11 → devs_common-2.0.12}/PKG-INFO +1 -1
  2. {devs_common-2.0.11 → devs_common-2.0.12}/devs_common/core/container.py +104 -87
  3. {devs_common-2.0.11 → devs_common-2.0.12}/devs_common/templates/Dockerfile +5 -0
  4. {devs_common-2.0.11 → devs_common-2.0.12}/devs_common.egg-info/PKG-INFO +1 -1
  5. {devs_common-2.0.11 → devs_common-2.0.12}/pyproject.toml +1 -1
  6. {devs_common-2.0.11 → devs_common-2.0.12}/LICENSE +0 -0
  7. {devs_common-2.0.11 → devs_common-2.0.12}/README.md +0 -0
  8. {devs_common-2.0.11 → devs_common-2.0.12}/devs_common/__init__.py +0 -0
  9. {devs_common-2.0.11 → devs_common-2.0.12}/devs_common/config.py +0 -0
  10. {devs_common-2.0.11 → devs_common-2.0.12}/devs_common/core/__init__.py +0 -0
  11. {devs_common-2.0.11 → devs_common-2.0.12}/devs_common/core/project.py +0 -0
  12. {devs_common-2.0.11 → devs_common-2.0.12}/devs_common/core/workspace.py +0 -0
  13. {devs_common-2.0.11 → devs_common-2.0.12}/devs_common/devs_config.py +0 -0
  14. {devs_common-2.0.11 → devs_common-2.0.12}/devs_common/exceptions.py +0 -0
  15. {devs_common-2.0.11 → devs_common-2.0.12}/devs_common/templates/devcontainer.json +0 -0
  16. {devs_common-2.0.11 → devs_common-2.0.12}/devs_common/templates/scripts/copy-ssh.sh +0 -0
  17. {devs_common-2.0.11 → devs_common-2.0.12}/devs_common/templates/scripts/post-create-wrapper.sh +0 -0
  18. {devs_common-2.0.11 → devs_common-2.0.12}/devs_common/templates/scripts/python-venv-integration.sh +0 -0
  19. {devs_common-2.0.11 → devs_common-2.0.12}/devs_common/templates/scripts/screenshot-webapp.sh +0 -0
  20. {devs_common-2.0.11 → devs_common-2.0.12}/devs_common/templates/scripts/setup-workspace.sh +0 -0
  21. {devs_common-2.0.11 → devs_common-2.0.12}/devs_common/templates/scripts/start-tunnel.sh +0 -0
  22. {devs_common-2.0.11 → devs_common-2.0.12}/devs_common/templates/scripts/tunnel-status.sh +0 -0
  23. {devs_common-2.0.11 → devs_common-2.0.12}/devs_common/templates/scripts/webapp-screenshot.py +0 -0
  24. {devs_common-2.0.11 → devs_common-2.0.12}/devs_common/templates/sudo-scripts/init-firewall.sh +0 -0
  25. {devs_common-2.0.11 → devs_common-2.0.12}/devs_common/templates/sudo-scripts/setup-devs-env.sh +0 -0
  26. {devs_common-2.0.11 → devs_common-2.0.12}/devs_common/templates/sudo-scripts/setup-vscode-cache.sh +0 -0
  27. {devs_common-2.0.11 → devs_common-2.0.12}/devs_common/templates/sudo-scripts/start-services.sh +0 -0
  28. {devs_common-2.0.11 → devs_common-2.0.12}/devs_common/templates/vscode-settings-reference.md +0 -0
  29. {devs_common-2.0.11 → devs_common-2.0.12}/devs_common/utils/__init__.py +0 -0
  30. {devs_common-2.0.11 → devs_common-2.0.12}/devs_common/utils/config_hash.py +0 -0
  31. {devs_common-2.0.11 → devs_common-2.0.12}/devs_common/utils/console.py +0 -0
  32. {devs_common-2.0.11 → devs_common-2.0.12}/devs_common/utils/devcontainer.py +0 -0
  33. {devs_common-2.0.11 → devs_common-2.0.12}/devs_common/utils/devcontainer_template.py +0 -0
  34. {devs_common-2.0.11 → devs_common-2.0.12}/devs_common/utils/docker_client.py +0 -0
  35. {devs_common-2.0.11 → devs_common-2.0.12}/devs_common/utils/file_utils.py +0 -0
  36. {devs_common-2.0.11 → devs_common-2.0.12}/devs_common/utils/git_utils.py +0 -0
  37. {devs_common-2.0.11 → devs_common-2.0.12}/devs_common/utils/repo_cache.py +0 -0
  38. {devs_common-2.0.11 → devs_common-2.0.12}/devs_common.egg-info/SOURCES.txt +0 -0
  39. {devs_common-2.0.11 → devs_common-2.0.12}/devs_common.egg-info/dependency_links.txt +0 -0
  40. {devs_common-2.0.11 → devs_common-2.0.12}/devs_common.egg-info/requires.txt +0 -0
  41. {devs_common-2.0.11 → devs_common-2.0.12}/devs_common.egg-info/top_level.txt +0 -0
  42. {devs_common-2.0.11 → devs_common-2.0.12}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: devs-common
3
- Version: 2.0.11
3
+ Version: 2.0.12
4
4
  Summary: Shared utilities for devs package ecosystem
5
5
  Author: Dan Lester
6
6
  License-Expression: MIT
@@ -882,13 +882,99 @@ class ContainerManager:
882
882
 
883
883
  return tunnel_name
884
884
 
885
+ def _run_tunnel_auth(self, container_name: str, debug: bool = False) -> bool:
886
+ """Run interactive VS Code tunnel authentication in a container.
887
+
888
+ Returns:
889
+ True if authentication succeeded
890
+ """
891
+ console.print("[yellow]Follow the prompts to authenticate:[/yellow]")
892
+ console.print(" 1. A URL and code will be displayed")
893
+ console.print(" 2. Open the URL in your browser")
894
+ console.print(" 3. Enter the code to authenticate with GitHub/Microsoft")
895
+ console.print("")
896
+
897
+ cmd = [
898
+ 'docker', 'exec', '-it',
899
+ container_name,
900
+ '/usr/local/bin/code', 'tunnel', 'user', 'login',
901
+ '--provider', 'github'
902
+ ]
903
+
904
+ if debug:
905
+ console.print(f"[dim]Running: {' '.join(cmd)}[/dim]")
906
+
907
+ result = subprocess.run(cmd, check=False)
908
+
909
+ if result.returncode == 0:
910
+ console.print("")
911
+ console.print("[bold green]Authentication successful![/bold green]")
912
+ console.print(" Auth persists across container stop/restart cycles.")
913
+ return True
914
+ else:
915
+ console.print("")
916
+ console.print("[yellow]Authentication was cancelled or failed.[/yellow]")
917
+ return False
918
+
919
+ def _start_and_poll_tunnel(self, container_name: str, dev_name: str, tunnel_cmd: str,
920
+ log_file: str, vscode_cmd: str, web_url: str, debug: bool = False) -> bool:
921
+ """Start a detached tunnel and poll the log for startup confirmation.
922
+
923
+ Returns:
924
+ True if tunnel started (or timed out), False if auth is needed.
925
+ """
926
+ subprocess.run(
927
+ ['docker', 'exec', '-d', container_name,
928
+ '/bin/sh', '-c', f'{tunnel_cmd} > {log_file} 2>&1'],
929
+ check=True
930
+ )
931
+
932
+ console.print("[dim]Waiting for tunnel to start...[/dim]")
933
+ for i in range(15):
934
+ time.sleep(1)
935
+ result = subprocess.run(
936
+ ['docker', 'exec', container_name, 'cat', log_file],
937
+ capture_output=True, text=True
938
+ )
939
+ output = result.stdout
940
+
941
+ if 'Open this link' in output or 'vscode.dev/tunnel' in output:
942
+ console.print(f"[bold green]Tunnel running![/bold green]")
943
+ console.print("")
944
+ console.print("[bold]Connect:[/bold]")
945
+ console.print(f" Desktop: {vscode_cmd}")
946
+ console.print(f" Browser: {web_url}")
947
+ console.print("")
948
+ console.print(f"[dim]Use 'devs tunnel {dev_name} --status' to check status[/dim]")
949
+ console.print(f"[dim]Use 'devs tunnel {dev_name} --kill' to stop[/dim]")
950
+ return True
951
+
952
+ if 'log in' in output.lower() or 'device' in output.lower() or 'invalid' in output.lower():
953
+ subprocess.run(
954
+ ['docker', 'exec', container_name, '/usr/local/bin/code', 'tunnel', 'kill'],
955
+ capture_output=True
956
+ )
957
+ return False
958
+
959
+ if debug and output.strip():
960
+ console.print(f"[dim]Log ({i+1}s): {output.strip()[-100:]}[/dim]")
961
+
962
+ # Timeout
963
+ result = subprocess.run(
964
+ ['docker', 'exec', container_name, 'cat', log_file],
965
+ capture_output=True, text=True
966
+ )
967
+ if result.stdout.strip():
968
+ console.print("[yellow]Tunnel may still be starting. Log output:[/yellow]")
969
+ console.print(result.stdout.strip())
970
+ else:
971
+ console.print("[yellow]Tunnel started but no output yet. Check status with:[/yellow]")
972
+ console.print(f" devs tunnel {dev_name} --status")
973
+ return True
974
+
885
975
  def tunnel_auth(self, dev_name: str, workspace_dir: Path, debug: bool = False, live: bool = False, extra_env: Optional[Dict[str, str]] = None) -> None:
886
976
  """Authenticate VS Code tunnel (interactive, per-container setup).
887
977
 
888
- Runs the device code flow interactively inside the container. Auth is
889
- stored in the container's filesystem and persists across stop/restart
890
- cycles (but not container removal/recreation).
891
-
892
978
  Args:
893
979
  dev_name: Development environment name
894
980
  workspace_dir: Workspace directory path
@@ -909,34 +995,11 @@ class ContainerManager:
909
995
  console.print("🔐 Setting up VS Code tunnel authentication...")
910
996
  console.print(f" Container: {container_name}")
911
997
  console.print("")
912
- console.print("[yellow]Follow the prompts to authenticate:[/yellow]")
913
- console.print(" 1. A URL and code will be displayed")
914
- console.print(" 2. Open the URL in your browser")
915
- console.print(" 3. Enter the code to authenticate with GitHub/Microsoft")
916
- console.print("")
917
998
 
918
- cmd = [
919
- 'docker', 'exec', '-it',
920
- container_name,
921
- '/usr/local/bin/code', 'tunnel', 'user', 'login',
922
- '--provider', 'github'
923
- ]
924
-
925
- if debug:
926
- console.print(f"[dim]Running: {' '.join(cmd)}[/dim]")
927
-
928
- result = subprocess.run(cmd, check=False)
929
-
930
- if result.returncode == 0:
931
- console.print("")
932
- console.print("[bold green]Authentication successful![/bold green]")
933
- console.print(" Auth persists across container stop/restart cycles.")
999
+ if self._run_tunnel_auth(container_name, debug=debug):
934
1000
  console.print("")
935
1001
  console.print(f" Start a tunnel: devs tunnel {dev_name}")
936
1002
  console.print(f" Then connect: code --remote tunnel+{tunnel_name} {container_workspace_dir}")
937
- else:
938
- console.print("")
939
- console.print("[yellow]Authentication was cancelled or failed.[/yellow]")
940
1003
 
941
1004
  except (DockerError, subprocess.SubprocessError) as e:
942
1005
  raise ContainerError(f"Failed to authenticate tunnel in {dev_name}: {e}")
@@ -944,8 +1007,7 @@ class ContainerManager:
944
1007
  def start_tunnel(self, dev_name: str, workspace_dir: Path, debug: bool = False, live: bool = False, extra_env: Optional[Dict[str, str]] = None) -> None:
945
1008
  """Start a VS Code tunnel in the background.
946
1009
 
947
- Requires prior authentication via tunnel_auth(). The tunnel runs
948
- detached inside the container.
1010
+ If authentication is needed, prompts interactively then retries.
949
1011
 
950
1012
  Args:
951
1013
  dev_name: Development environment name
@@ -966,71 +1028,26 @@ class ContainerManager:
966
1028
  log_file = '/tmp/vscode-tunnel.log'
967
1029
  tunnel_cmd = f'/usr/local/bin/code tunnel --accept-server-license-terms --name {tunnel_name}'
968
1030
  vscode_cmd = f"code --remote tunnel+{tunnel_name} {container_workspace_dir}"
1031
+ web_url = f"https://vscode.dev/tunnel/{tunnel_name}{container_workspace_dir}"
969
1032
 
970
1033
  console.print(f"[bold cyan]Starting VS Code tunnel for: {dev_name}[/bold cyan]")
971
1034
  console.print(f" Container: {container_name}")
972
1035
  console.print(f" Tunnel name: {tunnel_name}")
973
1036
  console.print("")
974
1037
 
975
- # Start detached
976
- cmd = [
977
- 'docker', 'exec', '-d',
978
- container_name,
979
- '/bin/sh', '-c',
980
- f'{tunnel_cmd} > {log_file} 2>&1'
981
- ]
982
-
983
- if debug:
984
- console.print(f"[dim]Running: {' '.join(cmd)}[/dim]")
985
-
986
- subprocess.run(cmd, check=True)
987
-
988
- # Poll the log to confirm startup and show the URL
989
- console.print("[dim]Waiting for tunnel to start...[/dim]")
990
- for i in range(15):
991
- time.sleep(1)
992
- result = subprocess.run(
993
- ['docker', 'exec', container_name, 'cat', log_file],
994
- capture_output=True, text=True
995
- )
996
- output = result.stdout
997
-
998
- if 'Open this link' in output or 'vscode.dev/tunnel' in output:
999
- console.print(f"[bold green]Tunnel running![/bold green]")
1000
- console.print("")
1001
- console.print("[bold]Connect from your local machine:[/bold]")
1002
- console.print(f" {vscode_cmd}")
1003
- console.print("")
1004
- for line in output.split('\n'):
1005
- if 'vscode.dev/tunnel' in line:
1006
- console.print(f"[dim]Or open in browser: {line.strip()}[/dim]")
1007
- console.print(f"[dim]Use 'devs tunnel {dev_name} --status' to check status[/dim]")
1008
- console.print(f"[dim]Use 'devs tunnel {dev_name} --kill' to stop[/dim]")
1009
- return
1010
-
1011
- if 'log in' in output.lower() or 'device' in output.lower() or 'invalid' in output.lower():
1012
- console.print("[yellow]Authentication required.[/yellow]")
1013
- console.print(f" Run: devs tunnel {dev_name} --auth")
1014
- subprocess.run(
1015
- ['docker', 'exec', container_name, '/usr/local/bin/code', 'tunnel', 'kill'],
1016
- capture_output=True
1017
- )
1038
+ if not self._start_and_poll_tunnel(container_name, dev_name, tunnel_cmd,
1039
+ log_file, vscode_cmd, web_url, debug=debug):
1040
+ # Auth needed
1041
+ console.print("[yellow]Authentication required. Starting login...[/yellow]")
1042
+ console.print("")
1043
+ if not self._run_tunnel_auth(container_name, debug=debug):
1018
1044
  return
1019
1045
 
1020
- if debug and output.strip():
1021
- console.print(f"[dim]Log ({i+1}s): {output.strip()[-100:]}[/dim]")
1022
-
1023
- # Timeout - show what we have
1024
- result = subprocess.run(
1025
- ['docker', 'exec', container_name, 'cat', log_file],
1026
- capture_output=True, text=True
1027
- )
1028
- if result.stdout.strip():
1029
- console.print("[yellow]Tunnel may still be starting. Log output:[/yellow]")
1030
- console.print(result.stdout.strip())
1031
- else:
1032
- console.print("[yellow]Tunnel started but no output yet. Check status with:[/yellow]")
1033
- console.print(f" devs tunnel {dev_name} --status")
1046
+ console.print("")
1047
+ console.print("[dim]Restarting tunnel...[/dim]")
1048
+ console.print("")
1049
+ self._start_and_poll_tunnel(container_name, dev_name, tunnel_cmd,
1050
+ log_file, vscode_cmd, web_url, debug=debug)
1034
1051
 
1035
1052
  except (DockerError, subprocess.SubprocessError) as e:
1036
1053
  raise ContainerError(f"Failed to start tunnel in {dev_name}: {e}")
@@ -125,9 +125,14 @@ RUN curl -fsSL https://claude.ai/install.sh | bash
125
125
  # Install OpenAI Codex CLI
126
126
  RUN npm install -g @openai/codex
127
127
 
128
+ # Install cloudflared for tunnel support (proxies all HTTP methods correctly)
128
129
  # Install VS Code CLI for tunnel support (needs root for /usr/local/bin)
129
130
  # Using the standalone CLI which supports the 'tunnel' command
130
131
  USER root
132
+ RUN ARCH=$(dpkg --print-architecture) && \
133
+ curl -fSL "https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-${ARCH}" -o /usr/local/bin/cloudflared && \
134
+ chmod +x /usr/local/bin/cloudflared
135
+
131
136
  RUN ARCH=$(dpkg --print-architecture) && \
132
137
  if [ "$ARCH" = "amd64" ]; then \
133
138
  curl -fSL 'https://update.code.visualstudio.com/latest/cli-linux-x64/stable' -o /tmp/vscode-cli.tar.gz; \
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: devs-common
3
- Version: 2.0.11
3
+ Version: 2.0.12
4
4
  Summary: Shared utilities for devs package ecosystem
5
5
  Author: Dan Lester
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "devs-common"
7
- version = "2.0.11"
7
+ version = "2.0.12"
8
8
  description = "Shared utilities for devs package ecosystem"
9
9
  authors = [
10
10
  {name = "Dan Lester"},
File without changes
File without changes
File without changes