仅当从命令行调用并依赖另一个任务时才运行任务,即使它失败

时间:2021-04-29 18:06:36

标签: gradle build.gradle gradle-kotlin-dsl

我将首先解释我的用例,然后是我想要做什么的概括描述。

用例:公开测试结果

默认 $gradle test 将运行 test 任务并运行所有单元测试。我想添加一个 browseTest 任务,如果在命令行中指定,它将在默认浏览器中打开测试报告。我已经有了打开测试结果的代码,但我需要弄清楚如何执行它。这是我认为它应该如何工作:

<头>
命令 最新测试 运行测试? 测试结果? 打开测试报告?
测试 没有 是的 成功/失败 没有
测试 是的 没有 成功/失败 没有
浏览测试 没有 是的 成功/失败 是的
浏览测试 是的 没有 成功/失败 是的

一般情况

我希望能够让一项任务(即 browseTest)向任务图中添加另一项任务(例如 task),而不依赖于其他任务的成功或失败。如果我使用 dependsOn,那么第一个失败会阻止第二个任务的执行。使用 mustRunAfter 指定排序但不将任务添加到任务图中(因此不会被执行)。

如果有类似下面的东西,我相信它会得到我想要的:

task("browseTest") {
    addsToTaskGraph("test")
    mustRunAfter("test")
    doLast {
        // Open test results in browser
    }
}

2 个答案:

答案 0 :(得分:0)

更新答案

我再次回答了我自己的问题。我能够成功创建一个 runsAfter 扩展函数。我希望能够清理触发回调/挂钩的逻辑,但总体而言,这是非常用户友好的,并且不会过于低效。

val browseTest = task("browseTest") {
    runsAfter(tasks.test.name) {
        // perform "callback"
        val file = project.file("build/reports/tests/test/index.html")
        browse(file.absolutePath)
    }
}

fun Task.runsAfter(vararg paths: String, action: () -> Unit) {
    val parent = this
    val helper = task("${name}__runsAfter") {
        doLast {
            if (gradle.taskGraph.hasTask(parent)) {
                action()
            }
        }
    }
    paths.forEach {
        dependsOn(it)
        tasks[it].finalizedBy(helper)
    }
}

原答案

虽然我不太喜欢这个结果,但我能够让它发挥作用。如果有人有任何其他想法,请告诉我。

val browseTest = task("browseTest") {
    dependsOn("test")
    val parent = this
    val helper = task("browseTestHelper") {
        doLast {
            if (gradle.taskGraph.hasTask(parent)) {
                // Open test results in browser
                val file = project.file("build/reports/tests/test/index.html")
                browse(file.absolutePath)
            }
        }
    }
    tasks.test {
        finalizedBy(helper)
    }
}

这里有几块:

  1. browseTest 任务依赖于 test,这意味着如果指定了 test,它将强制运行。
  2. browseTest 任务实际上没有任何事情。
  3. 创建了一个 browseTestHelper 任务。
  4. test 任务配置为由 browseTestHelper“完成”。
  5. browseTestHelper 运行时,它会检查原始 browseTest 任务是否在任务图中。如果没有,它不会做任何事情。
    • 这是必要的,因为即使未调用 browseTestHelper 任务,也会创建 test 任务并完成 browseTest
  6. 如果 browseTest 在任务图中,它会执行其操作。

有几种方法可以改进:

  1. 除了 val parent = this 行之外,还有没有更好的方法来访问“父”任务?
  2. 您能否有条件地向任务添加终结器,同时查看任务图中是否存在任务?
    • 我猜不会,因为添加终结器的行为会影响任务图。
  3. 有没有办法将所有这些都包装在一个函数中,您可以通过一个函数调用来调用它?

做这样的事情会很棒:

val browseTest = task("browseTest") {
    runsAfter("test") {
        val file = project.file("build/reports/tests/test/index.html")
        browse(file.absolutePath)
    }
}

答案 1 :(得分:0)

您自己的答案似乎很复杂。在我看来,您的问题的本质是在 test 将成为构建的一部分时不要让任务 browseTest 失败:

<块引用>

如果我使用 dependsOn,那么第一个任务失败会阻止第二个任务的执行。

好吧,您可以简单地使用其 test 属性来防止任务 ignoreFailures 构建失败。将此属性与您的条件相结合,您就可以开始了。

我将在这里使用 Groovy 代码,因为我对 Gradle Kotlin DSL 不太熟悉:

test {
    doFirst {
        ignoreFailures = gradle.taskGraph.hasTask('browseTest')
    }
}

task browseTest {
    dependsOn 'test'
    doLast {
        // open in browser
    }
}
相关问题