checkowners 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checkowners-0.1.0/.github/workflows/ci.yml +46 -0
- checkowners-0.1.0/.github/workflows/publish.yml +43 -0
- checkowners-0.1.0/.gitignore +218 -0
- checkowners-0.1.0/CLAUDE.md +73 -0
- checkowners-0.1.0/LICENSE +21 -0
- checkowners-0.1.0/PKG-INFO +128 -0
- checkowners-0.1.0/PRODUCT.md +553 -0
- checkowners-0.1.0/README.md +102 -0
- checkowners-0.1.0/checkowners/__init__.py +3 -0
- checkowners-0.1.0/checkowners/analyze.py +108 -0
- checkowners-0.1.0/checkowners/cli.py +224 -0
- checkowners-0.1.0/checkowners/config.py +98 -0
- checkowners-0.1.0/checkowners/drift.py +100 -0
- checkowners-0.1.0/checkowners/generate.py +76 -0
- checkowners-0.1.0/checkowners/models.py +60 -0
- checkowners-0.1.0/checkowners/notify.py +46 -0
- checkowners-0.1.0/checkowners/validate.py +70 -0
- checkowners-0.1.0/idea.md +190 -0
- checkowners-0.1.0/plan.md +201 -0
- checkowners-0.1.0/pyproject.toml +76 -0
- checkowners-0.1.0/tests/__init__.py +0 -0
- checkowners-0.1.0/tests/test_analyze.py +197 -0
- checkowners-0.1.0/tests/test_cli.py +256 -0
- checkowners-0.1.0/tests/test_config.py +126 -0
- checkowners-0.1.0/tests/test_drift.py +180 -0
- checkowners-0.1.0/tests/test_generate.py +149 -0
- checkowners-0.1.0/tests/test_notify.py +67 -0
- checkowners-0.1.0/tests/test_validate.py +91 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ["3.11", "3.12", "3.13"]
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
with:
|
|
18
|
+
fetch-depth: 0
|
|
19
|
+
|
|
20
|
+
- uses: actions/setup-python@v5
|
|
21
|
+
with:
|
|
22
|
+
python-version: ${{ matrix.python-version }}
|
|
23
|
+
|
|
24
|
+
- name: Install hatch
|
|
25
|
+
run: pip install hatch
|
|
26
|
+
|
|
27
|
+
- name: Run tests
|
|
28
|
+
run: hatch run test
|
|
29
|
+
|
|
30
|
+
lint:
|
|
31
|
+
runs-on: ubuntu-latest
|
|
32
|
+
steps:
|
|
33
|
+
- uses: actions/checkout@v4
|
|
34
|
+
|
|
35
|
+
- uses: actions/setup-python@v5
|
|
36
|
+
with:
|
|
37
|
+
python-version: "3.13"
|
|
38
|
+
|
|
39
|
+
- name: Install hatch
|
|
40
|
+
run: pip install hatch
|
|
41
|
+
|
|
42
|
+
- name: Run linters
|
|
43
|
+
run: hatch run lint
|
|
44
|
+
|
|
45
|
+
- name: Check formatting
|
|
46
|
+
run: hatch run fmt --check
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
id-token: write
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
build:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
|
|
16
|
+
- uses: actions/setup-python@v5
|
|
17
|
+
with:
|
|
18
|
+
python-version: "3.13"
|
|
19
|
+
|
|
20
|
+
- name: Install hatch
|
|
21
|
+
run: pip install hatch
|
|
22
|
+
|
|
23
|
+
- name: Build package
|
|
24
|
+
run: hatch build
|
|
25
|
+
|
|
26
|
+
- uses: actions/upload-artifact@v4
|
|
27
|
+
with:
|
|
28
|
+
name: dist
|
|
29
|
+
path: dist/
|
|
30
|
+
|
|
31
|
+
publish:
|
|
32
|
+
needs: build
|
|
33
|
+
runs-on: ubuntu-latest
|
|
34
|
+
environment: pypi
|
|
35
|
+
permissions:
|
|
36
|
+
id-token: write
|
|
37
|
+
steps:
|
|
38
|
+
- uses: actions/download-artifact@v4
|
|
39
|
+
with:
|
|
40
|
+
name: dist
|
|
41
|
+
path: dist/
|
|
42
|
+
|
|
43
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[codz]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py.cover
|
|
50
|
+
.hypothesis/
|
|
51
|
+
.pytest_cache/
|
|
52
|
+
cover/
|
|
53
|
+
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
*.pot
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
local_settings.py
|
|
61
|
+
db.sqlite3
|
|
62
|
+
db.sqlite3-journal
|
|
63
|
+
|
|
64
|
+
# Flask stuff:
|
|
65
|
+
instance/
|
|
66
|
+
.webassets-cache
|
|
67
|
+
|
|
68
|
+
# Scrapy stuff:
|
|
69
|
+
.scrapy
|
|
70
|
+
|
|
71
|
+
# Sphinx documentation
|
|
72
|
+
docs/_build/
|
|
73
|
+
|
|
74
|
+
# PyBuilder
|
|
75
|
+
.pybuilder/
|
|
76
|
+
target/
|
|
77
|
+
|
|
78
|
+
# Jupyter Notebook
|
|
79
|
+
.ipynb_checkpoints
|
|
80
|
+
|
|
81
|
+
# IPython
|
|
82
|
+
profile_default/
|
|
83
|
+
ipython_config.py
|
|
84
|
+
|
|
85
|
+
# pyenv
|
|
86
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
87
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
88
|
+
# .python-version
|
|
89
|
+
|
|
90
|
+
# pipenv
|
|
91
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
92
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
93
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
94
|
+
# install all needed dependencies.
|
|
95
|
+
# Pipfile.lock
|
|
96
|
+
|
|
97
|
+
# UV
|
|
98
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
99
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
100
|
+
# commonly ignored for libraries.
|
|
101
|
+
# uv.lock
|
|
102
|
+
|
|
103
|
+
# poetry
|
|
104
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
105
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
106
|
+
# commonly ignored for libraries.
|
|
107
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
108
|
+
# poetry.lock
|
|
109
|
+
# poetry.toml
|
|
110
|
+
|
|
111
|
+
# pdm
|
|
112
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
113
|
+
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
|
114
|
+
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
|
115
|
+
# pdm.lock
|
|
116
|
+
# pdm.toml
|
|
117
|
+
.pdm-python
|
|
118
|
+
.pdm-build/
|
|
119
|
+
|
|
120
|
+
# pixi
|
|
121
|
+
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
|
122
|
+
# pixi.lock
|
|
123
|
+
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
|
124
|
+
# in the .venv directory. It is recommended not to include this directory in version control.
|
|
125
|
+
.pixi
|
|
126
|
+
|
|
127
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
128
|
+
__pypackages__/
|
|
129
|
+
|
|
130
|
+
# Celery stuff
|
|
131
|
+
celerybeat-schedule
|
|
132
|
+
celerybeat.pid
|
|
133
|
+
|
|
134
|
+
# Redis
|
|
135
|
+
*.rdb
|
|
136
|
+
*.aof
|
|
137
|
+
*.pid
|
|
138
|
+
|
|
139
|
+
# RabbitMQ
|
|
140
|
+
mnesia/
|
|
141
|
+
rabbitmq/
|
|
142
|
+
rabbitmq-data/
|
|
143
|
+
|
|
144
|
+
# ActiveMQ
|
|
145
|
+
activemq-data/
|
|
146
|
+
|
|
147
|
+
# SageMath parsed files
|
|
148
|
+
*.sage.py
|
|
149
|
+
|
|
150
|
+
# Environments
|
|
151
|
+
.env
|
|
152
|
+
.envrc
|
|
153
|
+
.venv
|
|
154
|
+
env/
|
|
155
|
+
venv/
|
|
156
|
+
ENV/
|
|
157
|
+
env.bak/
|
|
158
|
+
venv.bak/
|
|
159
|
+
|
|
160
|
+
# Spyder project settings
|
|
161
|
+
.spyderproject
|
|
162
|
+
.spyproject
|
|
163
|
+
|
|
164
|
+
# Rope project settings
|
|
165
|
+
.ropeproject
|
|
166
|
+
|
|
167
|
+
# mkdocs documentation
|
|
168
|
+
/site
|
|
169
|
+
|
|
170
|
+
# mypy
|
|
171
|
+
.mypy_cache/
|
|
172
|
+
.dmypy.json
|
|
173
|
+
dmypy.json
|
|
174
|
+
|
|
175
|
+
# Pyre type checker
|
|
176
|
+
.pyre/
|
|
177
|
+
|
|
178
|
+
# pytype static type analyzer
|
|
179
|
+
.pytype/
|
|
180
|
+
|
|
181
|
+
# Cython debug symbols
|
|
182
|
+
cython_debug/
|
|
183
|
+
|
|
184
|
+
# PyCharm
|
|
185
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
186
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
187
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
188
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
189
|
+
# .idea/
|
|
190
|
+
|
|
191
|
+
# Abstra
|
|
192
|
+
# Abstra is an AI-powered process automation framework.
|
|
193
|
+
# Ignore directories containing user credentials, local state, and settings.
|
|
194
|
+
# Learn more at https://abstra.io/docs
|
|
195
|
+
.abstra/
|
|
196
|
+
|
|
197
|
+
# Visual Studio Code
|
|
198
|
+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
|
199
|
+
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
|
200
|
+
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
|
201
|
+
# you could uncomment the following to ignore the entire vscode folder
|
|
202
|
+
# .vscode/
|
|
203
|
+
# Temporary file for partial code execution
|
|
204
|
+
tempCodeRunnerFile.py
|
|
205
|
+
|
|
206
|
+
# Ruff stuff:
|
|
207
|
+
.ruff_cache/
|
|
208
|
+
|
|
209
|
+
# PyPI configuration file
|
|
210
|
+
.pypirc
|
|
211
|
+
|
|
212
|
+
# Marimo
|
|
213
|
+
marimo/_static/
|
|
214
|
+
marimo/_lsp/
|
|
215
|
+
__marimo__/
|
|
216
|
+
|
|
217
|
+
# Streamlit
|
|
218
|
+
.streamlit/secrets.toml
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## What This Is
|
|
6
|
+
|
|
7
|
+
checkOwners: a CODEOWNERS inference engine driven by git commit history with drift detection and CI integration. Pure-git, no-LLM. Python 3.11+, packaged with hatch, distributed on PyPI and as a composite GitHub Action.
|
|
8
|
+
|
|
9
|
+
## Commands
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
hatch run test # pytest with coverage (85%+ target)
|
|
13
|
+
hatch run test -- tests/test_analyze.py # single test file
|
|
14
|
+
hatch run test -- -k "test_name" # single test by name
|
|
15
|
+
hatch run lint # ruff check + mypy --strict
|
|
16
|
+
hatch run fmt # ruff format
|
|
17
|
+
hatch build # sdist + wheel in dist/
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
CLI entry point is `checkowners` (Typer app in `checkowners/cli.py`):
|
|
21
|
+
`analyze`, `generate`, `print`, `validate`, `drift`, `notify`, `sync`.
|
|
22
|
+
All subcommands support `--json` for structured JSON output.
|
|
23
|
+
|
|
24
|
+
## Architecture
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
checkowners/
|
|
28
|
+
cli.py # Typer app; all subcommands registered here
|
|
29
|
+
analyze.py # git log + git blame -> OwnershipMap
|
|
30
|
+
generate.py # OwnershipMap -> .github/CODEOWNERS writer
|
|
31
|
+
drift.py # State machine: inferred vs. current diff -> DriftResult
|
|
32
|
+
notify.py # Webhook POST on drift events
|
|
33
|
+
validate.py # Syntax-only CODEOWNERS validator
|
|
34
|
+
config.py # PyYAML loader for .github/checkowners.yml
|
|
35
|
+
models.py # Dataclasses: OwnershipMap, DriftResult, Config
|
|
36
|
+
state.py # ~/.checkowners/state.json reader/writer
|
|
37
|
+
tests/
|
|
38
|
+
test_analyze.py
|
|
39
|
+
test_generate.py
|
|
40
|
+
test_drift.py
|
|
41
|
+
test_validate.py
|
|
42
|
+
integration/ # Fixture git repos via GitPython
|
|
43
|
+
action.yml # Composite GitHub Action
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Key data flow: `config.py` loads `.github/checkowners.yml` -> `analyze.py` runs git log/blame -> `models.OwnershipMap` -> `generate.py` writes CODEOWNERS or `drift.py` compares against existing CODEOWNERS -> `models.DriftResult` -> `notify.py` posts webhooks. `state.py` persists inference results to `~/.checkowners/state.json`.
|
|
47
|
+
|
|
48
|
+
## Conventions
|
|
49
|
+
|
|
50
|
+
- Functional style throughout; no classes except dataclasses in `models.py`
|
|
51
|
+
- Type hints on every function signature; strict mypy (`--strict`)
|
|
52
|
+
- All file paths via `pathlib.Path`, never hardcoded strings
|
|
53
|
+
- Config file: `.github/checkowners.yml` (per-repo; loaded via `config.py`)
|
|
54
|
+
- State file: `~/.checkowners/state.json` (auto-maintained; never hardcode path)
|
|
55
|
+
- Every write to `.github/CODEOWNERS` must include: `# Generated by checkOwners. Do not edit manually.`
|
|
56
|
+
- Default exclusions: `*.lock`, `dist/**`, `vendor/**`
|
|
57
|
+
- Config defaults: `lookback_days: 180`, `min_commits: 3`, `top_n_owners: 2`
|
|
58
|
+
- Drift state machine modes: `commit`, `repo`, `both`
|
|
59
|
+
|
|
60
|
+
## Testing
|
|
61
|
+
|
|
62
|
+
- Unit tests mock all subprocess calls (`git log`, `git blame`); never require a real git repo
|
|
63
|
+
- Integration tests in `tests/integration/` use fixture repos created with GitPython
|
|
64
|
+
- Every module has a corresponding `tests/test_<module>.py`
|
|
65
|
+
|
|
66
|
+
## Do NOT
|
|
67
|
+
|
|
68
|
+
- Add LLM or AI dependencies; checkOwners is pure-git inference only
|
|
69
|
+
- Write to `.github/CODEOWNERS` without the machine-generated header
|
|
70
|
+
- Make external network calls except in `notify.py` (webhook POST) and `action.yml` (GitHub API)
|
|
71
|
+
- Use classes except dataclasses in `models.py`
|
|
72
|
+
- Skip type annotations on any function signature
|
|
73
|
+
- Modify `state.json` schema without bumping the schema version field
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Samir Musali
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: checkowners
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Infer and maintain CODEOWNERS from git history. Drift detection, CI-native JSON output, and GitHub Actions integration.
|
|
5
|
+
Author-email: Samir Musali <samir.musali@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Keywords: cli,codeowners,drift-detection,git,github-actions,ownership
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Environment :: Console
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
18
|
+
Classifier: Typing :: Typed
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Requires-Dist: gitpython>=3.1.0
|
|
21
|
+
Requires-Dist: pygithub>=2.0.0
|
|
22
|
+
Requires-Dist: pyyaml>=6.0
|
|
23
|
+
Requires-Dist: rich>=13.0.0
|
|
24
|
+
Requires-Dist: typer>=0.9.0
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
# checkowners
|
|
28
|
+
|
|
29
|
+
[](https://pypi.org/project/checkowners/)
|
|
30
|
+
[](LICENSE)
|
|
31
|
+
|
|
32
|
+
Infer and maintain CODEOWNERS from git history. Drift detection, CI-native JSON output, and GitHub Actions integration.
|
|
33
|
+
|
|
34
|
+
## Installation
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pip install checkowners
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Quick Start
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# Infer ownership from git commit history
|
|
44
|
+
checkowners analyze
|
|
45
|
+
|
|
46
|
+
# Generate .github/CODEOWNERS from inference
|
|
47
|
+
checkowners generate
|
|
48
|
+
|
|
49
|
+
# Detect drift between inferred and current CODEOWNERS
|
|
50
|
+
checkowners drift
|
|
51
|
+
|
|
52
|
+
# Validate CODEOWNERS syntax
|
|
53
|
+
checkowners validate
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
All commands support `--json` for structured output.
|
|
57
|
+
|
|
58
|
+
## Commands
|
|
59
|
+
|
|
60
|
+
| Command | Description |
|
|
61
|
+
|---------|-------------|
|
|
62
|
+
| `checkowners analyze` | Infer file ownership from git history |
|
|
63
|
+
| `checkowners generate` | Write `.github/CODEOWNERS` from inferred ownership |
|
|
64
|
+
| `checkowners print` | Print inferred owners to stdout |
|
|
65
|
+
| `checkowners validate` | Validate existing CODEOWNERS syntax (no git access) |
|
|
66
|
+
| `checkowners drift` | Compare inferred vs. current CODEOWNERS |
|
|
67
|
+
| `checkowners notify` | POST webhook on drift events |
|
|
68
|
+
| `checkowners sync` | Generate CODEOWNERS and commit the result |
|
|
69
|
+
|
|
70
|
+
## Configuration
|
|
71
|
+
|
|
72
|
+
Create `.github/checkowners.yml` in your repository:
|
|
73
|
+
|
|
74
|
+
```yaml
|
|
75
|
+
analysis:
|
|
76
|
+
lookback_days: 180 # How far back to analyze
|
|
77
|
+
min_commits: 3 # Minimum commits to qualify as owner
|
|
78
|
+
top_n_owners: 2 # Max owners per path
|
|
79
|
+
|
|
80
|
+
paths:
|
|
81
|
+
exclude:
|
|
82
|
+
- "*.lock"
|
|
83
|
+
- "dist/**"
|
|
84
|
+
- "vendor/**"
|
|
85
|
+
|
|
86
|
+
output:
|
|
87
|
+
header: "# Generated by checkOwners. Do not edit manually."
|
|
88
|
+
include_unowned: false # Show paths with no inferred owner
|
|
89
|
+
|
|
90
|
+
drift:
|
|
91
|
+
mode: commit # commit | repo | both
|
|
92
|
+
compare_to: auto
|
|
93
|
+
|
|
94
|
+
notifications:
|
|
95
|
+
webhook_url: "" # POST here on drift events
|
|
96
|
+
include_unchanged: false
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
All fields are optional. Defaults are shown above.
|
|
100
|
+
|
|
101
|
+
## Drift Detection Modes
|
|
102
|
+
|
|
103
|
+
- **commit**: detect paths in the inferred map that are missing from CODEOWNERS
|
|
104
|
+
- **repo**: detect CODEOWNERS entries that are stale or have changed owners
|
|
105
|
+
- **both**: run both checks
|
|
106
|
+
|
|
107
|
+
## GitHub Actions
|
|
108
|
+
|
|
109
|
+
```yaml
|
|
110
|
+
- name: Check CODEOWNERS drift
|
|
111
|
+
run: |
|
|
112
|
+
pip install checkowners
|
|
113
|
+
checkowners drift --json
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Set `drift.mode: both` in your config and fail the step when `drift_detected` is true.
|
|
117
|
+
|
|
118
|
+
## Development
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
hatch run test # pytest with coverage
|
|
122
|
+
hatch run lint # ruff check + mypy --strict
|
|
123
|
+
hatch run fmt # ruff format
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## License
|
|
127
|
+
|
|
128
|
+
MIT
|