verifaied 0.2.0.dev31__tar.gz → 0.3.0.dev32__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.
- {verifaied-0.2.0.dev31 → verifaied-0.3.0.dev32}/PKG-INFO +77 -15
- {verifaied-0.2.0.dev31 → verifaied-0.3.0.dev32}/README.md +75 -13
- {verifaied-0.2.0.dev31 → verifaied-0.3.0.dev32}/pyproject.toml +2 -2
- {verifaied-0.2.0.dev31 → verifaied-0.3.0.dev32}/src/verifaied/cli.py +78 -16
- {verifaied-0.2.0.dev31 → verifaied-0.3.0.dev32}/src/verifaied/client.py +8 -1
- verifaied-0.3.0.dev32/src/verifaied/uploader.py +285 -0
- verifaied-0.3.0.dev32/tests/conftest.py +20 -0
- {verifaied-0.2.0.dev31 → verifaied-0.3.0.dev32}/tests/test_cli.py +219 -1
- {verifaied-0.2.0.dev31 → verifaied-0.3.0.dev32}/tests/test_client.py +30 -3
- {verifaied-0.2.0.dev31 → verifaied-0.3.0.dev32}/tests/test_uploader.py +162 -5
- {verifaied-0.2.0.dev31 → verifaied-0.3.0.dev32}/uv.lock +1 -1
- verifaied-0.2.0.dev31/src/verifaied/uploader.py +0 -175
- {verifaied-0.2.0.dev31 → verifaied-0.3.0.dev32}/.gitignore +0 -0
- {verifaied-0.2.0.dev31 → verifaied-0.3.0.dev32}/LICENSE +0 -0
- {verifaied-0.2.0.dev31 → verifaied-0.3.0.dev32}/src/verifaied/__init__.py +0 -0
- {verifaied-0.2.0.dev31 → verifaied-0.3.0.dev32}/src/verifaied/__main__.py +0 -0
- {verifaied-0.2.0.dev31 → verifaied-0.3.0.dev32}/src/verifaied/analyzer.py +0 -0
- {verifaied-0.2.0.dev31 → verifaied-0.3.0.dev32}/src/verifaied/config.py +0 -0
- {verifaied-0.2.0.dev31 → verifaied-0.3.0.dev32}/src/verifaied/prompts.py +0 -0
- {verifaied-0.2.0.dev31 → verifaied-0.3.0.dev32}/tests/__init__.py +0 -0
- {verifaied-0.2.0.dev31 → verifaied-0.3.0.dev32}/tests/test_check.py +0 -0
- {verifaied-0.2.0.dev31 → verifaied-0.3.0.dev32}/tests/test_check_done.py +0 -0
- {verifaied-0.2.0.dev31 → verifaied-0.3.0.dev32}/tests/test_config.py +0 -0
- {verifaied-0.2.0.dev31 → verifaied-0.3.0.dev32}/tests/test_prompt_parity.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: verifaied
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary: Find what's untested in your
|
|
3
|
+
Version: 0.3.0.dev32
|
|
4
|
+
Summary: Find what's untested in your code — locally, no account required
|
|
5
5
|
Project-URL: Homepage, https://pypi.org/project/verifaied/
|
|
6
6
|
Author: Kyle Richards
|
|
7
7
|
License: MIT License
|
|
@@ -40,17 +40,19 @@ Description-Content-Type: text/markdown
|
|
|
40
40
|
|
|
41
41
|
# verifaied
|
|
42
42
|
|
|
43
|
-
Find what's untested in your
|
|
43
|
+
Find what's untested in your code, so you (or your coding agent) can fix
|
|
44
44
|
it without waiting for CI.
|
|
45
45
|
|
|
46
46
|
Two verbs:
|
|
47
47
|
|
|
48
48
|
- **`verifaied check`** — runs entirely on your machine. No account, no API
|
|
49
|
-
token, no network call. Tells you which functions are untested or
|
|
50
|
-
partially covered, and can hand you a ready-to-paste prompt for each
|
|
49
|
+
token, no network call. Tells you which Python functions are untested or
|
|
50
|
+
only partially covered, and can hand you a ready-to-paste prompt for each
|
|
51
|
+
one.
|
|
51
52
|
- **`verifaied upload`** — syncs the same coverage to the [verifAIed](https://verifaied.app)
|
|
52
53
|
app, which adds history, branch diffing against CI, a web UI, and AI-written
|
|
53
|
-
test prompts.
|
|
54
|
+
test prompts. Handles both Python (pytest-cov) and TypeScript/JavaScript
|
|
55
|
+
(vitest/Istanbul). Needs a free account.
|
|
54
56
|
|
|
55
57
|
## Install
|
|
56
58
|
|
|
@@ -78,6 +80,10 @@ verifaied check
|
|
|
78
80
|
Nothing leaves your machine — the whole analysis is a local AST pass over the
|
|
79
81
|
source `coverage.json` already points at.
|
|
80
82
|
|
|
83
|
+
`check` is Python-only: it reads pytest-cov's `coverage.json`. Point it at a
|
|
84
|
+
vitest/Istanbul report and it exits with a note to use `verifaied upload`,
|
|
85
|
+
which analyzes TypeScript/JavaScript on the hosted side.
|
|
86
|
+
|
|
81
87
|
Add `--prompt` to get a concrete, ready-to-paste instruction for each one,
|
|
82
88
|
generated from the function's signature and its uncovered lines (no LLM, no
|
|
83
89
|
cost, unlimited):
|
|
@@ -133,17 +139,37 @@ verifaied upload --repo kyle/verifaied # owner/name slug
|
|
|
133
139
|
verifaied upload --repo <UUID> # raw UUID, no lookup
|
|
134
140
|
```
|
|
135
141
|
|
|
136
|
-
The CLI reads
|
|
137
|
-
|
|
138
|
-
`/repositories/<id>/local-coverage`.
|
|
139
|
-
|
|
140
|
-
|
|
142
|
+
The CLI reads your coverage report(s), pulls the source for every file
|
|
143
|
+
they reference from your working tree, and posts everything to
|
|
144
|
+
`/repositories/<id>/local-coverage`. With no `--coverage` flag it
|
|
145
|
+
auto-discovers the report, looking in order for `./coverage.json`
|
|
146
|
+
(pytest-cov), then `./coverage/coverage-final.json`, then a bare
|
|
147
|
+
`./coverage-final.json` (vitest/Istanbul). Pass `--coverage` one or more
|
|
148
|
+
times to name reports explicitly — repeat it to push Python and
|
|
149
|
+
TypeScript coverage in a single upload; multiple reports merge per
|
|
150
|
+
language on the server. The response prints a summary of untested /
|
|
151
|
+
partial / failing functions so you (or your LLM) can fix them on the
|
|
152
|
+
next iteration.
|
|
153
|
+
|
|
154
|
+
### What the upload reports
|
|
155
|
+
|
|
156
|
+
The summary leads with counts so an LLM (or you) sees the deltas first:
|
|
157
|
+
|
|
158
|
+
- **functions matched** — functions in the uploaded report the analyzer
|
|
159
|
+
resolved against your source.
|
|
160
|
+
- **functions preserved** — functions retained from a previous upload in
|
|
161
|
+
another language for the same branch (only shown when non-zero). A
|
|
162
|
+
Python-only upload never wipes the TypeScript functions a vitest upload
|
|
163
|
+
recorded on the same branch, and vice versa — the branch's total tracked
|
|
164
|
+
functions is _matched + preserved_.
|
|
165
|
+
- **untested / partial / failing tests** — what to fix next.
|
|
141
166
|
|
|
142
167
|
### What gets uploaded
|
|
143
168
|
|
|
144
|
-
The CLI only sends files that appear in
|
|
145
|
-
|
|
146
|
-
|
|
169
|
+
The CLI only sends files that appear in your coverage report — i.e.
|
|
170
|
+
exactly the files your test run instrumented (pytest-cov's `"files"`
|
|
171
|
+
map, or the top-level file keys in an Istanbul `coverage-final.json`).
|
|
172
|
+
There is no directory walk and no glob:
|
|
147
173
|
|
|
148
174
|
- `.env`, build artefacts, vendored libraries, and anything outside
|
|
149
175
|
your coverage scope are never read.
|
|
@@ -169,13 +195,49 @@ expand **Recently deleted** under the branches grid, and use
|
|
|
169
195
|
to the uploaded source — the per-card **Delete** is a soft-delete that
|
|
170
196
|
keeps the data around for accidental-deletion recovery.
|
|
171
197
|
|
|
198
|
+
### TypeScript / JavaScript
|
|
199
|
+
|
|
200
|
+
`upload` ingests vitest/Istanbul coverage the same way it ingests
|
|
201
|
+
pytest-cov's. Generate it with vitest's **Istanbul** provider:
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
npm i -D @vitest/coverage-istanbul
|
|
205
|
+
vitest run --coverage --coverage.provider=istanbul --coverage.reporter=json \
|
|
206
|
+
--reporter=junit --outputFile.junit=junit.xml
|
|
207
|
+
verifaied upload --junit junit.xml
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
The Istanbul provider is required. Istanbul applies your source maps, so
|
|
211
|
+
`coverage/coverage-final.json` reports the original `.ts` / `.tsx`
|
|
212
|
+
sources — which is what verifAIed matches function definitions against.
|
|
213
|
+
vitest's default **v8** provider emits a different report shape that
|
|
214
|
+
verifAIed rejects, so `--coverage.provider=istanbul` is not optional. The
|
|
215
|
+
`--reporter=junit --outputFile.junit=junit.xml` flags produce the JUnit
|
|
216
|
+
XML that feeds the failing-tests panel; hand it to the upload with
|
|
217
|
+
`--junit`.
|
|
218
|
+
|
|
219
|
+
If a report references only compiled output — paths under `dist/`,
|
|
220
|
+
`build/`, `out/`, or `.next/` with no `.ts` / `.tsx` / `.jsx` entries —
|
|
221
|
+
the upload is refused. That's the source-maps-not-applied case: coverage
|
|
222
|
+
landed on bundled JS instead of your sources, and verifAIed can't map it
|
|
223
|
+
back. Switching to the Istanbul provider is the fix.
|
|
224
|
+
|
|
225
|
+
**Supported formats.** pytest-cov `coverage.json` and Istanbul
|
|
226
|
+
`coverage-final.json` only. LCOV, Cobertura XML, jest's own format, and
|
|
227
|
+
c8 / v8-style JSON are rejected with a clear error naming the two
|
|
228
|
+
supported shapes.
|
|
229
|
+
|
|
172
230
|
### `upload` flags
|
|
173
231
|
|
|
174
232
|
- `--repo / -r <UUID|owner/name>` — repository to upload to. If omitted,
|
|
175
233
|
the CLI auto-detects from the GitHub `origin` remote.
|
|
176
234
|
- `--branch / -b <name>` — branch to attach the upload to (default:
|
|
177
235
|
`git branch --show-current`, then `local`)
|
|
178
|
-
- `--coverage <path>` —
|
|
236
|
+
- `--coverage <path>` — coverage report to upload. **Repeatable** — pass
|
|
237
|
+
it once per report to combine Python and TypeScript coverage in one
|
|
238
|
+
upload. Accepts pytest-cov `coverage.json` and Istanbul
|
|
239
|
+
`coverage-final.json`. Default: auto-detect `./coverage.json`, then
|
|
240
|
+
`./coverage/coverage-final.json` (or a bare `./coverage-final.json`).
|
|
179
241
|
- `--junit <path>` — optional JUnit XML for failing-test detail
|
|
180
242
|
- `--commit-sha <sha>` — commit sha for display (default: `git rev-parse HEAD`)
|
|
181
243
|
- `--root <path>` — root the coverage paths are relative to (default: cwd)
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
# verifaied
|
|
2
2
|
|
|
3
|
-
Find what's untested in your
|
|
3
|
+
Find what's untested in your code, so you (or your coding agent) can fix
|
|
4
4
|
it without waiting for CI.
|
|
5
5
|
|
|
6
6
|
Two verbs:
|
|
7
7
|
|
|
8
8
|
- **`verifaied check`** — runs entirely on your machine. No account, no API
|
|
9
|
-
token, no network call. Tells you which functions are untested or
|
|
10
|
-
partially covered, and can hand you a ready-to-paste prompt for each
|
|
9
|
+
token, no network call. Tells you which Python functions are untested or
|
|
10
|
+
only partially covered, and can hand you a ready-to-paste prompt for each
|
|
11
|
+
one.
|
|
11
12
|
- **`verifaied upload`** — syncs the same coverage to the [verifAIed](https://verifaied.app)
|
|
12
13
|
app, which adds history, branch diffing against CI, a web UI, and AI-written
|
|
13
|
-
test prompts.
|
|
14
|
+
test prompts. Handles both Python (pytest-cov) and TypeScript/JavaScript
|
|
15
|
+
(vitest/Istanbul). Needs a free account.
|
|
14
16
|
|
|
15
17
|
## Install
|
|
16
18
|
|
|
@@ -38,6 +40,10 @@ verifaied check
|
|
|
38
40
|
Nothing leaves your machine — the whole analysis is a local AST pass over the
|
|
39
41
|
source `coverage.json` already points at.
|
|
40
42
|
|
|
43
|
+
`check` is Python-only: it reads pytest-cov's `coverage.json`. Point it at a
|
|
44
|
+
vitest/Istanbul report and it exits with a note to use `verifaied upload`,
|
|
45
|
+
which analyzes TypeScript/JavaScript on the hosted side.
|
|
46
|
+
|
|
41
47
|
Add `--prompt` to get a concrete, ready-to-paste instruction for each one,
|
|
42
48
|
generated from the function's signature and its uncovered lines (no LLM, no
|
|
43
49
|
cost, unlimited):
|
|
@@ -93,17 +99,37 @@ verifaied upload --repo kyle/verifaied # owner/name slug
|
|
|
93
99
|
verifaied upload --repo <UUID> # raw UUID, no lookup
|
|
94
100
|
```
|
|
95
101
|
|
|
96
|
-
The CLI reads
|
|
97
|
-
|
|
98
|
-
`/repositories/<id>/local-coverage`.
|
|
99
|
-
|
|
100
|
-
|
|
102
|
+
The CLI reads your coverage report(s), pulls the source for every file
|
|
103
|
+
they reference from your working tree, and posts everything to
|
|
104
|
+
`/repositories/<id>/local-coverage`. With no `--coverage` flag it
|
|
105
|
+
auto-discovers the report, looking in order for `./coverage.json`
|
|
106
|
+
(pytest-cov), then `./coverage/coverage-final.json`, then a bare
|
|
107
|
+
`./coverage-final.json` (vitest/Istanbul). Pass `--coverage` one or more
|
|
108
|
+
times to name reports explicitly — repeat it to push Python and
|
|
109
|
+
TypeScript coverage in a single upload; multiple reports merge per
|
|
110
|
+
language on the server. The response prints a summary of untested /
|
|
111
|
+
partial / failing functions so you (or your LLM) can fix them on the
|
|
112
|
+
next iteration.
|
|
113
|
+
|
|
114
|
+
### What the upload reports
|
|
115
|
+
|
|
116
|
+
The summary leads with counts so an LLM (or you) sees the deltas first:
|
|
117
|
+
|
|
118
|
+
- **functions matched** — functions in the uploaded report the analyzer
|
|
119
|
+
resolved against your source.
|
|
120
|
+
- **functions preserved** — functions retained from a previous upload in
|
|
121
|
+
another language for the same branch (only shown when non-zero). A
|
|
122
|
+
Python-only upload never wipes the TypeScript functions a vitest upload
|
|
123
|
+
recorded on the same branch, and vice versa — the branch's total tracked
|
|
124
|
+
functions is _matched + preserved_.
|
|
125
|
+
- **untested / partial / failing tests** — what to fix next.
|
|
101
126
|
|
|
102
127
|
### What gets uploaded
|
|
103
128
|
|
|
104
|
-
The CLI only sends files that appear in
|
|
105
|
-
|
|
106
|
-
|
|
129
|
+
The CLI only sends files that appear in your coverage report — i.e.
|
|
130
|
+
exactly the files your test run instrumented (pytest-cov's `"files"`
|
|
131
|
+
map, or the top-level file keys in an Istanbul `coverage-final.json`).
|
|
132
|
+
There is no directory walk and no glob:
|
|
107
133
|
|
|
108
134
|
- `.env`, build artefacts, vendored libraries, and anything outside
|
|
109
135
|
your coverage scope are never read.
|
|
@@ -129,13 +155,49 @@ expand **Recently deleted** under the branches grid, and use
|
|
|
129
155
|
to the uploaded source — the per-card **Delete** is a soft-delete that
|
|
130
156
|
keeps the data around for accidental-deletion recovery.
|
|
131
157
|
|
|
158
|
+
### TypeScript / JavaScript
|
|
159
|
+
|
|
160
|
+
`upload` ingests vitest/Istanbul coverage the same way it ingests
|
|
161
|
+
pytest-cov's. Generate it with vitest's **Istanbul** provider:
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
npm i -D @vitest/coverage-istanbul
|
|
165
|
+
vitest run --coverage --coverage.provider=istanbul --coverage.reporter=json \
|
|
166
|
+
--reporter=junit --outputFile.junit=junit.xml
|
|
167
|
+
verifaied upload --junit junit.xml
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
The Istanbul provider is required. Istanbul applies your source maps, so
|
|
171
|
+
`coverage/coverage-final.json` reports the original `.ts` / `.tsx`
|
|
172
|
+
sources — which is what verifAIed matches function definitions against.
|
|
173
|
+
vitest's default **v8** provider emits a different report shape that
|
|
174
|
+
verifAIed rejects, so `--coverage.provider=istanbul` is not optional. The
|
|
175
|
+
`--reporter=junit --outputFile.junit=junit.xml` flags produce the JUnit
|
|
176
|
+
XML that feeds the failing-tests panel; hand it to the upload with
|
|
177
|
+
`--junit`.
|
|
178
|
+
|
|
179
|
+
If a report references only compiled output — paths under `dist/`,
|
|
180
|
+
`build/`, `out/`, or `.next/` with no `.ts` / `.tsx` / `.jsx` entries —
|
|
181
|
+
the upload is refused. That's the source-maps-not-applied case: coverage
|
|
182
|
+
landed on bundled JS instead of your sources, and verifAIed can't map it
|
|
183
|
+
back. Switching to the Istanbul provider is the fix.
|
|
184
|
+
|
|
185
|
+
**Supported formats.** pytest-cov `coverage.json` and Istanbul
|
|
186
|
+
`coverage-final.json` only. LCOV, Cobertura XML, jest's own format, and
|
|
187
|
+
c8 / v8-style JSON are rejected with a clear error naming the two
|
|
188
|
+
supported shapes.
|
|
189
|
+
|
|
132
190
|
### `upload` flags
|
|
133
191
|
|
|
134
192
|
- `--repo / -r <UUID|owner/name>` — repository to upload to. If omitted,
|
|
135
193
|
the CLI auto-detects from the GitHub `origin` remote.
|
|
136
194
|
- `--branch / -b <name>` — branch to attach the upload to (default:
|
|
137
195
|
`git branch --show-current`, then `local`)
|
|
138
|
-
- `--coverage <path>` —
|
|
196
|
+
- `--coverage <path>` — coverage report to upload. **Repeatable** — pass
|
|
197
|
+
it once per report to combine Python and TypeScript coverage in one
|
|
198
|
+
upload. Accepts pytest-cov `coverage.json` and Istanbul
|
|
199
|
+
`coverage-final.json`. Default: auto-detect `./coverage.json`, then
|
|
200
|
+
`./coverage/coverage-final.json` (or a bare `./coverage-final.json`).
|
|
139
201
|
- `--junit <path>` — optional JUnit XML for failing-test detail
|
|
140
202
|
- `--commit-sha <sha>` — commit sha for display (default: `git rev-parse HEAD`)
|
|
141
203
|
- `--root <path>` — root the coverage paths are relative to (default: cwd)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "verifaied"
|
|
3
|
-
version = "0.
|
|
4
|
-
description = "Find what's untested in your
|
|
3
|
+
version = "0.3.0.dev32"
|
|
4
|
+
description = "Find what's untested in your code — locally, no account required"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.10"
|
|
7
7
|
license = { file = "LICENSE" }
|
|
@@ -41,6 +41,7 @@ from verifaied.uploader import (
|
|
|
41
41
|
UploaderError,
|
|
42
42
|
build_payload,
|
|
43
43
|
detect_branch,
|
|
44
|
+
detect_coverage_kind,
|
|
44
45
|
detect_repo_slug,
|
|
45
46
|
)
|
|
46
47
|
|
|
@@ -91,10 +92,14 @@ def upload_command(
|
|
|
91
92
|
"-b",
|
|
92
93
|
help="Branch name. Defaults to `git branch --show-current`.",
|
|
93
94
|
),
|
|
94
|
-
coverage: Path
|
|
95
|
-
|
|
95
|
+
coverage: list[Path] = typer.Option(
|
|
96
|
+
[],
|
|
96
97
|
"--coverage",
|
|
97
|
-
help=
|
|
98
|
+
help=(
|
|
99
|
+
"Coverage report path(s). Repeatable. Accepts pytest-cov "
|
|
100
|
+
"coverage.json and Istanbul coverage-final.json. Default: "
|
|
101
|
+
"auto-detect coverage.json and/or coverage/coverage-final.json."
|
|
102
|
+
),
|
|
98
103
|
),
|
|
99
104
|
junit: Path | None = typer.Option(
|
|
100
105
|
None,
|
|
@@ -109,7 +114,11 @@ def upload_command(
|
|
|
109
114
|
root: Path | None = typer.Option(
|
|
110
115
|
None,
|
|
111
116
|
"--root",
|
|
112
|
-
help=
|
|
117
|
+
help=(
|
|
118
|
+
"Root the coverage paths are relative to (default: cwd). Its "
|
|
119
|
+
"position inside the git repo is the prefix every stored path "
|
|
120
|
+
"is rooted by, so paths display repo-rooted."
|
|
121
|
+
),
|
|
113
122
|
),
|
|
114
123
|
api_url: str | None = typer.Option(
|
|
115
124
|
None,
|
|
@@ -138,23 +147,31 @@ def upload_command(
|
|
|
138
147
|
# block the preview. Skip the token check for that path.
|
|
139
148
|
if not dry_run and not resolved_token:
|
|
140
149
|
err_console.print(
|
|
141
|
-
|
|
150
|
+
"[red]VerifAIed error[/red]: no API token "
|
|
151
|
+
f"(set ${ENV_API_TOKEN} or pass --token)"
|
|
142
152
|
)
|
|
143
153
|
raise typer.Exit(code=1)
|
|
144
154
|
|
|
145
|
-
coverage_path = coverage or Path("coverage.json")
|
|
146
155
|
root_path = root or Path.cwd()
|
|
156
|
+
coverage_paths = list(coverage) if coverage else _discover_coverage_files()
|
|
157
|
+
if not coverage_paths:
|
|
158
|
+
err_console.print(
|
|
159
|
+
"[red]VerifAIed error[/red]: no coverage file found (looked for "
|
|
160
|
+
"coverage.json, coverage/coverage-final.json, coverage-final.json). "
|
|
161
|
+
"Run `pytest --cov --cov-report=json` or `vitest run --coverage` first."
|
|
162
|
+
)
|
|
163
|
+
raise typer.Exit(code=1)
|
|
147
164
|
|
|
148
165
|
try:
|
|
149
166
|
payload = build_payload(
|
|
150
|
-
|
|
167
|
+
coverage_paths=coverage_paths,
|
|
151
168
|
root=root_path,
|
|
152
169
|
branch=branch,
|
|
153
170
|
commit_sha=commit_sha,
|
|
154
171
|
junit_path=junit,
|
|
155
172
|
)
|
|
156
173
|
except UploaderError as e:
|
|
157
|
-
err_console.print(f"[red]error[/red]: {e}")
|
|
174
|
+
err_console.print(f"[red]VerifAIed error[/red]: {e}")
|
|
158
175
|
raise typer.Exit(code=1) from e
|
|
159
176
|
|
|
160
177
|
if dry_run:
|
|
@@ -169,7 +186,7 @@ def upload_command(
|
|
|
169
186
|
repo_input=repo, api_url=resolved_url, token=resolved_token
|
|
170
187
|
)
|
|
171
188
|
except (UploaderError, ApiError, RepoNotFoundError) as e:
|
|
172
|
-
err_console.print(f"[red]error[/red]: {e}")
|
|
189
|
+
err_console.print(f"[red]VerifAIed error[/red]: {e}")
|
|
173
190
|
raise typer.Exit(code=2 if isinstance(e, ApiError) else 1) from e
|
|
174
191
|
|
|
175
192
|
try:
|
|
@@ -180,7 +197,7 @@ def upload_command(
|
|
|
180
197
|
payload=payload,
|
|
181
198
|
)
|
|
182
199
|
except ApiError as e:
|
|
183
|
-
err_console.print(f"[red]error[/red]: {e.message}")
|
|
200
|
+
err_console.print(f"[red]VerifAIed error[/red]: {e.message}")
|
|
184
201
|
raise typer.Exit(code=2) from e
|
|
185
202
|
|
|
186
203
|
_render(result, api_url=resolved_url, repo_id=repo_id)
|
|
@@ -191,7 +208,11 @@ def check_command(
|
|
|
191
208
|
coverage: Path | None = typer.Option(
|
|
192
209
|
None,
|
|
193
210
|
"--coverage",
|
|
194
|
-
help=
|
|
211
|
+
help=(
|
|
212
|
+
"Path to the coverage report (default: ./coverage.json). "
|
|
213
|
+
"check is Python-only; Istanbul/vitest reports are refused — "
|
|
214
|
+
"use `verifaied upload` for TS/JS."
|
|
215
|
+
),
|
|
195
216
|
),
|
|
196
217
|
root: Path | None = typer.Option(
|
|
197
218
|
None,
|
|
@@ -245,7 +266,7 @@ def check_command(
|
|
|
245
266
|
raw = coverage_path.read_text(encoding="utf-8")
|
|
246
267
|
except OSError as e:
|
|
247
268
|
err_console.print(
|
|
248
|
-
f"[red]error[/red]: cannot read {coverage_path} — run "
|
|
269
|
+
f"[red]VerifAIed error[/red]: cannot read {coverage_path} — run "
|
|
249
270
|
f"`pytest --cov --cov-report=json` first"
|
|
250
271
|
)
|
|
251
272
|
raise typer.Exit(code=1) from e
|
|
@@ -253,9 +274,23 @@ def check_command(
|
|
|
253
274
|
try:
|
|
254
275
|
coverage_json = json.loads(raw)
|
|
255
276
|
except json.JSONDecodeError as e:
|
|
256
|
-
err_console.print(
|
|
277
|
+
err_console.print(
|
|
278
|
+
f"[red]VerifAIed error[/red]: {coverage_path} is not valid JSON: {e}"
|
|
279
|
+
)
|
|
257
280
|
raise typer.Exit(code=1) from e
|
|
258
281
|
|
|
282
|
+
if isinstance(coverage_json, dict):
|
|
283
|
+
try:
|
|
284
|
+
kind = detect_coverage_kind(coverage_json)
|
|
285
|
+
except UploaderError:
|
|
286
|
+
kind = None
|
|
287
|
+
if kind == "istanbul":
|
|
288
|
+
err_console.print(
|
|
289
|
+
"[red]VerifAIed error[/red]: verifaied check is Python-only "
|
|
290
|
+
"today — for TS/JS use `verifaied upload` (hosted analysis)."
|
|
291
|
+
)
|
|
292
|
+
raise typer.Exit(code=1)
|
|
293
|
+
|
|
259
294
|
summary = analyze_coverage(coverage_json, root=root_path)
|
|
260
295
|
|
|
261
296
|
if summary.files_missing:
|
|
@@ -357,7 +392,8 @@ def check_done_command(
|
|
|
357
392
|
resolved_token = resolved_api_token(token)
|
|
358
393
|
if not resolved_token:
|
|
359
394
|
err_console.print(
|
|
360
|
-
|
|
395
|
+
"[red]VerifAIed error[/red]: no API token "
|
|
396
|
+
f"(set ${ENV_API_TOKEN} or pass --token)"
|
|
361
397
|
)
|
|
362
398
|
raise typer.Exit(code=1)
|
|
363
399
|
|
|
@@ -366,7 +402,7 @@ def check_done_command(
|
|
|
366
402
|
repo_input=repo, api_url=resolved_url, token=resolved_token
|
|
367
403
|
)
|
|
368
404
|
except (UploaderError, ApiError, RepoNotFoundError) as e:
|
|
369
|
-
err_console.print(f"[red]error[/red]: {e}")
|
|
405
|
+
err_console.print(f"[red]VerifAIed error[/red]: {e}")
|
|
370
406
|
raise typer.Exit(code=2 if isinstance(e, ApiError) else 1) from e
|
|
371
407
|
|
|
372
408
|
resolved_branch = branch or detect_branch()
|
|
@@ -378,7 +414,7 @@ def check_done_command(
|
|
|
378
414
|
branch=resolved_branch,
|
|
379
415
|
)
|
|
380
416
|
except ApiError as e:
|
|
381
|
-
err_console.print(f"[red]error[/red]: {e.message}")
|
|
417
|
+
err_console.print(f"[red]VerifAIed error[/red]: {e.message}")
|
|
382
418
|
raise typer.Exit(code=2) from e
|
|
383
419
|
|
|
384
420
|
if as_json:
|
|
@@ -530,6 +566,21 @@ def _resolve_repo(*, repo_input: str | None, api_url: str, token: str) -> UUID:
|
|
|
530
566
|
return resolve_repo_id(api_url=api_url, token=token, owner=slug[0], name=slug[1])
|
|
531
567
|
|
|
532
568
|
|
|
569
|
+
def _discover_coverage_files() -> list[Path]:
|
|
570
|
+
"""Default coverage discovery when no ``--coverage`` is passed: pytest's
|
|
571
|
+
``coverage.json`` and/or vitest's ``coverage/coverage-final.json``
|
|
572
|
+
(falling back to a bare ``coverage-final.json``). Order preserved so a
|
|
573
|
+
same-file collision resolves later-wins consistently with the backend."""
|
|
574
|
+
found: list[Path] = []
|
|
575
|
+
if Path("coverage.json").is_file():
|
|
576
|
+
found.append(Path("coverage.json"))
|
|
577
|
+
if Path("coverage/coverage-final.json").is_file():
|
|
578
|
+
found.append(Path("coverage/coverage-final.json"))
|
|
579
|
+
elif Path("coverage-final.json").is_file():
|
|
580
|
+
found.append(Path("coverage-final.json"))
|
|
581
|
+
return found
|
|
582
|
+
|
|
583
|
+
|
|
533
584
|
def _render(result: UploadResult, *, api_url: str, repo_id: UUID) -> None:
|
|
534
585
|
"""Print a compact, scannable summary. Designed for LLM tail-reads
|
|
535
586
|
too: the counts come first so the model sees the deltas without
|
|
@@ -538,6 +589,8 @@ def _render(result: UploadResult, *, api_url: str, repo_id: UUID) -> None:
|
|
|
538
589
|
table.add_row("branch", result.branch)
|
|
539
590
|
table.add_row("files received", str(result.files_received))
|
|
540
591
|
table.add_row("functions matched", str(result.functions_matched))
|
|
592
|
+
if result.functions_preserved > 0:
|
|
593
|
+
table.add_row("functions preserved", str(result.functions_preserved))
|
|
541
594
|
table.add_row("untested", str(len(result.untested)))
|
|
542
595
|
table.add_row("partial", str(len(result.partial)))
|
|
543
596
|
table.add_row("failing tests", str(len(result.failing_tests)))
|
|
@@ -576,6 +629,15 @@ def _render_dry_run(payload, *, repo_input: str | None) -> None:
|
|
|
576
629
|
header.add_row("repo", repo_display)
|
|
577
630
|
header.add_row("branch", payload.branch)
|
|
578
631
|
header.add_row("commit", payload.commit_sha or "-")
|
|
632
|
+
if payload.path_prefix is None:
|
|
633
|
+
prefix_display = "(none — server resolves against the repo tree)"
|
|
634
|
+
elif payload.path_prefix == "":
|
|
635
|
+
prefix_display = '"" (repo root)'
|
|
636
|
+
else:
|
|
637
|
+
prefix_display = (
|
|
638
|
+
f"{payload.path_prefix} — stored as {payload.path_prefix}/<path>"
|
|
639
|
+
)
|
|
640
|
+
header.add_row("path prefix", prefix_display)
|
|
579
641
|
header.add_row("files", str(len(payload.files)))
|
|
580
642
|
total_bytes = sum(
|
|
581
643
|
len(content.encode("utf-8")) for content in payload.files.values()
|
|
@@ -45,6 +45,9 @@ class UploadResult:
|
|
|
45
45
|
failing_tests: list[dict[str, Any]]
|
|
46
46
|
message: str
|
|
47
47
|
summary: dict[str, Any]
|
|
48
|
+
# Functions retained from earlier uploads of other languages (0 on
|
|
49
|
+
# older backends that don't return the field).
|
|
50
|
+
functions_preserved: int = 0
|
|
48
51
|
|
|
49
52
|
|
|
50
53
|
def upload(
|
|
@@ -65,7 +68,10 @@ def upload(
|
|
|
65
68
|
"branch": payload.branch,
|
|
66
69
|
"commit_sha": payload.commit_sha,
|
|
67
70
|
"files": payload.files,
|
|
68
|
-
"
|
|
71
|
+
"coverage_reports": payload.coverage_reports,
|
|
72
|
+
# Sent unconditionally; older backends ignore the extra field.
|
|
73
|
+
# ``None`` tells the server to resolve the prefix itself.
|
|
74
|
+
"path_prefix": payload.path_prefix,
|
|
69
75
|
}
|
|
70
76
|
if payload.junit_xml is not None:
|
|
71
77
|
body["junit_xml"] = payload.junit_xml
|
|
@@ -92,6 +98,7 @@ def upload(
|
|
|
92
98
|
failing_tests=data.get("failing_tests") or [],
|
|
93
99
|
message=data.get("message") or "",
|
|
94
100
|
summary=data.get("summary") or {},
|
|
101
|
+
functions_preserved=data.get("functions_preserved") or 0,
|
|
95
102
|
)
|
|
96
103
|
|
|
97
104
|
|