Skip to content

feat: add --offline flag to skip network access during up#316

Open
hubertdeng123 wants to merge 5 commits intomainfrom
hubert/offline-mode
Open

feat: add --offline flag to skip network access during up#316
hubertdeng123 wants to merge 5 commits intomainfrom
hubert/offline-mode

Conversation

@hubertdeng123
Copy link
Copy Markdown
Member

Summary

  • Adds a global --offline flag that allows devservices up to work without network access by using cached dependencies and skipping docker image pulls
  • When --offline is used and dependencies are not cached locally, a clear error message is shown asking the user to run without --offline first
  • Useful for working on airplanes, trains, or other places with spotty/no internet

Closes #315

Test plan

  • Added unit tests for offline mode with cached dependencies (verifies no network calls made)
  • Added unit test for offline mode without cached dependencies (verifies clear error)
  • Existing install_and_verify_dependencies tests still pass
  • ruff and mypy pass clean

🤖 Generated with Claude Code

Allows using cached dependencies and local docker images when working
without internet. Useful for airplane/train work or spotty connections.

Closes #315

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Comment on lines +239 to +248
if offline:
are_dependencies_valid = verify_local_dependencies(matching_dependencies)
if not are_dependencies_valid:
raise DependencyError(
repo_name="",
repo_link="",
branch="",
stderr="Cannot use --offline: some dependencies are not cached locally. Run 'devservices up' without --offline first to cache dependencies",
)
return get_installed_remote_dependencies(matching_dependencies)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: The DependencyError raised in offline mode doesn't display the helpful error message from stderr because its __str__ method omits it, showing a malformed message instead.
Severity: MEDIUM

Suggested Fix

Update the __str__ method for the DependencyError class in exceptions.py to include the self.stderr attribute in the output string. This will ensure the helpful error message is displayed when the exception is printed.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location: devservices/utils/dependencies.py#L239-L248

Potential issue: When running with the `--offline` flag where dependencies are not
cached, a `DependencyError` is raised. The helpful error message is stored in the
`stderr` attribute of this exception. However, the `DependencyError.__str__` method does
not include the `stderr` attribute in its output. As a result, when the exception is
caught and displayed to the user, they see a confusing, malformed error message like
`DependencyError: () on .` instead of the intended actionable message.

Did we get this right? 👍 / 👎 to inform future reviews.

Comment thread devservices/utils/dependencies.py
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Comment thread devservices/utils/dependencies.py
- Use de.stderr for offline DependencyError instead of garbled __str__
- Remove "try purge" suggestion for offline errors (not applicable)
- Change skip_pull message to "using cached images" (no abstraction leak)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Comment thread devservices/utils/dependencies.py
branch="",
stderr="Cannot use --offline: some dependencies are not cached locally. Run 'devservices up' without --offline first to cache dependencies",
)
return get_installed_remote_dependencies(matching_dependencies)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Offline validation misses nested dependencies, yields confusing error

Medium Severity

In offline mode, verify_local_dependencies only validates direct dependencies, but then get_installed_remote_dependencies recursively traverses nested (transitive) dependencies. If a nested dependency isn't cached, it raises DependencyNotInstalledError (with stderr=None). In the error handler, since offline is true, no helpful guidance is appended — the user just sees a generic "Dependency not installed correctly" message without being told to run without --offline first.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 9f976b4. Configure here.

hubertdeng123 and others added 2 commits April 30, 2026 20:29
Mocks DEVSERVICES_SUPERVISOR_CONFIG_DIR to tmp_path to prevent xdist
race conditions when multiple workers write supervisor configs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

There are 3 total unresolved issues (including 1 from previous review).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 48f958a. Configure here.

for future in concurrent.futures.as_completed(futures):
_ = future.result()
else:
status.info("Skipping image pull (using cached images)")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Offline mode doesn't prevent docker compose up from pulling

Medium Severity

When skip_pull is True (offline mode), the explicit docker compose pull step is skipped, but the subsequent docker compose up -d command is still issued without --pull never. By default, docker compose up uses --pull policy (which resolves to pulling missing images). In a truly offline environment, if any image isn't locally cached (e.g., after a docker image prune), docker compose up will still attempt a network pull — causing hangs or timeouts rather than a clear immediate failure. The options list needs to include --pull never when skip_pull is True.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 48f958a. Configure here.

msg = de.stderr if de.stderr else str(de)
if not offline:
msg = f"{msg}. If this error persists, try running `devservices purge`"
status.failure(msg)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error message displays raw bytes in non-offline path

Medium Severity

The changed error handling now uses de.stderr if de.stderr else str(de) for the failure message. However, DependencyError.stderr is populated from CalledProcessError.stderr (via stderr=e.stderr in _update_dependency, _checkout_dependency, etc.), and _run_command calls subprocess.run without text=True, meaning e.stderr is bytes, not str. In the non-offline path, when a git operation fails, msg becomes a bytes object, and the subsequent f-string renders it as b'fatal: ...' — producing a confusing, ugly user-facing error message. Previously, str(de) was always used, which gave a clean formatted message.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 48f958a. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Offline mode/flag

1 participant