repo-review 0.12.0__tar.gz → 0.12.2__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.
- {repo_review-0.12.0 → repo_review-0.12.2}/.github/workflows/ci.yml +6 -3
- {repo_review-0.12.0 → repo_review-0.12.2}/.pre-commit-config.yaml +5 -5
- {repo_review-0.12.0 → repo_review-0.12.2}/PKG-INFO +3 -2
- {repo_review-0.12.0 → repo_review-0.12.2}/README.md +1 -1
- {repo_review-0.12.0 → repo_review-0.12.2}/action.yml +1 -1
- {repo_review-0.12.0 → repo_review-0.12.2}/docs/index.html +5 -5
- {repo_review-0.12.0 → repo_review-0.12.2}/docs/webapp.js +193 -26
- {repo_review-0.12.0 → repo_review-0.12.2}/docs/webapp.md +1 -1
- {repo_review-0.12.0 → repo_review-0.12.2}/pyproject.toml +3 -1
- {repo_review-0.12.0 → repo_review-0.12.2}/src/repo_review/__main__.py +8 -1
- {repo_review-0.12.0 → repo_review-0.12.2}/src/repo_review/_version.py +9 -4
- {repo_review-0.12.0 → repo_review-0.12.2}/src/repo_review/html.py +5 -8
- {repo_review-0.12.0 → repo_review-0.12.2}/src/repo_review/processor.py +21 -1
- {repo_review-0.12.0 → repo_review-0.12.2}/tests/test_package.py +2 -1
- {repo_review-0.12.0 → repo_review-0.12.2}/.devcontainer/devcontainer.json +0 -0
- {repo_review-0.12.0 → repo_review-0.12.2}/.git_archival.txt +0 -0
- {repo_review-0.12.0 → repo_review-0.12.2}/.gitattributes +0 -0
- {repo_review-0.12.0 → repo_review-0.12.2}/.github/CONTRIBUTING.md +0 -0
- {repo_review-0.12.0 → repo_review-0.12.2}/.github/ISSUE_TEMPLATE/new-issue.md +0 -0
- {repo_review-0.12.0 → repo_review-0.12.2}/.github/dependabot.yml +0 -0
- {repo_review-0.12.0 → repo_review-0.12.2}/.github/release.yml +0 -0
- {repo_review-0.12.0 → repo_review-0.12.2}/.github/workflows/cd.yml +0 -0
- {repo_review-0.12.0 → repo_review-0.12.2}/.gitignore +0 -0
- {repo_review-0.12.0 → repo_review-0.12.2}/.pre-commit-hooks.yaml +0 -0
- {repo_review-0.12.0 → repo_review-0.12.2}/.readthedocs.yaml +0 -0
- {repo_review-0.12.0 → repo_review-0.12.2}/LICENSE +0 -0
- {repo_review-0.12.0 → repo_review-0.12.2}/docs/.nojekyll +0 -0
- {repo_review-0.12.0 → repo_review-0.12.2}/docs/api/repo_review.resources.rst +1 -1
- {repo_review-0.12.0 → repo_review-0.12.2}/docs/api/repo_review.rst +9 -9
- {repo_review-0.12.0 → repo_review-0.12.2}/docs/changelog.md +0 -0
- {repo_review-0.12.0 → repo_review-0.12.2}/docs/checks.md +0 -0
- {repo_review-0.12.0 → repo_review-0.12.2}/docs/cli.md +0 -0
- {repo_review-0.12.0 → repo_review-0.12.2}/docs/conf.py +0 -0
- {repo_review-0.12.0 → repo_review-0.12.2}/docs/families.md +0 -0
- {repo_review-0.12.0 → repo_review-0.12.2}/docs/fixtures.md +0 -0
- {repo_review-0.12.0 → repo_review-0.12.2}/docs/index.md +0 -0
- {repo_review-0.12.0 → repo_review-0.12.2}/docs/intro.md +0 -0
- {repo_review-0.12.0 → repo_review-0.12.2}/docs/plugins.md +0 -0
- {repo_review-0.12.0 → repo_review-0.12.2}/docs/programmatic.md +0 -0
- {repo_review-0.12.0 → repo_review-0.12.2}/src/repo_review/__init__.py +0 -0
- {repo_review-0.12.0 → repo_review-0.12.2}/src/repo_review/_compat/__init__.py +0 -0
- {repo_review-0.12.0 → repo_review-0.12.2}/src/repo_review/_compat/importlib/__init__.py +0 -0
- {repo_review-0.12.0 → repo_review-0.12.2}/src/repo_review/_compat/importlib/resources/__init__.py +0 -0
- {repo_review-0.12.0 → repo_review-0.12.2}/src/repo_review/_compat/importlib/resources/abc.py +0 -0
- {repo_review-0.12.0 → repo_review-0.12.2}/src/repo_review/_compat/tomllib.py +0 -0
- {repo_review-0.12.0 → repo_review-0.12.2}/src/repo_review/_compat/typing.py +0 -0
- {repo_review-0.12.0 → repo_review-0.12.2}/src/repo_review/_version.pyi +0 -0
- {repo_review-0.12.0 → repo_review-0.12.2}/src/repo_review/checks.py +0 -0
- {repo_review-0.12.0 → repo_review-0.12.2}/src/repo_review/families.py +0 -0
- {repo_review-0.12.0 → repo_review-0.12.2}/src/repo_review/fixtures.py +0 -0
- {repo_review-0.12.0 → repo_review-0.12.2}/src/repo_review/ghpath.py +0 -0
- {repo_review-0.12.0 → repo_review-0.12.2}/src/repo_review/py.typed +0 -0
- {repo_review-0.12.0 → repo_review-0.12.2}/src/repo_review/resources/__init__.py +0 -0
- {repo_review-0.12.0 → repo_review-0.12.2}/src/repo_review/resources/repo-review.schema.json +0 -0
- {repo_review-0.12.0 → repo_review-0.12.2}/src/repo_review/schema.py +0 -0
- {repo_review-0.12.0 → repo_review-0.12.2}/src/repo_review/testing.py +0 -0
- {repo_review-0.12.0 → repo_review-0.12.2}/tests/conftest.py +0 -0
- {repo_review-0.12.0 → repo_review-0.12.2}/tests/test_checks.py +0 -0
- {repo_review-0.12.0 → repo_review-0.12.2}/tests/test_cmd.py +0 -0
- {repo_review-0.12.0 → repo_review-0.12.2}/tests/test_depends.py +0 -0
- {repo_review-0.12.0 → repo_review-0.12.2}/tests/test_families.py +0 -0
- {repo_review-0.12.0 → repo_review-0.12.2}/tests/test_fixtures.py +0 -0
- {repo_review-0.12.0 → repo_review-0.12.2}/tests/test_multi.py +0 -0
- {repo_review-0.12.0 → repo_review-0.12.2}/tests/test_self.py +0 -0
- {repo_review-0.12.0 → repo_review-0.12.2}/tests/test_utilities/pyproject.py +0 -0
- {repo_review-0.12.0 → repo_review-0.12.2}/tests/test_utilities/pyproject.toml +0 -0
@@ -48,17 +48,20 @@ jobs:
|
|
48
48
|
fetch-depth: 0
|
49
49
|
persist-credentials: false
|
50
50
|
|
51
|
+
# Last one is activated
|
52
|
+
# yaml circular import issue on 3.14t on ubuntu
|
51
53
|
- uses: actions/setup-python@v5
|
52
54
|
with:
|
53
55
|
python-version: |
|
54
56
|
3.10
|
55
57
|
3.11
|
56
58
|
3.12
|
59
|
+
3.14
|
57
60
|
3.13
|
58
61
|
allow-prereleases: true
|
59
62
|
|
60
63
|
- name: Setup uv
|
61
|
-
uses:
|
64
|
+
uses: astral-sh/setup-uv@v6
|
62
65
|
|
63
66
|
- name: Install hatch
|
64
67
|
run: uv pip install --system hatch
|
@@ -88,7 +91,7 @@ jobs:
|
|
88
91
|
persist-credentials: false
|
89
92
|
|
90
93
|
- name: Setup uv
|
91
|
-
uses:
|
94
|
+
uses: astral-sh/setup-uv@v6
|
92
95
|
|
93
96
|
- uses: actions/setup-python@v5
|
94
97
|
with:
|
@@ -120,4 +123,4 @@ jobs:
|
|
120
123
|
- name: Run repo-review action
|
121
124
|
uses: ./
|
122
125
|
with:
|
123
|
-
plugins: sp-repo-review==2025.
|
126
|
+
plugins: sp-repo-review==2025.05.02
|
@@ -11,14 +11,14 @@ repos:
|
|
11
11
|
additional_dependencies: [black==24.*]
|
12
12
|
|
13
13
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
14
|
-
rev: "v0.
|
14
|
+
rev: "v0.11.8"
|
15
15
|
hooks:
|
16
16
|
- id: ruff
|
17
17
|
args: ["--fix", "--show-fixes"]
|
18
18
|
- id: ruff-format
|
19
19
|
|
20
20
|
- repo: https://github.com/rbubley/mirrors-prettier
|
21
|
-
rev: "v3.
|
21
|
+
rev: "v3.5.3"
|
22
22
|
hooks:
|
23
23
|
- id: prettier
|
24
24
|
types_or: [yaml, markdown, html, css, scss, javascript, json]
|
@@ -45,7 +45,7 @@ repos:
|
|
45
45
|
- id: rst-inline-touching-normal
|
46
46
|
|
47
47
|
- repo: https://github.com/pre-commit/mirrors-mypy
|
48
|
-
rev: v1.
|
48
|
+
rev: v1.15.0
|
49
49
|
hooks:
|
50
50
|
- id: mypy
|
51
51
|
files: (src|web|tests)
|
@@ -79,12 +79,12 @@ repos:
|
|
79
79
|
exclude: .pre-commit-config.yaml
|
80
80
|
|
81
81
|
- repo: https://github.com/henryiii/validate-pyproject-schema-store
|
82
|
-
rev: 2025.
|
82
|
+
rev: 2025.04.28
|
83
83
|
hooks:
|
84
84
|
- id: validate-pyproject
|
85
85
|
|
86
86
|
- repo: https://github.com/python-jsonschema/check-jsonschema
|
87
|
-
rev: 0.
|
87
|
+
rev: 0.33.0
|
88
88
|
hooks:
|
89
89
|
- id: check-dependabot
|
90
90
|
- id: check-github-workflows
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: repo_review
|
3
|
-
Version: 0.12.
|
3
|
+
Version: 0.12.2
|
4
4
|
Summary: Framework that can run checks on repos
|
5
5
|
Project-URL: Changelog, https://github.com/scientific-python/repo-review/releases
|
6
6
|
Project-URL: Demo, https://scientific-python.github.io/repo-review
|
@@ -22,6 +22,7 @@ Classifier: Programming Language :: Python :: 3.10
|
|
22
22
|
Classifier: Programming Language :: Python :: 3.11
|
23
23
|
Classifier: Programming Language :: Python :: 3.12
|
24
24
|
Classifier: Programming Language :: Python :: 3.13
|
25
|
+
Classifier: Programming Language :: Python :: 3.14
|
25
26
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
26
27
|
Classifier: Topic :: Software Development :: Quality Assurance
|
27
28
|
Classifier: Typing :: Typed
|
@@ -128,7 +129,7 @@ repos have some [pre-commit][] check.
|
|
128
129
|
## Development of repo-review and plugins
|
129
130
|
|
130
131
|
This project is intended to be fun and easy to develop and design checks for -
|
131
|
-
it requires and uses Python 3.10
|
132
|
+
it requires and uses Python 3.10+, and uses a lot of the new features in 3.9 and
|
132
133
|
3.10. It's maybe not entirely conventional, but it enables very simple plugin
|
133
134
|
development. It works locally, remotely, and in WebAssembly (using
|
134
135
|
[Pyodide][]). [See the docs][writing-a-plugin].
|
@@ -90,7 +90,7 @@ repos have some [pre-commit][] check.
|
|
90
90
|
## Development of repo-review and plugins
|
91
91
|
|
92
92
|
This project is intended to be fun and easy to develop and design checks for -
|
93
|
-
it requires and uses Python 3.10
|
93
|
+
it requires and uses Python 3.10+, and uses a lot of the new features in 3.9 and
|
94
94
|
3.10. It's maybe not entirely conventional, but it enables very simple plugin
|
95
95
|
development. It works locally, remotely, and in WebAssembly (using
|
96
96
|
[Pyodide][]). [See the docs][writing-a-plugin].
|
@@ -6,7 +6,7 @@
|
|
6
6
|
content="initial-scale=1, width=device-width"
|
7
7
|
/>
|
8
8
|
<script
|
9
|
-
src="https://cdn.jsdelivr.net/pyodide/v0.27.
|
9
|
+
src="https://cdn.jsdelivr.net/pyodide/v0.27.6/full/pyodide.js"
|
10
10
|
crossorigin
|
11
11
|
></script>
|
12
12
|
<!-- Production -->
|
@@ -64,10 +64,10 @@
|
|
64
64
|
<App
|
65
65
|
header={true}
|
66
66
|
deps={[
|
67
|
-
"repo-review~=0.
|
68
|
-
"sp-repo-review==2025.
|
69
|
-
"validate-pyproject
|
70
|
-
"validate-pyproject
|
67
|
+
"repo-review~=0.12.1",
|
68
|
+
"sp-repo-review==2025.05.02",
|
69
|
+
"validate-pyproject[all]~=0.24.0",
|
70
|
+
"validate-pyproject-schema-store==2025.04.28",
|
71
71
|
]}
|
72
72
|
/>,
|
73
73
|
);
|
@@ -1,5 +1,5 @@
|
|
1
1
|
const DEFAULT_MSG =
|
2
|
-
"Enter a GitHub repo and branch to review. Runs Python entirely in your browser using WebAssembly. Built with React, MaterialUI, and Pyodide.";
|
2
|
+
"Enter a GitHub repo and branch/tag to review. Runs Python entirely in your browser using WebAssembly. Built with React, MaterialUI, and Pyodide.";
|
3
3
|
|
4
4
|
const urlParams = new URLSearchParams(window.location.search);
|
5
5
|
const baseurl = window.location.pathname;
|
@@ -93,7 +93,7 @@ function Results(props) {
|
|
93
93
|
variant="body2"
|
94
94
|
color="text.disabled"
|
95
95
|
>
|
96
|
-
{
|
96
|
+
{` [skipped] ${result.skip_reason}`}
|
97
97
|
</MaterialUI.Typography>
|
98
98
|
);
|
99
99
|
const msg = (
|
@@ -154,6 +154,39 @@ function Results(props) {
|
|
154
154
|
);
|
155
155
|
}
|
156
156
|
|
157
|
+
async function fetchRepoRefs(repo) {
|
158
|
+
if (!repo) return { branches: [], tags: [] };
|
159
|
+
try {
|
160
|
+
// Fetch both branches and tags from GitHub API
|
161
|
+
const [branchesResponse, tagsResponse] = await Promise.all([
|
162
|
+
fetch(`https://api.github.com/repos/${repo}/branches`),
|
163
|
+
fetch(`https://api.github.com/repos/${repo}/tags`),
|
164
|
+
]);
|
165
|
+
|
166
|
+
if (!branchesResponse.ok || !tagsResponse.ok) {
|
167
|
+
console.error("Error fetching repo data");
|
168
|
+
return { branches: [], tags: [] };
|
169
|
+
}
|
170
|
+
|
171
|
+
const branches = await branchesResponse.json();
|
172
|
+
const tags = await tagsResponse.json();
|
173
|
+
|
174
|
+
return {
|
175
|
+
branches: branches.map((branch) => ({
|
176
|
+
name: branch.name,
|
177
|
+
type: "branch",
|
178
|
+
})),
|
179
|
+
tags: tags.map((tag) => ({
|
180
|
+
name: tag.name,
|
181
|
+
type: "tag",
|
182
|
+
})),
|
183
|
+
};
|
184
|
+
} catch (error) {
|
185
|
+
console.error("Error fetching repo references:", error);
|
186
|
+
return { branches: [], tags: [] };
|
187
|
+
}
|
188
|
+
}
|
189
|
+
|
157
190
|
async function prepare_pyodide(deps) {
|
158
191
|
const deps_str = deps.map((i) => `"${i}"`).join(", ");
|
159
192
|
const pyodide = await loadPyodide();
|
@@ -196,27 +229,58 @@ class App extends React.Component {
|
|
196
229
|
this.state = {
|
197
230
|
results: [],
|
198
231
|
repo: urlParams.get("repo") || "",
|
199
|
-
|
232
|
+
ref: urlParams.get("ref") || "",
|
233
|
+
refType: urlParams.get("refType") || "branch",
|
234
|
+
refs: { branches: [], tags: [] },
|
200
235
|
msg: `<p>${DEFAULT_MSG}</p><h4>Packages:</h4> ${deps_str}`,
|
201
236
|
progress: false,
|
237
|
+
loadingRefs: false,
|
202
238
|
err_msg: "",
|
239
|
+
skip_reason: "",
|
203
240
|
url: "",
|
204
241
|
};
|
205
242
|
this.pyodide_promise = prepare_pyodide(props.deps);
|
243
|
+
this.refInputDebounce = null;
|
244
|
+
}
|
245
|
+
|
246
|
+
async fetchRepoReferences(repo) {
|
247
|
+
if (!repo) return;
|
248
|
+
|
249
|
+
this.setState({ loadingRefs: true });
|
250
|
+
const refs = await fetchRepoRefs(repo);
|
251
|
+
this.setState({
|
252
|
+
refs: refs,
|
253
|
+
loadingRefs: false,
|
254
|
+
});
|
255
|
+
}
|
256
|
+
|
257
|
+
handleRepoChange(repo) {
|
258
|
+
this.setState({ repo });
|
259
|
+
|
260
|
+
// debounce the API call to avoid too many requests
|
261
|
+
clearTimeout(this.refInputDebounce);
|
262
|
+
this.refInputDebounce = setTimeout(() => {
|
263
|
+
this.fetchRepoReferences(repo);
|
264
|
+
}, 500);
|
265
|
+
}
|
266
|
+
|
267
|
+
handleRefChange(ref, refType) {
|
268
|
+
this.setState({ ref, refType });
|
206
269
|
}
|
207
270
|
|
208
271
|
handleCompute() {
|
209
|
-
if (!this.state.repo || !this.state.
|
272
|
+
if (!this.state.repo || !this.state.ref) {
|
210
273
|
this.setState({ results: [], msg: DEFAULT_MSG });
|
211
274
|
window.history.replaceState(null, "", baseurl);
|
212
275
|
alert(
|
213
|
-
`Please enter a repo (${this.state.repo}) and branch (${this.state.
|
276
|
+
`Please enter a repo (${this.state.repo}) and branch/tag (${this.state.ref})`,
|
214
277
|
);
|
215
278
|
return;
|
216
279
|
}
|
217
280
|
const local_params = new URLSearchParams({
|
218
281
|
repo: this.state.repo,
|
219
|
-
|
282
|
+
ref: this.state.ref,
|
283
|
+
refType: this.state.refType,
|
220
284
|
});
|
221
285
|
window.history.replaceState(null, "", `${baseurl}?${local_params}`);
|
222
286
|
this.setState({
|
@@ -231,22 +295,24 @@ class App extends React.Component {
|
|
231
295
|
families_checks = pyodide.runPython(`
|
232
296
|
from repo_review.processor import process, md_as_html
|
233
297
|
from repo_review.ghpath import GHPath
|
298
|
+
from dataclasses import replace
|
234
299
|
|
235
|
-
package = GHPath(repo="${state.repo}", branch="${state.
|
236
|
-
|
300
|
+
package = GHPath(repo="${state.repo}", branch="${state.ref}")
|
301
|
+
families, checks = process(package)
|
237
302
|
|
238
|
-
for v in
|
303
|
+
for v in families.values():
|
239
304
|
if v.get("description"):
|
240
305
|
v["description"] = md_as_html(v["description"])
|
306
|
+
checks = [res.md_as_html() for res in checks]
|
241
307
|
|
242
|
-
|
308
|
+
(families, checks)
|
243
309
|
`);
|
244
310
|
} catch (e) {
|
245
311
|
if (e.message.includes("KeyError: 'tree'")) {
|
246
312
|
this.setState({
|
247
313
|
msg: DEFAULT_MSG,
|
248
314
|
progress: false,
|
249
|
-
err_msg: "Invalid repository or branch. Please try again.",
|
315
|
+
err_msg: "Invalid repository or branch/tag. Please try again.",
|
250
316
|
});
|
251
317
|
return;
|
252
318
|
}
|
@@ -276,15 +342,16 @@ class App extends React.Component {
|
|
276
342
|
name: val.name.toString(),
|
277
343
|
description: val.description.toString(),
|
278
344
|
state: val.result,
|
279
|
-
err_msg: val.
|
345
|
+
err_msg: val.err_msg.toString(),
|
280
346
|
url: val.url.toString(),
|
347
|
+
skip_reason: val.skip_reason.toString(),
|
281
348
|
});
|
282
349
|
}
|
283
350
|
|
284
351
|
this.setState({
|
285
352
|
results: results,
|
286
353
|
families: families,
|
287
|
-
msg: `Results for ${state.repo}@${state.
|
354
|
+
msg: `Results for ${state.repo}@${state.ref} (${state.refType})`,
|
288
355
|
progress: false,
|
289
356
|
err_msg: "",
|
290
357
|
url: "",
|
@@ -296,13 +363,78 @@ class App extends React.Component {
|
|
296
363
|
}
|
297
364
|
|
298
365
|
componentDidMount() {
|
299
|
-
if (urlParams.get("repo")
|
300
|
-
this.
|
366
|
+
if (urlParams.get("repo")) {
|
367
|
+
this.fetchRepoReferences(urlParams.get("repo"));
|
368
|
+
|
369
|
+
if (urlParams.get("ref")) {
|
370
|
+
this.handleCompute();
|
371
|
+
}
|
301
372
|
}
|
302
373
|
}
|
303
374
|
|
304
375
|
render() {
|
305
|
-
const
|
376
|
+
const priorityBranches = ["HEAD", "main", "master", "develop", "stable"];
|
377
|
+
const branchMap = new Map(
|
378
|
+
this.state.refs.branches.map((branch) => [branch.name, branch]),
|
379
|
+
);
|
380
|
+
|
381
|
+
let availableOptions = [];
|
382
|
+
|
383
|
+
// If no repo is entered or API hasn't returned any branches/tags yet,
|
384
|
+
// show all five priority branches.
|
385
|
+
if (
|
386
|
+
this.state.repo === "" ||
|
387
|
+
(this.state.refs.branches.length === 0 &&
|
388
|
+
this.state.refs.tags.length === 0)
|
389
|
+
) {
|
390
|
+
availableOptions = [
|
391
|
+
{ label: "HEAD (default branch)", value: "HEAD", type: "branch" },
|
392
|
+
{ label: "main (branch)", value: "main", type: "branch" },
|
393
|
+
{ label: "master (branch)", value: "master", type: "branch" },
|
394
|
+
{ label: "develop (branch)", value: "develop", type: "branch" },
|
395
|
+
{ label: "stable (branch)", value: "stable", type: "branch" },
|
396
|
+
];
|
397
|
+
} else {
|
398
|
+
const prioritizedBranches = [
|
399
|
+
{ label: "HEAD (default branch)", value: "HEAD", type: "branch" },
|
400
|
+
];
|
401
|
+
|
402
|
+
priorityBranches.slice(1).forEach((branchName) => {
|
403
|
+
if (branchMap.has(branchName)) {
|
404
|
+
prioritizedBranches.push({
|
405
|
+
label: `${branchName} (branch)`,
|
406
|
+
value: branchName,
|
407
|
+
type: "branch",
|
408
|
+
});
|
409
|
+
// Remove from map so it doesn't get added twice.
|
410
|
+
branchMap.delete(branchName);
|
411
|
+
}
|
412
|
+
});
|
413
|
+
|
414
|
+
const otherBranches = [];
|
415
|
+
branchMap.forEach((branch) => {
|
416
|
+
otherBranches.push({
|
417
|
+
label: `${branch.name} (branch)`,
|
418
|
+
value: branch.name,
|
419
|
+
type: "branch",
|
420
|
+
});
|
421
|
+
});
|
422
|
+
otherBranches.sort((a, b) => a.value.localeCompare(b.value));
|
423
|
+
|
424
|
+
const tagOptions = this.state.refs.tags.map((tag) => ({
|
425
|
+
label: `${tag.name} (tag)`,
|
426
|
+
value: tag.name,
|
427
|
+
type: "tag",
|
428
|
+
}));
|
429
|
+
tagOptions.sort((a, b) => a.value.localeCompare(b.value));
|
430
|
+
|
431
|
+
availableOptions = [
|
432
|
+
...prioritizedBranches,
|
433
|
+
...otherBranches,
|
434
|
+
...tagOptions,
|
435
|
+
];
|
436
|
+
}
|
437
|
+
|
306
438
|
return (
|
307
439
|
<MyThemeProvider>
|
308
440
|
<MaterialUI.CssBaseline />
|
@@ -322,29 +454,64 @@ class App extends React.Component {
|
|
322
454
|
autoFocus={true}
|
323
455
|
onKeyDown={(e) => {
|
324
456
|
if (e.keyCode === 13)
|
325
|
-
document.getElementById("
|
457
|
+
document.getElementById("ref-select").focus();
|
326
458
|
}}
|
327
|
-
onInput={(e) => this.
|
459
|
+
onInput={(e) => this.handleRepoChange(e.target.value)}
|
328
460
|
defaultValue={urlParams.get("repo")}
|
329
461
|
sx={{ flexGrow: 3 }}
|
330
462
|
/>
|
331
463
|
<MaterialUI.Autocomplete
|
332
464
|
disablePortal
|
333
|
-
id="
|
334
|
-
options={
|
465
|
+
id="ref-select"
|
466
|
+
options={availableOptions}
|
467
|
+
loading={this.state.loadingRefs}
|
335
468
|
freeSolo={true}
|
336
469
|
onKeyDown={(e) => {
|
337
470
|
if (e.keyCode === 13) this.handleCompute();
|
338
471
|
}}
|
339
|
-
|
340
|
-
|
472
|
+
getOptionLabel={(option) =>
|
473
|
+
typeof option === "string" ? option : option.label
|
474
|
+
}
|
475
|
+
renderOption={(props, option) => (
|
476
|
+
<li {...props}>{option.label}</li>
|
477
|
+
)}
|
478
|
+
onInputChange={(e, value) => {
|
479
|
+
// If the user enters free text, treat it as a branch
|
480
|
+
if (typeof value === "string") {
|
481
|
+
this.handleRefChange(value, "branch");
|
482
|
+
}
|
483
|
+
}}
|
484
|
+
onChange={(e, option) => {
|
485
|
+
if (option) {
|
486
|
+
if (typeof option === "object") {
|
487
|
+
this.handleRefChange(option.value, option.type);
|
488
|
+
} else {
|
489
|
+
this.handleRefChange(option, "branch");
|
490
|
+
}
|
491
|
+
}
|
492
|
+
}}
|
493
|
+
defaultValue={urlParams.get("ref")}
|
341
494
|
renderInput={(params) => (
|
342
495
|
<MaterialUI.TextField
|
343
496
|
{...params}
|
344
|
-
label="Branch"
|
497
|
+
label="Branch/Tag"
|
345
498
|
variant="outlined"
|
346
|
-
helperText="e.g. main"
|
347
|
-
sx={{ flexGrow: 2, minWidth:
|
499
|
+
helperText="e.g. HEAD, main, or v1.0.0"
|
500
|
+
sx={{ flexGrow: 2, minWidth: 200 }}
|
501
|
+
InputProps={{
|
502
|
+
...params.InputProps,
|
503
|
+
endAdornment: (
|
504
|
+
<React.Fragment>
|
505
|
+
{this.state.loadingRefs ? (
|
506
|
+
<MaterialUI.CircularProgress
|
507
|
+
color="inherit"
|
508
|
+
size={20}
|
509
|
+
/>
|
510
|
+
) : null}
|
511
|
+
{params.InputProps.endAdornment}
|
512
|
+
</React.Fragment>
|
513
|
+
),
|
514
|
+
}}
|
348
515
|
/>
|
349
516
|
)}
|
350
517
|
/>
|
@@ -354,7 +521,7 @@ class App extends React.Component {
|
|
354
521
|
variant="contained"
|
355
522
|
size="large"
|
356
523
|
disabled={
|
357
|
-
this.state.progress || !this.state.repo || !this.state.
|
524
|
+
this.state.progress || !this.state.repo || !this.state.ref
|
358
525
|
}
|
359
526
|
>
|
360
527
|
<MaterialUI.Icon>start</MaterialUI.Icon>
|
@@ -20,7 +20,7 @@ You can also use the `html` output and write your own webapp. You need to provid
|
|
20
20
|
|
21
21
|
```html
|
22
22
|
<script
|
23
|
-
src="https://cdn.jsdelivr.net/pyodide/v0.
|
23
|
+
src="https://cdn.jsdelivr.net/pyodide/v0.27.6/full/pyodide.js"
|
24
24
|
crossorigin
|
25
25
|
></script>
|
26
26
|
```
|
@@ -24,6 +24,7 @@ classifiers = [
|
|
24
24
|
"Programming Language :: Python :: 3.11",
|
25
25
|
"Programming Language :: Python :: 3.12",
|
26
26
|
"Programming Language :: Python :: 3.13",
|
27
|
+
"Programming Language :: Python :: 3.14",
|
27
28
|
"Programming Language :: Python",
|
28
29
|
"Topic :: Software Development :: Quality Assurance",
|
29
30
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
@@ -148,7 +149,7 @@ skip-install = true
|
|
148
149
|
scripts.serve = "cd docs && echo 'Serving on http://localhost:8080' && python -m http.server 8080"
|
149
150
|
|
150
151
|
[[tool.hatch.envs.hatch-test.matrix]]
|
151
|
-
python = ["3.13", "3.12", "3.11", "3.10"]
|
152
|
+
python = ["3.14", "3.13", "3.12", "3.11", "3.10"]
|
152
153
|
|
153
154
|
|
154
155
|
[tool.pytest.ini_options]
|
@@ -201,6 +202,7 @@ messages_control.disable = [
|
|
201
202
|
"used-before-assignment", # False positive on conditional import
|
202
203
|
"unnecessary-ellipsis", # Not correct for typing
|
203
204
|
"import-outside-toplevel", # better handled elsewhere
|
205
|
+
"duplicate-code", # Triggers incorrectly
|
204
206
|
]
|
205
207
|
|
206
208
|
|
@@ -6,6 +6,7 @@ import json
|
|
6
6
|
import os
|
7
7
|
import sys
|
8
8
|
import typing
|
9
|
+
import urllib.error
|
9
10
|
from collections.abc import Mapping, Sequence
|
10
11
|
from pathlib import Path
|
11
12
|
from typing import Any, Literal
|
@@ -17,6 +18,7 @@ if typing.TYPE_CHECKING:
|
|
17
18
|
else:
|
18
19
|
import rich_click as click
|
19
20
|
|
21
|
+
import rich
|
20
22
|
import rich.console
|
21
23
|
import rich.markdown
|
22
24
|
import rich.syntax
|
@@ -265,7 +267,12 @@ def _remote_path_processor(package: Path) -> Path | GHPath:
|
|
265
267
|
msg = "online repo must be of the form 'gh:org/repo@branch[:path]' (:branch missing)"
|
266
268
|
raise click.BadParameter(msg)
|
267
269
|
org_repo, branch = org_repo_branch.split("@", maxsplit=1)
|
268
|
-
|
270
|
+
try:
|
271
|
+
return GHPath(repo=org_repo, branch=branch, path=p[0] if p else "")
|
272
|
+
except urllib.error.HTTPError as e:
|
273
|
+
rich.print(f"[red][bold]Error[/bold] accessing {e.url}", file=sys.stderr)
|
274
|
+
rich.print(f"[red]{e}", file=sys.stderr)
|
275
|
+
raise SystemExit(1) from None
|
269
276
|
|
270
277
|
|
271
278
|
@click.command(context_settings={"help_option_names": ["-h", "--help"]})
|
@@ -1,8 +1,13 @@
|
|
1
|
-
# file generated by
|
1
|
+
# file generated by setuptools-scm
|
2
2
|
# don't change, don't track in version control
|
3
|
+
|
4
|
+
__all__ = ["__version__", "__version_tuple__", "version", "version_tuple"]
|
5
|
+
|
3
6
|
TYPE_CHECKING = False
|
4
7
|
if TYPE_CHECKING:
|
5
|
-
from typing import Tuple
|
8
|
+
from typing import Tuple
|
9
|
+
from typing import Union
|
10
|
+
|
6
11
|
VERSION_TUPLE = Tuple[Union[int, str], ...]
|
7
12
|
else:
|
8
13
|
VERSION_TUPLE = object
|
@@ -12,5 +17,5 @@ __version__: str
|
|
12
17
|
__version_tuple__: VERSION_TUPLE
|
13
18
|
version_tuple: VERSION_TUPLE
|
14
19
|
|
15
|
-
__version__ = version = '0.12.
|
16
|
-
__version_tuple__ = version_tuple = (0, 12,
|
20
|
+
__version__ = version = '0.12.2'
|
21
|
+
__version_tuple__ = version_tuple = (0, 12, 2)
|
@@ -6,10 +6,8 @@ import io
|
|
6
6
|
import typing
|
7
7
|
from collections.abc import Mapping, Sequence
|
8
8
|
|
9
|
-
import markdown_it
|
10
|
-
|
11
9
|
from .families import Family, get_family_description, get_family_name
|
12
|
-
from .processor import Result
|
10
|
+
from .processor import Result, md_as_html
|
13
11
|
|
14
12
|
if typing.TYPE_CHECKING:
|
15
13
|
from .__main__ import Status
|
@@ -37,16 +35,15 @@ def to_html(
|
|
37
35
|
"""
|
38
36
|
out = io.StringIO()
|
39
37
|
print = functools.partial(builtins.print, file=out)
|
40
|
-
md = markdown_it.MarkdownIt()
|
41
38
|
|
42
39
|
for family in families:
|
43
40
|
family_name = get_family_name(families, family)
|
44
41
|
family_description = get_family_description(families, family)
|
45
|
-
family_results = [r for r in processed if r.family == family]
|
42
|
+
family_results = [r.md_as_html() for r in processed if r.family == family]
|
46
43
|
if family_results or family_description:
|
47
44
|
print(f"<h3>{family_name}</h3>")
|
48
45
|
if family_description:
|
49
|
-
print(
|
46
|
+
print("<p>", md_as_html(family_description), "</p>")
|
50
47
|
if family_results:
|
51
48
|
print("<table>")
|
52
49
|
print(
|
@@ -82,7 +79,7 @@ def to_html(
|
|
82
79
|
if result.skip_reason:
|
83
80
|
description += (
|
84
81
|
f'<br/><span style="color:DarkKhaki;"><b>Skipped:</b> '
|
85
|
-
f"<em>{
|
82
|
+
f"<em>{result.skip_reason}</em></span>"
|
86
83
|
)
|
87
84
|
print(f'<tr style="color: {color};">')
|
88
85
|
print(f'<td><span role="img" aria-label="{result_txt}">{icon}</span></td>')
|
@@ -93,7 +90,7 @@ def to_html(
|
|
93
90
|
print("<td>")
|
94
91
|
print(description)
|
95
92
|
print("<br/>")
|
96
|
-
print(
|
93
|
+
print(result.err_msg)
|
97
94
|
print("</td>")
|
98
95
|
print("</tr>")
|
99
96
|
if family_results:
|
@@ -3,11 +3,12 @@ from __future__ import annotations
|
|
3
3
|
import copy
|
4
4
|
import dataclasses
|
5
5
|
import graphlib
|
6
|
+
import sys
|
6
7
|
import textwrap
|
7
8
|
import typing
|
8
9
|
import warnings
|
9
10
|
from collections.abc import Mapping, Set
|
10
|
-
from typing import Any, TypeVar
|
11
|
+
from typing import TYPE_CHECKING, Any, TypeVar
|
11
12
|
|
12
13
|
import markdown_it
|
13
14
|
|
@@ -24,6 +25,12 @@ from .families import Family, collect_families
|
|
24
25
|
from .fixtures import apply_fixtures, collect_fixtures, compute_fixtures, pyproject
|
25
26
|
from .ghpath import EmptyTraversable
|
26
27
|
|
28
|
+
if TYPE_CHECKING:
|
29
|
+
if sys.version_info >= (3, 11):
|
30
|
+
from typing import Self
|
31
|
+
else:
|
32
|
+
from typing_extensions import Self
|
33
|
+
|
27
34
|
__all__ = [
|
28
35
|
"CollectionReturn",
|
29
36
|
"ProcessReturn",
|
@@ -84,9 +91,22 @@ class Result:
|
|
84
91
|
def err_as_html(self) -> str:
|
85
92
|
"""
|
86
93
|
Produces HTML from the error message, assuming it is in markdown.
|
94
|
+
Deprecated, use :meth:`md_as_html` directly instead.
|
87
95
|
"""
|
88
96
|
return md_as_html(self.err_msg)
|
89
97
|
|
98
|
+
def md_as_html(self) -> Self:
|
99
|
+
"""
|
100
|
+
Process fields that are assumed to be markdown.
|
101
|
+
|
102
|
+
.. versionadded:: 0.12.1
|
103
|
+
"""
|
104
|
+
return dataclasses.replace(
|
105
|
+
self,
|
106
|
+
err_msg=md_as_html(self.err_msg),
|
107
|
+
skip_reason=md_as_html(self.skip_reason),
|
108
|
+
)
|
109
|
+
|
90
110
|
|
91
111
|
class ProcessReturn(typing.NamedTuple):
|
92
112
|
"""
|
@@ -25,7 +25,8 @@ def test_local():
|
|
25
25
|
assert "BSD License" in results.families["general"]["description"]
|
26
26
|
assert "[tool.repo-review]" in results.families["validate-pyproject"]["description"]
|
27
27
|
for result in results.results:
|
28
|
-
|
28
|
+
if result.result is not None:
|
29
|
+
assert result.result
|
29
30
|
|
30
31
|
|
31
32
|
def test_broken_validate_pyproject(tmp_path: Path) -> None:
|
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
|
@@ -3,8 +3,8 @@ repo\_review package
|
|
3
3
|
|
4
4
|
.. automodule:: repo_review
|
5
5
|
:members:
|
6
|
-
:undoc-members:
|
7
6
|
:show-inheritance:
|
7
|
+
:undoc-members:
|
8
8
|
|
9
9
|
Subpackages
|
10
10
|
-----------
|
@@ -22,61 +22,61 @@ repo\_review.checks module
|
|
22
22
|
|
23
23
|
.. automodule:: repo_review.checks
|
24
24
|
:members:
|
25
|
-
:undoc-members:
|
26
25
|
:show-inheritance:
|
26
|
+
:undoc-members:
|
27
27
|
|
28
28
|
repo\_review.families module
|
29
29
|
----------------------------
|
30
30
|
|
31
31
|
.. automodule:: repo_review.families
|
32
32
|
:members:
|
33
|
-
:undoc-members:
|
34
33
|
:show-inheritance:
|
34
|
+
:undoc-members:
|
35
35
|
|
36
36
|
repo\_review.fixtures module
|
37
37
|
----------------------------
|
38
38
|
|
39
39
|
.. automodule:: repo_review.fixtures
|
40
40
|
:members:
|
41
|
-
:undoc-members:
|
42
41
|
:show-inheritance:
|
42
|
+
:undoc-members:
|
43
43
|
|
44
44
|
repo\_review.ghpath module
|
45
45
|
--------------------------
|
46
46
|
|
47
47
|
.. automodule:: repo_review.ghpath
|
48
48
|
:members:
|
49
|
-
:undoc-members:
|
50
49
|
:show-inheritance:
|
50
|
+
:undoc-members:
|
51
51
|
|
52
52
|
repo\_review.html module
|
53
53
|
------------------------
|
54
54
|
|
55
55
|
.. automodule:: repo_review.html
|
56
56
|
:members:
|
57
|
-
:undoc-members:
|
58
57
|
:show-inheritance:
|
58
|
+
:undoc-members:
|
59
59
|
|
60
60
|
repo\_review.processor module
|
61
61
|
-----------------------------
|
62
62
|
|
63
63
|
.. automodule:: repo_review.processor
|
64
64
|
:members:
|
65
|
-
:undoc-members:
|
66
65
|
:show-inheritance:
|
66
|
+
:undoc-members:
|
67
67
|
|
68
68
|
repo\_review.schema module
|
69
69
|
--------------------------
|
70
70
|
|
71
71
|
.. automodule:: repo_review.schema
|
72
72
|
:members:
|
73
|
-
:undoc-members:
|
74
73
|
:show-inheritance:
|
74
|
+
:undoc-members:
|
75
75
|
|
76
76
|
repo\_review.testing module
|
77
77
|
---------------------------
|
78
78
|
|
79
79
|
.. automodule:: repo_review.testing
|
80
80
|
:members:
|
81
|
-
:undoc-members:
|
82
81
|
:show-inheritance:
|
82
|
+
:undoc-members:
|
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
|
{repo_review-0.12.0 → repo_review-0.12.2}/src/repo_review/_compat/importlib/resources/__init__.py
RENAMED
File without changes
|
{repo_review-0.12.0 → repo_review-0.12.2}/src/repo_review/_compat/importlib/resources/abc.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
|
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
|