Back to list
angreal

angreal-integrations

by angreal

Task automation and project templating tool. Define reusable commands in Python, scaffold projects from templates, and let tasks travel with your codebase. Rust core, Python API.

10🍴 4📅 Jan 18, 2026

SKILL.md


name: angreal-integrations description: This skill should be used when the user asks to "use Git in a task", "manage virtual environments", "use Docker Compose", "clone a repository", "create a venv", "run docker-compose", "use angreal.integrations", "render a template", "scaffold files", "generate files from template", "use render_template", "use render_directory", "use Flox", "flox environment", "cross-language environment", "flox services", "@flox_required", or needs guidance on the built-in Git, VirtualEnv, Docker, Flox, or Tera templating integrations available in angreal tasks. version: 2.8.1

Angreal Integrations

Angreal provides built-in integrations for common development tools: Git, virtual environments, Docker Compose, Flox environments, and Tera templating.

Tera Templating

Generate files and directories using the Tera template engine. Useful for scaffolding code, configs, or any structured files within tasks.

Import

import angreal

render_template()

Render a template string with variable substitution:

import angreal

# Simple variable substitution
template = "Hello {{ name }}!"
result = angreal.render_template(template, {"name": "World"})
# result == "Hello World!"

# With conditionals and loops
template = """
# {{ project_name }}

{% if use_docker %}
## Docker Setup
Run `docker-compose up` to start.
{% endif %}

## Dependencies
{% for dep in dependencies %}
- {{ dep }}
{% endfor %}
"""

result = angreal.render_template(template, {
    "project_name": "My App",
    "use_docker": True,
    "dependencies": ["requests", "click"]
})

Signature: angreal.render_template(template: str, context: dict) -> str

render_directory()

Render an entire directory tree, processing both file contents and file/directory names:

import angreal

# Source directory structure:
# templates/
#   {{ module_name }}/
#     __init__.py
#     {{ module_name }}.py

rendered_files = angreal.render_directory(
    "templates",           # src: source directory
    "output",              # dst: destination directory
    False,                 # force: overwrite existing files?
    {"module_name": "mymodule"}  # context: template variables
)

# Creates:
# output/
#   mymodule/
#     __init__.py
#     mymodule.py

print(f"Created {len(rendered_files)} files")

# Context is optional - copy without templating
angreal.render_directory("static_files", "output", False, None)

Signature: angreal.render_directory(src: str, dst: str, force: bool, context: dict | None) -> list[str]

Returns a list of created file paths.

generate_context()

Load variables from a TOML file, optionally prompting the user:

import angreal

# Load from TOML without prompting
context = angreal.generate_context("config.toml", take_input=False)

# Load and prompt user for values
context = angreal.generate_context("config.toml", take_input=True)

Signature: angreal.generate_context(path: str, take_input: bool) -> dict

get_context()

Get the context from the current project's .angreal/angreal.toml:

import angreal

# Returns dict from .angreal/angreal.toml or empty dict if not found
context = angreal.get_context()
project_name = context.get("project_name", "unknown")

Signature: angreal.get_context() -> dict

Tera Syntax Quick Reference

{# Comments #}

{{ variable }}                    {# Variable substitution #}
{{ name | upper }}                {# Filters: upper, lower, title, trim #}
{{ items | length }}              {# Get length #}
{{ value | default("fallback") }} {# Default value #}

{% if condition %}                {# Conditionals #}
{% elif other %}
{% else %}
{% endif %}

{% for item in items %}           {# Loops #}
  {{ loop.index }}: {{ item }}
{% endfor %}

{% raw %}{{ not processed }}{% endraw %}  {# Escape template syntax #}

Complete Scaffolding Example

import angreal
import os

@angreal.command(name="scaffold", about="Generate a new module")
@angreal.argument(name="name", long="name", required=True, help="Module name")
@angreal.argument(name="with_tests", long="with-tests", is_flag=True, takes_value=False)
def scaffold(name, with_tests=False):
    project_root = angreal.get_root().parent

    # Template for module file
    module_template = '''"""{{ name }} module."""

class {{ name | title }}:
    """{{ description }}"""

    def __init__(self):
        pass
'''

    # Template for test file
    test_template = '''"""Tests for {{ name }}."""
import pytest
from {{ name }} import {{ name | title }}

def test_{{ name }}_init():
    instance = {{ name | title }}()
    assert instance is not None
'''

    context = {
        "name": name,
        "description": f"The {name} module"
    }

    # Create module
    module_content = angreal.render_template(module_template, context)
    module_path = os.path.join(project_root, "src", f"{name}.py")
    os.makedirs(os.path.dirname(module_path), exist_ok=True)
    with open(module_path, "w") as f:
        f.write(module_content)
    print(f"Created {module_path}")

    # Create test if requested
    if with_tests:
        test_content = angreal.render_template(test_template, context)
        test_path = os.path.join(project_root, "tests", f"test_{name}.py")
        os.makedirs(os.path.dirname(test_path), exist_ok=True)
        with open(test_path, "w") as f:
            f.write(test_content)
        print(f"Created {test_path}")

    return 0

Git Integration

Full-featured Git wrapper for repository operations.

Import

from angreal.integrations.git import Git, clone

Basic Usage

from angreal.integrations.git import Git

# Initialize with working directory (default: current directory)
git = Git()
git = Git("/path/to/repo")

# All methods return (exit_code, stderr, stdout) tuples
exit_code, stderr, stdout = git.status()

Git Class Methods

MethodSignatureDescription
initinit(bare=False)Initialize a new repository
addadd(*paths)Stage files for commit
commitcommit(message, all=False)Create a commit
pushpush(remote=None, branch=None)Push to remote
pullpull(remote=None, branch=None)Pull from remote
statusstatus(short=False)Show working tree status
branchbranch(name=None, delete=False)List or manage branches
checkoutcheckout(branch, create=False)Switch branches
tagtag(name, message=None)Create a tag

Callable Interface

The Git object is callable for arbitrary git commands:

git = Git()

# Call any git command
exit_code, stderr, stdout = git("log", "--oneline", "-5")
exit_code, stderr, stdout = git("diff", "HEAD~1")
exit_code, stderr, stdout = git("stash", "pop")

Clone Function

from angreal.integrations.git import clone

# Clone a repository
path = clone("https://github.com/user/repo.git")
path = clone("https://github.com/user/repo.git", "/custom/destination")

Complete Example

import angreal
from angreal.integrations.git import Git

@angreal.command(name="release", about="Create a release")
@angreal.argument(name="version", long="version", required=True, help="Version tag")
def release(version):
    git = Git()

    # Check for clean working tree
    exit_code, stderr, stdout = git.status(short=True)
    if stdout.strip():
        print("Error: Working tree not clean")
        return 1

    # Create and push tag
    git.tag(version, message=f"Release {version}")
    git.push("origin", version)

    print(f"Released {version}")
    return 0

VirtualEnv Integration

Manage Python virtual environments with automatic activation.

Import

from angreal.integrations.venv import VirtualEnv, venv_required

Creating Virtual Environments

from angreal.integrations.venv import VirtualEnv

# Create venv (default path: .venv, created immediately)
venv = VirtualEnv()

# Custom path
venv = VirtualEnv("/path/to/venv")

# Defer creation
venv = VirtualEnv(".venv", now=False)
venv.create()  # Create manually later

# With requirements
venv = VirtualEnv(".venv", requirements="requirements.txt")
venv = VirtualEnv(".venv", requirements=["requests", "click"])

VirtualEnv Constructor

VirtualEnv(
    path=".venv",           # Path to virtual environment
    python=None,            # Python version (e.g., "3.11")
    requirements=None,      # Requirements file or package list
    now=True,               # Create immediately if True
)

VirtualEnv Methods

MethodDescription
create()Create the virtual environment
activate()Activate the venv in current process
deactivate()Deactivate the venv
install(packages)Install packages (string, list, or requirements file)
install_requirements()Install requirements passed to constructor
remove()Delete the virtual environment

VirtualEnv Properties

PropertyTypeDescription
pathPathAbsolute path to venv directory
namestrName of the venv
python_executablePathPath to Python interpreter
existsboolWhether the venv exists

Context Manager Usage

from angreal.integrations.venv import VirtualEnv

# Automatically activate and deactivate
with VirtualEnv(".venv") as venv:
    # venv is activated here
    import subprocess
    subprocess.run(["python", "-c", "import sys; print(sys.prefix)"])
# venv is deactivated here

The @venv_required Decorator

Automatically manage venv lifecycle for a task:

import angreal
from angreal.integrations.venv import venv_required

@angreal.command(name="test", about="Run tests in venv")
@venv_required(".venv", requirements=["pytest"])
def test():
    import subprocess
    # Runs inside activated venv
    subprocess.run(["pytest", "tests/"])

Complete Example

import angreal
from angreal.integrations.venv import VirtualEnv

@angreal.command(name="setup", about="Setup development environment")
def setup():
    venv = VirtualEnv(".venv", requirements="requirements-dev.txt")
    venv.install_requirements()

    print(f"Virtual environment created at {venv.path}")
    print(f"Activate with: source {venv.path}/bin/activate")
    return 0

Docker Compose Integration

Manage Docker Compose services from tasks.

Import

from angreal.integrations.docker import DockerCompose, compose

Basic Usage

from angreal.integrations.docker import DockerCompose

# Create instance from compose file
dc = DockerCompose("docker-compose.yml")
dc = DockerCompose("docker-compose.yml", project_name="myproject")

# Or use the convenience function
from angreal.integrations.docker import compose
dc = compose("docker-compose.yml")

DockerCompose Methods

All methods return a ComposeResult object with: success, exit_code, stdout, stderr.

Service Lifecycle

# Start services
result = dc.up(detach=True)
result = dc.up(detach=True, build=True, services=["web", "db"])

# Stop and remove
result = dc.down(volumes=True, remove_orphans=True)

# Start/stop without removing
result = dc.start(services=["web"])
result = dc.stop(services=["web"], timeout="30s")

# Restart
result = dc.restart(services=["web"])

Build and Pull

# Build images
result = dc.build(no_cache=True, parallel=True)
result = dc.build(services=["web"])

# Pull images
result = dc.pull(services=["db"])

Inspection

# List services
result = dc.ps(all=True)

# View logs
result = dc.logs(services=["web"], follow=False, tail="100")

# Validate config
result = dc.config(quiet=True)

Execute Commands

# Run command in service container
result = dc.exec(
    "web",
    ["python", "manage.py", "migrate"],
    user="app",
    workdir="/app"
)

Method Signatures

up()

dc.up(
    detach=True,           # Run in background
    build=False,           # Build images before starting
    remove_orphans=False,  # Remove containers for undefined services
    force_recreate=False,  # Recreate even if unchanged
    no_recreate=False,     # Don't recreate existing containers
    services=None,         # List of services to start
)

down()

dc.down(
    volumes=False,         # Remove named volumes
    remove_orphans=False,  # Remove undefined service containers
    timeout=None,          # Shutdown timeout (e.g., "30s")
)

logs()

dc.logs(
    services=None,         # Services to show logs for
    follow=False,          # Follow log output
    timestamps=False,      # Show timestamps
    tail=None,             # Number of lines (e.g., "100")
    since=None,            # Show logs since timestamp
)

exec()

dc.exec(
    service,               # Service name
    command,               # Command as list of strings
    detach=False,          # Run in background
    tty=True,              # Allocate pseudo-TTY
    user=None,             # Run as user
    workdir=None,          # Working directory
    env=None,              # Environment variables dict
)

Static Methods

# Check if Docker Compose is available
if DockerCompose.is_available():
    dc = DockerCompose("docker-compose.yml")

Properties

PropertyTypeDescription
compose_filestrPath to compose file
working_dirstrWorking directory
project_namestrProject name (if set)

Complete Example

import angreal
from angreal.integrations.docker import DockerCompose

@angreal.command(name="dev", about="Start development environment")
@angreal.argument(name="build", long="build", is_flag=True, takes_value=False)
def dev(build=False):
    dc = DockerCompose("docker-compose.yml", project_name="myapp")

    if not DockerCompose.is_available():
        print("Error: Docker Compose not found")
        return 1

    result = dc.up(detach=True, build=build)
    if not result.success:
        print(f"Failed: {result.stderr}")
        return 1

    print("Services started:")
    dc.ps()
    return 0

@angreal.command(name="logs", about="View service logs")
@angreal.argument(name="service", long="service", help="Service name")
@angreal.argument(name="follow", short="f", long="follow", is_flag=True, takes_value=False)
def logs(service=None, follow=False):
    dc = DockerCompose("docker-compose.yml")
    services = [service] if service else None
    result = dc.logs(services=services, follow=follow, tail="100")
    print(result.stdout)
    return 0

Flox Integration

Cross-language development environment and services management using Flox (Nix-based).

When to Use Flox vs VirtualEnv vs Docker

Use CaseBest Integration
Python-only projectVirtualEnv (simplest)
Multi-language projectFlox (cross-language)
Need containerized servicesDocker Compose
Need lightweight servicesFlox (process-based)
Team-wide consistencyFlox (manifest.toml)

Import

from angreal.integrations.flox import Flox, FloxServices, flox_required

The @flox_required Decorator

Automatically manage Flox environment and services for a task:

import angreal
from angreal.integrations.flox import flox_required

@angreal.command(name="test", about="Run tests with database")
@flox_required(".", services=["postgres"])
def run_tests():
    import subprocess
    subprocess.run(["pytest", "-v"])

# Without services
@angreal.command(name="build", about="Build in Flox environment")
@flox_required(".")
def build():
    import subprocess
    subprocess.run(["npm", "run", "build"])

Flox Class

from angreal.integrations.flox import Flox

# Create instance
flox = Flox(".")

# Check availability
if not Flox.is_available():
    print("Install Flox: curl -fsSL https://flox.dev/install | bash")

# Check environment
print(f"Exists: {flox.exists}")
print(f"Has manifest: {flox.has_manifest}")
print(f"Version: {Flox.version()}")

Flox Methods

MethodDescription
activate()Activate environment in current process
deactivate()Restore original environment
run(cmd, args)Execute command in Flox environment

Context Manager Usage

from angreal.integrations.flox import Flox

with Flox(".") as flox:
    # Environment is activated
    exit_code, stdout, stderr = flox.run("node", ["--version"])
    print(f"Node version: {stdout}")
# Environment is automatically deactivated

Flox Services

from angreal.integrations.flox import Flox

flox = Flox(".")

# Start services
handle = flox.services.start("postgres", "redis")

# Check status
for svc in flox.services.status():
    print(f"{svc.name}: {svc.status} (PID: {svc.pid})")

# View logs
logs = flox.services.logs("postgres", tail=50)

# Stop services
handle.stop()

Cross-Session Service Management

import angreal
from angreal.integrations.flox import Flox, FloxServiceHandle

@angreal.command(name="dev-start", about="Start dev services")
def start_dev():
    flox = Flox(".")
    handle = flox.services.start("postgres", "redis")
    handle.save()  # Persists to .flox-services.json
    print("Development services started")

@angreal.command(name="dev-stop", about="Stop dev services")
def stop_dev():
    handle = FloxServiceHandle.load()
    handle.stop()
    print("Development services stopped")

Complete Flox Example

import angreal
from angreal.integrations.flox import Flox, flox_required

@angreal.command(name="status", about="Show environment status")
def status():
    flox = Flox(".")
    print(f"Flox version: {Flox.version()}")
    print(f"Environment: {flox.path}")
    print(f"Exists: {flox.exists}")
    print("\nServices:")
    for svc in flox.services.status():
        pid_info = f"PID {svc.pid}" if svc.pid else "stopped"
        print(f"  {svc.name}: {svc.status} ({pid_info})")

@angreal.command(name="test", about="Run tests")
@flox_required(".", services=["postgres"])
def test():
    import subprocess
    return subprocess.run(["pytest", "-v"]).returncode

Combining Integrations

import angreal
from angreal.integrations.git import Git
from angreal.integrations.docker import DockerCompose
from angreal.integrations.venv import VirtualEnv

@angreal.command(name="ci", about="Run CI pipeline locally")
def ci():
    git = Git()

    # Ensure clean state
    _, _, status = git.status(short=True)
    if status.strip():
        print("Commit or stash changes first")
        return 1

    # Start services
    dc = DockerCompose("docker-compose.test.yml")
    dc.up(detach=True, build=True)

    try:
        # Run tests in venv
        with VirtualEnv(".venv", requirements="requirements-test.txt") as venv:
            import subprocess
            result = subprocess.run(["pytest", "-v"])
            return result.returncode
    finally:
        dc.down(volumes=True)

Score

Total Score

65/100

Based on repository quality metrics

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

0/10
説明文

100文字以上の説明がある

+10
人気

GitHub Stars 100以上

0/15
最近の活動

1ヶ月以内に更新

+10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

+5

Reviews

💬

Reviews coming soon