From 32b23d684bf404cdc7208e9ede5a6a39469792c0 Mon Sep 17 00:00:00 2001 From: Manuel Andruccioli Date: Tue, 12 Mar 2024 12:37:14 +0100 Subject: [PATCH] feat(plugin): add first try of task --- .github/workflows/build-and-deploy.yml | 42 +++++---- .gitignore | 2 + build.gradle.kts | 5 +- release.config.js | 4 +- settings.gradle.kts | 2 +- .../gradle/CheckNodeTask.kt | 27 ++++++ .../zuccherosintattico/gradle/Typescript.kt | 14 +-- .../gradle/TypescriptPluginTest.kt | 52 +++++++++++ .../danilopianini/gradle/test/TestingDSL.kt | 75 ---------------- .../org/danilopianini/gradle/test/Tests.kt | 89 ------------------- .../gradle/test/test0/build.gradle.kts | 6 -- .../gradle/test/test0/settings.gradle.kts | 13 --- .../danilopianini/gradle/test/test0/test.yaml | 7 -- .../plugin-base-env/build.gradle.kts | 6 ++ .../src/main/typescript/index.ts | 10 +++ 15 files changed, 136 insertions(+), 218 deletions(-) create mode 100644 src/main/kotlin/io/github/zuccherosintattico/gradle/CheckNodeTask.kt create mode 100644 src/test/kotlin/io/github/zuccherosintattico/gradle/TypescriptPluginTest.kt delete mode 100644 src/test/kotlin/org/danilopianini/gradle/test/TestingDSL.kt delete mode 100644 src/test/kotlin/org/danilopianini/gradle/test/Tests.kt delete mode 100644 src/test/resources/org/danilopianini/gradle/test/test0/build.gradle.kts delete mode 100644 src/test/resources/org/danilopianini/gradle/test/test0/settings.gradle.kts delete mode 100644 src/test/resources/org/danilopianini/gradle/test/test0/test.yaml create mode 100644 src/test/resources/plugin-base-env/build.gradle.kts create mode 100644 src/test/resources/plugin-base-env/src/main/typescript/index.ts diff --git a/.github/workflows/build-and-deploy.yml b/.github/workflows/build-and-deploy.yml index 733112d..376cf00 100644 --- a/.github/workflows/build-and-deploy.yml +++ b/.github/workflows/build-and-deploy.yml @@ -17,25 +17,29 @@ jobs: - name: Checkout uses: DanySK/action-checkout@0.2.14 - uses: DanySK/build-check-deploy-gradle-action@2.4.6 + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} with: # Dry-deployment check-command: ./gradlew build --parallel - deploy-command: >- - ./gradlew - uploadKotlinOSSRHToMavenCentralNexus - uploadPluginMavenToMavenCentralNexus - uploadPluginMarkerMavenToMavenCentralNexus - close - drop - --parallel +# deploy-command: >- +# ./gradlew +# uploadKotlinOSSRHToMavenCentralNexus +# uploadPluginMavenToMavenCentralNexus +# uploadPluginMarkerMavenToMavenCentralNexus +# close +# drop +# --parallel should-run-codecov: ${{ runner.os == 'Linux' }} - should-deploy: >- - ${{ - runner.os == 'Linux' - && !github.event.repository.fork - && github.event_name != 'pull_request' - }} - maven-central-password: ${{ secrets.MAVEN_CENTRAL_PASSWORD }} + should-deploy: false +# should-deploy: >- +# ${{ +# runner.os == 'Linux' +# && !github.event.repository.fork +# && github.event_name != 'pull_request' +# }} +# maven-central-username: ${{ secrets.MAVEN_CENTRAL_USERNAME }} +# maven-central-password: ${{ secrets.MAVEN_CENTRAL_PASSWORD }} signing-key: ${{ secrets.SIGNING_KEY }} signing-password: ${{ secrets.SIGNING_PASSWORD }} release: @@ -54,8 +58,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4.1.1 - with: - token: ${{ secrets.DEPLOYMENT_TOKEN }} +# with: +# token: ${{ secrets.DEPLOYMENT_TOKEN }} - name: Find the version of Node from package.json id: node-version run: echo "version=$(jq -r .engines.node package.json)" >> $GITHUB_OUTPUT @@ -75,8 +79,8 @@ jobs: github-token: ${{ github.token }} gradle-publish-secret: ${{ secrets.GRADLE_PUBLISH_SECRET }} gradle-publish-key: ${{ secrets.GRADLE_PUBLISH_KEY }} - maven-central-username: 'zucchero-sintattico' - maven-central-password: ${{ secrets.MAVEN_CENTRAL_PASSWORD }} +# maven-central-username: ${{ secrets.MAVEN_CENTRAL_USERNAME }} +# maven-central-password: ${{ secrets.MAVEN_CENTRAL_PASSWORD }} signing-key: ${{ secrets.SIGNING_KEY }} signing-password: ${{ secrets.SIGNING_PASSWORD }} success: diff --git a/.gitignore b/.gitignore index 1c6753e..540dc3a 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ build/ .idea/ node_modules/ + +.DS_Store \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 463c9ba..5432f5b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -28,7 +28,7 @@ inner class ProjectInfo { val vcsUrl = "$website.git" val scm = "scm:git:$website.git" val pluginImplementationClass = "$group.gradle.Typescript" - val tags = listOf("typescript", "plugin", "gradle") + val tags = listOf("typescript", "compilation") } val info = ProjectInfo() @@ -110,10 +110,13 @@ signing { * Publication on Maven Central and the Plugin portal */ publishOnCentral { + configureMavenCentral.set(false) // Waiting for support for new central portal + projectLongName.set(info.longName) projectDescription.set(description ?: TODO("Missing description")) projectUrl.set(info.website) scmConnection.set(info.scm) + group = "io.github.zucchero-sintattico" repository("https://maven.pkg.github.com/zucchero-sintattico/${rootProject.name}".lowercase(), name = "github") { user.set(System.getenv("GITHUB_ACTOR")) password.set(System.getenv("GITHUB_TOKEN")) diff --git a/release.config.js b/release.config.js index 92bfc56..7ae11d9 100644 --- a/release.config.js +++ b/release.config.js @@ -1,5 +1,5 @@ -var publishCmd = ` -./gradlew uploadKotlinOSSRHToMavenCentralNexus uploadPluginMavenToMavenCentralNexus uploadPluginMarkerMavenToMavenCentralNexus release || exit 1 +// ./gradlew uploadKotlinOSSRHToMavenCentralNexus uploadPluginMavenToMavenCentralNexus uploadPluginMarkerMavenToMavenCentralNexus release || exit 1 +let publishCmd = ` ./gradlew publishPlugins -Pgradle.publish.key=$GRADLE_PUBLISH_KEY -Pgradle.publish.secret=$GRADLE_PUBLISH_SECRET || exit 2 ./gradlew publishKotlinOSSRHPublicationToGithubRepository publishPluginMavenPublicationToGithubRepository || true ` diff --git a/settings.gradle.kts b/settings.gradle.kts index 0c74aa2..cec27a8 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -14,7 +14,7 @@ gradleEnterprise { gitHooks { preCommit { - tasks("ktlintCheck") + tasks("ktlintCheck", "detekt", "--parallel") } commitMsg { conventionalCommits() } createHooks(true) diff --git a/src/main/kotlin/io/github/zuccherosintattico/gradle/CheckNodeTask.kt b/src/main/kotlin/io/github/zuccherosintattico/gradle/CheckNodeTask.kt new file mode 100644 index 0000000..da6182c --- /dev/null +++ b/src/main/kotlin/io/github/zuccherosintattico/gradle/CheckNodeTask.kt @@ -0,0 +1,27 @@ +package io.github.zuccherosintattico.gradle + +import org.gradle.api.DefaultTask +import org.gradle.api.tasks.TaskAction + +/** + * A task to check if Node is installed. + */ +open class CheckNodeTask : DefaultTask() { + init { + group = "Node" + description = "Check if Node is installed" + } + + /** + * The task action to check if Node is installed. + */ + @TaskAction + fun checkNode() { + val res = Runtime.getRuntime().runCatching { exec("node --version").waitFor() } + if (res.isSuccess) { + logger.quiet("Node is installed") + } else { + logger.error("Node is not installed") + } + } +} diff --git a/src/main/kotlin/io/github/zuccherosintattico/gradle/Typescript.kt b/src/main/kotlin/io/github/zuccherosintattico/gradle/Typescript.kt index d88b15c..4d5bbf8 100644 --- a/src/main/kotlin/io/github/zuccherosintattico/gradle/Typescript.kt +++ b/src/main/kotlin/io/github/zuccherosintattico/gradle/Typescript.kt @@ -20,8 +20,12 @@ import java.io.Serializable open class Typescript : Plugin { override fun apply(project: Project) { val extension = project.extensions.create("typescript") - project.tasks.register("hello") { - author.set(extension.author) + + val checkNodeTask = project.tasks.register("checkNode") + + project.tasks.register("compileTypescript") { + dependsOn(checkNodeTask) + sourceSet.set(extension.sourceSet) } } } @@ -35,13 +39,13 @@ open class TypescriptTask : DefaultTask() { * Just a template. */ @Input - val author: Property = project.objects.property() + val sourceSet: Property = project.objects.property() /** * Read-only property calculated from the greeting. */ @Internal - val message: Provider = author.map { "Hello from $it" } + val message: Provider = sourceSet.map { "Hello from $it" } /** * Just a template. @@ -60,7 +64,7 @@ open class TypescriptExtension(objects: ObjectFactory) : Serializable { /** * Just a template. */ - val author: Property = objects.property() + val sourceSet: Property = objects.property().convention("src/main/typescript") companion object { private const val serialVersionUID = 1L diff --git a/src/test/kotlin/io/github/zuccherosintattico/gradle/TypescriptPluginTest.kt b/src/test/kotlin/io/github/zuccherosintattico/gradle/TypescriptPluginTest.kt new file mode 100644 index 0000000..e364173 --- /dev/null +++ b/src/test/kotlin/io/github/zuccherosintattico/gradle/TypescriptPluginTest.kt @@ -0,0 +1,52 @@ +package io.github.zuccherosintattico.gradle + +import io.kotest.core.spec.style.AnnotationSpec +import io.kotest.matchers.paths.shouldExist +import org.gradle.testkit.runner.GradleRunner +import java.io.File +import java.nio.file.Files +import kotlin.io.path.createTempDirectory + +class TypescriptPluginTest : AnnotationSpec() { + + @BeforeAll + fun setup() { + val testkitProperties = javaClass.classLoader.getResource("testkit-gradle.properties")?.readText() + checkNotNull(testkitProperties) { + "No file testkit-gradle.properties was generated" + } + } + + @Test + fun `test the plugin`() { + val folder = createTempDirectory("test") + folder.shouldExist() + + val testResources = File("src/test/resources/plugin-base-env").toPath() + Files.walk(testResources) + .filter { it != testResources } + .map { testResources.relativize(it) } + .map { testResources.resolve(it) to folder.resolve(it) } + .forEach { (source, destination) -> + Files.createDirectories(destination.parent) + Files.copy(source, destination) + } + + println(folder.toFile()) + + val result = with(GradleRunner.create()) { + withProjectDir(folder.toFile()) + withArguments("compileTypescript", "--stacktrace") + withPluginClasspath() + build() + } + + result.output.lines().forEach { println(it) } + +// ProcessBuilder("npx", "tsc", "--outDir", "./build/bin", "src/main/typescript/*.ts") +// .directory(folder.toFile()) +// .inheritIO() +// .start() +// .waitFor() + } +} diff --git a/src/test/kotlin/org/danilopianini/gradle/test/TestingDSL.kt b/src/test/kotlin/org/danilopianini/gradle/test/TestingDSL.kt deleted file mode 100644 index f86479f..0000000 --- a/src/test/kotlin/org/danilopianini/gradle/test/TestingDSL.kt +++ /dev/null @@ -1,75 +0,0 @@ -package org.danilopianini.gradle.test - -import com.uchuhimo.konf.ConfigSpec -import org.gradle.internal.impldep.org.apache.commons.lang.StringUtils -import java.io.File - -object Root : ConfigSpec("") { - val tests by required>() -} - -data class Test( - val description: String, - val configuration: Configuration, - val expectation: Expectation, -) - -data class Configuration(val tasks: List, val options: List = emptyList()) - -@Suppress("ConstructorParameterNaming") -data class Expectation( - val file_exists: List = emptyList(), - val success: List = emptyList(), - val failure: List = emptyList(), - val output_contains: List = emptyList(), - val output_doesnt_contain: List = emptyList(), -) - -enum class Permission(private val hasPermission: File.() -> Boolean) { - R(File::canRead), W(File::canWrite), X(File::canExecute); - - fun requireOnFile(file: File) = require(file.hasPermission()) { - "File ${file.absolutePath} must have permission $name, but it does not." - } -} - -data class ExistingFile( - val name: String, - val findRegex: List = emptyList(), - val content: String? = null, - val trim: Boolean = false, - val permissions: List = emptyList(), -) { - fun validate(actualFile: File): Unit = with(actualFile) { - require(exists()) { - "File $name does not exist." - } - if (content != null) { - val text = readText() - require(text == content) { - """ - Content of $name does not match expectations. - - Expected: - $content - - Actual: - $text - - Difference starts at index ${StringUtils.indexOfDifference(content, text)}: - ${StringUtils.difference(content, text)} - """.trimIndent() - } - } - findRegex.forEach { regexString -> - val regex = Regex(regexString) - requireNotNull(readLines().find { regex.matches(it) }) { - """ - None of the lines in $name matches the regular expression $findRegex. File content: - ${readText()} - """.trimIndent() - } - } - permissions.forEach { it.requireOnFile(this) } - } -} diff --git a/src/test/kotlin/org/danilopianini/gradle/test/Tests.kt b/src/test/kotlin/org/danilopianini/gradle/test/Tests.kt deleted file mode 100644 index 3a3c9d5..0000000 --- a/src/test/kotlin/org/danilopianini/gradle/test/Tests.kt +++ /dev/null @@ -1,89 +0,0 @@ -package org.danilopianini.gradle.test - -import com.uchuhimo.konf.Config -import com.uchuhimo.konf.source.yaml -import io.github.classgraph.ClassGraph -import io.kotest.core.spec.style.StringSpec -import io.kotest.matchers.file.shouldBeAFile -import io.kotest.matchers.file.shouldExist -import io.kotest.matchers.shouldBe -import io.kotest.matchers.string.shouldContain -import io.kotest.matchers.string.shouldNotContain -import org.gradle.internal.impldep.org.junit.rules.TemporaryFolder -import org.gradle.testkit.runner.BuildResult -import org.gradle.testkit.runner.GradleRunner -import org.gradle.testkit.runner.TaskOutcome -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import java.io.File - -class Tests : StringSpec( - { - val testkitProperties = javaClass.classLoader.getResource("testkit-gradle.properties")?.readText() - checkNotNull(testkitProperties) { - "No file testkit-gradle.properties was generated" - } - val scan = ClassGraph() - .enableAllInfo() - .acceptPackages(Tests::class.java.`package`.name) - .scan() - scan.getResourcesWithLeafName("test.yaml") - .flatMap { resource -> - log.debug("Found test list in {}", resource) - val yamlFile = File(resource.classpathElementFile.absolutePath + "/" + resource.path) - val testConfiguration = Config { - addSpec(Root) - }.from.yaml.inputStream(resource.open()) - testConfiguration[Root.tests].map { it to yamlFile.parentFile } - } - .forEach { (test, location) -> - log.debug("Test to be executed: {} from {}", test, location) - val testFolder = folder { - location.copyRecursively(this.root) - } - log.debug("Test has been copied into {} and is ready to get executed", testFolder) - test.description { - File(testFolder.root, "gradle.properties").writeText(testkitProperties) - val result = GradleRunner.create() - .withProjectDir(testFolder.root) - .withPluginClasspath() - .withArguments(test.configuration.tasks + test.configuration.options) - .run { if (test.expectation.failure.isEmpty()) build() else buildAndFail() } - println(result.tasks) - println(result.output) - test.expectation.output_contains.forEach { - result.output shouldContain it - } - test.expectation.output_doesnt_contain.forEach { - result.output shouldNotContain it - } - test.expectation.success.forEach { - result.outcomeOf(it) shouldBe TaskOutcome.SUCCESS - } - test.expectation.failure.forEach { - result.outcomeOf(it) shouldBe TaskOutcome.FAILED - } - test.expectation.file_exists.forEach { - val file = File("${testFolder.root.absolutePath}/${it.name}").apply { - shouldExist() - shouldBeAFile() - } - it.validate(file) - } - } - } - }, -) { - companion object { - val log: Logger = LoggerFactory.getLogger(Tests::class.java) - - private fun BuildResult.outcomeOf(name: String) = checkNotNull(task(":$name")?.outcome) { - "Task $name was not present among the executed tasks" - } - - private fun folder(closure: TemporaryFolder.() -> Unit) = TemporaryFolder().apply { - create() - closure() - } - } -} diff --git a/src/test/resources/org/danilopianini/gradle/test/test0/build.gradle.kts b/src/test/resources/org/danilopianini/gradle/test/test0/build.gradle.kts deleted file mode 100644 index 8516131..0000000 --- a/src/test/resources/org/danilopianini/gradle/test/test0/build.gradle.kts +++ /dev/null @@ -1,6 +0,0 @@ -plugins { - id("org.danilopianini.template-for-gradle-plugins") -} -hello { - author.set("Danilo Pianini") -} diff --git a/src/test/resources/org/danilopianini/gradle/test/test0/settings.gradle.kts b/src/test/resources/org/danilopianini/gradle/test/test0/settings.gradle.kts deleted file mode 100644 index 18610f5..0000000 --- a/src/test/resources/org/danilopianini/gradle/test/test0/settings.gradle.kts +++ /dev/null @@ -1,13 +0,0 @@ -plugins { - id("com.gradle.enterprise") version "3.15.1" -} - -gradleEnterprise { - buildScan { - termsOfServiceUrl = "https://gradle.com/terms-of-service" - termsOfServiceAgree = "yes" - publishOnFailure() - } -} - -rootProject.name = "test0" diff --git a/src/test/resources/org/danilopianini/gradle/test/test0/test.yaml b/src/test/resources/org/danilopianini/gradle/test/test0/test.yaml deleted file mode 100644 index fa266e2..0000000 --- a/src/test/resources/org/danilopianini/gradle/test/test0/test.yaml +++ /dev/null @@ -1,7 +0,0 @@ -tests: - - description: "A greeting should get printed" - configuration: - tasks: hello - expectation: - output_contains: "Hello from Danilo Pianini" - success: hello diff --git a/src/test/resources/plugin-base-env/build.gradle.kts b/src/test/resources/plugin-base-env/build.gradle.kts new file mode 100644 index 0000000..8dcc6ce --- /dev/null +++ b/src/test/resources/plugin-base-env/build.gradle.kts @@ -0,0 +1,6 @@ +plugins { + id("io.github.zucchero-sintattico.typescript-gradle-plugin") +} +typescript { + sourceSet = "src/main/typescript" +} diff --git a/src/test/resources/plugin-base-env/src/main/typescript/index.ts b/src/test/resources/plugin-base-env/src/main/typescript/index.ts new file mode 100644 index 0000000..556cb96 --- /dev/null +++ b/src/test/resources/plugin-base-env/src/main/typescript/index.ts @@ -0,0 +1,10 @@ +class Person { + constructor(public name: string, public age: number) { + } + toString() { + return `${this.name} is ${this.age} years old`; + } +} + +let p = new Person("John", 42); +console.log(p.toString());