github-security-report 0.20.0__tar.gz → 0.21.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.
- {github_security_report-0.20.0 → github_security_report-0.21.1}/PKG-INFO +32 -19
- {github_security_report-0.20.0 → github_security_report-0.21.1}/README.md +29 -16
- {github_security_report-0.20.0 → github_security_report-0.21.1}/pyproject.toml +4 -4
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/_version.py +2 -2
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/cli/progress.py +62 -42
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/cli/serialise.py +2 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/malware.py +4 -2
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/render/html.py +2 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/render/markdown.py +3 -1
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/render/terminal.py +6 -1
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/templates/report.html.j2 +1 -1
- {github_security_report-0.20.0 → github_security_report-0.21.1}/.gitignore +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/LICENSE +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/LICENSES/Apache-2.0.txt +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/scripts/README.md +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/__init__.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/authors.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/categories/__init__.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/categories/keys.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/categories/signals.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/categories/tables.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/classify.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/cli/__init__.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/cli/__main__.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/cli/app.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/cli/boundary.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/cli/modes.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/cli/options.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/cli/outputs.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/cli/publish.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/cli/remediation.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/cli/summary.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/client/__init__.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/client/accounting.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/client/alerts.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/client/analyses.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/client/batch_errors.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/client/codeql_parsers.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/client/copilot.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/client/endpoints.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/client/errors.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/client/events.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/client/failures.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/client/org_reads.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/client/paging.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/client/parsers.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/client/queries.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/client/reads.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/client/repo_parser.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/client/reviews.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/client/transport.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/client/workflow_runs.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/client/writes.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/code_quality.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/codeql/__init__.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/codeql/cleanup.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/codeql/facts.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/codeql/tables.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/collect/__init__.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/collect/codeql.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/collect/context.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/collect/extras.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/collect/graph.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/collect/org.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/collect/protocols.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/collect/repo.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/collect/run_sync.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/collect/scoping.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/collect/workflow_runs.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/config/__init__.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/config/blocks.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/config/loader.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/config/models.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/config/order.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/config/schema.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/gating.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/gitctx.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/issue_presentation.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/issues.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/layout.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/models.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/ordering.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/posture/__init__.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/posture/enablement.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/posture/facts.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/posture/quality.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/posture/releases.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/pulls/__init__.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/pulls/columns.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/pulls/counting.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/pulls/presentation.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/pulls/table.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/py.typed +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/ranking.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/remediate/__init__.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/remediate/codeql.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/remediate/model.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/render/__init__.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/render/slack.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/render/slack_limits.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/render/slack_text.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/render/terminal_notices.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/report/__init__.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/report/aggregate.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/report/display.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/report/signals.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/report/tables.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/rulesets.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/runner.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/scope.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/secret_patterns.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/severity.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/summary.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/templates/index.html.j2 +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/workflow_runs/__init__.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/workflow_runs/aggregate.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/workflow_runs/models.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/workflow_runs/store.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/workflow_runs/store_codec.py +0 -0
- {github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/workflow_runs/tables.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: github-security-report
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.21.1
|
|
4
4
|
Summary: Security and quality reporting across GitHub organisations
|
|
5
5
|
Project-URL: Homepage, https://github.com/lfreleng-actions/github-security-report-action
|
|
6
6
|
Project-URL: Repository, https://github.com/lfreleng-actions/github-security-report-action
|
|
@@ -32,11 +32,11 @@ Requires-Dist: rich==15.0.0
|
|
|
32
32
|
Requires-Dist: typer==0.27.3
|
|
33
33
|
Provides-Extra: dev
|
|
34
34
|
Requires-Dist: mypy==2.4.0; extra == 'dev'
|
|
35
|
-
Requires-Dist: pytest-asyncio==1.4.
|
|
35
|
+
Requires-Dist: pytest-asyncio==1.4.1; extra == 'dev'
|
|
36
36
|
Requires-Dist: pytest-cov==7.1.0; extra == 'dev'
|
|
37
37
|
Requires-Dist: pytest==9.1.1; extra == 'dev'
|
|
38
38
|
Requires-Dist: respx==0.23.1; extra == 'dev'
|
|
39
|
-
Requires-Dist: ruff==0.17.
|
|
39
|
+
Requires-Dist: ruff==0.17.3; extra == 'dev'
|
|
40
40
|
Requires-Dist: syrupy==6.1.1; extra == 'dev'
|
|
41
41
|
Requires-Dist: types-jsonschema==4.26.0.20261006; extra == 'dev'
|
|
42
42
|
Requires-Dist: types-pyyaml==6.0.12.20250915; extra == 'dev'
|
|
@@ -415,6 +415,18 @@ that only shape an organisation-wide run — `output_dir`, `pages_url`,
|
|
|
415
415
|
misconfigured workflow says so instead of quietly producing a report that
|
|
416
416
|
ignores them.
|
|
417
417
|
|
|
418
|
+
### Runtime isolation
|
|
419
|
+
|
|
420
|
+
The action installs its own uv-managed Python and runs the tool from an
|
|
421
|
+
action-owned virtual environment with `python -I`, so interpreter settings an
|
|
422
|
+
earlier step of the caller's job exports (`UV_PYTHON`, `VIRTUAL_ENV`,
|
|
423
|
+
`CONDA_PREFIX`, `PYTHONPATH` and similar) cannot choose what runs.
|
|
424
|
+
|
|
425
|
+
The action trusts the caller's `PATH`. The runner resolves `bash` for every
|
|
426
|
+
`shell: bash` step on it, and setup-uv runs helpers such as `tar` from it, so
|
|
427
|
+
no composite action can defend against a caller that has put a directory from
|
|
428
|
+
its checkout on `PATH`.
|
|
429
|
+
|
|
418
430
|
## Configuration
|
|
419
431
|
|
|
420
432
|
Configuration is JSON, supplied as a plain `vars.` entry or base64-encoded in a
|
|
@@ -1850,24 +1862,25 @@ Repo mode leaves nothing behind.
|
|
|
1850
1862
|
|
|
1851
1863
|
### Progress and run summary
|
|
1852
1864
|
|
|
1853
|
-
On an interactive terminal an org-mode run keeps
|
|
1854
|
-
of the screen while it collects
|
|
1855
|
-
|
|
1856
|
-
request counts with how many are in flight, and
|
|
1857
|
-
|
|
1865
|
+
On an interactive terminal an org-mode run keeps three live lines at the
|
|
1866
|
+
bottom of the screen while it collects, one kind of information each: where
|
|
1867
|
+
the run is (the organisation and the phase it is in), its API traffic (the
|
|
1868
|
+
REST and GraphQL request counts with how many are in flight, and external
|
|
1869
|
+
requests once there are any), and its progress (the elapsed time, the
|
|
1870
|
+
repository count where the phase works per repository, and a countdown while
|
|
1871
|
+
GitHub has the run paused for a rate limit). A fourth line appears while the
|
|
1858
1872
|
workflow-run store is being built for the first time, since that one-off
|
|
1859
|
-
dominates a cold run.
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1873
|
+
dominates a cold run. Each line stays one line: on a terminal too narrow for
|
|
1874
|
+
it, it is cut short with an ellipsis rather than wrapped, and the elapsed
|
|
1875
|
+
time, leading its line, is the last thing to go. Warnings logged in the
|
|
1876
|
+
meantime are held and replayed, in order, once the display comes down.
|
|
1863
1877
|
|
|
1864
1878
|
```text
|
|
1865
|
-
⠧ lfreleng-actions · probing repositories
|
|
1866
|
-
|
|
1879
|
+
⠧ lfreleng-actions · probing repositories
|
|
1880
|
+
REST 812 (9 in flight) · GraphQL 24
|
|
1881
|
+
38.2 s · 41/120 repos · ⏳ rate limited by GitHub (core); resuming in 42 s
|
|
1867
1882
|
```
|
|
1868
1883
|
|
|
1869
|
-
<!-- markdownlint-enable MD013 -->
|
|
1870
|
-
|
|
1871
1884
|
Before each organisation's report, a dim three-line summary says what the
|
|
1872
1885
|
run cost and what the store holds, so a slow run explains itself without
|
|
1873
1886
|
`--verbose`:
|
|
@@ -1890,9 +1903,9 @@ reads `in memory (not cached)` and, since its store is empty every time, is
|
|
|
1890
1903
|
never a first run. The summary is for the terminal only: nothing of it
|
|
1891
1904
|
reaches the Markdown, HTML, JSON or Slack output.
|
|
1892
1905
|
|
|
1893
|
-
The live
|
|
1894
|
-
not a terminal, and with `--verbose`, whose log lines would fight it for
|
|
1895
|
-
screen. The summary prints in every case. The same figures reach the log at
|
|
1906
|
+
The live display is off under `--no-progress`, when `CI` is set, when stdout
|
|
1907
|
+
is not a terminal, and with `--verbose`, whose log lines would fight it for
|
|
1908
|
+
the screen. The summary prints in every case. The same figures reach the log at
|
|
1896
1909
|
`INFO` under `--verbose`.
|
|
1897
1910
|
|
|
1898
1911
|
### GitHub Enterprise Server
|
|
@@ -371,6 +371,18 @@ that only shape an organisation-wide run — `output_dir`, `pages_url`,
|
|
|
371
371
|
misconfigured workflow says so instead of quietly producing a report that
|
|
372
372
|
ignores them.
|
|
373
373
|
|
|
374
|
+
### Runtime isolation
|
|
375
|
+
|
|
376
|
+
The action installs its own uv-managed Python and runs the tool from an
|
|
377
|
+
action-owned virtual environment with `python -I`, so interpreter settings an
|
|
378
|
+
earlier step of the caller's job exports (`UV_PYTHON`, `VIRTUAL_ENV`,
|
|
379
|
+
`CONDA_PREFIX`, `PYTHONPATH` and similar) cannot choose what runs.
|
|
380
|
+
|
|
381
|
+
The action trusts the caller's `PATH`. The runner resolves `bash` for every
|
|
382
|
+
`shell: bash` step on it, and setup-uv runs helpers such as `tar` from it, so
|
|
383
|
+
no composite action can defend against a caller that has put a directory from
|
|
384
|
+
its checkout on `PATH`.
|
|
385
|
+
|
|
374
386
|
## Configuration
|
|
375
387
|
|
|
376
388
|
Configuration is JSON, supplied as a plain `vars.` entry or base64-encoded in a
|
|
@@ -1806,24 +1818,25 @@ Repo mode leaves nothing behind.
|
|
|
1806
1818
|
|
|
1807
1819
|
### Progress and run summary
|
|
1808
1820
|
|
|
1809
|
-
On an interactive terminal an org-mode run keeps
|
|
1810
|
-
of the screen while it collects
|
|
1811
|
-
|
|
1812
|
-
request counts with how many are in flight, and
|
|
1813
|
-
|
|
1821
|
+
On an interactive terminal an org-mode run keeps three live lines at the
|
|
1822
|
+
bottom of the screen while it collects, one kind of information each: where
|
|
1823
|
+
the run is (the organisation and the phase it is in), its API traffic (the
|
|
1824
|
+
REST and GraphQL request counts with how many are in flight, and external
|
|
1825
|
+
requests once there are any), and its progress (the elapsed time, the
|
|
1826
|
+
repository count where the phase works per repository, and a countdown while
|
|
1827
|
+
GitHub has the run paused for a rate limit). A fourth line appears while the
|
|
1814
1828
|
workflow-run store is being built for the first time, since that one-off
|
|
1815
|
-
dominates a cold run.
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1829
|
+
dominates a cold run. Each line stays one line: on a terminal too narrow for
|
|
1830
|
+
it, it is cut short with an ellipsis rather than wrapped, and the elapsed
|
|
1831
|
+
time, leading its line, is the last thing to go. Warnings logged in the
|
|
1832
|
+
meantime are held and replayed, in order, once the display comes down.
|
|
1819
1833
|
|
|
1820
1834
|
```text
|
|
1821
|
-
⠧ lfreleng-actions · probing repositories
|
|
1822
|
-
|
|
1835
|
+
⠧ lfreleng-actions · probing repositories
|
|
1836
|
+
REST 812 (9 in flight) · GraphQL 24
|
|
1837
|
+
38.2 s · 41/120 repos · ⏳ rate limited by GitHub (core); resuming in 42 s
|
|
1823
1838
|
```
|
|
1824
1839
|
|
|
1825
|
-
<!-- markdownlint-enable MD013 -->
|
|
1826
|
-
|
|
1827
1840
|
Before each organisation's report, a dim three-line summary says what the
|
|
1828
1841
|
run cost and what the store holds, so a slow run explains itself without
|
|
1829
1842
|
`--verbose`:
|
|
@@ -1846,9 +1859,9 @@ reads `in memory (not cached)` and, since its store is empty every time, is
|
|
|
1846
1859
|
never a first run. The summary is for the terminal only: nothing of it
|
|
1847
1860
|
reaches the Markdown, HTML, JSON or Slack output.
|
|
1848
1861
|
|
|
1849
|
-
The live
|
|
1850
|
-
not a terminal, and with `--verbose`, whose log lines would fight it for
|
|
1851
|
-
screen. The summary prints in every case. The same figures reach the log at
|
|
1862
|
+
The live display is off under `--no-progress`, when `CI` is set, when stdout
|
|
1863
|
+
is not a terminal, and with `--verbose`, whose log lines would fight it for
|
|
1864
|
+
the screen. The summary prints in every case. The same figures reach the log at
|
|
1852
1865
|
`INFO` under `--verbose`.
|
|
1853
1866
|
|
|
1854
1867
|
### GitHub Enterprise Server
|
|
@@ -60,12 +60,12 @@ Issues = "https://github.com/lfreleng-actions/github-security-report-action/issu
|
|
|
60
60
|
[project.optional-dependencies]
|
|
61
61
|
dev = [
|
|
62
62
|
"pytest==9.1.1",
|
|
63
|
-
"pytest-asyncio==1.4.
|
|
63
|
+
"pytest-asyncio==1.4.1",
|
|
64
64
|
"pytest-cov==7.1.0",
|
|
65
65
|
"respx==0.23.1",
|
|
66
66
|
"syrupy==6.1.1",
|
|
67
67
|
"mypy==2.4.0",
|
|
68
|
-
"ruff==0.17.
|
|
68
|
+
"ruff==0.17.3",
|
|
69
69
|
"types-jsonschema==4.26.0.20261006",
|
|
70
70
|
"types-PyYAML==6.0.12.20250915",
|
|
71
71
|
]
|
|
@@ -92,12 +92,12 @@ include = ["src", "README.md", "LICENSE*", "pyproject.toml"]
|
|
|
92
92
|
[dependency-groups]
|
|
93
93
|
dev = [
|
|
94
94
|
"pytest==9.1.1",
|
|
95
|
-
"pytest-asyncio==1.4.
|
|
95
|
+
"pytest-asyncio==1.4.1",
|
|
96
96
|
"pytest-cov==7.1.0",
|
|
97
97
|
"respx==0.23.1",
|
|
98
98
|
"syrupy==6.1.1",
|
|
99
99
|
"mypy==2.4.0",
|
|
100
|
-
"ruff==0.17.
|
|
100
|
+
"ruff==0.17.3",
|
|
101
101
|
"types-jsonschema==4.26.0.20261006",
|
|
102
102
|
"types-PyYAML==6.0.12.20250915",
|
|
103
103
|
]
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '0.
|
|
22
|
-
__version_tuple__ = version_tuple = (0,
|
|
21
|
+
__version__ = version = '0.21.1'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 21, 1)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -6,14 +6,20 @@
|
|
|
6
6
|
:class:`~github_security_report.client.events.ProgressSink` the transport
|
|
7
7
|
reports requests to and the
|
|
8
8
|
:class:`~github_security_report.collect.context.CollectProgress` the
|
|
9
|
-
collectors name their phases through, and renders what it hears as
|
|
10
|
-
``Live``
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
the
|
|
15
|
-
|
|
16
|
-
|
|
9
|
+
collectors name their phases through, and renders what it hears as a Rich
|
|
10
|
+
``Live`` display of three lines per organisation, one category each so none
|
|
11
|
+
is cut short on an 80-column terminal:
|
|
12
|
+
|
|
13
|
+
1. where the run is: a spinner, the organisation and the phase;
|
|
14
|
+
2. API traffic: the REST and GraphQL request counts with how many are in
|
|
15
|
+
flight, and the external count once one has been made;
|
|
16
|
+
3. progress: the clock, the repository count where the phase is
|
|
17
|
+
per-repository, and a rate-limit backoff's countdown while it lasts.
|
|
18
|
+
|
|
19
|
+
A fourth line appears only while the workflow-run store is built for the
|
|
20
|
+
first time, a notice too long to share the third.
|
|
21
|
+
|
|
22
|
+
While the display is on screen, WARNINGs bound for the terminal are held and
|
|
17
23
|
replayed once it stops, so a transport warning cannot tear through it and the
|
|
18
24
|
operator still reads every one, in order, in a single block before the run
|
|
19
25
|
summary. Off (a :class:`NullTracker`) in CI, when stdout is not a terminal,
|
|
@@ -167,7 +173,7 @@ class _LogHold:
|
|
|
167
173
|
|
|
168
174
|
|
|
169
175
|
class RunTracker:
|
|
170
|
-
"""One organisation's live progress
|
|
176
|
+
"""One organisation's live progress display (see the module docstring)."""
|
|
171
177
|
|
|
172
178
|
def __init__(
|
|
173
179
|
self, console: Console, *, clock: Callable[[], float] = time.monotonic
|
|
@@ -193,7 +199,7 @@ class RunTracker:
|
|
|
193
199
|
|
|
194
200
|
# -- lifecycle ----------------------------------------------------------
|
|
195
201
|
def start(self, org: str) -> None:
|
|
196
|
-
"""Reset the counters for ``org`` and put the
|
|
202
|
+
"""Reset the counters for ``org`` and put the display on screen."""
|
|
197
203
|
self._org = org
|
|
198
204
|
self._started = self._clock()
|
|
199
205
|
self._stopped = None
|
|
@@ -221,7 +227,7 @@ class RunTracker:
|
|
|
221
227
|
self._live = live
|
|
222
228
|
|
|
223
229
|
def stop(self) -> None:
|
|
224
|
-
"""Freeze the
|
|
230
|
+
"""Freeze the display, take it down and replay held warnings.
|
|
225
231
|
|
|
226
232
|
Best effort: callers run this from ``finally``, so a display that
|
|
227
233
|
fails to draw its last frame must neither replace the error the run
|
|
@@ -294,56 +300,70 @@ class RunTracker:
|
|
|
294
300
|
def _clock_text(self) -> str:
|
|
295
301
|
return f"{self._elapsed():.1f} s"
|
|
296
302
|
|
|
297
|
-
def
|
|
298
|
-
"""The line
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
303
|
+
def _where(self) -> str:
|
|
304
|
+
"""The first line's text: the organisation and the phase."""
|
|
305
|
+
phase = self._phase if self._stopped is None else "done"
|
|
306
|
+
return f"{self._org} · {phase}"
|
|
307
|
+
|
|
308
|
+
def _traffic(self) -> str:
|
|
309
|
+
"""The second line: the request counts by kind."""
|
|
310
|
+
parts = [self._count("REST", "rest"), self._count("GraphQL", "graphql")]
|
|
304
311
|
if self._requests.get("external"):
|
|
305
312
|
parts.append(self._count("external", "external"))
|
|
306
313
|
return " · ".join(parts)
|
|
307
314
|
|
|
308
|
-
def
|
|
309
|
-
"""The
|
|
310
|
-
|
|
315
|
+
def _backoff(self) -> str | None:
|
|
316
|
+
"""The rate-limit countdown, while a backoff is in progress."""
|
|
317
|
+
if not self._backoffs:
|
|
318
|
+
return None
|
|
319
|
+
wait = max(0.0, self._resume_at - self._clock())
|
|
320
|
+
where = f" ({self._limited_resource})" if self._limited_resource else ""
|
|
321
|
+
return f"⏳ rate limited by GitHub{where}; resuming in {wait:.0f} s"
|
|
322
|
+
|
|
323
|
+
def _progress(self) -> str:
|
|
324
|
+
"""The third line, clock first so truncation takes it last."""
|
|
325
|
+
parts = [self._clock_text()]
|
|
326
|
+
if self._repos_total is not None:
|
|
327
|
+
parts.append(f"{self._repos_done}/{self._repos_total} repos")
|
|
328
|
+
backoff = self._backoff()
|
|
329
|
+
if backoff is not None:
|
|
330
|
+
parts.append(backoff)
|
|
331
|
+
return " · ".join(parts)
|
|
311
332
|
|
|
312
|
-
def
|
|
313
|
-
"""The
|
|
314
|
-
|
|
315
|
-
wait = max(0.0, self._resume_at - self._clock())
|
|
316
|
-
where = f" ({self._limited_resource})" if self._limited_resource else ""
|
|
317
|
-
return "⏳", f"rate limited by GitHub{where}; resuming in {wait:.0f} s"
|
|
333
|
+
def lines(self) -> list[str]:
|
|
334
|
+
"""The display as plain text, one string per row, without the spinner."""
|
|
335
|
+
rows = [self._where(), self._traffic(), self._progress()]
|
|
318
336
|
if self._notice:
|
|
319
|
-
|
|
320
|
-
return
|
|
337
|
+
rows.append(f"📦 {self._notice}")
|
|
338
|
+
return rows
|
|
321
339
|
|
|
322
340
|
@staticmethod
|
|
323
341
|
def _cell(text: str, style: str = "") -> Text:
|
|
324
342
|
return Text(text, style=style, no_wrap=True, overflow="ellipsis")
|
|
325
343
|
|
|
326
344
|
def render(self) -> RenderableType:
|
|
327
|
-
"""
|
|
328
|
-
|
|
329
|
-
Each row is one line:
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
345
|
+
"""Three or four physical lines, however narrow the terminal.
|
|
346
|
+
|
|
347
|
+
Each row is one line: its text truncates with an ellipsis rather than
|
|
348
|
+
wrapping, since a wrapped line is as unreadable as a torn one. The
|
|
349
|
+
clock leads the third row so it is the last thing on it to go: it is
|
|
350
|
+
what tells a stalled run from a slow one. The rows after the first
|
|
351
|
+
leave the spinner's column empty, so their text aligns under the
|
|
352
|
+
first row's.
|
|
334
353
|
"""
|
|
335
354
|
grid = Table.grid(padding=(0, 1))
|
|
336
355
|
grid.add_column(no_wrap=True)
|
|
337
356
|
grid.add_column()
|
|
338
|
-
grid.add_column(no_wrap=True)
|
|
339
357
|
marker: RenderableType = (
|
|
340
358
|
self._spinner if self._stopped is None else Text("✓", style="green")
|
|
341
359
|
)
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
360
|
+
where, traffic, progress, *notice = self.lines()
|
|
361
|
+
grid.add_row(marker, self._cell(where))
|
|
362
|
+
grid.add_row("", self._cell(traffic))
|
|
363
|
+
style = "yellow" if self._backoffs else ""
|
|
364
|
+
grid.add_row("", self._cell(progress, style))
|
|
365
|
+
for text in notice:
|
|
366
|
+
grid.add_row("", self._cell(text, "yellow"))
|
|
347
367
|
return grid
|
|
348
368
|
|
|
349
369
|
|
|
@@ -40,6 +40,8 @@ def _table_to_dict(section: TableSection) -> dict:
|
|
|
40
40
|
"fail_count": section.fail_count,
|
|
41
41
|
"unknown_count": section.unknown_count,
|
|
42
42
|
"not_applicable_count": section.not_applicable_count,
|
|
43
|
+
# One string per note; a note with a detail line carries it after a
|
|
44
|
+
# newline (the malware coverage note's headline and detail).
|
|
43
45
|
"notes": list(section.notes),
|
|
44
46
|
"description": section.resolved_description(),
|
|
45
47
|
# Aggregate rows beneath the totals, over every row (report.json is the
|
|
@@ -26,9 +26,11 @@ _COLUMNS = ("Repository", "Package", "Ecosystem", "Manifest", "Severity", "Age")
|
|
|
26
26
|
# Severity and Age sort on numbers (the severity rung, the days), so a
|
|
27
27
|
# configured sort on either defaults to worst or oldest first.
|
|
28
28
|
_NUMERIC_COLUMNS = frozenset({4, 5})
|
|
29
|
+
# A headline and its detail, split by a newline: each surface sets the detail
|
|
30
|
+
# on a line of its own under the headline (see the summary renderers).
|
|
29
31
|
COVERAGE_NOTE = (
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
+
"Malware alert enablement/status NOT currently available through APIs\n"
|
|
33
|
+
"Repositories without findings may have the feature off so coverage unknown"
|
|
32
34
|
)
|
|
33
35
|
|
|
34
36
|
|
|
@@ -139,6 +139,8 @@ def _summary_context(
|
|
|
139
139
|
"kind": line.kind,
|
|
140
140
|
"emoji": SUMMARY_EMOJI[line.kind],
|
|
141
141
|
"text": line.text,
|
|
142
|
+
# A multi-line note: the template breaks between the parts.
|
|
143
|
+
"parts": line.text.split("\n"),
|
|
142
144
|
"label": line.names_label,
|
|
143
145
|
"names": names,
|
|
144
146
|
"names_hidden": hidden,
|
|
@@ -185,7 +185,9 @@ def _summary_lines(
|
|
|
185
185
|
"""
|
|
186
186
|
out: list[str] = []
|
|
187
187
|
for line in lines:
|
|
188
|
-
|
|
188
|
+
# A multi-line note keeps its detail on a line of its own.
|
|
189
|
+
text = line.text.replace("\n", "<br>\n")
|
|
190
|
+
out.append(f"{SUMMARY_EMOJI[line.kind]} {text}")
|
|
189
191
|
out.append("")
|
|
190
192
|
for line in lines:
|
|
191
193
|
if not line.listed:
|
|
@@ -13,6 +13,7 @@ from __future__ import annotations
|
|
|
13
13
|
from collections.abc import Callable, Sequence
|
|
14
14
|
from pathlib import Path
|
|
15
15
|
|
|
16
|
+
from rich.cells import cell_len
|
|
16
17
|
from rich.console import Console
|
|
17
18
|
from rich.markup import escape
|
|
18
19
|
from rich.table import Table
|
|
@@ -140,7 +141,11 @@ def _render_summary(
|
|
|
140
141
|
"""
|
|
141
142
|
for line in lines:
|
|
142
143
|
style = _SUMMARY_STYLE[line.kind]
|
|
143
|
-
|
|
144
|
+
emoji = SUMMARY_EMOJI[line.kind]
|
|
145
|
+
# A multi-line note's detail aligns under its headline's text.
|
|
146
|
+
indent = " " * (2 + cell_len(emoji) + 1)
|
|
147
|
+
text = line.text.replace("\n", f"\n{indent}")
|
|
148
|
+
console.print(f" [{style}]{emoji} {text}[/{style}]")
|
|
144
149
|
for line in lines:
|
|
145
150
|
if line.listed:
|
|
146
151
|
style = _SUMMARY_STYLE[line.kind]
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
{%- macro render_summary(summary) -%}
|
|
57
57
|
{% if summary %}
|
|
58
58
|
<ul class="summary">
|
|
59
|
-
{% for line in summary %}<li class="kind-{{ line.kind }}"><span aria-hidden="true">{{ line.emoji }}</span> {
|
|
59
|
+
{% for line in summary %}<li class="kind-{{ line.kind }}"><span aria-hidden="true">{{ line.emoji }}</span> {% for part in line.parts %}{% if not loop.first %}<br>{% endif %}{{ part }}{% endfor %}</li>{% endfor %}
|
|
60
60
|
{% for line in summary %}{% if line.label and line.names %}<li class="names"><strong>{{ line.label }}:</strong> {% for r in line.names %}{% if r.url %}<a href="{{ r.url }}">{{ r.name }}</a>{% else %}{{ r.name }}{% endif %}{% if not loop.last %}, {% endif %}{% endfor %}{% if line.names_hidden %} … (+{{ line.names_hidden }} more){% endif %}</li>{% endif %}{% endfor %}
|
|
61
61
|
</ul>
|
|
62
62
|
{% endif %}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/gating.py
RENAMED
|
File without changes
|
{github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/gitctx.py
RENAMED
|
File without changes
|
|
File without changes
|
{github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/issues.py
RENAMED
|
File without changes
|
{github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/layout.py
RENAMED
|
File without changes
|
{github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/models.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/py.typed
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/runner.py
RENAMED
|
File without changes
|
{github_security_report-0.20.0 → github_security_report-0.21.1}/src/github_security_report/scope.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|