fix(skills): address QA round 19
QA19-1: In pyproject dependency parsing, treat Poetry/table-form keys as dependency names and only scan quoted requirement specs on [project] dependencies / optional-dependency array lines. Prevents extras like extras = ["fastapi"] from emitting dep:fastapi. QA19-2: Support legacy Gradle Spring Boot declaration styles via apply plugin: 'org.springframework.boot' and apply(plugin = ...). QA19-3: Scope Maven Spring Boot detection to org.springframework.boot groupId instead of any artifactId that happens to start with spring-boot. Add regression tests for: - Poetry dependency table extras mentioning fastapi - legacy apply plugin Spring Boot detection - Maven artifactId-only spring-boot false positive
This commit is contained in:
parent
d8443b89e5
commit
f860fe0a91
2 changed files with 57 additions and 10 deletions
|
|
@ -916,16 +916,22 @@ function extractRequirementName(spec: string): string | null {
|
|||
function containsFastapiInPyproject(content: string): boolean {
|
||||
for (const line of content.split("\n")) {
|
||||
const keyMatch = line.match(/^\s*([A-Za-z0-9_.-]+)\s*=/);
|
||||
if (keyMatch && normalizePackageName(keyMatch[1]) === "fastapi") {
|
||||
return true;
|
||||
if (keyMatch) {
|
||||
const key = normalizePackageName(keyMatch[1]);
|
||||
if (key === "fastapi") {
|
||||
return true;
|
||||
}
|
||||
if (key !== "dependencies") {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const quotedSpecRe = /["']([^"']+)["']/g;
|
||||
let match: RegExpExecArray | null;
|
||||
while ((match = quotedSpecRe.exec(content)) !== null) {
|
||||
if (extractRequirementName(match[1]) === "fastapi") {
|
||||
return true;
|
||||
const quotedSpecRe = /["']([^"']+)["']/g;
|
||||
let match: RegExpExecArray | null;
|
||||
while ((match = quotedSpecRe.exec(line)) !== null) {
|
||||
if (extractRequirementName(match[1]) === "fastapi") {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -934,11 +940,11 @@ function containsFastapiInPyproject(content: string): boolean {
|
|||
|
||||
function containsDirectSpringBootReference(relativePath: string, content: string): boolean {
|
||||
if (relativePath.endsWith("pom.xml")) {
|
||||
return /<groupId>\s*org\.springframework\.boot\s*<\/groupId>|<artifactId>\s*spring-boot[^<]*<\/artifactId>/i.test(content);
|
||||
return /<groupId>\s*org\.springframework\.boot\s*<\/groupId>/i.test(content);
|
||||
}
|
||||
|
||||
if (relativePath.endsWith("build.gradle") || relativePath.endsWith("build.gradle.kts")) {
|
||||
return /(id\s*\(?\s*["']org\.springframework\.boot["']|(?:implementation|api|compileOnly|runtimeOnly|testImplementation|annotationProcessor|kapt)\s*\(?\s*["'][^"']*org\.springframework\.boot:[^"']*spring-boot[^"']*["'])/i.test(content);
|
||||
return /(id\s*\(?\s*["']org\.springframework\.boot["']|apply\s*\(?\s*plugin\s*[:=]\s*["']org\.springframework\.boot["']|(?:implementation|api|compileOnly|runtimeOnly|testImplementation|annotationProcessor|kapt)\s*\(?\s*["'][^"']*org\.springframework\.boot:[^"']*spring-boot[^"']*["'])/i.test(content);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -808,6 +808,21 @@ test("detectProjectSignals: pyproject metadata mention does not trigger dep:fast
|
|||
}
|
||||
});
|
||||
|
||||
test("detectProjectSignals: pyproject dependency table extras do not trigger dep:fastapi", () => {
|
||||
const dir = makeTempDir("signals-fastapi-pyproject-table-extra");
|
||||
try {
|
||||
writeFileSync(
|
||||
join(dir, "pyproject.toml"),
|
||||
'[tool.poetry.dependencies]\npython = "^3.12"\nmy-sdk = { version = "^1.0", extras = ["fastapi"] }\n',
|
||||
"utf-8",
|
||||
);
|
||||
const signals = detectProjectSignals(dir);
|
||||
assert.ok(!signals.detectedFiles.includes("dep:fastapi"), "dependency table extras should not imply FastAPI framework usage");
|
||||
} finally {
|
||||
cleanup(dir);
|
||||
}
|
||||
});
|
||||
|
||||
test("detectProjectSignals: pyproject optional-dependency group name does not trigger dep:fastapi", () => {
|
||||
const dir = makeTempDir("signals-fastapi-pyproject-extra-name");
|
||||
try {
|
||||
|
|
@ -959,6 +974,17 @@ test("detectProjectSignals: nested Spring Boot Gradle service emits dep:spring-b
|
|||
}
|
||||
});
|
||||
|
||||
test("detectProjectSignals: legacy apply plugin syntax emits dep:spring-boot", () => {
|
||||
const dir = makeTempDir("signals-spring-apply-plugin");
|
||||
try {
|
||||
writeFileSync(join(dir, "build.gradle"), "apply plugin: 'org.springframework.boot'", "utf-8");
|
||||
const signals = detectProjectSignals(dir);
|
||||
assert.ok(signals.detectedFiles.includes("dep:spring-boot"), "apply plugin syntax should trigger Spring Boot detection");
|
||||
} finally {
|
||||
cleanup(dir);
|
||||
}
|
||||
});
|
||||
|
||||
test("detectProjectSignals: nested Spring Boot Kotlin DSL service still uses neutral java/kotlin language hint", () => {
|
||||
const dir = makeTempDir("signals-spring-gradle-kts-nested");
|
||||
try {
|
||||
|
|
@ -1013,6 +1039,21 @@ test("detectProjectSignals: build metadata mentioning spring-boot does not emit
|
|||
}
|
||||
});
|
||||
|
||||
test("detectProjectSignals: Maven artifactId alone does not emit dep:spring-boot", () => {
|
||||
const dir = makeTempDir("signals-spring-maven-artifact-only");
|
||||
try {
|
||||
writeFileSync(
|
||||
join(dir, "pom.xml"),
|
||||
'<project><modelVersion>4.0.0</modelVersion><groupId>com.example</groupId><artifactId>spring-boot-tools</artifactId></project>',
|
||||
"utf-8",
|
||||
);
|
||||
const signals = detectProjectSignals(dir);
|
||||
assert.ok(!signals.detectedFiles.includes("dep:spring-boot"), "artifactId alone should not imply Spring Boot");
|
||||
} finally {
|
||||
cleanup(dir);
|
||||
}
|
||||
});
|
||||
|
||||
test("detectProjectSignals: Spring Boot version-catalog alias emits dep:spring-boot", () => {
|
||||
const dir = makeTempDir("signals-spring-version-catalog");
|
||||
try {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue