devs-cli 3.2.4__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_cli-3.2.4/devs_cli.egg-info → devs_cli-3.3.1}/PKG-INFO +1 -1
- {devs_cli-3.2.4 → devs_cli-3.3.1}/devs/cli.py +27 -19
- {devs_cli-3.2.4 → devs_cli-3.3.1/devs_cli.egg-info}/PKG-INFO +1 -1
- {devs_cli-3.2.4 → devs_cli-3.3.1}/pyproject.toml +1 -1
- {devs_cli-3.2.4 → devs_cli-3.3.1}/tests/test_container_manager.py +6 -5
- {devs_cli-3.2.4 → devs_cli-3.3.1}/LICENSE +0 -0
- {devs_cli-3.2.4 → devs_cli-3.3.1}/README.md +0 -0
- {devs_cli-3.2.4 → devs_cli-3.3.1}/devs/__init__.py +0 -0
- {devs_cli-3.2.4 → devs_cli-3.3.1}/devs/config.py +0 -0
- {devs_cli-3.2.4 → devs_cli-3.3.1}/devs/core/__init__.py +0 -0
- {devs_cli-3.2.4 → devs_cli-3.3.1}/devs/core/integration.py +0 -0
- {devs_cli-3.2.4 → devs_cli-3.3.1}/devs/exceptions.py +0 -0
- {devs_cli-3.2.4 → devs_cli-3.3.1}/devs/utils/__init__.py +0 -0
- {devs_cli-3.2.4 → devs_cli-3.3.1}/devs_cli.egg-info/SOURCES.txt +0 -0
- {devs_cli-3.2.4 → devs_cli-3.3.1}/devs_cli.egg-info/dependency_links.txt +0 -0
- {devs_cli-3.2.4 → devs_cli-3.3.1}/devs_cli.egg-info/entry_points.txt +0 -0
- {devs_cli-3.2.4 → devs_cli-3.3.1}/devs_cli.egg-info/requires.txt +0 -0
- {devs_cli-3.2.4 → devs_cli-3.3.1}/devs_cli.egg-info/top_level.txt +0 -0
- {devs_cli-3.2.4 → devs_cli-3.3.1}/setup.cfg +0 -0
- {devs_cli-3.2.4 → devs_cli-3.3.1}/tests/test_cli.py +0 -0
- {devs_cli-3.2.4 → devs_cli-3.3.1}/tests/test_cli_clean.py +0 -0
- {devs_cli-3.2.4 → devs_cli-3.3.1}/tests/test_cli_misc.py +0 -0
- {devs_cli-3.2.4 → devs_cli-3.3.1}/tests/test_cli_start.py +0 -0
- {devs_cli-3.2.4 → devs_cli-3.3.1}/tests/test_cli_stop.py +0 -0
- {devs_cli-3.2.4 → devs_cli-3.3.1}/tests/test_cli_vscode.py +0 -0
- {devs_cli-3.2.4 → devs_cli-3.3.1}/tests/test_e2e.py +0 -0
- {devs_cli-3.2.4 → devs_cli-3.3.1}/tests/test_error_parsing.py +0 -0
- {devs_cli-3.2.4 → devs_cli-3.3.1}/tests/test_integration.py +0 -0
- {devs_cli-3.2.4 → devs_cli-3.3.1}/tests/test_live_mode.py +0 -0
- {devs_cli-3.2.4 → devs_cli-3.3.1}/tests/test_project.py +0 -0
- {devs_cli-3.2.4 → devs_cli-3.3.1}/tests/test_repo_cache.py +0 -0
- {devs_cli-3.2.4 → devs_cli-3.3.1}/tests/test_workspace_manager.py +0 -0
|
@@ -168,46 +168,52 @@ def cli(ctx, debug: bool, repo: str) -> None:
|
|
|
168
168
|
@cli.command()
|
|
169
169
|
@click.argument('dev_names', nargs=-1, required=True)
|
|
170
170
|
@click.option('--rebuild', is_flag=True, help='Force rebuild of container images')
|
|
171
|
+
@click.option('--rebuild-if-changed', 'rebuild_if_changed', is_flag=True, help='Rebuild if devcontainer files have changed since last build')
|
|
171
172
|
@click.option('--live', is_flag=True, help='Mount current directory as workspace instead of copying')
|
|
172
173
|
@click.option('--env', multiple=True, help='Environment variables to pass to container (format: VAR=value)')
|
|
173
174
|
@debug_option
|
|
174
|
-
def start(dev_names: tuple, rebuild: bool, live: bool, env: tuple, debug: bool) -> None:
|
|
175
|
+
def start(dev_names: tuple, rebuild: bool, rebuild_if_changed: bool, live: bool, env: tuple, debug: bool) -> None:
|
|
175
176
|
"""Start named devcontainers.
|
|
176
|
-
|
|
177
|
+
|
|
177
178
|
DEV_NAMES: One or more development environment names to start
|
|
178
|
-
|
|
179
|
+
|
|
179
180
|
Example: devs start sally bob
|
|
180
181
|
Example: devs start sally --live # Mount current directory directly
|
|
182
|
+
Example: devs start sally --rebuild-if-changed # Rebuild only if devcontainer files changed
|
|
181
183
|
Example: devs start sally --env QUART_PORT=5001 --env DB_HOST=localhost:3307
|
|
182
184
|
"""
|
|
183
185
|
check_dependencies()
|
|
184
186
|
project = get_project()
|
|
185
|
-
|
|
187
|
+
|
|
186
188
|
console.print(f"🚀 Starting devcontainers for project: {project.info.name}")
|
|
187
|
-
|
|
189
|
+
|
|
188
190
|
container_manager = ContainerManager(project, config)
|
|
189
191
|
workspace_manager = WorkspaceManager(project, config)
|
|
190
|
-
|
|
192
|
+
|
|
191
193
|
for dev_name in dev_names:
|
|
192
194
|
console.print(f" Starting: {dev_name}")
|
|
193
|
-
|
|
195
|
+
|
|
194
196
|
# Load environment variables from DEVS.yml and merge with CLI --env flags
|
|
195
197
|
devs_env = DevsConfigLoader.load_env_vars(dev_name, project.info.name)
|
|
196
198
|
cli_env = parse_env_vars(env) if env else {}
|
|
197
199
|
extra_env = merge_env_vars(devs_env, cli_env) if devs_env or cli_env else None
|
|
198
|
-
|
|
200
|
+
|
|
199
201
|
if extra_env:
|
|
200
202
|
console.print(f"🔧 Environment variables: {', '.join(f'{k}={v}' for k, v in extra_env.items())}")
|
|
201
|
-
|
|
203
|
+
|
|
202
204
|
try:
|
|
203
205
|
# Create/ensure workspace exists (handles live mode internally)
|
|
204
206
|
workspace_dir = workspace_manager.create_workspace(dev_name, live=live)
|
|
205
|
-
|
|
206
|
-
# Ensure container is running
|
|
207
|
+
|
|
208
|
+
# Ensure container is running.
|
|
209
|
+
# --rebuild: always force a full rebuild
|
|
210
|
+
# --rebuild-if-changed: rebuild only when devcontainer file content has changed
|
|
211
|
+
# (default): never auto-rebuild
|
|
207
212
|
if container_manager.ensure_container_running(
|
|
208
|
-
dev_name,
|
|
209
|
-
workspace_dir,
|
|
213
|
+
dev_name,
|
|
214
|
+
workspace_dir,
|
|
210
215
|
force_rebuild=rebuild,
|
|
216
|
+
check_rebuild=rebuild_if_changed,
|
|
211
217
|
debug=debug,
|
|
212
218
|
live=live,
|
|
213
219
|
extra_env=extra_env
|
|
@@ -215,7 +221,7 @@ def start(dev_names: tuple, rebuild: bool, live: bool, env: tuple, debug: bool)
|
|
|
215
221
|
continue
|
|
216
222
|
else:
|
|
217
223
|
console.print(f" ⚠️ Failed to start {dev_name}, continuing with others...")
|
|
218
|
-
|
|
224
|
+
|
|
219
225
|
except (ContainerError, WorkspaceError) as e:
|
|
220
226
|
console.print(f" ❌ Error starting {dev_name}: {e}")
|
|
221
227
|
continue
|
|
@@ -268,8 +274,8 @@ def vscode(dev_names: tuple, delay: float, live: bool, env: tuple, debug: bool)
|
|
|
268
274
|
# Ensure workspace exists (handles live mode internally)
|
|
269
275
|
workspace_dir = workspace_manager.create_workspace(dev_name, live=live)
|
|
270
276
|
|
|
271
|
-
# Ensure container is running before launching VS Code
|
|
272
|
-
if container_manager.ensure_container_running(dev_name, workspace_dir, debug=debug, live=live, extra_env=extra_env):
|
|
277
|
+
# Ensure container is running before launching VS Code (no auto-rebuild in CLI)
|
|
278
|
+
if container_manager.ensure_container_running(dev_name, workspace_dir, check_rebuild=False, debug=debug, live=live, extra_env=extra_env):
|
|
273
279
|
workspace_dirs.append(workspace_dir)
|
|
274
280
|
valid_dev_names.append(dev_name)
|
|
275
281
|
else:
|
|
@@ -776,11 +782,13 @@ def tunnel(dev_name: str, auth: bool, status: bool, kill_tunnel: bool, live: boo
|
|
|
776
782
|
extra_env=extra_env
|
|
777
783
|
)
|
|
778
784
|
if is_running:
|
|
779
|
-
console.print(f"Tunnel
|
|
785
|
+
console.print(f"[bold]Tunnel for {dev_name}:[/bold]")
|
|
780
786
|
console.print(status_msg)
|
|
781
787
|
else:
|
|
782
|
-
console.print(f"
|
|
783
|
-
console.print(f"
|
|
788
|
+
console.print(f"[bold]Tunnel for {dev_name}:[/bold]")
|
|
789
|
+
console.print(f"[dim] Not running[/dim]")
|
|
790
|
+
if status_msg:
|
|
791
|
+
console.print(f" {status_msg}")
|
|
784
792
|
|
|
785
793
|
elif kill_tunnel:
|
|
786
794
|
# Kill the tunnel
|
|
@@ -246,7 +246,7 @@ class TestContainerManager:
|
|
|
246
246
|
assert result is False
|
|
247
247
|
|
|
248
248
|
def test_should_rebuild_image_no_existing(self, mock_project):
|
|
249
|
-
"""Test should_rebuild_image when no
|
|
249
|
+
"""Test should_rebuild_image returns False when no existing container."""
|
|
250
250
|
with patch('devs_common.utils.docker_client.docker') as mock_docker:
|
|
251
251
|
mock_docker_instance = MagicMock()
|
|
252
252
|
mock_docker.from_env.return_value = mock_docker_instance
|
|
@@ -257,13 +257,14 @@ class TestContainerManager:
|
|
|
257
257
|
|
|
258
258
|
manager = ContainerManager(mock_project)
|
|
259
259
|
|
|
260
|
-
# Mock no existing
|
|
261
|
-
manager.docker.
|
|
260
|
+
# Mock no existing containers
|
|
261
|
+
manager.docker.find_containers_by_labels = MagicMock(return_value=[])
|
|
262
262
|
|
|
263
|
-
|
|
263
|
+
project_labels = {'devs.project': 'test-org-test-repo', 'devs.dev': 'alice'}
|
|
264
|
+
should_rebuild, reason = manager.should_rebuild_image("alice", project_labels)
|
|
264
265
|
|
|
265
266
|
assert should_rebuild is False
|
|
266
|
-
assert "No existing
|
|
267
|
+
assert "No existing container" in reason
|
|
267
268
|
|
|
268
269
|
def test_find_aborted_containers(self, mock_project):
|
|
269
270
|
"""Test finding aborted containers."""
|
|
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
|
|
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
|