From 8d8378804ff914640cb9048491a4a23d4eef72d3 Mon Sep 17 00:00:00 2001 From: vividcoder Date: Fri, 27 Feb 2026 23:12:55 +0800 Subject: [PATCH] ci: enhance PR lint with stricter title and scope checks - Add no trailing period rule for PR title - Add no capitalized description rule - Add known scope validation (warning only, non-blocking) - Add CONTRIBUTING.md link in error messages - Merge title and description checks into a single step - Update CONTRIBUTING.md with complete commit type, scope reference, and PR title/description requirements --- .github/workflows/pr-check.yml | 83 ++++++++++++++++++--------- CONTRIBUTING.md | 102 ++++++++++++++++++++++++++++----- 2 files changed, 144 insertions(+), 41 deletions(-) diff --git a/.github/workflows/pr-check.yml b/.github/workflows/pr-check.yml index 731bfbbce90..7601447fd2f 100644 --- a/.github/workflows/pr-check.yml +++ b/.github/workflows/pr-check.yml @@ -15,55 +15,82 @@ jobs: runs-on: ubuntu-latest steps: - - name: Check PR title format + - name: Validate PR title and description uses: actions/github-script@v7 with: script: | const title = context.payload.pull_request.title; - + const body = context.payload.pull_request.body; const errors = []; + const warnings = []; + + const allowedTypes = ['feat','fix','refactor','docs','style','test','chore','ci','perf','build','revert']; + const knownScopes = [ + 'framework','chainbase','actuator','consensus','common','crypto','plugins','protocol', + 'net','db','vm','tvm','api','jsonrpc','rpc','http','event','config', + 'block','proposal','trie','log','metrics','test','docker','version', + 'freezeV2','DynamicEnergy','stable-coin','reward','lite','toolkit' + ]; - // Title should not be empty or too short + // 1. Title length check if (!title || title.trim().length < 10) { errors.push('PR title is too short (minimum 10 characters).'); } - - // Title should not exceed 72 characters - if (title.length > 72) { + if (title && title.length > 72) { errors.push(`PR title is too long (${title.length}/72 characters).`); } - // Title should follow conventional format: type: description - // Allowed types: feat, fix, refactor, docs, style, test, chore, ci, perf, build, revert - const conventionalRegex = /^(feat|fix|refactor|docs|style|test|chore|ci|perf|build|revert)(\(.+\))?:\s.+/; - if (!conventionalRegex.test(title)) { + // 2. Conventional format check + const conventionalRegex = /^(feat|fix|refactor|docs|style|test|chore|ci|perf|build|revert)(\([^)]+\))?:\s.+/; + if (title && !conventionalRegex.test(title)) { errors.push( - 'PR title must follow conventional format: `type: description`\n' + - 'Allowed types: feat, fix, refactor, docs, style, test, chore, ci, perf, build, revert\n' + - 'Example: `feat: add new transaction validation`' + 'PR title must follow conventional format: `type(scope): description`\n' + + ' Allowed types: ' + allowedTypes.map(t => `\`${t}\``).join(', ') + '\n' + + ' Example: `feat(tvm): add blob opcodes`' ); } - if (errors.length > 0) { - const message = '### PR Title Check Failed\n\n' + errors.map(e => `- ${e}`).join('\n'); - core.setFailed(message); - } else { - core.info('PR title format is valid.'); + // 3. No trailing period + if (title && title.endsWith('.')) { + errors.push('PR title should not end with a period (.).'); } - - name: Check PR description - uses: actions/github-script@v7 - with: - script: | - const body = context.payload.pull_request.body; + // 4. Description part should not start with a capital letter + if (title) { + const descMatch = title.match(/^\w+(?:\([^)]+\))?:\s*(.+)/); + if (descMatch) { + const desc = descMatch[1]; + if (/^[A-Z]/.test(desc)) { + errors.push('Description should not start with a capital letter.'); + } + } + } + + // 5. Scope validation (warning only) + if (title) { + const scopeMatch = title.match(/^\w+\(([^)]+)\):/); + if (scopeMatch && !knownScopes.includes(scopeMatch[1])) { + warnings.push(`Unknown scope \`${scopeMatch[1]}\`. See CONTRIBUTING.md for known scopes.`); + } + } + // 6. PR description check if (!body || body.trim().length < 20) { - core.setFailed( - '### PR Description Check Failed\n\n' + - 'PR description is too short or empty. Please describe what this PR does and why.' - ); + errors.push('PR description is too short or empty (minimum 20 characters). Please describe what this PR does and why.'); + } + + // Output warnings + for (const w of warnings) { + core.warning(w); + } + + // Output result + if (errors.length > 0) { + const docLink = 'See [CONTRIBUTING.md](https://github.com/' + context.repo.owner + '/' + context.repo.repo + '/blob/develop/CONTRIBUTING.md#pull-request-guidelines) for details.'; + const message = '### PR Lint Failed\n\n' + errors.map(e => `- ${e}`).join('\n') + '\n\n' + docLink; + core.setFailed(message); } else { - core.info('PR description is valid.'); + core.info('PR lint passed.'); } build: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 79bf8567a61..c61b00d6afc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -16,6 +16,8 @@ Here are some guidelines to get started quickly and easily: - [Commit Messages](#Commit-Messages) - [Branch Naming Conventions](#Branch-Naming-Conventions) - [Pull Request Guidelines](#Pull-Request-Guidelines) + - [PR Title Format](#PR-Title-Format) + - [PR Description](#PR-Description) - [Special Situations And How To Deal With Them](#Special-Situations-And-How-To-Deal-With-Them) - [Conduct](#Conduct) @@ -165,15 +167,70 @@ Template: The message header is a single line that contains succinct description of the change containing a `commit type`, an optional `scope` and a subject. `commit type` describes the kind of change that this commit is providing: -* feat (new feature) -* fix (bug fix) -* docs (changes to documentation) -* style (formatting, missing semi colons, etc. no code change) -* refactor (refactoring production code) -* test (adding or refactoring tests. no production code change) -* chore (updating grunt tasks etc. no production code change) -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. +| Type | Purpose | Example | +|------|---------|---------| +| `feat` | New feature | `feat(tvm): add blob opcodes` | +| `fix` | Bug fix | `fix(db): improve resource management` | +| `docs` | Documentation only | `docs: fix formatting issues in README` | +| `style` | Code style (no logic change) | `style: fix import order and line length` | +| `refactor` | Code refactoring (no behavior change) | `refactor(config): simplify parameters` | +| `test` | Adding or updating tests | `test(vm): add unit tests for opcodes` | +| `chore` | Build tooling, dependencies, etc. | `chore(version): bump to v4.7.8` | +| `ci` | CI/CD configuration | `ci: add PR check workflow` | +| `perf` | Performance improvement | `perf(trie): optimize query performance` | +| `build` | Build system changes | `build: add aarch64 support for RocksDB` | +| `revert` | Reverting a previous commit | `revert: restore ApiUtilTest.java` | + +The `scope` is optional and specifies the place or module of the commit change. + +#### Module Scopes + +| Scope | Description | +|-------|-------------| +| `framework` | Core framework, services, APIs, RPC interfaces | +| `chainbase` | Blockchain storage, state management, database layer | +| `actuator` | Transaction execution engine, smart contract operations | +| `consensus` | Consensus mechanism (DPoS, PBFT) | +| `common` | Common utilities, configuration, shared infrastructure | +| `crypto` | Cryptographic functions, key management, signatures | +| `plugins` | Node tools (Toolkit, ArchiveManifest, database plugins) | +| `protocol` | Protocol definitions, protobuf messages, gRPC contracts | + +#### Functional Domain Scopes + +| Scope | Description | Example | +|-------|-------------|---------| +| `net` | P2P networking, message handling, peer sync | `feat(net): optimize sync logic` | +| `db` | Database operations, queries, persistence | `fix(db): handle null pointer in query` | +| `vm` / `tvm` | Virtual machine, bytecode execution, EIP impl | `feat(tvm): implement eip-7823` | +| `api` | HTTP/gRPC API endpoints | `fix(api): handle null response` | +| `jsonrpc` | JSON-RPC interface (Ethereum-compatible) | `fix(jsonrpc): support blockHash param` | +| `rpc` | gRPC services and methods | `fix(rpc): handle timeout correctly` | +| `http` | HTTP server and endpoints | `feat(http): add new endpoint` | +| `event` | Event logging and event service | `feat(event): optimize concurrent writes` | +| `config` | Configuration management, feature flags | `refactor(config): simplify parameters` | +| `block` | Block processing, validation, structure | `fix(block): validate block header` | +| `proposal` | On-chain governance proposals | `feat(proposal): add Osaka proposal` | +| `trie` | Merkle tree, state trie operations | `perf(trie): optimize tree query` | +| `log` | Application logging | `refactor(log): reduce noise` | +| `metrics` | Performance monitoring, Prometheus | `feat(metrics): add Prometheus support` | +| `test` | Test infrastructure and utilities | `test(proposal): add unit test cases` | +| `docker` | Docker containerization and deployment | `feat(docker): add ARM64 support` | +| `version` | Version and release management | `chore(version): bump to v4.7.8` | + +#### Feature Scopes + +| Scope | Description | +|-------|-------------| +| `freezeV2` | Resource delegation / freeze-unfreeze V2 mechanism | +| `DynamicEnergy` | Dynamic energy pricing mechanism | +| `stable-coin` | Stable coin features and operations | +| `reward` | Block producer rewards distribution | +| `lite` | Lite fullnode functionality | +| `toolkit` | Node maintenance tools (Toolkit.jar) | + +You can use `*` if there isn't a more fitting scope. The subject contains a succinct description of the change: 1. Limit the subject line, which briefly describes the purpose of the commit, to 50 characters. @@ -204,13 +261,32 @@ If the purpose of this submission is to modify one issue, you need to refer to t 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.). ### Pull Request Guidelines +#### PR Title Format + +PR titles must follow the conventional commit format and will be checked by CI: + +``` +type(scope): description +``` + +| Rule | Requirement | +|------|-------------| +| Format | `type: description` or `type(scope): description` | +| Length | 10 ~ 72 characters | +| Type must be one of | `feat` `fix` `refactor` `docs` `style` `test` `chore` `ci` `perf` `build` `revert` | + +#### PR Description + +- PR description must not be empty, minimum **20 characters**. +- Should explain **what** the PR does and **why**. + +#### General Rules + 1. Create one PR for one issue. 2. Avoid massive PRs. -3. Write an overview of the purpose of the PR in its title. -4. Write a description of the PR for future reviewers. -5. Elaborate on the feedback you need (if any). -6. Do not capitalize the first letter. -7. Do not put a period (.) in the end. +3. Elaborate on the feedback you need (if any). +4. Do not capitalize the first letter of the description. +5. Do not put a period (.) at the end of the title.