Skip to content

Commit 0005d3d

Browse files
ryandensnahsra
authored andcommitted
♻️ extract build logic to task
1 parent 3b59863 commit 0005d3d

File tree

2 files changed

+198
-159
lines changed

2 files changed

+198
-159
lines changed
Lines changed: 6 additions & 159 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
import com.github.javaparser.JavaParser
2-
import com.google.gson.Gson
3-
import java.util.stream.Collectors
1+
import io.codemodder.docs.GenerateDocsTask
42

53
plugins {
64
id("io.codemodder.java")
@@ -12,162 +10,11 @@ plugins {
1210
* The documentation is generated from the codemod source files and the codemod resources directory.
1311
*
1412
* To run:
15-
* `./gradlew :core-codemods:generateDocs -PcodemodDocsDir=$DOCS/docs/codemods/java/`
13+
* `./gradlew :core-codemods:generateDocs --codemodDocsDir $DOCS/docs/codemods/java/`
1614
*/
17-
val generateDocs by tasks.registering {
18-
group = "custom"
15+
tasks.register<GenerateDocsTask>("generateDocs") {
16+
group = "documentation"
1917
description = "generate markdown docs for all codemods"
20-
21-
if (!project.properties.containsKey("codemodDocsDir")) {
22-
throw IllegalArgumentException("codemodDocsDir property is required")
23-
}
24-
25-
val codemodDocsDir = file(project.properties["codemodDocsDir"]!!)
26-
println("Using docs directory: $codemodDocsDir")
27-
if (!codemodDocsDir.exists()) {
28-
throw IllegalArgumentException("codemodDocsDir does not exist")
29-
}
30-
31-
if (codemodDocsDir.list().isEmpty()) {
32-
println("Docs directory was empty")
33-
}
34-
35-
val javaMainSources = java.sourceSets.main.get().java.srcDirs
36-
val defaultCodemodSource = file("src/main/java/io/codemodder/codemods/DefaultCodemods.java").readText()
37-
38-
// for every codemod in the project
39-
for (javaMainSource in javaMainSources) {
40-
val codemodFiles = javaMainSource.walkTopDown().filter { it.name.endsWith("Codemod.java") }
41-
for (codemodFile in codemodFiles) {
42-
val codemodName = codemodFile.nameWithoutExtension
43-
44-
// check if it's in the DefaultCodemods list -- we don't do docs for the others
45-
if (!defaultCodemodSource.contains(codemodName)) {
46-
println("Skipping $codemodName")
47-
continue
48-
} else {
49-
println("Processing $codemodName")
50-
}
51-
val parsedJavaFile = JavaParser().parse(codemodFile).result
52-
val codemodClass = parsedJavaFile.get().types[0]
53-
54-
// get the codemod annotation which has some metadata
55-
val codemodAnnotation = codemodClass.annotations
56-
.stream()
57-
.filter { it.nameAsString == "Codemod" }
58-
.findFirst()
59-
.get()
60-
.asNormalAnnotationExpr()
61-
62-
// get the name and other metadata
63-
val annotationParameters = codemodAnnotation.pairs
64-
val id = annotationParameters.stream()
65-
.filter { it.nameAsString == "id" }
66-
.findFirst()
67-
.get()
68-
.value
69-
.asStringLiteralExpr()
70-
.value
71-
72-
val fileName = id.replace(":", "_").replace("/", "_") + ".md"
73-
74-
val importance = annotationParameters.stream()
75-
.filter { it.nameAsString == "importance" }
76-
.findFirst()
77-
.get()
78-
.value
79-
.asFieldAccessExpr()
80-
.nameAsString
81-
82-
val mergeGuidance = annotationParameters.stream()
83-
.filter { it.nameAsString == "reviewGuidance" }
84-
.findFirst()
85-
.get()
86-
.value
87-
.asFieldAccessExpr()
88-
.toString()
89-
90-
// the other metadata is in the resources
91-
val resourceDir = file("src/main/resources/io/codemodder/codemods/$codemodName")
92-
val description = resourceDir.resolve("description.md").readText()
93-
val reportJson = resourceDir.resolve("report.json").readText()
94-
val report = Gson().fromJson(reportJson, Map::class.java)
95-
96-
val summary = report["summary"] as String
97-
98-
// add the scanning tool to the summary
99-
var needsScanningTool = "No"
100-
if (id.startsWith("sonar")) {
101-
needsScanningTool = "Yes (Sonar)"
102-
} else if (id.startsWith("codeql")) {
103-
needsScanningTool = "Yes (CodeQL)"
104-
} else if (id.startsWith("semgrep")) {
105-
needsScanningTool = "Yes (Semgrep)"
106-
}
107-
108-
// get the merge advice
109-
var mergeGuidanceStr = "Merge After Review"
110-
if (mergeGuidance == "ReviewGuidance.MERGE_WITHOUT_REVIEW") {
111-
mergeGuidanceStr = "Merge Without Review"
112-
} else if (mergeGuidance == "ReviewGuidance.MERGE_AFTER_CURSORY_REVIEW") {
113-
mergeGuidanceStr = "Merge After Cursory Review"
114-
}
115-
116-
val mergeGuidanceJustification = report["reviewGuidanceIJustification"]
117-
118-
val faqs = report["faqs"]
119-
120-
val references = report["references"] as List<String>
121-
val referencesStr = references.stream().map { " * [$it]($it)" }.collect(Collectors.joining("\n"))
122-
123-
val codemodDocsFile = file("$codemodDocsDir/$fileName")
124-
125-
// escape the quotes in summary
126-
val escapedSummary = summary.replace("\"", "\\\"")
127-
var doc = """
128-
---
129-
title: "$escapedSummary"
130-
sidebar_position: 1
131-
---
132-
133-
## $id
134-
135-
| Importance | Review Guidance | Requires Scanning Tool |
136-
|-------------|----------------------|------------------------|
137-
| $importance | $mergeGuidanceStr | $needsScanningTool |
138-
139-
""".trimIndent()
140-
141-
doc += "\n$description\n"
142-
143-
val hasFaq = mergeGuidanceJustification != null || faqs != null
144-
145-
if (hasFaq) {
146-
doc += "## F.A.Q.\n\n"
147-
}
148-
149-
if (mergeGuidanceJustification != null) {
150-
doc += "### Why is this codemod marked as $mergeGuidanceStr?\n\n"
151-
doc += "$mergeGuidanceJustification\n\n"
152-
}
153-
154-
if (faqs != null) {
155-
faqs as List<Map<String, String>>
156-
for (faq in faqs) {
157-
doc += """
158-
### ${faq["question"]}
159-
160-
${faq["answer"]}
161-
162-
""".trimIndent()
163-
doc += "\n"
164-
}
165-
}
166-
167-
doc += "\n## References\n"
168-
doc += referencesStr
169-
doc += "\n" // file editors like a trailing newline
170-
codemodDocsFile.writeText(doc)
171-
}
172-
}
18+
javaMainSources.from(java.sourceSets.main.get().java.srcDirs)
19+
defaultCodemodSource.set(layout.projectDirectory.file("src/main/java/io/codemodder/codemods/DefaultCodemods.java"))
17320
}
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
package io.codemodder.docs
2+
3+
import com.github.javaparser.JavaParser
4+
import com.google.gson.Gson
5+
import org.gradle.api.DefaultTask
6+
import org.gradle.api.file.*
7+
import org.gradle.api.tasks.Input
8+
import org.gradle.api.tasks.InputDirectory
9+
import org.gradle.api.tasks.InputFile
10+
import org.gradle.api.tasks.InputFiles
11+
import org.gradle.api.tasks.Internal
12+
import org.gradle.api.tasks.TaskAction
13+
import org.gradle.api.tasks.options.Option
14+
import org.gradle.internal.enterprise.test.FileProperty
15+
import java.util.stream.Collectors
16+
import javax.inject.Inject
17+
18+
/**
19+
* A task to generate documentation for the codemodder plugin
20+
*
21+
* This task requires the user to specify command line flag `codemodDocsDir` to specify the directory to write the docs
22+
*/
23+
abstract class GenerateDocsTask : DefaultTask() {
24+
25+
@get:Internal
26+
abstract val codemodDocsDir: DirectoryProperty
27+
28+
@get:InputFiles
29+
abstract val javaMainSources: ConfigurableFileCollection
30+
31+
@get:InputFile
32+
abstract val defaultCodemodSource: RegularFileProperty
33+
34+
@Option(option = "codemodDocsDir", description = "The dir to write the docs to")
35+
fun setCodemodderDocsDir(value: String) {
36+
codemodDocsDir.set(project.file(value))
37+
}
38+
39+
@get:Inject
40+
abstract val projectLayout: ProjectLayout
41+
42+
@TaskAction
43+
fun generateDocs() {
44+
val codemodDocsDir = this.codemodDocsDir.get().asFile
45+
if (!codemodDocsDir.exists()) {
46+
throw IllegalArgumentException("codemodDocsDir does not exist")
47+
}
48+
49+
if (codemodDocsDir.list().isEmpty()) {
50+
println("Docs directory was empty")
51+
}
52+
53+
val defaultCodemodSource = projectLayout.projectDirectory.file("src/main/java/io/codemodder/codemods/DefaultCodemods.java").asFile.readText()
54+
55+
56+
for (javaMainSource in javaMainSources) {
57+
val codemodFiles = javaMainSource.walkTopDown().filter { it.name.endsWith("Codemod.java") }
58+
for (codemodFile in codemodFiles) {
59+
val codemodName = codemodFile.nameWithoutExtension
60+
61+
// check if it's in the DefaultCodemods list -- we don't do docs for the others
62+
if (!defaultCodemodSource.contains(codemodName)) {
63+
println("Skipping $codemodName")
64+
continue
65+
} else {
66+
println("Processing $codemodName")
67+
}
68+
val parsedJavaFile = JavaParser().parse(codemodFile).result
69+
val codemodClass = parsedJavaFile.get().types[0]
70+
71+
// get the codemod annotation which has some metadata
72+
val codemodAnnotation = codemodClass.annotations
73+
.stream()
74+
.filter { it.nameAsString == "Codemod" }
75+
.findFirst()
76+
.get()
77+
.asNormalAnnotationExpr()
78+
79+
// get the name and other metadata
80+
val annotationParameters = codemodAnnotation.pairs
81+
val id = annotationParameters.stream()
82+
.filter { it.nameAsString == "id" }
83+
.findFirst()
84+
.get()
85+
.value
86+
.asStringLiteralExpr()
87+
.value
88+
89+
val fileName = id.replace(":", "_").replace("/", "_") + ".md"
90+
91+
val importance = annotationParameters.stream()
92+
.filter { it.nameAsString == "importance" }
93+
.findFirst()
94+
.get()
95+
.value
96+
.asFieldAccessExpr()
97+
.nameAsString
98+
99+
val mergeGuidance = annotationParameters.stream()
100+
.filter { it.nameAsString == "reviewGuidance" }
101+
.findFirst()
102+
.get()
103+
.value
104+
.asFieldAccessExpr()
105+
.toString()
106+
107+
// the other metadata is in the resources
108+
val resourceDir = projectLayout.projectDirectory.dir("src/main/resources/io/codemodder/codemods/$codemodName")
109+
val description = resourceDir.file("description.md").asFile.readText()
110+
val reportJson = resourceDir.file("report.json").asFile.readText()
111+
val report = Gson().fromJson(reportJson, Map::class.java)
112+
113+
val summary = report["summary"] as String
114+
115+
// add the scanning tool to the summary
116+
var needsScanningTool = "No"
117+
if (id.startsWith("sonar")) {
118+
needsScanningTool = "Yes (Sonar)"
119+
} else if (id.startsWith("codeql")) {
120+
needsScanningTool = "Yes (CodeQL)"
121+
} else if (id.startsWith("semgrep")) {
122+
needsScanningTool = "Yes (Semgrep)"
123+
}
124+
125+
// get the merge advice
126+
var mergeGuidanceStr = "Merge After Review"
127+
if (mergeGuidance == "ReviewGuidance.MERGE_WITHOUT_REVIEW") {
128+
mergeGuidanceStr = "Merge Without Review"
129+
} else if (mergeGuidance == "ReviewGuidance.MERGE_AFTER_CURSORY_REVIEW") {
130+
mergeGuidanceStr = "Merge After Cursory Review"
131+
}
132+
133+
val mergeGuidanceJustification = report["reviewGuidanceIJustification"]
134+
135+
val faqs = report["faqs"]
136+
137+
val references = report["references"] as List<String>
138+
val referencesStr = references.stream().map { " * [$it]($it)" }.collect(Collectors.joining("\n"))
139+
140+
val codemodDocsFile = projectLayout.projectDirectory.file("$codemodDocsDir/$fileName")
141+
142+
// escape the quotes in summary
143+
val escapedSummary = summary.replace("\"", "\\\"")
144+
var doc = """
145+
---
146+
title: "$escapedSummary"
147+
sidebar_position: 1
148+
---
149+
150+
## $id
151+
152+
| Importance | Review Guidance | Requires Scanning Tool |
153+
|-------------|----------------------|------------------------|
154+
| $importance | $mergeGuidanceStr | $needsScanningTool |
155+
156+
""".trimIndent()
157+
158+
doc += "\n$description\n"
159+
160+
val hasFaq = mergeGuidanceJustification != null || faqs != null
161+
162+
if (hasFaq) {
163+
doc += "## F.A.Q.\n\n"
164+
}
165+
166+
if (mergeGuidanceJustification != null) {
167+
doc += "### Why is this codemod marked as $mergeGuidanceStr?\n\n"
168+
doc += "$mergeGuidanceJustification\n\n"
169+
}
170+
171+
if (faqs != null) {
172+
faqs as List<Map<String, String>>
173+
for (faq in faqs) {
174+
doc += """
175+
### ${faq["question"]}
176+
177+
${faq["answer"]}
178+
179+
""".trimIndent()
180+
doc += "\n"
181+
}
182+
}
183+
184+
doc += "\n## References\n"
185+
doc += referencesStr
186+
doc += "\n" // file editors like a trailing newline
187+
codemodDocsFile.asFile.writeText(doc)
188+
}
189+
}
190+
}
191+
192+
}

0 commit comments

Comments
 (0)