[Gradle, JS] Reporting in case when yarn.lock was changed during build
---- [Gradle, JS] Change naming [Gradle, JS] storeYarnLock not finalizing kotlinNpmInstall [Gradle, JS] Add double clean to test for check correctness without input yarn.lock [Gradle, JS] Consider reportNewYarnLock in yarn.lock service [Gradle, JS] actualizeYarnLock for explicit actualizing yarn.lock [Gradle, JS] Not update yarn.lock automatically [Gradle, JS] Add test on yarn.lock reporting [Gradle, JS] Error report by default [Gradle, JS] Report with failing after build, not failing task [Gradle, JS] Report about yarn.lock mismatch reporting Merge-request: KT-MR-6614 Merged-by: Ilya Goncharov <Ilya.Goncharov@jetbrains.com> ^KT-52950 fixed
This commit is contained in:
+203
-2
@@ -17,6 +17,9 @@ import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProject
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProjectModules
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.PackageJson
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.fromSrcPackageJson
|
||||
import org.jetbrains.kotlin.gradle.targets.js.yarn.YARN_LOCK_MISMATCH_MESSAGE
|
||||
import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnLockCopyTask.Companion.STORE_YARN_LOCK_NAME
|
||||
import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnLockCopyTask.Companion.UPGRADE_YARN_LOCK
|
||||
import org.jetbrains.kotlin.gradle.tasks.USING_JS_INCREMENTAL_COMPILATION_MESSAGE
|
||||
import org.jetbrains.kotlin.gradle.tasks.USING_JS_IR_BACKEND_MESSAGE
|
||||
import org.jetbrains.kotlin.gradle.testbase.*
|
||||
@@ -25,7 +28,6 @@ import org.jetbrains.kotlin.gradle.util.normalizePath
|
||||
import org.junit.jupiter.api.Disabled
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.condition.DisabledIf
|
||||
import java.io.File
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Paths
|
||||
import java.util.zip.ZipFile
|
||||
@@ -33,7 +35,6 @@ import kotlin.io.path.*
|
||||
import kotlin.streams.toList
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertNotEquals
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
@JsGradlePluginTests
|
||||
@@ -867,6 +868,10 @@ abstract class AbstractKotlin2JsGradlePluginIT(protected val irBackend: Boolean)
|
||||
}
|
||||
}
|
||||
|
||||
build(UPGRADE_YARN_LOCK) {
|
||||
assertTasksExecuted(":$UPGRADE_YARN_LOCK")
|
||||
}
|
||||
|
||||
build("jsJar") {
|
||||
val archive = Files.list(projectPath.resolve("build").resolve("libs")).use { files ->
|
||||
files
|
||||
@@ -1178,6 +1183,202 @@ abstract class AbstractKotlin2JsGradlePluginIT(protected val irBackend: Boolean)
|
||||
}
|
||||
}
|
||||
|
||||
@DisplayName("Failing with yarn.lock update")
|
||||
@GradleTest
|
||||
fun testFailingWithYarnLockUpdate(gradleVersion: GradleVersion) {
|
||||
project("kotlin-js-yarn-lock-project", gradleVersion) {
|
||||
build("compileKotlinJs") {
|
||||
assertTasksExecuted(":$STORE_YARN_LOCK_NAME")
|
||||
}
|
||||
|
||||
projectPath.resolve("kotlin-js-store").deleteRecursively()
|
||||
|
||||
buildGradleKts.modify {
|
||||
it + "\n" +
|
||||
"""
|
||||
rootProject.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin::class.java) {
|
||||
rootProject.the<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension>().reportNewYarnLock = true
|
||||
}
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
buildAndFail("compileKotlinJs") {
|
||||
assertTasksFailed(":$STORE_YARN_LOCK_NAME")
|
||||
}
|
||||
|
||||
buildGradleKts.modify {
|
||||
it + "\n" +
|
||||
"""
|
||||
dependencies {
|
||||
implementation(npm("decamelize", "6.0.0"))
|
||||
}
|
||||
|
||||
rootProject.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin::class.java) {
|
||||
rootProject.the<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension>().yarnLockMismatchReport =
|
||||
org.jetbrains.kotlin.gradle.targets.js.yarn.YarnLockMismatchReport.FAIL
|
||||
}
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
buildAndFail("compileKotlinJs") {
|
||||
assertTasksFailed(":$STORE_YARN_LOCK_NAME")
|
||||
}
|
||||
|
||||
// yarn.lock was not updated
|
||||
buildAndFail("compileKotlinJs") {
|
||||
assertTasksFailed(":$STORE_YARN_LOCK_NAME")
|
||||
}
|
||||
|
||||
build(UPGRADE_YARN_LOCK) {
|
||||
assertTasksExecuted(":$UPGRADE_YARN_LOCK")
|
||||
}
|
||||
|
||||
buildGradleKts.modify {
|
||||
val replaced = it.replace("implementation(npm(\"decamelize\", \"6.0.0\"))", "")
|
||||
replaced + "\n" +
|
||||
"""
|
||||
rootProject.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin::class.java) {
|
||||
rootProject.the<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension>().yarnLockMismatchReport =
|
||||
org.jetbrains.kotlin.gradle.targets.js.yarn.YarnLockMismatchReport.WARNING
|
||||
}
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
build("compileKotlinJs") {
|
||||
assertTasksExecuted(":$STORE_YARN_LOCK_NAME")
|
||||
|
||||
assertOutputContains(YARN_LOCK_MISMATCH_MESSAGE)
|
||||
}
|
||||
|
||||
build(UPGRADE_YARN_LOCK) {
|
||||
assertTasksExecuted(":$UPGRADE_YARN_LOCK")
|
||||
}
|
||||
|
||||
buildGradleKts.modify {
|
||||
it + "\n" +
|
||||
"""
|
||||
dependencies {
|
||||
implementation(npm("decamelize", "6.0.0"))
|
||||
}
|
||||
|
||||
rootProject.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin::class.java) {
|
||||
rootProject.the<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension>().yarnLockMismatchReport =
|
||||
org.jetbrains.kotlin.gradle.targets.js.yarn.YarnLockMismatchReport.NONE
|
||||
}
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
build("compileKotlinJs") {
|
||||
assertTasksExecuted(":$STORE_YARN_LOCK_NAME")
|
||||
|
||||
assertOutputDoesNotContain(YARN_LOCK_MISMATCH_MESSAGE)
|
||||
}
|
||||
|
||||
build(UPGRADE_YARN_LOCK) {
|
||||
assertTasksExecuted(":$UPGRADE_YARN_LOCK")
|
||||
}
|
||||
|
||||
buildGradleKts.modify {
|
||||
val replaced = it.replace("implementation(npm(\"decamelize\", \"6.0.0\"))", "")
|
||||
replaced + "\n" +
|
||||
"""
|
||||
rootProject.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin::class.java) {
|
||||
rootProject.the<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension>().yarnLockMismatchReport =
|
||||
org.jetbrains.kotlin.gradle.targets.js.yarn.YarnLockMismatchReport.FAIL_AFTER_BUILD
|
||||
}
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
buildAndFail("compileKotlinJs") {
|
||||
assertTasksExecuted(":$STORE_YARN_LOCK_NAME")
|
||||
|
||||
assertOutputContains(YARN_LOCK_MISMATCH_MESSAGE)
|
||||
}
|
||||
|
||||
buildAndFail("compileKotlinJs") {
|
||||
assertTasksUpToDate(":$STORE_YARN_LOCK_NAME")
|
||||
|
||||
assertOutputContains(YARN_LOCK_MISMATCH_MESSAGE)
|
||||
}
|
||||
|
||||
projectPath.resolve("kotlin-js-store").deleteRecursively()
|
||||
|
||||
buildGradleKts.modify {
|
||||
it + "\n" +
|
||||
"""
|
||||
rootProject.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin::class.java) {
|
||||
rootProject.the<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension>().yarnLockMismatchReport =
|
||||
org.jetbrains.kotlin.gradle.targets.js.yarn.YarnLockMismatchReport.NONE
|
||||
rootProject.the<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension>().reportNewYarnLock =
|
||||
true
|
||||
}
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
build("compileKotlinJs") {
|
||||
assertTasksExecuted(":$STORE_YARN_LOCK_NAME")
|
||||
|
||||
assertOutputDoesNotContain(YARN_LOCK_MISMATCH_MESSAGE)
|
||||
}
|
||||
|
||||
buildGradleKts.modify {
|
||||
it + "\n" +
|
||||
"""
|
||||
dependencies {
|
||||
implementation(npm("decamelize", "6.0.0"))
|
||||
}
|
||||
|
||||
rootProject.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin::class.java) {
|
||||
rootProject.the<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension>().yarnLockMismatchReport =
|
||||
org.jetbrains.kotlin.gradle.targets.js.yarn.YarnLockMismatchReport.FAIL
|
||||
rootProject.the<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension>().yarnLockAutoReplace =
|
||||
true
|
||||
}
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
buildAndFail("compileKotlinJs") {
|
||||
assertTasksFailed(":$STORE_YARN_LOCK_NAME")
|
||||
}
|
||||
|
||||
//yarn.lock was updated
|
||||
build("compileKotlinJs") {
|
||||
assertTasksExecuted(":$STORE_YARN_LOCK_NAME")
|
||||
}
|
||||
|
||||
buildGradleKts.modify {
|
||||
val replaced = it.replace("implementation(npm(\"decamelize\", \"6.0.0\"))", "")
|
||||
replaced + "\n" +
|
||||
"""
|
||||
rootProject.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin::class.java) {
|
||||
rootProject.the<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension>().yarnLockMismatchReport =
|
||||
org.jetbrains.kotlin.gradle.targets.js.yarn.YarnLockMismatchReport.FAIL_AFTER_BUILD
|
||||
}
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
buildAndFail("compileKotlinJs") {
|
||||
assertTasksExecuted(":$STORE_YARN_LOCK_NAME")
|
||||
|
||||
assertOutputContains(YARN_LOCK_MISMATCH_MESSAGE)
|
||||
}
|
||||
|
||||
//yarn.lock was updated
|
||||
build("compileKotlinJs") {
|
||||
assertTasksUpToDate(":$STORE_YARN_LOCK_NAME")
|
||||
}
|
||||
|
||||
// check if everything ok without build/js/yarn.lock
|
||||
build("clean") {
|
||||
assertTasksExecuted(":clean")
|
||||
}
|
||||
|
||||
build("clean") {
|
||||
assertTasksUpToDate(":clean")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DisplayName("webpack configuration is valid")
|
||||
@GradleTest
|
||||
fun testWebpackConfig(gradleVersion: GradleVersion) {
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
plugins {
|
||||
id("org.jetbrains.kotlin.js")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("org.jetbrains.kotlin:kotlin-stdlib-js")
|
||||
testImplementation("org.jetbrains.kotlin:kotlin-test-js")
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
kotlin {
|
||||
js {
|
||||
useCommonJs()
|
||||
binaries.executable()
|
||||
nodejs {
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
kotlin.tests.individualTaskReports=true
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package com.example
|
||||
|
||||
fun best(): Int {
|
||||
return 42
|
||||
}
|
||||
|
||||
fun simpleBest(): Int {
|
||||
return 73
|
||||
}
|
||||
+10
-2
@@ -52,7 +52,11 @@ internal class DukatCompilationResolverPlugin(
|
||||
it.group = DUKAT_TASK_GROUP
|
||||
it.description = "Integrated generation Kotlin/JS external declarations for .d.ts files in $compilation"
|
||||
it.externalsOutputFormat = externalsOutputFormat
|
||||
it.dependsOn(nodeJs_.npmInstallTaskProvider, npmProject.packageJsonTask)
|
||||
it.dependsOn(
|
||||
nodeJs_.npmInstallTaskProvider,
|
||||
nodeJs_.storeYarnLockTaskProvider,
|
||||
npmProject.packageJsonTask
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +86,11 @@ internal class DukatCompilationResolverPlugin(
|
||||
) {
|
||||
it.group = DUKAT_TASK_GROUP
|
||||
it.description = "Generate Kotlin/JS external declarations for .d.ts files of all NPM dependencies in ${compilation}"
|
||||
it.dependsOn(nodeJs_.npmInstallTaskProvider, npmProject.packageJsonTask)
|
||||
it.dependsOn(
|
||||
nodeJs_.npmInstallTaskProvider,
|
||||
nodeJs_.storeYarnLockTaskProvider,
|
||||
npmProject.packageJsonTask
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-1
@@ -47,7 +47,11 @@ abstract class KotlinBrowserJsIr @Inject constructor(target: KotlinJsIrTarget) :
|
||||
get() = "Run all ${target.name} tests inside browser using karma and webpack"
|
||||
|
||||
override fun configureTestDependencies(test: KotlinJsTest) {
|
||||
test.dependsOn(nodeJs.npmInstallTaskProvider, nodeJs.nodeJsSetupTaskProvider)
|
||||
test.dependsOn(
|
||||
nodeJs.npmInstallTaskProvider,
|
||||
nodeJs.storeYarnLockTaskProvider,
|
||||
nodeJs.nodeJsSetupTaskProvider
|
||||
)
|
||||
}
|
||||
|
||||
override fun configureDefaultTestFramework(test: KotlinJsTest) {
|
||||
@@ -256,6 +260,7 @@ abstract class KotlinBrowserJsIr @Inject constructor(target: KotlinJsIrTarget) :
|
||||
) {
|
||||
dependsOn(
|
||||
nodeJs.npmInstallTaskProvider,
|
||||
nodeJs.storeYarnLockTaskProvider,
|
||||
target.project.tasks.named(compilation.processResourcesTaskName)
|
||||
)
|
||||
|
||||
|
||||
+5
-1
@@ -47,7 +47,11 @@ abstract class KotlinNodeJsIr @Inject constructor(target: KotlinJsIrTarget) :
|
||||
}
|
||||
|
||||
override fun configureTestDependencies(test: KotlinJsTest) {
|
||||
test.dependsOn(nodeJs.npmInstallTaskProvider, nodeJs.nodeJsSetupTaskProvider)
|
||||
test.dependsOn(
|
||||
nodeJs.npmInstallTaskProvider,
|
||||
nodeJs.storeYarnLockTaskProvider,
|
||||
nodeJs.nodeJsSetupTaskProvider
|
||||
)
|
||||
}
|
||||
|
||||
override fun configureDefaultTestFramework(test: KotlinJsTest) {
|
||||
|
||||
+4
-1
@@ -100,7 +100,10 @@ constructor(
|
||||
it.nodeJs = nodeJs
|
||||
it.executable = nodeJs.requireConfigured().nodeExecutable
|
||||
it.workingDir = npmProject.dir
|
||||
it.dependsOn(nodeJs.npmInstallTaskProvider)
|
||||
it.dependsOn(
|
||||
nodeJs.npmInstallTaskProvider,
|
||||
nodeJs.storeYarnLockTaskProvider,
|
||||
)
|
||||
it.dependsOn(compilation.compileKotlinTaskProvider)
|
||||
if (compilation.platformType == KotlinPlatformType.wasm) {
|
||||
it.nodeArgs.addWasmExperimentalArguments()
|
||||
|
||||
+11
@@ -19,6 +19,9 @@ import org.jetbrains.kotlin.gradle.targets.js.npm.tasks.KotlinNpmCachesSetup
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.tasks.KotlinNpmInstallTask
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.tasks.RootPackageJsonTask
|
||||
import org.jetbrains.kotlin.gradle.targets.js.yarn.Yarn
|
||||
import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnLockCopyTask
|
||||
import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnLockCopyTask.Companion.RESTORE_YARN_LOCK_NAME
|
||||
import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnLockCopyTask.Companion.STORE_YARN_LOCK_NAME
|
||||
import org.jetbrains.kotlin.gradle.tasks.internal.CleanableStore
|
||||
import java.io.File
|
||||
import java.io.Serializable
|
||||
@@ -73,6 +76,14 @@ open class NodeJsRootExtension(@Transient val rootProject: Project) : Configurat
|
||||
val npmCachesSetupTaskProvider: TaskProvider<out KotlinNpmCachesSetup>?
|
||||
get() = rootProject?.tasks?.withType(KotlinNpmCachesSetup::class.java)?.named(KotlinNpmCachesSetup.NAME)
|
||||
|
||||
@Suppress("UNNECESSARY_SAFE_CALL", "SAFE_CALL_WILL_CHANGE_NULLABILITY") // TODO: investigate this warning; fixing it breaks integration tests.
|
||||
val storeYarnLockTaskProvider: TaskProvider<out YarnLockCopyTask>?
|
||||
get() = rootProject?.tasks?.withType(YarnLockCopyTask::class.java)?.named(STORE_YARN_LOCK_NAME)
|
||||
|
||||
@Suppress("UNNECESSARY_SAFE_CALL", "SAFE_CALL_WILL_CHANGE_NULLABILITY") // TODO: investigate this warning; fixing it breaks integration tests.
|
||||
val restoreYarnLockTaskProvider: TaskProvider<out YarnLockCopyTask>?
|
||||
get() = rootProject?.tasks?.withType(YarnLockCopyTask::class.java)?.named(RESTORE_YARN_LOCK_NAME)
|
||||
|
||||
val rootPackageDir: File by lazy {
|
||||
rootProject.buildDir.resolve("js")
|
||||
}
|
||||
|
||||
+4
-1
@@ -80,7 +80,10 @@ internal class KotlinCompilationNpmResolver(
|
||||
npmProject.publicPackageJsonTaskName,
|
||||
listOf(compilation)
|
||||
) {
|
||||
it.dependsOn(nodeJs_.npmInstallTaskProvider)
|
||||
it.dependsOn(
|
||||
nodeJs_.npmInstallTaskProvider,
|
||||
nodeJs_.storeYarnLockTaskProvider,
|
||||
)
|
||||
it.dependsOn(packageJsonTaskHolder)
|
||||
}.also { packageJsonTask ->
|
||||
if (compilation.isMain()) {
|
||||
|
||||
+4
-1
@@ -68,7 +68,10 @@ internal class KotlinProjectNpmResolver(
|
||||
nodeJs.taskRequirements.addTaskRequirements(task)
|
||||
}
|
||||
task.dependsOn(packageJsonTaskHolder)
|
||||
task.dependsOn(nodeJs.npmInstallTaskProvider)
|
||||
task.dependsOn(
|
||||
nodeJs.npmInstallTaskProvider,
|
||||
nodeJs.storeYarnLockTaskProvider,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
@@ -133,6 +133,7 @@ abstract class KotlinPackageJsonTask : DefaultTask() {
|
||||
nodeJs.rootPackageJsonTaskProvider!!.configure { it.mustRunAfter(packageJsonTask) }
|
||||
|
||||
compilation.compileKotlinTaskProvider.dependsOn(npmInstallTask!!)
|
||||
compilation.compileKotlinTaskProvider.dependsOn(nodeJs.storeYarnLockTaskProvider!!)
|
||||
compilation.compileKotlinTaskProvider.dependsOn(packageJsonTask)
|
||||
|
||||
return packageJsonTask
|
||||
|
||||
+1
@@ -278,6 +278,7 @@ abstract class KotlinBrowserJs @Inject constructor(target: KotlinJsTarget) :
|
||||
) {
|
||||
dependsOn(
|
||||
nodeJs.npmInstallTaskProvider,
|
||||
nodeJs.storeYarnLockTaskProvider,
|
||||
target.project.tasks.named(compilation.processResourcesTaskName)
|
||||
)
|
||||
|
||||
|
||||
+6
-1
@@ -106,7 +106,12 @@ abstract class KotlinJsSubTarget(
|
||||
val compileOutputFile = compileTask.flatMap { it.outputFileProperty }
|
||||
testJs.inputFileProperty.fileProvider(compileOutputFile)
|
||||
|
||||
testJs.dependsOn(nodeJs.npmInstallTaskProvider, compileTask, nodeJs.nodeJsSetupTaskProvider)
|
||||
testJs.dependsOn(
|
||||
nodeJs.npmInstallTaskProvider,
|
||||
nodeJs.storeYarnLockTaskProvider,
|
||||
compileTask,
|
||||
nodeJs.nodeJsSetupTaskProvider
|
||||
)
|
||||
|
||||
testJs.onlyIf {
|
||||
compileOutputFile.get().exists()
|
||||
|
||||
+3
@@ -17,4 +17,7 @@ data class YarnEnv(
|
||||
val ivyDependency: String,
|
||||
val standalone: Boolean,
|
||||
val ignoreScripts: Boolean,
|
||||
val yarnLockMismatchReport: YarnLockMismatchReport,
|
||||
val reportNewYarnLock: Boolean,
|
||||
val yarnLockAutoReplace: Boolean,
|
||||
) : Serializable
|
||||
|
||||
+59
-1
@@ -6,15 +6,18 @@
|
||||
package org.jetbrains.kotlin.gradle.targets.js.yarn
|
||||
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.GradleException
|
||||
import org.gradle.api.file.FileSystemOperations
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.tasks.*
|
||||
import org.jetbrains.kotlin.gradle.utils.contentEquals
|
||||
import java.io.File
|
||||
import javax.inject.Inject
|
||||
|
||||
abstract class YarnLockCopyTask : DefaultTask() {
|
||||
|
||||
@get:InputFile
|
||||
@get:PathSensitive(PathSensitivity.RELATIVE)
|
||||
abstract val inputFile: RegularFileProperty
|
||||
@@ -35,11 +38,66 @@ abstract class YarnLockCopyTask : DefaultTask() {
|
||||
abstract val fs: FileSystemOperations
|
||||
|
||||
@TaskAction
|
||||
fun copy() {
|
||||
open fun copy() {
|
||||
fs.copy { copy ->
|
||||
copy.from(inputFile)
|
||||
copy.rename { fileName.get() }
|
||||
copy.into(outputDirectory)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val STORE_YARN_LOCK_NAME = "kotlinStoreYarnLock"
|
||||
val RESTORE_YARN_LOCK_NAME = "kotlinRestoreYarnLock"
|
||||
val UPGRADE_YARN_LOCK = "kotlinUpgradeYarnLock"
|
||||
}
|
||||
}
|
||||
|
||||
abstract class YarnLockStoreTask : YarnLockCopyTask() {
|
||||
@get:Internal
|
||||
abstract val yarnLockMismatchReportService: Property<YarnLockMismatchReportService>
|
||||
|
||||
@Input
|
||||
lateinit var yarnLockMismatchReport: Provider<YarnLockMismatchReport>
|
||||
|
||||
@Input
|
||||
lateinit var reportNewYarnLock: Provider<Boolean>
|
||||
|
||||
@Input
|
||||
lateinit var yarnLockAutoReplace: Provider<Boolean>
|
||||
|
||||
override fun copy() {
|
||||
val outputFile = outputDirectory.get().asFile.resolve(fileName.get())
|
||||
|
||||
val shouldReportMismatch = if (!outputFile.exists()) {
|
||||
reportNewYarnLock.get()
|
||||
} else {
|
||||
yarnLockMismatchReport.get() != YarnLockMismatchReport.NONE && !contentEquals(inputFile.get().asFile, outputFile)
|
||||
}
|
||||
|
||||
if (!outputFile.exists() || yarnLockAutoReplace.get()) {
|
||||
super.copy()
|
||||
}
|
||||
|
||||
if (shouldReportMismatch) {
|
||||
when (yarnLockMismatchReport.get()) {
|
||||
YarnLockMismatchReport.NONE -> {}
|
||||
YarnLockMismatchReport.WARNING -> {
|
||||
logger.warn(YARN_LOCK_MISMATCH_MESSAGE)
|
||||
}
|
||||
YarnLockMismatchReport.FAIL -> throw GradleException(YARN_LOCK_MISMATCH_MESSAGE)
|
||||
YarnLockMismatchReport.FAIL_AFTER_BUILD -> {
|
||||
yarnLockMismatchReportService.get().failOnClose()
|
||||
}
|
||||
else -> error("Unknown yarn.lock mismatch report kind")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class YarnLockMismatchReport {
|
||||
NONE,
|
||||
WARNING,
|
||||
FAIL,
|
||||
FAIL_AFTER_BUILD,
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.gradle.targets.js.yarn
|
||||
|
||||
import org.gradle.api.GradleException
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.services.BuildService
|
||||
import org.gradle.api.services.BuildServiceParameters
|
||||
import org.gradle.tooling.events.FinishEvent
|
||||
import org.gradle.tooling.events.OperationCompletionListener
|
||||
import org.jetbrains.kotlin.gradle.utils.contentEquals
|
||||
|
||||
abstract class YarnLockMismatchReportService : BuildService<YarnLockMismatchReportService.Params>, AutoCloseable,
|
||||
OperationCompletionListener {
|
||||
@Volatile
|
||||
private var shouldFailOnClose: Boolean = false
|
||||
|
||||
// Some parameters for the web server
|
||||
internal interface Params : BuildServiceParameters {
|
||||
val inputFile: RegularFileProperty
|
||||
val outputFile: RegularFileProperty
|
||||
val shouldFailOnClose: Property<Boolean>
|
||||
val reportNewYarnLock: Property<Boolean>
|
||||
}
|
||||
|
||||
fun failOnClose() {
|
||||
shouldFailOnClose = true
|
||||
}
|
||||
|
||||
override fun onFinish(event: FinishEvent?) {
|
||||
// noop
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
if (!parameters.inputFile.get().asFile.exists()) {
|
||||
return
|
||||
}
|
||||
|
||||
if (parameters.reportNewYarnLock.get() && !parameters.outputFile.get().asFile.exists()) {
|
||||
throw GradleException(YARN_LOCK_MISMATCH_MESSAGE)
|
||||
}
|
||||
|
||||
if (shouldFailOnClose || parameters.shouldFailOnClose.get() && !contentEquals(parameters.inputFile.get().asFile, parameters.outputFile.get().asFile)) {
|
||||
throw GradleException(YARN_LOCK_MISMATCH_MESSAGE)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val YARN_LOCK_MISMATCH_MESSAGE = "yarn.lock was changed. Run `kotlinActualizeYarnLock` task to actualize yarn.lock file"
|
||||
+38
-7
@@ -9,12 +9,16 @@ import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.plugins.BasePlugin
|
||||
import org.gradle.api.tasks.TaskProvider
|
||||
import org.jetbrains.kotlin.gradle.plugin.BuildEventsListenerRegistryHolder
|
||||
import org.jetbrains.kotlin.gradle.targets.js.MultiplePluginDeclarationDetector
|
||||
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.RequiresNpmDependencies
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.resolver.implementing
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.tasks.KotlinNpmInstallTask
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.tasks.RootPackageJsonTask
|
||||
import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnLockCopyTask.Companion.RESTORE_YARN_LOCK_NAME
|
||||
import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnLockCopyTask.Companion.STORE_YARN_LOCK_NAME
|
||||
import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnLockCopyTask.Companion.UPGRADE_YARN_LOCK
|
||||
import org.jetbrains.kotlin.gradle.tasks.CleanDataTask
|
||||
import org.jetbrains.kotlin.gradle.tasks.registerTask
|
||||
|
||||
@@ -69,14 +73,42 @@ open class YarnPlugin : Plugin<Project> {
|
||||
it.dependsOn(packageJsonUmbrella)
|
||||
}
|
||||
|
||||
val storeYarnLock = tasks.register("kotlinStoreYarnLock", YarnLockCopyTask::class.java) {
|
||||
it.dependsOn(kotlinNpmInstall)
|
||||
it.inputFile.set(nodeJs.rootPackageDir.resolve("yarn.lock"))
|
||||
it.outputDirectory.set(yarnRootExtension.lockFileDirectory)
|
||||
it.fileName.set(yarnRootExtension.lockFileName)
|
||||
val service = project.gradle.sharedServices
|
||||
.registerIfAbsent("kotlin-store-yarn-lock-mismatch-reporter", YarnLockMismatchReportService::class.java) {
|
||||
it.parameters.inputFile.set(nodeJs.rootPackageDir.resolve("yarn.lock"))
|
||||
it.parameters.outputFile.set(yarnRootExtension.lockFileDirectory.resolve(yarnRootExtension.lockFileName))
|
||||
it.parameters.shouldFailOnClose.set(
|
||||
provider { yarnRootExtension.requireConfigured().yarnLockMismatchReport == YarnLockMismatchReport.FAIL_AFTER_BUILD }
|
||||
)
|
||||
it.parameters.reportNewYarnLock.set(
|
||||
provider { yarnRootExtension.requireConfigured().reportNewYarnLock }
|
||||
)
|
||||
}
|
||||
|
||||
BuildEventsListenerRegistryHolder.getInstance(project).listenerRegistry
|
||||
.onTaskCompletion(service)
|
||||
|
||||
val storeYarnLock = tasks.register(STORE_YARN_LOCK_NAME, YarnLockStoreTask::class.java) { task ->
|
||||
task.dependsOn(kotlinNpmInstall)
|
||||
task.inputFile.set(nodeJs.rootPackageDir.resolve("yarn.lock"))
|
||||
task.outputDirectory.set(yarnRootExtension.lockFileDirectory)
|
||||
task.fileName.set(yarnRootExtension.lockFileName)
|
||||
|
||||
task.yarnLockMismatchReportService.set(service)
|
||||
|
||||
task.yarnLockMismatchReport = provider { yarnRootExtension.requireConfigured().yarnLockMismatchReport }
|
||||
task.reportNewYarnLock = provider { yarnRootExtension.requireConfigured().reportNewYarnLock }
|
||||
task.yarnLockAutoReplace = provider { yarnRootExtension.requireConfigured().yarnLockAutoReplace }
|
||||
}
|
||||
|
||||
val restoreYarnLock = tasks.register("kotlinRestoreYarnLock", YarnLockCopyTask::class.java) {
|
||||
tasks.register(UPGRADE_YARN_LOCK, YarnLockCopyTask::class.java) { task ->
|
||||
task.dependsOn(kotlinNpmInstall)
|
||||
task.inputFile.set(nodeJs.rootPackageDir.resolve("yarn.lock"))
|
||||
task.outputDirectory.set(yarnRootExtension.lockFileDirectory)
|
||||
task.fileName.set(yarnRootExtension.lockFileName)
|
||||
}
|
||||
|
||||
val restoreYarnLock = tasks.register(RESTORE_YARN_LOCK_NAME, YarnLockCopyTask::class.java) {
|
||||
val lockFile = yarnRootExtension.lockFileDirectory.resolve(yarnRootExtension.lockFileName)
|
||||
it.inputFile.set(yarnRootExtension.lockFileDirectory.resolve(yarnRootExtension.lockFileName))
|
||||
it.outputDirectory.set(nodeJs.rootPackageDir)
|
||||
@@ -88,7 +120,6 @@ open class YarnPlugin : Plugin<Project> {
|
||||
|
||||
kotlinNpmInstall.configure {
|
||||
it.dependsOn(restoreYarnLock)
|
||||
it.finalizedBy(storeYarnLock)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+9
@@ -44,6 +44,12 @@ open class YarnRootExtension(
|
||||
|
||||
var ignoreScripts by Property(true)
|
||||
|
||||
var yarnLockMismatchReport: YarnLockMismatchReport by Property(YarnLockMismatchReport.FAIL)
|
||||
|
||||
var reportNewYarnLock: Boolean by Property(false)
|
||||
|
||||
var yarnLockAutoReplace: Boolean by Property(false)
|
||||
|
||||
val yarnSetupTaskProvider: TaskProvider<YarnSetupTask>
|
||||
get() = project.tasks
|
||||
.withType(YarnSetupTask::class.java)
|
||||
@@ -107,6 +113,9 @@ open class YarnRootExtension(
|
||||
standalone = !download,
|
||||
ivyDependency = "com.yarnpkg:yarn:$version@tar.gz",
|
||||
ignoreScripts = ignoreScripts,
|
||||
yarnLockMismatchReport = yarnLockMismatchReport,
|
||||
reportNewYarnLock = reportNewYarnLock,
|
||||
yarnLockAutoReplace = yarnLockAutoReplace,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+20
@@ -95,3 +95,23 @@ internal inline fun <T> withTemporaryDirectory(prefix: String, action: (director
|
||||
directory.deleteRecursively()
|
||||
}
|
||||
}
|
||||
|
||||
fun contentEquals(file1: File, file2: File): Boolean {
|
||||
file1.useLines { seq1 ->
|
||||
file2.useLines { seq2 ->
|
||||
val iterator1 = seq1.iterator()
|
||||
val iterator2 = seq2.iterator()
|
||||
|
||||
while(iterator1.hasNext() == iterator2.hasNext()) {
|
||||
|
||||
if (!iterator1.hasNext()) return true
|
||||
|
||||
if (iterator1.next() != iterator2.next()) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user