Validate manifest structure and required fields.
(self)
| 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"] |
| 179 | for field in ["id", "name", "version", "description"]: |
| 180 | if field not in pack: |
| 181 | raise PresetValidationError(f"Missing preset.{field}") |
| 182 | |
| 183 | # Validate pack ID format |
| 184 | if not re.match(r'^[a-z0-9-]+$', pack["id"]): |
| 185 | raise PresetValidationError( |
| 186 | f"Invalid preset ID '{pack['id']}': " |
| 187 | "must be lowercase alphanumeric with hyphens only" |
| 188 | ) |
| 189 | |
| 190 | # Validate semantic version |
| 191 | try: |
| 192 | pkg_version.Version(pack["version"]) |
| 193 | except pkg_version.InvalidVersion: |
| 194 | raise PresetValidationError(f"Invalid version: {pack['version']}") |
| 195 | |
| 196 | # Validate requires section |
| 197 | requires = self.data["requires"] |
| 198 | if "speckit_version" not in requires: |
| 199 | raise PresetValidationError("Missing requires.speckit_version") |
| 200 | |
| 201 | # Validate provides section |
| 202 | provides = self.data["provides"] |
| 203 | if "templates" not in provides or not provides["templates"]: |
| 204 | raise PresetValidationError( |
| 205 | "Preset must provide at least one template" |
| 206 | ) |
| 207 | |
| 208 | # Validate templates |
| 209 | for tmpl in provides["templates"]: |
| 210 | if "type" not in tmpl or "name" not in tmpl or "file" not in tmpl: |
| 211 | raise PresetValidationError( |
| 212 | "Template missing 'type', 'name', or 'file'" |
| 213 | ) |
| 214 | |
| 215 | if tmpl["type"] not in VALID_PRESET_TEMPLATE_TYPES: |
| 216 | raise PresetValidationError( |
| 217 | f"Invalid template type '{tmpl['type']}': " |
| 218 | f"must be one of {sorted(VALID_PRESET_TEMPLATE_TYPES)}" |
| 219 | ) |
| 220 |
no test coverage detected