Skip to content
Merged
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
56 changes: 56 additions & 0 deletions .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: Coverage

on:
pull_request:
push:
branches:
- master

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
coverage:
runs-on: ubuntu-latest
timeout-minutes: 15

steps:
- name: Checkout Repository
uses: actions/checkout@v7
with:
fetch-depth: 0

- name: Set Up Java 17 for Compatibility Tests
uses: actions/setup-java@v5
with:
distribution: temurin
java-version: 17

- name: Save Java 17 Toolchain
run: echo "EMBED_CODE_JAVA_17_HOME=$JAVA_HOME" >> "$GITHUB_ENV"

- name: Set Up Java 25
uses: actions/setup-java@v5
with:
distribution: temurin
java-version: 25

- name: Set Up Gradle
uses: gradle/actions/setup-gradle@v6

- name: Generate Test Coverage
run: >-
./gradlew
-Dorg.gradle.java.installations.paths="$EMBED_CODE_JAVA_17_HOME"
--no-configuration-cache
:gradle-plugin:jacocoTestReport

- name: Upload Coverage
uses: codecov/codecov-action@v7
with:
files: ./gradle-plugin/build/reports/jacoco/test/jacocoTestReport.xml
token: ${{ secrets.CODECOV_TOKEN }}
6 changes: 6 additions & 0 deletions PROJECT.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ Embed Code configuration file and do not need to install the executable or Kotli
- `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.
- `.github/workflows/coverage.yml`: JaCoCo report generation and Codecov upload, using the patch
target defined in `codecov.yml`.
- `.claude/commands/`: thin Claude slash-command entry points for deliberate workflows.
- `.agents/guidelines/`: shared writing, English-language, and project-ownership rules.
- `.agents/skills/`: repository engineering, test, writing, review, and security workflows.
Expand Down Expand Up @@ -87,6 +89,10 @@ version numbers into agent guidance where a durable source path is sufficient.
- Keep tests offline with deterministic fixtures, temporary directories, and loopback HTTP servers.
- Use `./gradlew test` for unit tests, `./gradlew functionalTest` for TestKit tests, and
`./gradlew check` for both plus plugin validation.
- Use `./gradlew --no-configuration-cache :gradle-plugin:jacocoTestReport` to combine unit and
TestKit coverage into Codecov XML. The coverage-only TestKit task instruments forked builds and
does not support the configuration cache; the ordinary functional suite still verifies
configuration-cache reuse.
- Use `./gradlew :buildSrc:check` for the build-logic tests and Detekt analysis. `buildSrc` is a
separate build, so the root `check` task does not reach them; CI runs the task explicitly.
- Use `./gradlew generateDependencyReports` after changing dependencies or the plugin version.
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
[![Build on Ubuntu and Windows][build-badge]][gh-actions]
[![Codecov][coverage-badge]][codecov]
[![license](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg?style=flat)](http://www.apache.org/licenses/LICENSE-2.0)

# Embed Code Gradle plugin
Expand Down Expand Up @@ -178,5 +179,7 @@ consuming project's `settings.gradle.kts`. Adding it only to the regular
The plugin is available under the [Apache License 2.0](LICENSE).

[build-badge]: https://github.com/SpineEventEngine/embed-code-gradle-plugin/actions/workflows/check.yml/badge.svg
[codecov]: https://codecov.io/gh/SpineEventEngine/embed-code-gradle-plugin
[coverage-badge]: https://codecov.io/gh/SpineEventEngine/embed-code-gradle-plugin/graph/badge.svg
[embed-code]: https://github.com/SpineEventEngine/embed-code-go
[gh-actions]: https://github.com/SpineEventEngine/embed-code-gradle-plugin/actions
10 changes: 10 additions & 0 deletions codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
coverage:
status:
project:
default:
informational: true
patch:
default:
target: 90%

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.

Must fix — the patch target is not met by this PR itself.

codecov/patch on this head commit reports 69.07% of diff hit (target 90.00%), so the check this file introduces is red on the very change that adds it, and it will stay red for anyone rebasing on it.

Reproduced locally with ./gradlew --no-configuration-cache :gradle-plugin:jacocoTestReport. The uncovered new lines are all error-handling wrappers plus one synthetic overload:

  • InstallationDirectory.kt 66–69, 142–147, 182–185 — the IOExceptionGradleException wrappers in prepare(), createDirectoriesSafely(), and isRedirectingFileSystemEntry().
  • Checksum.kt 191–192 — the IOException catch in readChecksumMetadata.
  • Checksum.kt 152, 155 — the synthetic default-argument overload of readGitHubReleaseMetadata (see the separate comment there).

Smallest correction: either cover the reachable ones (a read-only installation root exercises the prepare() wrapper; a closed loopback socket exercises the readChecksumMetadata catch) and drop the default argument, or set a target this repository can actually sustain and raise it later. Merging a permanently failing status check is the outcome to avoid.

threshold: 0%
only_pulls: true
208 changes: 135 additions & 73 deletions dependencies.md

Large diffs are not rendered by default.

63 changes: 60 additions & 3 deletions gradle-plugin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,17 @@ import io.spine.embedcode.gradle.report.DependencyMarkdownReportRenderer
import io.spine.embedcode.gradle.report.GenerateDependencyPom
import io.spine.embedcode.gradle.report.UpdateDependencyReports
import org.apache.tools.ant.filters.ReplaceTokens
import org.gradle.api.Action
import org.gradle.api.publish.maven.MavenPublication
import org.gradle.api.tasks.Sync
import org.gradle.plugin.compatibility.compatibility
import org.gradle.testing.jacoco.plugins.JacocoTaskExtension

plugins {
id("jvm-module")
id("dokka-configuration")
`java-gradle-plugin`
jacoco
`maven-publish`
}

Expand Down Expand Up @@ -98,9 +101,7 @@ configurations[functionalTestSourceSet.runtimeOnlyConfigurationName].extendsFrom
configurations.testRuntimeOnly.get(),
)

val functionalTest = tasks.register<Test>("functionalTest") {
description = "Runs TestKit functional tests."
group = LifecycleBasePlugin.VERIFICATION_GROUP
val configureFunctionalTest = Action<Test> {
testClassesDirs = functionalTestSourceSet.output.classesDirs
classpath = functionalTestSourceSet.runtimeClasspath
useJUnitPlatform()
Expand All @@ -113,10 +114,66 @@ val functionalTest = tasks.register<Test>("functionalTest") {
shouldRunAfter(tasks.test)
}

val functionalTest = tasks.register<Test>("functionalTest") {
configureFunctionalTest.execute(this)
description = "Runs TestKit functional tests."
group = LifecycleBasePlugin.VERIFICATION_GROUP
extensions.configure<JacocoTaskExtension> {
isEnabled = false
}
}

val testKitCoverageData = layout.buildDirectory.file("jacoco/testKit.exec")
val coverageFunctionalTest = tasks.register<Test>("coverageFunctionalTest") {
configureFunctionalTest.execute(this)
description = "Runs TestKit functional tests and collects plugin coverage."
group = LifecycleBasePlugin.VERIFICATION_GROUP
extensions.configure<JacocoTaskExtension> {
isEnabled = false
setDestinationFile(testKitCoverageData.map { file -> file.asFile })
}
outputs.file(testKitCoverageData)
notCompatibleWithConfigurationCache(
"JaCoCo coverage of forked TestKit builds requires execution-time agent paths.",
)
doFirst {
val coverageDataFile = testKitCoverageData.get().asFile
coverageDataFile.delete()
val jacocoExtension = extensions.getByType<JacocoTaskExtension>()
// TestKit daemons use another working directory, so JaCoCo needs an absolute destination.
val childJvmArgument =
Regex("""destfile=[^,]+""").replaceFirst(
jacocoExtension.asJvmArg,
Regex.escapeReplacement("destfile=${coverageDataFile.absolutePath}"),
)
systemProperty(
"io.spine.embedcode.gradle.testkit.coverage.jvm-argument",
childJvmArgument,
)
}
}

tasks.check {
dependsOn(functionalTest)
}

tasks.jacocoTestReport {
dependsOn(tasks.test, coverageFunctionalTest)
executionData(
tasks.test.map { task ->
task.extensions.getByType<JacocoTaskExtension>().destinationFile
},
coverageFunctionalTest.map { task ->
task.extensions.getByType<JacocoTaskExtension>().destinationFile
},
)
reports {
xml.required.set(true)
html.required.set(false)
csv.required.set(false)
}
}

base {
archivesName.set("embed-code-gradle-plugin")
}
Expand Down
Loading