Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ image-mcp-server: ## Build mcp-server image
docker build -t $(REGISTRY)/mcp-server:$(TAG) -f mcp-server/Dockerfile .

# -------- codegen --------
.PHONY: generate manifests
.PHONY: generate manifests module-schema
generate: ## Run controller-gen deepcopy generators
cd operator && go run sigs.k8s.io/controller-tools/cmd/controller-gen object paths=./api/...

Expand All @@ -264,6 +264,10 @@ manifests: ## Regenerate CRDs + RBAC manifests (and sync chart CRD copies)
output:rbac:artifacts:config=config/rbac
cp operator/config/crd/gameplane.local_*.yaml charts/gameplane/crds/

module-schema: ## Regenerate the editor JSON Schema for module template.yaml from the CRD
python3 hack/gen-module-schema.py
@echo "commit modules/.schema/gametemplate.schema.json in the gameplane-module repo + bump the submodule pointer"

# -------- local dev cluster (kind) --------
.PHONY: dev-up dev-down dev-load dev-push dev-install
dev-up: ## Create/prepare cluster + install Gameplane (CLUSTER=kind|remote)
Expand Down
13 changes: 13 additions & 0 deletions docs/module-authoring.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,19 @@ modules/<name>/
submodule mounts the `gameplane-module` repo root at `modules/`, so the paths
below still resolve.

### Editor schemas

Both YAML files carry a first-line `# yaml-language-server: $schema=…` modeline
pointing at JSON Schemas under `modules/.schema/`, so editors (VS Code's
`redhat.vscode-yaml`) validate and autocomplete them instead of guessing an
unrelated schema. `module.schema.json` is hand-maintained; `gametemplate.schema.json`
is **generated from the GameTemplate CRD** — after changing CRD spec fields,
regenerate and commit it (in the submodule):

```sh
make module-schema # hack/gen-module-schema.py → modules/.schema/gametemplate.schema.json
```

`template.yaml` is the same `GameTemplate` you would write today, with one
difference: omit `metadata.name`. The name is set on install from
`module.yaml#name`, so a single bundle can be installed under different
Expand Down
60 changes: 60 additions & 0 deletions hack/gen-module-schema.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#!/usr/bin/env python3
"""Generate a JSON Schema for module template.yaml files from the GameTemplate CRD.

template.yaml in a Gameplane module IS a GameTemplate custom resource, so its
schema is the CRD's openAPIV3Schema. Editors (redhat.vscode-yaml) otherwise
guess a schema from their schema store and mis-flag valid fields; a
`# yaml-language-server: $schema=` modeline in each template.yaml points at the
file this writes.

Output goes into the gameplane-module submodule so it lives with the files it
validates (and resolves for a standalone checkout of that repo too). Re-run
after CRD changes: make module-schema

The module.yaml schema is hand-maintained (small, stable manifest) at the same
location and is NOT generated here.
"""
import json
import sys
from pathlib import Path

import yaml

REPO = Path(__file__).resolve().parent.parent
CRD = REPO / "charts/gameplane/crds/gameplane.local_gametemplates.yaml"
OUT = REPO / "modules/.schema/gametemplate.schema.json"


def main() -> int:
if not CRD.exists():
print(f"CRD not found: {CRD}", file=sys.stderr)
return 1
crd = yaml.safe_load(CRD.read_text())
versions = crd["spec"]["versions"]
# Single served version today; take the storage version to be safe.
ver = next((v for v in versions if v.get("storage")), versions[0])
schema = ver["schema"]["openAPIV3Schema"]

# JSON Schema envelope. The CRD's openAPIV3Schema already describes the whole
# document (apiVersion/kind/metadata/spec/status); we only add the draft
# marker + a title and pin apiVersion/kind so a wrong header is flagged.
schema["$schema"] = "http://json-schema.org/draft-07/schema#"
schema["title"] = "Gameplane GameTemplate (module template.yaml)"
schema.setdefault("properties", {})
schema["properties"]["apiVersion"] = {
"const": f"{crd['spec']['group']}/{ver['name']}",
"description": "gameplane.local/v1alpha1",
}
schema["properties"]["kind"] = {
"const": crd["spec"]["names"]["kind"],
"description": "GameTemplate",
}

OUT.parent.mkdir(parents=True, exist_ok=True)
OUT.write_text(json.dumps(schema, indent=2, sort_keys=False) + "\n")
print(f"wrote {OUT.relative_to(REPO)} ({OUT.stat().st_size} bytes)")
return 0


if __name__ == "__main__":
raise SystemExit(main())
Loading