迭代代码
In software development, code review is important for maintaining code quality.
在软件开发中,代码审查对于保持代码质量很重要。
To help project maintainers implement code-review policies, GitHub provides a convenient approach, protected branch, which enforces pull requests to satisfy certain review policies before merging.
为了帮助项目维护者实施代码审查策略,GitHub提供了一种便捷的方法, 受保护的分支 ,该分支强制合并请求,以在合并之前满足某些审查策略。
强制执行代码审查策略的缺点 (The Downside of Enforcing Code-Review Policies)
Although protected branches sound like a great plan, in practice, they can be annoying because not all pull requests require human attention.
尽管受保护的分支听起来像是一个伟大的计划,但实际上,它们可能会令人讨厌,因为并非所有拉动请求都需要人工关注。
For example, if I want to update my contact information in a markdown file, there’s no point in asking another developer to review my change.
例如,如果我想更新降价文件中的联系信息,就没有必要要求其他开发人员来审查我的更改。
潜在的解决方法 (Potential Workarounds)
There are a few workarounds:
有一些解决方法:
- Split a monorepo project into subprojects with different review policies 将Monorepo项目拆分为具有不同审核策略的子项目
- Set up branches for less critical changes 为不太重要的更改设置分支
I’ve tried both.
我都尝试过。
The problem with the first approach is the number of repositories grows quickly, and soon it becomes annoying for developers to jump between repositories. Also, it’s hard to document the purpose of each repository.
第一种方法的问题在于,存储库的数量快速增长,很快,开发人员在存储库之间跳转就变得很烦人。 另外,很难记录每个存储库的用途。
For the second option, syncing between branches requires a lot of effort.
对于第二个选项,分支之间的同步需要大量的精力。
Also for both, migrating code in less strict areas to locations under review policy is nontrivial work.
同样对于两者而言,将不那么严格的区域中的代码迁移到受审核策略约束的位置也是一项艰巨的工作。
建立更好的解决方案 (Build a Better Solution)
After dealing with the mess for a while, I decided it was time to resolve this with a systematic approach:
处理了一段时间后,我决定是时候用系统的方法解决这个问题:
It’d be great to have an automated code-review app that lets the repository owner define what pull requests are safe/trivial and automatically approves them and lets the protected-branch approach handle the rest.
拥有一个自动化的代码审查应用程序非常好,它可以使存储库所有者定义哪些请求是安全/重要的,然后自动批准它们,并让受保护的分支机构处理其余请求。
For example, the app can detect that the pull request only modifies files under an area designated just for myself — which could be a directory named with my GitHub username:
例如,应用程序可以检测到拉取请求仅修改了仅为我指定的区域下的文件,该区域可以是使用我的GitHub用户名命名的目录:
It can automatically approve it, saving other developers some time:
它可以自动批准它,从而节省了其他开发人员一些时间:
Sound good to me! How can we make this happen?
对我来说听起来不错! 我们如何做到这一点?
Since the app requires notifications for pull-request activities and needs to interact with GitHub, which aligns with the definition of a GitHub app, I chose to build on top of the GitHub app infrastructure with Probot (a framework that abstracts low-level details of the GitHub app API).
由于该应用程序需要通知请求拉动活动并且需要与GitHub进行交互(这与GitHub应用程序的定义保持一致),因此我选择使用Probot在GitHub应用程序基础架构之上构建(该框架抽象了以下内容的底层细节): GitHub应用API)。
The user defines a configuration file at the root of the repository that describes the rules to determine if a pull request is safe/trivial.
用户在存储库的根目录处定义一个配置文件,该配置文件描述用于确定拉取请求是否安全/无关紧要的规则。
Every time a pull request opens, the app will get a notification and reads the configuration file mentioned above from the repository.
每次打开请求请求时,应用程序都会收到通知,并从存储库中读取上述配置文件。
Then, the app should leave an approval review if the rules apply.
然后,如果规则适用,则该应用应留下批准审核。
设计规则 (Design the rules)
To help developers work simultaneously, the first draft of configuration supports inserting usernames.
为了帮助开发人员同时工作,配置初稿支持插入用户名。
It might sound a bit abstract at first, but let’s explain with an example.
一开始听起来可能有点抽象,但是让我们用一个例子来解释。
For example, the user can define the following configuration to allow pull requests targeting files inside a directory named with the user’s GitHub username:
例如,用户可以定义以下配置,以允许将请求请求定位到以用户的GitHub用户名命名的目录中的文件:
ownership_rules:
directory_matching_rules:
- name: personal projects in experimental
path: playground/{{username}}/**/*
- name: personal documentation
path: docs/personal/{{username}}/**/*
If it’s still a bit unclear, let’s insert a real username into the example.
如果仍然不清楚,让我们在示例中插入一个真实的用户名。
Taking my GitHub account, tianhaoz95
, as an example, if I open a pull request to modify plaground/tianhaoz95/README.md
, it’ll be automatically approved with the configuration above.
以我的GitHub帐户tianhaoz95
为例,如果我打开一个请求请求来修改plaground/tianhaoz95/README.md
,则上面的配置将自动批准它。
实施应用 (Implement the app)
Since the full implementation can get complicated, I’ll only present an overly simplified version in the post.
由于完整的实现可能会变得很复杂,因此在本文中我只会介绍一个过于简化的版本。
For the full source code, please see the repository:
有关完整的源代码,请参见存储库:
1. Subscribe to pull-request activities
1.订阅拉取请求活动
Probot is a convenient framework that maps incoming webhook events to handler functions so we don’t need to do the plumbing.
Probot是一个方便的框架,可将传入的Webhook事件映射到处理程序函数,因此我们无需做管道。
The following code will instruct Probot to subscribe to all pull -equest activities and run the handler function when an activity is detected:
以下代码将指示Probot订阅所有pull -equest活动并在检测到活动时运行处理程序功能:
import { Application } from "probot";
import { maybeApproveChange } from "./core";
export = (app: Application): void => {
app.on(
[
"pull_request.opened",
"pull_request.reopened",
"pull_request.synchronize",
],
async (context) => {
await maybeApproveChange(context);
},
);
};
The function maybeApproveChange
posts an approval review if the pull requests meet the criteria. There are more details on this function in step 5.
如果拉取请求符合条件,则函数maybeApproveChange
发布批准审核。 在步骤5中有关于此功能的更多详细信息。
2. Get a list of modified files from a pull-request event
2.从请求请求事件中获取已修改文件的列表
GitHub provides a convenient API to get a file-change list from pull requests. We can access the API with the following code:
GitHub提供了一个便捷的API,可从请求请求中获取文件更改列表。 我们可以使用以下代码访问API:
export const getChangedFiles = async (context: Context): Promise<string[]> => {
const changedFilesResponse = await context.github.pulls.listFiles(context.repo({
"pull_number": context.payload.pull_request.number,
}));
const changedFiles: string[] = [];
for (const changedFileData of changedFilesResponse.data) {
changedFiles.push(changedFileData.filename);
}
return changedFiles;
};
3. Read the configuration from the repository
3.从存储库中读取配置
Reading standard GitHub configuration files (files inside the .github
directory) is easy thanks to Probot’s abstraction. We can simply call the config
method on the Probot context
to get the parsed yml
configuration:
由于Probot的抽象,读取标准GitHub配置文件( .github
目录中的文件)非常容易。 我们可以简单地在Probot context
调用config
方法来获取已解析的yml
配置:
export const getConfig = async (context: Context): Promise<Config> => {
const rawConfig = await context?.config("approveman.yml");
return parseConfig(rawConfig);
};
The code above reads the content in .github/approveman.yml
, parses the yaml
file, and converts it to a JavaScript object.
上面的代码读取.github/approveman.yml
的内容,解析yaml
文件,并将其转换为JavaScript对象。
4. Compare the list of modified files against the configuration
4.将修改后的文件列表与配置进行比较
To make sure a pull request is safe, all of the files in the pull request need to follow at least one of the rules:
为了确保请求请求是安全的,请求请求中的所有文件都必须至少遵循以下规则之一:
const matchRule = (rule: DirectoryMatchingRule,
filename: string, info: UserInfo, context: Context): boolean => {
const renderedRule = render(rule.path, info);
return minimatch(filename, renderedRule);
};
const matchOneOfRules = (rules: DirectoryMatchingRule[],
filename: string, info: UserInfo, context: Context): boolean => {
let matchOneOf = false;
for (const rule of rules) {
if (matchRule(rule, filename, info, context)) {
matchOneOf = true;
}
}
return matchOneOf;
};
export const ownsAllFiles = (rules: DirectoryMatchingRule[],
filenames: string[], info: UserInfo, context: Context): boolean => {
let ownsAll = true;
for (const filename of filenames) {
if (!matchOneOfRules(rules, filename, info, context)) {
ownsAll = false;
}
}
return ownsAll;
};
Note:
注意:
- The first function checks if a single file matches a single rule 第一个功能检查单个文件是否匹配单个规则
- The second function checks if a single file matches at least one of the rules 第二个功能检查单个文件是否符合至少一个规则
- The third function check if all files match at least one of the rules 第三个功能检查是否所有文件均符合至少一个规则
5. Maybe approve the pull request
5.也许批准请求请求
After verifying if the pull request is safe, the next step is to approve if it is:
验证拉取请求是否安全之后,下一步是批准:
const approveChange = async (context: Context): Promise<void> => {
const req = context.repo({
"event": "APPROVE" as ReviewEvent,
"pull_number": context.payload.pull_request.number,
});
await context.github.pulls.createReview(req);
};
export const maybeApproveChange = async (context: Context): Promise<void> => {
try {
const changedFiles = await getChangedFiles(context);
const rules = await getConfig(context);
const info = getUserInfo(context);
if (ownsAllFiles(rules, changedFiles, info, context)) {
await approveChange(context);
await createPassingStatus(context);
} else {
await dismissAllApprovals(context);
}
} catch (err) {
await createCrashStatus(context, err);
}
};
Note:
注意:
- The first function sends a request to the GitHub API to add an approval on the pull request 第一个函数将请求发送到GitHub API,以在拉取请求上添加批准
- The second function is a high-level wrapper that uses everything we’ve built from steps 1-4. 第二个功能是高级包装程序,它使用我们从步骤1-4构建的所有内容。
外卖 (Takeaways)
If you, too, have questioned difficult code-review policies, here’s an app for you!
如果您也对困难的代码审查政策提出质疑,那么这里有一个适合您的应用程序!
I’ve met some developers in the open-source community who had complained that the hardcoded code-review policies had slowed things down or raised the hurdle for first-time contributors.
我遇到了一些开源社区的开发人员,他们抱怨硬编码的代码审查策略使事情变慢或增加了初学者的负担。
If you’ve asked the same question, the app I built, ApproveMan, is there for you to improve development efficiency.
如果您提出了相同的问题,那么我为您开发的应用程序ApproveMan可以提高开发效率。
The app is available here:
该应用程序在这里可用:
如果您有更多创造性的方式来配置自动代码审查,欢迎您参与! (If you have more creative ways to configure automatic code review, contributions are welcome!)
Defining safe pull requests with the GitHub username is the first schema I came up with — but definitely not the only one that can be useful.
用GitHub用户名定义安全拉取请求是我想到的第一个模式,但绝对不是唯一有用的模式。
If you have any creative configuration rules, ApproveMan is open source, and your awesome idea is a pull request away.
如果您有任何创造性的配置规则,ApproveMan是开源的,那么您的绝妙主意就是撤消请求。
如果您有关于如何简化开源开发的想法,请考虑将其视为构建GitHub集成的教程。 (If you have ideas on how to streamline open-source development, consider this a tutorial for building GitHub integrations)
Code review isn’t the only place where we can improve our workflow.
代码审查并不是我们可以改善工作流程的唯一途径。
For people who’ve imagined a better workflow, I hope this article, as an end-to-end GitHub integration-building experience, can help you get started.
对于那些曾经设想过更好的工作流程的人们,我希望本文作为端到端GitHub集成构建的经验,可以帮助您入门。
Thanks for reading!
谢谢阅读!
翻译自: https://medium.com/better-programming/iterate-faster-with-automatic-code-review-2138731b55a6
迭代代码