Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion PROJECT.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,15 @@ Embed Code configuration file and do not need to install the executable or Kotli
- `version.gradle.kts`: the plugin version and default Embed Code application version.
- `gradle.properties`: Gradle runtime, parallelism, and configuration-cache settings,
plus Kotlin style and dependency defaults.
- `buildSrc/`: build settings, dependency coordinates, and the shared `jvm-module` convention.
- `buildSrc/`: build settings, dependency coordinates, and the shared JVM and Detekt convention.
- `gradle-plugin/build.gradle.kts`: plugin declaration, generated version source, functional
test source set, publication metadata, and Plugin Portal configuration.
- `gradle-plugin/src/main/kotlin/`: extension, plugin, task, platform, version, JSON, checksum,
download, installation, and execution logic.
- `gradle-plugin/src/main/templates/`: generated default-version source template.
- `gradle-plugin/src/test/kotlin/`: focused unit specifications.
- `gradle-plugin/src/functionalTest/kotlin/`: TestKit consumer-build specifications.
- `config/detekt/`: project-specific Detekt rules and the baseline for existing findings.
- `scripts/check_agent_config.py` and `scripts/tests/`: deterministic validation and tests.
- `.github/workflows/check.yml`: agent configuration and Ubuntu and Windows build verification.
- `.agents/skills/`: repository engineering, test, writing, review, and security workflows.
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,8 @@ Run compilation, plugin validation, and the complete test suite:
./gradlew check
```

Fast unit tests run under `test`. TestKit coverage runs separately under
`functionalTest`; the `check` task includes both.
Fast unit tests run under `test`, TestKit coverage runs under `functionalTest`, and Detekt static
analysis runs under `detekt`. The `check` task includes all three.

To test the plugin in another project, publish it to the local Maven repository:

Expand Down
11 changes: 11 additions & 0 deletions buildSrc/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,23 @@ val kotlinVersion = "2.4.10"
*/
val pluginPublishVersion = "2.1.1"

/**
* Version of the Detekt Gradle plugin.
*
* `buildSrc` needs this version before its dependency objects are compiled.
* Keep in sync with `io.spine.embedcode.gradle.dependency.Detekt.version`.
*/
val detektVersion = "2.0.0-alpha.5"

dependencies {
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion")
implementation(
"com.gradle.plugin-publish:com.gradle.plugin-publish.gradle.plugin:" +
pluginPublishVersion,
)
implementation(
"dev.detekt:dev.detekt.gradle.plugin:$detektVersion",
)
}

kotlin {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright 2026, TeamDev. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Redistribution and use in source and/or binary forms, with or without
* modification, must retain the above copyright notice and the following
* disclaimer.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package io.spine.embedcode.gradle.dependency

/**
* Detekt static-analysis plugin used by JVM modules.
*/
object Detekt {

// The alpha version is used, because latest stable version doesn't support JDK 25.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit (wording, optional): "because latest stable version doesn't support JDK 25" reads more naturally as "because the latest stable version does not support JDK 25" (add the article; the Spine writing style also tends to avoid contractions in doc prose).

const val version = "2.0.0-alpha.5"
}
22 changes: 22 additions & 0 deletions buildSrc/src/main/kotlin/jvm-module.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,17 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

import dev.detekt.gradle.Detekt as DetektTask
import dev.detekt.gradle.extensions.DetektExtension
import io.spine.embedcode.gradle.BuildSettings
import io.spine.embedcode.gradle.dependency.Detekt
import io.spine.embedcode.gradle.dependency.JUnit
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion

plugins {
`java-library`
id("dev.detekt")
kotlin("jvm")
}

Expand Down Expand Up @@ -58,6 +62,24 @@ tasks.withType<JavaCompile>().configureEach {
options.release.set(BuildSettings.bytecodeVersion)
}

extensions.configure<DetektExtension> {
toolVersion.set(Detekt.version)
config.setFrom(rootProject.layout.projectDirectory.file("config/detekt/detekt.yml"))
baseline.set(rootProject.layout.projectDirectory.file("config/detekt/baseline.xml"))
buildUponDefaultConfig.set(true)
source.setFrom(
files(
"src/main/kotlin",
"src/test/kotlin",
"src/functionalTest/kotlin",
),
)
}

tasks.withType<DetektTask>().configureEach {
jvmTarget.set(BuildSettings.bytecodeVersion.toString())
}

dependencies {
testImplementation(JUnit.Jupiter.lib)
testRuntimeOnly(JUnit.PlatformLauncher.lib)
Expand Down
20 changes: 20 additions & 0 deletions config/detekt/baseline.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.0" ?>
<SmellBaseline>
<ManuallySuppressedIssues/>
<CurrentIssues>
<ID>LongMethod:EmbedCodePlugin.kt:EmbedCodePlugin$override fun apply</ID>
<ID>LongMethod:InstallEmbedCodeTask.kt:InstallEmbedCodeTask$@TaskAction public fun install</ID>
<ID>MagicNumber:InstallEmbedCodeTask.kt:InstallEmbedCodeTask.Companion$200</ID>
<ID>MagicNumber:InstallEmbedCodeTask.kt:InstallEmbedCodeTask.Companion$299</ID>
<ID>ReturnCount:Checksum.kt:internal fun githubReleaseApi: URI?</ID>
<ID>ReturnCount:InstallEmbedCodeTask.kt:InstallEmbedCodeTask$@TaskAction public fun install</ID>
<ID>ReturnCount:InstallEmbedCodeTask.kt:InstallEmbedCodeTask$private fun installFromVerifiedAsset: Boolean</ID>
<ID>ReturnCount:InstallEmbedCodeTask.kt:InstallEmbedCodeTask$private fun isLocallyVerifiedExecutable: Boolean</ID>
<ID>ReturnCount:InstallEmbedCodeTask.kt:InstallEmbedCodeTask$private fun isPreviouslyVerifiedInstallation: Boolean</ID>
<ID>ThrowsCount:Checksum.kt:internal fun parseGitHubAssetSha256: String</ID>
<ID>ThrowsCount:EmbedCodeVersion.kt:internal fun validateVersion: String</ID>
<ID>ThrowsCount:InstallEmbedCodeTask.kt:InstallEmbedCodeTask$@TaskAction public fun install</ID>
<ID>ThrowsCount:InstallEmbedCodeTask.kt:InstallEmbedCodeTask.Companion$fun createDirectoriesSafely</ID>
<ID>TooManyFunctions:InstallEmbedCodeTask.kt:InstallEmbedCodeTask$Companion</ID>
</CurrentIssues>
</SmellBaseline>
38 changes: 38 additions & 0 deletions config/detekt/detekt.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
naming:
ClassNaming:
excludes: &testFiles
- "**/*Test.kt"
- "**/*Spec.kt"
MatchingDeclarationName:
excludes: *testFiles
FunctionNaming:
excludes: *testFiles

style:
UnusedPrivateFunction:
allowedNames: '(_|ignored|expected|serialVersionUID|about|ABOUT)'
UnusedPrivateProperty:
allowedNames: '(_|ignored|expected|serialVersionUID|about|ABOUT)'
MagicNumber:
excludes: *testFiles
ignoreNumbers:
- '-1'
- '0'
- '1'
- '2'
- '3'
MaxLineLength:
maxLineLength: 100
excludeCommentStatements: true
ForbiddenComment:
allowedPatterns: 'TODO:'

complexity:
LargeClass:
excludes: *testFiles
TooManyFunctions:
excludes:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit (consistency): TooManyFunctions.excludes re-lists the test-file globs literally, whereas every other rule in this file reuses the *testFiles YAML anchor defined on ClassNaming (line 3). Using excludes: *testFiles here too keeps the exclusion set defined in a single place, so future changes to which files count as tests only need one edit.

- '**/*Test.kt'
- '**/*Spec.kt'
LongMethod:
excludes: *testFiles
Original file line number Diff line number Diff line change
Expand Up @@ -1189,8 +1189,9 @@ internal class EmbedCodePluginSpec {
server.createContext("/releases/download/") { exchange ->
val relativePath = exchange.requestURI.path.removePrefix("/releases/download/")
downloads.incrementAndGet()
val asset = releaseDirectory.resolve("download").resolve(relativePath).normalize()
if (!asset.startsWith(releaseDirectory.resolve("download")) || !Files.isRegularFile(asset)) {
val downloadDirectory = releaseDirectory.resolve("download")
val asset = downloadDirectory.resolve(relativePath).normalize()
if (!asset.startsWith(downloadDirectory) || !Files.isRegularFile(asset)) {
exchange.sendResponseHeaders(404, -1)
} else {
val content = Files.readAllBytes(asset)
Expand Down