ud-kibot 0.1.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.
- ud_kibot-0.1.1/LICENSE +21 -0
- ud_kibot-0.1.1/PKG-INFO +98 -0
- ud_kibot-0.1.1/README.md +60 -0
- ud_kibot-0.1.1/pyproject.toml +78 -0
- ud_kibot-0.1.1/setup.cfg +4 -0
- ud_kibot-0.1.1/src/ud_kibot/__init__.py +3 -0
- ud_kibot-0.1.1/src/ud_kibot/__main__.py +4 -0
- ud_kibot-0.1.1/src/ud_kibot/assets/resolver.py +15 -0
- ud_kibot-0.1.1/src/ud_kibot/cli/__init__.py +0 -0
- ud_kibot-0.1.1/src/ud_kibot/cli/main.py +372 -0
- ud_kibot-0.1.1/src/ud_kibot/config/__init__.py +0 -0
- ud_kibot-0.1.1/src/ud_kibot/config/loader.py +55 -0
- ud_kibot-0.1.1/src/ud_kibot/config/lock.py +35 -0
- ud_kibot-0.1.1/src/ud_kibot/config/schema.py +76 -0
- ud_kibot-0.1.1/src/ud_kibot/config/user_schema.py +48 -0
- ud_kibot-0.1.1/src/ud_kibot/doctor/__init__.py +0 -0
- ud_kibot-0.1.1/src/ud_kibot/doctor/checks.py +84 -0
- ud_kibot-0.1.1/src/ud_kibot/hooks.py +36 -0
- ud_kibot-0.1.1/src/ud_kibot/kibot/__init__.py +0 -0
- ud_kibot-0.1.1/src/ud_kibot/kibot/invoke.py +123 -0
- ud_kibot-0.1.1/src/ud_kibot/pages.py +47 -0
- ud_kibot-0.1.1/src/ud_kibot/paths.py +63 -0
- ud_kibot-0.1.1/src/ud_kibot/template/__init__.py +0 -0
- ud_kibot-0.1.1/src/ud_kibot/template/installer.py +50 -0
- ud_kibot-0.1.1/src/ud_kibot/template/manifest.py +31 -0
- ud_kibot-0.1.1/src/ud_kibot/template/renderer.py +144 -0
- ud_kibot-0.1.1/src/ud_kibot/template/resolver.py +168 -0
- ud_kibot-0.1.1/src/ud_kibot/template/stage.py +30 -0
- ud_kibot-0.1.1/src/ud_kibot/validate.py +84 -0
- ud_kibot-0.1.1/src/ud_kibot.egg-info/PKG-INFO +98 -0
- ud_kibot-0.1.1/src/ud_kibot.egg-info/SOURCES.txt +41 -0
- ud_kibot-0.1.1/src/ud_kibot.egg-info/dependency_links.txt +1 -0
- ud_kibot-0.1.1/src/ud_kibot.egg-info/entry_points.txt +2 -0
- ud_kibot-0.1.1/src/ud_kibot.egg-info/requires.txt +15 -0
- ud_kibot-0.1.1/src/ud_kibot.egg-info/top_level.txt +1 -0
- ud_kibot-0.1.1/tests/test_cli.py +50 -0
- ud_kibot-0.1.1/tests/test_config.py +70 -0
- ud_kibot-0.1.1/tests/test_coverage_extra.py +144 -0
- ud_kibot-0.1.1/tests/test_hooks.py +33 -0
- ud_kibot-0.1.1/tests/test_release_utils.py +50 -0
- ud_kibot-0.1.1/tests/test_runtime.py +70 -0
- ud_kibot-0.1.1/tests/test_template.py +77 -0
- ud_kibot-0.1.1/tests/test_verify_release.py +36 -0
ud_kibot-0.1.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 UltraDuino contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
ud_kibot-0.1.1/PKG-INFO
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ud-kibot
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: UltraDuino KiCad/KiBot project automation CLI
|
|
5
|
+
Author-email: UltraDuino <ops@ultraduino.org>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://gitlab.com/ultraduino/ud-kibot
|
|
8
|
+
Project-URL: Repository, https://gitlab.com/ultraduino/ud-kibot.git
|
|
9
|
+
Project-URL: Documentation, https://gitlab.com/ultraduino/ud-kibot/-/blob/develop/README.md
|
|
10
|
+
Keywords: kicad,kibot,gitlab,pcb,ci,cli
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
15
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
16
|
+
Classifier: Operating System :: MacOS
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
21
|
+
Requires-Python: >=3.11
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Requires-Dist: typer>=0.12
|
|
25
|
+
Requires-Dist: pydantic>=2.0
|
|
26
|
+
Requires-Dist: jinja2>=3.1
|
|
27
|
+
Requires-Dist: pyyaml>=6.0
|
|
28
|
+
Requires-Dist: rich>=13.0
|
|
29
|
+
Requires-Dist: packaging>=24.0
|
|
30
|
+
Provides-Extra: dev
|
|
31
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
32
|
+
Requires-Dist: pytest-cov>=5.0; extra == "dev"
|
|
33
|
+
Requires-Dist: ruff>=0.8; extra == "dev"
|
|
34
|
+
Provides-Extra: release
|
|
35
|
+
Requires-Dist: build>=1.2; extra == "release"
|
|
36
|
+
Requires-Dist: twine>=5.0; extra == "release"
|
|
37
|
+
Dynamic: license-file
|
|
38
|
+
|
|
39
|
+
# ud-kibot
|
|
40
|
+
|
|
41
|
+
UltraDuino CLI for KiCad/KiBot project automation.
|
|
42
|
+
|
|
43
|
+
## Install
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
pip install ud-kibot
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Development install:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
cd ud-kibot
|
|
53
|
+
pip install -e ".[dev,release]"
|
|
54
|
+
pre-commit install
|
|
55
|
+
pre-commit install --hook-type commit-msg
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Quick start
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
ud-kibot config init
|
|
62
|
+
cd ../ucube_left_card
|
|
63
|
+
ud-kibot init . --board ucube_left_card --skip-pcb-scaffold
|
|
64
|
+
ud-kibot install . --template-path ../ultraduino-kibot-template
|
|
65
|
+
ud-kibot doctor .
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Development
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
make lint
|
|
72
|
+
make test
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Coverage gate: **80%** (`pyproject.toml`).
|
|
76
|
+
|
|
77
|
+
## Release flow
|
|
78
|
+
|
|
79
|
+
Same model as `esys-ci-host`:
|
|
80
|
+
|
|
81
|
+
1. Update `[Unreleased]` in `CHANGELOG.md`
|
|
82
|
+
2. `make bump-release` (or `python scripts/bump_release.py`)
|
|
83
|
+
3. Merge to `master`, wait for green CI
|
|
84
|
+
4. `make tag-release` (or `python scripts/tag_release.py --yes`)
|
|
85
|
+
5. `git push origin vX.Y.Z`
|
|
86
|
+
|
|
87
|
+
Tag pipelines build the wheel, smoke-install it, publish to GitLab PyPI, then optionally TestPyPI/PyPI.
|
|
88
|
+
|
|
89
|
+
## User configuration
|
|
90
|
+
|
|
91
|
+
- Linux/macOS: `~/.config/ud-kibot/config.yaml`
|
|
92
|
+
- Windows: `%APPDATA%\ud-kibot\config.yaml`
|
|
93
|
+
|
|
94
|
+
Default template: `https://gitlab.com/ultraduino/ultraduino-kibot-template.git`
|
|
95
|
+
|
|
96
|
+
## License
|
|
97
|
+
|
|
98
|
+
MIT — see [LICENSE](LICENSE).
|
ud_kibot-0.1.1/README.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# ud-kibot
|
|
2
|
+
|
|
3
|
+
UltraDuino CLI for KiCad/KiBot project automation.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install ud-kibot
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Development install:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
cd ud-kibot
|
|
15
|
+
pip install -e ".[dev,release]"
|
|
16
|
+
pre-commit install
|
|
17
|
+
pre-commit install --hook-type commit-msg
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Quick start
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
ud-kibot config init
|
|
24
|
+
cd ../ucube_left_card
|
|
25
|
+
ud-kibot init . --board ucube_left_card --skip-pcb-scaffold
|
|
26
|
+
ud-kibot install . --template-path ../ultraduino-kibot-template
|
|
27
|
+
ud-kibot doctor .
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Development
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
make lint
|
|
34
|
+
make test
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Coverage gate: **80%** (`pyproject.toml`).
|
|
38
|
+
|
|
39
|
+
## Release flow
|
|
40
|
+
|
|
41
|
+
Same model as `esys-ci-host`:
|
|
42
|
+
|
|
43
|
+
1. Update `[Unreleased]` in `CHANGELOG.md`
|
|
44
|
+
2. `make bump-release` (or `python scripts/bump_release.py`)
|
|
45
|
+
3. Merge to `master`, wait for green CI
|
|
46
|
+
4. `make tag-release` (or `python scripts/tag_release.py --yes`)
|
|
47
|
+
5. `git push origin vX.Y.Z`
|
|
48
|
+
|
|
49
|
+
Tag pipelines build the wheel, smoke-install it, publish to GitLab PyPI, then optionally TestPyPI/PyPI.
|
|
50
|
+
|
|
51
|
+
## User configuration
|
|
52
|
+
|
|
53
|
+
- Linux/macOS: `~/.config/ud-kibot/config.yaml`
|
|
54
|
+
- Windows: `%APPDATA%\ud-kibot\config.yaml`
|
|
55
|
+
|
|
56
|
+
Default template: `https://gitlab.com/ultraduino/ultraduino-kibot-template.git`
|
|
57
|
+
|
|
58
|
+
## License
|
|
59
|
+
|
|
60
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "ud-kibot"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "UltraDuino KiCad/KiBot project automation CLI"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
authors = [{ name = "UltraDuino", email = "ops@ultraduino.org" }]
|
|
14
|
+
keywords = ["kicad", "kibot", "gitlab", "pcb", "ci", "cli"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 3 - Alpha",
|
|
17
|
+
"Environment :: Console",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"Operating System :: Microsoft :: Windows",
|
|
20
|
+
"Operating System :: POSIX :: Linux",
|
|
21
|
+
"Operating System :: MacOS",
|
|
22
|
+
"Programming Language :: Python :: 3",
|
|
23
|
+
"Programming Language :: Python :: 3.11",
|
|
24
|
+
"Programming Language :: Python :: 3.12",
|
|
25
|
+
"Topic :: Software Development :: Build Tools",
|
|
26
|
+
]
|
|
27
|
+
dependencies = [
|
|
28
|
+
"typer>=0.12",
|
|
29
|
+
"pydantic>=2.0",
|
|
30
|
+
"jinja2>=3.1",
|
|
31
|
+
"pyyaml>=6.0",
|
|
32
|
+
"rich>=13.0",
|
|
33
|
+
"packaging>=24.0",
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
[project.urls]
|
|
37
|
+
Homepage = "https://gitlab.com/ultraduino/ud-kibot"
|
|
38
|
+
Repository = "https://gitlab.com/ultraduino/ud-kibot.git"
|
|
39
|
+
Documentation = "https://gitlab.com/ultraduino/ud-kibot/-/blob/develop/README.md"
|
|
40
|
+
|
|
41
|
+
[project.optional-dependencies]
|
|
42
|
+
dev = [
|
|
43
|
+
"pytest>=8.0",
|
|
44
|
+
"pytest-cov>=5.0",
|
|
45
|
+
"ruff>=0.8",
|
|
46
|
+
]
|
|
47
|
+
release = [
|
|
48
|
+
"build>=1.2",
|
|
49
|
+
"twine>=5.0",
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
[project.scripts]
|
|
53
|
+
ud-kibot = "ud_kibot.cli.main:app"
|
|
54
|
+
|
|
55
|
+
[tool.setuptools.packages.find]
|
|
56
|
+
where = ["src"]
|
|
57
|
+
|
|
58
|
+
[tool.setuptools.dynamic]
|
|
59
|
+
version = { attr = "ud_kibot.__version__" }
|
|
60
|
+
|
|
61
|
+
[tool.pytest.ini_options]
|
|
62
|
+
testpaths = ["tests"]
|
|
63
|
+
pythonpath = ["src"]
|
|
64
|
+
|
|
65
|
+
[tool.ruff]
|
|
66
|
+
line-length = 100
|
|
67
|
+
target-version = "py311"
|
|
68
|
+
src = ["src"]
|
|
69
|
+
|
|
70
|
+
[tool.ruff.lint]
|
|
71
|
+
select = ["E", "F", "I", "UP"]
|
|
72
|
+
|
|
73
|
+
[tool.coverage.run]
|
|
74
|
+
source = ["ud_kibot"]
|
|
75
|
+
|
|
76
|
+
[tool.coverage.report]
|
|
77
|
+
fail_under = 80
|
|
78
|
+
show_missing = true
|
ud_kibot-0.1.1/setup.cfg
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Resolve project logo: project override wins over template default."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def resolve_logo(project_dir: Path, template_dir: Path) -> Path:
|
|
9
|
+
project_logo = project_dir / "assets" / "logo.png"
|
|
10
|
+
if project_logo.is_file():
|
|
11
|
+
return project_logo
|
|
12
|
+
template_logo = template_dir / "assets" / "logo.png"
|
|
13
|
+
if template_logo.is_file():
|
|
14
|
+
return template_logo
|
|
15
|
+
return project_logo
|
|
File without changes
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
"""Typer CLI entry point."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import typer
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
from rich.table import Table
|
|
10
|
+
|
|
11
|
+
from ud_kibot import __version__
|
|
12
|
+
from ud_kibot.config.loader import load_user_config, save_user_config
|
|
13
|
+
from ud_kibot.config.schema import (
|
|
14
|
+
BoardMeta,
|
|
15
|
+
CiMeta,
|
|
16
|
+
DocumentationMeta,
|
|
17
|
+
GitLabCiMeta,
|
|
18
|
+
KibotMeta,
|
|
19
|
+
KicadMeta,
|
|
20
|
+
ProjectConfig,
|
|
21
|
+
ProjectMeta,
|
|
22
|
+
SiteMeta,
|
|
23
|
+
TemplatePin,
|
|
24
|
+
)
|
|
25
|
+
from ud_kibot.config.user_schema import (
|
|
26
|
+
DefaultsConfig,
|
|
27
|
+
TemplateSourceConfig,
|
|
28
|
+
UserConfig,
|
|
29
|
+
)
|
|
30
|
+
from ud_kibot.doctor.checks import doctor_project
|
|
31
|
+
from ud_kibot.hooks import install_pre_commit_hooks
|
|
32
|
+
from ud_kibot.kibot.invoke import prepare_and_run
|
|
33
|
+
from ud_kibot.pages import assemble_pages
|
|
34
|
+
from ud_kibot.paths import normalize_project_dir
|
|
35
|
+
from ud_kibot.template.installer import install_project_scaffold
|
|
36
|
+
from ud_kibot.template.renderer import render_kibot_main
|
|
37
|
+
from ud_kibot.template.resolver import (
|
|
38
|
+
ensure_template_cached,
|
|
39
|
+
fetch_git_template,
|
|
40
|
+
resolve_template_for_project,
|
|
41
|
+
template_source_for_id,
|
|
42
|
+
)
|
|
43
|
+
from ud_kibot.validate import validate_project
|
|
44
|
+
|
|
45
|
+
app = typer.Typer(
|
|
46
|
+
name="ud-kibot",
|
|
47
|
+
help="UltraDuino KiCad/KiBot project automation.",
|
|
48
|
+
no_args_is_help=True,
|
|
49
|
+
)
|
|
50
|
+
config_app = typer.Typer(help="User configuration.")
|
|
51
|
+
template_app = typer.Typer(help="Template cache management.")
|
|
52
|
+
app.add_typer(config_app, name="config")
|
|
53
|
+
app.add_typer(template_app, name="template")
|
|
54
|
+
|
|
55
|
+
console = Console()
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _version_callback(value: bool) -> None:
|
|
59
|
+
if value:
|
|
60
|
+
typer.echo(__version__)
|
|
61
|
+
raise typer.Exit()
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@app.callback()
|
|
65
|
+
def main_callback(
|
|
66
|
+
version: bool = typer.Option(
|
|
67
|
+
False,
|
|
68
|
+
"--version",
|
|
69
|
+
"-V",
|
|
70
|
+
callback=_version_callback,
|
|
71
|
+
is_eager=True,
|
|
72
|
+
help="Show version.",
|
|
73
|
+
),
|
|
74
|
+
) -> None:
|
|
75
|
+
"""Root callback."""
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _resolve_path(path: Path) -> Path:
|
|
79
|
+
return normalize_project_dir(path)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _resolve_template_path(template_path: Path | None) -> Path | None:
|
|
83
|
+
return template_path.resolve() if template_path else None
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@config_app.command("init")
|
|
87
|
+
def config_init(
|
|
88
|
+
non_interactive: bool = typer.Option(
|
|
89
|
+
False, "--non-interactive", help="Skip prompts; accept defaults."
|
|
90
|
+
),
|
|
91
|
+
accept_defaults: bool = typer.Option(
|
|
92
|
+
False, "--accept-defaults", help="Alias for non-interactive with defaults."
|
|
93
|
+
),
|
|
94
|
+
) -> None:
|
|
95
|
+
"""Initialize user configuration."""
|
|
96
|
+
existing = load_user_config()
|
|
97
|
+
if existing.first_run_complete and not non_interactive and not accept_defaults:
|
|
98
|
+
overwrite = typer.confirm("User config already exists. Overwrite?", default=False)
|
|
99
|
+
if not overwrite:
|
|
100
|
+
raise typer.Exit()
|
|
101
|
+
|
|
102
|
+
if non_interactive or accept_defaults:
|
|
103
|
+
config = UserConfig.create_default()
|
|
104
|
+
else:
|
|
105
|
+
use_default = typer.confirm(
|
|
106
|
+
"Use ultraduino-kibot-template as default template?\n"
|
|
107
|
+
" https://gitlab.com/ultraduino/ultraduino-kibot-template.git",
|
|
108
|
+
default=True,
|
|
109
|
+
)
|
|
110
|
+
if use_default:
|
|
111
|
+
config = UserConfig.create_default()
|
|
112
|
+
else:
|
|
113
|
+
template_id = typer.prompt("Template id", default="custom-template")
|
|
114
|
+
git_url = typer.prompt("Template git URL")
|
|
115
|
+
config = UserConfig(
|
|
116
|
+
first_run_complete=True,
|
|
117
|
+
defaults=DefaultsConfig(template_id=template_id),
|
|
118
|
+
templates={
|
|
119
|
+
template_id: TemplateSourceConfig(git_url=git_url, default_ref="master"),
|
|
120
|
+
},
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
path = save_user_config(config)
|
|
124
|
+
console.print(f"[green]Wrote user config:[/green] {path}")
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
@config_app.command("show")
|
|
128
|
+
def config_show() -> None:
|
|
129
|
+
"""Show user configuration."""
|
|
130
|
+
from ud_kibot.paths import user_config_path
|
|
131
|
+
|
|
132
|
+
config = load_user_config()
|
|
133
|
+
console.print(config.model_dump_json(indent=2))
|
|
134
|
+
console.print(f"\nPath: {user_config_path()}")
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
@app.command("init")
|
|
138
|
+
def project_init(
|
|
139
|
+
path: Path = typer.Argument(Path("."), help="Project directory."),
|
|
140
|
+
board: str = typer.Option(..., "--board", "-b", help="KiCad project basename."),
|
|
141
|
+
title: str = typer.Option("", "--title", help="Board title."),
|
|
142
|
+
skip_pcb_scaffold: bool = typer.Option(
|
|
143
|
+
False, "--skip-pcb-scaffold", help="Do not create pcb/ scaffold."
|
|
144
|
+
),
|
|
145
|
+
) -> None:
|
|
146
|
+
"""Initialize a PCB project with ud-kibot.yaml and directories."""
|
|
147
|
+
project_dir = _resolve_path(path)
|
|
148
|
+
project_dir.mkdir(parents=True, exist_ok=True)
|
|
149
|
+
pcb_dir = project_dir / "pcb"
|
|
150
|
+
pcb_dir.mkdir(exist_ok=True)
|
|
151
|
+
|
|
152
|
+
if not skip_pcb_scaffold:
|
|
153
|
+
(pcb_dir / "lib").mkdir(exist_ok=True)
|
|
154
|
+
(pcb_dir / "Templates").mkdir(exist_ok=True)
|
|
155
|
+
|
|
156
|
+
for sub in ("assets", "kibot/local", "scripts", ".ud-kibot"):
|
|
157
|
+
(project_dir / sub).mkdir(parents=True, exist_ok=True)
|
|
158
|
+
|
|
159
|
+
config_path = project_dir / "ud-kibot.yaml"
|
|
160
|
+
if not config_path.is_file():
|
|
161
|
+
board_title = title or board.replace("_", " ").title()
|
|
162
|
+
config = ProjectConfig(
|
|
163
|
+
template=TemplatePin(),
|
|
164
|
+
project=ProjectMeta(id=board, name=board_title),
|
|
165
|
+
board=BoardMeta(basename=board, title=board_title),
|
|
166
|
+
kicad=KicadMeta(),
|
|
167
|
+
kibot=KibotMeta(),
|
|
168
|
+
documentation=DocumentationMeta(site=SiteMeta(title=f"UltraDuino · {board}")),
|
|
169
|
+
ci=CiMeta(gitlab=GitLabCiMeta()),
|
|
170
|
+
)
|
|
171
|
+
from ud_kibot.config.loader import save_project_config
|
|
172
|
+
|
|
173
|
+
save_project_config(project_dir, config)
|
|
174
|
+
console.print(f"[green]Created[/green] {config_path}")
|
|
175
|
+
else:
|
|
176
|
+
console.print(f"[yellow]Exists[/yellow] {config_path}")
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
@app.command("install")
|
|
180
|
+
def project_install(
|
|
181
|
+
path: Path = typer.Argument(Path("."), help="Project directory."),
|
|
182
|
+
template_path: Path | None = typer.Option(
|
|
183
|
+
None, "--template-path", help="Local template checkout (dev)."
|
|
184
|
+
),
|
|
185
|
+
force: bool = typer.Option(False, "--force", help="Overwrite scaffold files."),
|
|
186
|
+
skip_hooks: bool = typer.Option(False, "--skip-hooks", help="Do not run pre-commit install."),
|
|
187
|
+
) -> None:
|
|
188
|
+
"""Bind project to template and install minimal scaffold."""
|
|
189
|
+
project_dir = _resolve_path(path)
|
|
190
|
+
written = install_project_scaffold(
|
|
191
|
+
project_dir,
|
|
192
|
+
template_path=_resolve_template_path(template_path),
|
|
193
|
+
force=force,
|
|
194
|
+
)
|
|
195
|
+
console.print(f"[green]Installed template scaffold ({len(written)} files).[/green]")
|
|
196
|
+
for item in written:
|
|
197
|
+
console.print(f" - {item.relative_to(project_dir)}")
|
|
198
|
+
if not skip_hooks:
|
|
199
|
+
_report_hook_install(project_dir)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
@app.command("install-hooks")
|
|
203
|
+
def project_install_hooks(
|
|
204
|
+
path: Path = typer.Argument(Path("."), help="Project directory."),
|
|
205
|
+
) -> None:
|
|
206
|
+
"""Install pre-commit and commit-msg hooks for a PCB project."""
|
|
207
|
+
_report_hook_install(_resolve_path(path))
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def _report_hook_install(project_dir: Path) -> None:
|
|
211
|
+
ok, message = install_pre_commit_hooks(project_dir)
|
|
212
|
+
if ok:
|
|
213
|
+
console.print(f"[green]{message}[/green]")
|
|
214
|
+
else:
|
|
215
|
+
console.print(f"[yellow]Pre-commit hooks not installed: {message}[/yellow]")
|
|
216
|
+
console.print(" Install pre-commit, then run: ud-kibot install-hooks .")
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
@app.command("upgrade")
|
|
220
|
+
def project_upgrade(
|
|
221
|
+
path: Path = typer.Argument(Path("."), help="Project directory."),
|
|
222
|
+
template_path: Path | None = typer.Option(None, "--template-path"),
|
|
223
|
+
force: bool = typer.Option(False, "--force"),
|
|
224
|
+
) -> None:
|
|
225
|
+
"""Refresh template cache and re-render project files."""
|
|
226
|
+
project_install(path=path, template_path=template_path, force=force)
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
@app.command("repair")
|
|
230
|
+
def project_repair(
|
|
231
|
+
path: Path = typer.Argument(Path("."), help="Project directory."),
|
|
232
|
+
template_path: Path | None = typer.Option(None, "--template-path"),
|
|
233
|
+
) -> None:
|
|
234
|
+
"""Re-fetch template, rewrite lock, and re-render."""
|
|
235
|
+
project_dir = _resolve_path(path)
|
|
236
|
+
ensure_template_cached(project_dir, template_path=_resolve_template_path(template_path))
|
|
237
|
+
install_project_scaffold(
|
|
238
|
+
project_dir,
|
|
239
|
+
template_path=_resolve_template_path(template_path),
|
|
240
|
+
force=True,
|
|
241
|
+
)
|
|
242
|
+
console.print("[green]Repair complete.[/green]")
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
@app.command("render-config")
|
|
246
|
+
def render_config(
|
|
247
|
+
path: Path = typer.Argument(Path("."), help="Project directory."),
|
|
248
|
+
template_path: Path | None = typer.Option(None, "--template-path"),
|
|
249
|
+
) -> None:
|
|
250
|
+
"""Render KiBot main config and refresh scaffold renders."""
|
|
251
|
+
project_dir = _resolve_path(path)
|
|
252
|
+
template_dir, _ = resolve_template_for_project(
|
|
253
|
+
project_dir, template_path=_resolve_template_path(template_path)
|
|
254
|
+
)
|
|
255
|
+
output = render_kibot_main(project_dir, template_dir)
|
|
256
|
+
console.print(f"[green]Rendered[/green] {output}")
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
@template_app.command("fetch")
|
|
260
|
+
def template_fetch(
|
|
261
|
+
template_id: str | None = typer.Argument(None, help="Template id."),
|
|
262
|
+
project: Path | None = typer.Option(None, "--project", help="Fetch template for a project."),
|
|
263
|
+
template_path: Path | None = typer.Option(None, "--template-path"),
|
|
264
|
+
ref: str | None = typer.Option(None, "--ref"),
|
|
265
|
+
) -> None:
|
|
266
|
+
"""Fetch template into global cache."""
|
|
267
|
+
if project is not None:
|
|
268
|
+
directory = ensure_template_cached(
|
|
269
|
+
_resolve_path(project), template_path=_resolve_template_path(template_path)
|
|
270
|
+
)
|
|
271
|
+
console.print(f"[green]Cached[/green] {directory}")
|
|
272
|
+
return
|
|
273
|
+
|
|
274
|
+
user = load_user_config()
|
|
275
|
+
tid = template_id or user.defaults.template_id
|
|
276
|
+
if tid not in user.templates:
|
|
277
|
+
raise typer.BadParameter(f"Unknown template id: {tid}")
|
|
278
|
+
git_url, default_ref = template_source_for_id(user, tid)
|
|
279
|
+
directory = fetch_git_template(tid, git_url, ref or default_ref)
|
|
280
|
+
from ud_kibot.template.manifest import load_manifest
|
|
281
|
+
|
|
282
|
+
manifest = load_manifest(directory)
|
|
283
|
+
console.print(f"[green]Fetched[/green] {manifest.id}@{manifest.version} → {directory}")
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
@app.command("validate")
|
|
287
|
+
def project_validate(
|
|
288
|
+
path: Path = typer.Argument(Path("."), help="Project directory."),
|
|
289
|
+
template_path: Path | None = typer.Option(None, "--template-path"),
|
|
290
|
+
strict: bool = typer.Option(False, "--strict"),
|
|
291
|
+
) -> None:
|
|
292
|
+
"""Validate project configuration and template binding."""
|
|
293
|
+
issues = validate_project(
|
|
294
|
+
_resolve_path(path),
|
|
295
|
+
template_path=_resolve_template_path(template_path),
|
|
296
|
+
strict=strict,
|
|
297
|
+
)
|
|
298
|
+
exit_code = 0
|
|
299
|
+
for issue in issues:
|
|
300
|
+
if issue.level == "error":
|
|
301
|
+
console.print(f"[red]ERROR[/red] {issue.message}")
|
|
302
|
+
exit_code = 1
|
|
303
|
+
else:
|
|
304
|
+
console.print(f"[yellow]WARN[/yellow] {issue.message}")
|
|
305
|
+
if exit_code:
|
|
306
|
+
raise typer.Exit(code=exit_code)
|
|
307
|
+
console.print("[green]Validation passed.[/green]")
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
@app.command("doctor")
|
|
311
|
+
def project_doctor(
|
|
312
|
+
path: Path = typer.Argument(Path("."), help="Project directory."),
|
|
313
|
+
template_path: Path | None = typer.Option(None, "--template-path"),
|
|
314
|
+
) -> None:
|
|
315
|
+
"""Run health checks."""
|
|
316
|
+
results = doctor_project(
|
|
317
|
+
_resolve_path(path), template_path=_resolve_template_path(template_path)
|
|
318
|
+
)
|
|
319
|
+
table = Table("Check", "Status", "Detail")
|
|
320
|
+
failed = False
|
|
321
|
+
non_fatal = frozenset({"docker", "user-config"})
|
|
322
|
+
for item in results:
|
|
323
|
+
ok = item.ok
|
|
324
|
+
if item.name == "validate" and not ok:
|
|
325
|
+
failed = True
|
|
326
|
+
if item.name == "validate-warning":
|
|
327
|
+
status = "warn"
|
|
328
|
+
else:
|
|
329
|
+
status = "ok" if ok else "fail"
|
|
330
|
+
if not ok and item.name not in non_fatal:
|
|
331
|
+
failed = True
|
|
332
|
+
table.add_row(item.name, status, item.detail)
|
|
333
|
+
console.print(table)
|
|
334
|
+
if failed:
|
|
335
|
+
raise typer.Exit(code=1)
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
@app.command("run-kibot")
|
|
339
|
+
def run_kibot(
|
|
340
|
+
path: Path = typer.Argument(Path("."), help="Project directory."),
|
|
341
|
+
variant: str | None = typer.Option(None, "--variant", "-v"),
|
|
342
|
+
group: str | None = typer.Option(None, "--group", "-g"),
|
|
343
|
+
skip_pre: str | None = typer.Option(None, "--skip-pre"),
|
|
344
|
+
template_path: Path | None = typer.Option(None, "--template-path"),
|
|
345
|
+
no_docker: bool = typer.Option(False, "--no-docker"),
|
|
346
|
+
) -> None:
|
|
347
|
+
"""Render config and run KiBot."""
|
|
348
|
+
code = prepare_and_run(
|
|
349
|
+
_resolve_path(path),
|
|
350
|
+
template_path=_resolve_template_path(template_path),
|
|
351
|
+
variant=variant,
|
|
352
|
+
group=group,
|
|
353
|
+
skip_pre=skip_pre,
|
|
354
|
+
no_docker=no_docker,
|
|
355
|
+
)
|
|
356
|
+
raise typer.Exit(code=code)
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
@app.command("assemble-pages")
|
|
360
|
+
def assemble_pages_cmd(
|
|
361
|
+
path: Path = typer.Argument(Path("."), help="Project directory."),
|
|
362
|
+
template_path: Path | None = typer.Option(None, "--template-path"),
|
|
363
|
+
) -> None:
|
|
364
|
+
"""Assemble GitLab Pages site into public/."""
|
|
365
|
+
public = assemble_pages(
|
|
366
|
+
_resolve_path(path), template_path=_resolve_template_path(template_path)
|
|
367
|
+
)
|
|
368
|
+
console.print(f"[green]Pages assembled at[/green] {public}")
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
if __name__ == "__main__":
|
|
372
|
+
app()
|
|
File without changes
|