diff --git a/.coveralls b/.coveralls deleted file mode 100644 index 59132f1..0000000 --- a/.coveralls +++ /dev/null @@ -1 +0,0 @@ -aEpUWEZrYzE2bzNKTlBYUjdUSThlc1hBWW5qVGZUVzJsCg== \ No newline at end of file diff --git a/.github/workflows/test-workflow.yaml b/.github/workflows/test-workflow.yaml index fc59bf1..a1e44d3 100644 --- a/.github/workflows/test-workflow.yaml +++ b/.github/workflows/test-workflow.yaml @@ -12,4 +12,6 @@ jobs: steps: - uses: actions/checkout@v2 - name: test and publish coverage - run: export COVERALLS_REPO_TOKEN=$(cat .coveralls | base64 -d | xargs) && ./gradlew jacocoTestReport coverallsJacoco + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: ./gradlew jacocoTestReport coverallsJacoco diff --git a/README.md b/README.md index 5d7ad82..673870e 100644 --- a/README.md +++ b/README.md @@ -133,6 +133,18 @@ jobs: run: ./gradlew test jacocoTestReport coverallsJacoco ``` +For running on publicly forked PRs, the plugin uses a (undocumented) API and uses `GITHUB_TOKEN` to identify the repo instead, as follows: +```yaml +jobs: + tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: test and publish coverage + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: ./gradlew test jacocoTestReport coverallsJacoco +``` ### Buildkite See [buildkite environment variables documentation](https://buildkite.com/docs/pipelines/environment-variables#defining-your-own) diff --git a/build.gradle.kts b/build.gradle.kts index dd217e4..570f9a8 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,7 +1,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile group = "com.github.nbaztec" -version = "1.1.1" +version = "1.1.2" tasks.withType { kotlinOptions.jvmTarget = JavaVersion.VERSION_1_8.toString() @@ -57,11 +57,7 @@ plugins { `maven-publish` id("org.jetbrains.kotlin.jvm") version "1.3.72" id("com.gradle.plugin-publish") version "0.12.0" - id("com.github.nbaztec.coveralls-jacoco") version "1.1.0" -} - -coverallsJacoco { - rootPackage = "org.gradle.plugin.coveralls.jacoco" + id("com.github.nbaztec.coveralls-jacoco") version "1.1.2-beta1" } publishing { diff --git a/src/main/kotlin/CoverallsJacocoPlugin.kt b/src/main/kotlin/CoverallsJacocoPlugin.kt index d233510..3a69757 100644 --- a/src/main/kotlin/CoverallsJacocoPlugin.kt +++ b/src/main/kotlin/CoverallsJacocoPlugin.kt @@ -5,6 +5,7 @@ import org.gradle.api.Project import org.gradle.api.tasks.SourceSet open class CoverallsJacocoPluginExtension { + @Deprecated("the plugin now auto detects the root package") var rootPackage: String? = null var reportPath = "build/reports/jacoco/test/jacocoTestReport.xml" var apiEndpoint = "https://coveralls.io/api/v1/jobs" diff --git a/src/main/kotlin/CoverallsReporter.kt b/src/main/kotlin/CoverallsReporter.kt index 7e3252a..502b143 100644 --- a/src/main/kotlin/CoverallsReporter.kt +++ b/src/main/kotlin/CoverallsReporter.kt @@ -14,6 +14,7 @@ import org.gradle.api.Project data class Request( val repo_token: String, val service_name: String, + val repo_name: String?, val service_number: String?, val service_job_id: String?, val service_pull_request: String?, @@ -46,12 +47,13 @@ class CoverallsReporter(val envGetter: EnvGetter) { logger.info("retrieving ci service info") val serviceInfo = ServiceInfoParser(envGetter).parse() - val repoToken = envGetter("COVERALLS_REPO_TOKEN") + val repoToken = envGetter("COVERALLS_REPO_TOKEN") ?: envGetter("GITHUB_TOKEN") check(repoToken != null && repoToken.isNotBlank()) { "COVERALLS_REPO_TOKEN not set" } val req = Request( repoToken, serviceInfo.name, + serviceInfo.repoName, serviceInfo.number, serviceInfo.jobId, serviceInfo.pr, diff --git a/src/main/kotlin/ServiceInfoParser.kt b/src/main/kotlin/ServiceInfoParser.kt index 9789169..652b3ce 100644 --- a/src/main/kotlin/ServiceInfoParser.kt +++ b/src/main/kotlin/ServiceInfoParser.kt @@ -1,6 +1,6 @@ package org.gradle.plugin.coveralls.jacoco -data class ServiceInfo(val name: String, val number: String? = null, val jobId: String? = null, val pr: String? = null, val branch: String? = null) +data class ServiceInfo(val name: String, val repoName: String? = null, val number: String? = null, val jobId: String? = null, val pr: String? = null, val branch: String? = null) class ServiceInfoParser(val envGetter: EnvGetter) { private val isJenkins = envGetter("JENKINS_URL") != null @@ -8,6 +8,7 @@ class ServiceInfoParser(val envGetter: EnvGetter) { private val isCircleCI = envGetter("CIRCLECI") == "true" private val isCodeship = envGetter("CI_NAME") == "codeship" private val isGithubActions = envGetter("GITHUB_ACTIONS") != null + private val isGithubActionsToken = envGetter("GITHUB_TOKEN") != null private val isBuildkite = envGetter("BUILDKITE") == "true" fun parse(): ServiceInfo { @@ -36,6 +37,17 @@ class ServiceInfoParser(val envGetter: EnvGetter) { pr = envGetter("CI_PR_NUMBER"), branch = envGetter("CI_BRANCH") ) + isGithubActions && isGithubActionsToken -> ServiceInfo( + name = "github", + repoName = envGetter("GITHUB_REPOSITORY"), + jobId = envGetter("BUILD_NUMBER"), + pr = envGetter("GITHUB_REF")?.let { ref -> + "refs/pull/(\\d+)/merge".toRegex().find(ref)?.let { + it.groupValues[1] + } + }, + branch = envGetter("CI_BRANCH") + ) isGithubActions -> ServiceInfo( name = "github-actions", jobId = envGetter("BUILD_NUMBER"), diff --git a/src/test/kotlin/CoverallsJacocoPluginTest.kt b/src/test/kotlin/CoverallsJacocoPluginTest.kt index 052e084..7981c87 100644 --- a/src/test/kotlin/CoverallsJacocoPluginTest.kt +++ b/src/test/kotlin/CoverallsJacocoPluginTest.kt @@ -12,8 +12,21 @@ import org.gradle.api.tasks.SourceSetContainer import org.gradle.testfixtures.ProjectBuilder import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test +import kotlin.reflect.full.memberProperties +import kotlin.reflect.jvm.isAccessible internal class CoverallsJacocoPluginTest { + @Test + fun `CoverallsJacocoPluginExtension deprecates rootPackage`() { + val ext = CoverallsJacocoPluginExtension() + val actual = ext::class.memberProperties.find { it.name == "rootPackage" }!!.let { + it.isAccessible = true + it.annotations.first().annotationClass + } + + assertEquals(Deprecated::class, actual) + } + @Test fun `CoverallsJacocoPlugin creates extension and task with correct name`() { val project = mockk(relaxed = true) @@ -46,7 +59,6 @@ internal class CoverallsJacocoPluginTest { fun `CoverallsJacocoPluginExtension has meaningful defaults`() { val extension = CoverallsJacocoPluginExtension() assertEquals(extension.reportPath, "build/reports/jacoco/test/jacocoTestReport.xml") - assertEquals(extension.rootPackage, null) assertEquals(extension.reportSourceSets, emptySet()) assertEquals(extension.apiEndpoint, "https://coveralls.io/api/v1/jobs") } diff --git a/src/test/kotlin/CoverallsReporterTest.kt b/src/test/kotlin/CoverallsReporterTest.kt index ace30db..6feff6e 100644 --- a/src/test/kotlin/CoverallsReporterTest.kt +++ b/src/test/kotlin/CoverallsReporterTest.kt @@ -96,6 +96,48 @@ internal class CoverallsReporterTest { Unit } + @Test + fun `CoverallsReporter parses info and sends report to coveralls server when GITHUB_TOKEN is set`() = runBlocking { + var actual = "" + val mockServer = HttpServer.create(InetSocketAddress("localhost", 0), 0).apply { + createContext("/") { http -> + actual = http.requestBody.reader().readText() + .trim() + .split("\r\n") + .drop(1).dropLast(1) + .joinToString("\n") + + http.sendResponseHeaders(200, 0) + http.close() + } + } + GlobalScope.launch { + mockServer.start() + } + + val envGetter = createEnvGetter(mapOf( + "GITHUB_TOKEN" to "test-token", + "GITHUB_ACTIONS" to "true" + )) + val project = mockProject { _, pluginExtension -> + every { pluginExtension.apiEndpoint } returns "http://${mockServer.address.hostName}:${mockServer.address.port}" + } + + delay(50) + val reporter = CoverallsReporter(envGetter) + reporter.report(project) + mockServer.stop(2) + + val expected = """Content-Disposition: form-data; name="json_file"; filename="json_file" +Content-Type: application/json; charset=UTF-8 +Content-Transfer-Encoding: binary + +{"repo_token":"test-token","service_name":"github","git":{"head":{"id":"4cd72eadcc34861139b338dd859344d419244e0b","author_name":"John Doe","author_email":"test@example.com","committer_name":"John Doe","committer_email":"test@example.com","message":"test commit\n"},"branch":"master","remotes":[{"name":"origin","url":"git@github.com:test/testrepo.git"}]},"source_files":[{"name":"javaStyleSrc/main/kotlin/foo/bar/baz/Main.kt","source_digest":"36083cd4c2ac736f9210fd3ed23504b5","coverage":[null,null,null,null,1,1,1,1,null,1,1,0,0,1,1,null,1,1,1]}]} + """.trimIndent() + + assertEquals(expected, actual) + } + @Test fun `CoverallsReporter parses info and sends report to coveralls server`() = runBlocking { var actual = "" @@ -148,7 +190,6 @@ Content-Transfer-Encoding: binary val pluginExtension = mockk { every { reportPath } returns testReport.path - every { rootPackage } returns null every { reportPath } returns testReport.path every { reportSourceSets } returns emptySet() } diff --git a/src/test/kotlin/DataClassTest.kt b/src/test/kotlin/DataClassTest.kt index f6cdad3..684cb22 100644 --- a/src/test/kotlin/DataClassTest.kt +++ b/src/test/kotlin/DataClassTest.kt @@ -37,12 +37,13 @@ internal class DataClassTest { @Test fun `data class ServiceInfo`() { - val svcInfo = ServiceInfo("1", "job number", "2", "3", "4") - assertEquals("1", svcInfo.name) - assertEquals("job number", svcInfo.number) - assertEquals("2", svcInfo.jobId) - assertEquals("3", svcInfo.pr) - assertEquals("4", svcInfo.branch) + val svcInfo = ServiceInfo("name", "repo_name","number", "jobId", "pr", "branch") + assertEquals("name", svcInfo.name) + assertEquals("repo_name", svcInfo.repoName) + assertEquals("number", svcInfo.number) + assertEquals("jobId", svcInfo.jobId) + assertEquals("pr", svcInfo.pr) + assertEquals("branch", svcInfo.branch) } @Test @@ -65,12 +66,22 @@ internal class DataClassTest { fun `data class Request`() { val gitInfo = mockk() val sourceFiles = emptyList() - val req = Request("1", "2", "job number", "3", "4", gitInfo, sourceFiles) - assertEquals("1", req.repo_token) - assertEquals("2", req.service_name) - assertEquals("job number", req.service_number) - assertEquals("3", req.service_job_id) - assertEquals("4", req.service_pull_request) + val req = Request( + "repo_token", + "service_name", + "repo_name", + "service_number", + "service_job_id", + "service_pull_request", + gitInfo, + sourceFiles + ) + assertEquals("repo_token", req.repo_token) + assertEquals("service_name", req.service_name) + assertEquals("repo_name", req.repo_name) + assertEquals("service_number", req.service_number) + assertEquals("service_job_id", req.service_job_id) + assertEquals("service_pull_request", req.service_pull_request) assertEquals(gitInfo, req.git) assertEquals(sourceFiles, req.source_files) } diff --git a/src/test/kotlin/ServiceInfoParserTest.kt b/src/test/kotlin/ServiceInfoParserTest.kt index 87a1de9..d85a989 100644 --- a/src/test/kotlin/ServiceInfoParserTest.kt +++ b/src/test/kotlin/ServiceInfoParserTest.kt @@ -127,6 +127,22 @@ internal class ServiceInfoParserTest { assertEquals(expected, actual) } + @Test + fun `ServiceInfoParser parses github env on pr`() { + val envGetter = createEnvGetter(mapOf( + "GITHUB_ACTIONS" to "true", + "GITHUB_REPOSITORY" to "foo/bar", + "GITHUB_TOKEN" to "token", + "BUILD_NUMBER" to "1", + "GITHUB_REF" to "refs/pull/123/merge", + "CI_BRANCH" to "foobar" + )) + + val actual = ServiceInfoParser(envGetter).parse() + val expected = ServiceInfo(name = "github", repoName = "foo/bar", jobId = "1", pr = "123", branch = "foobar") + assertEquals(expected, actual) + } + @Test fun `ServiceInfoParser parses github actions env on pr`() { val envGetter = createEnvGetter(mapOf( diff --git a/src/test/kotlin/SourceReportParserTest.kt b/src/test/kotlin/SourceReportParserTest.kt index 464d714..0b44f8d 100644 --- a/src/test/kotlin/SourceReportParserTest.kt +++ b/src/test/kotlin/SourceReportParserTest.kt @@ -40,7 +40,6 @@ internal class SourceReportParserTest { } every { extensions.getByType(CoverallsJacocoPluginExtension::class.java) } returns mockk { every { reportPath } returns testReport.path - every { rootPackage } returns null every { reportSourceSets } returns emptySet() } } @@ -56,7 +55,7 @@ internal class SourceReportParserTest { } @Test - fun `SourceReportParser parses simple jacoco report with kotlin styled rootPackage`() { + fun `SourceReportParser parses simple jacoco report with kotlin styled root package`() { val project = mockk { every { projectDir } returns File("src/test/resources/testrepo") every { extensions.getByType(SourceSetContainer::class.java) } returns mockk { @@ -64,7 +63,6 @@ internal class SourceReportParserTest { } every { extensions.getByType(CoverallsJacocoPluginExtension::class.java) } returns mockk { every { reportPath } returns testReport.path - every { rootPackage } returns "foo.bar.baz" every { reportSourceSets } returns emptySet() } } @@ -90,7 +88,6 @@ internal class SourceReportParserTest { every { projectDir } returns File("src/test/resources/testrepo") every { extensions.getByType(CoverallsJacocoPluginExtension::class.java) } returns mockk { every { reportPath } returns testReport.path - every { rootPackage } returns "foo.bar.baz" every { reportSourceSets } returns listOf( mockk { every { allJava.srcDirs } returns setOf(testKotlinStyleSourceDir) }, mockk { every { allJava.srcDirs } returns setOf(testKotlinStyleSourceDirAdditional) }