在上一篇文章中,结合节点库已有节点,建木 CI 以一个相对简单的流程 DSL 为大家展示了prettier节点的基本用法。
通过预览管道,可以看出流程总共被分成三个部分:
将代码从远程仓库克隆下来
对整个项目文件全部进行格式化
将格式化后的代码重新推送到远程仓库
确实,如果只单纯的对远程仓库中的代码进行格式化,上次的流程完全可以解决。但如果将它运用到真正的项目开发过程中的话那就显得差点意思。对于上一篇的流程来说,它只会在 push 代码到远程仓库才被触发,并且每次流程被执行时,prettier 节点都会全盘的对这个项目下的所有文件进行格式化,即使对于一个没有任何改动的文件也是如此。
prettier 前端代码格式化流程在项目中的应用
在解决上面流程中提到的不足点之前,首先我们要搞清楚在真实项目开发过程中,哪些场景下是有必要做代码格式化的?其实在大多情况下,通过 Git 代码托管平台例如:gitee、github 来进行代码管理时,我们只需要保证每一次 push 的代码以及在合并 Pull Request(以下简称 pr)时原分支的代码都是符合统一规范的代码即可。
在上篇文章中我们为仓库配置了 webhook,并指定了在 push 操作后触发流程。为了达到上述需求,我们还需要在仓库 webhooks 的选择事件里勾选 Pull Request。这样在仓库进行 pr 操作时也会触发格式化流程。
该如何避免在每一次流程中,prettier 节点对整个项目进行格式化?参考prettier节点的使用说明可以知道,输入参数 files 中里可以具体指定需要格式化的文件,节点内部会循环查找对应的文件并格式化。如何才能拿到这些信息呢?不用着急,基于节点库中已有的jsonpath、pr-file-diff节点,现在可以非常方便的在 push 跟 pr 操作中提取到文件的变动信息。
复制下面的 DSL,对之前代码格式化流程做一个升级。
name: prettier节点测试
description: prettier格式化前端代码
trigger:
type: webhook
param:
# webhook响应头中的token值
- name: gitee_token
type: SECRET
exp: $.header.x-gitee-token
# gitee中的webhook钩子事件
- name: gitee_event
type: STRING
exp: $.header.x-gitee-event
# 仓库项目名
- name: project_name
type: STRING
exp: $.body.json.project.name
# 此次提交pr的地址
- name: pr_url
type: STRING
exp: $.body.json.url
# push操作,此次提交的文件修改的信息
- name: trigger_data
type: STRING
exp: $.body.json.commits
# webhook返回的分支信息
- name: gitee_ref
type: STRING
exp: $.body.json.ref
# 提交pr的原分支
- name: source_branch
type: STRING
exp: $.body.json.source_branch
# 提交pr的目标分支
- name: target_branch
type: STRING
exp: $.body.json.target_branch
# 提交的commit信息
- name: commit_message
type: STRING
exp: $.body.json.commits[0].message
# 获取pr操作时的仓库地址
- name: pr_repository_url
type: STRING
exp: $.body.json.source_repo.project.git_http_url
# 获取push操作时的仓库地址
- name: push_repository_url
type: STRING
exp: $.body.json.repository.git_http_url
# 项目的fullName
- name: project_full_name
type: STRING
exp: $.body.json.project.full_name
# pr用户
- name: pr_user
type: STRING
exp: $.body.json.author.name
# push用户
- name: push_user
type: STRING
exp: $.body.json.pusher.name
# 获取pr操作时的仓库html地址
- name: pr_repository_html_url
type: STRING
exp: $.body.json.source_repo.project.html_url
# 获取push操作时的仓库html地址
- name: push_repository_html_url
type: STRING
exp: $.body.json.repository.html_url
# webhook行为描述
- name: action_desc
type: STRING
exp: $.body.json.action_desc
# 控制流程只在对仓库master分支进行push操作和新建pr时触发
only: >
(${trigger.gitee_event} == "Merge Request Hook" && ${trigger.pr_repository_url} != "https://gitee.com/oschina/git-osc" && ${trigger.action_desc} == "open" && ${trigger.pr_repository_url} == ${trigger.push_repository_url} || (${trigger.gitee_event} == "Push Hook" && ${trigger.gitee_ref} == "refs/heads/master" && ${trigger.commit_message} != "refactor: auto format code"))
workflow:
start:
type: start
alias: 开始
targets:
- string_repository_url
- string_repository_branch
string_repository_url:
type: string:1.0.0-nodejs16.13.1
alias: 获取仓库地址
sources:
- start
targets:
- git_clone
param:
expression: >
"${trigger.gitee_event}" === "Merge Request Hook" ? "${trigger.pr_repository_url}" : "${trigger.push_repository_url}"
string_repository_branch:
type: string:1.0.0-nodejs16.13.1
alias: 获取仓库分支
sources:
- start
targets:
- git_clone
param:
expression: >
"${trigger.gitee_event}" === "Merge Request Hook" ? "refs/heads/${trigger.source_branch}" : "${trigger.gitee_ref}"
git_clone:
type: git_clone:1.2.1
alias: 克隆项目
sources:
- string_repository_url
- string_repository_branch
targets:
- isPushRequest
param:
remote_url: ${string_repository_url.result}
ref: ${string_repository_branch.result}
username: ((gitee.comyan_username))
password: ((gitee.comyan_password))
isPushRequest:
type: condition
alias: 是否为push
sources:
- git_clone
expression: ${trigger.gitee_event} == "Push Hook"
cases:
true: jsonpath
false: pr_file_diff
jsonpath:
type: jsonpath:1.0.0
alias: 提取变动的文件路径列表
sources:
- isPushRequest
targets:
- string_push
param:
expression: "$..['modified,added']"
data: '${trigger.trigger_data}'
string_push:
type: string:1.0.0-nodejs16.13.1
alias: 构造prettier输入参数
sources:
- jsonpath
targets:
- condition_push
param:
expression: >-
JSON.stringify(${jsonpath.result}.map(arr=>arr.map(item=> '${git_clone.git_path}' + '/' + item )).flat(2))
condition_push:
sources:
- string_push
alias: 判断文件列表是否为空
type: condition
expression: ${string_push.result}=="[]"
cases:
true: end
false: prettier_push
pr_file_diff:
type: gitee:1.0.0-pr-file-diff
alias: 提取变动的文件路径列表
sources:
- isPushRequest
targets:
- string_pr
param:
access_token: ((gitee.comyan_git_token))
pr_url: ${trigger.pr_url}
string_pr:
type: string:1.0.0-nodejs16.13.1
alias: 构造prettier输入参数
sources:
- pr_file_diff
targets:
- condition_pr
param:
expression: >-
JSON.stringify(JSON.parse('${pr_file_diff.diff}').map(item => "${git_clone.git_path}" + "/" + item.filepath ).filter(item=>item))
condition_pr:
sources:
- string_pr
alias: 判断文件列表是否为空
type: condition
expression: ${string_pr.result}=="[]"
cases:
true: end
false: prettier_pr
prettier_push:
type: prettier:1.0.0-2.5.1
alias: 格式化
sources:
- condition_push
targets:
- git_push
param:
files: ${string_push.result}
config_path: ${git_clone.git_path}/peizhi.js
prettier_pr:
type: prettier:1.0.0-2.5.1
alias: 格式化
sources:
- condition_pr
targets:
- git_push_pr
param:
files: ${string_pr.result}
config_path: ${git_clone.git_path}/peizhi.js
git_push:
type: git_push:1.0.4
alias: push格式化代码
sources:
- prettier_push
targets:
- end
param:
remote_url: ${string_repository_url.result}
remote_branch: ${git_clone.git_branch}
username: ((gitee.comyan_username))
password: ((gitee.comyan_password))
source_path: ${git_clone.git_path}
target_dir: ${trigger.project_name}
commit_message: "refactor: auto format code"
git_push_pr:
type: git_push:1.0.4
alias: push格式化代码
sources:
- prettier_pr
targets:
- end
param:
remote_url: ${string_repository_url.result}
remote_branch: ${trigger.source_branch}
username: ((gitee.comyan_username))
password: ((gitee.comyan_password))
source_path: ${git_clone.git_path}
target_dir: ${trigger.project_name}
commit_message: "refactor: auto format code"
end:
type: end
alias: 结束
sources:
- git_push
- git_push_pr
- condition_push
- condition_pr
复制代码
点击项目的预览流程按钮
从流程预览图中可以看出,push 跟 pr 操作被分为两个不同分支,并且在 prettier 节点被触发之前,我们已对此次操作中存在变动的文件信息做了单独的提取。
接下来分别对 push 和 pr 两个场景进行测试
等待流程执行成功,可以看见这次 prettier 节点只对有变化的文件进行了代码格式化。
在 comyan 分支下修改 222.js 文件
可以看见代码被推送到 comyan 分支下,但是此次操作并没有触发流程。
通过查看项目的 webhook 请求记录,可以发现这次请求状态是失败的。点击详情具体看看是什么原因造成的?
查看触发器参数值可以发现,流程 only 表达式中指定的 gitee_ref 跟此次 webhook 请求返回的 gitee_ref 不匹配,流程执行被限制。
流程执行 pr 分支
等待流程执行成功,刷新页面,可以发现在此次 pr 的提交里新增了一条 refactor: auto format code 记录。
之前在 comyan 分支下修改的文件在提交 pr 的时被自动格式化
经过上面 3 个场景的演示,你可能能够理解为什么在流程中我们对非主分支下 push 代码的操作做了条件限制。在通过新建 pr 对不同分支进行代码合并时,流程会对原分支代码格式化。加上这种机制,它可以减少流程被触发的次数。
本文为建木博主「comyan」的原创投稿文章,转载请联系授权。
项⽬官⽹:https://jianmu.dev
项⽬托管:https://gitee.com/jianmu-dev
项⽬文档:https://docs.jianmu.dev
评论