Skip to content

feat(cloud): Add CloudWorkspace.from_env() classmethod#909

Merged
Aaron ("AJ") Steers (aaronsteers) merged 2 commits intomainfrom
devin/1765488100-cloudworkspace-from-env
Dec 11, 2025
Merged

feat(cloud): Add CloudWorkspace.from_env() classmethod#909
Aaron ("AJ") Steers (aaronsteers) merged 2 commits intomainfrom
devin/1765488100-cloudworkspace-from-env

Conversation

@aaronsteers
Copy link
Member

@aaronsteers Aaron ("AJ") Steers (aaronsteers) commented Dec 11, 2025

Summary

Adds a CloudWorkspace.from_env() classmethod that creates a workspace instance by resolving credentials from environment variables. This provides an ergonomic alternative to the constructor while keeping the original constructor strict about requiring explicit credentials.

# Before: must always pass credentials explicitly
workspace = CloudWorkspace(
    workspace_id="...",
    client_id="...",
    client_secret="...",
)

# After: can use env vars for convenience
workspace = CloudWorkspace.from_env()
# or with explicit workspace_id
workspace = CloudWorkspace.from_env(workspace_id="...")

This addresses a pattern seen in airbyte-ops-mcp where callers were attempting to construct CloudWorkspace without credentials, expecting env var resolution.

Review & Testing Checklist for Human

  • No unit tests added - verify if tests should be added for the new from_env() method
  • Verify the env var names in the docstring (AIRBYTE_CLOUD_WORKSPACE_ID, AIRBYTE_CLOUD_API_URL) match the actual constants in airbyte/constants.py
  • Test manually: set env vars and call CloudWorkspace.from_env() to verify it works end-to-end

Test Plan

  1. Set AIRBYTE_CLOUD_CLIENT_ID, AIRBYTE_CLOUD_CLIENT_SECRET, and AIRBYTE_CLOUD_WORKSPACE_ID env vars
  2. Call CloudWorkspace.from_env() and verify it returns a valid workspace
  3. Call workspace.connect() to verify credentials work

Notes

Summary by CodeRabbit

  • New Features
    • CloudWorkspace can now be initialized directly from environment variables, automatically resolving workspace ID, client credentials, and API endpoint configuration.

✏️ Tip: You can customize this high-level summary in your review settings.

Important

Auto-merge enabled.

This PR is set to merge automatically when all requirements are met.

…construction

Co-Authored-By: AJ Steers <aj@airbyte.io>
@devin-ai-integration
Copy link
Contributor

Original prompt from AJ Steers
Received message in Slack channel #ask-devin-ai:

@Devin - The Ops MCP repo is trying to construct a CloudWorkspace() object without passing client_id and client_secret. Advise if we should pass the keys explicitly, or update the PyAirbyte implementation to get those (when not passed) via env vars, or both.
Thread URL: https://airbytehq-team.slack.com/archives/C08BHPUMEPJ/p1765487798643479

@devin-ai-integration
Copy link
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

@github-actions
Copy link

👋 Greetings, Airbyte Team Member!

Here are some helpful tips and reminders for your convenience.

Testing This PyAirbyte Version

You can test this version of PyAirbyte using the following:

# Run PyAirbyte CLI from this branch:
uvx --from 'git+https://github.com/airbytehq/PyAirbyte.git@devin/1765488100-cloudworkspace-from-env' pyairbyte --help

# Install PyAirbyte from this branch for development:
pip install 'git+https://github.com/airbytehq/PyAirbyte.git@devin/1765488100-cloudworkspace-from-env'

Helpful Resources

PR Slash Commands

Airbyte Maintainers can execute the following slash commands on your PR:

  • /fix-pr - Fixes most formatting and linting issues
  • /poetry-lock - Updates poetry.lock file
  • /test-pr - Runs tests with the updated PyAirbyte

Community Support

Questions? Join the #pyairbyte channel in our Slack workspace.

📝 Edit this welcome message.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 11, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

Adds a from_env classmethod to CloudWorkspace that constructs a workspace by resolving workspace ID, client ID, client secret, and API root from environment via resolver helpers imported from airbyte.cloud.auth.

Changes

Cohort / File(s) Summary
CloudWorkspace factory method
airbyte/cloud/workspaces.py
Added `CloudWorkspace.from_env(cls, workspace_id: str

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

  • Verify resolver function calls and parameter usage match the expected semantics (workspace_id/api_root overrides).
  • Confirm docstring and type hints reflect behavior and possible exceptions from the resolvers.
  • Check for duplicate imports or import ordering issues in the file.
  • Any tests covering environment resolution behavior present or needed, wdyt?

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and specifically describes the main change: adding a CloudWorkspace.from_env() classmethod to enable environment-based initialization.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch devin/1765488100-cloudworkspace-from-env

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
airbyte/cloud/workspaces.py (2)

116-121: Consider documenting all required environment variables in the docstring.

The docstring mentions AIRBYTE_CLOUD_WORKSPACE_ID and AIRBYTE_CLOUD_API_URL, but AIRBYTE_CLOUD_CLIENT_ID and AIRBYTE_CLOUD_CLIENT_SECRET are also required (they're resolved without defaults in lines 141-142). Including all four env vars in the Args section would give users a complete picture of what they need to set, wdyt?

Something like:

         Args:
             workspace_id: The workspace ID. If not provided, will be resolved from
                 the `AIRBYTE_CLOUD_WORKSPACE_ID` environment variable.
+            client_id: The client ID will be resolved from the 
+                `AIRBYTE_CLOUD_CLIENT_ID` environment variable.
+            client_secret: The client secret will be resolved from the
+                `AIRBYTE_CLOUD_CLIENT_SECRET` environment variable.
             api_root: The API root URL. If not provided, will be resolved from
                 the `AIRBYTE_CLOUD_API_URL` environment variable, or default to
                 the Airbyte Cloud API.

103-144: Consider adding unit tests for the new classmethod.

The PR checklist notes that no unit tests were added. Since this is a new public API method, adding tests would help ensure the environment variable resolution works correctly and document the expected behavior. This could include tests for:

  • Successful creation when all env vars are set
  • Error handling when required env vars are missing
  • Precedence when both env vars and explicit args are provided

Would you like me to help draft some unit tests for this method?

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 441105e and e811db8.

📒 Files selected for processing (1)
  • airbyte/cloud/workspaces.py (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
airbyte/cloud/workspaces.py (2)
airbyte/cloud/auth.py (4)
  • resolve_cloud_api_url (25-33)
  • resolve_cloud_client_id (17-22)
  • resolve_cloud_client_secret (9-14)
  • resolve_cloud_workspace_id (36-41)
tests/integration_tests/cloud/conftest.py (1)
  • workspace_id (49-50)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Pytest (All, Python 3.10, Windows)
  • GitHub Check: Pytest (All, Python 3.10, Ubuntu)
  • GitHub Check: Pytest (All, Python 3.11, Ubuntu)
  • GitHub Check: Pytest (All, Python 3.11, Windows)
  • GitHub Check: Pytest (Fast)
  • GitHub Check: Pytest (No Creds)
🔇 Additional comments (1)
airbyte/cloud/workspaces.py (1)

48-53: LGTM! Clean imports for environment resolution.

The imports are well-organized and all four resolver functions are used in the new from_env classmethod below.

@github-actions
Copy link

github-actions bot commented Dec 11, 2025

PyTest Results (Fast Tests Only, No Creds)

348 tests  ±0   348 ✅ ±0   6m 0s ⏱️ -6s
  1 suites ±0     0 💤 ±0 
  1 files   ±0     0 ❌ ±0 

Results for commit 7e0b1ff. ± Comparison against base commit 441105e.

♻️ This comment has been updated with latest results.

@github-actions
Copy link

github-actions bot commented Dec 11, 2025

PyTest Results (Full)

416 tests  ±0   399 ✅ ±0   25m 1s ⏱️ +33s
  1 suites ±0    17 💤 ±0 
  1 files   ±0     0 ❌ ±0 

Results for commit 7e0b1ff. ± Comparison against base commit 441105e.

♻️ This comment has been updated with latest results.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds a from_env() classmethod to CloudWorkspace that enables creating workspace instances from environment variables, providing a more ergonomic alternative to explicitly passing credentials while maintaining backward compatibility with the strict constructor.

Key Changes:

  • Added from_env() classmethod that resolves credentials from environment variables
  • Imported credential resolution functions from airbyte.cloud.auth
  • Comprehensive docstring with usage examples and error documentation

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
airbyte/cloud/workspaces.py (1)

103-151: Consider adding a small test suite around from_env behavior?

Since this is now a primary ergonomic entrypoint, would it be worth adding a few tests to lock in behavior—for example: (1) happy path when all four env vars are set, (2) failure when required secrets are missing, and (3) precedence when both an explicit workspace_id and AIRBYTE_CLOUD_WORKSPACE_ID are present? That could help prevent regressions as the auth helpers evolve over time, even if you’re manually validating today. Wdyt?

🧹 Nitpick comments (1)
airbyte/cloud/workspaces.py (1)

103-151: Clarify env‑vs‑argument precedence and SecretString handling in from_env?

This factory looks great for ergonomics; a couple of small clarifications might help future readers:

  • Because the resolve_cloud_* functions take an input_value fallback, it seems like env vars will generally win over the explicit workspace_id/api_root arguments when both are present, whereas the current docstring reads as if the arguments are primary and env vars are only used when args are omitted. Would it be worth tweaking the docstring to explicitly state the precedence (e.g., “env vars are used if set; arguments act as fallbacks when the env var is missing”), and to call out that AIRBYTE_CLOUD_WORKSPACE_ID is effectively required unless you pass workspace_id? Wdyt?

  • resolve_cloud_client_id / resolve_cloud_client_secret already return SecretString, and __post_init__ wraps self.client_id / self.client_secret in SecretString again. Depending on how SecretString is implemented, double‑wrapping could be a no‑op or could accidentally wrap a masked string. Would it make sense to either (a) guard the wrapping with an isinstance(..., SecretString) check in __post_init__, or (b) have from_env pass the underlying raw value instead, so we only wrap once? Could you confirm which behavior is safest with the current SecretString implementation?
    Wdyt?

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e811db8 and 7e0b1ff.

📒 Files selected for processing (1)
  • airbyte/cloud/workspaces.py (2 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2024-10-11T22:05:15.550Z
Learnt from: aaronsteers
Repo: airbytehq/PyAirbyte PR: 411
File: airbyte/cli.py:26-26
Timestamp: 2024-10-11T22:05:15.550Z
Learning: In the PyAirbyte project, when reviewing Python code and encountering a TODO comment without an issue link, I should post a friendly reminder to resolve it before merging, instead of suggesting to add an issue link.

Applied to files:

  • airbyte/cloud/workspaces.py
📚 Learning: 2024-10-06T23:44:31.534Z
Learnt from: aaronsteers
Repo: airbytehq/PyAirbyte PR: 411
File: airbyte/cli.py:111-160
Timestamp: 2024-10-06T23:44:31.534Z
Learning: In PyAirbyte, error messages in functions like `_resolve_source_job` in `airbyte/cli.py` are designed to decouple the message text from dynamic values, following a structlog-inspired design. Dynamic values are provided via parameters like `input_value`. This approach helps avoid including PII in the message strings, which may be used in telemetry.

Applied to files:

  • airbyte/cloud/workspaces.py
🧬 Code graph analysis (1)
airbyte/cloud/workspaces.py (2)
airbyte/cloud/auth.py (4)
  • resolve_cloud_api_url (25-33)
  • resolve_cloud_client_id (17-22)
  • resolve_cloud_client_secret (9-14)
  • resolve_cloud_workspace_id (36-41)
tests/integration_tests/cloud/conftest.py (1)
  • workspace_id (49-50)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Pytest (All, Python 3.11, Windows)
  • GitHub Check: Pytest (Fast)
  • GitHub Check: Pytest (All, Python 3.10, Ubuntu)
  • GitHub Check: Pytest (No Creds)
  • GitHub Check: Pytest (All, Python 3.10, Windows)
  • GitHub Check: Pytest (All, Python 3.11, Ubuntu)
🔇 Additional comments (1)
airbyte/cloud/workspaces.py (1)

48-53: Imports cleanly align with the new env‑resolver usage.

Using the dedicated resolve_cloud_* helpers here keeps CloudWorkspace consistent with the rest of the cloud auth surface, and the imported symbols match what from_env actually uses. Looks good to me.

@aaronsteers Aaron ("AJ") Steers (aaronsteers) merged commit f882e93 into main Dec 11, 2025
23 checks passed
@aaronsteers Aaron ("AJ") Steers (aaronsteers) deleted the devin/1765488100-cloudworkspace-from-env branch December 11, 2025 22:22
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.

2 participants