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.
@@ -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
+ [![PyPI version](https://img.shields.io/pypi/v/checkowners)](https://pypi.org/project/checkowners/)
30
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](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