Skip to content

Commit

Permalink
chore: generate code on the fly without storing in repo (#1704)
Browse files Browse the repository at this point in the history
Part of #1701.
  • Loading branch information
krzema12 authored Nov 1, 2024
1 parent 1ee6b7f commit ae1e045
Show file tree
Hide file tree
Showing 14 changed files with 58 additions and 2,040 deletions.
47 changes: 0 additions & 47 deletions .github/workflows/check-if-generated-code-up-to-date.main.kts

This file was deleted.

67 changes: 0 additions & 67 deletions .github/workflows/check-if-generated-code-up-to-date.yaml

This file was deleted.

12 changes: 2 additions & 10 deletions automation/code-generator/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,11 @@ plugins {
buildsrc.convention.`kotlin-jvm`

kotlin("plugin.serialization")

application
}

dependencies {
implementation("com.google.devtools.ksp:symbol-processing-api:2.0.21-1.0.26")
implementation("com.squareup:kotlinpoet:2.0.0")
implementation("com.squareup:kotlinpoet-ksp:2.0.0")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3")
}

application {
mainClass.set("io.github.typesafegithub.workflows.codegenerator.GenerationEntryPointKt")
}

tasks.run.configure {
workingDir(rootProject.layout.projectDirectory)
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package io.github.typesafegithub.workflows.codegenerator

import com.google.devtools.ksp.processing.CodeGenerator
import com.google.devtools.ksp.processing.Resolver
import com.google.devtools.ksp.processing.SymbolProcessor
import com.google.devtools.ksp.symbol.KSAnnotated
import io.github.typesafegithub.workflows.dsl.expressions.generateEventPayloads

class KspGenerator(
private val codeGenerator: CodeGenerator,
) : SymbolProcessor {
private var wasRun = false

override fun process(resolver: Resolver): List<KSAnnotated> {
if (!wasRun) {
generateEventPayloads(codeGenerator = codeGenerator)
wasRun = true
}
return emptyList()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package io.github.typesafegithub.workflows.codegenerator

import com.google.devtools.ksp.processing.SymbolProcessor
import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
import com.google.devtools.ksp.processing.SymbolProcessorProvider

class KspGeneratorProvider : SymbolProcessorProvider {
override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor =
KspGenerator(
codeGenerator = environment.codeGenerator,
)
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.github.typesafegithub.workflows.dsl.expressions

import com.google.devtools.ksp.processing.CodeGenerator
import com.squareup.kotlinpoet.AnnotationSpec
import com.squareup.kotlinpoet.ClassName
import com.squareup.kotlinpoet.FileSpec
Expand All @@ -8,30 +9,26 @@ import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
import com.squareup.kotlinpoet.PropertySpec
import com.squareup.kotlinpoet.TypeSpec
import com.squareup.kotlinpoet.asClassName
import com.squareup.kotlinpoet.ksp.writeTo
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
import java.io.File
import kotlin.io.path.Path
import kotlin.io.path.invariantSeparatorsPathString

fun main() {
generateEventPayloads()
}

/*
/**
* Generate type-safe accessors for GitHub Event payloads
* The payloads depend on the kind of the event: pull request, push, ...
*
* We read event payloads from binding-generate/src/test/resources/payloads
* We generate it inside github-workflows-kt/src/main/kotlin/io/github/typesafegithub/workflows/expressions/contexts
* The generated files are added using KSP's [codeGenerator].
*
* The JSONs come from https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads
* Feel free to add any payload you might need from that page
* */
fun generateEventPayloads() {
println("== generateEventPayloads()")
println("GitHubContext subclass will be generated in ${kotlinGenDir.absolutePath}")
*/
fun generateEventPayloads(codeGenerator: CodeGenerator) {
val params =
listOf(
PayloadEventParams("PushEvent", "event-push.json"),
Expand All @@ -41,15 +38,14 @@ fun generateEventPayloads() {
)
params
.map { it.generateTypesafePayload() }
.forEach { fileSpec -> fileSpec.writeTo(kotlinGenDir) }
println("\n")
.forEach { fileSpec ->
fileSpec.writeTo(codeGenerator = codeGenerator, aggregating = false)
}
}

private val EXPRESSIONS = "io.github.typesafegithub.workflows.dsl.expressions"
private val PACKAGE = "$EXPRESSIONS.contexts"
private val resourcesDir = File("automation/code-generator/src/main/resources/payloads")

private val kotlinGenDir = File("github-workflows-kt/src/gen/kotlin")
private val resourcesDir = Path("/payloads")

// ClassNames
private val fakeList = ClassName(EXPRESSIONS, "FakeList")
Expand All @@ -61,23 +57,22 @@ private val annotationFileSuppress =
.addMember("%S", "ObjectPropertyNaming")
.build()

private val fileComment = "File auto-generated by :gradlew code-generator:run"
private val fileComment = "File auto-generated by KSP"

data class PayloadEventParams(
val className: String,
val path: String,
) {
val jsonFile = resourcesDir.resolve(path)
val jsonFile = resourcesDir.resolve(path).invariantSeparatorsPathString

init {
check(className == className.toPascalCase())
check(jsonFile.canRead()) { "Can't read ${jsonFile.canonicalPath}" }
}
}

fun PayloadEventParams.generateTypesafePayload(): FileSpec {
println("Parsing ${jsonFile.canonicalPath}")
val element: JsonObject = Json.parseToJsonElement(jsonFile.readText()) as JsonObject
val jsonFileContent = this::class.java.getResource(jsonFile)?.readText() ?: error("Cannot read $jsonFile!")
val element: JsonObject = Json.parseToJsonElement(jsonFileContent) as JsonObject
val objects: Map<String, JsonObject> = findAllObjects(element, "event")
val fileSpec = generateObjectTypes(objects, className)
return fileSpec
Expand Down Expand Up @@ -133,7 +128,6 @@ fun JsonObject.generateObjectType(
key: String,
filename: String,
): TypeSpec {
println("Generating class ${payloadClassName(key, filename)} : ExpressionContext(\"github.$key\")")
val properties = mapNotNull { it.generatePropertySpec(key, filename) }
return TypeSpec
.objectBuilder(payloadClassName(key, filename))
Expand Down Expand Up @@ -167,7 +161,6 @@ fun Map.Entry<String, JsonElement>.generatePropertySpec(
.build()
}
else -> {
println("Warning: unhandled $child")
null
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
io.github.typesafegithub.workflows.codegenerator.KspGeneratorProvider
12 changes: 3 additions & 9 deletions github-workflows-kt/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ plugins {
buildsrc.convention.`duplicate-versions`

kotlin("plugin.serialization")
id("com.google.devtools.ksp") version "2.0.21-1.0.26"

// Code quality.
id("io.gitlab.arturbosch.detekt")
Expand All @@ -24,6 +25,7 @@ dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:1.7.3")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3")
implementation(projects.sharedInternal)
ksp(projects.automation.codeGenerator)

testImplementation("dev.zacsweers.kctfork:core:0.5.1")
// Needed to use the right version of the compiler for the libraries that depend on it.
Expand All @@ -38,14 +40,6 @@ dependencies {
testImplementation("EndBug:add-and-commit:v9")
}

sourceSets {
main {
java {
setSrcDirs(listOf("src/gen/kotlin"))
}
}
}

tasks.withType<KotlinCompile> {
compilerOptions {
freeCompilerArgs.addAll(
Expand All @@ -68,7 +62,7 @@ kotlin {
}

fun ConfigurableKtLintTask.kotlinterConfig() {
exclude { it.file.invariantSeparatorsPath.contains("/gen/") }
exclude { it.file.invariantSeparatorsPath.contains("/generated/") }
}

tasks.lintKotlinMain {
Expand Down
Loading

0 comments on commit ae1e045

Please sign in to comment.