LogoCyanPrint
ProcessorsTutorials

Your First Processor

Create a simple processor that transforms files

Your First Processor

Create a processor that transforms template files. This tutorial builds a simple uppercase transformer that works with the try command.

Prerequisites

  • Docker installed and running
  • Runtime requirements (choose one):
    • TypeScript: Node.js 18+ or Bun installed
    • Python: Python 3.11+ and pip
    • C#: .NET 8 SDK

Initialize Project

mkdir my-processor
cd my-processor
bun init -y
bun add @atomicloud/cyan-sdk
mkdir my-processor
cd my-processor
python -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
pip install cyanprintsdk
mkdir my-processor
cd my-processor
dotnet new console
dotnet add package AtomiCloud.CyanPrint

Create Processor

Create Processor Logic

Create index.ts:

index.ts
import { StartProcessorWithLambda } from '@atomicloud/cyan-sdk';
StartProcessorWithLambda(async (input, fileHelper) => {
Entry point - starts the processor server on port 5551
const files = fileHelper.resolveAll();
Load all files into memory
// Transform each file
files.forEach(file => {
file.content = file.content.toUpperCase();
Transform content (example: uppercase)
file.writeFile();
Write to output directory
});
return { directory: input.writeDirectory };
Return output directory
});

Create index.py:

index.py
from cyanprintsdk import start_processor_with_fn
from cyanprintsdk.domain.processor.input import ProcessorInput
from cyanprintsdk.domain.core.fs.cyan_fs_helper import CyanFileHelper
async def my_processor(input: ProcessorInput, fileHelper: CyanFileHelper):
files = fileHelper.resolve_all()
Load all files into memory
# Transform each file
for file in files:
file.content = file.content.upper()
Transform content (example: uppercase)
file.write_file()
Write to output directory
return {"directory": input.write_directory}
Return output directory
start_processor_with_fn(my_processor)

Create requirements.txt:

requirements.txt
cyanprintsdk>=1.0.0

Create Program.cs:

Program.cs
using sulfone_helium;
using sulfone_helium.Domain.Core.FileSystem;
using sulfone_helium.Domain.Processor;
CyanEngine.StartProcessor(args, async (input, fileHelper) =>
Entry point - starts the processor server on port 5551
{
var files = fileHelper.ResolveAll();
Load all files into memory
// Transform each file
foreach (var file in files)
{
file.Content = file.Content.ToUpper();
Transform content (example: uppercase)
file.WriteFile();
Write to output directory
}
return new ProcessorOutput { Directory = input.WriteDirectory };
Return output directory
});

Create Dockerfile

Create Dockerfile:

Dockerfile
FROM oven/bun:1.1.31
WORKDIR /app
# Mark as CyanPrint processor
LABEL cyanprint.dev=true
# Install dependencies
COPY package.json bun.lockb* ./
RUN bun install
# Copy processor code
COPY . .
# Run processor
CMD ["bun", "run", "index.ts"]

Create Dockerfile:

Dockerfile
FROM python:3.11-slim
WORKDIR /app
# Mark as CyanPrint processor
LABEL cyanprint.dev=true
# Install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy processor code
COPY . .
# Run processor
CMD ["python", "index.py"]

Create Dockerfile:

Dockerfile
FROM mcr.microsoft.com/dotnet/sdk:8.0
WORKDIR /app
# Mark as CyanPrint processor
LABEL cyanprint.dev=true
# Install dependencies
COPY *.csproj .
RUN dotnet restore
# Copy processor code
COPY . .
# Run processor
CMD ["dotnet", "run"]

Understanding the Code

Entry Point

StartProcessorWithLambda receives two arguments:

ArgumentTypePurpose
inputProcessorInputContains directories, globs, and config
fileHelperCyanFileHelperFile operations API

start_processor_with_fn receives two arguments:

ArgumentTypePurpose
inputProcessorInputContains directories, globs, and config
fileHelperCyanFileHelperFile operations API

StartProcessor receives two arguments:

ArgumentTypePurpose
inputCyanProcessorInputContains directories, globs, and config
fileHelperCyanFileHelperFile operations API

CyanFileHelper

The file helper provides all file operations:

MethodPurposeMemory Usage
resolveAll()Load all files with contentHigh
read(glob)Load specific filesMedium
get(glob)Get references without contentLow
readAsStream(glob)Stream large filesLow
copy(glob)Copy without loadingMinimal

VirtualFile

Each file has:

Prop

Type

MethodDescription
writeFile()Write file to output directory

Test with Try Command

Build the Processor

docker build -t my-processor:dev .

Create a Test Template

Create a simple template that uses your processor:

mkdir -p ../test-template/templates
echo "hello world" > ../test-template/templates/test.txt

Create ../test-template/cyan/index.ts:

../test-template/cyan/index.ts
import { StartTemplateWithLambda, GlobType } from '@atomicloud/cyan-sdk';
StartTemplateWithLambda(async (i, d) => {
return {
processors: [{
name: 'my-processor:dev',
files: [{ root: 'templates', glob: '**/*', exclude: [], type: GlobType.Template }],
config: {}
}]
};
});

Create ../test-template/cyan/index.py:

../test-template/cyan/index.py
from cyanprintsdk import start_template_with_fn, GlobType
from cyanprintsdk.domain.core.cyan import Cyan, CyanProcessor, CyanGlob
async def my_template(i, d):
return Cyan(
processors=[
CyanProcessor(
name="my-processor:dev",
files=[
CyanGlob(
root="templates",
glob="**/*",
exclude=[],
type=GlobType.Template
)
],
config={}
)
]
)
start_template_with_fn(my_template)

Create ../test-template/cyan/Program.cs:

../test-template/cyan/Program.cs
using sulfone_helium;
using sulfone_helium.Domain.Core;
CyanEngine.StartTemplate(args, async (i, d) =>
{
return new Cyan
{
Processors =
[
new CyanProcessor
{
Name = "my-processor:dev",
Files =
[
new CyanGlob
{
Root = "templates",
Glob = "**/*",
Exclude = [],
Type = GlobType.Template
}
],
Config = new { }
}
]
};
});

Run Try Command

cd ../test-template
cyanprint try template . ./output

Check Output

cat ../test-template/output/test.txt
# Output: HELLO WORLD

The processor transformed hello world to HELLO WORLD during the try command execution.

Project Structure

index.ts
package.json
Dockerfile
index.py
requirements.txt
Dockerfile
Program.cs
my-processor.csproj
Dockerfile

Automated Testing

Set up snapshot tests to verify your processor produces consistent output:

cyanprint test processor .

Create a test.cyan.yaml with input fixtures and expected snapshots. See Automated Testing for the full guide.

What You Learned

  • How to create a processor project
  • Using the processor entry point (port 5551)
  • Loading files with resolveAll()
  • Transforming file content
  • Writing files with writeFile()
  • Testing processors with the try command
  • Setting up automated snapshot tests

Next Steps

Learn more about file operations: