Skip to content

Conversation

@ishaksebsib
Copy link
Contributor

@ishaksebsib ishaksebsib commented Oct 29, 2025

Description

Adds a complete template system to the Helix CLI, allowing users to initialize projects from Git-based templates with Handlebars variable substitution and caching.

Key Features

Template Sources:

  • Official templates: helix init --template <template-name>
  • With version/branch: helix init --template <template-name>@v1.0
  • HTTPS URLs: helix init --template https://github.com/user/repo@main
  • SSH URLs: helix init --template [email protected]:user/repo.git@v2

Caching System:

  • Commit-hash based caching for immutability and reliability
  • Two-level cache structure: ~/.helix/templates/<url_hash>/<commit_hash>/
  • Caches rendered templates (ready to use)
  • Cache invalidation via git ls-remote to check for updates
  • Network failure resilience: falls back to cached version when offline

Template Processing:

  • Handlebars variable substitution (e.g., {{project_name}})
  • .hbs file extension support for template files

User Experience:

# Initialize from official template
helix init --template basic

# Initialize from specific version
helix init --template [email protected]

# Initialize from custom GitHub repo
helix init --template https://github.com/user/custom-template

# Use SSH URL
helix int --template [email protected]:user/template.git@main

Implementation Details

New Files:

  • commands/templates/mod.rs - Template source parsing and configuration
  • commands/templates/fetcher.rs - Git operations, caching, and cache validation
  • commands/templates/processor.rs - Handlebars rendering and file copying

Dependencies:

  • Added handlebars = "6.3.2" for template rendering
  • Added tempfile = "3.23.0" for temporary files

Modified Files:

  • commands/init.rs - Integrated template processing into the init flow
  • commands/mod.rs - Exposed templates module

Related Issues

None

Checklist when merging to main

  • No compiler warnings (if applicable)
  • Code is formatted with rustfmt
  • No useless or dead code (if applicable)
  • Code is easy to understand
  • Doc comments are used for all functions, enums, structs, and fields (where appropriate)
  • All tests pass
  • Performance has not regressed (assuming change was not to fix a bug)
  • Version number has been updated in helix-cli/Cargo.toml and helixdb/Cargo.toml

Additional Notes

None

Greptile Overview

Updated On: 2025-10-30 07:55:59 UTC

Greptile Summary

Implements a complete Git-based template system for the Helix CLI that enables project initialization from official or custom templates with Handlebars variable substitution. The implementation includes a sophisticated commit-hash based caching mechanism with network resilience and atomic operations.

Architecture Highlights:

  • Three-module design: mod.rs (URL parsing), fetcher.rs (Git operations & caching), processor.rs (Handlebars rendering)
  • Two-level cache structure: ~/.helix/templates/{url_hash}/{commit_hash}/ ensures immutability and proper invalidation
  • Cache validation via git ls-remote checks for updates, with graceful fallback to cached versions when offline
  • Atomic rendering: uses temporary directories with atomic rename to prevent race conditions
  • Template validation ensures helix.toml or helix.toml.hbs exists before processing

Implementation Quality:

  • Properly handles edge cases: symlinks are skipped, hidden files excluded (except .gitignore), .git directories filtered
  • URL parsing correctly handles SSH URLs ([email protected]:user/repo.git@v2) and HTTPS URLs with @ in usernames
  • Error handling distinguishes between network errors (allows cache fallback) and auth/not-found errors (fails with clear message)
  • Template variables passed to Handlebars with proper error wrapping
  • The init.rs integration maintains existing project protection (checks for helix.toml before processing)

Previous Review Comments Addressed:

  • Race condition fix (commit db4d905c): Atomic rendering now uses temp directories within the base cache dir with atomic rename
  • Symlink handling (commit 723a8865): Explicitly checks and skips symlinks during rendering
  • Temp cleanup: Properly handled by TempDir RAII pattern

Code is production-ready with no critical issues found.

Important Files Changed

File Analysis

Filename Score Overview
helix-cli/src/commands/templates/mod.rs 5/5 Defines TemplateSource enum for parsing official templates and Git URLs with comprehensive URL parsing logic and unit tests
helix-cli/src/commands/templates/fetcher.rs 5/5 Implements Git-based template fetching with atomic caching, commit-hash validation, and network resilience with proper fallback to cached versions
helix-cli/src/commands/templates/processor.rs 5/5 Handles Handlebars template rendering with proper .hbs extension removal, skips symlinks and hidden files (except .gitignore), and correctly skips .git directories
helix-cli/src/commands/init.rs 5/5 Integrates template system into init command, changes template parameter from unused String to functional Option<String>, maintains existing project protection check

Sequence Diagram

sequenceDiagram
    participant User
    participant CLI as helix init
    participant Init as init.rs
    participant Parser as TemplateSource::parse
    participant Fetcher as TemplateFetcher
    participant Git as Git Commands
    participant Cache as ~/.helix/templates
    participant Processor as TemplateProcessor
    participant HBS as Handlebars
    participant Project as Project Directory

    User->>CLI: helix init --template [email protected]
    CLI->>Init: run(template: Some("[email protected]"))
    
    Init->>Init: Check helix.toml exists
    Init->>Init: Create project directory
    
    Init->>Parser: parse("[email protected]")
    Parser-->>Init: TemplateSource::Official{name: "basic", git_ref: Some("v1.0")}
    
    Init->>Fetcher: fetch(source, variables)
    
    Fetcher->>Git: git --version
    Git-->>Fetcher: version info
    
    Fetcher->>Fetcher: check_cache_validity(source)
    Fetcher->>Git: git ls-remote https://github.com/HelixDB/basic v1.0
    
    alt Network Success
        Git-->>Fetcher: commit_hash: abc123...
        Fetcher->>Cache: Check if abc123 exists
        
        alt Cache Valid
            Cache-->>Fetcher: Cache exists
            Fetcher-->>Init: Return cache_path
        else Cache Invalid
            Fetcher->>Fetcher: fetch_and_render(source, variables)
            Fetcher->>Git: git clone --depth 1 --branch v1.0
            Git-->>Fetcher: Clone to temp directory
            
            Fetcher->>Fetcher: validate_template (check helix.toml/helix.toml.hbs)
            
            Fetcher->>Fetcher: render_and_cache
            Fetcher->>Processor: render_to_cache(temp_dir, temp_cache_dir, variables)
            
            loop For each file/directory
                alt File ends with .hbs
                    Processor->>HBS: render_template(content, variables)
                    HBS-->>Processor: rendered content
                    Processor->>Cache: Write to temp cache (remove .hbs extension)
                else Regular file
                    Processor->>Cache: Copy to temp cache
                else Directory
                    Processor->>Processor: Recurse into directory
                end
            end
            
            Processor-->>Fetcher: Rendering complete
            Fetcher->>Cache: Atomic rename temp_cache_dir to abc123
            Fetcher-->>Init: Return cache_path
        end
    else Network Error
        Git-->>Fetcher: Network error
        Fetcher->>Cache: Check for any cached version
        
        alt Cache Exists
            Cache-->>Fetcher: Return latest cached version
            Fetcher-->>Init: Return cache_path (with warning)
        else No Cache
            Fetcher-->>Init: Error: network error and no cache
        end
    end
    
    Init->>Processor: process(cache_dir, project_dir)
    
    loop For each cached file
        alt Is .git directory
            Processor->>Processor: Skip
        else Is directory
            Processor->>Project: Create directory and recurse
        else Is file
            Processor->>Project: Copy file
        end
    end
    
    Processor-->>Init: Copy complete
    Init-->>User: Success message with next steps
Loading

@ishaksebsib ishaksebsib marked this pull request as ready for review October 29, 2025 15:43
Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

7 files reviewed, 3 comments

Edit Code Review Agent Settings | Greptile

@ishaksebsib
Copy link
Contributor Author

@greptile

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

3 files reviewed, no comments

Edit Code Review Agent Settings | Greptile

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