From f2caa5a88d240aeec24c3603956f9072e2f0a9aa Mon Sep 17 00:00:00 2001 From: Salvydas Lukosius Date: Sat, 16 May 2026 01:22:29 +0100 Subject: [PATCH] ci: enforce branch model, conventional commits, and no-AI-files policy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add .github/copilot-instructions.md with commit format rules, branch model documentation, and prohibition on AI co-author trailers or AI config files (AGENTS.md, CLAUDE.md, GEMINI.md, .cursorrules…) - Add .github/workflows/commit-lint.yml: PR workflow that validates conventional commit subject format, blocks AI co-author trailers, checks PR title format, and enforces branch naming convention - Update .github/workflows/trunk-check.yml: run Trunk on next branch in addition to main to catch issues before integration - Update .github/PULL_REQUEST_TEMPLATE.md: document the branch model (branch from next, PR to next, next→main for releases) and add checklist items for commit hygiene - Update docs/CONTRIBUTING.md: full branch model diagram, commit message guide, and explicit list of files not to add to the repo - Update .gitignore: add AI assistant config file patterns --- .github/PULL_REQUEST_TEMPLATE.md | 64 +++++++++-------- .github/copilot-instructions.md | 52 ++++++++++++++ .github/workflows/commit-lint.yml | 116 ++++++++++++++++++++++++++++++ .github/workflows/trunk-check.yml | 2 +- .gitignore | 13 +++- docs/CONTRIBUTING.md | 61 ++++++++++++++-- 6 files changed, 271 insertions(+), 37 deletions(-) create mode 100644 .github/copilot-instructions.md create mode 100644 .github/workflows/commit-lint.yml diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index dc09aad9..78894d19 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,43 +1,47 @@ - + + Branch model: + • Create your branch FROM next: git checkout -b fix/my-fix next + • Open this PR TARGETING next — never target main directly + • main is only updated from next via a release PR -## Motivation and Context + Commit messages must follow Conventional Commits: + type(scope): short description (≤72 chars, imperative mood) + Types: feat fix perf refactor docs test ci chore revert + Example: fix(self-update): correctly propagate exit code on failure - - + See .github/copilot-instructions.md for full guidelines. +--> -## How Has This Been Tested? - - - - +## Description -## Screenshots (if appropriate): + -## Types of changes +## Related issues - + -- [ ] Bug fix (non-breaking change which fixes an issue) -- [ ] New feature (non-breaking change which adds functionality) -- [ ] Breaking change (fix or feature that would cause existing functionality to change) -- [ ] Documentation update (if none of the other choices apply) -- [ ] Chore (general maintenance, refactoring, or code style update) +## Type of change -## Checklist: + - - +- [ ] `fix` — bug fix (non-breaking) +- [ ] `feat` — new feature (non-breaking) +- [ ] `feat!` / `fix!` — breaking change +- [ ] `perf` — performance improvement +- [ ] `refactor` — code change with no functional impact +- [ ] `docs` — documentation only +- [ ] `ci` — CI/workflow change +- [ ] `chore` — maintenance / dependency bump -- [ ] My code follows the code style of this project. -- [ ] I have read the **CONTRIBUTING** document. -- [ ] I have added tests to cover my changes. -- [ ] All new and existing tests passed. -- [ ] I have updated the documentation accordingly. +## Checklist -## Other information (if applicable) +- [ ] My branch was created from `next` (not `main`) +- [ ] This PR targets the `next` branch +- [ ] Commit messages follow Conventional Commits format +- [ ] No AI co-author trailers in commit messages +- [ ] I have read [CONTRIBUTING.md](docs/CONTRIBUTING.md) +- [ ] Existing tests pass (`zsh -n zi.zsh` / Trunk checks) +- [ ] Documentation updated if needed - diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 00000000..614209bf --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,52 @@ +# GitHub Copilot Instructions for zi + +## Commit messages + +- Use **Conventional Commits** format: `type(scope): short description` +- Allowed types: `feat`, `fix`, `perf`, `refactor`, `docs`, `test`, `ci`, `chore`, `revert` +- Subject line: imperative mood, ≤72 characters, no trailing period +- Body: wrap at 72 characters, explain *what* and *why* (not *how*) +- **Never add AI co-author trailers** — do not append `Co-authored-by: Copilot`, `Co-authored-by: Claude`, `Co-authored-by: GitHub Copilot`, or any AI-generated attribution to commit messages +- Breaking changes: add `!` after the type (`feat!:`) and include `BREAKING CHANGE:` in the footer + +Examples: +``` +feat(loader): add lazy-loading for completions + +Defer completion setup until first TAB press to cut startup time +for users with large fpath trees. + +BREAKING CHANGE: ZI_COMPLETION_LAZY must be set before zi is sourced. +``` + +``` +fix(self-update): correctly set exit code on network failure +``` + +## Branch model + +``` +main ← production releases (tags only, squash-merged from next) +next ← integration branch (PR target for all work) + └─ feat/ feature branches + └─ fix/ bug-fix branches + └─ perf/ performance improvements + └─ docs/ documentation updates + └─ ci/ CI/workflow changes + └─ chore/ maintenance, refactors +``` + +- **All branches must be created from `next`**, not `main` +- Open PRs **targeting `next`**, never `main` directly +- `next` → `main` is the only path to production; it requires all CI checks to pass + +## AI-generated files + +- Do **not** suggest adding `AGENTS.md`, `CLAUDE.md`, `GEMINI.md`, `.cursorrules`, `.aider*`, or similar AI-specific config files to this repository +- Copilot configuration lives **only** in `.github/copilot-instructions.md` + +## Code style + +- Zsh files: 2-space indent, `# vim: ft=zsh sw=2 ts=2 et` modeline, LF endings +- Every plugin entry file must resolve `$0` via the ZERO pattern +- Follow the [Z-Shell Plugin Standard](https://wiki.zshell.dev/community/zsh_plugin_standard) diff --git a/.github/workflows/commit-lint.yml b/.github/workflows/commit-lint.yml new file mode 100644 index 00000000..7477c560 --- /dev/null +++ b/.github/workflows/commit-lint.yml @@ -0,0 +1,116 @@ +--- +name: "📝 Commit Lint" + +on: + pull_request: + types: [opened, synchronize, reopened, edited] + branches: + - main + - next + +permissions: + pull-requests: read + contents: read + +jobs: + commit-lint: + name: "📝 Validate Commits" + runs-on: ubuntu-latest + steps: + - name: "⤵️ Check out code from GitHub" + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: "📝 Lint commit messages" + env: + BASE_SHA: ${{ github.event.pull_request.base.sha }} + HEAD_SHA: ${{ github.event.pull_request.head.sha }} + run: | + set -euo pipefail + + CONVENTIONAL_PATTERN='^(feat|fix|perf|refactor|docs|test|ci|chore|revert)(\([^)]+\))?!?: .{1,72}$' + AI_COAUTHOR_PATTERN='Co-authored-by:.*[Cc]opilot|Co-authored-by:.*[Cc]laude|Co-authored-by:.*[Gg][Pp][Tt]|Co-authored-by:.*[Oo]pen[Aa][Ii]|Co-authored-by:.*[Gg]emini|Co-authored-by:.*[Cc]ursor' + + errors=0 + + while IFS= read -r sha; do + subject=$(git show -s --format='%s' "$sha") + body=$(git show -s --format='%b' "$sha") + + # Skip merge commits + parent_count=$(git cat-file -p "$sha" | grep -c '^parent' || true) + if [ "$parent_count" -gt 1 ]; then + echo "⏭ Skip merge commit: $sha" + continue + fi + + # Validate conventional commit subject + if ! echo "$subject" | grep -qE "$CONVENTIONAL_PATTERN"; then + echo "❌ $sha — subject does not follow Conventional Commits:" + echo " Got: $subject" + echo " Expected: type(scope): description (≤72 chars)" + errors=$((errors + 1)) + else + echo "✅ $sha — $subject" + fi + + # Block AI co-author trailers + full_message=$(git show -s --format='%B' "$sha") + if echo "$full_message" | grep -qE "$AI_COAUTHOR_PATTERN"; then + echo "❌ $sha — AI co-author trailer detected:" + echo "$full_message" | grep -E "$AI_COAUTHOR_PATTERN" | sed 's/^/ /' + errors=$((errors + 1)) + fi + done < <(git log --format='%H' "${BASE_SHA}..${HEAD_SHA}") + + if [ "$errors" -gt 0 ]; then + echo "" + echo "💡 Fix commits with: git rebase -i ${BASE_SHA}" + echo " See .github/copilot-instructions.md for commit guidelines." + exit 1 + fi + + echo "" + echo "✅ All commit messages are valid." + + pr-title: + name: "📋 Validate PR Title" + runs-on: ubuntu-latest + steps: + - name: "📋 Check PR title follows Conventional Commits" + env: + PR_TITLE: ${{ github.event.pull_request.title }} + run: | + PATTERN='^(feat|fix|perf|refactor|docs|test|ci|chore|revert)(\([^)]+\))?!?: .{1,72}$' + if ! echo "$PR_TITLE" | grep -qE "$PATTERN"; then + echo "❌ PR title does not follow Conventional Commits format." + echo " Got: $PR_TITLE" + echo " Expected: type(scope): short description" + echo " Types: feat, fix, perf, refactor, docs, test, ci, chore, revert" + exit 1 + fi + echo "✅ PR title: $PR_TITLE" + + branch-naming: + name: "🌿 Validate Branch Name" + runs-on: ubuntu-latest + steps: + - name: "🌿 Check branch naming convention" + env: + BRANCH: ${{ github.head_ref }} + run: | + PATTERN='^(feat|fix|perf|refactor|docs|test|ci|chore|revert)/[a-z0-9][a-z0-9-]*[a-z0-9]$' + # Allow dependabot branches + if echo "$BRANCH" | grep -q '^dependabot/'; then + echo "✅ Dependabot branch: $BRANCH" + exit 0 + fi + if ! echo "$BRANCH" | grep -qE "$PATTERN"; then + echo "❌ Branch name does not follow naming convention." + echo " Got: $BRANCH" + echo " Expected: type/short-description" + echo " Example: feat/lazy-completions fix/self-update-exit-code" + exit 1 + fi + echo "✅ Branch name: $BRANCH" diff --git a/.github/workflows/trunk-check.yml b/.github/workflows/trunk-check.yml index 4bf797d1..79b8fc93 100644 --- a/.github/workflows/trunk-check.yml +++ b/.github/workflows/trunk-check.yml @@ -2,7 +2,7 @@ name: Trunk Code Quality on: push: - branches: [main] + branches: [main, next] tags: ["v*.*.*"] pull_request: types: [opened, synchronize] diff --git a/.gitignore b/.gitignore index f18a949a..7221f808 100644 --- a/.gitignore +++ b/.gitignore @@ -127,4 +127,15 @@ TAGS CVS .#* -.trunk/out* \ No newline at end of file +.trunk/out* +# AI assistant config files — not part of this project +# (Copilot config belongs in .github/copilot-instructions.md only) +AGENTS.md +CLAUDE.md +GEMINI.md +.cursorrules +.cursorignore +.aider* +.continue/ +.codeium/ +.tabnine* diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index 37779e87..615734ea 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -1,7 +1,58 @@ -# Contributing Guidelines +# Contributing to zi -When contributing, please first [discuss](https://github.com/z-shell/zi/issues/new/choose) the change you wish. The -[contributing guidelines](https://github.com/z-shell/community/blob/main/docs/CONTRIBUTING_GUIDELINES.md) and other relevant documents can be found in the -[community](https://github.com/z-shell/community) repository. +Thank you for contributing! Please follow the guidelines below to keep the project history clean and easy to navigate. + +## Branch model + +``` +main ←─── production (tagged releases only) + ↑ +next ←─── integration branch ← open all PRs here + ↑ + ├── feat/ new features + ├── fix/ bug fixes + ├── perf/ performance improvements + ├── refactor/ code refactors + ├── docs/ documentation updates + └── ci/ CI / workflow changes +``` + +1. **Always branch from `next`**: `git checkout -b fix/my-issue next` +2. **Open PRs targeting `next`** — never target `main` directly +3. `next` → `main` happens via a release PR once `next` is stable + +## Commit message format + +All commits must follow [Conventional Commits](https://www.conventionalcommits.org/): + +``` +type(scope): short description + +Optional body — explain what and why, not how. +Wrap at 72 characters. + +Optional footer(s): +Fixes #123 +BREAKING CHANGE: description of what breaks +``` + +**Allowed types:** `feat` `fix` `perf` `refactor` `docs` `test` `ci` `chore` `revert` + +**Rules:** +- Subject line: imperative mood, ≤72 characters, no trailing period +- Breaking changes: use `!` suffix (`feat!:`) and add `BREAKING CHANGE:` footer +- **No AI co-author trailers** — do not add `Co-authored-by: Copilot` or similar + +To clean up commits before opening a PR: `git rebase -i $(git merge-base HEAD next)` + +## What not to add + +- `AGENTS.md`, `CLAUDE.md`, `GEMINI.md`, `.cursorrules`, or any AI-specific config files +- Secrets, credentials, or tokens of any kind + +## Discussion and issues + +Before starting significant work, [open an issue](https://github.com/z-shell/zi/issues/new/choose) to discuss the change. + +See also the [community contributing guidelines](https://github.com/z-shell/community/blob/main/docs/CONTRIBUTING_GUIDELINES.md) and the [Code of Conduct](CODE_OF_CONDUCT.md). -Please note we have a [code of conduct](CODE_OF_CONDUCT.md), please follow it in all your interactions with the project.