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-processorcd my-processorbun init -ybun add @atomicloud/cyan-sdk
mkdir my-processorcd my-processorpython -m venv .venvsource .venv/bin/activate # On Windows: .venv\Scripts\activatepip install cyanprintsdk
mkdir my-processorcd my-processordotnet new consoledotnet add package AtomiCloud.CyanPrint
Create Processor
Create Processor Logic
Create index.ts:
import { StartProcessorWithLambda } from '@atomicloud/cyan-sdk';StartProcessorWithLambda(async (input, fileHelper) => {Entry point - starts the processor server on port 5551const files = fileHelper.resolveAll();Load all files into memory// Transform each filefiles.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:
from cyanprintsdk import start_processor_with_fnfrom cyanprintsdk.domain.processor.input import ProcessorInputfrom cyanprintsdk.domain.core.fs.cyan_fs_helper import CyanFileHelperasync def my_processor(input: ProcessorInput, fileHelper: CyanFileHelper):files = fileHelper.resolve_all()Load all files into memory# Transform each filefor file in files:file.content = file.content.upper()Transform content (example: uppercase)file.write_file()Write to output directoryreturn {"directory": input.write_directory}Return output directorystart_processor_with_fn(my_processor)
Create requirements.txt:
cyanprintsdk>=1.0.0
Create 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 fileforeach (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:
FROM oven/bun:1.1.31WORKDIR /app# Mark as CyanPrint processorLABEL cyanprint.dev=true# Install dependenciesCOPY package.json bun.lockb* ./RUN bun install# Copy processor codeCOPY . .# Run processorCMD ["bun", "run", "index.ts"]
Create Dockerfile:
FROM python:3.11-slimWORKDIR /app# Mark as CyanPrint processorLABEL cyanprint.dev=true# Install dependenciesCOPY requirements.txt .RUN pip install --no-cache-dir -r requirements.txt# Copy processor codeCOPY . .# Run processorCMD ["python", "index.py"]
Create Dockerfile:
FROM mcr.microsoft.com/dotnet/sdk:8.0WORKDIR /app# Mark as CyanPrint processorLABEL cyanprint.dev=true# Install dependenciesCOPY *.csproj .RUN dotnet restore# Copy processor codeCOPY . .# Run processorCMD ["dotnet", "run"]
Understanding the Code
Entry Point
StartProcessorWithLambda receives two arguments:
| Argument | Type | Purpose |
|---|---|---|
input | ProcessorInput | Contains directories, globs, and config |
fileHelper | CyanFileHelper | File operations API |
start_processor_with_fn receives two arguments:
| Argument | Type | Purpose |
|---|---|---|
input | ProcessorInput | Contains directories, globs, and config |
fileHelper | CyanFileHelper | File operations API |
StartProcessor receives two arguments:
| Argument | Type | Purpose |
|---|---|---|
input | CyanProcessorInput | Contains directories, globs, and config |
fileHelper | CyanFileHelper | File operations API |
CyanFileHelper
The file helper provides all file operations:
| Method | Purpose | Memory Usage |
|---|---|---|
resolveAll() | Load all files with content | High |
read(glob) | Load specific files | Medium |
get(glob) | Get references without content | Low |
readAsStream(glob) | Stream large files | Low |
copy(glob) | Copy without loading | Minimal |
VirtualFile
Each file has:
Prop
Type
| Method | Description |
|---|---|
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/templatesecho "hello world" > ../test-template/templates/test.txt
Create ../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:
from cyanprintsdk import start_template_with_fn, GlobTypefrom cyanprintsdk.domain.core.cyan import Cyan, CyanProcessor, CyanGlobasync 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:
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-templatecyanprint 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
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
trycommand - Setting up automated snapshot tests
Next Steps
Learn more about file operations:
- Resolve All Files - Load all files
- Lazy Load Files - Efficient loading
- CyanFileHelper API - Full API reference
- Automated Testing - Snapshot-based testing