Skip to content

Synthesize

Synthesize is a DAG-shaped workflow runner for local development.

In Synthesize, a flow is a graph (potentially disjoint) of nodes, each of which runs a recipe whenever one of that node's triggers activates. Synthesize has a wide variety of triggers:

  • Recipe B should run after recipe A runs.
  • Recipe W should run every time file F changes.
  • Recipe R should be restarted if it ever exits.
  • Recipe O should run once when the flow starts.

These can all coexist as part of same flow, and can even be combined for a single recipe, allowing for complex nodes like "restart recipe W if it exits or if file F changes".

Features

  • Nodes naturally run concurrently based on their triggers.
  • Recipe and trigger definitions can be factored out and shared across multiple nodes and flows.
  • Recipes are just shell commands, so you can use any tools you'd like. Synthesize works with your existing tools, it doesn't replace them.
  • Recipes can be parameterized with arguments (each recipe is actually a Jinja template) and environment variables. Arguments and environment variables can also be provided at the flow and recipe levels (most specific wins).
  • Nodes can have multiple triggers, allowing you to express complex triggering conditions.
  • All command output is combined in a single output stream, with each node's output prefixed with a timestamp and its name.
  • The current time and the status of each node is displayed at the bottom of your terminal.
  • You can generate Mermaid diagrams of your flows for debugging and documentation.

Examples

As an example, here is Synthesize's own synth.yaml configuration file:

flows:
  dev:
    description: run tests, type-checking, and docs dev server
    nodes:
      tests:
        recipe: tests
        triggers:
        - code-changes
      types:
        recipe: types
        triggers:
        - code-changes
      docs:
        recipe: docs
        triggers:
        - delay: 1
        - watch: ["docs/hooks/"]

recipes:
  tests:
    commands: |
      uv run pytest

  types:
    commands: |
      uv run mypy

  docs:
    commands: |
      uv run mkdocs serve --strict

triggers:
  code-changes:
    watch:
    - src/
    - tests/
    - docs/examples/
    - docs/hooks/
    - pyproject.toml
    - .coveragerc
flowchart TD
  tests(tests)
  w_bb19edcd7b45bfcfbfcab4096798a339f9610617[("src/
tests/
docs/examples/
docs/hooks/
pyproject.toml
.coveragerc")]
  w_bb19edcd7b45bfcfbfcab4096798a339f9610617 -->|👁| tests
  types(types)
  w_bb19edcd7b45bfcfbfcab4096798a339f9610617 -->|👁| types
  docs(docs)
  docs -->|∞ 1s| docs
  w_f8192994ea74c4d2311ffd6eb936c7c30e96617f[("docs/hooks/")]
  w_f8192994ea74c4d2311ffd6eb936c7c30e96617f -->|👁| docs

Installation

Add Synthesize as a development dependency in your project:

uv add --dev synthesize

Then use uv run synth --help to see what's available.

Synthesize does not work on Windows

We recommend using the Windows Subsystem for Linux (WSL) to run Synthesize on Windows.

Inspirations