运行 仅当从依赖于另一个任务的命令行调用的任务,即使它失败
Run task only if invoked from command line with dependency on another task even if it fails
我会先解释我的用例,然后再概括描述我想做什么。
用例:打开测试结果
默认 $gradle test
将 运行 test
任务和 运行 所有单元测试。我想添加一个 browseTest
任务,如果在命令行中指定,它将在默认浏览器中打开测试报告。我已经有了打开测试结果的代码,但我需要弄清楚如何执行它。以下是我认为它应该如何工作:
Command
Tests up-to-date
Run tests?
Test result?
Open test report?
test
no
yes
success/failure
no
test
yes
no
success/failure
no
browseTest
no
yes
success/failure
yes
browseTest
yes
no
success/failure
yes
一般情况
我希望能够让一项任务(即 browseTest
)将另一项任务(例如 task
)添加到任务图中,而不依赖于其他任务的成功或失败。如果我使用 dependsOn
那么第一个 take 失败会阻止第二个任务执行。使用 mustRunAfter
指定顺序但不会将任务添加到任务图中(因此不会执行)。
如果有类似下面的东西,我相信它会得到我想要的:
task("browseTest") {
addsToTaskGraph("test")
mustRunAfter("test")
doLast {
// Open test results in browser
}
}
更新答案
我又回答了我自己的问题。我能够成功创建 runsAfter
扩展函数。我希望能够清理触发 callback/hook 的逻辑,但总的来说,这对用户非常友好,而且效率不高。
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)
}
}
这里有几张:
browseTest
任务依赖于test
,也就是说如果指定它会强制test
到运行。
browseTest
任务实际上没有做任何事情。
- 已创建
browseTestHelper
个任务。
test
任务配置为由 browseTestHelper
“完成”。
- 当
browseTestHelper
运行 时,它检查原始 browseTest
任务是否在任务图中。如果没有,它什么都不做。
- 这是必要的,因为
browseTestHelper
任务已创建并完成 test
,即使未调用 browseTest
任务也是如此。
- 如果
browseTest
在任务图中,它将执行其操作。
有几种方法可以改进:
- 有没有比
val parent = this
行更好的访问“父”任务的方法?
- 您能否有条件地向任务添加终结器,同时查看任务图中是否存在任务?
- 我猜不会,因为添加终结器的行为会影响任务图。
- 有没有办法将这一切包装在一个函数中,您只需调用一个函数即可调用?
做这样的事情会很棒:
val browseTest = task("browseTest") {
runsAfter("test") {
val file = project.file("build/reports/tests/test/index.html")
browse(file.absolutePath)
}
}
您自己的答案似乎太复杂了。在我看来,你的问题的本质是当 browseTest
将成为构建的一部分时,不要让任务 test
失败:
If I use dependsOn
then the first task failing prevents the second task from executing.
嗯,您可以简单地通过使用其 ignoreFailures
属性 来防止任务 test
构建失败。将此 属性 与您的情况相结合,您就可以开始了。
我将在这里使用 Groovy 代码,因为我对 Gradle Kotlin DSL 不是很熟悉:
test {
doFirst {
ignoreFailures = gradle.taskGraph.hasTask('browseTest')
}
}
task browseTest {
dependsOn 'test'
doLast {
// open in browser
}
}
我会先解释我的用例,然后再概括描述我想做什么。
用例:打开测试结果
默认 $gradle test
将 运行 test
任务和 运行 所有单元测试。我想添加一个 browseTest
任务,如果在命令行中指定,它将在默认浏览器中打开测试报告。我已经有了打开测试结果的代码,但我需要弄清楚如何执行它。以下是我认为它应该如何工作:
Command | Tests up-to-date | Run tests? | Test result? | Open test report? |
---|---|---|---|---|
test | no | yes | success/failure | no |
test | yes | no | success/failure | no |
browseTest | no | yes | success/failure | yes |
browseTest | yes | no | success/failure | yes |
一般情况
我希望能够让一项任务(即 browseTest
)将另一项任务(例如 task
)添加到任务图中,而不依赖于其他任务的成功或失败。如果我使用 dependsOn
那么第一个 take 失败会阻止第二个任务执行。使用 mustRunAfter
指定顺序但不会将任务添加到任务图中(因此不会执行)。
如果有类似下面的东西,我相信它会得到我想要的:
task("browseTest") {
addsToTaskGraph("test")
mustRunAfter("test")
doLast {
// Open test results in browser
}
}
更新答案
我又回答了我自己的问题。我能够成功创建 runsAfter
扩展函数。我希望能够清理触发 callback/hook 的逻辑,但总的来说,这对用户非常友好,而且效率不高。
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)
}
}
这里有几张:
browseTest
任务依赖于test
,也就是说如果指定它会强制test
到运行。browseTest
任务实际上没有做任何事情。- 已创建
browseTestHelper
个任务。 test
任务配置为由browseTestHelper
“完成”。- 当
browseTestHelper
运行 时,它检查原始browseTest
任务是否在任务图中。如果没有,它什么都不做。- 这是必要的,因为
browseTestHelper
任务已创建并完成test
,即使未调用browseTest
任务也是如此。
- 这是必要的,因为
- 如果
browseTest
在任务图中,它将执行其操作。
有几种方法可以改进:
- 有没有比
val parent = this
行更好的访问“父”任务的方法? - 您能否有条件地向任务添加终结器,同时查看任务图中是否存在任务?
- 我猜不会,因为添加终结器的行为会影响任务图。
- 有没有办法将这一切包装在一个函数中,您只需调用一个函数即可调用?
做这样的事情会很棒:
val browseTest = task("browseTest") {
runsAfter("test") {
val file = project.file("build/reports/tests/test/index.html")
browse(file.absolutePath)
}
}
您自己的答案似乎太复杂了。在我看来,你的问题的本质是当 browseTest
将成为构建的一部分时,不要让任务 test
失败:
If I use
dependsOn
then the first task failing prevents the second task from executing.
嗯,您可以简单地通过使用其 ignoreFailures
属性 来防止任务 test
构建失败。将此 属性 与您的情况相结合,您就可以开始了。
我将在这里使用 Groovy 代码,因为我对 Gradle Kotlin DSL 不是很熟悉:
test {
doFirst {
ignoreFailures = gradle.taskGraph.hasTask('browseTest')
}
}
task browseTest {
dependsOn 'test'
doLast {
// open in browser
}
}