进阶篇|有了这招,用文本编辑器搞前端代码都能保证格式统一
2022-04-11 comyan
进阶篇|有了这招,用文本编辑器搞前端代码都能保证格式统一
作者:comyan 发布时间:2022-04-11 10:00:00

上一篇文章中,结合节点库已有节点,建木CI以一个相对简单的流程DSL为大家展示了prettier节点的基本用法。

通过预览管道,可以看出流程总共被分成三个部分:

  1. 将代码从远程仓库克隆下来
  2. 对整个项目文件全部进行格式化
  3. 将格式化后的代码重新推送到远程仓库

确实,如果只单纯的对远程仓库中的代码进行格式化,上次的流程完全可以解决。但如果将它运用到真正的项目开发过程中的话那就显得差点意思。对于上一篇的流程来说,它只会在push代码到远程仓库才被触发,并且每次流程被执行时,prettier节点都会全盘的对这个项目下的所有文件进行格式化,即使对于一个没有任何改动的文件也是如此。

prettier前端代码格式化流程在项目中的应用

在解决上面流程中提到的不足点之前,首先我们要搞清楚在真实项目开发过程中,哪些场景下是有必要做代码格式化的?其实在大多情况下,通过Git代码托管平台例如:gitee、github来进行代码管理时,我们只需要保证每一次push的代码以及在合并Pull Request(以下简称pr)时原分支的代码都是符合统一规范的代码即可。

  • 为项目的webhook添加pr事件钩子

在上篇文章中我们为仓库配置了webhook,并指定了在push操作后触发流程。为了达到上述需求,我们还需要在仓库webhooks的选择事件里勾选Pull Request。这样在仓库进行pr操作时也会触发格式化流程。

  • 利用jsonpath、pr-file-diff节点提取需要进行格式化的文件路径信息

该如何避免在每一次流程中,prettier节点对整个项目进行格式化?参考prettier节点的使用说明可以知道,输入参数files中里可以具体指定需要格式化的文件,节点内部会循环查找对应的文件并格式化。如何才能拿到这些信息呢?不用着急,基于节点库中已有的jsonpathpr-file-diff节点,现在可以非常方便的在push跟pr操作中提取到文件的变动信息。

复制下面的dsl,对之前代码格式化流程做一个升级。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
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两个场景进行测试

  • 本地主分支push代码

等待流程执行成功,可以看见这次prettier节点只对有变化的文件进行了代码格式化。

  • 本地非主分支push代码

在comyan分支下修改222.js文件

可以看见代码被推送到comyan分支下,但是此次操作并没有触发流程。

通过查看项目的webhook请求记录,可以发现这次请求状态是失败的。点击详情具体看看是什么原因造成的?

查看触发器参数值可以发现,流程only表达式中指定的gitee_ref跟此次webhook请求返回的gitee_ref不匹配,流程执行被限制。

  • 新建pr合并不同分支代码

流程执行pr分支

等待流程执行成功,刷新页面,可以发现在此次pr的提交里新增了一条refactor: auto format code记录。

之前在comyan分支下修改的文件在提交pr的时被自动格式化

经过上面3个场景的演示,你可能能够理解为什么在流程中我们对非主分支下push代码的操作做了条件限制。在通过新建pr对不同分支进行代码合并时,流程会对原分支代码格式化。加上这种机制,它可以减少流程被触发的次数。