Skip to content

feat (cli): helix sync command#831

Merged
xav-db merged 7 commits into
devfrom
helix-sync
Feb 17, 2026
Merged

feat (cli): helix sync command#831
xav-db merged 7 commits into
devfrom
helix-sync

Conversation

@xav-db

@xav-db xav-db commented Jan 27, 2026

Copy link
Copy Markdown
Member

Greptile Overview

Greptile Summary

Implements helix sync command to pull source files and configuration from deployed Helix Cloud instances back to local projects, replacing the unimplemented helix pull command.

Key Changes:

  • Renamed pull command to sync with full implementation for Helix Cloud instances
  • Fetches .hx source files and helix.toml config from cloud via new /sync API endpoint
  • Implements intelligent diff detection and user confirmation before overwriting local files
  • Merges remote helix.toml with local config (preserves local instances, updates project and cloud sections)
  • Added WorkspaceConfig struct and input_workspace_id prompt (unused, likely for future features)
  • Push command now sends pruned helix.toml to cloud for storage and later retrieval

Issues Found:

  • New files from remote aren't shown in the confirmation prompt - only existing files that differ are tracked in the differing_files list

Important Files Changed

Filename Overview
helix-cli/src/commands/sync.rs New sync command implementation for pulling source files from cloud instances, with proper error handling and user confirmation for file overwrites
helix-cli/src/commands/integrations/helix.rs Added helix.toml serialization to deployment payload for server-side storage
helix-cli/src/main.rs Renamed Pull command to Sync with updated documentation

Sequence Diagram

sequenceDiagram
    participant User
    participant CLI as helix sync
    participant Cloud as Helix Cloud
    participant FS as File System

    User->>CLI: helix sync instance_name
    CLI->>FS: Load ProjectContext
    FS-->>CLI: Project config
    
    CLI->>CLI: Validate instance type
    Note over CLI: Only Helix Cloud supported
    
    CLI->>Cloud: GET /sync endpoint
    
    alt Success
        Cloud-->>CLI: SyncResponse with files
        
        CLI->>FS: Check for file differences
        FS-->>CLI: List of differing files
        
        alt Files differ
            CLI->>User: Show differing files
            CLI->>User: Confirm overwrite?
            User-->>CLI: Confirmation
            
            alt User confirms
                CLI->>FS: Write files to queries dir
                CLI->>FS: Merge helix.toml config
                FS-->>CLI: Done
                CLI->>User: Success message
            else User declines
                CLI->>User: Sync aborted
            end
        else No differences
            CLI->>FS: Write files
            CLI->>User: Success message
        end
        
    else Error
        Cloud-->>CLI: Error response
        CLI->>User: Error message
    end
Loading

@xav-db xav-db changed the base branch from main to dev January 27, 2026 09:12

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

1 file reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Comment on lines +171 to +180
for (filename, content) in &sync_response.hx_files {
let file_path = queries_dir.join(filename);
if file_path.exists() {
if let Ok(local_content) = std::fs::read_to_string(&file_path) {
if local_content != *content {
differing_files.push(filename.clone());
}
}
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

New files from remote aren't added to differing_files list, so users won't see them in the confirmation prompt. Only existing files that differ are tracked.

Suggested change
for (filename, content) in &sync_response.hx_files {
let file_path = queries_dir.join(filename);
if file_path.exists() {
if let Ok(local_content) = std::fs::read_to_string(&file_path) {
if local_content != *content {
differing_files.push(filename.clone());
}
}
}
}
// Collect files that differ from local
let mut differing_files: Vec<String> = Vec::new();
for (filename, content) in &sync_response.hx_files {
let file_path = queries_dir.join(filename);
if file_path.exists() {
if let Ok(local_content) = std::fs::read_to_string(&file_path) {
if local_content != *content {
differing_files.push(filename.clone());
}
}
} else {
// New file from remote
differing_files.push(filename.clone());
}
}

@matthewsanetra

Copy link
Copy Markdown
Contributor

@greptileai update the sequence diagram

@greptile-apps

greptile-apps Bot commented Jan 28, 2026

Copy link
Copy Markdown
Contributor

Looking at the sync command implementation, here's an updated sequence diagram that better reflects the actual flow:

sequenceDiagram
    participant User
    participant CLI as helix sync
    participant Cloud as Helix Cloud
    participant FS as File System

    User->>CLI: helix sync [instance_name]
    
    alt Has instance_name
        CLI->>FS: Load ProjectContext
        FS-->>CLI: Project config
        CLI->>CLI: Get instance config
        CLI->>CLI: Validate instance type (Helix Cloud only)
        CLI->>Cloud: GET /api/clusters/{cluster_id}/sync
    else No instance_name (interactive)
        alt Project exists
            CLI->>FS: Load ProjectContext
            FS-->>CLI: Project config with instances
            CLI->>User: Select instance from helix.toml
            User-->>CLI: Selected instance
        else No project
            CLI->>User: Select workspace and cluster
            CLI->>Cloud: GET /api/cli/workspaces (fetch workspaces)
            Cloud-->>CLI: List of workspaces
            CLI->>User: Select workspace
            User-->>CLI: Selected workspace
            CLI->>Cloud: GET /api/cli/workspaces/{workspace_id}/clusters
            Cloud-->>CLI: List of clusters
            CLI->>User: Select cluster
            User-->>CLI: Selected cluster
        end
        CLI->>Cloud: GET /api/clusters/{cluster_id}/sync
    end
    
    alt Success Response
        Cloud-->>CLI: SyncResponse{helix_toml, hx_files}
        
        CLI->>FS: Check existing files for differences
        FS-->>CLI: List of differing files
        
        alt Files differ
            CLI->>User: Show differing files list
            CLI->>User: Confirm overwrite?
            User-->>CLI: Yes/No confirmation
            
            alt User confirms
                CLI->>FS: Write .hx files to queries directory
                CLI->>FS: Merge remote helix.toml with local config
                Note over CLI,FS: Preserves local instances,<br/>updates project & cloud sections
                CLI->>User: Success message with file count
            else User declines
                CLI->>User: "Sync aborted by user"
            end
        else No differences or new files
            CLI->>FS: Write all files directly
            CLI->>FS: Merge helix.toml (if provided)
            CLI->>User: Success message with file count
        end
        
    else Error Response
        alt 404 Not Found
            CLI->>User: "No source files found. Deploy first with helix push"
        else 401 Unauthorized
            CLI->>User: "Authentication failed. Run helix auth login"
        else 403 Forbidden
            CLI->>User: "Access denied to cluster"
        else Other Error
            CLI->>User: Generic error message
        end
    end
Loading

Key updates from the original diagram:

  1. Added workspace/cluster selection flow for when no project exists
  2. Removed "Local instance" support - the command only works with Helix Cloud instances
  3. Enhanced error handling with specific status code responses (404, 401, 403)
  4. Added helix.toml merging logic - preserves local instances while updating project and cloud sections
  5. Clarified file writing process - writes to queries directory based on project config
  6. More accurate confirmation flow - only prompts when files actually differ

@xav-db xav-db merged commit 2bfc031 into dev Feb 17, 2026
25 checks passed
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