Configuration as Code, use YAML to write automated workflows that run on Deno, with any Deno modules, Typescript/Javascript codes
APACHE-2.0 License
使用denoflow,你将可以使用人体工学设计的yaml配置文件来编写自动化工作流程,简单而强大,在Deno上运行,可以使用任何Deno模块、Typescript/Javascript代码。Denoflow会按照你的要求执行你的工作流。可以把它看作是某种配置即代码工具。
项目仍处于非常早期的阶段,谨慎使用!
理想的结果是拥有一些UI界面,使用rjsf等json schema语言来渲染界面的工具生成一个web gui,gui可以帮助我们生成yaml配置。
理想的运行环境是使用云无服务器平台,或CI平台,如Github Actions,Gitlab CI,使用Deno自我托管,或任何Docker运行时。
Deno Deploy 目前还不支持,因为Deno Deploy不支持运行字符串生成的代码。
查看Github Actions的作为运行时的例子: test.yml
加入Discord聊天频道,讨论Denoflow!
稳定的Deno Land的版本见Denoflow
现在可以通过Online Playground来在线尝试和探索 Denoflow
我之前写过actionsflow,必须在github actions中运行,或者本地启用Docker运行,实际上经验来说,这对于大多数人想要的轻量级工作流来说太重了,我发现Deno的特性使得它非常适合做基于yaml配置的灵活工作流。所以,Denoflow的设计特别轻量,甚至没有常驻的功能,必须由外部的东西去运行它,比如github actions的trigger,或者无服务器的定时触发,http触发等。
先安装Deno
从Hacker News的API获取数据,发送到某个webhook的示例:
mkdir workflows
touch workflows/fetch.yml
sources:
- from: https://deno.land/x/[email protected]/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/[email protected]/mod.ts
use: post
args:
- https://enyvb91j5zjv9.x.pipedream.net/
- ${{ctx.item}}
- headers:
'Content-Type': 'application/json'
打开: https://requestbin.com/r/enyvb91j5zjv9/23eNPamD4DK4YK1rfEB1FAQOKIj , 查看实时的webhook请求
deno run --allow-read --allow-net --allow-write --allow-env --allow-run https://deno.land/x/denoflow/cli.ts run
或者简单点,直接给予所有权限(不建议生产环境这样做):
deno run -A https://deno.land/x/denoflow/cli.ts run
Denoflow默认将扫描
workflows
目录并运行所有有效的.yml
文件。
Denoflow的仓库最新版本:
https://denopkg.com/denoflow/denoflow@main/cli.ts
。
你也可以在线体验该示例
如果你更喜欢用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)}}
在 Online Playground 体验该实例
发送RSS源最新文章到Discord频道消息的示例:
touch workflows/rss.yml
sources:
- from: https://deno.land/x/[email protected]/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}) }}
或者,如果你更喜欢原生一点的方法:
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 run
在 Online Playground尝试该示例
一个简单的通过脚本生成数据的例子:
sources:
- run: return [{id:"1"}]
force: true
steps:
- run: console.log("item",ctx.item);
在Online Playground尝试该示例
更多的例子在workflows目录下,你也可以在这里提交你的工作流
sources?
:获取数据的地方,Source[]
,可以是一个或多个来源。每个源都应该返回一个对象数组,比如[{"id":"1"}]
,item的key可以通过key
指定
from
? : 从url
或file path
里导入ts/js脚本。use
? : 从上面的from
中运行moduleName
,或者如果没有提供from
的话,比如fetch
,将会被当作全局函数,args
数组将被传递给函数作为参数,返回值将被附加到ctx.result
和ctx.sources[index].result
,如果use
是一个Class类,那么ctx.result
将是该类的实例。 use
也可以是Deno.cwd
的东西,用来调用Deno提供的函数。run
? :运行ts/js代码,你可以在这里处理use
结果。返回一个可以被字符串化为json的结果。返回值将被附加到ctx.result
和ctx.sources[index].result
。itemsPath
? : 结果中对象数组的路径,如https://test.owenyoung.com/slim.json
中将是hits
。key
? : 识别对象的唯一key,如https://test.owenyoung.com/slim.json
中的objectID
,如果不提供,将使用id
,denoflow将对id进行去重。reverse?',
boolean', 是否需要反向排序数组filter?
, string
, 脚本代码,应处理ctx.item
,并且返回true
或false
。cmd
: string
, 在所有其他任务之后执行一个shell命令,返回值将附加到ctx.cmdResult
和ctx.sources[index].cmdResult
。post?
:后置脚本代码,你可以在这里做一些检查、清理的事情,或者改变ctx.state
。filter
? 从所有合并的sources的items
中过滤,应该处理ctx.items
,预期返回一个新的boolean[]
。
from
? : 从url
或file path
导入ts/js脚本。use
? : 从上面的from
中运行moduleName
,或者如果没有提供from
的话,比如fetch
,将会被当作全局函数,args
数组将被传递给函数作为参数,返回值将被附加到ctx.result
和ctx.sources[index].result
,如果use
是一个Class类,那么ctx.result
将是该类的实例。 use
也可以是Deno.cwd
的东西,用来调用Deno提供的函数。run
?:运行ts/js代码,你可以在这里处理use
的结果。处理ctx.items
,预期返回一个新的boolean[]
,标志哪个项目将被使用。例如,run: return ctx.items.map(item => item.title.value.includes('test'))
。cmd
? : string
,在所有其他任务之后执行一个shell命令,返回值将被附加到ctx.cmdResult
和filter.cmdResult
。post?
:后置脚本代码,你可以在这里做一些检查、清理,改变ctx.state的事情。steps
? 要运行的步骤,Step[]
,可以是一个或多个步骤。
from
? : 从url
或file path
导入脚本。use
? : 从上面的from
中运行moduleName
,或者如果没有提供from
的话,比如fetch
,将会被当作全局函数,args
数组将被传递给函数作为参数,返回值将被附加到ctx.result
和ctx.sources[index].result
,如果use
是一个Class类,那么ctx.result
将是该类的实例。 use
也可以是Deno.cwd
的东西,用来调用Deno提供的函数。run
? : 运行ts/js代码,你可以在这里处理use
结果。返回一个可以被字符串化为json的结果。该结果将被附加到ctx.step[index].result
中。cmd
? : 执行shell命令,将在run
之后运行,结果将被附加到ctx.step[index].cmdResult
。post?
:后置脚本代码,你可以在这里做一些检查、清理的事情,改变ctx.statepost
? 最后的后脚本代码,在所有步骤完成后运行,你可以在这里做一些检查、清洁的事情。你可以在这里使用所有steps
里的的参数。你也可以直接把Denoflow安装到本机:
deno install -n denoflow --allow-read --allow-net --allow-write --allow-run --allow-env https://deno.land/x/denoflow/cli.ts
然后就可以使用 denoflow run
, 或者 denoflow run <files>
来运行你的工作流文件。
更新到最新版本:
deno cache --reload https://deno.land/x/denoflow/cli.ts
命令行参数:
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 message
YAML语法:
如果你还不熟悉YAML语法的话,可以花5分钟熟悉一下:
你可以在任何字段中使用${{variable}}
来向你的工作流插入变量,我们在所有可以使用脚本的地方都注入了ctx
全局变量,利用ctx去访问任何需要的数据,比如说:
steps:
- if: ${{ctx.items.lengh>10}}
run: console.log(ctx.item);
所有的ctx
变量,可以参考接口配置文件
你可以简单地使用ctx.state
来获取或设置状态,例如。
let currentState = ctx.state || {};
let sent = ctx.state.send || [];
if(sent.includes(ctx.item.id)){
sent.push(ctx.item.id)。
}
ctx.state = {
sent
};
// denoflow将为你保存状态,接下来你可以读取它
默认配置,state将以json
格式保存到data
文件夹。你也可以使用sqlite来存储数据。在工作流配置文件中设置database: sqlite://data.sqlite
即可
因为denoflow是为无服务器设计的,很简单,所以它本身不能运行定时工作流。但是你可以使用cron
或其他触发器来触发Denoflow的工作流。比如说使用cron
:
*/15 * * * deno run --allow-read --allow-net --allow-write --allow-env --allow-run https://deno.land/x/denoflow/cli.ts run workflows/schedule15.yml
像上面一样,denoflow不能直接处理webhook,你可以把webhook转发给denoflow,比如使用github actions的例子。
Webhook.yml
:
sources:
- run: return [ctx.env.event]
force: true
steps:
- run: console.log("item",ctx.item);
.github/workflows/webhook.yml
:
name: Webhook
on:
repository_dispatch:
workflow_dispatch:
jobs:
denoflow:
runs-on: ubuntu-latest
concurrency: denoflow
steps:
- name: Check out repository code
uses: actions/checkout@v2
- uses: denoland/setup-deno@v1
with:
deno-version: v1.x
- env:
event: ${{toJSON(github.event)}}
run: deno run --allow-read --allow-net --allow-write --allow-env --allow-run https://deno.land/x/denoflow/cli.ts run workflows/webhook.yml
Denoflow还处于早期的阶段,如果你有任何建议,非常欢迎issue和pull request。谢谢!