qm-template 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.
Files changed (54) hide show
  1. qm_template-0.1.0/.copier-answers.yml +23 -0
  2. qm_template-0.1.0/.dockerignore +8 -0
  3. qm_template-0.1.0/.editorconfig +16 -0
  4. qm_template-0.1.0/.github/dependabot.yml +15 -0
  5. qm_template-0.1.0/.github/workflows/ci.yaml +57 -0
  6. qm_template-0.1.0/.github/workflows/docs.yaml +61 -0
  7. qm_template-0.1.0/.github/workflows/publish-to-pypi.yaml +46 -0
  8. qm_template-0.1.0/.gitignore +354 -0
  9. qm_template-0.1.0/.pre-commit-config.yaml +41 -0
  10. qm_template-0.1.0/AGENTS.md +53 -0
  11. qm_template-0.1.0/CHANGELOG.md +41 -0
  12. qm_template-0.1.0/Dockerfile +11 -0
  13. qm_template-0.1.0/LICENSE +18 -0
  14. qm_template-0.1.0/PKG-INFO +222 -0
  15. qm_template-0.1.0/README.md +202 -0
  16. qm_template-0.1.0/docs/getting-started.md +107 -0
  17. qm_template-0.1.0/docs/getting-started.zh.md +101 -0
  18. qm_template-0.1.0/docs/index.md +27 -0
  19. qm_template-0.1.0/docs/index.zh.md +23 -0
  20. qm_template-0.1.0/justfile +49 -0
  21. qm_template-0.1.0/mkdocs.yaml +54 -0
  22. qm_template-0.1.0/pyproject.toml +60 -0
  23. qm_template-0.1.0/qm-template.example.toml +1 -0
  24. qm_template-0.1.0/ruff.toml +78 -0
  25. qm_template-0.1.0/src/qm_template/__init__.py +10 -0
  26. qm_template-0.1.0/src/qm_template/__main__.py +6 -0
  27. qm_template-0.1.0/src/qm_template/checksum.py +65 -0
  28. qm_template-0.1.0/src/qm_template/cli.py +64 -0
  29. qm_template-0.1.0/src/qm_template/commands.py +204 -0
  30. qm_template-0.1.0/src/qm_template/config.default.toml +90 -0
  31. qm_template-0.1.0/src/qm_template/config.py +239 -0
  32. qm_template-0.1.0/src/qm_template/distros/__init__.py +24 -0
  33. qm_template-0.1.0/src/qm_template/distros/alpine.py +31 -0
  34. qm_template-0.1.0/src/qm_template/distros/archlinux.py +28 -0
  35. qm_template-0.1.0/src/qm_template/distros/base.py +65 -0
  36. qm_template-0.1.0/src/qm_template/distros/debian.py +47 -0
  37. qm_template-0.1.0/src/qm_template/distros/opensuse.py +39 -0
  38. qm_template-0.1.0/src/qm_template/distros/redhat.py +132 -0
  39. qm_template-0.1.0/src/qm_template/distros/ubuntu.py +42 -0
  40. qm_template-0.1.0/src/qm_template/download.py +153 -0
  41. qm_template-0.1.0/src/qm_template/errors.py +6 -0
  42. qm_template-0.1.0/src/qm_template/http.py +49 -0
  43. qm_template-0.1.0/src/qm_template/log.py +13 -0
  44. qm_template-0.1.0/src/qm_template/pve.py +209 -0
  45. qm_template-0.1.0/src/qm_template/shell.py +17 -0
  46. qm_template-0.1.0/tests/test_checksum.py +78 -0
  47. qm_template-0.1.0/tests/test_cli.py +89 -0
  48. qm_template-0.1.0/tests/test_config.py +153 -0
  49. qm_template-0.1.0/tests/test_distros.py +190 -0
  50. qm_template-0.1.0/tests/test_download.py +62 -0
  51. qm_template-0.1.0/tests/test_http.py +22 -0
  52. qm_template-0.1.0/tests/test_pve.py +120 -0
  53. qm_template-0.1.0/tests/test_shell.py +25 -0
  54. qm_template-0.1.0/uv.lock +1080 -0
@@ -0,0 +1,23 @@
1
+ # Changes here will be overwritten by Copier; NEVER EDIT MANUALLY
2
+ _commit: v0.1.0-7-gb2e1a57
3
+ _src_path: .
4
+ author_email: git@ak1ra.xyz
5
+ author_name: ak1ra
6
+ copyright_date: '2026'
7
+ copyright_holder: ak1ra
8
+ copyright_license: MIT
9
+ default_branch: master
10
+ docs_hosting: github-pages
11
+ docs_site_url: https://ak1ra-lab.github.io/qm-template/
12
+ project_description: Proxmox VE template helper scripts
13
+ project_kind: cli
14
+ project_name: qm-template
15
+ python_min_version: '3.11'
16
+ python_package_command_line_name: qm-template
17
+ python_package_distribution_name: qm-template
18
+ python_package_import_name: qm_template
19
+ repository_name: qm-template
20
+ repository_namespace: ak1ra-lab
21
+ repository_provider_kind: github
22
+ requires_python: '>=3.11'
23
+ with_ansible: false
@@ -0,0 +1,8 @@
1
+ .git
2
+ .venv
3
+ .pytest_cache
4
+ .ruff_cache
5
+ __pycache__
6
+ dist
7
+ site
8
+ htmlcov
@@ -0,0 +1,16 @@
1
+ root = true
2
+
3
+ [*]
4
+ charset = utf-8
5
+ end_of_line = lf
6
+ indent_size = 4
7
+ indent_style = space
8
+ insert_final_newline = true
9
+ trim_trailing_whitespace = true
10
+
11
+ [*.{yml,yaml}]
12
+ indent_size = 2
13
+
14
+ [*.md]
15
+ indent_size = 2
16
+ trim_trailing_whitespace = false
@@ -0,0 +1,15 @@
1
+ ---
2
+ version: 2
3
+ updates:
4
+ - package-ecosystem: github-actions
5
+ directory: /
6
+ schedule:
7
+ interval: weekly
8
+ cooldown:
9
+ default-days: 7
10
+ - package-ecosystem: uv
11
+ directory: /
12
+ schedule:
13
+ interval: weekly
14
+ cooldown:
15
+ default-days: 7
@@ -0,0 +1,57 @@
1
+ name: ci
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - master
7
+ pull_request:
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ concurrency:
13
+ group: ci-${{ github.ref }}
14
+ cancel-in-progress: true
15
+
16
+ jobs:
17
+ checks:
18
+ runs-on: ubuntu-latest
19
+ steps:
20
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
21
+ with:
22
+ persist-credentials: false
23
+
24
+ - name: Install uv
25
+ uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
26
+
27
+ - name: Install the project
28
+ run: uv sync --locked --group dev
29
+
30
+ - name: Run pre-commit
31
+ run: uv run pre-commit run --all-files
32
+
33
+ tests:
34
+ runs-on: ubuntu-latest
35
+ strategy:
36
+ fail-fast: false
37
+ matrix:
38
+ python-version:
39
+ - "3.11"
40
+ - "3.12"
41
+ - "3.13"
42
+ - "3.14"
43
+ steps:
44
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
45
+ with:
46
+ persist-credentials: false
47
+
48
+ - name: Install uv
49
+ uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
50
+ with:
51
+ python-version: ${{ matrix.python-version }}
52
+
53
+ - name: Install the project
54
+ run: uv sync --locked --group dev
55
+
56
+ - name: Run tests
57
+ run: uv run pytest -v tests/
@@ -0,0 +1,61 @@
1
+ ---
2
+ name: docs
3
+
4
+ on:
5
+ workflow_dispatch: ~
6
+ push:
7
+ branches:
8
+ - master
9
+ paths:
10
+ - "src/**"
11
+ - "docs/**"
12
+ - "README.md"
13
+ - "mkdocs.yaml"
14
+ - "pyproject.toml"
15
+ - "uv.lock"
16
+ - ".github/workflows/docs.yaml"
17
+
18
+ permissions:
19
+ contents: read
20
+
21
+ concurrency:
22
+ group: docs
23
+ cancel-in-progress: false
24
+
25
+ jobs:
26
+ build:
27
+ runs-on: ubuntu-latest
28
+ steps:
29
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
30
+ with:
31
+ fetch-depth: 0
32
+ fetch-tags: true
33
+ persist-credentials: false
34
+
35
+ - name: Install uv
36
+ uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
37
+
38
+ - name: Install the project
39
+ run: uv sync --locked --group docs
40
+
41
+ - name: Build documentation
42
+ run: NO_MKDOCS_2_WARNING=1 uv run mkdocs build
43
+
44
+ - name: Upload artifact
45
+ uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
46
+ with:
47
+ path: site
48
+
49
+ deploy:
50
+ needs: build
51
+ runs-on: ubuntu-latest
52
+ permissions:
53
+ pages: write
54
+ id-token: write
55
+ environment:
56
+ name: github-pages
57
+ url: ${{ steps.deployment.outputs.page_url }}
58
+ steps:
59
+ - name: Deploy to GitHub Pages
60
+ id: deployment
61
+ uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5.0.1
@@ -0,0 +1,46 @@
1
+ ---
2
+ name: publish-to-pypi
3
+
4
+ on:
5
+ workflow_dispatch: ~
6
+ push:
7
+ tags:
8
+ - "v*"
9
+
10
+ permissions: {}
11
+
12
+ jobs:
13
+ build-n-push:
14
+ name: upload release to PyPI
15
+ runs-on: ubuntu-latest
16
+ environment:
17
+ name: pypi
18
+ url: https://pypi.org/project/qm-template/
19
+
20
+ permissions:
21
+ contents: read
22
+ id-token: write
23
+
24
+ steps:
25
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
26
+ with:
27
+ fetch-depth: 0
28
+ fetch-tags: true
29
+ persist-credentials: false
30
+
31
+ - name: Install uv
32
+ uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
33
+ with:
34
+ enable-cache: false
35
+
36
+ - name: Install the project
37
+ run: uv sync --locked --group dev
38
+
39
+ - name: Run tests
40
+ run: uv run pytest -v tests/
41
+
42
+ - name: Build a binary wheel and a source tarball
43
+ run: uv build -v
44
+
45
+ - name: Publish package distributions to PyPI
46
+ uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
@@ -0,0 +1,354 @@
1
+ # https://github.com/github/gitignore/blob/main/Global/Archives.gitignore
2
+ # It's better to unpack these files and commit the raw source because
3
+ # git has its own built in compression methods.
4
+ *.7z
5
+ *.jar
6
+ *.rar
7
+ *.zip
8
+ *.gz
9
+ *.gzip
10
+ *.tgz
11
+ *.bzip
12
+ *.bzip2
13
+ *.bz2
14
+ *.xz
15
+ *.lzma
16
+ *.cab
17
+ *.xar
18
+ *.zst
19
+ *.tzst
20
+
21
+ # Packing-only formats
22
+ *.iso
23
+ *.tar
24
+
25
+ # Package management formats
26
+ *.dmg
27
+ *.xpi
28
+ *.gem
29
+ *.egg
30
+ *.deb
31
+ *.rpm
32
+ *.msi
33
+ *.msm
34
+ *.msp
35
+ *.txz
36
+
37
+ # https://github.com/github/gitignore/blob/main/Global/Linux.gitignore
38
+ *~
39
+
40
+ # temporary files which can be created if a process still has a handle open of a deleted file
41
+ .fuse_hidden*
42
+
43
+ # Metadata left by Dolphin file manager, which comes with KDE Plasma
44
+ .directory
45
+
46
+ # Linux trash folder which might appear on any partition or disk
47
+ .Trash-*
48
+
49
+ # .nfs files are created when an open file is removed but is still being accessed
50
+ .nfs*
51
+
52
+ # Log files created by default by the nohup command
53
+ nohup.out
54
+
55
+ # https://github.com/github/gitignore/blob/main/Global/macOS.gitignore
56
+ # General
57
+ .DS_Store
58
+ __MACOSX/
59
+ .AppleDouble
60
+ .LSOverride
61
+ Icon\[
62
+ ]
63
+
64
+ # Thumbnails
65
+ ._*
66
+
67
+ # Files that might appear in the root of a volume
68
+ .DocumentRevisions-V100
69
+ .fseventsd
70
+ .Spotlight-V100
71
+ .TemporaryItems
72
+ .Trashes
73
+ .VolumeIcon.icns
74
+ .com.apple.timemachine.donotpresent
75
+
76
+ # Directories potentially created on remote AFP share
77
+ .AppleDB
78
+ .AppleDesktop
79
+ Network Trash Folder
80
+ Temporary Items
81
+ .apdisk
82
+
83
+ # https://github.com/github/gitignore/blob/main/Global/Windows.gitignore
84
+ # Windows thumbnail cache files
85
+ Thumbs.db
86
+ Thumbs.db:encryptable
87
+ ehthumbs.db
88
+ ehthumbs_vista.db
89
+
90
+ # Dump file
91
+ *.stackdump
92
+
93
+ # Folder config file
94
+ [Dd]esktop.ini
95
+
96
+ # Recycle Bin used on file shares
97
+ $RECYCLE.BIN/
98
+
99
+ # Windows Installer files
100
+ *.cab
101
+ *.msi
102
+ *.msix
103
+ *.msm
104
+ *.msp
105
+ *.exe
106
+
107
+ # Windows shortcuts
108
+ *.lnk
109
+
110
+ # https://github.com/github/gitignore/blob/main/Python.gitignore
111
+ # Byte-compiled / optimized / DLL files
112
+ __pycache__/
113
+ *.py[codz]
114
+ *$py.class
115
+
116
+ # C extensions
117
+ *.so
118
+
119
+ # Distribution / packaging
120
+ .Python
121
+ build/
122
+ develop-eggs/
123
+ dist/
124
+ downloads/
125
+ eggs/
126
+ .eggs/
127
+ lib/
128
+ lib64/
129
+ parts/
130
+ sdist/
131
+ var/
132
+ wheels/
133
+ share/python-wheels/
134
+ *.egg-info/
135
+ .installed.cfg
136
+ *.egg
137
+ MANIFEST
138
+
139
+ # PyInstaller
140
+ # Usually these files are written by a python script from a template
141
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
142
+ *.manifest
143
+ *.spec
144
+
145
+ # Installer logs
146
+ pip-log.txt
147
+ pip-delete-this-directory.txt
148
+
149
+ # Unit test / coverage reports
150
+ htmlcov/
151
+ .tox/
152
+ .nox/
153
+ .coverage
154
+ .coverage.*
155
+ .cache
156
+ nosetests.xml
157
+ coverage.xml
158
+ *.cover
159
+ *.py.cover
160
+ .hypothesis/
161
+ .pytest_cache/
162
+ cover/
163
+
164
+ # Translations
165
+ *.mo
166
+ *.pot
167
+
168
+ # Django stuff:
169
+ *.log
170
+ local_settings.py
171
+ db.sqlite3
172
+ db.sqlite3-journal
173
+
174
+ # Flask stuff:
175
+ instance/
176
+ .webassets-cache
177
+
178
+ # Scrapy stuff:
179
+ .scrapy
180
+
181
+ # Sphinx documentation
182
+ docs/_build/
183
+
184
+ # PyBuilder
185
+ .pybuilder/
186
+ target/
187
+
188
+ # Jupyter Notebook
189
+ .ipynb_checkpoints
190
+
191
+ # IPython
192
+ profile_default/
193
+ ipython_config.py
194
+
195
+ # pyenv
196
+ # For a library or package, you might want to ignore these files since the code is
197
+ # intended to run in multiple environments; otherwise, check them in:
198
+ .python-version
199
+
200
+ # pipenv
201
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
202
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
203
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
204
+ # install all needed dependencies.
205
+ # Pipfile.lock
206
+
207
+ # UV
208
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
209
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
210
+ # commonly ignored for libraries.
211
+ # uv.lock
212
+
213
+ # poetry
214
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
215
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
216
+ # commonly ignored for libraries.
217
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
218
+ # poetry.lock
219
+ # poetry.toml
220
+
221
+ # pdm
222
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
223
+ # pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
224
+ # https://pdm-project.org/en/latest/usage/project/#working-with-version-control
225
+ # pdm.lock
226
+ # pdm.toml
227
+ .pdm-python
228
+ .pdm-build/
229
+
230
+ # pixi
231
+ # Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
232
+ # pixi.lock
233
+ # Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
234
+ # in the .venv directory. It is recommended not to include this directory in version control.
235
+ .pixi
236
+
237
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
238
+ __pypackages__/
239
+
240
+ # Celery stuff
241
+ celerybeat-schedule
242
+ celerybeat.pid
243
+
244
+ # Redis
245
+ *.rdb
246
+ *.aof
247
+ *.pid
248
+
249
+ # RabbitMQ
250
+ mnesia/
251
+ rabbitmq/
252
+ rabbitmq-data/
253
+
254
+ # ActiveMQ
255
+ activemq-data/
256
+
257
+ # SageMath parsed files
258
+ *.sage.py
259
+
260
+ # Environments
261
+ .env
262
+ .envrc
263
+ .venv
264
+ env/
265
+ venv/
266
+ ENV/
267
+ env.bak/
268
+ venv.bak/
269
+
270
+ # Spyder project settings
271
+ .spyderproject
272
+ .spyproject
273
+
274
+ # Rope project settings
275
+ .ropeproject
276
+
277
+ # mkdocs documentation
278
+ /site
279
+
280
+ # mypy
281
+ .mypy_cache/
282
+ .dmypy.json
283
+ dmypy.json
284
+
285
+ # Pyre type checker
286
+ .pyre/
287
+
288
+ # pytype static type analyzer
289
+ .pytype/
290
+
291
+ # Cython debug symbols
292
+ cython_debug/
293
+
294
+ # PyCharm
295
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
296
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
297
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
298
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
299
+ # .idea/
300
+
301
+ # Abstra
302
+ # Abstra is an AI-powered process automation framework.
303
+ # Ignore directories containing user credentials, local state, and settings.
304
+ # Learn more at https://abstra.io/docs
305
+ .abstra/
306
+
307
+ # Visual Studio Code
308
+ # Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
309
+ # that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
310
+ # and can be added to the global gitignore or merged into this file. However, if you prefer,
311
+ # you could uncomment the following to ignore the entire vscode folder
312
+ .vscode/
313
+
314
+ # Ruff stuff:
315
+ .ruff_cache/
316
+
317
+ # PyPI configuration file
318
+ .pypirc
319
+
320
+ # Marimo
321
+ marimo/_static/
322
+ marimo/_lsp/
323
+ __marimo__/
324
+
325
+ # Streamlit
326
+ .streamlit/secrets.toml
327
+
328
+ # Ansible
329
+ .ansible/*
330
+ inventory/*.yaml
331
+ playbooks/host_vars/**/*.yaml
332
+ playbooks/group_vars/**/*.yaml
333
+ playbooks/host_vars/**/*.patch
334
+ playbooks/group_vars/**/*.patch
335
+
336
+ # Ansible runtime scripts and templates
337
+ playbooks/scripts/*
338
+ playbooks/templates/*
339
+
340
+ # 示例 Ansible inventory
341
+ !inventory/hosts.example.yaml
342
+
343
+ # General configuration file and examples folder
344
+ config/*
345
+ credentials/
346
+ credentials.yaml
347
+ examples/*
348
+
349
+ ## LLM
350
+ .agents/sessions/*
351
+
352
+ # Local qm-template configuration (keep the example file)
353
+ qm-template.*.toml
354
+ !qm-template.example.toml
@@ -0,0 +1,41 @@
1
+ ---
2
+ # See https://pre-commit.com for more information
3
+ # See https://pre-commit.com/hooks.html for more hooks
4
+ #
5
+ # Run `uv run pre-commit autoupdate` after init to bump hook revisions.
6
+ repos:
7
+ - repo: https://github.com/pre-commit/pre-commit-hooks
8
+ rev: v6.0.0
9
+ hooks:
10
+ - id: check-added-large-files
11
+ - id: check-json
12
+ - id: check-merge-conflict
13
+ - id: check-toml
14
+ - id: check-yaml
15
+ - id: detect-private-key
16
+ - id: end-of-file-fixer
17
+ - id: mixed-line-ending
18
+ args: [--fix=lf]
19
+ - id: trailing-whitespace
20
+
21
+ - repo: https://github.com/astral-sh/ruff-pre-commit
22
+ rev: v0.16.7
23
+ hooks:
24
+ - id: ruff-check
25
+ args: [--fix]
26
+ - id: ruff-format
27
+
28
+ - repo: https://github.com/astral-sh/uv-pre-commit
29
+ rev: 0.12.13
30
+ hooks:
31
+ - id: uv-lock
32
+
33
+ - repo: local
34
+ hooks:
35
+ - id: ty
36
+ name: ty
37
+ entry: uv run ty check
38
+ language: system
39
+ files: ^src/
40
+ types_or: [python, pyi]
41
+ require_serial: true
@@ -0,0 +1,53 @@
1
+ # AGENTS.md
2
+
3
+ `qm-template` is a Python CLI that downloads distro cloud images and creates
4
+ Proxmox VE VM templates. It targets a Proxmox VE host and is normally run as
5
+ root. Application code lives under `src/qm_template`.
6
+
7
+ `download` shells out to `axel`/`aria2c`/`wget`/`curl`; `create` shells out to
8
+ Proxmox VE (`qm`, `pvesm`). Neither must be exercised in tests - tests mock or
9
+ only cover pure logic.
10
+
11
+ ## Tooling
12
+
13
+ `just <recipe>` is a thin wrapper around `uv run ...` (see `justfile`); calling
14
+ `uv run ...` directly is equivalent.
15
+
16
+ - Env/deps: **uv** (`just sync` = `uv sync --group dev`). Never
17
+ `pip`/`poetry`/`pipenv`.
18
+ - Lint+format: **ruff** (`just lint` = `uv run ruff check --fix` then
19
+ `uv run ruff format`, over `src/ tests/`). Never `black`/`isort`/`flake8`.
20
+ - Typecheck: **ty**, `src/` only (`just typecheck` = `uv run ty check src/`).
21
+ Never `mypy`.
22
+ - Tests: **pytest** (`just test` = `uv run pytest -v tests/`). Extra args go
23
+ *before* `tests/`, e.g. `just test -k <pattern>`.
24
+ - Docs: `just docs-build` and `just docs-serve` (mkdocs-material).
25
+ - Build: `just build` = `uv build`.
26
+ - Pre-commit: `uv run pre-commit run --all-files` is the final gate before
27
+ committing.
28
+
29
+ Python changes MUST pass ruff, ty, and pytest, then pre-commit.
30
+
31
+ ## Versioning
32
+
33
+ Version is dynamic via `hatch-vcs` (Git tags). DO NOT bump version strings
34
+ manually; push a tag starting with `v` (e.g. `v0.1.0`) and CI publishes to
35
+ PyPI.
36
+
37
+ ## Conventions
38
+
39
+ - CLI: `argparse` with `download`, `create` and `distros` subcommands;
40
+ entrypoint `qm-template`. Runtime dependencies stay empty (standard library
41
+ only).
42
+ - Defaults: config `/etc/qm-template/config.toml`, images
43
+ `/var/lib/qm-template`, mirroring the upstream layout
44
+ (`<distro>/<release>/[<tag>/]<filename>`). Dated builds are pinned where the
45
+ upstream provides them.
46
+ - Tests live under `tests/` and track public behavior, especially the CLI.
47
+ - `pyproject.toml` is the single source of truth for dependencies and metadata.
48
+
49
+ ## Docs (bilingual)
50
+
51
+ `docs/` is served by mkdocs-static-i18n (`docs_structure: suffix`): every page
52
+ has an English `*.md` and a Simplified-Chinese `*.zh.md` sibling. Update BOTH
53
+ language files when changing user-facing behavior.