Spry LogoOpsfolio
Core Concepts

Code Cells

Executable units within a Spryfile

Code cells are the executable units within a Spryfile.

What is a Code Cell?

A code cell is a fenced code block that Spry can execute or materialize. The fence info string contains metadata that tells Spry how to handle the code.

Syntax

```language identity --flags { json5-attributes }
code content
```

Components

Language - The interpreter/handler (e.g., bash, sql, python)

Identity - Unique name for this cell (e.g., setup-env)

Flags - Command-line style options (e.g., --dep other-task)

Attributes - JSON5 object with additional config (e.g., { timeout: 30 })

Parsing Order

The info string is parsed in this order:

  1. Language - First token (required)
  2. Identity - Second token if not a flag (optional)
  3. Flags - POSIX-style options
  4. Attributes - JSON5 object at the end

Examples

Basic Shell Task

```bash greet
echo "Hello, World!"
```

With Description

```bash greet --descr "Display a greeting message"
echo "Hello, World!"
```

With Dependencies

```bash process-data --dep fetch-data --descr "Process the fetched data"
python process.py data.json
```

With Multiple Flags

```bash build --dep test --descr "Build the project" --capture ./build.log
npm run build
```

With JSON5 Attributes

```bash long-task --descr "Task with timeout" { timeout: 300, retry: 3 }
./long-running-script.sh
```

Cell Types

Spry classifies cells into two natures based on their language:

Executable Cells

Languages that run as processes via the unified spawn framework:

LanguageAliasesEngineDescription
shellbash, sh, zsh, fishOS ShellShell scripts
pwshpowershellOS ShellPowerShell scripts
cmdOS ShellWindows command scripts
sqlSQL ShellSQL via psql, sqlite3, or duckdb
pythonpyShebangPython scripts
typescripttsDenoTypeScript via Deno
javascriptjsDenoJavaScript via Deno
deno-taskDenoDeno task runner

Executable cells:

  • Run via lib/spawn execution framework
  • Capture stdout/stderr
  • Return exit codes
  • Can be awaited by dependent tasks
  • Interpolation is OFF by default

Execution Engines

Spry uses a unified execution framework with distinct engine types:

Engine TypeExamplesDescription
OS Shellbash, sh, pwsh, cmd, fishOperating system shells
SQL Shellpsql, sqlite3, duckdbDatabase CLI tools
Functionenv, envrcIn-process execution (no subprocess)
UsingExternal referencesReference to catalog-defined resources

The engine resolves the runtime based on the language and available tools.

Materializable Cells

Languages that produce content for external systems:

LanguageDescription
sqlSQL statements (for SQLPage, databases)
htmlHTML markup
jsonJSON data
yamlYAML configuration
env, envrcEnvironment variable files
utf8Binary/blob content

Materializable cells:

  • Content is stored or emitted (filesystem, database, etc.)
  • Interpolation is ON by default
  • Can be injected into other cells via PARTIAL
  • Used with SQLPage and other playbooks

Function Engine Languages

Some languages use in-process function engines rather than spawning external processes:

LanguagePurpose
envEnvironment variable files
envrcdirenv configuration files

These are processed by FunctionEngine implementations in lib/spawn/function-shell.ts. They:

  • Execute in-process (no subprocess spawned)
  • Echo input directly to output or perform TypeScript-based processing
  • Are treated as materializable with memoizeOnly behavior
  • Behave identically to other engines from the caller's perspective

Capture-Only Languages

These languages are never executed, only captured:

LanguagePurpose
envEnvironment variable files
envrcdirenv configuration files

Directive Cells

Special cells that configure behavior:

DirectivePurpose
PARTIALTemplate fragment for injection
DEFAULTSSet default flags for cells
contribute expandExpand compact specs into multiple code blocks

Contribute Expand

The contribute expand directive allows you to declare multiple code blocks in a compact format:

```contribute expand
sql core-ddl -X prime --include ../../src/core-ddl.sqlite.sql
sql info-schema -X prime --include ../../src/info-schema.sqlite.sql
```

This expands into equivalent individual code blocks:

```sql core-ddl -X prime --include ../../src/core-ddl.sqlite.sql
```

```sql info-schema -X prime --include ../../src/info-schema.sqlite.sql
```

You can also set a default body with the REMARKS directive:

```contribute expand
REMARKS select 1 as placeholder;
sql a --include ./a.sql
sql b --include ./b.sql
```

Lines starting with #, //, or -- are treated as comments.

Cell Identity

Every executable cell needs a unique identity:

```bash my-unique-name
echo "I can be referenced as 'my-unique-name'"
```

Identity Rules

  • Must be unique within the document
  • Should be descriptive (build-frontend not step1)
  • Use kebab-case by convention
  • Used in --dep references

Common Flags

--descr

Add a description to the task:

```bash setup --descr "Initialize the development environment"
npm install
```

Type: String
Default: None

--dep, -D

Declare dependencies on other tasks (can be repeated):

```bash deploy --dep build --dep test
kubectl apply -f deployment.yaml
```
```bash deploy -D build -D test
kubectl apply -f deployment.yaml
```

Type: String (task identity), repeatable
Default: None

--capture, -C

Capture task output to a file or memory:

```bash get-version --capture ./version.txt
git describe --tags
```
```bash get-value -C
echo "computed result"
```
```bash get-data --capture mykey
curl https://api.example.com/data
```

Type: Path (starting with ./) or memory key
Default: None

When the path starts with ./, it's treated as a relative filesystem path. Otherwise, it's treated as an in-memory capture key.

--gitignore

Add captured file to .gitignore:

```bash generate-secrets --capture ./secrets.env --gitignore
openssl rand -hex 32
```

Type: Boolean flag or string
Default: false
Requires: --capture with a file path

--interpolate, -I

Enable template interpolation (off by default for executables):

```bash greet -I
echo "Hello, ${NAME}!"
```

Type: Boolean flag
Default: false for executables, true for materializables

--injectable, -J

Mark as available for PARTIAL injection:

```sql create-table --injectable
CREATE TABLE users (id INT PRIMARY KEY);
```

Type: Boolean flag
Default: false

--executable, -X

Mark a materializable cell as also executable:

```sql setup-db -X
CREATE TABLE IF NOT EXISTS users (id INT);
```

Type: Boolean flag
Default: false

--silent

Suppress output during execution:

```bash background-task --silent
./noisy-script.sh 2>/dev/null
```

Type: Boolean flag
Default: false

--graph, -G

Assign to a named graph for selective execution:

```bash deploy-step -G deploy
kubectl apply -f manifests/
```

Type: String, repeatable
Default: None

Tasks with a graph can be run selectively:

spry rb run --graph deploy

--include

Include external file content into the cell. Multiple --include flags can be specified to concatenate multiple files:

```sql setup --include ./schema.sql --include ./seed-data.sql
-- Additional SQL after includes
```

Type: String (file path), repeatable
Default: None

Supported path formats:

  • Relative paths: --include ./path/to/file.sql
  • Absolute paths: --include /absolute/path/file.sql
  • Quoted paths with spaces: --include "./path with spaces/file.sql"

The included files are loaded and concatenated in order.

--conf

Specify a configuration file for the task:

```bash complex-task --conf ./config.json
./run-with-config.sh
```

Type: String (file path)
Default: None

JSON5 Attributes

Extended configuration after flags:

```bash task --descr "Description" { timeout: 300, retry: 3 }
./script.sh
```

Common Attributes

AttributeTypeDescription
routeobjectSQLPage route configuration
timeoutnumberTimeout in seconds
retrynumberNumber of retry attempts
retryDelaynumberSeconds between retries
envobjectAdditional environment variables
workdirstringWorking directory

SQLPage Route Attributes

For SQLPage materializables:

```sql index.sql { route: { caption: "Home", icon: "home" } }
SELECT 'card' as component;
```

Attribute Examples

```sql dashboard.sql { route: { caption: "Dashboard", icon: "chart-bar", order: 1 } }
SELECT 'shell' as component, 'Dashboard' as title;
```
```bash with-env { env: { DEBUG: "true", LOG_LEVEL: "verbose" } }
./script.sh
```
```bash in-subdir { workdir: "./subproject" }
npm run build
```

Interpolation

Cells can include interpolated values:

```bash deploy -I
echo "Deploying version ${VERSION} to ${ENVIRONMENT}"
kubectl set image deployment/app app=${IMAGE}:${VERSION}
```

Interpolation sources:

  • Environment variables: ${env.VAR_NAME}
  • Frontmatter values: ${config.key}
  • Captured outputs: ${TASK_NAME}
  • Memory stores: ${memory.key}

Interpolation Defaults

  • Executable cells have interpolation OFF by default (use -I to enable)

Anonymous Cells

Cells without identity are not directly executable but may still be processed:

```bash
# This cell has no identity
# It won't appear in task lists
echo "Anonymous cell"
```

Anonymous cells are useful for:

  • Display-only code examples
  • Non-runnable documentation
  • Code that shouldn't be in the workflow

Flag Shortcuts

Quick reference for commonly used flag shortcuts:

ShortcutFull Flag
-C--capture
-D--dep
-G--graph
-B--branch
-H--hook
-I--interpolate
-J--injectable
-X--executable

Complete Examples

Minimal Cell

```bash hello
echo "Hello"
```

Fully Specified Cell

```bash deploy-production --descr "Deploy to production environment" --dep build --dep test --capture ./logs/deploy.log --gitignore -G deploy { route: { caption: "Deploy" } }
kubectl apply -f k8s/production/
kubectl rollout status deployment/myapp --timeout=300s
```

SQL with Interpolation

```sql report --descr "Generate daily report" -I
SELECT * FROM events
WHERE date >= '${START_DATE}'
  AND date <= '${END_DATE}'
ORDER BY date DESC;
```

PARTIAL with Injection

```sql PARTIAL common-header --inject **/*.sql
SELECT 'shell' as component,
       '${APP_NAME}' as title,
       'database' as icon;
```

Chain of Dependencies

```bash step-1 --descr "First step"
echo "Step 1"
```

```bash step-2 --dep step-1 --descr "Second step"
echo "Step 2"
```

```bash step-3 --dep step-2 --descr "Third step" --capture ./result.txt
echo "Final result"
```

Environment File

```envrc prepare-env -C ./.envrc --gitignore --descr "Generate .envrc file"
export SPRY_DB="sqlite://app.db?mode=rwc"
export PORT=9227
```

Multiple File Includes

```sql init-database --include ./schema/tables.sql --include ./schema/indexes.sql --include ./data/seed.sql
-- Additional setup after includes
SELECT 'Database initialized' as status;
```

Recursive Task

```bash converge-config --recurse --descr "Converge configuration until stable"
# This task re-runs until output stabilizes
process-config.sh
```

Contribute Expand Block

```contribute expand
sql schema-core -X --include ./src/core.sql
sql schema-ext -X --include ./src/extensions.sql
sql seed-data -X --include ./data/seed.sql
```

Validation

Spry validates cell arguments for:

  • Valid flag names
  • Required values for flags that need them
  • Dependency references to existing tasks
  • No circular dependencies
  • Unique identities within a document
spry axiom inspect myfile.md
spry rb issues myfile.md

Best Practices

tag

Always name executable cells

Use descriptive identities for all executable cells

message-square

Add descriptions

Explain what each task does with --descr

git-branch

Explicit dependencies

Don't rely on file order, use --dep flags

code

Be careful with interpolation

Only enable when needed for executables

layers

Use appropriate languages

Match the task to the right language handler

network

Use graphs for organization

Group related tasks with --graph

How is this guide?

Last updated on

On this page