mkit-cli 0.4.0__tar.gz → 0.5.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.
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/CHANGELOG.md +9 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/PKG-INFO +1 -1
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/docs/commands.md +1 -1
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/docs/config.md +18 -5
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/src/mkit/__init__.py +1 -1
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/src/mkit/commands/guide.py +2 -1
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/src/mkit/guides.py +42 -8
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/tests/test_guides.py +31 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/.gitattributes +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/.github/workflows/publish.yml +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/.github/workflows/tests.yml +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/.gitignore +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/AGENTS.md +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/CONTRIBUTING.md +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/LICENSE +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/README.md +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/SECURITY.md +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/docs/about-screen.md +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/docs/app-ui.md +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/docs/aso.md +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/docs/in-app-review.md +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/docs/in-app-update.md +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/docs/news-topics.md +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/docs/store-art.md +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/docs/store.md +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/docs/support-replies.md +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/pyproject.toml +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/scripts/gen_docs.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/scripts/release.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/src/mkit/art.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/src/mkit/aso.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/src/mkit/cli.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/src/mkit/cloud/__init__.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/src/mkit/cloud/auth.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/src/mkit/cloud/fbproject.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/src/mkit/cloud/firebase.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/src/mkit/cloud/insight.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/src/mkit/cloud/money.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/src/mkit/cloud/play.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/src/mkit/cloud/store.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/src/mkit/commands/__init__.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/src/mkit/commands/art.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/src/mkit/commands/aso.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/src/mkit/commands/dev.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/src/mkit/commands/doctor.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/src/mkit/commands/firebase_cmds.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/src/mkit/commands/init.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/src/mkit/commands/insight.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/src/mkit/commands/meta.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/src/mkit/commands/money.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/src/mkit/commands/release.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/src/mkit/commands/selfcmd.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/src/mkit/commands/store.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/src/mkit/config.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/src/mkit/data/art/art.toml +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/src/mkit/data/art/en.json +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/src/mkit/data/datasafety-questions.csv +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/src/mkit/datasafety.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/src/mkit/device.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/src/mkit/docgen.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/src/mkit/errors.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/src/mkit/git.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/src/mkit/hooks.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/src/mkit/kinds.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/src/mkit/notify.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/src/mkit/output.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/src/mkit/proc.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/src/mkit/registry.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/src/mkit/safety.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/src/mkit/secrets.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/src/mkit/toolchain.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/tests/conftest.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/tests/fakeplay.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/tests/test_art.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/tests/test_aso.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/tests/test_core.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/tests/test_datasafety.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/tests/test_device_logs.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/tests/test_docs.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/tests/test_firebase_apps.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/tests/test_network_errors.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/tests/test_products.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/tests/test_release.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/tests/test_store.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/tests/test_studio.py +0 -0
- {mkit_cli-0.4.0 → mkit_cli-0.5.0}/uv.lock +0 -0
|
@@ -7,6 +7,15 @@ it rather than doing the wrong thing.
|
|
|
7
7
|
|
|
8
8
|
## Unreleased
|
|
9
9
|
|
|
10
|
+
## 0.5.0 — 2026-10-23
|
|
11
|
+
|
|
12
|
+
**An app can keep its own version of any guide.** A file named like the guide
|
|
13
|
+
in the app's repo — `docs/guides/about-screen.md`, `docs/guides/app-ui.md`, or
|
|
14
|
+
under `[guide] app_dir` — is added after the guide and the studio's own rules,
|
|
15
|
+
under *This app's own rules*, and wins over both. `[guide] replace = ["ui"]` in
|
|
16
|
+
the app's `mkit.toml` makes that file the whole guide. Placeholders work in it,
|
|
17
|
+
`{{app.name}}` included, and `mkit guide --json` reports `app` and `replaced`.
|
|
18
|
+
|
|
10
19
|
## 0.4.0 — 2026-10-23
|
|
11
20
|
|
|
12
21
|
**Guides.** `mkit guide` reads the guides every app follows — the About screen,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: mkit-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Release Android apps to Google Play from the command line: build, upload, listing, screenshots, data safety, in-app products, vitals and Firebase, in one tool built for humans and AI agents.
|
|
5
5
|
Project-URL: Homepage, https://github.com/almasumdev/mkit-cli
|
|
6
6
|
Project-URL: Documentation, https://github.com/almasumdev/mkit-cli/blob/main/docs/commands.md
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# mkit command reference
|
|
2
2
|
|
|
3
|
-
Generated from the command registry (mkit 0.
|
|
3
|
+
Generated from the command registry (mkit 0.5.x). Do not edit by hand: run `python scripts/gen_docs.py`.
|
|
4
4
|
|
|
5
5
|
Global options, accepted before or after any command: `--json`, `--human`, `--project PATH`, `--dry-run`, `--strict`, `--yes --confirm PACKAGE`, `--no-color`.
|
|
6
6
|
|
|
@@ -128,13 +128,26 @@ signoff = "The <App> team" # the line above the email address
|
|
|
128
128
|
|
|
129
129
|
[guide]
|
|
130
130
|
house_dir = "~/studio/house" # this studio's own rules, one file per guide
|
|
131
|
+
# In an app's mkit.toml:
|
|
132
|
+
# app_dir = "docs/guides" # this app's own rules (the default)
|
|
133
|
+
# replace = ["ui"] # guides this app does its own way, start to finish
|
|
131
134
|
```
|
|
132
135
|
|
|
133
|
-
A
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
136
|
+
A guide is read in three layers, each winning over the one before:
|
|
137
|
+
|
|
138
|
+
1. **The guide** — the same for everyone.
|
|
139
|
+
2. **The studio's rules** — `house_dir/<file>`: its worked examples, a look its
|
|
140
|
+
apps share, a rule it chose differently. Appended under *This studio's own
|
|
141
|
+
rules*. Without `house_dir`, mkit looks in `docs/house/` beside its own
|
|
142
|
+
source checkout.
|
|
143
|
+
3. **The app's rules** — `<app>/docs/guides/<file>` in the app's own repo, or
|
|
144
|
+
`app_dir`. Appended last, under *This app's own rules*: its own layout idea,
|
|
145
|
+
a part it does differently, the components to use. An app that lists the
|
|
146
|
+
guide in `replace` uses its file alone, and neither layer above is read.
|
|
147
|
+
|
|
148
|
+
The file is named like the guide's: `about-screen.md`, `app-ui.md`,
|
|
149
|
+
`in-app-review.md` — `mkit guide` lists them. Placeholders work in all three,
|
|
150
|
+
`{{app.name}}` included.
|
|
138
151
|
|
|
139
152
|
## Store listing file
|
|
140
153
|
|
|
@@ -25,7 +25,8 @@ def guide(ctx, args):
|
|
|
25
25
|
out.table([(r['name'], r['about']) for r in rows], ('name', 'what it covers'))
|
|
26
26
|
out.line()
|
|
27
27
|
out.line('Read one with `mkit guide <name>`. Your details come from [studio] and [about] '
|
|
28
|
-
'in ~/.mkit/config.toml or this app\'s mkit.toml
|
|
28
|
+
'in ~/.mkit/config.toml or this app\'s mkit.toml; an app\'s own rules go in '
|
|
29
|
+
'its docs/guides/<file>.', 'dim')
|
|
29
30
|
return {'guides': rows}
|
|
30
31
|
|
|
31
32
|
try:
|
|
@@ -25,6 +25,10 @@ for it and nobody else - in a house folder: the file of the same name, which is
|
|
|
25
25
|
appended after the guide under a heading saying it overrides what it
|
|
26
26
|
contradicts. By default that folder is docs/house in the engine's own git
|
|
27
27
|
checkout; [guide] house_dir points anywhere else.
|
|
28
|
+
|
|
29
|
+
An app may keep its own on top, in its own repo: docs/guides/<file> (or
|
|
30
|
+
[guide] app_dir), appended last and winning over both. An app that names a
|
|
31
|
+
guide in [guide] replace uses its own file alone.
|
|
28
32
|
"""
|
|
29
33
|
import os
|
|
30
34
|
import re
|
|
@@ -129,20 +133,50 @@ def _keep_parts(text, enabled):
|
|
|
129
133
|
return '\n'.join(out)
|
|
130
134
|
|
|
131
135
|
|
|
136
|
+
def app_file(filename, project):
|
|
137
|
+
"""The app's own version of a guide, in its repo: docs/guides/ unless [guide] app_dir says."""
|
|
138
|
+
if project is None:
|
|
139
|
+
return None
|
|
140
|
+
folder = project.root / project.get('guide.app_dir', 'docs/guides')
|
|
141
|
+
found = folder / filename
|
|
142
|
+
return found if found.is_file() else None
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def _append(text, heading, note, extra):
|
|
146
|
+
return (text.rstrip('\n') + f'\n\n---\n\n## {heading}\n\n*{note}*\n\n'
|
|
147
|
+
+ extra.read_text(encoding='utf-8').strip() + '\n')
|
|
148
|
+
|
|
149
|
+
|
|
132
150
|
def render(name, project=None, raw=False):
|
|
133
|
-
"""The guide as text, filled in for this studio. Returns (text, info).
|
|
151
|
+
"""The guide as text, filled in for this studio and app. Returns (text, info).
|
|
152
|
+
|
|
153
|
+
Three layers, each winning over the one before: the guide, the studio's
|
|
154
|
+
house rules, the app's own file. An app that lists the guide in
|
|
155
|
+
[guide] replace uses its own file alone.
|
|
156
|
+
"""
|
|
134
157
|
filename, _ = GUIDES[name]
|
|
135
158
|
path = _locate(filename)
|
|
136
159
|
if path is None:
|
|
137
160
|
raise FileNotFoundError(filename)
|
|
138
|
-
text = path.read_text(encoding='utf-8')
|
|
139
161
|
get = settings(project)
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
162
|
+
app = app_file(filename, project)
|
|
163
|
+
replaced = app is not None and name in (get('guide.replace') or ())
|
|
164
|
+
if replaced:
|
|
165
|
+
text, house = app.read_text(encoding='utf-8'), None
|
|
166
|
+
else:
|
|
167
|
+
text = path.read_text(encoding='utf-8')
|
|
168
|
+
house = house_file(filename, get)
|
|
169
|
+
if house:
|
|
170
|
+
text = _append(text, "This studio's own rules",
|
|
171
|
+
'Kept by this studio, beside the guide above. Where the two disagree, '
|
|
172
|
+
'these win.', house)
|
|
173
|
+
if app:
|
|
174
|
+
text = _append(text, "This app's own rules",
|
|
175
|
+
"Kept in this app's repo. Where they disagree with anything above, "
|
|
176
|
+
'they win.', app)
|
|
177
|
+
info = {'guide': name, 'file': str(app if replaced else path),
|
|
178
|
+
'house': str(house) if house else None, 'app': str(app) if app else None,
|
|
179
|
+
'replaced': replaced, 'unset': []}
|
|
146
180
|
if raw:
|
|
147
181
|
return text, info
|
|
148
182
|
|
|
@@ -109,3 +109,34 @@ def test_the_wheel_packs_every_guide():
|
|
|
109
109
|
pytest.skip('this build does not package the guides')
|
|
110
110
|
for filename, _ in guides.GUIDES.values():
|
|
111
111
|
assert f'"docs/{filename}" = "mkit/_guides/{filename}"' in text, filename
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def test_an_app_adds_its_own_rules_last(capsys, isolated, project, monkeypatch, tmp_path):
|
|
115
|
+
house = tmp_path / 'house'
|
|
116
|
+
house.mkdir()
|
|
117
|
+
(house / 'app-ui.md').write_text('Studio look.\n', encoding='utf-8')
|
|
118
|
+
monkeypatch.setenv('MKIT_GUIDE_HOUSE_DIR', str(house))
|
|
119
|
+
(project / 'docs' / 'guides').mkdir(parents=True)
|
|
120
|
+
(project / 'docs' / 'guides' / 'app-ui.md').write_text('Cards everywhere in {{app.name}}.\n',
|
|
121
|
+
encoding='utf-8')
|
|
122
|
+
monkeypatch.chdir(project)
|
|
123
|
+
code, env = run(capsys, 'guide', 'ui')
|
|
124
|
+
text = env['result']['text']
|
|
125
|
+
assert code == 0 and env['result']['app'] and not env['result']['replaced']
|
|
126
|
+
assert '## Colours from one place' in text
|
|
127
|
+
assert text.index('Studio look.') < text.index("## This app's own rules")
|
|
128
|
+
assert text.rstrip().endswith('Cards everywhere in Demo.')
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def test_an_app_can_replace_a_guide_outright(capsys, isolated, no_house, project, monkeypatch):
|
|
132
|
+
with open(project / 'mkit.toml', 'a', encoding='utf-8') as f:
|
|
133
|
+
f.write('[guide]\napp_dir = "design"\nreplace = ["about"]\n')
|
|
134
|
+
(project / 'design').mkdir()
|
|
135
|
+
(project / 'design' / 'about-screen.md').write_text('# Our About page\n\nOne line.\n',
|
|
136
|
+
encoding='utf-8')
|
|
137
|
+
monkeypatch.chdir(project)
|
|
138
|
+
code, env = run(capsys, 'guide', 'about')
|
|
139
|
+
assert code == 0 and env['result']['replaced']
|
|
140
|
+
assert env['result']['text'].strip() == '# Our About page\n\nOne line.'
|
|
141
|
+
code, env = run(capsys, 'guide', 'ui')
|
|
142
|
+
assert env['result']['app'] is None and '## Colours from one place' in env['result']['text']
|
|
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
|
|
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
|
|
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
|
|
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
|