Skip to content

Commit 2e1432b

Browse files
authored
Merge pull request #209 from aliansoftwareteam/ci/conventional-commits-lint
ci(lint): enforce Conventional Commits and branch-name conventions
2 parents 80b0776 + f468227 commit 2e1432b

7 files changed

Lines changed: 1209 additions & 22 deletions

File tree

.github/ISSUE_TEMPLATE/config.yml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Disable the "Open a blank issue" link so every report starts from a template.
2+
blank_issues_enabled: false
3+
4+
contact_links:
5+
- name: 📖 Documentation & User Guide
6+
url: https://help.alianhub.com
7+
about: Most usage questions are answered in the docs. Please check here first.
8+
9+
- name: 💬 Live Demo
10+
url: https://demo.alianhub.com
11+
about: Try AlianHub in your browser before opening an issue.
12+
13+
- name: 🛟 GitHub Discussions
14+
url: https://github.com/aliansoftwareteam/AlianHub-Project-Management-System/discussions
15+
about: For questions, ideas, or general discussion — not bug reports.
16+
17+
- name: 🔒 Report a security vulnerability (privately)
18+
url: https://github.com/aliansoftwareteam/AlianHub-Project-Management-System/security/advisories/new
19+
about: Please do NOT open a public issue for security problems. Use a private advisory instead.

.github/pull_request_template.md

Lines changed: 72 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,77 @@
1-
## Pull Request Template Chooser
2-
Please click the link that matches your contribution type to load the correct format.
1+
<!--
2+
🎉 Thanks for contributing to AlianHub!
33
4-
> **Note:** Clicking a link will reload this page and clear any text you've already typed here.
4+
📋 Before opening this PR, please confirm:
5+
1. PR title follows Conventional Commits: <type>(<optional-scope>): <description>
6+
✅ feat(workload-report): add weekly view
7+
✅ fix(auth): correct JWT expiry handling
8+
❌ Added a new report (no type prefix)
9+
❌ feat: Added new report (subject starts with uppercase)
10+
2. Branch name follows <type>/<kebab-description> — see BRANCHING.md
11+
3. Target branch is `staging` (NOT `main` — hotfixes are the only exception)
512
6-
- [**Bug Fix**](?expand=1&template=bug_fix.md)
7-
*Use this for fixing broken logic or UI glitches.*
13+
📝 Want a more detailed template? Open this PR using one of:
14+
• ?expand=1&template=new_feature.md
15+
• ?expand=1&template=bug_fix.md
16+
• ?expand=1&template=refactor.md
17+
-->
818

9-
- [**New Feature**](?expand=1&template=new_feature.md)
10-
*Use this for adding new functionality or components.*
19+
## Summary
1120

12-
- [**Refactor**](?expand=1&template=refactor.md)
13-
*Use this for code cleanup, performance tweaks, or technical debt.*
21+
<!-- 1–3 sentences: what does this PR do, and why? -->
1422

15-
---
16-
### General Summary
17-
*If you don't want to use a specific template, please provide a brief summary of your changes below.*
23+
## Type of change
24+
25+
<!-- Check all that apply. The first one should match your PR title prefix.
26+
Note: `hotfix` is a branch-type only — for PR title and commit messages on
27+
hotfix branches, use `fix` as the type. -->
28+
29+
- [ ] 🚀 `feat` — New feature
30+
- [ ] 🐛 `fix` — Bug fix (use this for `hotfix/*` branches too)
31+
- [ ] ♻️ `refactor` — Code refactor (no behavior change)
32+
- [ ] 🔧 `chore` — Build / config / deps / housekeeping
33+
- [ ] 📘 `docs` — Documentation only
34+
- [ ]`perf` — Performance improvement
35+
- [ ] 🧪 `test` — Adding or fixing tests
36+
- [ ] 🤖 `ci` — CI configuration
37+
- [ ] 📦 `build` — Build system changes
38+
- [ ] 🎨 `style` — Formatting only (no code change)
39+
40+
## Related issue
41+
42+
<!-- Link the issue this PR closes (or relates to). Delete if not applicable. -->
43+
44+
Closes #<issue_number>
45+
46+
## Test plan
47+
48+
<!-- How can a reviewer verify this works? Be concrete with steps and expected results. -->
49+
50+
- [ ]
51+
- [ ]
52+
53+
## Screenshots / demo
54+
55+
<!-- For UI changes: drag-drop screenshots, GIFs, or short videos. Delete if not applicable. -->
56+
57+
## Breaking changes
58+
59+
<!-- Check one. -->
60+
61+
- [ ] ❌ No breaking changes
62+
- [ ] ⚠️ Yes — described below with migration steps
63+
64+
## Checklist
65+
66+
- [ ] PR title follows [Conventional Commits](https://www.conventionalcommits.org/) (`<type>: <description>`)
67+
- [ ] Branch name follows `<type>/<kebab-description>` (see [BRANCHING.md](../BRANCHING.md))
68+
- [ ] PR targets the correct branch (`staging` for most work; `main` only for `hotfix/*` or `release/v*`)
69+
- [ ] All existing tests pass locally (`npm test`)
70+
- [ ] New tests added for new behavior (if applicable)
71+
- [ ] Documentation updated (README / CLAUDE.md / inline comments) where relevant
72+
- [ ] I have performed a self-review of my changes
73+
- [ ] I have read the [Contributing Guidelines](../CONTRIBUTING.md) and [Code of Conduct](../CODE_OF_CONDUCT.md)
74+
75+
## Notes for reviewers
76+
77+
<!-- Anything reviewers should know? Trade-offs, follow-ups, areas needing extra eyes. Delete if not applicable. -->

.github/workflows/branch-name.yml

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
name: Validate branch name
2+
3+
on:
4+
pull_request:
5+
types:
6+
- opened
7+
- reopened
8+
- edited
9+
- synchronize
10+
11+
permissions:
12+
contents: read
13+
pull-requests: read
14+
15+
concurrency:
16+
group: branch-name-${{ github.event.pull_request.number }}
17+
cancel-in-progress: true
18+
19+
jobs:
20+
validate:
21+
name: Branch name matches convention
22+
runs-on: ubuntu-latest
23+
steps:
24+
- name: Check branch name follows <type>/<kebab-description>
25+
env:
26+
BRANCH: ${{ github.head_ref }}
27+
run: |
28+
set -euo pipefail
29+
30+
echo "Validating branch name: $BRANCH"
31+
32+
# Standard topic branches: <type>/<kebab-case-description>
33+
# Types kept in sync with BRANCHING.md § Topic branches.
34+
STANDARD='^(feat|fix|hotfix|refactor|chore|docs|perf|test|ci|build|style)\/[a-z0-9]+(-[a-z0-9.]+)*$'
35+
36+
# Release branches: release/vMAJOR.MINOR.PATCH[-prerelease]
37+
RELEASE='^release\/v[0-9]+\.[0-9]+\.[0-9]+(-[a-z0-9.-]+)?$'
38+
39+
# Hotfix-backport branches: hotfix-backport/<kebab-description>
40+
BACKPORT='^hotfix-backport\/[a-z0-9]+(-[a-z0-9.]+)*$'
41+
42+
# Dependabot / Renovate auto-update branches are exempt
43+
BOT='^(dependabot|renovate)\/.+$'
44+
45+
if [[ "$BRANCH" =~ $STANDARD ]] || \
46+
[[ "$BRANCH" =~ $RELEASE ]] || \
47+
[[ "$BRANCH" =~ $BACKPORT ]] || \
48+
[[ "$BRANCH" =~ $BOT ]]; then
49+
echo "✅ Branch name '$BRANCH' is valid."
50+
exit 0
51+
fi
52+
53+
echo "❌ Branch name '$BRANCH' does not follow the convention."
54+
echo ""
55+
echo "Expected: <type>/<short-kebab-case-description>"
56+
echo ""
57+
echo "Allowed types:"
58+
echo " feat, fix, hotfix, refactor, chore, docs,"
59+
echo " perf, test, ci, build, style"
60+
echo ""
61+
echo "Special-case patterns:"
62+
echo " release/vMAJOR.MINOR.PATCH (release branches)"
63+
echo " hotfix-backport/<description> (backporting hotfixes to staging)"
64+
echo " dependabot/... renovate/... (bot-generated, auto-exempt)"
65+
echo ""
66+
echo "Examples:"
67+
echo " feat/employee-workload-report"
68+
echo " fix/login-validation-error"
69+
echo " chore/upgrade-deps"
70+
echo " release/v14.0.27"
71+
echo ""
72+
echo "See BRANCHING.md (§ Topic branches) for the full convention."
73+
exit 1

.github/workflows/commitlint.yml

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
name: Lint PR title & commits
2+
3+
on:
4+
pull_request:
5+
types:
6+
- opened
7+
- edited
8+
- synchronize
9+
- reopened
10+
11+
permissions:
12+
contents: read
13+
pull-requests: read
14+
15+
concurrency:
16+
group: commitlint-${{ github.event.pull_request.number }}
17+
cancel-in-progress: true
18+
19+
jobs:
20+
# ────────────────────────────────────────────────────────────────────
21+
# PR TITLE — must follow Conventional Commits, since we use squash
22+
# merging on main and the PR title becomes the squash-commit message.
23+
# ────────────────────────────────────────────────────────────────────
24+
lint-pr-title:
25+
name: PR title (Conventional Commits)
26+
runs-on: ubuntu-latest
27+
steps:
28+
- name: Validate PR title
29+
uses: amannn/action-semantic-pull-request@0723387faaf9b38adef4775cd42cfd5155ed6017 # v5.5.3
30+
env:
31+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
32+
with:
33+
# Keep in sync with commitlint.config.js → type-enum
34+
types: |
35+
build
36+
chore
37+
ci
38+
docs
39+
feat
40+
fix
41+
perf
42+
refactor
43+
revert
44+
style
45+
test
46+
requireScope: false
47+
# Subject must start with a lowercase letter
48+
subjectPattern: ^(?![A-Z]).+$
49+
subjectPatternError: |
50+
The subject in PR title "{title}" should start with a lowercase
51+
letter. For example, use "feat: add login page" — not
52+
"feat: Add login page".
53+
# Don't fail if the PR is still in draft / WIP
54+
ignoreLabels: |
55+
wip
56+
do not merge
57+
58+
# ────────────────────────────────────────────────────────────────────
59+
# COMMITS — every commit in the PR must pass commitlint.
60+
# ────────────────────────────────────────────────────────────────────
61+
lint-commits:
62+
name: Commit messages (commitlint)
63+
runs-on: ubuntu-latest
64+
steps:
65+
- name: Checkout (with full history)
66+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
67+
with:
68+
fetch-depth: 0
69+
persist-credentials: false
70+
71+
- name: Setup Node.js
72+
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
73+
with:
74+
node-version: 20
75+
cache: npm
76+
77+
- name: Install commitlint dependencies
78+
run: npm ci --no-audit --no-fund
79+
80+
- name: Lint every commit in the PR
81+
run: |
82+
npx commitlint \
83+
--from "${{ github.event.pull_request.base.sha }}" \
84+
--to "${{ github.event.pull_request.head.sha }}" \
85+
--verbose

commitlint.config.js

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/**
2+
* Commitlint configuration for AlianHub.
3+
*
4+
* Enforces the Conventional Commits standard on:
5+
* - Every commit message (when wired to a local pre-commit hook)
6+
* - Every commit in a pull request (CI: .github/workflows/commitlint.yml)
7+
* - Every pull request title (CI: .github/workflows/commitlint.yml)
8+
*
9+
* Accepted types stay in sync with the branch-type prefixes documented in
10+
* BRANCHING.md → "Topic branches". Update both files together if you add a type.
11+
*
12+
* Docs:
13+
* - Conventional Commits: https://www.conventionalcommits.org/
14+
* - commitlint: https://commitlint.js.org/
15+
*
16+
* Run locally:
17+
* npm run lint:commits
18+
*/
19+
module.exports = {
20+
extends: ['@commitlint/config-conventional'],
21+
rules: {
22+
// Accepted types — keep in sync with BRANCHING.md § Topic branches.
23+
// `hotfix` and `release` are branch-only types — for commits within
24+
// a hotfix, use `fix`; for release prep, use `chore(release)`.
25+
'type-enum': [
26+
2, // error
27+
'always',
28+
[
29+
'build', // Build system or external dependencies
30+
'chore', // Maintenance: deps, config, license, repo housekeeping
31+
'ci', // CI configuration
32+
'docs', // Documentation only
33+
'feat', // New feature
34+
'fix', // Bug fix
35+
'perf', // Performance improvement
36+
'refactor', // Code change that neither fixes a bug nor adds a feature
37+
'revert', // Revert a previous commit
38+
'style', // Formatting, whitespace (no code change)
39+
'test', // Adding or fixing tests
40+
],
41+
],
42+
43+
// Header (first line) length — generous to accommodate scope + description
44+
'header-max-length': [2, 'always', 100],
45+
46+
// Subject (text after the colon) rules
47+
'subject-case': [
48+
2,
49+
'never',
50+
['sentence-case', 'start-case', 'pascal-case', 'upper-case'],
51+
],
52+
'subject-empty': [2, 'never'],
53+
'subject-full-stop': [2, 'never', '.'],
54+
55+
// Type and scope must be lowercase
56+
'type-case': [2, 'always', 'lower-case'],
57+
'type-empty': [2, 'never'],
58+
'scope-case': [2, 'always', 'lower-case'],
59+
60+
// Body and footer: warn-only on line length (Conventional Commits-tolerant)
61+
'body-max-line-length': [1, 'always', 200],
62+
'footer-max-line-length': [1, 'always', 200],
63+
},
64+
};

0 commit comments

Comments
 (0)