MCPcopy
hub / github.com/github/spec-kit / PresetManifest

Class PresetManifest

src/specify_cli/presets/__init__.py:121–310  ·  view source on GitHub ↗

Represents and validates a preset manifest (preset.yml).

Source from the content-addressed store, hash-verified

119
120
121class PresetManifest:
122 """Represents and validates a preset manifest (preset.yml)."""
123
124 SCHEMA_VERSION = "1.0"
125 REQUIRED_FIELDS = ["schema_version", "preset", "requires", "provides"]
126
127 def __init__(self, manifest_path: Path):
128 """Load and validate preset manifest.
129
130 Args:
131 manifest_path: Path to preset.yml file
132
133 Raises:
134 PresetValidationError: If manifest is invalid
135 """
136 self.path = manifest_path
137 self.data = self._load_yaml(manifest_path)
138 self._validate()
139
140 def _load_yaml(self, path: Path) -> dict:
141 """Load YAML file safely."""
142 try:
143 with open(path, 'r', encoding='utf-8') as f:
144 data = yaml.safe_load(f)
145 except yaml.YAMLError as e:
146 raise PresetValidationError(f"Invalid YAML in {path}: {e}")
147 except FileNotFoundError:
148 raise PresetValidationError(f"Manifest not found: {path}")
149 except UnicodeDecodeError as e:
150 raise PresetValidationError(
151 f"Manifest is not valid UTF-8: {path} ({e.reason} at byte {e.start})"
152 )
153 except OSError as e:
154 raise PresetValidationError(f"Could not read manifest {path}: {e}")
155 if data is None:
156 return {}
157 if not isinstance(data, dict):
158 raise PresetValidationError(
159 f"Manifest must be a YAML mapping, got {type(data).__name__}: {path}"
160 )
161 return data
162
163 def _validate(self):
164 """Validate manifest structure and required fields."""
165 # Check required top-level fields
166 for field in self.REQUIRED_FIELDS:
167 if field not in self.data:
168 raise PresetValidationError(f"Missing required field: {field}")
169
170 # Validate schema version
171 if self.data["schema_version"] != self.SCHEMA_VERSION:
172 raise PresetValidationError(
173 f"Unsupported schema version: {self.data['schema_version']} "
174 f"(expected {self.SCHEMA_VERSION})"
175 )
176
177 # Validate preset metadata
178 pack = self.data["preset"]

Calls

no outgoing calls