Gradle: introduce CleanabeStore
This commit is contained in:
+29
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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 org.jetbrains.kotlin.gradle.tasks
|
||||
|
||||
import org.jetbrains.kotlin.gradle.BaseGradleIT
|
||||
import org.jetbrains.kotlin.gradle.GradleVersionRequired
|
||||
import org.jetbrains.kotlin.gradle.transformProjectWithPluginsDsl
|
||||
import org.junit.Test
|
||||
|
||||
|
||||
class CleanDataTaskIT : BaseGradleIT() {
|
||||
|
||||
override val defaultGradleVersion: GradleVersionRequired
|
||||
get() = GradleVersionRequired.AtLeast("5.5.1")
|
||||
|
||||
@Test
|
||||
fun testDownloadedFolderDeletion() {
|
||||
val project = transformProjectWithPluginsDsl("cleanTask")
|
||||
|
||||
project.build("testCleanTask") {
|
||||
assertSuccessful()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsPlatform
|
||||
|
||||
import java.io.File
|
||||
import java.time.*
|
||||
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.js" version "<pluginMarkerVersion>"
|
||||
}
|
||||
|
||||
String nodeJsVersion
|
||||
String nodeJsLocation
|
||||
|
||||
tasks.create("checkDownloadedFolder") {
|
||||
description = "updated last modified date for downloaded folder"
|
||||
dependsOn "build"
|
||||
doLast {
|
||||
println("check downloaded folder existance")
|
||||
def downloadedFolder = new File(project.gradle.gradleUserHomeDir, nodeJsLocation)
|
||||
if (!downloadedFolder.exists() || !downloadedFolder.isDirectory()) {
|
||||
throw new InvalidUserDataException("Downloaded folder was not found")
|
||||
}
|
||||
println("try to update lastModifiedDate")
|
||||
println(downloadedFolder.lastModified())
|
||||
def successUpdated = downloadedFolder.setLastModified(LocalDateTime.now().minusDays(35).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli())
|
||||
println(downloadedFolder.lastModified())
|
||||
if (!successUpdated) {
|
||||
throw new InvalidUserDataException("Unable to update last modified date")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.create("testCleanTask") {
|
||||
description = "check clean task behaviour"
|
||||
doLast {
|
||||
println("check deletion of downloaded folder")
|
||||
def downloadedFolder = new File(project.gradle.gradleUserHomeDir, nodeJsLocation)
|
||||
if (downloadedFolder.exists()) {
|
||||
throw new InvalidUserDataException("Folder was not deleted")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
project.kotlinNodeJs.nodeVersion = "10.15.3"
|
||||
|
||||
project.tasks.nodeKotlinClean.dependsOn(checkDownloadedFolder)
|
||||
project.tasks.testCleanTask.dependsOn(nodeKotlinClean)
|
||||
|
||||
nodeJsVersion = project.extensions.getByName("kotlinNodeJs").nodeVersion
|
||||
println("Node js version: $nodeJsVersion")
|
||||
|
||||
String platform = NodeJsPlatform.name
|
||||
String architecture = NodeJsPlatform.architecture
|
||||
|
||||
nodeJsLocation = "/nodejs/node-v$nodeJsVersion-$platform-$architecture"
|
||||
println("Use $nodeJsLocation location")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-js"
|
||||
}
|
||||
|
||||
kotlin.target.browser {}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
maven { url '<mavenLocalUrl>' }
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = 'cleanTask'
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>CleanTask</title>
|
||||
<script src="CleanTask.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import kotlin.browser.document
|
||||
|
||||
fun main() {
|
||||
document.write("Hello, world!")
|
||||
}
|
||||
+4
-1
@@ -8,6 +8,7 @@ import org.jetbrains.kotlin.gradle.targets.js.npm.KotlinNpmResolutionManager
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmApi
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.tasks.KotlinNpmInstallTask
|
||||
import org.jetbrains.kotlin.gradle.targets.js.yarn.Yarn
|
||||
import org.jetbrains.kotlin.gradle.tasks.internal.CleanableStore
|
||||
import java.io.File
|
||||
|
||||
open class NodeJsRootExtension(val rootProject: Project) {
|
||||
@@ -20,6 +21,7 @@ open class NodeJsRootExtension(val rootProject: Project) {
|
||||
}
|
||||
|
||||
var installationDir = gradleHome.resolve("nodejs")
|
||||
var cleanableStore = CleanableStore[installationDir.absolutePath]
|
||||
|
||||
var download = true
|
||||
var nodeDownloadBaseUrl = "https://nodejs.org/dist"
|
||||
@@ -64,7 +66,8 @@ open class NodeJsRootExtension(val rootProject: Project) {
|
||||
val platform = NodeJsPlatform.name
|
||||
val architecture = NodeJsPlatform.architecture
|
||||
|
||||
val nodeDir = installationDir.resolve("node-v$nodeVersion-$platform-$architecture")
|
||||
val nodeDirName = "node-v$nodeVersion-$platform-$architecture"
|
||||
val nodeDir = cleanableStore[nodeDirName].use()
|
||||
val isWindows = NodeJsPlatform.name == NodeJsPlatform.WIN
|
||||
val nodeBinDir = if (isWindows) nodeDir else nodeDir.resolve("bin")
|
||||
|
||||
|
||||
+8
-2
@@ -3,9 +3,9 @@ package org.jetbrains.kotlin.gradle.targets.js.nodejs
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.plugins.BasePlugin
|
||||
import org.gradle.api.tasks.Delete
|
||||
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension.Companion.EXTENSION_NAME
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.tasks.KotlinNpmInstallTask
|
||||
import org.jetbrains.kotlin.gradle.tasks.CleanDataTask
|
||||
|
||||
open class NodeJsRootPlugin : Plugin<Project> {
|
||||
override fun apply(project: Project): Unit = project.run {
|
||||
@@ -15,7 +15,7 @@ open class NodeJsRootPlugin : Plugin<Project> {
|
||||
"NodeJsRootPlugin can be applied only to root project"
|
||||
}
|
||||
|
||||
this.extensions.create(EXTENSION_NAME, NodeJsRootExtension::class.java, this)
|
||||
val settings = this.extensions.create(EXTENSION_NAME, NodeJsRootExtension::class.java, this)
|
||||
|
||||
val setupTask = tasks.create(NodeJsSetupTask.NAME, NodeJsSetupTask::class.java) {
|
||||
it.group = TASKS_GROUP_NAME
|
||||
@@ -27,6 +27,12 @@ open class NodeJsRootPlugin : Plugin<Project> {
|
||||
it.group = TASKS_GROUP_NAME
|
||||
it.description = "Find, download and link NPM dependencies and projects"
|
||||
}
|
||||
|
||||
tasks.create("node" + CleanDataTask.NAME, CleanDataTask::class.java) {
|
||||
it.cleanableStore = settings.cleanableStore
|
||||
it.group = TASKS_GROUP_NAME
|
||||
it.description = "Clean unused local node version"
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
+3
-1
@@ -25,7 +25,8 @@ abstract class YarnBasics : NpmApi {
|
||||
) {
|
||||
val nodeJs = NodeJsRootPlugin.apply(project)
|
||||
val nodeJsEnv = nodeJs.environment
|
||||
val yarnEnv = YarnPlugin.apply(project).environment
|
||||
val yarnPlugin = YarnPlugin.apply(project)
|
||||
val yarnEnv = yarnPlugin.environment
|
||||
|
||||
project.execWithProgress(description) { exec ->
|
||||
exec.executable = nodeJsEnv.nodeExecutable
|
||||
@@ -34,6 +35,7 @@ abstract class YarnBasics : NpmApi {
|
||||
if (project.logger.isDebugEnabled) "--verbose" else ""
|
||||
exec.workingDir = dir
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected fun yarnLockReadTransitiveDependencies(
|
||||
|
||||
+9
-1
@@ -8,6 +8,8 @@ package org.jetbrains.kotlin.gradle.targets.js.yarn
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin
|
||||
import org.jetbrains.kotlin.gradle.tasks.CleanDataTask
|
||||
import org.jetbrains.kotlin.gradle.tasks.internal.CleanableStore
|
||||
|
||||
open class YarnPlugin : Plugin<Project> {
|
||||
override fun apply(project: Project): Unit = project.run {
|
||||
@@ -17,10 +19,16 @@ open class YarnPlugin : Plugin<Project> {
|
||||
|
||||
val nodeJs = NodeJsRootPlugin.apply(this)
|
||||
|
||||
this.extensions.create(YarnRootExtension.YARN, YarnRootExtension::class.java, this)
|
||||
val yarnRootExtension = this.extensions.create(YarnRootExtension.YARN, YarnRootExtension::class.java, this)
|
||||
|
||||
tasks.create(YarnSetupTask.NAME, YarnSetupTask::class.java) {
|
||||
it.dependsOn(nodeJs.nodeJsSetupTask)
|
||||
}
|
||||
|
||||
tasks.create("yarn" + CleanDataTask.NAME, CleanDataTask::class.java) {
|
||||
it.cleanableStore = yarnRootExtension.cleanableStore
|
||||
it.description = "Clean unused local yarn version"
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
+11
-8
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.gradle.targets.js.yarn
|
||||
import org.gradle.api.Project
|
||||
import org.jetbrains.kotlin.gradle.logging.kotlinInfo
|
||||
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin
|
||||
import org.jetbrains.kotlin.gradle.tasks.internal.CleanableStore
|
||||
|
||||
open class YarnRootExtension(val project: Project) {
|
||||
init {
|
||||
@@ -20,6 +21,9 @@ open class YarnRootExtension(val project: Project) {
|
||||
|
||||
var installationDir = gradleHome.resolve("yarn")
|
||||
|
||||
// TODO: Split configuration and execution phases to support changed installationDir
|
||||
val cleanableStore = CleanableStore[installationDir.path]
|
||||
|
||||
var downloadBaseUrl = "https://github.com/yarnpkg/yarn/releases/download"
|
||||
var version = "1.21.1"
|
||||
|
||||
@@ -31,21 +35,20 @@ open class YarnRootExtension(val project: Project) {
|
||||
val useWorkspaces: Boolean
|
||||
get() = !disableWorkspaces
|
||||
|
||||
// TODO: Split configuration and execution phases to support changed settings
|
||||
internal val environment = YarnEnv(
|
||||
downloadUrl = "$downloadBaseUrl/v$version/yarn-v$version.tar.gz",
|
||||
home = cleanableStore["yarn-v$version"].use()
|
||||
)
|
||||
|
||||
internal fun executeSetup() {
|
||||
NodeJsRootPlugin.apply(project).executeSetup()
|
||||
|
||||
val env = environment
|
||||
if (!env.home.isDirectory) {
|
||||
if (!environment.home.isDirectory) {
|
||||
yarnSetupTask.setup()
|
||||
}
|
||||
}
|
||||
|
||||
internal val environment
|
||||
get() = YarnEnv(
|
||||
downloadUrl = "$downloadBaseUrl/v$version/yarn-v$version.tar.gz",
|
||||
home = installationDir.resolve("yarn-v$version")
|
||||
)
|
||||
|
||||
companion object {
|
||||
const val YARN: String = "kotlinYarn"
|
||||
|
||||
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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 org.jetbrains.kotlin.gradle.tasks
|
||||
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.jetbrains.kotlin.gradle.tasks.internal.CleanableStore
|
||||
import java.time.Duration
|
||||
import java.time.Instant
|
||||
|
||||
/**
|
||||
* Task to clean all old loaded files based on a date of special access file.
|
||||
* The access file should be updated every time when loaded files are used
|
||||
*/
|
||||
open class CleanDataTask : DefaultTask() {
|
||||
|
||||
/**
|
||||
* Path to folder.
|
||||
* Use path instead of file to avoid file scanning for change check
|
||||
*/
|
||||
@Input
|
||||
lateinit var cleanableStore: CleanableStore
|
||||
|
||||
/**
|
||||
* Time to live in days
|
||||
*/
|
||||
@Input
|
||||
var timeToLiveInDays: Long = 30
|
||||
|
||||
@Suppress("unused")
|
||||
@TaskAction
|
||||
fun exec() {
|
||||
val expirationDate = Instant.now().minus(Duration.ofDays(timeToLiveInDays))
|
||||
|
||||
cleanableStore.cleanDir(expirationDate)
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val NAME: String = "KotlinClean"
|
||||
}
|
||||
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.tasks
|
||||
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.jetbrains.kotlin.gradle.tasks.internal.CleanableStore
|
||||
import java.time.*
|
||||
|
||||
/**
|
||||
* Task to clean all old loaded files based on a last modification date.
|
||||
* All registered store in {@link CleanableStore} would be cleaned
|
||||
*/
|
||||
class CleanOldStoredDataTask : DefaultTask() {
|
||||
|
||||
/**
|
||||
* Time to live in days
|
||||
*/
|
||||
@Input
|
||||
val timeToLiveInDays: Long = 30
|
||||
|
||||
@Suppress("unused")
|
||||
@TaskAction
|
||||
fun exec() {
|
||||
val expirationDate = Instant.now().minus(Duration.ofDays(timeToLiveInDays))
|
||||
|
||||
CleanableStore.stores.forEach { (_, store) -> store.cleanDir(expirationDate) }
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val NAME: String = "clean store"
|
||||
}
|
||||
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.tasks.internal
|
||||
|
||||
import org.jetbrains.kotlin.gradle.tasks.CleanDataTask
|
||||
import java.time.Instant
|
||||
|
||||
/**
|
||||
* Store of tracked directories that can be cleaned with [CleanDataTask].
|
||||
* All directories that was not marked as used at least [CleanDataTask.timeToLiveInDays] days will be removed.
|
||||
*
|
||||
* To register store call `CleanableStore["/path/to/dir"]`.
|
||||
* Now you will be able to access files via `CleanableStore["/path/to/dir"]["file/name"].use()`
|
||||
* and it would update usage of th store.
|
||||
*/
|
||||
interface CleanableStore {
|
||||
fun cleanDir(expirationDate: Instant)
|
||||
|
||||
operator fun get(fileName: String): DownloadedFile
|
||||
|
||||
fun markUsed()
|
||||
|
||||
companion object {
|
||||
private val mutableStores = mutableMapOf<String, CleanableStore>()
|
||||
|
||||
val stores: Map<String, CleanableStore>
|
||||
get() = mutableStores.toMap()
|
||||
|
||||
operator fun get(path: String): CleanableStore =
|
||||
mutableStores.getOrPut(path) { CleanableStoreImpl(path) }
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.tasks.internal
|
||||
|
||||
import org.gradle.util.GFileUtils
|
||||
import java.io.File
|
||||
import java.nio.file.Files
|
||||
import java.time.Instant
|
||||
|
||||
internal class CleanableStoreImpl(dirPath: String) : CleanableStore {
|
||||
private val dir = File(dirPath)
|
||||
|
||||
override fun get(fileName: String): DownloadedFile =
|
||||
DownloadedFile(this, dir.resolve(fileName))
|
||||
|
||||
override fun markUsed() {
|
||||
if (dir.exists()) {
|
||||
GFileUtils.touchExisting(dir)
|
||||
}
|
||||
}
|
||||
|
||||
override fun cleanDir(expirationDate: Instant) {
|
||||
fun modificationDate(file: File): Instant {
|
||||
return Files.getLastModifiedTime(file.toPath()).toInstant()
|
||||
}
|
||||
|
||||
dir.listFiles()
|
||||
?.filter { file ->
|
||||
modificationDate(file).isBefore(expirationDate)
|
||||
}
|
||||
?.forEach { file -> file.deleteRecursively() }
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.tasks.internal
|
||||
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* Wrapper around [File] to be able to mark [store] storage as used by calling [use]`()`.
|
||||
*/
|
||||
class DownloadedFile internal constructor(
|
||||
private val store: CleanableStore,
|
||||
private val file: File
|
||||
) {
|
||||
fun use(): File {
|
||||
store.markUsed()
|
||||
return file
|
||||
}
|
||||
|
||||
fun resolve(fileName: String): DownloadedFile =
|
||||
DownloadedFile(store, file.resolve(fileName))
|
||||
}
|
||||
Reference in New Issue
Block a user