Table of Contents
- Gpack Development Guide
Gpack Development Guide
This guide covers creating, testing, and publishing gpacks to the ggen marketplace.
Overview
Gpacks are versioned template collections that can be shared across the ggen community. They include:
- Templates:
.tmpl
files with YAML frontmatter - Macros: Reusable template fragments (
.tera
files) - RDF Graphs: Semantic models and SPARQL queries
- Tests: Golden tests for validation
- Dependencies: Other gpacks this gpack depends on
Getting Started
Initialize New Gpack
# Create new gpack
ggen pack init
# This creates:
# ├── ggen.toml # Gpack manifest
# ├── templates/ # Template directory
# ├── macros/ # Macro directory
# ├── graphs/ # RDF graphs
# ├── tests/ # Test directory
# └── README.md # Documentation
Gpack Structure
my-gpack/
├── ggen.toml # Gpack manifest
├── templates/ # Template files
│ └── cli/
│ └── subcommand/
│ ├── rust.tmpl
│ ├── graphs/ # Local RDF data
│ │ ├── cli.ttl
│ │ └── shapes/
│ │ └── cli.shacl.ttl
│ └── queries/ # Local SPARQL queries
│ └── commands.rq
├── macros/ # Reusable fragments
│ └── common.tera
├── tests/ # Golden tests
│ └── test_hello.rs
├── README.md # Documentation
└── .gitignore # Git ignore file
Gpack Manifest (ggen.toml
)
Basic Manifest
[gpack]
id = "io.ggen.rust.cli-subcommand"
name = "Rust CLI subcommand"
version = "0.1.0"
description = "Generate clap subcommands"
license = "MIT"
authors = ["Your Name <your.email@example.com>"]
repository = "https://github.com/your-org/your-gpack"
homepage = "https://github.com/your-org/your-gpack"
keywords = ["rust", "cli", "clap"]
ggen_compat = ">=0.2 <0.4"
[dependencies]
"io.ggen.macros.std" = "^0.2"
[templates]
entrypoints = ["cli/subcommand/rust.tmpl"]
includes = ["macros/**/*.tera"]
[rdf]
base = "http://example.org/"
prefixes.ex = "http://example.org/"
files = ["templates/**/graphs/*.ttl"]
inline = ["@prefix ex: <http://example.org/> . ex:Foo a ex:Type ."]
Manifest Fields
Required Fields
id
: Unique identifier (reverse domain notation)name
: Human-readable nameversion
: Semantic versiondescription
: Brief descriptionlicense
: License identifierggen_compat
: Required ggen version range
Optional Fields
authors
: List of authorsrepository
: Source repository URLhomepage
: Project homepagekeywords
: Search keywordsreadme
: Path to README filechangelog
: Path to changelog file
Template Development
Template Structure
---
to: "src/cmds/{{ name | snake_case }}.rs"
vars:
name: "example"
description: "Example command"
rdf:
inline:
- mediaType: text/turtle
text: |
@prefix cli: <urn:ggen:cli#> .
[] a cli:Command ;
cli:name "{{ name }}" ;
cli:description "{{ description }}" .
sparql:
vars:
- name: slug
query: |
PREFIX cli: <urn:ggen:cli#>
SELECT ?slug WHERE { ?c a cli:Command ; cli:name ?slug } LIMIT 1
determinism:
seed: "{{ name }}"
---
// Generated by gpack: {{ gpack.id }}
// Template: {{ template.path }}
use clap::Parser;
#[derive(Parser)]
pub struct {{ name | pascal }}Args {
/// {{ description }}
#[arg(short, long)]
pub verbose: bool,
}
pub fn {{ name | snake_case }}(args: {{ name | pascal }}Args) -> Result<(), Box<dyn std::error::Error>> {
println!("{{ name | pascal }} command executed");
if args.verbose {
println!("Verbose mode enabled");
}
Ok(())
}
Template Best Practices
- Use semantic variable names:
name
,description
,version
- Include RDF models: Define semantic structure
- Add SPARQL queries: Extract variables from graphs
- Include determinism: Use seeds for reproducibility
- Add comments: Document generated code
- Use filters: Apply transformations (
| snake_case
,| pascal
)
Macro Development
Creating Macros
{#- Common CLI argument structure #}
{% macro cli_args(name, description) %}
#[derive(Parser)]
pub struct {{ name | pascal }}Args {
/// {{ description }}
#[arg(short, long)]
pub verbose: bool,
}
{% endmacro %}
{#- Common error handling #}
{% macro error_handling() %}
-> Result<(), Box<dyn std::error::Error>> {
// Error handling logic
Ok(())
}
{% endmacro %}
Using Macros
---
to: "src/cmds/{{ name }}.rs"
---
{% import "macros/common.tera" as common %}
{{ common::cli_args(name, description) }}
pub fn {{ name }}(args: {{ name | pascal }}Args) {{ common::error_handling() }}
RDF Graph Development
Graph Structure
@prefix cli: <urn:ggen:cli#> .
@prefix ex: <http://example.org/> .
@base <http://example.org/> .
ex:Command a cli:Command ;
cli:name "example" ;
cli:description "Example command" ;
cli:subcommands (
ex:StatusCommand
ex:ConfigCommand
) .
ex:StatusCommand a cli:Command ;
cli:name "status" ;
cli:description "Show status" .
ex:ConfigCommand a cli:Command ;
cli:name "config" ;
cli:description "Manage configuration" .
SPARQL Queries
PREFIX cli: <urn:ggen:cli#>
PREFIX ex: <http://example.org/>
# Extract command names
SELECT ?name WHERE {
?cmd a cli:Command ;
cli:name ?name .
}
# Extract subcommands
SELECT ?parent ?child WHERE {
?parent a cli:Command ;
cli:subcommands ?child .
?child a cli:Command .
}
Testing
Golden Tests
#![allow(unused)] fn main() { // tests/test_hello.rs #[cfg(test)] mod tests { use super::*; #[test] fn test_hello_command() { // Test generated code compiles let args = HelloArgs { verbose: false }; let result = hello(args); assert!(result.is_ok()); } } }
Test Configuration
# ggen.toml
[tests]
golden = ["tests/*.rs"]
variables = [
{ name = "test1", description = "Test command 1" },
{ name = "test2", description = "Test command 2" }
]
Running Tests
# Run all tests
ggen pack test
# Run specific test
ggen pack test --test test_hello
# Run with verbose output
ggen pack test --verbose
Linting and Validation
Lint Gpack
# Lint gpack for publishing
ggen pack lint
# Lint specific template
ggen pack lint --template templates/cli/subcommand/rust.tmpl
# Lint with fixes
ggen pack lint --fix
Validation Checks
The linter checks for:
- Manifest validity: Correct
ggen.toml
structure - Template syntax: Valid YAML frontmatter
- RDF validity: Well-formed RDF graphs
- SPARQL syntax: Valid SPARQL queries
- Dependencies: Resolvable dependencies
- Versioning: Semantic versioning compliance
Publishing
Prepare for Publishing
# Update version
# Edit ggen.toml:
# version = "0.2.0"
# Run tests
ggen pack test
# Lint gpack
ggen pack lint
# Generate changelog
ggen pack changelog
Publish to Registry
# Publish gpack
ggen pack publish
# Publish with specific version
ggen pack publish --version 0.2.0
# Publish with dry run
ggen pack publish --dry-run
Publishing Process
- Validation: Gpack is validated against schema
- Testing: Golden tests are run
- Linting: Code quality checks
- Registry Upload: Gpack is uploaded to registry
- Index Update: Registry index is updated
- Notification: Community is notified
Versioning
Semantic Versioning
Follow semantic versioning (semver):
- Major (1.0.0 → 2.0.0): Breaking changes
- Minor (1.0.0 → 1.1.0): New features
- Patch (1.0.0 → 1.0.1): Bug fixes
Version Guidelines
- 0.x.x: Development versions
- 1.x.x: Stable versions
- Pre-release: Use
-alpha
,-beta
,-rc
suffixes
Changelog
# Changelog
## [0.2.0] - 2024-01-15
### Added
- New CLI subcommand template
- Support for verbose flag
- Error handling macros
### Changed
- Updated RDF model structure
- Improved SPARQL queries
### Fixed
- Template variable resolution
- Macro import issues
## [0.1.0] - 2024-01-01
### Added
- Initial release
- Basic CLI subcommand template
Dependencies
Adding Dependencies
# ggen.toml
[dependencies]
"io.ggen.macros.std" = "^0.2"
"io.ggen.common.rdf" = "~0.1.0"
"io.ggen.rust.cli" = ">=0.1.0 <0.3.0"
Dependency Types
- Caret (^): Compatible versions (^0.2.0 = >=0.2.0 <0.3.0)
- Tilde (~): Patch-level changes (~0.1.0 = >=0.1.0 <0.2.0)
- Exact: Specific version (=0.2.1)
- Range: Version range (>=0.1.0 <0.3.0)
Dependency Resolution
# Check dependencies
ggen pack deps
# Update dependencies
ggen pack update
# Resolve conflicts
ggen pack resolve
Best Practices
Gpack Design
- Single Responsibility: One gpack, one purpose
- Consistent API: Use standard variable names
- Documentation: Include README and examples
- Testing: Comprehensive golden tests
- Versioning: Follow semver strictly
Template Quality
- Readability: Clear, well-commented code
- Maintainability: Modular, reusable templates
- Performance: Efficient SPARQL queries
- Security: Validate all inputs
- Accessibility: Follow language best practices
Community Guidelines
- Naming: Use descriptive, consistent names
- Licensing: Choose appropriate licenses
- Contributing: Welcome community contributions
- Support: Provide issue tracking
- Updates: Regular maintenance and updates
Troubleshooting
Common Issues
Template Not Found
# Check template path
ggen pack lint --template templates/cli/subcommand/rust.tmpl
# Verify entrypoints in manifest
cat ggen.toml | grep entrypoints
Dependency Conflicts
# Check dependency tree
ggen pack deps --tree
# Resolve conflicts
ggen pack resolve --force
RDF Validation Errors
# Validate RDF graphs
ggen pack lint --rdf-only
# Check SPARQL syntax
ggen pack lint --sparql-only
Test Failures
# Run tests with verbose output
ggen pack test --verbose
# Check test configuration
cat ggen.toml | grep -A 10 "\[tests\]"
Getting Help
- Documentation: Check this guide and other docs
- Community: Join ggen community forums
- Issues: Report bugs and request features
- Discussions: Ask questions and share ideas
Advanced Topics
Custom Filters
#![allow(unused)] fn main() { // Add custom Tera filters use tera::{Filter, Value, Result}; pub fn custom_filter(value: &Value, _: &HashMap<String, Value>) -> Result<Value> { // Custom filter logic Ok(value.clone()) } }
Plugin System
# ggen.toml
[plugins]
"io.ggen.plugin.custom" = "^0.1.0"
CI/CD Integration
# .github/workflows/publish.yml
name: Publish Gpack
on:
push:
tags:
- 'v*'
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install ggen
run: cargo install ggen
- name: Test gpack
run: ggen pack test
- name: Lint gpack
run: ggen pack lint
- name: Publish gpack
run: ggen pack publish
env:
GGEN_REGISTRY_TOKEN: ${{ secrets.GGEN_REGISTRY_TOKEN }}
This guide provides comprehensive coverage of gpack development, from initial creation to publishing and maintenance. Follow these practices to create high-quality, maintainable gpacks for the ggen community.