Skip to content

Commit 13b68ea

Browse files
authored
ci: enhance PR lint and add PR guidelines to CONTRIBUTING.md (#6564)
1 parent d61e134 commit 13b68ea

2 files changed

Lines changed: 151 additions & 34 deletions

File tree

.github/workflows/pr-check.yml

Lines changed: 55 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -15,55 +15,82 @@ jobs:
1515
runs-on: ubuntu-latest
1616

1717
steps:
18-
- name: Check PR title format
18+
- name: Validate PR title and description
1919
uses: actions/github-script@v7
2020
with:
2121
script: |
2222
const title = context.payload.pull_request.title;
23-
23+
const body = context.payload.pull_request.body;
2424
const errors = [];
25+
const warnings = [];
26+
27+
const allowedTypes = ['feat','fix','refactor','docs','style','test','chore','ci','perf','build','revert'];
28+
const knownScopes = [
29+
'framework','chainbase','actuator','consensus','common','crypto','plugins','protocol',
30+
'net','db','vm','tvm','api','jsonrpc','rpc','http','event','config',
31+
'block','proposal','trie','log','metrics','test','docker','version',
32+
'freezeV2','DynamicEnergy','stable-coin','reward','lite','toolkit'
33+
];
2534
26-
// Title should not be empty or too short
35+
// 1. Title length check
2736
if (!title || title.trim().length < 10) {
2837
errors.push('PR title is too short (minimum 10 characters).');
2938
}
30-
31-
// Title should not exceed 72 characters
32-
if (title.length > 72) {
39+
if (title && title.length > 72) {
3340
errors.push(`PR title is too long (${title.length}/72 characters).`);
3441
}
3542
36-
// Title should follow conventional format: type: description
37-
// Allowed types: feat, fix, refactor, docs, style, test, chore, ci, perf, build, revert
38-
const conventionalRegex = /^(feat|fix|refactor|docs|style|test|chore|ci|perf|build|revert)(\(.+\))?:\s.+/;
39-
if (!conventionalRegex.test(title)) {
43+
// 2. Conventional format check
44+
const conventionalRegex = /^(feat|fix|refactor|docs|style|test|chore|ci|perf|build|revert)(\([^)]+\))?:\s\S.*/;
45+
if (title && !conventionalRegex.test(title)) {
4046
errors.push(
41-
'PR title must follow conventional format: `type: description`\n' +
42-
'Allowed types: feat, fix, refactor, docs, style, test, chore, ci, perf, build, revert\n' +
43-
'Example: `feat: add new transaction validation`'
47+
'PR title must follow conventional format: `type(scope): description`\n' +
48+
' Allowed types: ' + allowedTypes.map(t => `\`${t}\``).join(', ') + '\n' +
49+
' Example: `feat(tvm): add blob opcodes`'
4450
);
4551
}
4652
47-
if (errors.length > 0) {
48-
const message = '### PR Title Check Failed\n\n' + errors.map(e => `- ${e}`).join('\n');
49-
core.setFailed(message);
50-
} else {
51-
core.info('PR title format is valid.');
53+
// 3. No trailing period
54+
if (title && title.endsWith('.')) {
55+
errors.push('PR title should not end with a period (.).');
5256
}
5357
54-
- name: Check PR description
55-
uses: actions/github-script@v7
56-
with:
57-
script: |
58-
const body = context.payload.pull_request.body;
58+
// 4. Description part should not start with a capital letter
59+
if (title) {
60+
const descMatch = title.match(/^\w+(?:\([^)]+\))?:\s*(.+)/);
61+
if (descMatch) {
62+
const desc = descMatch[1];
63+
if (/^[A-Z]/.test(desc)) {
64+
errors.push('Description should not start with a capital letter.');
65+
}
66+
}
67+
}
68+
69+
// 5. Scope validation (warning only)
70+
if (title) {
71+
const scopeMatch = title.match(/^\w+\(([^)]+)\):/);
72+
if (scopeMatch && !knownScopes.includes(scopeMatch[1])) {
73+
warnings.push(`Unknown scope \`${scopeMatch[1]}\`. See CONTRIBUTING.md for known scopes.`);
74+
}
75+
}
5976
77+
// 6. PR description check
6078
if (!body || body.trim().length < 20) {
61-
core.setFailed(
62-
'### PR Description Check Failed\n\n' +
63-
'PR description is too short or empty. Please describe what this PR does and why.'
64-
);
79+
errors.push('PR description is too short or empty (minimum 20 characters). Please describe what this PR does and why.');
80+
}
81+
82+
// Output warnings
83+
for (const w of warnings) {
84+
core.warning(w);
85+
}
86+
87+
// Output result
88+
if (errors.length > 0) {
89+
const docLink = 'See [CONTRIBUTING.md](https://github.com/' + context.repo.owner + '/' + context.repo.repo + '/blob/develop/CONTRIBUTING.md#pull-request-guidelines) for details.';
90+
const message = '### PR Lint Failed\n\n' + errors.map(e => `- ${e}`).join('\n') + '\n\n' + docLink;
91+
core.setFailed(message);
6592
} else {
66-
core.info('PR description is valid.');
93+
core.info('PR lint passed.');
6794
}
6895
6996
build:

CONTRIBUTING.md

Lines changed: 96 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ Here are some guidelines to get started quickly and easily:
1616
- [Commit Messages](#Commit-Messages)
1717
- [Branch Naming Conventions](#Branch-Naming-Conventions)
1818
- [Pull Request Guidelines](#Pull-Request-Guidelines)
19+
- [PR Title Format](#PR-Title-Format)
20+
- [Type and Scope Reference](#Type-and-Scope-Reference)
21+
- [PR Description](#PR-Description)
1922
- [Special Situations And How To Deal With Them](#Special-Situations-And-How-To-Deal-With-Them)
2023
- [Conduct](#Conduct)
2124

@@ -172,8 +175,12 @@ The message header is a single line that contains succinct description of the ch
172175
* refactor (refactoring production code)
173176
* test (adding or refactoring tests. no production code change)
174177
* chore (updating grunt tasks etc. no production code change)
178+
* ci (CI/CD configuration)
179+
* perf (performance improvement)
180+
* build (build system changes)
181+
* revert (reverting a previous commit)
175182
176-
The `scope` can be anything specifying place of the commit change. For example:`protobuf`,`api`,`test`,`docs`,`build`,`db`,`net`.You can use * if there isn't a more fitting scope.
183+
The `scope` can be anything specifying place of the commit change. For example: `framework`, `api`, `tvm`, `db`, `net`. For a full list of scopes, see [Type and Scope Reference](#type-and-scope-reference). You can use `*` if there isn't a more fitting scope.
177184
178185
The subject contains a succinct description of the change:
179186
1. Limit the subject line, which briefly describes the purpose of the commit, to 50 characters.
@@ -204,13 +211,96 @@ If the purpose of this submission is to modify one issue, you need to refer to t
204211
4. Use `feature/` as the prefix of the `feature` branch, briefly describe the feature in the name, and connect words with underline (e.g., feature/new_resource_model, etc.).
205212
### Pull Request Guidelines
206213
214+
#### PR Title Format
215+
216+
PR titles must follow the conventional commit format and will be checked by CI:
217+
218+
```
219+
type(scope): description
220+
```
221+
222+
| Rule | Requirement |
223+
|------|-------------|
224+
| Format | `type: description` or `type(scope): description` |
225+
| Length | 10 ~ 72 characters |
226+
| Type must be one of | `feat` `fix` `refactor` `docs` `style` `test` `chore` `ci` `perf` `build` `revert` |
227+
228+
#### Type and Scope Reference
229+
230+
**Type Reference**
231+
232+
| Type | Purpose | Example |
233+
|------|---------|---------|
234+
| `feat` | New feature | `feat(tvm): add blob opcodes` |
235+
| `fix` | Bug fix | `fix(db): improve resource management` |
236+
| `docs` | Documentation only | `docs: fix formatting issues in README` |
237+
| `style` | Code style (no logic change) | `style: fix import order and line length` |
238+
| `refactor` | Code refactoring (no behavior change) | `refactor(config): simplify parameters` |
239+
| `test` | Adding or updating tests | `test(vm): add unit tests for opcodes` |
240+
| `chore` | Build tooling, dependencies, etc. | `chore(version): bump to v4.7.8` |
241+
| `ci` | CI/CD configuration | `ci: add PR check workflow` |
242+
| `perf` | Performance improvement | `perf(trie): optimize query performance` |
243+
| `build` | Build system changes | `build: add aarch64 support for RocksDB` |
244+
| `revert` | Reverting a previous commit | `revert: restore ApiUtilTest.java` |
245+
246+
**Module Scopes**
247+
248+
| Scope | Description |
249+
|-------|-------------|
250+
| `framework` | Core framework, services, APIs, RPC interfaces |
251+
| `chainbase` | Blockchain storage, state management, database layer |
252+
| `actuator` | Transaction execution engine, smart contract operations |
253+
| `consensus` | Consensus mechanism (DPoS, PBFT) |
254+
| `common` | Common utilities, configuration, shared infrastructure |
255+
| `crypto` | Cryptographic functions, key management, signatures |
256+
| `plugins` | Node tools (Toolkit, ArchiveManifest, database plugins) |
257+
| `protocol` | Protocol definitions, protobuf messages, gRPC contracts |
258+
259+
**Functional Domain Scopes**
260+
261+
| Scope | Description | Example |
262+
|-------|-------------|---------|
263+
| `net` | P2P networking, message handling, peer sync | `feat(net): optimize sync logic` |
264+
| `db` | Database operations, queries, persistence | `fix(db): handle null pointer in query` |
265+
| `vm` / `tvm` | Virtual machine, bytecode execution, EIP impl | `feat(tvm): implement eip-7823` |
266+
| `api` | HTTP/gRPC API endpoints | `fix(api): handle null response` |
267+
| `jsonrpc` | JSON-RPC interface (Ethereum-compatible) | `fix(jsonrpc): support blockHash param` |
268+
| `rpc` | gRPC services and methods | `fix(rpc): handle timeout correctly` |
269+
| `http` | HTTP server and endpoints | `feat(http): add new endpoint` |
270+
| `event` | Event logging and event service | `feat(event): optimize concurrent writes` |
271+
| `config` | Configuration management, feature flags | `refactor(config): simplify parameters` |
272+
| `block` | Block processing, validation, structure | `fix(block): validate block header` |
273+
| `proposal` | On-chain governance proposals | `feat(proposal): add Osaka proposal` |
274+
| `trie` | Merkle tree, state trie operations | `perf(trie): optimize tree query` |
275+
| `log` | Application logging | `refactor(log): reduce noise` |
276+
| `metrics` | Performance monitoring, Prometheus | `feat(metrics): add Prometheus support` |
277+
| `test` | Test infrastructure and utilities | `test(proposal): add unit test cases` |
278+
| `docker` | Docker containerization and deployment | `feat(docker): add ARM64 support` |
279+
| `version` | Version and release management | `chore(version): bump to v4.7.8` |
280+
281+
**Feature Scopes**
282+
283+
| Scope | Description |
284+
|-------|-------------|
285+
| `freezeV2` | Resource delegation / freeze-unfreeze V2 mechanism |
286+
| `DynamicEnergy` | Dynamic energy pricing mechanism |
287+
| `stable-coin` | Stable coin features and operations |
288+
| `reward` | Block producer rewards distribution |
289+
| `lite` | Lite fullnode functionality |
290+
| `toolkit` | Node maintenance tools (Toolkit.jar) |
291+
292+
#### PR Description
293+
294+
- PR description must not be empty, minimum **20 characters**.
295+
- Should explain **what** the PR does and **why**.
296+
297+
#### General Rules
298+
207299
1. Create one PR for one issue.
208300
2. Avoid massive PRs.
209-
3. Write an overview of the purpose of the PR in its title.
210-
4. Write a description of the PR for future reviewers.
211-
5. Elaborate on the feedback you need (if any).
212-
6. Do not capitalize the first letter.
213-
7. Do not put a period (.) in the end.
301+
3. Elaborate on the feedback you need (if any).
302+
4. Do not capitalize the first letter of the description.
303+
5. Do not put a period (.) at the end of the title.
214304
215305
216306

0 commit comments

Comments
 (0)