Skip to content

Commit

Permalink
feat(github-actions): add workflow generation
Browse files Browse the repository at this point in the history
- Modify `GenerateGitHubActionsAction.kt` to create the necessary directories for GitHub Actions workflows.
- Add unit test template for generating GitHub Actions workflows.
- Modify `TemplateRender.kt` to render the GitHub Actions workflow template.
- Modify `TestCodeGenTask.kt` to use the GitHub Actions workflow template for generating workflows.
- Add `TestGenPromptContext` data class to hold the prompt context for generating GitHub Actions workflows.
  • Loading branch information
phodal committed Jan 16, 2024
1 parent 2f11110 commit 9c018ca
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 32 deletions.
Expand Up @@ -10,6 +10,7 @@ import cc.unitmesh.devti.provider.WriteTestService
import cc.unitmesh.devti.provider.context.*
import cc.unitmesh.devti.statusbar.AutoDevStatus
import cc.unitmesh.devti.statusbar.AutoDevStatusService
import cc.unitmesh.devti.template.TemplateRender
import com.intellij.lang.LanguageCommenters
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.ReadAction
Expand All @@ -23,9 +24,18 @@ import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
import kotlinx.coroutines.InternalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.runBlocking

data class TestGenPromptContext(
var language: String = "",
var imports: String = "",
var frameworkedContext: String = "",
var currentClass: String = "",
var relatedClasses: String = "",
var testClassName: String = "",
var isNewFile: Boolean = true,
)

class TestCodeGenTask(val request: TestCodeGenRequest) :
Task.Backgroundable(request.project, AutoDevBundle.message("intentions.chat.code.test.name")) {

Expand All @@ -36,6 +46,9 @@ class TestCodeGenTask(val request: TestCodeGenRequest) :
val commenter = LanguageCommenters.INSTANCE.forLanguage(request.file.language) ?: null
val comment = commenter?.lineCommentPrefix ?: "//"

val templateRender = TemplateRender("genius/code")
val template = templateRender.getTemplate("test-gen.vm")

override fun run(indicator: ProgressIndicator) {
indicator.isIndeterminate = true
indicator.fraction = 0.1
Expand All @@ -55,59 +68,42 @@ class TestCodeGenTask(val request: TestCodeGenRequest) :
return
}

var prompter = "Write unit test for following $lang code."

indicator.text = AutoDevBundle.message("intentions.chat.code.test.step.collect-context")
indicator.fraction = 0.3

val testPromptContext = TestGenPromptContext()

val creationContext =
ChatCreationContext(ChatOrigin.Intention, actionType, request.file, listOf(), element = request.element)

val contextItems: List<ChatContextItem> = runBlocking {
return@runBlocking ChatContextProvider.collectChatContextList(request.project, creationContext)
}

contextItems.forEach {
prompter += it.text + "\n"
}

prompter += "\n"
prompter += ReadAction.compute<String, Throwable> {
if (testContext.relatedClasses.isEmpty()) {
return@compute ""
}

val relatedClasses = testContext.relatedClasses.joinToString("\n") {
testPromptContext.frameworkedContext = contextItems.joinToString("\n", transform = ChatContextItem::text)
ReadAction.compute<Unit, Throwable> {
testPromptContext.relatedClasses = testContext.relatedClasses.joinToString("\n") {
it.format()
}.lines().joinToString("\n") {
"$comment $it"
}

"$comment here are related classes:\n$relatedClasses\n"
testPromptContext.currentClass =
runReadAction { testContext.currentClass?.format() }?.lines()?.joinToString("\n") {
"$comment $it"
} ?: ""
}

if (testContext.currentClass != null) {
val currentClassInfo = runReadAction { testContext.currentClass.format() }.lines().joinToString("\n") {
"$comment $it"
}
prompter += "\n$comment here is current class information:\n$currentClassInfo\n"
}

val importString = testContext.imports.joinToString("\n") {
testPromptContext.imports = testContext.imports.joinToString("\n") {
"$comment $it"
}
testPromptContext.isNewFile = testContext.isNewFile

prompter += "\nCode:\n$importString\n```${lang.lowercase()}\n${request.selectText}\n```\n"

prompter += if (!testContext.isNewFile) {
"\nStart test code with `@Test` syntax here: \n"
} else {
"\nStart ${testContext.testClassName} with `import` syntax here: \n"
}
templateRender.context = testPromptContext
val prompter = templateRender.renderTemplate(template)

logger<AutoTestThisIntention>().info("Prompt: $prompter")


indicator.fraction = 0.8
indicator.text = AutoDevBundle.message("intentions.request.background.process.title")

Expand Down
14 changes: 14 additions & 0 deletions src/main/kotlin/cc/unitmesh/devti/template/TemplateRender.kt
Expand Up @@ -45,6 +45,20 @@ class TemplateRender(pathPrefix: String) {

return messages
}

fun renderTemplate(template: String): String {
val oldContextClassLoader = Thread.currentThread().getContextClassLoader()
Thread.currentThread().setContextClassLoader(TemplateRender::class.java.getClassLoader())

velocityContext.put("context", context)
val sw = StringWriter()
Velocity.evaluate(velocityContext, sw, "#" + this.javaClass.name, template)
val result = sw.toString()

Thread.currentThread().setContextClassLoader(oldContextClassLoader)

return result
}
}

class TemplateNotFoundError(path: String) : Exception("Prompt not found at path: $path")
Expand Up @@ -44,7 +44,7 @@ class GenerateGitHubActionsAction : AnAction(AutoDevBundle.message("action.new.g
templateRender.context = DevOpsContext.from(githubActions)
val template = templateRender.getTemplate("generate-github-action.vm")

val dir = project.guessProjectDir()!!.toNioPath().resolve(".github").resolve("workflows")
project.guessProjectDir()!!.toNioPath().resolve(".github").resolve("workflows")
.createDirectories()

val msgs = templateRender.buildMsgs(template)
Expand Down
21 changes: 21 additions & 0 deletions src/main/resources/genius/code/test-gen.vm
@@ -0,0 +1,21 @@
Write unit test for following ${context.lang} code.

${context.frameworkedContext}

${context.relatedClasses}

#if( $context.currentClass.length() > 0 )
Here is current class information:
${context.currentClass}
#end

${context.imports}

## if newFile
#if( $context.isNewFile )
Start method test code here:
#else
Start ${context.testClassName} with `import` syntax here:
#end


0 comments on commit 9c018ca

Please sign in to comment.