yal-cmd 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
yal_cmd-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Я́лла Нкарда́з д͏е Туде́рий
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.
yal_cmd-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,119 @@
1
+ Metadata-Version: 2.4
2
+ Name: yal-cmd
3
+ Version: 0.1.0
4
+ Summary: Yalla Nkardaz’s personal CLI toolkit
5
+ Author-email: Yalla Nkardaz <dev_nkardaz@proton.me>
6
+ License-Expression: MIT
7
+ Requires-Python: >=3.10
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Requires-Dist: requests
11
+ Requires-Dist: pyyaml
12
+ Requires-Dist: ruamel.yaml
13
+ Requires-Dist: tomli; python_version < "3.11"
14
+ Dynamic: license-file
15
+
16
+ # YAL
17
+
18
+ **YAL** is a command-line utility for project initialization and updates based on templates.
19
+
20
+ ## Commands
21
+
22
+ - `yal create <kind>[:<name>][@<ref>]` — uses a template to create a new project (currently supports only `book` kind)
23
+ - `yal update <kind>[:<name>]` — updates an existing project (currently supports only `book` kind)
24
+
25
+ ## Install utility
26
+
27
+ ```bash
28
+ pip install yal
29
+ ```
30
+
31
+ ## Create a project
32
+ Downloads the template (if not cached locally) and initiates the configuration process.
33
+ ```bash
34
+ # Basic usage
35
+ yal create book
36
+
37
+ # Using a specific template
38
+ yal create book:template-name
39
+
40
+ # Using a specific version (tag or commit hash)
41
+ yal create book@version
42
+
43
+ # Combined usage
44
+ yal create book:template-name@version
45
+ ```
46
+
47
+ ## yal.toml
48
+
49
+ A configuration file used as a prompt when creating a project from a template. The `yal.toml` file is located in the root of the template and may have a structure like the following:
50
+
51
+ ```toml
52
+ [meta]
53
+ yal-min-version = "0.1.0"
54
+
55
+ [[fields]]
56
+ id = "book-name"
57
+ type = "text"
58
+ required = true
59
+ default = "{placeholder}"
60
+ is-folder-name = true
61
+
62
+ [[fields]]
63
+ id = "book-author"
64
+ type = "text"
65
+ required = false
66
+ default = ""
67
+
68
+ [[fields]]
69
+ id = "book-lang"
70
+ type = "select"
71
+ options = ["ru", "en", "de"]
72
+ default = "ru"
73
+
74
+ [messages.ru]
75
+ book-name.prompt = "Введите название книги"
76
+ book-name.placeholder = "Книга без названия"
77
+ book-author.prompt = "Введите имя автора"
78
+ book-author.placeholder = "Аноним"
79
+ book-lang.prompt = "Выберите язык книги в формате ISO-639"
80
+
81
+ [messages.en]
82
+ book-name.prompt = "Enter book title"
83
+ book-name.placeholder = "Untitled book"
84
+ book-author.prompt = "Enter author name"
85
+ book-author.placeholder = "Anonymous"
86
+ book-lang.prompt = "Select book language in ISO-639 format"
87
+
88
+ [[targets]]
89
+ file = "/meta/book.yml"
90
+ format = "yaml"
91
+
92
+ [[targets.fields]]
93
+ field = "book-name"
94
+ key = "book.title"
95
+
96
+ [[targets.fields]]
97
+ field = "book-author"
98
+ key = "author[0].name"
99
+
100
+ [[targets.fields]]
101
+ field = "book-lang"
102
+ key = "property.language[ISO-639]"
103
+ ```
104
+
105
+
106
+ ## Built-in templates
107
+
108
+
109
+ ### \<Book\> Typst Book Template
110
+
111
+ - **Repo:** https://github.com/DemerNkardaz/Typst-Book-Template
112
+ - **Name:** default
113
+
114
+ A template for creating a book using [Typst](https://typst.app/), including a prebuilt structure, plugins, styles, settings, and a build system that combines Typst and Python tools.
115
+
116
+ Requires installation of the [Typst compiler](https://github.com/typst/typst/releases) and the Python packages `pyyaml`, `pikepdf`, and `pillow`.
117
+ ```bash
118
+ pip install pyyaml pikepdf pillow
119
+ ```
@@ -0,0 +1,104 @@
1
+ # YAL
2
+
3
+ **YAL** is a command-line utility for project initialization and updates based on templates.
4
+
5
+ ## Commands
6
+
7
+ - `yal create <kind>[:<name>][@<ref>]` — uses a template to create a new project (currently supports only `book` kind)
8
+ - `yal update <kind>[:<name>]` — updates an existing project (currently supports only `book` kind)
9
+
10
+ ## Install utility
11
+
12
+ ```bash
13
+ pip install yal
14
+ ```
15
+
16
+ ## Create a project
17
+ Downloads the template (if not cached locally) and initiates the configuration process.
18
+ ```bash
19
+ # Basic usage
20
+ yal create book
21
+
22
+ # Using a specific template
23
+ yal create book:template-name
24
+
25
+ # Using a specific version (tag or commit hash)
26
+ yal create book@version
27
+
28
+ # Combined usage
29
+ yal create book:template-name@version
30
+ ```
31
+
32
+ ## yal.toml
33
+
34
+ A configuration file used as a prompt when creating a project from a template. The `yal.toml` file is located in the root of the template and may have a structure like the following:
35
+
36
+ ```toml
37
+ [meta]
38
+ yal-min-version = "0.1.0"
39
+
40
+ [[fields]]
41
+ id = "book-name"
42
+ type = "text"
43
+ required = true
44
+ default = "{placeholder}"
45
+ is-folder-name = true
46
+
47
+ [[fields]]
48
+ id = "book-author"
49
+ type = "text"
50
+ required = false
51
+ default = ""
52
+
53
+ [[fields]]
54
+ id = "book-lang"
55
+ type = "select"
56
+ options = ["ru", "en", "de"]
57
+ default = "ru"
58
+
59
+ [messages.ru]
60
+ book-name.prompt = "Введите название книги"
61
+ book-name.placeholder = "Книга без названия"
62
+ book-author.prompt = "Введите имя автора"
63
+ book-author.placeholder = "Аноним"
64
+ book-lang.prompt = "Выберите язык книги в формате ISO-639"
65
+
66
+ [messages.en]
67
+ book-name.prompt = "Enter book title"
68
+ book-name.placeholder = "Untitled book"
69
+ book-author.prompt = "Enter author name"
70
+ book-author.placeholder = "Anonymous"
71
+ book-lang.prompt = "Select book language in ISO-639 format"
72
+
73
+ [[targets]]
74
+ file = "/meta/book.yml"
75
+ format = "yaml"
76
+
77
+ [[targets.fields]]
78
+ field = "book-name"
79
+ key = "book.title"
80
+
81
+ [[targets.fields]]
82
+ field = "book-author"
83
+ key = "author[0].name"
84
+
85
+ [[targets.fields]]
86
+ field = "book-lang"
87
+ key = "property.language[ISO-639]"
88
+ ```
89
+
90
+
91
+ ## Built-in templates
92
+
93
+
94
+ ### \<Book\> Typst Book Template
95
+
96
+ - **Repo:** https://github.com/DemerNkardaz/Typst-Book-Template
97
+ - **Name:** default
98
+
99
+ A template for creating a book using [Typst](https://typst.app/), including a prebuilt structure, plugins, styles, settings, and a build system that combines Typst and Python tools.
100
+
101
+ Requires installation of the [Typst compiler](https://github.com/typst/typst/releases) and the Python packages `pyyaml`, `pikepdf`, and `pillow`.
102
+ ```bash
103
+ pip install pyyaml pikepdf pillow
104
+ ```
@@ -0,0 +1,26 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "yal-cmd"
7
+ version = "0.1.0"
8
+ authors = [
9
+ { name = "Yalla Nkardaz", email = "dev_nkardaz@proton.me" }
10
+ ]
11
+ readme = "README.md"
12
+ description = "Yalla Nkardaz’s personal CLI toolkit"
13
+ requires-python = ">=3.10"
14
+ license = "MIT"
15
+ dependencies = [
16
+ "requests",
17
+ "pyyaml",
18
+ "ruamel.yaml",
19
+ "tomli; python_version < '3.11'",
20
+ ]
21
+
22
+ [project.scripts]
23
+ yal = "yal.cli:main"
24
+
25
+ [tool.setuptools.packages.find]
26
+ where = ["."]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
File without changes
@@ -0,0 +1,60 @@
1
+ """yal — точка входа CLI."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import sys
7
+
8
+ from yal.commands import create as cmd_create
9
+ from yal.commands import update as cmd_update
10
+
11
+
12
+ def build_parser() -> argparse.ArgumentParser:
13
+ parser = argparse.ArgumentParser(
14
+ prog="yal",
15
+ description="Yalla Nkardaz’s personal CLI toolkit",
16
+ )
17
+ sub = parser.add_subparsers(dest="command", metavar="<command>")
18
+
19
+ # ── create ───────────────────────────────────────────────────────────────
20
+ p_create = sub.add_parser("create", help="Create project from template")
21
+ p_create.add_argument(
22
+ "what",
23
+ help=(
24
+ "What to create. Format: <kind>[:<name>][@<ref>]\n"
25
+ "Example: book | book@1.7.1 | book@c651f7d | book:default@latest"
26
+ ),
27
+ )
28
+ p_create.add_argument(
29
+ "-o", "--output",
30
+ default=".",
31
+ metavar="DIR",
32
+ help="Where to save the result (default: current directory)",
33
+ )
34
+
35
+ # ── update ───────────────────────────────────────────────────────────────
36
+ p_update = sub.add_parser("update", help="Update template")
37
+ p_update.add_argument(
38
+ "what",
39
+ help=(
40
+ "What to update. Format: <kind>[:<name>]\n"
41
+ "Example: book | book:default"
42
+ ),
43
+ )
44
+
45
+ return parser
46
+
47
+
48
+ def main() -> None:
49
+ parser = build_parser()
50
+ args = parser.parse_args()
51
+
52
+ if args.command is None:
53
+ parser.print_help()
54
+ sys.exit(0)
55
+
56
+ if args.command == "create":
57
+ cmd_create.run(args)
58
+
59
+ if args.command == "update":
60
+ cmd_update.run(args)
@@ -0,0 +1,92 @@
1
+ """
2
+ Команда: yal create <kind>[:<name>][@<ref>]
3
+
4
+ Примеры:
5
+ yal create book
6
+ yal create book@latest
7
+ yal create book@1.7.1
8
+ yal create book@c651f7d
9
+ yal create book:default@1.7.1
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import argparse
15
+ import re
16
+ import sys
17
+ from pathlib import Path
18
+
19
+ from yal.i18n import t
20
+ from yal.templates.registry import get_entry, list_kinds
21
+ from yal.templates.book_handler import BookHandler
22
+ from yal import store, template_config
23
+
24
+ _HANDLERS = {
25
+ "book": BookHandler(),
26
+ }
27
+
28
+
29
+ def run(args: argparse.Namespace) -> None:
30
+ kind, name, ref = _parse_spec(args.what)
31
+
32
+ handler = _HANDLERS.get(kind)
33
+ if handler is None:
34
+ print(f"[yal] {t('errors.unknown-kind', kind=kind, available=', '.join(list_kinds()))}")
35
+ sys.exit(1)
36
+
37
+ try:
38
+ entry = get_entry(kind, name)
39
+ except ValueError as e:
40
+ print(f"[yal] {e}")
41
+ sys.exit(1)
42
+
43
+ output_dir = Path(args.output).resolve()
44
+
45
+ # 1. Получаем версию и загружаем конфигурацию до создания папок
46
+ version = handler._resolve_version(entry, name, ref)
47
+ src_dir = store.template_dir(kind, version)
48
+ config = template_config.load(src_dir)
49
+
50
+ values = {}
51
+ folder_name = None
52
+
53
+ # 2. Собираем значения и определяем имя папки
54
+ if config is not None:
55
+ values = template_config.collect(config)
56
+ folder_name = template_config.get_folder_name(config, values)
57
+
58
+ try:
59
+ # 3. Создаем структуру проекта с учетом имени
60
+ result = handler.create(
61
+ entry=entry,
62
+ name=name,
63
+ output_dir=output_dir,
64
+ ref=ref,
65
+ custom_folder_name=folder_name
66
+ )
67
+
68
+ # 4. Применяем настройки (запись значений в файлы)
69
+ if config is not None:
70
+ template_config.apply(config, values, result.dest)
71
+ else:
72
+ print(f"[yal] {t('config.no-config')}")
73
+
74
+ print(f"[yal] {t('create.created', path=result.dest)}")
75
+
76
+ except (ValueError, RuntimeError) as e:
77
+ print(f"[yal] {t('create.error', error=e)}")
78
+ sys.exit(1)
79
+
80
+
81
+ def _parse_spec(spec: str) -> tuple[str, str, str | None]:
82
+ pattern = r"^(?P<kind>[^:@]+)(?::(?P<name>[^@]+))?(?:@(?P<ref>.+))?$"
83
+ m = re.match(pattern, spec.strip())
84
+ if not m:
85
+ print(f"[yal] {t('errors.parse-spec', spec=spec)}")
86
+ print(f" {t('errors.parse-spec-hint', fmt='<kind>[:<name>][@<ref>]')}")
87
+ sys.exit(1)
88
+
89
+ kind = m.group("kind").lower()
90
+ name = (m.group("name") or "default").lower()
91
+ ref = m.group("ref") or None
92
+ return kind, name, ref
@@ -0,0 +1,119 @@
1
+ """
2
+ Команда: yal update <kind>[:<name>]
3
+
4
+ Примеры:
5
+ yal update book
6
+ yal update book:default
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import argparse
12
+ import re
13
+ import sys
14
+
15
+ from yal import github, store
16
+ from yal.i18n import t
17
+ from yal.templates.registry import get_entry, list_kinds
18
+ from yal.templates.book_handler import BookHandler, _confirm_download, _fetch_releases_safe
19
+
20
+ _HANDLERS = {
21
+ "book": BookHandler(),
22
+ }
23
+
24
+
25
+ def run(args: argparse.Namespace) -> None:
26
+ kind, name = _parse_spec(args.what)
27
+
28
+ if kind not in _HANDLERS:
29
+ print(f"[yal] {t('errors.unknown-kind', kind=kind, available=', '.join(list_kinds()))}")
30
+ sys.exit(1)
31
+
32
+ try:
33
+ entry = get_entry(kind, name)
34
+ except ValueError as e:
35
+ print(f"[yal] {e}")
36
+ sys.exit(1)
37
+
38
+ try:
39
+ _update(kind, name, entry)
40
+ except (ValueError, RuntimeError) as e:
41
+ print(f"[yal] {t('create.error', error=e)}")
42
+ sys.exit(1)
43
+
44
+
45
+ def _update(kind: str, name: str, entry) -> None:
46
+ releases = _fetch_releases_safe(entry.repo)
47
+ if releases:
48
+ _update_release(kind, name, entry, releases)
49
+ else:
50
+ _update_commit(kind, name, entry)
51
+
52
+
53
+ def _update_release(
54
+ kind: str,
55
+ name: str,
56
+ entry,
57
+ releases: list[github.ReleaseInfo],
58
+ ) -> None:
59
+ latest = releases[0]
60
+ version = latest.tag.lstrip("vV")
61
+
62
+ if store.is_installed(kind, version):
63
+ print(f"[yal] {t('update.already-latest', version=version)}")
64
+ return
65
+
66
+ local = store.latest_release_version(kind)
67
+ if local:
68
+ print(f"[yal] {t('update.upgrading', kind=kind, name=name, old=local, new=version)}")
69
+ else:
70
+ print(f"[yal] {t('update.installing', kind=kind, name=name, version=version)}")
71
+
72
+ if not _confirm_download(kind, name, version, t("download.release"), entry.repo):
73
+ raise RuntimeError(t("errors.cancelled", action=t("update.action")))
74
+
75
+ dest = store.template_dir(kind, version)
76
+ print(f"[yal] {t('download.release-downloading', tag=latest.tag)}")
77
+ github.download_release(latest, dest)
78
+ store.save_meta(kind, version, "release", entry.repo)
79
+ print(f"[yal] {t('update.installed', path=dest)}")
80
+
81
+
82
+ def _update_commit(kind: str, name: str, entry) -> None:
83
+ try:
84
+ info = github.get_latest_commit(entry.repo)
85
+ except Exception as e:
86
+ raise RuntimeError(t("update.commit-fail", error=e)) from e
87
+
88
+ version = info.sha7
89
+
90
+ if store.is_installed(kind, version):
91
+ print(f"[yal] {t('update.already-latest-commit', version=version)}")
92
+ return
93
+
94
+ local_versions = store.installed_versions(kind)
95
+ if local_versions:
96
+ print(f"[yal] {t('update.upgrading-commit', kind=kind, name=name, old=local_versions[-1], new=version)}")
97
+ else:
98
+ print(f"[yal] {t('update.installing-commit', kind=kind, name=name, version=version)}")
99
+
100
+ if not _confirm_download(kind, name, version, t("download.commit"), entry.repo):
101
+ raise RuntimeError(t("errors.cancelled", action=t("update.action")))
102
+
103
+ dest = store.template_dir(kind, version)
104
+ print(f"[yal] {t('download.commit-cloning', version=version)}")
105
+ github.clone_repo(entry.repo, dest, ref=info.sha)
106
+ store.save_meta(kind, version, "commit", entry.repo)
107
+ print(f"[yal] {t('update.installed', path=dest)}")
108
+
109
+
110
+ def _parse_spec(spec: str) -> tuple[str, str]:
111
+ m = re.match(r"^(?P<kind>[^:@]+)(?::(?P<name>[^@]+))?$", spec.strip())
112
+ if not m:
113
+ print(f"[yal] {t('errors.parse-spec', spec=spec)}")
114
+ print(f" {t('errors.parse-spec-hint', fmt='<kind>[:<name>]')}")
115
+ sys.exit(1)
116
+
117
+ kind = m.group("kind").lower()
118
+ name = (m.group("name") or "default").lower()
119
+ return kind, name