Skip to main content

Drupal Recipe Development Guide

Drupal Recipe Development Guide

Separation of Duties: See Separation of Duties - Drupal recipes are responsible for Drupal site configuration. They do NOT own agent manifests, execution, or OSSA spec.

Complete guide for developing and managing Drupal recipes for the LLM Platform.

Table of Contents

Overview

Drupal recipes provide a standardized way to install and configure Drupal sites with predefined sets of modules, configuration, and content. The LLM Platform uses recipes to deploy consistent AI-powered environments.

Key Concepts

  • Recipe: A YAML-based blueprint for site setup
  • Sub-Recipe: Modular recipe components that can be composed
  • Recipe Onboarding: Module for managing recipe deployment
  • Validation: Automated checks for recipe integrity

Recipe Structure

Basic Recipe

recipe_name/
 recipe.yml                 # Main recipe definition
 README.md                  # Documentation
 config/                    # Configuration files
    install/              # Config to import on install
    update/               # Config updates
 content/                   # Default content
    node/
 modules/                   # Custom modules for this recipe
 subrecipes/               # Modular sub-recipes
    core/
    api/
    security/
 tests/                    # Recipe tests
     RecipeTest.php

Recipe Definition (recipe.yml)

name: 'Recipe Name' description: 'Clear description of what this recipe provides' type: 'Site' version: '1.0.0' # Module dependencies install: - drupal:system - drupal:user - drupal:node - ai:ai - ai_agents:ai_agents - mcp_registry:mcp_registry - recipe_onboarding:recipe_onboarding # Configuration to apply config: import: # Core system config system.site: name: 'LLM Platform' mail: 'admin@example.com' slogan: 'AI-Powered Enterprise Platform' # Module-specific config ai.settings: default_provider: 'openai' cache_enabled: true actions: # Set module config ai.provider.openai.settings: model: 'gpt-4' temperature: 0.7 # Content to create content: node: - type: page title: 'Welcome' body: value: '<p>Welcome to the LLM Platform</p>' format: 'full_html' # Roles and permissions roles: ai_user: label: 'AI User' permissions: - 'access ai interface' - 'execute ai workflows' # Sub-recipes to apply recipes: - './subrecipes/core' - './subrecipes/security' - './subrecipes/api'

Development Workflow

1. Recipe Location

Source Location (edit here):

$LLM_ROOT/all_drupal_custom/recipes/recipe_name/

Deployment Location:

$LLM_ROOT/llm-platform/recipes/recipe_name/

2. Create New Recipe

# Manual creation mkdir -p $LLM_ROOT/all_drupal_custom/recipes/my_recipe cd $LLM_ROOT/all_drupal_custom/recipes/my_recipe # Create recipe.yml cat > recipe.yml << 'EOF' name: 'My Recipe' description: 'Recipe description' type: 'Site' version: '1.0.0' install: [] EOF # Create README cat > README.md << 'EOF' # My Recipe Description of recipe functionality. EOF

3. Apply Recipe

# Apply recipe to site drush recipe recipes/my_recipe # Apply with specific options drush recipe recipes/my_recipe --no-cache --verbose

4. Validation

# Validate recipe structure drush recipe:validate recipes/my_recipe # Via recipe_onboarding module drush recipe-onboarding:validate recipes/my_recipe

Sub-Recipes

Sub-recipes provide modular components that can be composed into larger recipes.

Sub-Recipe Structure

subrecipes/
 core/
    recipe.yml
    config/
 security/
    recipe.yml
    config/
 api/
     recipe.yml
     config/

Core Sub-Recipe Example

# subrecipes/core/recipe.yml name: 'LLM Core Foundation' description: 'Core LLM platform modules and configuration' type: 'Configuration' version: '1.0.0' install: - drupal:system - drupal:user - drupal:node - drupal:field - drupal:serialization - drupal:rest - drupal:jsonapi config: import: system.performance: cache: page: max_age: 3600 css: preprocess: true js: preprocess: true

Security Sub-Recipe Example

# subrecipes/security/recipe.yml name: 'LLM Security Framework' description: 'Security hardening and compliance' type: 'Configuration' version: '1.0.0' install: - security_review:security_review - seckit:seckit - password_policy:password_policy - autologout:autologout - tfa:tfa config: import: seckit.settings: seckit_xss: csp: checkbox: true policy: "default-src 'self'" seckit_csrf: origin: true password_policy.policy.admin: label: 'Admin Password Policy' password_length: 12 character_types: 3

API Management Sub-Recipe

# subrecipes/api/recipe.yml name: 'API Management' description: 'REST, GraphQL, and API tooling' type: 'Configuration' version: '1.0.0' install: - drupal:rest - drupal:jsonapi - drupal:serialization - graphql:graphql - graphql_compose:graphql_compose - openapi_ui:openapi_ui - openapi_ui_redoc:openapi_ui_redoc - simple_oauth:simple_oauth - api_normalization:api_normalization config: import: rest.settings: bc_entity_resource_permissions: false link_domain: ~

Validation

Recipe Onboarding Module

The recipe_onboarding module provides comprehensive validation:

# Install recipe_onboarding drush en recipe_onboarding -y # Validate recipe drush recipe-onboarding:validate recipes/my_recipe # List available recipes drush recipe-onboarding:list # Get recipe info drush recipe-onboarding:info recipes/my_recipe

Validation Checks

  1. Syntax: YAML is valid
  2. Structure: Required fields present
  3. Dependencies: Modules exist and are available
  4. Config: Configuration schemas are valid
  5. Content: Content types and fields exist
  6. Sub-Recipes: Referenced sub-recipes exist

Manual Validation

# Check YAML syntax php -r "yaml_parse_file('recipe.yml');" # Verify modules exist drush pm:list --type=module | grep module_name # Test config import drush config:import --partial --source=recipes/my_recipe/config/install

Deployment

1. Recipe Testing

# Test on fresh install lando rebuild -y drush si minimal -y drush recipe recipes/my_recipe drush uli # Verify installation drush pm:list --type=module --status=enabled drush config:status

2. Production Deployment

# Export recipe to production rsync -av recipes/my_recipe/ production:/var/www/html/recipes/my_recipe/ # Apply on production ssh production "cd /var/www/html && drush recipe recipes/my_recipe"

3. CI/CD Integration

# .gitlab-ci.yml test-recipe: stage: test script: - composer install - drush si minimal -y - drush recipe recipes/my_recipe - drush status

Examples

LLM Platform Recipe

The main platform recipe with sub-recipes:

# recipes/llm_platform/recipe.yml name: 'LLM Platform' description: 'Complete LLM Platform installation with AI agents, workflows, and APIs' type: 'Site' version: '1.0.0' # Sub-recipes provide modular components recipes: - './subrecipes/llm_core_foundation' - './subrecipes/llm_security_framework' - './subrecipes/llm_model_integration' - './subrecipes/api_management' - './subrecipes/ecosystem_orchestration' - './subrecipes/llm_user_workflows' - './subrecipes/llm_langflow_integration' # Main recipe modules install: - ai_agentic_workflows:ai_agentic_workflows - ai_agents_orchestra:ai_agents_orchestra - mcp_registry:mcp_registry - code_executor:code_executor - gov_compliance:gov_compliance config: import: system.site: name: 'LLM Platform' mail: 'admin@llm.local' slogan: 'Enterprise AI Platform' roles: llm_admin: label: 'LLM Administrator' permissions: - 'administer llm platform' - 'manage ai agents' - 'configure mcp registry'

Secure Drupal Recipe

Security-focused recipe:

# recipes/secure_drupal/recipe.yml name: 'Secure Drupal' description: 'Hardened Drupal with security best practices' type: 'Site' version: '1.0.0' install: - security_review:security_review - seckit:seckit - password_policy:password_policy - tfa:tfa - autologout:autologout - login_security:login_security - paranoia:paranoia config: import: seckit.settings: seckit_xss: csp: checkbox: true seckit_various: from_origin: true referrer_policy: true password_policy.policy.default: label: 'Default Password Policy' password_length: 14 character_types: 4 autologout.settings: timeout: 900 max_timeout: 1800

Recipe Management Commands

Drush Commands

# List all recipes drush recipe:list # Apply recipe drush recipe recipes/my_recipe # Validate recipe drush recipe:validate recipes/my_recipe # Export current config as recipe drush recipe:export --name=my_recipe --destination=recipes/ # Show recipe info drush recipe:info recipes/my_recipe # Rollback recipe (if supported) drush recipe:rollback recipes/my_recipe

Recipe Onboarding Commands

# Enable recipe onboarding drush en recipe_onboarding -y # List recipes drush recipe-onboarding:list # Deploy recipe with sub-recipe support drush recipe-onboarding:deploy recipes/llm_platform # Validate complex recipe drush recipe-onboarding:validate recipes/llm_platform --strict # Show recipe tree drush recipe-onboarding:tree recipes/llm_platform

Best Practices

1. Modular Design

Use sub-recipes for reusable components:

recipes/
 llm_platform/           # Main platform recipe
    recipe.yml
    subrecipes/
        core/          # Core functionality
        security/      # Security hardening
        api/          # API management
        ai/           # AI integration
 secure_drupal/         # Security-focused recipe
     recipe.yml

2. Version Control

Track recipe changes in git:

cd recipes/my_recipe git add recipe.yml config/ README.md git commit -m "feat: add AI provider configuration"

3. Documentation

Include comprehensive README:

# Recipe Name ## Purpose What this recipe provides ## Requirements - Drupal ^10.3 || ^11 - PHP 8.1+ - Required modules ## Installation drush recipe recipes/my_recipe ## Configuration Post-installation steps ## Troubleshooting Common issues and solutions

4. Testing

Test recipes thoroughly:

# Test on fresh install lando rebuild -y drush si minimal -y drush recipe recipes/my_recipe # Verify functionality drush pm:list --status=enabled drush config:status drush watchdog:show

Troubleshooting

Common Issues

Missing Dependencies

# Error: Module 'example' not found # Fix: Add to composer.json and install composer require drupal/example

Config Import Errors

# Error: Unknown configuration schema # Fix: Clear cache and retry drush cr drush recipe recipes/my_recipe

Sub-Recipe Not Found

# Error: Sub-recipe './subrecipes/core' does not exist # Fix: Verify path is relative to recipe.yml recipes: - './subrecipes/core' # Relative path

Resources

See Also