Skip to content
This repository was archived by the owner on Apr 13, 2022. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion plugin/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ apply plugin: 'maven'

archivesBaseName = 'license-tools'
group = 'com.cookpad.android.plugin'
version = '1.2.8'
version = '1.2.9'

dependencies {
compileOnly gradleApi()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@

package com.cookpad.android.plugin.license

import com.cookpad.android.plugin.license.task.CheckLicenses
import com.cookpad.android.plugin.license.task.GenerateLicenseJson
import com.cookpad.android.plugin.license.task.GenerateLicensePage
import com.cookpad.android.plugin.license.task.UpdateLicenses
import com.cookpad.android.plugin.license.task.*
import org.gradle.api.Plugin
import org.gradle.api.Project

Expand All @@ -18,6 +15,7 @@ class LicenseToolsPlugin : Plugin<Project> {
)
CheckLicenses.register(project)
UpdateLicenses.register(project)
UpdateMissingLicenses.register(project)
GenerateLicensePage.register(project)
GenerateLicenseJson.register(project)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ object Templates {

@Throws(NotEnoughInformationException::class)
private fun assertLicenseAndStatement(library: LibraryInfo) {
if (library.license.isNullOrBlank()) {
if (library.license.isNullOrBlank() && library.customLicenseName.isNullOrBlank()) {
throw NotEnoughInformationException(
library,
"Missing info in the \"license\" field"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ data class LibraryInfo(
val notice: String? = null,
val licenseUrl: String?,
val skip: Boolean? = null,
val forceGenerate: Boolean? = null
val forceGenerate: Boolean? = null,
val customLicenseName: String? = null,
val customLicenseContent: String? = null
) {

fun normalizedLicense(): String {
Expand Down Expand Up @@ -70,6 +72,7 @@ data class LibraryInfo(
amazon_software_license.matches(license) -> "amazon_software_license"
play_core_software_development_kit_terms_of_service.matches(license) -> "play_core_software_development_kit_terms_of_service"
pushwoosh_license.matches(license) -> "pushwoosh_license"
custom_license.matches(license) -> "custom_license"
else -> license
}
}
Expand Down Expand Up @@ -142,5 +145,6 @@ data class LibraryInfo(
val amazon_software_license = """(?i).*\bAmazon.*\bSoftware.*\bLicense\b.*""".toRegex()
val play_core_software_development_kit_terms_of_service = """(?i).*\bPlay.*\bCore.*\bSoftware.*\bDevelopment.*\bKit.*\bTerms.*\bof.*\bService\b.*""".toRegex()
val pushwoosh_license = """(?i).*\bPushwoosh.*\bLicense\b.*""".toRegex()
val custom_license = """(?i).*\bCustom.*\bLicense\b.*""".toRegex()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,60 @@ package com.cookpad.android.plugin.license.extension

import com.cookpad.android.plugin.license.data.LibraryInfo

internal fun LibraryInfo.generateLibraryInfoText(): String {
internal fun LibraryInfo.generateLibraryInfoText(updatedInfo: LibraryInfo? = null): String {
val text = StringBuffer()
text.append("- artifact: ${artifactId.withWildcardVersion()}\n")
text.append(" name: ${name ?: "#NAME#"}\n")
text.append(" copyrightHolder: ${copyrightHolder ?: "#COPYRIGHT_HOLDER#"}\n")
text.append(" license: ${license ?: "#LICENSE#"}\n")

var currentName = name ?: "#NAME#"
currentName = if (updatedInfo?.name != null && currentName == "#NAME#") {
updatedInfo.name
} else currentName
var currentCopyrightHolder = copyrightHolder ?: "#COPYRIGHT_HOLDER#"
currentCopyrightHolder = if (updatedInfo?.copyrightHolder != null && currentCopyrightHolder == "#COPYRIGHT_HOLDER#") {
updatedInfo.copyrightHolder
} else currentCopyrightHolder
var currentLicense = license ?: "#LICENSE#"
currentLicense = if (updatedInfo?.license != null && currentLicense == "#LICENSE#") {
updatedInfo.license
} else currentLicense



if (currentName != "#NAME#" || skip != true) {
text.append(" name: ${currentName}\n")
}
if (notice != null) {
if (notice.lines().size > 1) {
text.append(" notice: |\n ${notice.replace("\n", "\n ")}\n")
} else {
text.append(" notice: ${notice}\n")
}
} else if (currentCopyrightHolder != "#COPYRIGHT_HOLDER#" || skip != true) {
text.append(" copyrightHolder: ${currentCopyrightHolder}\n")
}
if (currentLicense != "#LICENSE#" || skip != true) {
text.append(" license: ${currentLicense}\n")
}
if (licenseUrl?.isNotBlank() == true) {
text.append(" licenseUrl: ${licenseUrl}\n")
} else if (updatedInfo?.licenseUrl?.isNotBlank() == true) {
text.append(" licenseUrl: ${updatedInfo.licenseUrl}\n")
}
if (url?.isNotBlank() == true) {
text.append(" url: ${url}\n")
} else if (updatedInfo?.url?.isNotBlank() == true) {
text.append(" url: ${updatedInfo.url}\n")
}

if (customLicenseName?.isNotBlank() == true) {
text.append(" customLicenseName: ${customLicenseName}\n")
}
if (customLicenseContent?.isNotBlank() == true) {
text.append(" customLicenseContent: |\n ${customLicenseContent.replace("\n", "\n ")}\n")
}

if (skip == true) {
text.append(" skip: true\n")
}
return text.toString().trim()
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,23 @@ import com.cookpad.android.plugin.license.data.LibraryInfo
import com.cookpad.android.plugin.license.exception.NotEnoughInformationException
import com.cookpad.android.plugin.license.extension.writeLicenseHtml
import com.cookpad.android.plugin.license.util.YamlUtils
import org.gradle.api.Action
import org.gradle.api.GradleException
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.internal.impldep.com.google.common.annotations.VisibleForTesting

object GenerateLicensePage {
fun register(project: Project): Task {
return project.task("generateLicensePage").doLast {
val ext = project.extensions.getByType(LicenseToolsPluginExtension::class.java)
val yamlInfoList = YamlUtils.loadToLibraryInfo(project.file(ext.licensesYaml))
project.writeLicenseHtml(yamlInfoList.toHtml(project))
return project.task("generateLicensePage").doLast(GenerateLicensePageAction())
}

// can't use lambdas to define the action if you want to allow this to be used as a cacheable task
class GenerateLicensePageAction : Action<Task> {
override fun execute(task: Task) {
val ext = task.project.extensions.getByType(LicenseToolsPluginExtension::class.java)
val yamlInfoList = YamlUtils.loadToLibraryInfo(task.project.file(ext.licensesYaml))
task.project.writeLicenseHtml(yamlInfoList.toHtml(task.project))
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) 2016 Cookpad Inc.

package com.cookpad.android.plugin.license.task

import com.cookpad.android.plugin.license.LicenseToolsPluginExtension
import com.cookpad.android.plugin.license.data.LibraryInfo
import com.cookpad.android.plugin.license.extension.generateLibraryInfoText
import com.cookpad.android.plugin.license.extension.notListedIn
import com.cookpad.android.plugin.license.util.YamlUtils
import org.gradle.api.Action
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.internal.impldep.com.google.common.annotations.VisibleForTesting

object UpdateMissingLicenses {
fun register(project: Project): Task {
return project.task("updateMissingLicenses").doLast(UpdateMissingLicensesAction())
}

// can't use lambdas to define the action if you want to allow this to be used as a cacheable task
class UpdateMissingLicensesAction : Action<Task> {
override fun execute(task: Task) {
val ext = task.project.extensions.getByType(LicenseToolsPluginExtension::class.java)
val resolvedArtifacts =
CheckLicenses.resolveProjectDependencies(task.project, ext.ignoredProjects)
val dependencyLicenses =
CheckLicenses.loadDependencyLicenses(task.project, resolvedArtifacts, ext.ignoredGroups)
updateMissingLicensesYaml(task.project, ext.licensesYaml, dependencyLicenses)

}
}
@VisibleForTesting
internal fun updateMissingLicensesYaml(
project: Project,
licensesYaml: String,
dependencyLicenses: List<LibraryInfo>
) {
// Dedup and sort dependencies
val sortedDependencies = dependencyLicenses.associateBy { it.artifactId.withWildcardVersion() }
.toSortedMap { o1, o2 ->
o1.compareTo(o2, ignoreCase = true)
}.values.toList()
val yamlList = YamlUtils.loadToLibraryInfo(project.file(licensesYaml))
val notInDependencies = sortedDependencies.notListedIn(yamlList)


project.file(licensesYaml).apply {
// Clean content
writeText("")
for (libraryInfo in yamlList) {
sortedDependencies.find { it.artifactId.matches(libraryInfo.artifactId) }?.let {
appendText("${libraryInfo.generateLibraryInfoText(it)}\n")
}
}
for (libraryInfo in notInDependencies) {
appendText("${libraryInfo.generateLibraryInfoText()}\n")
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ object YamlUtils {
forceGenerate = it.getOrDefault(
"forceGenerate",
"false"
).toString().toBoolean()
).toString().toBoolean(),
customLicenseName = it["customLicenseName"] as String?,
customLicenseContent = it["customLicenseContent"] as String?
)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<div class="library">
<h1 class="title">${library.name}</h1>
<p class="notice">${library.copyrightStatement.replaceAll("\n\n", "<br/>")}</p>
<% print library.url == null || library.url.isEmpty() ? "" : """<p><a href="${library.url}">${library.url}</a></p>""" %>
<div class="license">
<h2>${library.customLicenseName}</h2>
${library.customLicenseContent}
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package com.cookpad.android.plugin.license.task

import com.cookpad.android.plugin.license.data.ArtifactId
import com.cookpad.android.plugin.license.loadYamlFromResources
import com.cookpad.android.plugin.license.util.YamlUtils
import com.google.common.truth.Truth.assertThat
import com.nhaarman.mockitokotlin2.doReturn
import com.nhaarman.mockitokotlin2.whenever
import org.gradle.api.Project
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.junit.MockitoJUnitRunner
import java.io.File

@RunWith(MockitoJUnitRunner::class)
class UpdateMissingLicensesTest {

@get:Rule
val tempFolder = TemporaryFolder()
private lateinit var licensesYamlFile: File

@Mock
lateinit var project: Project

@Before
fun setUp() {
licensesYamlFile = tempFolder.newFile()
whenever(project.file(FILENAME_LICENSES_YAML)) doReturn licensesYamlFile
}

@After
fun tearDown() {
licensesYamlFile.delete()
}

@Test
fun updateLicensesYaml_addsMissingDependencies() {
val licenses = loadYamlFromResources("yaml/missing_licenses.yml")
assertThat(licenses.size).isEqualTo(3)
UpdateMissingLicenses.updateMissingLicensesYaml(project, FILENAME_LICENSES_YAML, licenses)

var result = YamlUtils.loadToLibraryInfo(licensesYamlFile)
assertThat(result.size).isEqualTo(3)

val update = loadYamlFromResources("yaml/missing_licenses_update.yml")
UpdateMissingLicenses.updateMissingLicensesYaml(project, FILENAME_LICENSES_YAML, update)
result = YamlUtils.loadToLibraryInfo(licensesYamlFile)
assertThat(result.size).isEqualTo(4)

assertThat(result.size).isEqualTo(update.size)
for (info in update) {
val finalInfo = result.find { it.artifactId.matches(info.artifactId) }
assertThat(finalInfo).isNotNull()
if (info.artifactId.matches(ArtifactId("com.android.support", "+", "+"))) {
assertThat(info).isNotEqualTo(finalInfo)
assertThat(finalInfo!!.license).isEqualTo("apache2")
} else {
assertThat(info).isEqualTo(finalInfo)
}
}
}

@Test
fun updateLicensesYaml_removeMissingDependencies() {
val licenses = loadYamlFromResources("yaml/missing_licenses_update.yml")
assertThat(licenses.size).isEqualTo(4)
UpdateMissingLicenses.updateMissingLicensesYaml(project, FILENAME_LICENSES_YAML, licenses)

var result = YamlUtils.loadToLibraryInfo(licensesYamlFile)
assertThat(result.size).isEqualTo(4)

val update = loadYamlFromResources("yaml/removed_licenses.yml")
UpdateMissingLicenses.updateMissingLicensesYaml(project, FILENAME_LICENSES_YAML, update)
result = YamlUtils.loadToLibraryInfo(licensesYamlFile)
assertThat(result.size).isEqualTo(3)
}

companion object {
private const val FILENAME_LICENSES_YAML = "licenses.yml"
}
}
41 changes: 41 additions & 0 deletions plugin/src/test/resources/yaml/licenses.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,44 @@
license: cc-by-4.0
licenseUrl: https://creativecommons.org/licenses/by/4.0/legalcode
url: https://creativecommons.org/
- artifact: org.concentus:Concentus:+
name: Concentus
copyrightHolder: |
Copyright (c) by various holding parties, including (but not limited to):
Skype Limited, Xiph.Org Foundation, CSIRO, Microsoft Corporation,
Jean-Marc Valin, Gregory Maxwell, Mark Borgerding, Timothy B. Terriberry,
Logan Stromberg. All rights are reserved by their respective holders.
license: Custom License
customLicenseName: Concentus License
customLicenseContent: |
Copyright (c) by various holding parties, including (but not limited to):
Skype Limited, Xiph.Org Foundation, CSIRO, Microsoft Corporation,
Jean-Marc Valin, Gregory Maxwell, Mark Borgerding, Timothy B. Terriberry,
Logan Stromberg. All rights are reserved by their respective holders.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

* Neither the name of Internet Society, IETF or IETF Trust, nor the
names of specific contributors, may be used to endorse or promote
products derived from this software without specific prior written
permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
licenseUrl: https://github.com/lostromb/concentus/blob/master/LICENSE
17 changes: 17 additions & 0 deletions plugin/src/test/resources/yaml/missing_licenses.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
- artifact: com.android.support:+:+
name: Android Support Libraries
copyrightHolder: The Android Open Source Project
license: apache2
- artifact: com.mopub:libAvid-mopub:+
name: AVID SDK (MoPub Android namespace)
copyrightHolder: MoPub (a division of Twitter, Inc.)
license: MoPub SDK License
licenseUrl: http://www.mopub.com/legal/sdk-license-agreement/
url: https://github.com/mopub/mopub-android-sdk
# This is example license
- artifact: com.example:example-common:+
name: example-common
copyrightHolder: Example Author
license: #LICENSE#
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
url: https://example.com/example/example-common
Loading