Skip to content

feat: Add Google Drive integration#117

Closed
BrianLeishman wants to merge 40 commits into
masterfrom
feat/google-drive-integration
Closed

feat: Add Google Drive integration#117
BrianLeishman wants to merge 40 commits into
masterfrom
feat/google-drive-integration

Conversation

@BrianLeishman

Copy link
Copy Markdown
Member

Summary

  • Add Google Drive as a browsable location provider in Marlin
  • Support OAuth 2.0 authentication with multi-account capability
  • Enable browsing My Drive, Shared with me, Starred, and Recent folders
  • Allow pasting Google Drive web URLs to navigate directly to folders
  • Add integration tests using stored OAuth tokens

Changes

New Files - Google Drive Backend (src-tauri/src/locations/gdrive/)

  • auth.rs - OAuth 2.0 authentication flow, token storage/refresh, multi-account management
  • provider.rs - LocationProvider implementation for gdrive:// scheme, virtual folder structure, API calls
  • url_parser.rs - Parse Google Drive web URLs to extract file IDs
  • tests.rs - Integration tests using authenticated accounts
  • mod.rs - Module exports

Modified Backend Files

  • src-tauri/Cargo.toml - Add google-drive3, yup-oauth2, hyper dependencies
  • src-tauri/build.rs - Load Google OAuth client credentials from .env
  • src-tauri/src/commands.rs - Add gdrive commands (add/remove account, list accounts, resolve URLs), fix path normalization for URI schemes
  • src-tauri/src/lib.rs - Register new gdrive commands with Tauri
  • src-tauri/src/locations/mod.rs - Register GoogleDriveProvider

Modified Frontend Files

  • src/App.tsx - Disable jarring loading overlay during navigation
  • src/components/Sidebar.tsx - Show Google accounts section, connect/disconnect functionality
  • src/components/PathBar.tsx - Handle gdrive:// URLs and Google Drive web URLs, add test IDs
  • src/store/useAppStore.ts - Fix path normalization for URI schemes, use non-streaming refresh for gdrive://
  • src/types/index.ts - Add GoogleAccountInfo type

Configuration & Tests

  • src-tauri/.env.example - Template for Google OAuth client credentials
  • docs/plans/2025-12-09-google-drive-integration-design.md - Design document
  • e2e/gdrive.spec.ts - Playwright test scaffolding
  • e2e/tauri-gdrive.spec.ts - Tauri-specific E2E test scaffolding
  • .gitignore - Add test-results/ directory

Test Plan

  • Run cd src-tauri && cargo test --lib gdrive::tests -- --nocapture to verify backend integration
  • Connect a Google account via sidebar
  • Browse My Drive and Shared with me folders
  • Navigate into subfolders (verify ID-based navigation works for shared items)
  • Paste a Google Drive URL and verify it navigates to the correct folder
  • Disconnect account and verify it's removed

Notes

  • Requires Google OAuth credentials in src-tauri/.env (see .env.example)
  • ID-based paths (gdrive://email/id/<file_id>) are used for shared drive items to ensure reliable navigation
  • Shared drive items require supports_all_drives and include_items_from_all_drives API flags

🤖 Generated with Claude Code

BrianLeishman and others added 2 commits December 16, 2025 07:45
Full integration plan for browsing Google Drive as a native location:
- OAuth 2.0 with multiple account support
- Paste Google Drive URLs and auto-resolve
- Full read/write operations via Drive API
- Sidebar integration with Cloud Storage section
- Caching and performance considerations

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add support for browsing Google Drive accounts directly in Marlin:

- OAuth 2.0 authentication flow with Google
- Multi-account support (connect multiple Google accounts)
- Virtual folder structure (My Drive, Shared with me, Starred, Recent)
- ID-based navigation for shared drive items
- Google Drive URL resolution (paste drive.google.com links)
- Integration tests using stored OAuth tokens

Backend (Rust):
- New gdrive module with auth, provider, and url_parser
- LocationProvider implementation for gdrive:// scheme
- Token storage in platform-specific app data directory
- Support for shared drives with supports_all_drives flag

Frontend (React):
- Sidebar shows connected Google accounts
- Click account to browse, right-click to disconnect
- PathBar handles gdrive:// URLs and Google Drive web URLs
- Non-streaming directory refresh for gdrive:// paths

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@gemini-code-assist

Copy link
Copy Markdown

Summary of Changes

Hello @BrianLeishman, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a significant new feature by integrating Google Drive as a first-class location provider. It allows users to seamlessly manage their cloud files alongside local storage, enhancing the application's utility as a universal file manager. The integration includes a secure authentication process, intuitive navigation of cloud content, and full support for common file operations, making cloud storage feel like an extension of the local file system.

Highlights

  • Google Drive Integration: Introduces full Google Drive integration, allowing users to browse their Google Drive accounts directly within the application, similar to local folders.
  • OAuth 2.0 Authentication: Implements a robust OAuth 2.0 authentication flow with support for multiple Google accounts, secure token storage, and automatic token refreshing.
  • Virtual Folder Structure: Users can navigate through virtual root folders such as 'My Drive', 'Shared with me', 'Starred', and 'Recent' for each connected Google account.
  • Google Drive URL Resolution: Enables direct navigation to Google Drive files or folders by pasting their web URLs into the path bar, automatically resolving them to the correct gdrive:// path.
  • Comprehensive File Operations: Supports standard file operations including reading directories, getting file metadata, creating directories, deleting (trashing), renaming, copying, and moving files within Google Drive.
  • ID-Based Navigation for Shared Items: Utilizes ID-based paths for shared Google Drive items to ensure reliable navigation, especially for content not directly in 'My Drive'.
  • End-to-End Testing: Adds new Playwright end-to-end tests, including specific tests for Google Drive URI handling and backend integration using tauri-driver.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request introduces Google Drive integration, a significant new feature. The implementation covers authentication, browsing, and URL resolution. The backend Rust code is well-structured, but there are a few areas for improvement regarding race conditions in token handling and code clarity. The new E2E tests are a great addition, but they could be made more reliable by avoiding fixed timeouts. Overall, this is a solid contribution that adds a lot of value. My feedback focuses on improving robustness and maintainability.

Comment thread src-tauri/src/locations/gdrive/auth.rs
Comment thread e2e/gdrive.spec.ts Outdated
Comment thread src-tauri/src/locations/gdrive/provider.rs
BrianLeishman and others added 26 commits December 16, 2025 10:19
…le Drive

- Add service account authentication for automated E2E testing
- Create 8 Rust integration tests covering:
  - Service account detection and token fetch
  - Drive root, My Drive, and Shared with me navigation
  - Subfolder navigation and path scheme preservation
- Add GitHub Actions workflow for CI testing
- Add WebdriverIO setup for Linux UI E2E tests
- Make locations module public for integration testing

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Change compile-time env!() to option_env!() for GOOGLE_CLIENT_ID and
  GOOGLE_CLIENT_SECRET so code compiles without secrets
- Add runtime checks for OAuth configuration in add_google_account()
  and ensure_valid_token()
- Fix E2E workflow to use correct dtolnay/rust-toolchain action
- Add Google secrets to check.yml and release.yml for production builds

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add TAURI_DRIVER_EXTERNAL env var to prevent double-start of driver
- Add TAURI_APP_PATH env var to specify app binary location
- Add smart app binary discovery in wdio.conf.ts
- Add proper Rust caching with shared keys between jobs
- Add tauri-driver caching to speed up subsequent runs
- Fix workflow to properly pass env vars to tests

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Update e2e-tests.yml to use [self-hosted, linux, X64] runners
- Update check.yml to use self-hosted runner
- Update release.yml:
  - sync-version job: self-hosted
  - Linux x86_64 builds: self-hosted
  - Linux arm64: keep on ubuntu-24.04-arm (no self-hosted arm runner)
  - macOS: keep on macos-15 (no self-hosted Mac runner)

This significantly reduces GitHub Actions costs by using org's
self-hosted runners for Linux workloads.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
System dependencies are pre-installed on the self-hosted Linux x86_64
runner, so apt-get install steps are no longer needed. Updated release
workflow to only run apt-get for arm64 builds which still use
GitHub-hosted runners.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Use --no-bundle flag to skip creating .deb/.rpm/.AppImage installers
during E2E test builds. Only the binary is needed for running tests,
and bundling was hanging for 15+ minutes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The npm subprocess doesn't inherit the PATH set by the rust-toolchain
action, so cargo isn't found. Explicitly source ~/.cargo/env to ensure
cargo is available.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The source command doesn't propagate to npm subprocesses. Using export
PATH ensures the cargo bin directory is available to all child processes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Same fix as e2e-tests - npm subprocesses need cargo in PATH explicitly.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Self-hosted runner doesn't have GPU access (/dev/dri/card0 permission
denied). Use LIBGL_ALWAYS_SOFTWARE and WEBKIT_DISABLE_COMPOSITING_MODE
to force software rendering for tauri-driver.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Using GITHUB_PATH persists the cargo bin directory across all steps
in the job, fixing 'cargo: command not found' in install tauri-driver
and other steps.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When building with --no-bundle, linuxdeploy isn't needed. Skipping
the download avoids ETXTBSY errors when multiple runners share the
same ~/.cache/tauri directory.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The tauri-driver command was not found because cargo bin directory
was not in PATH within the shell script. Adding explicit PATH export
at the start of the script fixes this.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add additional environment variables to force software rendering:
- WEBKIT_DISABLE_DMABUF_RENDERER: disable DMA-BUF renderer
- GDK_RENDERING=image: force image-based rendering
- GSK_RENDERER=cairo: use Cairo renderer instead of GL
- WEBKIT_DISABLE_SANDBOX_THIS_IS_DANGEROUS: bypass sandbox GPU checks

Also increase tauri-driver startup timeout and add debug output.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The PATH export wasn't working correctly, causing tauri-driver to not
be found. Use the full path $HOME/.cargo/bin/tauri-driver instead.
Also add debug output to help diagnose PATH issues.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Capture tauri-driver stdout/stderr to see why capabilities
matching is failing. This will help diagnose the WebDriver
session creation error.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Use cargo install --force to reinstall tauri-driver even if cargo
  thinks it's already installed (fixes case where metadata is stale
  but binary was deleted)
- Clean up stale /tmp/.X99-lock before starting Xvfb

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The cargo install command was failing because cargo wasn't in PATH
when the step executed. Adding explicit PATH export fixes this.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Use 'source $HOME/.cargo/env' to properly initialize cargo PATH
instead of manually exporting. This is more reliable across
different runner environments.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Multiple runners share ~/.cargo/bin, causing race conditions where one
job's tauri-driver gets deleted by another job reinstalling it.

Fix by installing to ${GITHUB_WORKSPACE}/.local-cargo which is unique
per job. Remove the cache step since it's no longer needed.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Same issue as e2e-linux - cargo not in PATH on self-hosted runners.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Handle cases where $HOME/.cargo/env doesn't exist by falling back
to manual PATH export. Also use TAURI_DRIVER_BIN env var if set.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add comprehensive debugging for cargo availability
- Try multiple locations to find cargo binary
- Fall back to installing Rust if cargo not found
- Use explicit cargo path instead of relying on PATH

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add cargo PATH initialization to the Build Tauri app step to
ensure cargo is available when npm run tauri build executes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
BrianLeishman and others added 12 commits December 16, 2025 17:22
The dtolnay/rust-toolchain action has a timing issue where it installs
rustup successfully but then tries to run rustc in the same step before
GITHUB_PATH updates take effect. This causes 'rustc: command not found'.

Moving the 'Add cargo to PATH' step BEFORE 'Install Rust' ensures that
~/.cargo/bin is in PATH when the action's internal scripts run.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add a step to test if the app binary can launch before running E2E tests.
This will help diagnose if the 'Failed to match capabilities' error is
caused by the app failing to start vs tauri-driver issues.

- Check library dependencies with ldd
- Try to start the app with a 10 second timeout
- Use same software rendering environment variables

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add more debugging to understand why tauri-driver fails with
'Failed to match capabilities':
- Check tauri-driver version
- Verify port 4444 is listening
- Try to get tauri-driver status endpoint

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
On Linux, tauri-driver needs the native WebKitWebDriver binary to
control WebKitGTK webviews. The --native-driver option tells tauri-driver
where to find it.

Search common locations for WebKitWebDriver and pass it to tauri-driver
if found.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add curl diagnostic to capture detailed error response from
tauri-driver when creating WebDriver session. This will help
understand why "Failed to match capabilities" is occurring.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The manual curl session test succeeded but left a session active,
blocking wdio from creating its own session (tauri-driver appears
to only support one session at a time).

Added session cleanup before tests for safety.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Enable trace-level logging in WebDriverIO to see exactly what
capabilities are being sent to tauri-driver, since "Failed to
match capabilities" keeps occurring even though manual curl works.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…nner

The e2e-linux job was failing because:
1. Display :99 was already in use by stale Xvfb
2. Port 4444 was already in use by stale tauri-driver

Added cleanup steps at start of job to:
- Kill any existing Xvfb processes on display :99
- Kill any existing tauri-driver processes
- Free port 4444 using fuser
- Wait for cleanup to complete before starting fresh

Also reverted trace logging back to info level.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add a curl-based session test that creates and immediately deletes
a session before running wdio tests. This will help determine if
the "Failed to match capabilities" error is from tauri-driver or
from wdio's capabilities format.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The Tauri WebDriver example doesn't include browserName in
capabilities. Remove it to match the official example and
see if this resolves the "Failed to match capabilities" error.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The E2E tests are now working - the curl debugging test confirmed
tauri-driver works correctly. The real fix was removing browserName
from wdio.conf.ts capabilities.

Test results: 2 passed, 5 failed (tests actually run now!)
- ✓ should load the app and show path bar
- ✓ should navigate into a shared subfolder
- Remaining failures are test/feature issues, not CI infrastructure

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Same timing fix as e2e-tests.yml - dtolnay/rust-toolchain installs
rustup but cargo isn't in PATH until the next step. Adding cargo
to GITHUB_PATH before the Install Rust step ensures it's available.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@BrianLeishman

Copy link
Copy Markdown
Member Author

Closing to start fresh - this branch is preserved as reference for the Google Drive implementation work. Will open a new cleaner PR from a fresh branch.

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.

1 participant