Denoflow
Table of Contents
About
Denoflow is a simple, powerful, serverless automated workflow tool based yaml workflow file and Deno, you can use any Deno modules, Typescript/Javascript code to run your workflow. Denoflow will execute your workflow as you want.
Itās still at a very early stage, use with care!
The Ideal result is having some app schemas, using rjsf to render a web gui, gui can help us to generate yaml configs.
The Ideal runtime is using cloud serverless platform, or CI platform, like Github Actions, Gitlab CI, self-hosted with Deno, or any Docker runtime.
Deno Deploy is not supported yet, because it doesnāt support Code generation from strings, Error Detail:
Code generation from strings disallowed for this contextSee Github Actions Example: test.yml
Now we can only write yaml by ourself, and actually, itās not that hard.
Join our Discord chat channel to discuss about Denoflow!
Stable deno land version see Denoflow
Playground
Try and exploring denoflow with the Online Playground
Why Deno?
- Deno uses URLs to import modules, which gives us very flexible and convenient workflow scripting capabilities, and you can use any module you want very easily.
- Deno is based on modern features of the JavaScript language, and officially provides a standard library that covers most of the commonly used features in workflows.
- Denoās default zero permissions design, which is important for workflow security, so that you can give your workflows only the permissions they need.
I have written actionsflow software, is must run in github actions, or local Docker, for workflow is too heavy, I found Deno features make it more suitable for doing flexible workflow based on yaml configuration, hope Denoflow can become a simple but powerful workflow assistant.
Prerequisites
Install Deno first.
Getting Started
Fetch from Hacker News API to webhook example:
mkdir workflowstouch workflows/fetch.ymlsources:
- from: https://deno.land/x/axiod@0.24/mod.ts
use: get
args:
- https://test.owenyoung.com/slim.json
itemsPath: data.hits
key: objectID
limit: 1
steps:
- run: console.log('item', ctx.item)
# Open: <https://requestbin.com/r/enyvb91j5zjv9/23eNPamD4DK4YK1rfEB1FAQOKIj> , See live webhook request.
- from: https://deno.land/x/axiod@0.24/mod.ts
use: post
args:
- https://enyvb91j5zjv9.x.pipedream.net/
- ${{ctx.item}}
- headers:
'Content-Type': 'application/json'Open: https://requestbin.com/r/enyvb91j5zjv9/23eNPamD4DK4YK1rfEB1FAQOKIj , See live webhook request.
deno run --allow-read --allow-net --allow-write --allow-env --allow-run https://deno.land/x/denoflow/cli.ts runOr simplly with all permissions:
deno run -A https://deno.land/x/denoflow/cli.ts run
It will scan the
workflowsdirectory and run all valid.ymlfiles.
latest version:
https://denopkg.com/denoflow/denoflow@main/cli.ts
If you prefer to use fetch:
sources:
- use: fetch
args:
- https://test.owenyoung.com/slim.json
run: return ctx.result.json()
itemsPath: hits
key: objectID
limit: 1
steps:
- use: fetch
args:
- https://enyvb91j5zjv9.x.pipedream.net/
- method: POST
headers:
Content-Type: application/json
body: ${{JSON.stringify(ctx.item)}}
RSS Feed to Discord Webhook Message
touch workflows/rss.ymlsources:
- from: https://deno.land/x/denoflow@0.0.19/sources/rss.ts
args:
- https://actionsflow.github.io/test-page/hn-rss.xml
limit: 1
steps:
- use: fetch
args:
- ${{env.DISCORD_WEBHOOK}}
- method: POST
headers:
Content-Type: application/json
body: ${{ JSON.stringify({content:ctx.item.title.value}) }}Or, if you prefer more raw way:
sources:
- use: fetch
args:
- https://actionsflow.github.io/test-page/hn-rss.xml
run: |
const rss = await import("https://deno.land/x/rss/mod.ts");
const xml = await ctx.result.text();
const feed = await rss.parseFeed(xml);
return feed.entries;
limit: 1
steps:
- use: fetch
args:
- ${{ctx.env.DISCORD_WEBHOOK}}
- method: POST
headers:
'Content-Type': 'application/json'
body: ${{ JSON.stringify({content:ctx.item.title.value}) }}deno run --allow-read --allow-net --allow-write --allow-run --allow-env https://deno.land/x/denoflow/cli.ts runA simple script generated exmaple:
sources:
- run: return [{id:"1"}]
force: true
steps:
- run: console.log("item",ctx.item);More examples are in workflows directory, you can submit your awesome workflows.
Try Online Playground to explore workflow.
Life Cycle
sources?: where to fetch the data,Source[], can be one or more sources. Every source should return an array of items.from?: import ts/js script fromurlorfile pathuse?: runmoduleNamefrom abovefrom, or iffromis not provided, runglobalFunctionlikefetch,argswill be passed to the function, the return value will be attached toctx.resultandctx.sources[index].result, ifuseis a class, thenctx.resultwill be the instance of the class.usecan also beDeno.cwdthings, to call Deno functions.run?: run ts/js code, you can handleuseresult here. Return a result that can be stringified to json. The return value will be attached toctx.resultandctx.sources[index].resultitemsPath?: the path to the items in the result, likehitsinhttps://test.owenyoung.com/slim.jsonkey?: the key to identify the item, likeobjectIDinhttps://test.owenyoung.com/slim.json, if not provided, will useid, denoflow will hash the id, then the same item withidwill be skipped.filter?,string, script code, should handlectx.item-> returntrueorfalsereverse?,boolean, reverse the itemscmd:string, exec a shell command after all other task, the return value will be attached toctx.cmdResultandctx.sources[index].cmdResultpost?: post script code, you can do some check, clean, things here, change ctx.state
filter? filter from all sources items, handlectx.items, expected return a newboolean[],from?: import ts/js script fromurlorfile pathuse?: runmoduleNamefrom abovefrom, or iffromis not provided, runglobalFunctionlikefetch,argswill be passed to the function, the return value will be attached toctx.resultandfilter.result. ifuseis a class, thenctx.resultwill be the instance of the class.run?: run ts/js code, you can handleuseresult here.handlectx.items, expected return a newboolean[], flag which item will be used. e.g.run: return ctx.items.map(item => item.title.value.includes('test'))cmd?:string, exec a shell command after all other task, the return value will be attached toctx.cmdResultandfilter.cmdResultpost?: post script code, you can do some check, clean, things here, change ctx.state
steps? the steps to run,Step[], can be one or more steps.from?: import script fromurlorfile pathuse?: runmoduleNamefrom abovefrom, or iffromis not provided, runglobalFunctionlikefetch,argswill be passed to the function. ifuseis a class, thenctx.resultwill be the instance of the class.run?: run ts/js code, you can handleuseresult here. Return a result that can be stringified to json. the result will be attached to thectx.steps[index].resultcmd?: exec shell commands, will be run afterrun, the result will be attached to thectx.steps[index].cmdResultpost?: post script code, you can do some check, clean, things here, change ctx.statepost?: post script code, you can do some check, clean, things here, change ctx.state
post? final post script code, run after all steps done, you can do some check, clean, things here. You can use all steps params here.
Installing
deno install -n denoflow --allow-read --allow-net --allow-write --allow-run --allow-env https://deno.land/x/denoflow/cli.tsThen, you can run it with denoflow run , or denoflow run <files>
Update to latest version
deno cache --reload https://deno.land/x/denoflow/cli.tsUsage
denoflow/0.0.17
Usage:
$ denoflow run [...files or url]
Options:
--force Force run workflow files, if true, will ignore to read/save state
--debug Debug mode, will print more info
--database Database uri, default json://data
--limit max items for workflow every runs
--sleep sleep time between sources, filter, steps, unit seconds
--stdin read yaml file from stdin, e.g. cat test.yml | denoflow run --stdin
-h, --help Display this messageYAML Syntax
You can use ${{variable}} in any fields to inject variables into your workflow, we inject ctx variable in template and script. For example:
Expressions
steps:
- if: ${{ctx.items.lengh>10}}
run: console.log(ctx.item);All ctx see Syntax in the following doc.
State
You can simply use ctx.state to get or set state, for example:
let currentState = ctx.state || {};
let sent = ctx.state.sent || [];
if(sent.includes(ctx.item.id)){
sent.push(ctx.item.id);
}
ctx.state = {
sent
};
// deno flow will save the state for you , next you can read it.
return;The state will be saved to data folder in json format. You can also use sqlite to store the state. Just set database: sqlite://data.sqlite in your workflow config file.
Syntax
All workflow syntax:
See Interface
Faq
How to schedule a workflow?
Cause denoflow designed for serverless, simple, so it self canāt schedule a workflow. You can use cron or other trigger to schedule a workflow. For example:
*/15 * * * * deno run --allow-read --allow-net --allow-write --allow-env --allow-run https://deno.land/x/denoflow/cli.ts run workflows/schedule15.ymlHow to handle a webhook?
Like above, denoflow canāt handle webhook directly, you can forward the webhook to denoflow, For github actions example:
Todo
- Support Sleep Option
- Support GUI generated workflow
- Support
cleancommand - denoflow playground
- support new instance