little-sister-github 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,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Michael Meyling
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,141 @@
1
+ Metadata-Version: 2.4
2
+ Name: little-sister-github
3
+ Version: 0.1.0
4
+ Summary: GitHub overview check type for little-sister.
5
+ Keywords: monitoring,status,github,little-sister
6
+ Author: Michael Meyling
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Classifier: Programming Language :: Python :: 3.14
15
+ Requires-Dist: little-sister>=0.3.11
16
+ Requires-Python: >=3.11
17
+ Project-URL: homepage, https://github.com/m-31/little-sister-github
18
+ Project-URL: repository, https://github.com/m-31/little-sister-github
19
+ Project-URL: issues, https://github.com/m-31/little-sister-github/issues
20
+ Project-URL: changelog, https://github.com/m-31/little-sister-github/blob/main/CHANGELOG.md
21
+ Description-Content-Type: text/markdown
22
+
23
+ # little-sister-github
24
+
25
+ The **`github`** check type for [little-sister](https://github.com/m-31/little-sister):
26
+ one node per configured team, with a child per aspect — open pull requests,
27
+ Dependabot advisories, code-scanning and secret-scanning alerts, SBOM presence,
28
+ workflow runs and open issues.
29
+
30
+ Every finding is an individually addressable line, so an operator who opens a
31
+ ticket for one alert can put **that line** into maintenance and the rest of the
32
+ aspect keeps reporting.
33
+
34
+ ## The contract
35
+
36
+ - **Requires** `little-sister >= 0.3.11` — a floor, never a pin.
37
+ - **Runs on** Python **3.11 or newer** — the library's floor, not a higher
38
+ one of its own.
39
+ - **Registers** one check type: **`github`**.
40
+
41
+ ## Install
42
+
43
+ ```toml
44
+ # your deployment's pyproject.toml
45
+ [project]
46
+ dependencies = ["little-sister", "little-sister-github"]
47
+
48
+ # Only while *this* one comes from git: little-sister resolves from the index.
49
+ # Delete the table once this package is on an index too — nothing else changes.
50
+ [tool.uv.sources]
51
+ little-sister-github = { git = "…/little-sister-github.git", tag = "v0.1.0" }
52
+ ```
53
+
54
+ ```python
55
+ # wsgi.py — registrations first, the app last. The order is load-bearing:
56
+ # importing little_sister.app builds the engine and loads the check configs, so
57
+ # every check type must already be registered. `isort: off` keeps an import
58
+ # sorter from quietly reversing that.
59
+ # isort: off
60
+ import little_sister_github # noqa: F401 registers the `github` type
61
+ from little_sister.app import app
62
+ # isort: on
63
+
64
+ __all__ = ["app"] # without it, lint calls the app import unused
65
+ ```
66
+
67
+ ## Configure
68
+
69
+ Copy [`examples/github.yaml`](https://github.com/m-31/little-sister-github/blob/v0.1.0/examples/github.yaml) into your deployment's
70
+ `config/checks/`, set `org`, `team` and the token reference, and you are done —
71
+ one file per team. The credential is a **reference**, never a value:
72
+
73
+ ```yaml
74
+ type: github
75
+ path: /platform/github
76
+ secrets:
77
+ token: env://PLATFORM_GITHUB_TOKEN
78
+ org: example-org
79
+ team: platform
80
+ ```
81
+
82
+ The token needs `read:org`, `repo`, `security_events` and dependency-graph read
83
+ access. Each team's check carries its own credential, so a second team is a second
84
+ config file rather than a code change.
85
+
86
+ The per-aspect display text ships **with the type** and expands `{org}` / `{team}`
87
+ from the config, so it is not copied per team. Your deployment's own policy — a
88
+ remediation deadline, who to notify — goes in that config's `subnodes:` block,
89
+ appended to the shipped text with `{default}`.
90
+
91
+ ## What it reads
92
+
93
+ | Aspect | Endpoint | Grade |
94
+ |---|---|---|
95
+ | `pull_requests` | `GET /repos/{r}/pulls?state=open` | any open PR (minus `ignore_title_prefixes`) → **WARN** |
96
+ | `security_advisories` | `GET /repos/{r}/dependabot/alerts?state=open` | one leaf per selected severity, graded by `security_advisories.severity_map` |
97
+ | `code_scanning_alerts` | `GET /repos/{r}/code-scanning/alerts?state=open` | one leaf per severity, graded by `code_scanning_alerts.severity_map` |
98
+ | `secret_scanning_alerts` | `GET /repos/{r}/secret-scanning/alerts?state=open` | any open alert → **ERROR**; scanning disabled → **ERROR** (`secret_scanning.require_enabled`) |
99
+ | `sbom_check` | `GET /repos/{r}/dependency-graph/sbom` | no dependency graph → **ERROR** (`sbom_check.ignore`) |
100
+ | `actions` | `GET /repos/{r}/actions/runs` | coded entries: last completed verdict, plus a newer in-flight run |
101
+ | `issues` | `GET /repos/{r}/issues?state=open` | any open issue → **WARN** (`issues.ignore`); issues disabled → **WARN** |
102
+
103
+ The check's own node carries the discovery coverage reading (`expect_min_repos`)
104
+ and the repository roster, and rolls up worst-of its aspects. Only stdlib
105
+ `urllib` is used — the package has no dependency but little-sister itself.
106
+
107
+ ## Develop
108
+
109
+ little-sister is declared as a **floor** — the release that promised the surface
110
+ this package imports — and it resolves **from the index**, like any other
111
+ dependency. There is no `[tool.uv.sources]` table here, and the committed
112
+ `uv.lock` is what a release runs against. To work against a local library
113
+ checkout, add the redirect and **do not commit it**: uv reads the sources table of
114
+ a dependency it resolves from a path or a checkout, so a committed line would
115
+ follow this package into every deployment that installs it.
116
+
117
+ ```toml
118
+ # pyproject.toml — locally, never committed
119
+ [tool.uv.sources]
120
+ little-sister = { path = "../little-sister" }
121
+ ```
122
+
123
+ Restore `uv.lock` with it. The next `uv run` — the pre-commit gate is one — rewrites
124
+ the lock to `source = { directory = … }`, so a redirect kept out of `pyproject.toml`
125
+ can still reach a commit through the lock beside it.
126
+
127
+ ```bash
128
+ uv sync
129
+ uv run ruff check
130
+ uv run mypy
131
+ uv run mypy --python-version 3.11 # against the floor, not the interpreter you have
132
+ uv run pytest -q
133
+ # The same gate runs before every commit once the hook is enabled:
134
+ git config core.hooksPath hooks
135
+ ```
136
+
137
+ The tests are fixture-based; nothing in this repository calls GitHub.
138
+
139
+ ## License
140
+
141
+ MIT — see [LICENSE](https://github.com/m-31/little-sister-github/blob/v0.1.0/LICENSE).
@@ -0,0 +1,119 @@
1
+ # little-sister-github
2
+
3
+ The **`github`** check type for [little-sister](https://github.com/m-31/little-sister):
4
+ one node per configured team, with a child per aspect — open pull requests,
5
+ Dependabot advisories, code-scanning and secret-scanning alerts, SBOM presence,
6
+ workflow runs and open issues.
7
+
8
+ Every finding is an individually addressable line, so an operator who opens a
9
+ ticket for one alert can put **that line** into maintenance and the rest of the
10
+ aspect keeps reporting.
11
+
12
+ ## The contract
13
+
14
+ - **Requires** `little-sister >= 0.3.11` — a floor, never a pin.
15
+ - **Runs on** Python **3.11 or newer** — the library's floor, not a higher
16
+ one of its own.
17
+ - **Registers** one check type: **`github`**.
18
+
19
+ ## Install
20
+
21
+ ```toml
22
+ # your deployment's pyproject.toml
23
+ [project]
24
+ dependencies = ["little-sister", "little-sister-github"]
25
+
26
+ # Only while *this* one comes from git: little-sister resolves from the index.
27
+ # Delete the table once this package is on an index too — nothing else changes.
28
+ [tool.uv.sources]
29
+ little-sister-github = { git = "…/little-sister-github.git", tag = "v0.1.0" }
30
+ ```
31
+
32
+ ```python
33
+ # wsgi.py — registrations first, the app last. The order is load-bearing:
34
+ # importing little_sister.app builds the engine and loads the check configs, so
35
+ # every check type must already be registered. `isort: off` keeps an import
36
+ # sorter from quietly reversing that.
37
+ # isort: off
38
+ import little_sister_github # noqa: F401 registers the `github` type
39
+ from little_sister.app import app
40
+ # isort: on
41
+
42
+ __all__ = ["app"] # without it, lint calls the app import unused
43
+ ```
44
+
45
+ ## Configure
46
+
47
+ Copy [`examples/github.yaml`](https://github.com/m-31/little-sister-github/blob/v0.1.0/examples/github.yaml) into your deployment's
48
+ `config/checks/`, set `org`, `team` and the token reference, and you are done —
49
+ one file per team. The credential is a **reference**, never a value:
50
+
51
+ ```yaml
52
+ type: github
53
+ path: /platform/github
54
+ secrets:
55
+ token: env://PLATFORM_GITHUB_TOKEN
56
+ org: example-org
57
+ team: platform
58
+ ```
59
+
60
+ The token needs `read:org`, `repo`, `security_events` and dependency-graph read
61
+ access. Each team's check carries its own credential, so a second team is a second
62
+ config file rather than a code change.
63
+
64
+ The per-aspect display text ships **with the type** and expands `{org}` / `{team}`
65
+ from the config, so it is not copied per team. Your deployment's own policy — a
66
+ remediation deadline, who to notify — goes in that config's `subnodes:` block,
67
+ appended to the shipped text with `{default}`.
68
+
69
+ ## What it reads
70
+
71
+ | Aspect | Endpoint | Grade |
72
+ |---|---|---|
73
+ | `pull_requests` | `GET /repos/{r}/pulls?state=open` | any open PR (minus `ignore_title_prefixes`) → **WARN** |
74
+ | `security_advisories` | `GET /repos/{r}/dependabot/alerts?state=open` | one leaf per selected severity, graded by `security_advisories.severity_map` |
75
+ | `code_scanning_alerts` | `GET /repos/{r}/code-scanning/alerts?state=open` | one leaf per severity, graded by `code_scanning_alerts.severity_map` |
76
+ | `secret_scanning_alerts` | `GET /repos/{r}/secret-scanning/alerts?state=open` | any open alert → **ERROR**; scanning disabled → **ERROR** (`secret_scanning.require_enabled`) |
77
+ | `sbom_check` | `GET /repos/{r}/dependency-graph/sbom` | no dependency graph → **ERROR** (`sbom_check.ignore`) |
78
+ | `actions` | `GET /repos/{r}/actions/runs` | coded entries: last completed verdict, plus a newer in-flight run |
79
+ | `issues` | `GET /repos/{r}/issues?state=open` | any open issue → **WARN** (`issues.ignore`); issues disabled → **WARN** |
80
+
81
+ The check's own node carries the discovery coverage reading (`expect_min_repos`)
82
+ and the repository roster, and rolls up worst-of its aspects. Only stdlib
83
+ `urllib` is used — the package has no dependency but little-sister itself.
84
+
85
+ ## Develop
86
+
87
+ little-sister is declared as a **floor** — the release that promised the surface
88
+ this package imports — and it resolves **from the index**, like any other
89
+ dependency. There is no `[tool.uv.sources]` table here, and the committed
90
+ `uv.lock` is what a release runs against. To work against a local library
91
+ checkout, add the redirect and **do not commit it**: uv reads the sources table of
92
+ a dependency it resolves from a path or a checkout, so a committed line would
93
+ follow this package into every deployment that installs it.
94
+
95
+ ```toml
96
+ # pyproject.toml — locally, never committed
97
+ [tool.uv.sources]
98
+ little-sister = { path = "../little-sister" }
99
+ ```
100
+
101
+ Restore `uv.lock` with it. The next `uv run` — the pre-commit gate is one — rewrites
102
+ the lock to `source = { directory = … }`, so a redirect kept out of `pyproject.toml`
103
+ can still reach a commit through the lock beside it.
104
+
105
+ ```bash
106
+ uv sync
107
+ uv run ruff check
108
+ uv run mypy
109
+ uv run mypy --python-version 3.11 # against the floor, not the interpreter you have
110
+ uv run pytest -q
111
+ # The same gate runs before every commit once the hook is enabled:
112
+ git config core.hooksPath hooks
113
+ ```
114
+
115
+ The tests are fixture-based; nothing in this repository calls GitHub.
116
+
117
+ ## License
118
+
119
+ MIT — see [LICENSE](https://github.com/m-31/little-sister-github/blob/v0.1.0/LICENSE).
@@ -0,0 +1,125 @@
1
+ [project]
2
+ name = "little-sister-github"
3
+ version = "0.1.0"
4
+ description = "GitHub overview check type for little-sister."
5
+ authors = [
6
+ { name = "Michael Meyling" }
7
+ ]
8
+ readme = {file = "README.md", content-type = "text/markdown"}
9
+ license = "MIT"
10
+ license-files = ["LICENSE"]
11
+ keywords = ["monitoring", "status", "github", "little-sister"]
12
+ classifiers = [
13
+ "Development Status :: 3 - Alpha",
14
+ "Programming Language :: Python :: 3",
15
+ "Programming Language :: Python :: 3.11",
16
+ "Programming Language :: Python :: 3.12",
17
+ "Programming Language :: Python :: 3.13",
18
+ "Programming Language :: Python :: 3.14",
19
+ ]
20
+ # The **library's floor**, not the interpreter this is developed on
21
+ # (little-sister ADR-0054). Asking for more than the library asks would mean
22
+ # somebody installs little-sister on 3.12 and then cannot install the package
23
+ # that was the reason they came — and metadata is frozen per artifact, so
24
+ # correcting it afterwards costs a version. The classifiers above repeat the
25
+ # same claim for a human reading the index rather than a resolver, which is why
26
+ # they start at the floor and run contiguously; the release check refuses a
27
+ # disagreement between the two.
28
+ requires-python = ">=3.11"
29
+ # A **floor, and never a pin.** A deployment pins little-sister; two plugins that
30
+ # each pinned an exact version could not be installed together at all. The floor
31
+ # names the release that promised the check-authoring surface this module imports
32
+ # (little-sister architecture.md §11) — a statement about that surface, not about
33
+ # a release. The upgrade a floor cannot see, the library moving *past* the
34
+ # surface, is caught by `require_api()` in `__init__.py`.
35
+ #
36
+ # **0.3.11 rather than 0.3.0**, which is where the surface was first promised:
37
+ # 0.3.0 was tagged but never uploaded, so it names no version a resolver can
38
+ # actually fetch, and it is 0.3.11 that lowered the library's own floor to 3.11 —
39
+ # the floor this package promises above. A lower number would ask the index for
40
+ # something it does not have, on an interpreter the answer could not run.
41
+ dependencies = [
42
+ "little-sister>=0.3.11",
43
+ ]
44
+
45
+ # The public home, and the reason it was absent until now: metadata pointing at a
46
+ # URL that does not resolve is worse than metadata that says nothing.
47
+ #
48
+ # `repository` is **load-bearing, not a duplicate of `homepage`**: the release
49
+ # refuses without it, and the published README's absolute links are built from it at
50
+ # build time — `blob/v<version>`, so a link is immutable and cannot outlive the
51
+ # artifact that carries it. Relocating the forge is then an edit to this one line.
52
+ #
53
+ # No `documentation` key: for a package this size the README *is* the documentation,
54
+ # and it arrives with the artifact. A key pointing at a directory that holds one
55
+ # design record would promise a manual that does not exist.
56
+ [project.urls]
57
+ homepage = "https://github.com/m-31/little-sister-github"
58
+ repository = "https://github.com/m-31/little-sister-github"
59
+ issues = "https://github.com/m-31/little-sister-github/issues"
60
+ changelog = "https://github.com/m-31/little-sister-github/blob/main/CHANGELOG.md"
61
+
62
+ # The one line that differs from a deployment's pyproject.toml: a plugin is a
63
+ # **built** package, so a consumer can install it from a tag (and later an index).
64
+ [tool.uv]
65
+ package = true
66
+
67
+ # **No `[tool.uv.sources]`, deliberately.** little-sister is on the index, so the
68
+ # floor above resolves like any other dependency and there is nothing left to
69
+ # redirect. The table is not merely unnecessary now, it is harmful: uv reads the
70
+ # sources table of a dependency it resolves from a path or a git checkout, so a
71
+ # committed line here would be imposed on **every deployment that installs this
72
+ # package** (little-sister `plugin-repository.md` §3).
73
+ #
74
+ # To develop against a local library checkout, add the redirect and do not commit
75
+ # it:
76
+ #
77
+ # [tool.uv.sources]
78
+ # little-sister = { path = "../little-sister" }
79
+ #
80
+ # It leaves a second trace: the next `uv run` rewrites the committed `uv.lock` to
81
+ # `source = { directory = … }`, and the pre-commit gate is a `uv run`. Restore both
82
+ # files before committing, or the path this table exists to keep out of the package
83
+ # arrives in the lock instead.
84
+
85
+ [dependency-groups]
86
+ # Floors match little-sister's dev group, so every repository in the family runs
87
+ # one gate and a contributor moving between them meets one set of rules.
88
+ dev = [
89
+ "mypy>=2.1.0",
90
+ "pytest>=9.0.3",
91
+ "pytest-cov>=7.1.0",
92
+ "ruff>=0.16.1",
93
+ "types-PyYAML",
94
+ ]
95
+
96
+ [tool.ruff]
97
+ target-version = "py311"
98
+
99
+ [tool.ruff.lint]
100
+ # Copied from little-sister as it stands. Beyond ruff's default (E, F): import
101
+ # sorting (I), pyupgrade (UP), bugbear (B), comprehensions (C4) and Ruff's own
102
+ # rules (RUF). The ambiguous-unicode rules are off: reason and subnode text use
103
+ # typographic "—", "…" and "×" on purpose.
104
+ select = ["E", "F", "W", "I", "UP", "B", "C4", "RUF"]
105
+ ignore = ["RUF001", "RUF002", "RUF003"]
106
+
107
+ [tool.mypy]
108
+ files = ["src"]
109
+ strict = true
110
+
111
+ [tool.pytest.ini_options]
112
+ # Adapted, not copied: little-sister's own `addopts` names `--cov=little_sister`,
113
+ # which from here would measure somebody else's code.
114
+ addopts = "--cov=little_sister_github --cov-report=term-missing"
115
+
116
+ [build-system]
117
+ # One **major** line, not the `<0.12.0` uv generates: that pins the minor uv shipped
118
+ # with and turns every uv upgrade into a release-blocking edit in every repository of
119
+ # the family. The backend's contract here is one sentence long — a
120
+ # `src/<import package>` layout — so a minor bump has nothing to break, and what
121
+ # would notice one that changed the artifact anyway is not vigilance but the publish
122
+ # step, which opens the wheel and the sdist and refuses anything the release tree
123
+ # drops. Same bound as little-sister, verified there against both minors.
124
+ requires = ["uv_build>=0.11.14,<1.0.0"]
125
+ build-backend = "uv_build"
@@ -0,0 +1,24 @@
1
+ """little-sister check type: ``github``.
2
+
3
+ Importing this package registers the type in little-sister's ``CHECK_TYPES``. A
4
+ deployment therefore needs one line in its ``wsgi.py``, before it imports
5
+ ``little_sister.app``::
6
+
7
+ import little_sister_github # noqa: F401 registers its type
8
+ from little_sister.app import app # noqa: E402 builds the engine
9
+
10
+ ``require_api`` declares the **check API epoch** this package was built for. It
11
+ refuses at startup, naming both epochs, when the library has moved past the
12
+ surface below — the upgrade an install-time floor cannot see, because a floor has
13
+ no ceiling (little-sister ADR-0051). Without it the same mismatch would surface as
14
+ an ``ImportError`` from inside this package, which reads like our bug.
15
+ """
16
+ from little_sister.checks import require_api
17
+
18
+ require_api(1)
19
+
20
+ from little_sister_github import github # noqa: E402 registers the type
21
+
22
+ __version__ = "0.1.0"
23
+
24
+ __all__ = ["github"]