Skip to content

Repository files navigation

TixGraft

A CLI tool for fetching reusable components from Git repositories using sparse checkout. TixGraft enables developers to pull specific files or directories from Git repositories, apply text replacements, and execute post-processing commands—all configured through a YAML file or command-line arguments.

Features

  • 🚀 Efficient Git Operations: Uses sparse checkout to fetch only the files you need
  • 📝 YAML Configuration: Comprehensive configuration with JSON schema validation
  • 🔧 CLI Interface: Full command-line support with argument precedence over config files
  • 🎯 Context System: Define required properties in .graft.yaml files for parameterized components
  • 🔄 Text Replacement: Replace placeholders with static values, environment variables, or context properties
  • Command Execution: Run commands after copying files with proper working directory context
  • Validation: Comprehensive validation with clear error messages including fail-fast context validation
  • 🛡️ Security: Path validation prevents directory traversal attacks

Installation

Install from crates.io

cargo install tixgraft

Install from GitHub

cargo install --git https://github.com/tixena/tixgraft

Build from source

git clone https://github.com/tixena/tixgraft
cd tixgraft

# Using just (recommended)
just setup
just build-release

# Or using cargo directly
cargo build --release

Quick Start

1. Create a tixgraft.yaml configuration file:

# Global settings (optional)
repository: "my_organization/scaffolds"  # Can be full URL or account/repo format
tag: "main"                    # Branch, tag, or commit hash

# Pull operations (required, minimum 1)
pulls:
  - source: "kubernetes/mongodb"    # Required
    target: "./k8s/mongodb"         # Required
    type: "directory"               # Optional, default: "directory"
    commands:                       # Optional, default: []
      - "kubectl apply -f ."
      - "echo 'MongoDB deployed'"
    replacements:                   # Optional, default: []
      - source: "{{NAMESPACE}}"
        target: "production"         # String literal
      - source: "{{REPLICAS}}"
        valueFromEnv: "REPLICA_COUNT" # Environment variable

2. Run tixgraft:

# Execute the configuration
tixgraft

# Preview what would be done
tixgraft --dry-run

# Use a different config file
tixgraft --config my-config.yaml

Configuration Reference

YAML Configuration Structure

# Global Settings (both optional)
repository: "my_organization/scaffolds"  # Repository URL or account/repo format
tag: "main"                    # Git reference (branch, tag, or commit)

# Pull Operations (required, minimum 1)
pulls:
  - source: "path/in/repo"      # Required: Source path in repository
    target: "./local/path"       # Required: Target path in workspace
    type: "directory"            # Optional: "file" or "directory" (default: "directory")
    repository: "override/repo"  # Optional: Override global repository
    tag: "v2.1.0"               # Optional: Override global tag
    reset: true                  # Optional: rm -rf target before copying (default: false)
    commands:                    # Optional: Commands to execute after copying
      - "npm install"
      - "npm run build"
    replacements:                # Optional: Text replacements
      - source: "{{PLACEHOLDER}}"
        target: "replacement"     # Static replacement
      - source: "{{ENV_VAR}}"
        valueFromEnv: "MY_VAR"   # From environment variable
    context:                      # Optional: Context values for .graft.yaml files
      projectName: "my-service"
      port: 8080

Context System

TixGraft supports a powerful context system that allows components to define required properties via .graft.yaml files. This enables parameterized, reusable components with validation.

Context in tixgraft.yaml

Context can be defined at the root level (shared by all pulls) or per-pull:

# Global context (available to all pulls)
context:
  organization: "myorg"
  environment: "production"

pulls:
  - source: "services/api"
    target: "./api"
    type: "directory"
    context:  # Pull-specific context (merged with global)
      serviceName: "user-api"
      port: 8080

Context via CLI

Context values can also be provided via command-line arguments:

# Simple key=value format
tixgraft --context projectName=MyApp --context port=8080

# JSON format for complex values (arrays, objects)
tixgraft --context-json services='[{"name":"api","port":8080}]'

Context hierarchy: CLI arguments > Pull config > Root config

.graft.yaml Files

Components can include a .graft.yaml file that defines:

  • Required and optional context properties with type validation
  • Text replacements using context values
  • Post-processing commands

Example .graft.yaml:

# Define required context properties
context:
  - name: serviceName
    description: "The name of the service"
    dataType: string

  - name: port
    description: "Service port number"
    dataType: number
    defaultValue: 8080

  - name: replicas
    description: "Number of replicas"
    dataType: number
    defaultValue: 3

# Text replacements using context
replacements:
  - source: "{{SERVICE_NAME}}"
    valueFromContext: serviceName
  - source: "{{PORT}}"
    valueFromContext: port
  - source: "{{REPLICAS}}"
    valueFromContext: replicas

# Post-processing commands
postCommands:
  - command: kubectl
    args: ["apply", "-f", "."]

Supported Data Types:

  • string: Text values
  • number: Integer or floating-point numbers
  • boolean: true/false values
  • array: Arrays of any type (use --context-json for complex arrays)

Type Coercion: String values are automatically coerced to the target type when possible:

  • "true", "yes", "1"true (boolean)
  • "8080"8080 (number)

Validation: TixGraft validates context properties fail-fast:

  • Missing required properties → Error with exit code 1
  • Invalid types that can't be coerced → Error with exit code 1
  • Properties not defined in context → Warning, but continues

Processing Flow:

  1. Files are copied to target directory
  2. .graft.yaml files are discovered recursively
  3. Context is validated against requirements
  4. Text replacements are applied using context values
  5. Post-commands are executed
  6. .graft.yaml files are cleaned up

Repository URL Formats

  • Short format: my_organization/repohttps://github.com/my_organization/repo.git
  • HTTPS: https://github.com/my_organization/repo.git
  • SSH: git@github.com:my_organization/repo.git
  • Enterprise: https://git.company.com/team/repo.git

Command-Line Interface

Global Arguments

  • --repository <repo>: Git repository URL or account/repo format
  • --tag <ref>: Git reference (branch, tag, or commit hash)
  • --config <path>: Alternative config file path (default: ./tixgraft.yaml)
  • --context <KEY=VALUE>: Context values in KEY=VALUE format (repeatable, multiple values with same key create array)
  • --context-json <KEY=JSON>: Context values as JSON for complex types like arrays or objects (repeatable)
  • --dry-run: Preview operations without executing
  • --to-command-line: Output the equivalent command-line invocation instead of executing
  • --output-format <format>: Output format for --to-command-line: shell or json (default: shell)
  • --verbose, -v: Enable verbose logging output
  • --help, -h: Show help information
  • --version: Show version

Per-Pull Arguments (repeatable)

  • --pull-repository <repo>: Repository for specific pull
  • --pull-tag <ref>: Git reference for specific pull
  • --pull-type <type>: Either "file" or "directory" (default: "directory")
  • --pull-source <path>: Source path in Git repository
  • --pull-target <path>: Target path in local workspace
  • --pull-reset: For directories, rm -rf target before copying
  • --pull-commands <cmd1,cmd2,...>: Comma-separated commands
  • --pull-replacement <SOURCE=TARGET>: Text replacement (format: "SOURCE=TARGET" or "SOURCE=env:VAR")

CLI-Only Usage

# Pull a single directory
tixgraft --repository my_organization/templates --pull-source kubernetes/app --pull-target ./k8s

# Pull multiple items with different repositories
tixgraft \
  --pull-repository my_organization/configs --pull-source docker/Dockerfile --pull-target ./Dockerfile --pull-type file \
  --pull-repository my_organization/scripts --pull-source ci/deploy.sh --pull-target ./scripts/deploy.sh --pull-type file

# Text replacements via CLI
tixgraft \
  --repository my_organization/templates \
  --pull-source kubernetes/app \
  --pull-target ./k8s \
  --pull-replacement "{{APP_NAME}}=my-app" \
  --pull-replacement "{{NAMESPACE}}=env:K8S_NAMESPACE"

Converting Configuration to Command Line

TixGraft can convert any YAML configuration to an equivalent command-line invocation using the --to-command-line flag. This is useful for:

  • Sharing workflows: Generate a single command others can run without needing a config file
  • Debugging configuration: Verify how YAML config is interpreted and merged
  • CI/CD integration: Convert human-friendly YAML to scriptable CLI commands
  • Documentation: Show concrete examples of complex configurations

Basic Usage

# Show the command-line equivalent of your config (default: shell format)
tixgraft --to-command-line

# Use a specific config file
tixgraft --config custom.yaml --to-command-line

# Output as JSON array (useful for programmatic consumption)
tixgraft --to-command-line --output-format json

# Apply CLI overrides before generating output
tixgraft --to-command-line --repository override/repo --tag v2.0

Example

Given this tixgraft.yaml:

repository: "my_organization/templates"
tag: "v1.0.0"
pulls:
  - source: "kubernetes/base"
    target: "./k8s"
    reset: true
    replacements:
      - source: "{{APP_NAME}}"
        target: "my-app"

Running tixgraft --to-command-line outputs:

tixgraft \
  --repository "my_organization/templates" \
  --tag "v1.0.0" \
  --pull-source "kubernetes/base" \
  --pull-target "./k8s" \
  --pull-reset \
  --pull-replacement "{{APP_NAME}}=my-app"

Output Formats

  • shell (default): Ready-to-execute shell command with proper escaping and line continuations
  • json: JSON array of arguments, useful for programmatic processing

Notes

  • The generated command excludes the --config argument (it's config-free)
  • Execution flags like --dry-run and --verbose are not included in the output
  • All shell special characters are properly escaped for safe execution
  • CLI argument overrides (--repository, --tag) are applied before generating the output

Examples

Basic Directory Copy

repository: "my_organization/templates"
tag: "main"
pulls:
  - source: "docker/nodejs"
    target: "./docker"

Multi-Repository Setup

pulls:
  - source: "configs/nginx"
    target: "./nginx"
    repository: "my_organization/configs"
    tag: "v1.2.0"
  - source: "scripts/deploy.sh"
    target: "./deploy.sh"
    type: "file"
    repository: "my_organization/scripts"
    tag: "latest"

With Text Replacements

repository: "my_organization/k8s-templates"
pulls:
  - source: "apps/web-service"
    target: "./k8s/web"
    replacements:
      - source: "{{APP_NAME}}"
        target: "my-web-app"
      - source: "{{NAMESPACE}}"
        valueFromEnv: "K8S_NAMESPACE"
      - source: "{{IMAGE_TAG}}"
        valueFromEnv: "BUILD_TAG"
    commands:
      - "kubectl apply -f ."

Using Context for Parameterized Components

When pulling components that include .graft.yaml files:

# tixgraft.yaml
repository: "my_organization/service-templates"
context:
  organization: "mycompany"
  environment: "production"

pulls:
  - source: "microservices/api-service"
    target: "./services/user-api"
    context:
      serviceName: "user-api"
      port: 8080
      replicas: 3

The pulled component's .graft.yaml might look like:

# services/user-api/.graft.yaml (in the repository)
context:
  - name: serviceName
    description: "Name of the microservice"
    dataType: string
  - name: port
    description: "Service port"
    dataType: number
  - name: replicas
    description: "Number of replicas"
    dataType: number
    defaultValue: 2

replacements:
  - source: "{{SERVICE_NAME}}"
    valueFromContext: serviceName
  - source: "{{PORT}}"
    valueFromContext: port
  - source: "{{REPLICAS}}"
    valueFromContext: replicas

postCommands:
  - command: sed
    args: ["-i", "", "s/ORG_PLACEHOLDER/{{organization}}/g", "deployment.yaml"]

You can also provide context via CLI:

tixgraft --context serviceName=user-api --context port=8080 --context replicas=3

Complex Kubernetes Setup

repository: "devops/k8s-scaffolds"
tag: "production"
pulls:
  - source: "base/namespace"
    target: "./k8s/00-namespace"
    replacements:
      - source: "{{NAMESPACE}}"
        valueFromEnv: "K8S_NAMESPACE"
  
  - source: "apps/mongodb"
    target: "./k8s/01-mongodb"
    reset: true
    replacements:
      - source: "{{STORAGE_CLASS}}"
        valueFromEnv: "STORAGE_CLASS"
      - source: "{{REPLICA_COUNT}}"
        target: "3"
    commands:
      - "kubectl apply -f ."
      - "kubectl wait --for=condition=ready pod -l app=mongodb --timeout=300s"

Error Handling

TixGraft uses specific exit codes for different error types:

  • 0: Success - all operations completed successfully
  • 1: Configuration Error - missing or invalid configuration
  • 2: Source Error - source path not found in repository
  • 3: Command Error - one or more commands failed
  • 4: Git Error - Git operation failed
  • 5: Filesystem Error - file operation failed

Requirements

  • Git: Version 2.25.0 or later (for sparse checkout support)
  • Rust: 1.70+ (for building from source)
  • Just: (optional, for development) - Install from just.systems

Security Considerations

  • Path validation prevents directory traversal attacks
  • Commands are executed in the target directory context
  • Repository URLs are validated before use
  • Binary files are skipped during text replacement

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Add tests for new functionality
  5. Run the development pipeline:
    # Using just (recommended)
    just dev     # format, lint, and test
    just ci      # full CI pipeline
    
    # Or using cargo directly
    cargo test && cargo clippy
  6. Submit a pull request

Development Commands

This project uses just for task running. Install it and run:

just --list          # Show all available commands
just setup           # Set up development environment
just dev             # Quick development checks (format, lint, test)
just ci              # Full CI pipeline
just build-release   # Build optimized binary
just install         # Install from local source
just docs            # Generate documentation

License

Licensed under either of:

at your option.

Changelog

See CHANGELOG.md for version history and changes.

About

No description, website, or topics provided.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages