mirror of
https://github.com/iflytek/skillhub.git
synced 2026-10-09 03:17:52 +00:00
fix(publish): simplify frontmatter validation errors
This commit is contained in:
parent
30545d6a8a
commit
74ac44ed65
5 changed files with 84 additions and 4 deletions
|
|
@ -7,8 +7,11 @@ import java.util.ArrayList;
|
|||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class SkillPackageValidator {
|
||||
private static final Pattern YAML_LINE_COLUMN = Pattern.compile("line\\s+(\\d+),\\s+column\\s+(\\d+)");
|
||||
|
||||
private final SkillMetadataParser metadataParser;
|
||||
private final int maxFileCount;
|
||||
|
|
@ -83,10 +86,7 @@ public class SkillPackageValidator {
|
|||
String content = new String(skillMd.content());
|
||||
metadataParser.parse(content);
|
||||
} catch (LocalizedDomainException e) {
|
||||
String detail = e.messageArgs().length == 0
|
||||
? e.messageCode()
|
||||
: e.messageCode() + " " + java.util.Arrays.toString(e.messageArgs());
|
||||
errors.add("Invalid SKILL.md frontmatter: " + detail);
|
||||
errors.add("Invalid SKILL.md frontmatter: " + formatMetadataError(e));
|
||||
}
|
||||
|
||||
// 3. Check file count
|
||||
|
|
@ -113,4 +113,38 @@ public class SkillPackageValidator {
|
|||
private boolean hasAllowedExtension(String normalizedPath) {
|
||||
return allowedExtensions.stream().anyMatch(normalizedPath::endsWith);
|
||||
}
|
||||
|
||||
private String formatMetadataError(LocalizedDomainException exception) {
|
||||
return switch (exception.messageCode()) {
|
||||
case "error.skill.metadata.requiredField.missing" ->
|
||||
"missing required field \"" + exception.messageArgs()[0] + "\"";
|
||||
case "error.skill.metadata.frontmatter.missingStart" ->
|
||||
"missing opening --- marker";
|
||||
case "error.skill.metadata.frontmatter.missingEnd" ->
|
||||
"missing closing --- marker";
|
||||
case "error.skill.metadata.frontmatter.missingContent" ->
|
||||
"frontmatter is empty";
|
||||
case "error.skill.metadata.yaml.notMap" ->
|
||||
"frontmatter must be a YAML object";
|
||||
case "error.skill.metadata.yaml.invalid" ->
|
||||
formatYamlSyntaxError(exception.messageArgs());
|
||||
default -> {
|
||||
if (exception.messageArgs().length == 0) {
|
||||
yield exception.messageCode();
|
||||
}
|
||||
yield exception.messageCode() + " " + java.util.Arrays.toString(exception.messageArgs());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private String formatYamlSyntaxError(Object[] args) {
|
||||
String raw = args.length > 0 && args[0] != null ? args[0].toString() : "";
|
||||
Matcher matcher = YAML_LINE_COLUMN.matcher(raw);
|
||||
if (matcher.find()) {
|
||||
return "invalid YAML near line " + matcher.group(1)
|
||||
+ ", column " + matcher.group(2)
|
||||
+ ". If a value contains a colon, wrap it in quotes.";
|
||||
}
|
||||
return "invalid YAML syntax";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -142,6 +142,31 @@ class SkillPackageValidatorTest {
|
|||
assertTrue(result.errors().stream().anyMatch(e -> e.contains("Invalid SKILL.md frontmatter") && e.contains("name")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testInvalidYamlFrontmatterUsesFriendlyMessage() {
|
||||
String skillMdContent = """
|
||||
---
|
||||
name: clawdbot
|
||||
description: Send messages from Clawdbot via the discord tool: send messages, react, post or edit
|
||||
version: 1.0.0
|
||||
---
|
||||
Body
|
||||
""";
|
||||
|
||||
List<PackageEntry> entries = List.of(
|
||||
new PackageEntry("SKILL.md", skillMdContent.getBytes(), skillMdContent.length(), "text/markdown")
|
||||
);
|
||||
|
||||
ValidationResult result = validator.validate(entries);
|
||||
|
||||
assertFalse(result.passed());
|
||||
assertTrue(result.errors().stream().anyMatch(e ->
|
||||
e.contains("Invalid SKILL.md frontmatter")
|
||||
&& e.contains("line")
|
||||
&& e.contains("column")));
|
||||
assertFalse(result.errors().stream().anyMatch(e -> e.contains("mapping values are not allowed here")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPackageTooLarge() {
|
||||
String skillMdContent = """
|
||||
|
|
|
|||
|
|
@ -591,6 +591,8 @@
|
|||
"versionExistsDescription": "This skill version has already been published. Update the version in SKILL.md, rebuild the package, and upload it again.",
|
||||
"precheckFailedTitle": "Pre-publish check failed",
|
||||
"precheckFailedDescription": "The package appears to contain a secret, token, or password. Replace real credentials with placeholders and try again.",
|
||||
"frontmatterFailedTitle": "SKILL.md format is invalid",
|
||||
"frontmatterFailedDescription": "Please check the YAML frontmatter at the top of SKILL.md. If a field value contains a colon, wrap it in quotes.",
|
||||
"selectRequired": "Please select namespace and file"
|
||||
},
|
||||
"toast": {
|
||||
|
|
|
|||
|
|
@ -591,6 +591,8 @@
|
|||
"versionExistsDescription": "当前技能版本已经发布过,请修改 SKILL.md 中的 version 后重新打包上传。",
|
||||
"precheckFailedTitle": "发布前校验未通过",
|
||||
"precheckFailedDescription": "技能包中包含疑似密钥、令牌或密码内容。请将真实凭证替换为占位符后再重试。",
|
||||
"frontmatterFailedTitle": "SKILL.md 格式有误",
|
||||
"frontmatterFailedDescription": "请检查 SKILL.md 顶部 frontmatter 的 YAML 格式。若字段值中包含冒号,请用引号包裹。",
|
||||
"selectRequired": "请选择命名空间和文件"
|
||||
},
|
||||
"toast": {
|
||||
|
|
|
|||
|
|
@ -32,6 +32,15 @@ function isPrecheckFailureMessage(message?: string): boolean {
|
|||
|| message.includes('looks like a secret or token')
|
||||
}
|
||||
|
||||
function isFrontmatterFailureMessage(message?: string): boolean {
|
||||
if (!message) {
|
||||
return false
|
||||
}
|
||||
|
||||
return message.includes('Invalid SKILL.md frontmatter')
|
||||
|| message.includes('技能包校验失败:Invalid SKILL.md frontmatter')
|
||||
}
|
||||
|
||||
export function PublishPage() {
|
||||
const { t } = useTranslation()
|
||||
const navigate = useNavigate()
|
||||
|
|
@ -87,6 +96,14 @@ export function PublishPage() {
|
|||
return
|
||||
}
|
||||
|
||||
if (error instanceof ApiError && isFrontmatterFailureMessage(error.serverMessage || error.message)) {
|
||||
toast.error(
|
||||
t('publish.frontmatterFailedTitle'),
|
||||
error.serverMessage || t('publish.frontmatterFailedDescription'),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
toast.error(t('publish.error'), error instanceof Error ? error.message : '')
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue