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.
- 🚀 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.yamlfiles 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
cargo install tixgraftcargo install --git https://github.com/tixena/tixgraftgit clone https://github.com/tixena/tixgraft
cd tixgraft
# Using just (recommended)
just setup
just build-release
# Or using cargo directly
cargo build --release# 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# Execute the configuration
tixgraft
# Preview what would be done
tixgraft --dry-run
# Use a different config file
tixgraft --config my-config.yaml# 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: 8080TixGraft supports a powerful context system that allows components to define required properties via .graft.yaml files. This enables parameterized, reusable components with validation.
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: 8080Context 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
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 valuesnumber: Integer or floating-point numbersboolean: true/false valuesarray: Arrays of any type (use--context-jsonfor 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:
- Files are copied to target directory
.graft.yamlfiles are discovered recursively- Context is validated against requirements
- Text replacements are applied using context values
- Post-commands are executed
.graft.yamlfiles are cleaned up
- Short format:
my_organization/repo→https://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
--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
--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")
# 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"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
# 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.0Given 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"- shell (default): Ready-to-execute shell command with proper escaping and line continuations
- json: JSON array of arguments, useful for programmatic processing
- The generated command excludes the
--configargument (it's config-free) - Execution flags like
--dry-runand--verboseare 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
repository: "my_organization/templates"
tag: "main"
pulls:
- source: "docker/nodejs"
target: "./docker"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"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 ."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: 3The 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=3repository: "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"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
- 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
- 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
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests for new functionality
- 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
- Submit a pull request
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 documentationLicensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
See CHANGELOG.md for version history and changes.