Repository navigation
fix: version workspace status JSON contract #2469
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,140 @@ | ||
| { | ||
| "$schema": "https://json-schema.org/draft/2020-12/schema", | ||
| "$id": "https://github.com/basefoundry/base/blob/main/docs/schemas/workspace-status.json", | ||
| "title": "Base workspace status report", | ||
| "description": "Stable schema version 1 for basectl workspace status --format json. Additional properties are allowed for additive, non-breaking fields.", | ||
| "type": "object", | ||
| "required": [ | ||
| "schema_version", | ||
| "workspace", | ||
| "status", | ||
| "project_count", | ||
| "projects" | ||
| ], | ||
| "properties": { | ||
| "schema_version": { | ||
| "const": 1 | ||
| }, | ||
| "workspace": { | ||
| "type": "string" | ||
| }, | ||
| "status": { | ||
| "enum": ["ok", "warn", "error"] | ||
| }, | ||
| "project_count": { | ||
| "type": "integer", | ||
| "minimum": 0 | ||
| }, | ||
| "projects": { | ||
| "type": "array", | ||
| "items": { | ||
| "$ref": "#/$defs/project" | ||
| } | ||
| }, | ||
| "workspace_manifest": { | ||
| "$ref": "workspace-manifest.json" | ||
| }, | ||
| "repository_count": { | ||
| "type": "integer", | ||
| "minimum": 0 | ||
| } | ||
| }, | ||
| "$defs": { | ||
| "project": { | ||
| "type": "object", | ||
| "required": [ | ||
| "name", | ||
| "status", | ||
| "path", | ||
| "manifest_path", | ||
| "venv", | ||
| "manifest", | ||
| "last_check", | ||
| "issues" | ||
| ], | ||
| "properties": { | ||
| "name": { | ||
| "type": "string" | ||
| }, | ||
| "status": { | ||
| "enum": ["ok", "warn", "error"] | ||
| }, | ||
| "path": { | ||
| "type": "string" | ||
| }, | ||
| "manifest_path": { | ||
| "type": ["string", "null"] | ||
| }, | ||
| "venv": { | ||
| "type": "string" | ||
| }, | ||
| "manifest": { | ||
| "type": "string" | ||
| }, | ||
| "last_check": { | ||
| "type": ["object", "null"], | ||
| "required": ["checked_at", "status"], | ||
| "properties": { | ||
| "checked_at": { | ||
| "type": "string" | ||
| }, | ||
| "status": { | ||
| "enum": ["ok", "warn", "error"] | ||
| } | ||
| } | ||
| }, | ||
| "issues": { | ||
| "type": "array", | ||
| "items": { | ||
| "type": "string" | ||
| } | ||
| }, | ||
| "repository": { | ||
| "type": "string" | ||
| }, | ||
| "expected": { | ||
| "type": "boolean" | ||
| }, | ||
| "required": { | ||
| "type": "boolean" | ||
| }, | ||
| "repo": { | ||
| "type": "string" | ||
| }, | ||
| "url": { | ||
| "type": "string" | ||
| }, | ||
| "default_branch": { | ||
| "type": "string" | ||
| }, | ||
| "python_runtime": { | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Pre-existing doc drift, now part of a published contract (non-blocking, probably a follow-up issue). Keeping it optional in the schema is correct. But a consumer reading the docs alongside this new stable schema will expect a field that never appears. Either fix that doc section (it's the 4th doc surface for this contract), or file an issue to track it. |
||
| "$ref": "#/$defs/python_runtime" | ||
| } | ||
| } | ||
| }, | ||
| "python_runtime": { | ||
| "type": "object", | ||
| "required": ["manager", "venv", "python", "version"], | ||
| "properties": { | ||
| "manager": { | ||
| "type": "string" | ||
| }, | ||
| "venv": { | ||
| "type": "string" | ||
| }, | ||
| "python": { | ||
| "type": "string" | ||
| }, | ||
| "version": { | ||
| "type": "string" | ||
| }, | ||
| "requires_python": { | ||
| "type": "string" | ||
| }, | ||
| "pyproject_requires_python": { | ||
| "type": "string" | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| from __future__ import annotations | ||
|
|
||
| import json | ||
| from pathlib import Path | ||
|
|
||
| from tests.contracts.workspace_contract_helpers import run_workspace_command | ||
| from tests.contracts.workspace_contract_helpers import validate_workspace_payload | ||
|
|
||
|
|
||
| REPO_ROOT = Path(__file__).resolve().parents[2] | ||
| SCHEMA_PATH = REPO_ROOT / "docs/schemas/workspace-status.json" | ||
|
|
||
|
|
||
| def test_workspace_status_command_matches_published_schema(tmp_path: Path) -> None: | ||
| home = tmp_path / "home" | ||
| base_home = tmp_path / "base" | ||
| workspace = tmp_path / "workspace" | ||
| manifest_path = tmp_path / "workspace.yaml" | ||
| home.mkdir() | ||
| base_home.mkdir() | ||
| workspace.mkdir() | ||
|
|
||
| for args, manifest_backed, expected_project_count in ( | ||
| (["status", "--workspace", str(workspace), "--format", "json"], False, 0), | ||
| ( | ||
| ["status", "--workspace", str(workspace), "--manifest", str(manifest_path), "--format", "json"], | ||
| True, | ||
| 1, | ||
| ), | ||
| ): | ||
| if manifest_backed: | ||
| manifest_path.write_text( | ||
| "schema_version: 1\nworkspace:\n name: status-suite\nrepos:\n - name: demo\n", | ||
| encoding="utf-8", | ||
| ) | ||
| (workspace / "demo").mkdir() | ||
| (workspace / "demo" / "base_manifest.yaml").write_text( | ||
| "project:\n name: demo\nartifacts: []\n", encoding="utf-8" | ||
| ) | ||
|
|
||
| result = run_workspace_command(args, base_home, home) | ||
|
|
||
| assert result.returncode == 0 | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Coverage gap (non-blocking). All three scenarios are happy paths with exit 0. The shapes most likely to drift are the non-ok ones, and none is exercised: |
||
| assert result.stderr == "" | ||
| payload = json.loads(result.stdout) | ||
| validate_workspace_payload(payload, SCHEMA_PATH) | ||
| assert payload["schema_version"] == 1 | ||
| assert payload["project_count"] == expected_project_count | ||
| if manifest_backed: | ||
| assert payload["workspace_manifest"]["name"] == "status-suite" | ||
| assert payload["repository_count"] == 1 | ||
|
|
||
| discovery_workspace = tmp_path / "discovery-workspace" | ||
| discovery_workspace.mkdir() | ||
| (discovery_workspace / "discovered").mkdir() | ||
| (discovery_workspace / "discovered" / "base_manifest.yaml").write_text( | ||
| "project:\n name: discovered\nartifacts: []\n", encoding="utf-8" | ||
| ) | ||
| result = run_workspace_command( | ||
| ["status", "--workspace", str(discovery_workspace), "--format", "json"], | ||
| base_home, | ||
| home, | ||
| ) | ||
|
|
||
| assert result.returncode == 0 | ||
| assert result.stderr == "" | ||
| payload = json.loads(result.stdout) | ||
| validate_workspace_payload(payload, SCHEMA_PATH) | ||
| assert payload["schema_version"] == 1 | ||
| assert payload["project_count"] == 1 | ||
| assert payload["projects"][0]["name"] == "discovered" | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Contract clarity (non-blocking). With a manifest,
project_countcounts only repos whosemanifestisvalid/invalid. In my probe, 5 records gaveproject_count: 2andrepository_count: 5. That's existing behavior, but a reader of a schema now marked stable will likely assumeproject_count == len(projects). Adescriptiononproject_countandrepository_countwould capture the rule.Related:
venv,manifestandrepoare free-form strings even though the values come from a fixed set (manifest: valid/invalid/missing/unknown;repo: present/missing; ...). Leaving them open is defensible under the additive-change policy, because adding an enum value would otherwise be breaking. Even so, listing the known values indescriptionwould help consumers.