heinzel-github 0.9.1__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.
- heinzel_github-0.9.1/.gitignore +15 -0
- heinzel_github-0.9.1/CHANGELOG.md +8 -0
- heinzel_github-0.9.1/LICENSE +21 -0
- heinzel_github-0.9.1/PKG-INFO +137 -0
- heinzel_github-0.9.1/README.md +124 -0
- heinzel_github-0.9.1/plugin-lock.json +40 -0
- heinzel_github-0.9.1/pyproject.toml +131 -0
- heinzel_github-0.9.1/requirements.lock +1154 -0
- heinzel_github-0.9.1/src/heinzel_github/__init__.py +70 -0
- heinzel_github-0.9.1/src/heinzel_github/_adapter.py +176 -0
- heinzel_github-0.9.1/src/heinzel_github/_client.py +202 -0
- heinzel_github-0.9.1/src/heinzel_github/_gh.py +349 -0
- heinzel_github-0.9.1/src/heinzel_github/_models.py +87 -0
- heinzel_github-0.9.1/src/heinzel_github/_module.py +5 -0
- heinzel_github-0.9.1/src/heinzel_github/_service.py +155 -0
- heinzel_github-0.9.1/src/heinzel_github/py.typed +0 -0
- heinzel_github-0.9.1/src/heinzel_github/skills/review-pull-request/SKILL.md +25 -0
- heinzel_github-0.9.1/src/heinzel_github/skills/review-pull-request/references/checklist.md +11 -0
- heinzel_github-0.9.1/tests/conftest.py +287 -0
- heinzel_github-0.9.1/tests/fake_gh.py +57 -0
- heinzel_github-0.9.1/tests/fixtures/issues.json +49 -0
- heinzel_github-0.9.1/tests/fixtures/pulls.json +54 -0
- heinzel_github-0.9.1/tests/integration/capability_test.py +225 -0
- heinzel_github-0.9.1/tests/integration/cli_test.py +59 -0
- heinzel_github-0.9.1/tests/integration/connector_test.py +92 -0
- heinzel_github-0.9.1/tests/integration/gh_test.py +244 -0
- heinzel_github-0.9.1/tests/unit/adapter_test.py +248 -0
- heinzel_github-0.9.1/tests/unit/client_test.py +184 -0
- heinzel_github-0.9.1/tests/unit/contract_test.py +36 -0
- heinzel_github-0.9.1/tests/unit/gh_test.py +61 -0
- heinzel_github-0.9.1/tests/unit/models_test.py +57 -0
- heinzel_github-0.9.1/tests/unit/service_test.py +149 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.9.1
|
|
4
|
+
|
|
5
|
+
Move this plugin to its independent repository project for the Heinzel 0.9.1 plugin contract.
|
|
6
|
+
Own its dependencies, tests and artifacts; consume public contracts without concrete provider dependencies.
|
|
7
|
+
|
|
8
|
+
Earlier history is preserved in the source repository named in README.md.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Helmut Hoffer von Ankershoffen
|
|
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,137 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: heinzel-github
|
|
3
|
+
Version: 0.9.1
|
|
4
|
+
Summary: Read pull requests and issues from GitHub: the code-host and tracker capabilities.
|
|
5
|
+
Project-URL: Source, https://github.com/helmut-hoffer-von-ankershoffen/heinzel-plugins/tree/main/plugins/heinzel-github
|
|
6
|
+
Project-URL: Changelog, https://github.com/helmut-hoffer-von-ankershoffen/heinzel-plugins/blob/main/plugins/heinzel-github/CHANGELOG.md
|
|
7
|
+
Project-URL: Issues, https://github.com/helmut-hoffer-von-ankershoffen/heinzel-plugins/issues
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Requires-Python: >=3.12
|
|
11
|
+
Requires-Dist: heinzel<0.10,>=0.9.1
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
14
|
+
# heinzel-github
|
|
15
|
+
|
|
16
|
+
Read pull requests and issues from GitHub, the `code-host` and `tracker` capabilities ([ADR 0028](https://github.com/helmut-hoffer-von-ankershoffen/heinzel/blob/main/docs/adr/0028-integrations-are-capability-adapters.md)): a first-party
|
|
17
|
+
[Heinzelmännchen](https://github.com/helmut-hoffer-von-ankershoffen/heinzel) plugin. It is independently released and installed explicitly:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
hz system plugin add github --confirm # a compatible plugin release; run it again to upgrade
|
|
21
|
+
hz system plugin remove heinzel-github
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Set up
|
|
25
|
+
|
|
26
|
+
The adapter talks to GitHub only through the [GitHub CLI](https://cli.github.com) `gh` (2.40.0 or later), which owns
|
|
27
|
+
authentication ([ADR 0035](https://github.com/helmut-hoffer-von-ankershoffen/heinzel/blob/main/docs/adr/0035-integrations-go-through-vendor-clis-as-an-ordinary-employee.md)). It reads what you can read, as you:
|
|
28
|
+
no app to register, no token to create or store. Install `gh` and log in once:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
gh auth login # or: gh auth login --hostname ghe.example.com
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
The adapter acts with whatever that login may do, which is usually more than reading; it only ever sends GET
|
|
35
|
+
requests. As for any `gh` command, a `GH_TOKEN` or `GITHUB_TOKEN` (`GH_ENTERPRISE_TOKEN` for a GitHub Enterprise
|
|
36
|
+
Server) in your environment takes precedence over the stored login: it is gh's own variable, passed to gh and never
|
|
37
|
+
read by Heinzelmännchen. If you stored a token for an earlier version, remove it:
|
|
38
|
+
`hz system credential remove github-token --service github` (or unset `HZ_CREDENTIAL_GITHUB_GITHUB_TOKEN`).
|
|
39
|
+
|
|
40
|
+
Bind the adapter in your user configuration, never in a project `hz.toml`, which Heinzelmännchen ignores for bindings:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
hz system config set capabilities.code_host github
|
|
44
|
+
hz system config set capabilities.tracker github
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
which writes
|
|
48
|
+
|
|
49
|
+
```toml
|
|
50
|
+
[capabilities]
|
|
51
|
+
code_host = "github"
|
|
52
|
+
tracker = "github"
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Or per shell: `HZ_CAPABILITIES__CODE_HOST=github`, `HZ_CAPABILITIES__TRACKER=github`.
|
|
56
|
+
|
|
57
|
+
For GitHub Enterprise Server, name its host in your user configuration or `HZ_GITHUB__HOST`; the adapter passes it to
|
|
58
|
+
`gh` as `--hostname` and `GH_HOST`, so `gh` uses its login for that host:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
hz system config set github.host ghe.example.com
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
or in the file (`api_url` from earlier versions is refused; `host` replaced it):
|
|
65
|
+
|
|
66
|
+
```toml
|
|
67
|
+
[github]
|
|
68
|
+
host = "ghe.example.com"
|
|
69
|
+
gh = "gh" # the GitHub CLI: a name on PATH (absolute entries only) or an absolute path
|
|
70
|
+
timeout = 60 # seconds per gh call
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
`host`, `gh` and `timeout` are ignored in a project `hz.toml`, so a repository can't send your login elsewhere or pick
|
|
74
|
+
the program that runs. The adapter's hosts come
|
|
75
|
+
from it: `hz system capability list` shows `api.github.com`, or your GitHub Enterprise Server's host.
|
|
76
|
+
|
|
77
|
+
### Is it connected?
|
|
78
|
+
|
|
79
|
+
Bound automatically while it is the only installed adapter of a capability (usually `code-host`; `tracker` once another
|
|
80
|
+
tracker is installed needs a choice), [ADR
|
|
81
|
+
0055](https://github.com/helmut-hoffer-von-ankershoffen/heinzel/blob/main/docs/adr/0055-connectors-show-their-sign-in-test-live-on-demand-and-bind-themselves-when-alone.md). `hz
|
|
82
|
+
system connector list` (or Settings → Connectors in `hz ui`) shows whether `gh` is logged in and as whom, read from gh's
|
|
83
|
+
hosts file without asking GitHub; `hz system connector test github` asks GitHub who gh is (`gh api user`, 15 s at most),
|
|
84
|
+
and only when you run it.
|
|
85
|
+
|
|
86
|
+
## Use
|
|
87
|
+
|
|
88
|
+
Workflows ask for `code-host` and `tracker`, never for GitHub:
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
from datetime import datetime
|
|
92
|
+
|
|
93
|
+
from heinzel import CodeHost, Module, PullRequest, Window
|
|
94
|
+
|
|
95
|
+
module = Module("digest", "Weekly digest.", requires=["code-host:read", "tracker:read"])
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
@module.operation
|
|
99
|
+
def merged(code_host: CodeHost, since: datetime, until: datetime) -> list[PullRequest]:
|
|
100
|
+
"""Pull requests merged this week."""
|
|
101
|
+
return code_host.merged_pull_requests("acme/widgets", Window(since=since, until=until))
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
| Method | Capability | `gh api` call |
|
|
105
|
+
| --- | --- | --- |
|
|
106
|
+
| `merged_pull_requests(repository, window)` | `code-host:read` | `repos/{owner}/{repo}/pulls?state=closed&sort=updated&direction=desc`, kept when `merged_at` is in the window; paging stops before the window |
|
|
107
|
+
| `issues_updated(project, window)` | `tracker:read` | `repos/{owner}/{repo}/issues?state=all&since=...&sort=updated&direction=asc`, without pull requests |
|
|
108
|
+
|
|
109
|
+
Repositories and projects are written `owner/repo`; windows are half-open `[since, until)` with timezone-aware times;
|
|
110
|
+
results are oldest first. The adapter pages with the API's `page` and `per_page` parameters rather than
|
|
111
|
+
`gh api --paginate`, so it can stop as soon as the window is covered. A call reads at most 100 pages of 100 (10,000
|
|
112
|
+
items), 50 MiB in all and ten times `github.timeout`, then fails and says what to ask for instead: merged pull requests page through every pull request updated since `since`, even after `until`, so they need
|
|
113
|
+
a window that starts more recently; issues stop at `until`, so they need a shorter window. Why the pulls list and not
|
|
114
|
+
the search API: [ADR 0033](https://github.com/helmut-hoffer-von-ankershoffen/heinzel/blob/main/docs/adr/0033-github-rest-api-over-urllib.md); why `gh`:
|
|
115
|
+
[ADR 0035](https://github.com/helmut-hoffer-von-ankershoffen/heinzel/blob/main/docs/adr/0035-integrations-go-through-vendor-clis-as-an-ordinary-employee.md).
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
hz github info # this plugin, its version and the GitHub host it reads
|
|
119
|
+
hz system capability list # who reads what, through which adapter and hosts
|
|
120
|
+
hz system doctor # gh on PATH, its version and login, the bindings; never contacts GitHub
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Errors say what to do: no `gh`, or one too old, names where to get it; no login, or a login GitHub rejects, names
|
|
124
|
+
`gh auth login --hostname <host>`; a rate limit says to wait and how to see when it resets; a missing or forbidden
|
|
125
|
+
repository says to check the spelling and what your account can see; a slow call names the timeout setting.
|
|
126
|
+
|
|
127
|
+
How `gh` is run: with an argument list, never a shell, from your home directory, in its own process group, stopped
|
|
128
|
+
(SIGTERM, then SIGKILL) at the timeout, with its output capped, and with an allowlisted environment: never
|
|
129
|
+
Heinzelmännchen's `HZ_CREDENTIAL_*` variables or debug switches such as `GH_DEBUG`, which would print request
|
|
130
|
+
headers. Doctor runs only `gh --version` and looks for the host in gh's hosts file (or a token variable gh reads); it
|
|
131
|
+
never runs a command that prints a token. Everything GitHub or `gh` says is redacted of anything shaped like a GitHub
|
|
132
|
+
token, stripped of control and bidirectional characters, and only then cut to length; every string the adapter
|
|
133
|
+
returns is cleaned too.
|
|
134
|
+
|
|
135
|
+
Development and release commands: [repository guide](../../README.md).
|
|
136
|
+
|
|
137
|
+
Migration provenance: heinzel, before the 0.9 plugin-boundary migration.
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# heinzel-github
|
|
2
|
+
|
|
3
|
+
Read pull requests and issues from GitHub, the `code-host` and `tracker` capabilities ([ADR 0028](https://github.com/helmut-hoffer-von-ankershoffen/heinzel/blob/main/docs/adr/0028-integrations-are-capability-adapters.md)): a first-party
|
|
4
|
+
[Heinzelmännchen](https://github.com/helmut-hoffer-von-ankershoffen/heinzel) plugin. It is independently released and installed explicitly:
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
hz system plugin add github --confirm # a compatible plugin release; run it again to upgrade
|
|
8
|
+
hz system plugin remove heinzel-github
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Set up
|
|
12
|
+
|
|
13
|
+
The adapter talks to GitHub only through the [GitHub CLI](https://cli.github.com) `gh` (2.40.0 or later), which owns
|
|
14
|
+
authentication ([ADR 0035](https://github.com/helmut-hoffer-von-ankershoffen/heinzel/blob/main/docs/adr/0035-integrations-go-through-vendor-clis-as-an-ordinary-employee.md)). It reads what you can read, as you:
|
|
15
|
+
no app to register, no token to create or store. Install `gh` and log in once:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
gh auth login # or: gh auth login --hostname ghe.example.com
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
The adapter acts with whatever that login may do, which is usually more than reading; it only ever sends GET
|
|
22
|
+
requests. As for any `gh` command, a `GH_TOKEN` or `GITHUB_TOKEN` (`GH_ENTERPRISE_TOKEN` for a GitHub Enterprise
|
|
23
|
+
Server) in your environment takes precedence over the stored login: it is gh's own variable, passed to gh and never
|
|
24
|
+
read by Heinzelmännchen. If you stored a token for an earlier version, remove it:
|
|
25
|
+
`hz system credential remove github-token --service github` (or unset `HZ_CREDENTIAL_GITHUB_GITHUB_TOKEN`).
|
|
26
|
+
|
|
27
|
+
Bind the adapter in your user configuration, never in a project `hz.toml`, which Heinzelmännchen ignores for bindings:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
hz system config set capabilities.code_host github
|
|
31
|
+
hz system config set capabilities.tracker github
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
which writes
|
|
35
|
+
|
|
36
|
+
```toml
|
|
37
|
+
[capabilities]
|
|
38
|
+
code_host = "github"
|
|
39
|
+
tracker = "github"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Or per shell: `HZ_CAPABILITIES__CODE_HOST=github`, `HZ_CAPABILITIES__TRACKER=github`.
|
|
43
|
+
|
|
44
|
+
For GitHub Enterprise Server, name its host in your user configuration or `HZ_GITHUB__HOST`; the adapter passes it to
|
|
45
|
+
`gh` as `--hostname` and `GH_HOST`, so `gh` uses its login for that host:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
hz system config set github.host ghe.example.com
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
or in the file (`api_url` from earlier versions is refused; `host` replaced it):
|
|
52
|
+
|
|
53
|
+
```toml
|
|
54
|
+
[github]
|
|
55
|
+
host = "ghe.example.com"
|
|
56
|
+
gh = "gh" # the GitHub CLI: a name on PATH (absolute entries only) or an absolute path
|
|
57
|
+
timeout = 60 # seconds per gh call
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
`host`, `gh` and `timeout` are ignored in a project `hz.toml`, so a repository can't send your login elsewhere or pick
|
|
61
|
+
the program that runs. The adapter's hosts come
|
|
62
|
+
from it: `hz system capability list` shows `api.github.com`, or your GitHub Enterprise Server's host.
|
|
63
|
+
|
|
64
|
+
### Is it connected?
|
|
65
|
+
|
|
66
|
+
Bound automatically while it is the only installed adapter of a capability (usually `code-host`; `tracker` once another
|
|
67
|
+
tracker is installed needs a choice), [ADR
|
|
68
|
+
0055](https://github.com/helmut-hoffer-von-ankershoffen/heinzel/blob/main/docs/adr/0055-connectors-show-their-sign-in-test-live-on-demand-and-bind-themselves-when-alone.md). `hz
|
|
69
|
+
system connector list` (or Settings → Connectors in `hz ui`) shows whether `gh` is logged in and as whom, read from gh's
|
|
70
|
+
hosts file without asking GitHub; `hz system connector test github` asks GitHub who gh is (`gh api user`, 15 s at most),
|
|
71
|
+
and only when you run it.
|
|
72
|
+
|
|
73
|
+
## Use
|
|
74
|
+
|
|
75
|
+
Workflows ask for `code-host` and `tracker`, never for GitHub:
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
from datetime import datetime
|
|
79
|
+
|
|
80
|
+
from heinzel import CodeHost, Module, PullRequest, Window
|
|
81
|
+
|
|
82
|
+
module = Module("digest", "Weekly digest.", requires=["code-host:read", "tracker:read"])
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@module.operation
|
|
86
|
+
def merged(code_host: CodeHost, since: datetime, until: datetime) -> list[PullRequest]:
|
|
87
|
+
"""Pull requests merged this week."""
|
|
88
|
+
return code_host.merged_pull_requests("acme/widgets", Window(since=since, until=until))
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
| Method | Capability | `gh api` call |
|
|
92
|
+
| --- | --- | --- |
|
|
93
|
+
| `merged_pull_requests(repository, window)` | `code-host:read` | `repos/{owner}/{repo}/pulls?state=closed&sort=updated&direction=desc`, kept when `merged_at` is in the window; paging stops before the window |
|
|
94
|
+
| `issues_updated(project, window)` | `tracker:read` | `repos/{owner}/{repo}/issues?state=all&since=...&sort=updated&direction=asc`, without pull requests |
|
|
95
|
+
|
|
96
|
+
Repositories and projects are written `owner/repo`; windows are half-open `[since, until)` with timezone-aware times;
|
|
97
|
+
results are oldest first. The adapter pages with the API's `page` and `per_page` parameters rather than
|
|
98
|
+
`gh api --paginate`, so it can stop as soon as the window is covered. A call reads at most 100 pages of 100 (10,000
|
|
99
|
+
items), 50 MiB in all and ten times `github.timeout`, then fails and says what to ask for instead: merged pull requests page through every pull request updated since `since`, even after `until`, so they need
|
|
100
|
+
a window that starts more recently; issues stop at `until`, so they need a shorter window. Why the pulls list and not
|
|
101
|
+
the search API: [ADR 0033](https://github.com/helmut-hoffer-von-ankershoffen/heinzel/blob/main/docs/adr/0033-github-rest-api-over-urllib.md); why `gh`:
|
|
102
|
+
[ADR 0035](https://github.com/helmut-hoffer-von-ankershoffen/heinzel/blob/main/docs/adr/0035-integrations-go-through-vendor-clis-as-an-ordinary-employee.md).
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
hz github info # this plugin, its version and the GitHub host it reads
|
|
106
|
+
hz system capability list # who reads what, through which adapter and hosts
|
|
107
|
+
hz system doctor # gh on PATH, its version and login, the bindings; never contacts GitHub
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Errors say what to do: no `gh`, or one too old, names where to get it; no login, or a login GitHub rejects, names
|
|
111
|
+
`gh auth login --hostname <host>`; a rate limit says to wait and how to see when it resets; a missing or forbidden
|
|
112
|
+
repository says to check the spelling and what your account can see; a slow call names the timeout setting.
|
|
113
|
+
|
|
114
|
+
How `gh` is run: with an argument list, never a shell, from your home directory, in its own process group, stopped
|
|
115
|
+
(SIGTERM, then SIGKILL) at the timeout, with its output capped, and with an allowlisted environment: never
|
|
116
|
+
Heinzelmännchen's `HZ_CREDENTIAL_*` variables or debug switches such as `GH_DEBUG`, which would print request
|
|
117
|
+
headers. Doctor runs only `gh --version` and looks for the host in gh's hosts file (or a token variable gh reads); it
|
|
118
|
+
never runs a command that prints a token. Everything GitHub or `gh` says is redacted of anything shaped like a GitHub
|
|
119
|
+
token, stripped of control and bidirectional characters, and only then cut to length; every string the adapter
|
|
120
|
+
returns is cleaned too.
|
|
121
|
+
|
|
122
|
+
Development and release commands: [repository guide](../../README.md).
|
|
123
|
+
|
|
124
|
+
Migration provenance: heinzel, before the 0.9 plugin-boundary migration.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"format": 1,
|
|
3
|
+
"sdk": 1,
|
|
4
|
+
"inputs": [
|
|
5
|
+
"coverage>=7.12",
|
|
6
|
+
"editables>=0.5",
|
|
7
|
+
"hatchling>=1.29.0",
|
|
8
|
+
"heinzel>=0.9.1,<0.10",
|
|
9
|
+
"mypy>=1.19.0,<3",
|
|
10
|
+
"pyright[nodejs]==1.1.414",
|
|
11
|
+
"pytest-timeout>=2.4.0,<3",
|
|
12
|
+
"pytest-xdist>=3.8",
|
|
13
|
+
"pytest>=9.0.3,<10",
|
|
14
|
+
"ruff==0.17.2"
|
|
15
|
+
],
|
|
16
|
+
"core": {
|
|
17
|
+
"version": "0.9.1",
|
|
18
|
+
"dependencies": [
|
|
19
|
+
"croniter>=6.2.4",
|
|
20
|
+
"fastapi>=0.161",
|
|
21
|
+
"keyring>=25.6",
|
|
22
|
+
"mcp>=2.3",
|
|
23
|
+
"packaging>=25.0",
|
|
24
|
+
"platformdirs>=4.5",
|
|
25
|
+
"pluggy<2,>=1.6.0",
|
|
26
|
+
"psutil==7.2.2",
|
|
27
|
+
"pydantic-ai-slim==2.61.0",
|
|
28
|
+
"pydantic-ai-slim[anthropic]==2.61.0; extra == \"anthropic\"",
|
|
29
|
+
"pydantic-ai-slim[openai]==2.61.0; extra == \"openai\"",
|
|
30
|
+
"pydantic-settings<3,>=2.11.0",
|
|
31
|
+
"pydantic<3,>=2.12.0",
|
|
32
|
+
"pyyaml<7,>=6.0.2",
|
|
33
|
+
"rich>=14.2.0",
|
|
34
|
+
"tomlkit>=0.13",
|
|
35
|
+
"typer>=0.23.0",
|
|
36
|
+
"uvicorn>=0.54",
|
|
37
|
+
"wsproto>=1.3"
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "heinzel-github"
|
|
3
|
+
version = "0.9.1"
|
|
4
|
+
description = "Read pull requests and issues from GitHub: the code-host and tracker capabilities."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
license-files = ["LICENSE"]
|
|
8
|
+
requires-python = ">=3.12"
|
|
9
|
+
dependencies = ["heinzel>=0.9.1,<0.10"]
|
|
10
|
+
|
|
11
|
+
[project.urls]
|
|
12
|
+
Source = "https://github.com/helmut-hoffer-von-ankershoffen/heinzel-plugins/tree/main/plugins/heinzel-github"
|
|
13
|
+
Changelog = "https://github.com/helmut-hoffer-von-ankershoffen/heinzel-plugins/blob/main/plugins/heinzel-github/CHANGELOG.md"
|
|
14
|
+
Issues = "https://github.com/helmut-hoffer-von-ankershoffen/heinzel-plugins/issues"
|
|
15
|
+
|
|
16
|
+
[project.entry-points."heinzel.modules"]
|
|
17
|
+
github = "heinzel_github"
|
|
18
|
+
|
|
19
|
+
[build-system]
|
|
20
|
+
requires = ["hatchling>=1.29.0"]
|
|
21
|
+
build-backend = "hatchling.build"
|
|
22
|
+
|
|
23
|
+
[dependency-groups]
|
|
24
|
+
test = ["editables>=0.5", "pytest>=9.0.3,<10", "pytest-timeout>=2.4.0,<3", "pytest-xdist>=3.8", "ruff==0.17.2", "mypy>=1.19.0,<3", "coverage>=7.12", "pyright[nodejs]==1.1.414"]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
[tool.ruff]
|
|
28
|
+
line-length = 120
|
|
29
|
+
fix = true
|
|
30
|
+
extend-exclude = ["template"] # rendered and checked by `make template-test`
|
|
31
|
+
|
|
32
|
+
[tool.ruff.lint]
|
|
33
|
+
select = ["ALL"]
|
|
34
|
+
# pydoclint (DOC): docstring sections must match what the code raises, returns and yields. Preview rules, opted into
|
|
35
|
+
# one by one so no other preview rule sneaks in.
|
|
36
|
+
preview = true
|
|
37
|
+
explicit-preview-rules = true
|
|
38
|
+
extend-select = ["DOC201", "DOC202", "DOC402", "DOC403", "DOC501", "DOC502"]
|
|
39
|
+
ignore = [
|
|
40
|
+
"COM812", # conflicts with the ruff formatter
|
|
41
|
+
"CPY001", # no copyright header in OSS files
|
|
42
|
+
"D203", # incompatible with D211
|
|
43
|
+
"DOC201", # a return value is documented by the type and the summary line; Returns: only when it adds meaning
|
|
44
|
+
"DOC502", # documenting exceptions raised by callees is useful for callers
|
|
45
|
+
"D213", # incompatible with D212
|
|
46
|
+
"FBT001", # boolean positional arguments are idiomatic for Typer options
|
|
47
|
+
"FBT002", # same as FBT001
|
|
48
|
+
"ISC001", # conflicts with the ruff formatter
|
|
49
|
+
"TD003", # todos don't need an issue link
|
|
50
|
+
"FIX002", # todos are allowed
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
[tool.ruff.lint.per-file-ignores]
|
|
54
|
+
".claude/hooks/*.py" = [
|
|
55
|
+
"D103", # hook entry points are documented by the module docstring
|
|
56
|
+
"INP001", # hooks are scripts, not a package
|
|
57
|
+
"S603", # subprocess calls with fixed argument lists
|
|
58
|
+
"S607", # tools (uv, git, make) are resolved from PATH on purpose
|
|
59
|
+
"T201", # hooks talk to the agent through stdout/stderr
|
|
60
|
+
]
|
|
61
|
+
"fuzz/*.py" = [
|
|
62
|
+
"INP001", # fuzz targets are scripts, not a package
|
|
63
|
+
]
|
|
64
|
+
"hatch_build.py" = [
|
|
65
|
+
"INP001", # the build hook hatch loads by path, not a package
|
|
66
|
+
]
|
|
67
|
+
"scripts/*.py" = [
|
|
68
|
+
"INP001", # scripts are not a package
|
|
69
|
+
"T201", # scripts report on stdout/stderr
|
|
70
|
+
]
|
|
71
|
+
"**/tests/**/*.py" = [
|
|
72
|
+
"ANN", # fixtures and tests don't need full annotations
|
|
73
|
+
"DOC", # test docstrings describe behaviour, not interfaces
|
|
74
|
+
"ARG", # fixtures are used for their side effects
|
|
75
|
+
"D", # test names are the documentation
|
|
76
|
+
"INP001", # tests are not a package
|
|
77
|
+
"PLC0415", # local imports keep fixtures isolated
|
|
78
|
+
"PLR2004", # magic values are fine in assertions
|
|
79
|
+
"S101", # asserts are what tests do
|
|
80
|
+
"S105", # fake tokens and passwords are test data (credentials, adapters for tracker and document services)
|
|
81
|
+
"S106", # same as S105, passed as arguments
|
|
82
|
+
"S603", # subprocess calls with trusted input
|
|
83
|
+
"S607", # partial executable paths (uv, hz) are resolved from PATH on purpose
|
|
84
|
+
"SLF001", # private access is needed for solitary unit tests
|
|
85
|
+
]
|
|
86
|
+
|
|
87
|
+
[tool.ruff.lint.pydocstyle]
|
|
88
|
+
convention = "google"
|
|
89
|
+
|
|
90
|
+
[tool.ruff.format]
|
|
91
|
+
docstring-code-format = true
|
|
92
|
+
|
|
93
|
+
[tool.mypy]
|
|
94
|
+
strict = true
|
|
95
|
+
mypy_path = ["src"]
|
|
96
|
+
files = ["src"]
|
|
97
|
+
plugins = ["pydantic.mypy"]
|
|
98
|
+
|
|
99
|
+
[tool.pydantic-mypy]
|
|
100
|
+
init_forbid_extra = true
|
|
101
|
+
init_typed = true
|
|
102
|
+
warn_required_dynamic_aliases = true
|
|
103
|
+
|
|
104
|
+
[tool.pytest]
|
|
105
|
+
minversion = "9.0"
|
|
106
|
+
strict = true
|
|
107
|
+
testpaths = ["tests"]
|
|
108
|
+
addopts = ["-ra", "--import-mode=importlib"]
|
|
109
|
+
filterwarnings = ["error"]
|
|
110
|
+
markers = ["unit: Offline isolated behavior.", "integration: Offline public-contract integration.", "e2e: Installed artifacts or real external services.", "perf: Explicit timing tests.", "long_running: Extended end-to-end budget."]
|
|
111
|
+
|
|
112
|
+
[tool.heinzel.plugin]
|
|
113
|
+
profiles = []
|
|
114
|
+
requires = ["core:plugin-api@1"]
|
|
115
|
+
provides = ["code-host:read", "tracker:read"]
|
|
116
|
+
|
|
117
|
+
[tool.coverage.run]
|
|
118
|
+
source = ["heinzel_github"]
|
|
119
|
+
branch = true
|
|
120
|
+
|
|
121
|
+
[tool.coverage.report]
|
|
122
|
+
fail_under = 90
|
|
123
|
+
show_missing = true
|
|
124
|
+
|
|
125
|
+
[tool.pyright]
|
|
126
|
+
typeCheckingMode = "strict"
|
|
127
|
+
include = ["src"]
|
|
128
|
+
pythonVersion = "3.12"
|
|
129
|
+
|
|
130
|
+
[tool.hatch.build.targets.sdist]
|
|
131
|
+
exclude = [".venv", ".acceptance", ".preview", "dist", "web/node_modules", "**/__pycache__", ".pytest_cache", ".mypy_cache", ".coverage*"]
|