Skip to content

Latest commit

 

History

History
442 lines (350 loc) · 10.7 KB

File metadata and controls

442 lines (350 loc) · 10.7 KB

Stack Packs

A stack pack teaches depgraf how to handle a specific language/framework combination. This guide covers how stack packs work, how to author new ones, and how to publish them for the community.


What Is a Stack Pack?

A stack pack is a directory containing declarative YAML files that define:

  1. How to detect the stack from project files
  2. How to generate OpenAPI specs using the framework's native tooling
  3. How to extract consumed endpoints, env vars, entities, routes, and test mappings from source code

depgraf ships with 8 built-in packs: python-fastapi, python-django, react-typescript, angular-typescript, java-spring, kotlin-ktor, node-express, node-nestjs. Community packs extend this to any language or framework.


Directory Structure

my-stack-pack/
  detect.yaml                # Stack detection rules
  generate.yaml              # Framework-native generator config
  rules/
    consumed-endpoints.yaml  # ast-grep rules for outbound HTTP calls
    entities.yaml            # ast-grep rules for ORM/data model definitions
    env-vars.yaml            # ast-grep rules for environment variable access
    routes.yaml              # ast-grep rules for route/endpoint definitions
    test-map.yaml            # ast-grep rules for test scenario IDs

All files are optional except detect.yaml. If a rule file is missing, that extraction category is skipped for workspaces using this stack.


Writing Detection Rules

detect.yaml tells depgraf how to identify a workspace using this stack. Detection checks for the presence of indicator files and specific content within them.

Example: Python + FastAPI

id: python-fastapi
name: "Python FastAPI"
type: backend

indicator_files:
  - path: "pyproject.toml"
    contains:
      any_of:
        - section: "[project.dependencies]"
          pattern: "fastapi"
        - section: "[tool.poetry.dependencies]"
          pattern: "fastapi"

  - path: "requirements.txt"
    contains:
      pattern: "fastapi"

priority: 100  # Higher priority wins when multiple stacks match

Example: React + TypeScript

id: react-typescript
name: "React TypeScript"
type: frontend

indicator_files:
  - path: "package.json"
    contains:
      all_of:
        - json_path: "$.dependencies.react-router-dom"
        - json_path: "$.devDependencies.typescript"

  - path: "tsconfig.json"
    exists: true

priority: 100

Detection Fields

Field Required Description
id Yes Unique stack identifier (used in depgraf.yaml)
name Yes Human-readable name
type Yes backend or frontend (affects which extraction rules apply)
indicator_files Yes List of files to check
indicator_files[].path Yes Relative path from workspace root
indicator_files[].exists No Just check if the file exists (no content match)
indicator_files[].contains No Content matching rules
contains.pattern No Substring to find anywhere in the file
contains.any_of No At least one of these patterns must match
contains.all_of No All patterns must match
contains.section No Limit the search to a specific section (TOML/INI)
contains.json_path No Match against a JSON/YAML path
priority No Disambiguation when multiple stacks match (default: 50)

Writing Generator Configs

generate.yaml defines how to produce an OpenAPI spec using the framework's native tooling.

Example: FastAPI

openapi:
  command: "python -c 'from app.main import app; import json; print(json.dumps(app.openapi()))'"
  output: "docs/api/openapi.json"
  timeout: 30  # seconds
  fallback: true

Example: Spring Boot

openapi:
  command: "./gradlew generateOpenApiDocs"
  output: "build/openapi.json"
  timeout: 60
  fallback: true

Example: Frontend (no native generator)

openapi: null  # No OpenAPI generation for frontend stacks

# depgraf will use ast-grep route extraction from rules/routes.yaml instead

Generator Fields

Field Required Description
openapi.command Yes Shell command to run (working directory = workspace root)
openapi.output Yes Where the command writes the OpenAPI spec (relative to workspace)
openapi.timeout No Max execution time in seconds (default: 30)
openapi.fallback No Fall back to ast-grep route extraction on failure (default: true)

The generator command must produce a valid OpenAPI 3.0+ JSON file. If it fails and fallback is true, depgraf falls back to ast-grep extraction using rules/routes.yaml.


Writing ast-grep Extraction Rules

Extraction rules are the core of a stack pack. Each rule file contains one or more ast-grep rules that match specific patterns in source code and transform the matches into structured JSON.

Consumed Endpoints

Find outbound HTTP calls to other services.

Python (requests/httpx):

rules:
  - id: python-httpx-call
    language: Python
    rule:
      kind: call
      has:
        kind: attribute
        pattern: $CLIENT.$METHOD
        has:
          kind: identifier
          regex: "^(get|post|put|patch|delete)$"
          field: attribute
      arguments:
        has:
          kind: string
          pattern: $PATH
    transform:
      method:
        source: $METHOD
        uppercase: true
      path:
        source: $PATH
        strip_quotes: true
      target_service:
        source: $CLIENT
        infer_service: true   # INVENTORY_CLIENT -> inventory-service

TypeScript (fetch/axios):

rules:
  - id: ts-fetch-call
    language: TypeScript
    rule:
      pattern: fetch($URL, { method: $METHOD, $$$REST })
    transform:
      method:
        source: $METHOD
        strip_quotes: true
        uppercase: true
      path:
        source: $URL
        strip_quotes: true
        extract_path: true    # Extract path from full URL or template literal

  - id: ts-axios-call
    language: TypeScript
    rule:
      pattern: $CLIENT.$METHOD($URL, $$$ARGS)
      has:
        kind: member_expression
        has:
          kind: property_identifier
          regex: "^(get|post|put|patch|delete)$"
    transform:
      method:
        source: $METHOD
        uppercase: true
      path:
        source: $URL
        strip_quotes: true

Environment Variables

Find env var access patterns.

Python:

rules:
  - id: python-env-access
    language: Python
    rule:
      any:
        - pattern: os.environ[$KEY]
        - pattern: os.environ.get($KEY, $$$DEFAULT)
        - pattern: os.getenv($KEY, $$$DEFAULT)
    transform:
      name:
        source: $KEY
        strip_quotes: true
      default:
        source: $DEFAULT
        strip_quotes: true
        optional: true

TypeScript:

rules:
  - id: ts-env-access
    language: TypeScript
    rule:
      pattern: process.env.$NAME
    transform:
      name:
        source: $NAME

Entity Schemas

Find ORM model definitions.

Python (SQLAlchemy):

rules:
  - id: python-sqlalchemy-model
    language: Python
    rule:
      kind: class_definition
      has:
        kind: argument_list
        inside:
          kind: class_definition
        has:
          pattern: Base
      has:
        kind: expression_statement
        has:
          kind: assignment
          pattern: __tablename__ = $TABLE
    transform:
      name:
        source: class_name
      table:
        source: $TABLE
        strip_quotes: true

TypeScript (TypeORM):

rules:
  - id: ts-typeorm-entity
    language: TypeScript
    rule:
      kind: class_declaration
      has:
        kind: decorator
        pattern: "@Entity($$$ARGS)"
    transform:
      name:
        source: class_name

Routes

Find route/endpoint definitions.

React Router:

rules:
  - id: react-route
    language: TypeScript
    rule:
      pattern: <Route path=$PATH component={$COMPONENT} $$$PROPS />
    transform:
      path:
        source: $PATH
        strip_quotes: true
      component:
        source: $COMPONENT

FastAPI:

rules:
  - id: fastapi-route
    language: Python
    rule:
      kind: decorated_definition
      has:
        kind: decorator
        pattern: "@$ROUTER.$METHOD($PATH, $$$ARGS)"
    transform:
      method:
        source: $METHOD
        uppercase: true
      path:
        source: $PATH
        strip_quotes: true
      handler:
        source: function_name

Test Mappings

Find test scenario IDs (e.g., TS-001-021) in test files.

rules:
  - id: python-test-scenario-id
    language: Python
    rule:
      kind: comment
      regex: "TS-\\d{3}-\\d{3}"
    transform:
      id:
        regex_extract: "TS-\\d{3}-\\d{3}"
      function:
        source: enclosing_function

Testing Your Stack Pack

1. Create a Fixture

Create a minimal project under tests/fixtures/your-stack/ with:

  • The indicator files for detection
  • Source files containing known patterns (consumed endpoints, env vars, entities)
  • Document the expected extraction output in a comment or companion file

2. Test Detection

cargo test test_detect_your_stack

Verify that depgraf init correctly identifies your stack from the fixture's indicator files.

3. Test Extraction

cargo test test_extract_your_stack

Use snapshot tests (insta) to capture and verify extraction output:

#[test]
fn test_extract_your_stack() {
    let output = run_extract("tests/fixtures/your-stack");
    insta::assert_yaml_snapshot!(output);
}

4. Test Edge Cases

Common edge cases to test:

  • Multi-line function calls
  • String concatenation / template literals for URLs
  • Aliased imports (e.g., import httpx as http)
  • Decorated / annotated methods
  • Nested class definitions (entities with inner classes)
  • Comments and docstrings that look like code

Publishing Community Packs

As a Git Repository

The simplest distribution: push your stack pack to a git repo. Users install it with:

depgraf pack install https://github.com/you/depgraf-pack-ruby-rails.git

The repo root should contain the stack pack files directly (detect.yaml, generate.yaml, rules/).

Naming Convention

Use depgraf-pack-<language>-<framework> for discoverability:

  • depgraf-pack-ruby-rails
  • depgraf-pack-go-chi
  • depgraf-pack-elixir-phoenix

What to Include

  • detect.yaml -- detection rules
  • generate.yaml -- generator config
  • rules/ -- extraction rules for all applicable categories
  • README.md -- what the pack supports, known limitations, example output
  • tests/ -- fixture project and tests (so users can verify it works)
  • LICENSE -- use MIT for maximum compatibility with depgraf's MIT license