bazis-cli 2.6.0__tar.gz → 2.6.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.
Files changed (38) hide show
  1. {bazis_cli-2.6.0 → bazis_cli-2.6.1}/PKG-INFO +1 -1
  2. {bazis_cli-2.6.0 → bazis_cli-2.6.1}/bazis/contrib/cli/_version.py +3 -3
  3. {bazis_cli-2.6.0 → bazis_cli-2.6.1}/bazis_cli.egg-info/PKG-INFO +1 -1
  4. {bazis_cli-2.6.0 → bazis_cli-2.6.1}/bazis_cli.egg-info/SOURCES.txt +1 -0
  5. {bazis_cli-2.6.0 → bazis_cli-2.6.1}/bazis_cli.egg-info/scm_file_list.json +1 -0
  6. {bazis_cli-2.6.0 → bazis_cli-2.6.1}/bazis_cli.egg-info/scm_version.json +2 -2
  7. bazis_cli-2.6.1/docs/releases/2.6.1.md +9 -0
  8. {bazis_cli-2.6.0 → bazis_cli-2.6.1}/evals/grade.py +70 -22
  9. {bazis_cli-2.6.0 → bazis_cli-2.6.1}/tests/test_cli.py +22 -0
  10. {bazis_cli-2.6.0 → bazis_cli-2.6.1}/tests/test_evals.py +82 -0
  11. {bazis_cli-2.6.0 → bazis_cli-2.6.1}/.dockerignore +0 -0
  12. {bazis_cli-2.6.0 → bazis_cli-2.6.1}/.github/workflows/build-publish.yml +0 -0
  13. {bazis_cli-2.6.0 → bazis_cli-2.6.1}/.github/workflows/release.yml +0 -0
  14. {bazis_cli-2.6.0 → bazis_cli-2.6.1}/.github/workflows/tests.yml +0 -0
  15. {bazis_cli-2.6.0 → bazis_cli-2.6.1}/.github/workflows/update-licenses.yml +0 -0
  16. {bazis_cli-2.6.0 → bazis_cli-2.6.1}/.gitignore +0 -0
  17. {bazis_cli-2.6.0 → bazis_cli-2.6.1}/CLAUDE.md +0 -0
  18. {bazis_cli-2.6.0 → bazis_cli-2.6.1}/README.md +0 -0
  19. {bazis_cli-2.6.0 → bazis_cli-2.6.1}/bazis/contrib/cli/AGENTS.md +0 -0
  20. {bazis_cli-2.6.0 → bazis_cli-2.6.1}/bazis/contrib/cli/__init__.py +0 -0
  21. {bazis_cli-2.6.0 → bazis_cli-2.6.1}/bazis/contrib/cli/agent.py +0 -0
  22. {bazis_cli-2.6.0 → bazis_cli-2.6.1}/bazis/contrib/cli/bazis_manifest.toml +0 -0
  23. {bazis_cli-2.6.0 → bazis_cli-2.6.1}/bazis/contrib/cli/main.py +0 -0
  24. {bazis_cli-2.6.0 → bazis_cli-2.6.1}/bazis_cli.egg-info/dependency_links.txt +0 -0
  25. {bazis_cli-2.6.0 → bazis_cli-2.6.1}/bazis_cli.egg-info/entry_points.txt +0 -0
  26. {bazis_cli-2.6.0 → bazis_cli-2.6.1}/bazis_cli.egg-info/requires.txt +0 -0
  27. {bazis_cli-2.6.0 → bazis_cli-2.6.1}/bazis_cli.egg-info/top_level.txt +0 -0
  28. {bazis_cli-2.6.0 → bazis_cli-2.6.1}/docs/releases/2.4.0.md +0 -0
  29. {bazis_cli-2.6.0 → bazis_cli-2.6.1}/docs/releases/2.4.1.md +0 -0
  30. {bazis_cli-2.6.0 → bazis_cli-2.6.1}/docs/releases/2.5.0.md +0 -0
  31. {bazis_cli-2.6.0 → bazis_cli-2.6.1}/docs/releases/2.6.0.md +0 -0
  32. {bazis_cli-2.6.0 → bazis_cli-2.6.1}/evals/__init__.py +0 -0
  33. {bazis_cli-2.6.0 → bazis_cli-2.6.1}/evals/run.py +0 -0
  34. {bazis_cli-2.6.0 → bazis_cli-2.6.1}/evals/tasks.toml +0 -0
  35. {bazis_cli-2.6.0 → bazis_cli-2.6.1}/pyproject.toml +0 -0
  36. {bazis_cli-2.6.0 → bazis_cli-2.6.1}/setup.cfg +0 -0
  37. {bazis_cli-2.6.0 → bazis_cli-2.6.1}/tests/__init__.py +0 -0
  38. {bazis_cli-2.6.0 → bazis_cli-2.6.1}/tests/conftest.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bazis-cli
3
- Version: 2.6.0
3
+ Version: 2.6.1
4
4
  Summary: The bazis command: creates, extends and audits Bazis products, backend and frontend, with Claude (Claude Agent SDK).
5
5
  Author-email: Ilya Kharyn <ilya.tt07@gmail.com>
6
6
  Maintainer-email: Ilya Kharyn <ilya.tt07@gmail.com>
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '2.6.0'
22
- __version_tuple__ = version_tuple = (2, 6, 0)
21
+ __version__ = version = '2.6.1'
22
+ __version_tuple__ = version_tuple = (2, 6, 1)
23
23
 
24
- __commit_id__ = commit_id = 'g04ed6b75d'
24
+ __commit_id__ = commit_id = 'g27dcdfabb'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bazis-cli
3
- Version: 2.6.0
3
+ Version: 2.6.1
4
4
  Summary: The bazis command: creates, extends and audits Bazis products, backend and frontend, with Claude (Claude Agent SDK).
5
5
  Author-email: Ilya Kharyn <ilya.tt07@gmail.com>
6
6
  Maintainer-email: Ilya Kharyn <ilya.tt07@gmail.com>
@@ -25,6 +25,7 @@ docs/releases/2.4.0.md
25
25
  docs/releases/2.4.1.md
26
26
  docs/releases/2.5.0.md
27
27
  docs/releases/2.6.0.md
28
+ docs/releases/2.6.1.md
28
29
  evals/__init__.py
29
30
  evals/grade.py
30
31
  evals/run.py
@@ -17,6 +17,7 @@
17
17
  "docs/releases/2.4.1.md",
18
18
  "docs/releases/2.5.0.md",
19
19
  "docs/releases/2.6.0.md",
20
+ "docs/releases/2.6.1.md",
20
21
  "evals/__init__.py",
21
22
  "evals/grade.py",
22
23
  "evals/run.py",
@@ -1,7 +1,7 @@
1
1
  {
2
- "tag": "2.6.0",
2
+ "tag": "2.6.1",
3
3
  "distance": 0,
4
- "node": "g04ed6b75d74ecf9bbfacf5be0bd136fda8106bf7",
4
+ "node": "g27dcdfabbf66447ef51bf37bdeb01b5ad7ce85f9",
5
5
  "dirty": false,
6
6
  "branch": "HEAD",
7
7
  "node_date": "2026-10-18"
@@ -0,0 +1,9 @@
1
+ # bazis-cli 2.6.1
2
+
3
+ ## Changes
4
+
5
+ - The evals grader keeps the settings of the machine (the database and GDAL) and creates
6
+ the database on the server of the project.
7
+ - The check that the migrations apply does not pass silently: when the database server is
8
+ unknown or createdb is missing, the check fails.
9
+ - The test of the requirements to the latest releases of bazis and bazis-mcp is restored.
@@ -51,6 +51,21 @@ BAZIS_CHECK_PREFIXES = (
51
51
  'async_request.', 'bg.', 'front.',
52
52
  )
53
53
  TIMEOUT = 900
54
+ #: the settings of the machine that the agent was given and the project must keep when it is
55
+ #: graded: the PostgreSQL and Redis servers and the GDAL and GEOS libraries. The other `BS_*`
56
+ #: variables are those of the project (its `.env`), the name of the database is the grader's
57
+ MACHINE_SETTINGS = (
58
+ 'BS_DATABASES__DEFAULT__HOST', 'BS_DATABASES__DEFAULT__PORT', 'BS_DATABASES__DEFAULT__USER',
59
+ 'BS_DATABASES__DEFAULT__PASSWORD', 'BS_CACHES__DEFAULT__LOCATION',
60
+ 'BS_GDAL_LIBRARY_PATH', 'BS_GEOS_LIBRARY_PATH',
61
+ )
62
+ #: run by `manage.py shell` of the project: prints the server of its database as JSON
63
+ DATABASE_SERVER = """\
64
+ import json
65
+ from django.conf import settings
66
+ db = settings.DATABASES['default']
67
+ print(json.dumps({key: db.get(key.upper()) or None for key in ('host', 'port', 'user', 'password')}))
68
+ """
54
69
  #: the TSX of `frontend/src/` that a product does not write: the generated screens and the
55
70
  #: template (its entry, its app and its login), besides the assets that bazis-front copied
56
71
  #: (the files of `assets` in its lock, among them the shadcn components of `components/ui/`)
@@ -132,13 +147,27 @@ def local_apps(project: Path) -> set[str]:
132
147
 
133
148
  def project_env(database: str) -> dict:
134
149
  """
135
- The environment of the project: its own .env and project.env, a database of its own.
150
+ The environment of the project: its own .env and project.env and the settings of the
151
+ machine (`MACHINE_SETTINGS`), a database of its own.
136
152
  """
137
- env = {k: v for k, v in os.environ.items() if not k.startswith(('BS_', 'DJANGO_'))}
153
+ env = {
154
+ k: v for k, v in os.environ.items()
155
+ if k in MACHINE_SETTINGS or not k.startswith(('BS_', 'DJANGO_'))
156
+ }
138
157
  env['BS_DATABASES__DEFAULT__NAME'] = database
139
158
  return env
140
159
 
141
160
 
161
+ def postgres_command(program: str, server: dict, *args: str) -> list[str]:
162
+ """
163
+ A PostgreSQL client command (`createdb`, `dropdb`) for the server of the project.
164
+ """
165
+ command = [program, '-h', server['host'] or 'localhost', '-U', server['user'] or 'postgres']
166
+ if server['port']:
167
+ command += ['-p', str(server['port'])]
168
+ return [*command, *args]
169
+
170
+
142
171
  def package_warnings(messages: list[dict]) -> list[str]:
143
172
  """
144
173
  The ids of the warnings of the Bazis packages among the messages of `bazis_doctor`; the
@@ -196,26 +225,7 @@ def grade(project: Path, task: dict) -> list[Check]:
196
225
 
197
226
  if task.get('frontend'):
198
227
  checks += frontend_checks(project, env, task.get('min_scenarios', 1))
199
- if shutil.which('createdb'):
200
- created = subprocess.run(
201
- ['createdb', '-h', 'localhost', '-U', 'postgres', database],
202
- env={**env, 'PGPASSWORD': 'postgres'}, capture_output=True, text=True,
203
- )
204
- try:
205
- migrate = run(project, ['manage.py', 'migrate', '--noinput'], env)
206
- migrated = created.returncode == 0 and migrate.returncode == 0
207
- checks.append(Check(
208
- 'migrations apply', migrated, created.stderr.strip() or tail(migrate, 6),
209
- ))
210
- if task.get('frontend'):
211
- checks += frontend_database_checks(project, env, migrated)
212
- finally:
213
- subprocess.run(
214
- ['dropdb', '-h', 'localhost', '-U', 'postgres', '--if-exists', '--force', database],
215
- env={**env, 'PGPASSWORD': 'postgres'}, capture_output=True,
216
- )
217
- elif task.get('frontend'):
218
- checks += frontend_database_checks(project, env, False, 'no PostgreSQL (createdb)')
228
+ checks += database_checks(project, env, database, bool(task.get('frontend')))
219
229
 
220
230
  collected = run(project, ['-m', 'pytest', '--collect-only', '-q', '-p', 'no:cacheprovider'], env)
221
231
  count = re.search(r'(\d+) tests? collected', collected.stdout)
@@ -229,6 +239,44 @@ def grade(project: Path, task: dict) -> list[Check]:
229
239
  return checks
230
240
 
231
241
 
242
+ def database_checks(project: Path, env: dict, database: str, frontend: bool) -> list[Check]:
243
+ """
244
+ The checks that need the database of the grading: the migrations apply to a database
245
+ created on the server of the project (dropped afterwards), then the checks of the
246
+ frontend on it. Without `createdb` or the server of the project they fail with the
247
+ reason instead of dropping out of the result.
248
+ """
249
+ server = None
250
+ if shutil.which('createdb'):
251
+ probe = run(project, ['manage.py', 'shell', '-c', DATABASE_SERVER], env)
252
+ server = mcp_project.parse_json(probe.stdout)
253
+ reason = 'server of the database: ' + (tail(probe, 6) or 'no output')
254
+ else:
255
+ reason = 'no PostgreSQL (createdb)'
256
+ if not server:
257
+ checks = [Check('migrations apply', False, reason)]
258
+ if frontend:
259
+ checks += frontend_database_checks(project, env, False, reason)
260
+ return checks
261
+ client_env = {**env, 'PGPASSWORD': server['password'] or ''}
262
+ created = subprocess.run(
263
+ postgres_command('createdb', server, database),
264
+ env=client_env, capture_output=True, text=True,
265
+ )
266
+ try:
267
+ migrate = run(project, ['manage.py', 'migrate', '--noinput'], env)
268
+ migrated = created.returncode == 0 and migrate.returncode == 0
269
+ checks = [Check('migrations apply', migrated, created.stderr.strip() or tail(migrate, 6))]
270
+ if frontend:
271
+ checks += frontend_database_checks(project, env, migrated)
272
+ return checks
273
+ finally:
274
+ subprocess.run(
275
+ postgres_command('dropdb', server, '--if-exists', '--force', database),
276
+ env=client_env, capture_output=True,
277
+ )
278
+
279
+
232
280
  def language_check(project: Path, info: dict, language: str, env: dict) -> Check:
233
281
  """
234
282
  The product is in the language of its description and in English (`LANGUAGES`), the
@@ -15,6 +15,8 @@
15
15
  import io
16
16
  import json
17
17
  import sys
18
+ import tomllib
19
+ from importlib import metadata
18
20
  from pathlib import Path
19
21
  from types import SimpleNamespace
20
22
 
@@ -615,3 +617,23 @@ def test_change_without_a_terminal(tmp_path, fake_agent, monkeypatch, capsys):
615
617
  assert 'bazis change "..."' in capsys.readouterr().err
616
618
  assert fake_agent == []
617
619
 
620
+
621
+ def test_requirements_are_the_latest_releases():
622
+ """
623
+ The CLI requires at least the releases that bazis-mcp describes: its catalog of the
624
+ released packages gives bazis, and the installed bazis-mcp is the release of that catalog.
625
+ """
626
+ from bazis.contrib.mcp import catalog
627
+
628
+ def version(text):
629
+ return tuple(int(part) for part in text.split('.')[:3])
630
+
631
+ installed = metadata.version('bazis-mcp')
632
+ if 'dev' in installed:
633
+ pytest.skip(f'bazis-mcp {installed} is not a release')
634
+ pyproject = tomllib.loads((Path(__file__).parent.parent / 'pyproject.toml').read_text())
635
+ released = {**{name: entry['version'] for name, entry in catalog.catalog().items()}, 'bazis-mcp': installed}
636
+ pins = dict(it.split('>=') for it in pyproject['project']['dependencies'] if it.startswith('bazis'))
637
+ assert pins.keys() == {'bazis', 'bazis-mcp'}
638
+ for name, minimum in pins.items():
639
+ assert version(minimum) >= version(released[name]), f'{name}>={minimum}, released {released[name]}'
@@ -12,10 +12,14 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ import contextlib
16
+ import io
15
17
  import json
16
18
  import os
17
19
  import subprocess
20
+ import sys
18
21
  import time
22
+ import types
19
23
 
20
24
  import pytest
21
25
 
@@ -98,11 +102,45 @@ def test_local_apps(tmp_path):
98
102
  def test_project_env_isolates_the_database(monkeypatch):
99
103
  monkeypatch.setenv('BS_DEBUG', 'false')
100
104
  monkeypatch.setenv('DJANGO_SETTINGS_MODULE', 'other.settings')
105
+ monkeypatch.setenv('BS_DATABASES__DEFAULT__NAME', 'other')
101
106
  env = grade.project_env('eval_x')
102
107
  assert env['BS_DATABASES__DEFAULT__NAME'] == 'eval_x'
103
108
  assert 'BS_DEBUG' not in env and 'DJANGO_SETTINGS_MODULE' not in env
104
109
 
105
110
 
111
+ def test_project_env_keeps_the_settings_of_the_machine(monkeypatch):
112
+ machine = {
113
+ 'BS_DATABASES__DEFAULT__PORT': '55910', 'BS_GDAL_LIBRARY_PATH': '/opt/lib/libgdal.dylib',
114
+ 'BS_GEOS_LIBRARY_PATH': '/opt/lib/libgeos_c.dylib', 'PGHOST': 'db', 'DYLD_LIBRARY_PATH': '/opt/lib',
115
+ }
116
+ for key, value in machine.items():
117
+ monkeypatch.setenv(key, value)
118
+ assert machine.items() <= grade.project_env('eval_x').items()
119
+
120
+
121
+ def test_database_server_of_the_project(monkeypatch):
122
+ settings = types.SimpleNamespace(DATABASES={'default': {
123
+ 'HOST': 'db', 'PORT': '55910', 'USER': 'owner', 'PASSWORD': 'secret', 'NAME': 'x',
124
+ }})
125
+ monkeypatch.setitem(sys.modules, 'django', types.ModuleType('django'))
126
+ monkeypatch.setitem(sys.modules, 'django.conf', types.SimpleNamespace(settings=settings))
127
+ output = io.StringIO()
128
+ with contextlib.redirect_stdout(output):
129
+ exec(grade.DATABASE_SERVER, {})
130
+ server = json.loads(output.getvalue())
131
+ assert server == {'host': 'db', 'port': '55910', 'user': 'owner', 'password': 'secret'}
132
+ assert grade.postgres_command('createdb', server, 'eval_x') == [
133
+ 'createdb', '-h', 'db', '-U', 'owner', '-p', '55910', 'eval_x',
134
+ ]
135
+
136
+
137
+ def test_postgres_command_defaults():
138
+ server = {'host': None, 'port': None, 'user': None, 'password': None}
139
+ assert grade.postgres_command('dropdb', server, '--force', 'x') == [
140
+ 'dropdb', '-h', 'localhost', '-U', 'postgres', '--force', 'x',
141
+ ]
142
+
143
+
106
144
  def test_summary(tmp_path):
107
145
  checks = [grade.Check('project loads', True), grade.Check('tests pass', False, 'boom\n1 failed')]
108
146
  result = {'task': 'tracker', 'seconds': 120, 'cost_usd': 1.5, 'turns': 30,
@@ -247,6 +285,50 @@ def test_frontend_database_checks_without_a_database(tmp_path):
247
285
  ]
248
286
 
249
287
 
288
+ def probe_stub(monkeypatch, output: str, returncode: int = 0):
289
+ monkeypatch.setattr(grade.shutil, 'which', lambda name: '/usr/bin/createdb')
290
+ monkeypatch.setattr(grade, 'run', lambda project, args, env: done(returncode, output))
291
+
292
+
293
+ @pytest.mark.parametrize('frontend', [False, True])
294
+ @pytest.mark.parametrize('output', ['Traceback: no module', 'not json'])
295
+ def test_database_checks_fail_when_the_server_is_unknown(tmp_path, monkeypatch, frontend, output):
296
+ probe_stub(monkeypatch, output, 1)
297
+ project = product(tmp_path, 1)
298
+ checks = grade.database_checks(project, {}, 'eval_x', frontend)
299
+ reason = 'server of the database: ' + output
300
+ expected = [('migrations apply', False, reason)]
301
+ if frontend:
302
+ expected += [('contract fresh', False, reason), ('end-to-end tests pass', False, reason)]
303
+ assert [(c.name, c.passed, c.detail) for c in checks] == expected
304
+
305
+
306
+ @pytest.mark.parametrize('frontend', [False, True])
307
+ def test_database_checks_without_createdb(tmp_path, monkeypatch, frontend):
308
+ monkeypatch.setattr(grade.shutil, 'which', lambda name: None)
309
+ monkeypatch.setattr(grade, 'run', lambda *a: pytest.fail('the server must not be probed'))
310
+ checks = grade.database_checks(product(tmp_path, 1), {}, 'eval_x', frontend)
311
+ assert [(c.name, c.passed, c.detail) for c in checks][0] == (
312
+ 'migrations apply', False, 'no PostgreSQL (createdb)',
313
+ )
314
+ assert len(checks) == (3 if frontend else 1)
315
+
316
+
317
+ def test_database_checks_create_migrate_and_drop(tmp_path, monkeypatch):
318
+ server = {'host': 'db', 'port': None, 'user': 'owner', 'password': 'secret'}
319
+ probe_stub(monkeypatch, json.dumps(server))
320
+ commands = []
321
+
322
+ def fake_subprocess(command, **kwargs):
323
+ commands.append(command[0])
324
+ return subprocess.CompletedProcess(command, 0, '', '')
325
+
326
+ monkeypatch.setattr(grade.subprocess, 'run', fake_subprocess)
327
+ checks = grade.database_checks(tmp_path, {}, 'eval_x', False)
328
+ assert [(c.name, c.passed) for c in checks] == [('migrations apply', True)]
329
+ assert commands == ['createdb', 'dropdb']
330
+
331
+
250
332
  def test_end_to_end_needs_the_test_data(tmp_path, monkeypatch):
251
333
  monkeypatch.setattr(grade, 'run', lambda project, args, env: done(1, 'Unknown command: e2e_data'))
252
334
  check = grade.end_to_end(product(tmp_path, 1), {})
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