From 37ea86fb916a24b9d7aa90fb0be55312557541a6 Mon Sep 17 00:00:00 2001 From: uzzu Date: Sun, 16 Feb 2020 15:36:20 +0900 Subject: [PATCH 1/3] Fix dotenv properties as testable --- .../kotlin/co/uzzu/dotenv/EnvProviders.kt | 24 +++++ .../co/uzzu/dotenv/gradle/DotEnvPlugin.kt | 29 +++--- .../co/uzzu/dotenv/gradle/DotEnvProperties.kt | 29 ++++-- .../co/uzzu/dotenv/InMemoryEnvProvider.kt | 8 ++ .../uzzu/dotenv/gradle/DotEnvPropertyTest.kt | 96 +++++++++++++++++++ .../co/uzzu/dotenv/gradle/DotEnvRootTest.kt | 96 +++++++++++++++++++ 6 files changed, 259 insertions(+), 23 deletions(-) create mode 100644 plugin/src/main/kotlin/co/uzzu/dotenv/EnvProviders.kt create mode 100644 plugin/src/test/kotlin/co/uzzu/dotenv/InMemoryEnvProvider.kt create mode 100644 plugin/src/test/kotlin/co/uzzu/dotenv/gradle/DotEnvPropertyTest.kt create mode 100644 plugin/src/test/kotlin/co/uzzu/dotenv/gradle/DotEnvRootTest.kt diff --git a/plugin/src/main/kotlin/co/uzzu/dotenv/EnvProviders.kt b/plugin/src/main/kotlin/co/uzzu/dotenv/EnvProviders.kt new file mode 100644 index 0000000..53335ff --- /dev/null +++ b/plugin/src/main/kotlin/co/uzzu/dotenv/EnvProviders.kt @@ -0,0 +1,24 @@ +package co.uzzu.dotenv + +/** + * Provides environment variable + */ +interface EnvProvider { + /** + * @return A environment variable for name. + */ + fun getenv(name: String): String? + + /** + * @return All environment variables. + */ + fun getenv(): Map +} + +/** + * EnvProvider implementation using System#getenv + */ +class SystemEnvProvider : EnvProvider { + override fun getenv(name: String): String? = System.getenv(name) + override fun getenv(): Map = System.getenv() +} diff --git a/plugin/src/main/kotlin/co/uzzu/dotenv/gradle/DotEnvPlugin.kt b/plugin/src/main/kotlin/co/uzzu/dotenv/gradle/DotEnvPlugin.kt index f374ab0..7280df7 100644 --- a/plugin/src/main/kotlin/co/uzzu/dotenv/gradle/DotEnvPlugin.kt +++ b/plugin/src/main/kotlin/co/uzzu/dotenv/gradle/DotEnvPlugin.kt @@ -1,6 +1,8 @@ package co.uzzu.dotenv.gradle import co.uzzu.dotenv.DotEnvParser +import co.uzzu.dotenv.EnvProvider +import co.uzzu.dotenv.SystemEnvProvider import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.plugins.ExtensionAware @@ -9,29 +11,30 @@ import java.nio.charset.Charset @Suppress("unused") class DotEnvPlugin : Plugin { override fun apply(target: Project) { - val envTemplate = target.rootProject.envTemplate() - val envSource = target.rootProject.envSource() - val envMerged = envTemplate.keys - .union(envSource.keys) - .map { it to envSource[it] } + val envProvider = SystemEnvProvider() + val dotenvTemplate = target.rootProject.dotenvTemplate() + val dotenvSource = target.rootProject.dotenvSource() + val dotenvMerged = dotenvTemplate.keys + .union(dotenvSource.keys) + .map { it to dotenvSource[it] } .toMap() - target.applyEnv(envMerged) - target.subprojects { it.applyEnv(envMerged) } + target.applyEnv(envProvider, dotenvMerged) + target.subprojects { it.applyEnv(envProvider, dotenvMerged) } } - private fun Project.applyEnv(envProperties: Map) { + private fun Project.applyEnv(envProvider: EnvProvider, dotenvProperties: Map) { val env = - extensions.create("env", DotEnvRoot::class.java, envProperties) as ExtensionAware - envProperties.forEach { (name, value) -> - env.extensions.create(name, DotEnvProperty::class.java, name, value) + extensions.create("env", DotEnvRoot::class.java, envProvider, dotenvProperties) as ExtensionAware + dotenvProperties.forEach { (name, value) -> + env.extensions.create(name, DotEnvProperty::class.java, envProvider, name, value) } } - private fun Project.envTemplate(filename: String = ".env.template") = + private fun Project.dotenvTemplate(filename: String = ".env.template"): Map = readText(filename).let(DotEnvParser::parse) - private fun Project.envSource(filename: String = ".env") = + private fun Project.dotenvSource(filename: String = ".env"): Map = readText(filename).let(DotEnvParser::parse) private fun Project.readText(filename: String): String { diff --git a/plugin/src/main/kotlin/co/uzzu/dotenv/gradle/DotEnvProperties.kt b/plugin/src/main/kotlin/co/uzzu/dotenv/gradle/DotEnvProperties.kt index f267db8..864f5bb 100644 --- a/plugin/src/main/kotlin/co/uzzu/dotenv/gradle/DotEnvProperties.kt +++ b/plugin/src/main/kotlin/co/uzzu/dotenv/gradle/DotEnvProperties.kt @@ -1,11 +1,16 @@ package co.uzzu.dotenv.gradle -open class DotEnvRoot(private val map: Map) { +import co.uzzu.dotenv.EnvProvider + +open class DotEnvRoot( + private val envProvider: EnvProvider, + private val map: Map +) { /** * @return Indicates an environment variable with specified name is present */ fun isPresent(name: String): Boolean = - System.getenv()[name]?.let { true } + envProvider.getenv()[name]?.let { true } ?: map[name]?.let { true } ?: false @@ -14,7 +19,7 @@ open class DotEnvRoot(private val map: Map) { * @throws IllegalStateException if it was not set */ fun fetch(name: String) = - System.getenv()[name] + envProvider.getenv()[name] ?: map[name] ?: throw IllegalStateException("""Environment variable $name was not set.""") @@ -23,7 +28,7 @@ open class DotEnvRoot(private val map: Map) { * @throws IllegalStateException if it was not set */ fun fetch(name: String, defaultValue: String) = - System.getenv()[name] + envProvider.getenv()[name] ?: map[name] ?: defaultValue @@ -31,17 +36,21 @@ open class DotEnvRoot(private val map: Map) { * @return An environment variable. If it was not set, returns specified default value */ fun fetchOrNull(name: String): String? = - System.getenv()[name] + envProvider.getenv()[name] ?: map[name] } -open class DotEnvProperty(private val name: String, private val dotenvValue: String?) { +open class DotEnvProperty( + private val envProvider: EnvProvider, + private val name: String, + private val dotenvValue: String? +) { /** * @return Indicates an environment variable is present */ val isPresent: Boolean - get() = System.getenv()[name]?.let { true } + get() = envProvider.getenv()[name]?.let { true } ?: dotenvValue?.let { true } ?: false @@ -51,7 +60,7 @@ open class DotEnvProperty(private val name: String, private val dotenvValue: Str */ val value: String get() = - System.getenv()[name] + envProvider.getenv()[name] ?: dotenvValue ?: throw IllegalStateException("""Environment variable $name was not set.""") @@ -59,7 +68,7 @@ open class DotEnvProperty(private val name: String, private val dotenvValue: Str * @return An environment variable. If it was not set, returns specified default value */ fun orElse(defaultValue: String): String = - System.getenv()[name] + envProvider.getenv()[name] ?: dotenvValue ?: defaultValue @@ -67,6 +76,6 @@ open class DotEnvProperty(private val name: String, private val dotenvValue: Str * @return An environment variable. If it was not set, returns null. */ fun orNull(): String? = - System.getenv()[name] + envProvider.getenv()[name] ?: dotenvValue } diff --git a/plugin/src/test/kotlin/co/uzzu/dotenv/InMemoryEnvProvider.kt b/plugin/src/test/kotlin/co/uzzu/dotenv/InMemoryEnvProvider.kt new file mode 100644 index 0000000..8dd3414 --- /dev/null +++ b/plugin/src/test/kotlin/co/uzzu/dotenv/InMemoryEnvProvider.kt @@ -0,0 +1,8 @@ +package co.uzzu.dotenv + +class InMemoryEnvProvider( + private val map: Map +) : EnvProvider { + override fun getenv(name: String): String? = map[name] + override fun getenv(): Map = map +} diff --git a/plugin/src/test/kotlin/co/uzzu/dotenv/gradle/DotEnvPropertyTest.kt b/plugin/src/test/kotlin/co/uzzu/dotenv/gradle/DotEnvPropertyTest.kt new file mode 100644 index 0000000..340c3c6 --- /dev/null +++ b/plugin/src/test/kotlin/co/uzzu/dotenv/gradle/DotEnvPropertyTest.kt @@ -0,0 +1,96 @@ +package co.uzzu.dotenv.gradle + +import co.uzzu.dotenv.InMemoryEnvProvider +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.Assertions.assertNull +import org.junit.jupiter.api.Assertions.assertThrows +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Test + +class DotEnvPropertyTest { + + @Test + fun isPresentByEnv() { + val envProvider = InMemoryEnvProvider(mapOf("FOO" to "env")) + val property = DotEnvProperty(envProvider, "FOO", null) + assertTrue(property.isPresent) + } + + @Test + fun isPresentByDotEnv() { + val envProvider = InMemoryEnvProvider(mapOf()) + val property = DotEnvProperty(envProvider, "FOO", "dotenv") + assertTrue(property.isPresent) + } + + @Test + fun isNotPresent() { + val envProvider = InMemoryEnvProvider(mapOf()) + val property = DotEnvProperty(envProvider, "FOO", null) + assertFalse(property.isPresent) + } + + @Test + fun valueFromEnv() { + val envProvider = InMemoryEnvProvider(mapOf("FOO" to "env")) + val property = DotEnvProperty(envProvider, "FOO", "dotenv") + assertEquals("env", property.value) + } + + @Test + fun valueFromDotEnv() { + val envProvider = InMemoryEnvProvider(mapOf()) + val property = DotEnvProperty(envProvider, "FOO", "dotenv") + assertEquals("dotenv", property.value) + } + + @Test + fun throwIfNoVariables() { + val envProvider = InMemoryEnvProvider(mapOf()) + val property = DotEnvProperty(envProvider, "FOO", null) + assertThrows(IllegalStateException::class.java) { property.value } + } + + @Test + fun valueOrElseFromEnv() { + val envProvider = InMemoryEnvProvider(mapOf("FOO" to "env")) + val property = DotEnvProperty(envProvider, "FOO", "dotenv") + assertEquals("env", property.value) + } + + @Test + fun valueOrElseFromDotEnv() { + val envProvider = InMemoryEnvProvider(mapOf()) + val property = DotEnvProperty(envProvider, "FOO", "dotenv") + assertEquals("dotenv", property.value) + } + + @Test + fun valueOrElse() { + val envProvider = InMemoryEnvProvider(mapOf()) + val property = DotEnvProperty(envProvider, "FOO", null) + assertEquals("default", property.orElse("default")) + } + + @Test + fun fetchOrNullFromEnv() { + val envProvider = InMemoryEnvProvider(mapOf("FOO" to "env")) + val property = DotEnvProperty(envProvider, "FOO", "dotenv") + assertEquals("env", property.orNull()) + } + + @Test + fun fetchOrNullFromDotEnv() { + val envProvider = InMemoryEnvProvider(mapOf()) + val property = DotEnvProperty(envProvider, "FOO", "dotenv") + assertEquals("dotenv", property.orNull()) + } + + @Test + fun fetchOrNull() { + val envProvider = InMemoryEnvProvider(mapOf()) + val property = DotEnvProperty(envProvider, "FOO", null) + assertNull(property.orNull()) + } +} diff --git a/plugin/src/test/kotlin/co/uzzu/dotenv/gradle/DotEnvRootTest.kt b/plugin/src/test/kotlin/co/uzzu/dotenv/gradle/DotEnvRootTest.kt new file mode 100644 index 0000000..371dca9 --- /dev/null +++ b/plugin/src/test/kotlin/co/uzzu/dotenv/gradle/DotEnvRootTest.kt @@ -0,0 +1,96 @@ +package co.uzzu.dotenv.gradle + +import co.uzzu.dotenv.InMemoryEnvProvider +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.Assertions.assertNull +import org.junit.jupiter.api.Assertions.assertThrows +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Test + +class DotEnvRootTest { + + @Test + fun isPresentByEnv() { + val envProvider = InMemoryEnvProvider(mapOf("FOO" to "env")) + val root = DotEnvRoot(envProvider, mapOf()) + assertTrue(root.isPresent("FOO")) + } + + @Test + fun isPresentByDotEnv() { + val envProvider = InMemoryEnvProvider(mapOf()) + val root = DotEnvRoot(envProvider, mapOf("FOO" to "dotenv")) + assertTrue(root.isPresent("FOO")) + } + + @Test + fun isNotPresent() { + val envProvider = InMemoryEnvProvider(mapOf()) + val root = DotEnvRoot(envProvider, mapOf()) + assertFalse(root.isPresent("FOO")) + } + + @Test + fun fetchFromEnv() { + val envProvider = InMemoryEnvProvider(mapOf("FOO" to "env")) + val root = DotEnvRoot(envProvider, mapOf("FOO" to "dotenv")) + assertEquals("env", root.fetch("FOO")) + } + + @Test + fun fetchFromDotEnv() { + val envProvider = InMemoryEnvProvider(mapOf()) + val root = DotEnvRoot(envProvider, mapOf("FOO" to "dotenv")) + assertEquals("dotenv", root.fetch("FOO")) + } + + @Test + fun throwIfNoVariables() { + val envProvider = InMemoryEnvProvider(mapOf()) + val root = DotEnvRoot(envProvider, mapOf()) + assertThrows(IllegalStateException::class.java) { root.fetch("FOO") } + } + + @Test + fun fetchOrElseFromEnv() { + val envProvider = InMemoryEnvProvider(mapOf("FOO" to "env")) + val root = DotEnvRoot(envProvider, mapOf("FOO" to "dotenv")) + assertEquals("env", root.fetch("FOO", "default")) + } + + @Test + fun fetchOrElseFromDotEnv() { + val envProvider = InMemoryEnvProvider(mapOf()) + val root = DotEnvRoot(envProvider, mapOf("FOO" to "dotenv")) + assertEquals("dotenv", root.fetch("FOO", "default")) + } + + @Test + fun fetchOrElse() { + val envProvider = InMemoryEnvProvider(mapOf()) + val root = DotEnvRoot(envProvider, mapOf()) + assertEquals("default", root.fetch("FOO", "default")) + } + + @Test + fun fetchOrNullFromEnv() { + val envProvider = InMemoryEnvProvider(mapOf("FOO" to "env")) + val root = DotEnvRoot(envProvider, mapOf("FOO" to "dotenv")) + assertEquals("env", root.fetch("FOO", "default")) + } + + @Test + fun fetchOrNullFromDotEnv() { + val envProvider = InMemoryEnvProvider(mapOf()) + val root = DotEnvRoot(envProvider, mapOf("FOO" to "dotenv")) + assertEquals("dotenv", root.fetch("FOO", "default")) + } + + @Test + fun fetchOrNull() { + val envProvider = InMemoryEnvProvider(mapOf()) + val root = DotEnvRoot(envProvider, mapOf()) + assertNull(root.fetchOrNull("FOO")) + } +} From 23f753cfcf27318c2ff4cd339a13c27fbcb69a65 Mon Sep 17 00:00:00 2001 From: uzzu Date: Fri, 17 Apr 2020 18:50:12 +0900 Subject: [PATCH 2/3] Add missing root project ktlint setting --- build.gradle.kts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/build.gradle.kts b/build.gradle.kts index c71e937..4120a84 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -11,6 +11,15 @@ allprojects { } } +ktlint { + verbose.set(true) + outputToConsole.set(true) + reporters { + reporter(org.jlleitschuh.gradle.ktlint.reporter.ReporterType.CHECKSTYLE) + } + ignoreFailures.set(true) +} + subprojects { apply(plugin = "org.jlleitschuh.gradle.ktlint") ktlint { From fcac8c79fdbeb1865cee3c54406a1f1c57bc8020 Mon Sep 17 00:00:00 2001 From: uzzu Date: Fri, 17 Apr 2020 18:50:58 +0900 Subject: [PATCH 3/3] Add danger --- .ruby-version | 1 + Dangerfile | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++ Gemfile | 8 +++++ Gemfile.lock | 70 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 161 insertions(+) create mode 100644 .ruby-version create mode 100644 Dangerfile create mode 100644 Gemfile create mode 100644 Gemfile.lock diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 0000000..5154b3f --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +2.6 diff --git a/Dangerfile b/Dangerfile new file mode 100644 index 0000000..af24e3e --- /dev/null +++ b/Dangerfile @@ -0,0 +1,82 @@ +# +# GitHub Comment +# +github.dismiss_out_of_range_messages({ + error: false, + warning: true, + message: true, + markdown: true +}) + +# +# File watching +# + +[ + ".idea/codeStyleSettings.xml", +].each do |file| + warn("Are you sure want to modify #{file} ?") if git.modified_files.include?(file) +end + +# +# Compiler warnings, errors +# + +warning_pattern = /w: (?(?:\/.+)+\.kt): \((?\d+), (?\d+)\): (?.*)/ +error_pattern = /e: (?(?:\/.+)+\.kt): \((?\d+), (?\d+)\): (?.*)/ + +target_files = (git.modified_files - git.deleted_files) + git.added_files +kotlin_compile_files = Dir.glob("**/build/kotlin/compile*Kotlin*.stdout") +unless kotlin_compile_files.empty? + compile_messages = File.read(kotlin_compile_files.first).strip + .split("\n") + .each { |s| + if match = s.match(warning_pattern) + file = Pathname(match[:path]).relative_path_from(Pathname(Dir.pwd)).to_s + if git.diff_for_file(file) + warn("#{match[:description]}", file: file, line: match[:line].to_i) + else + warn("#{file}: (#{match[:line]}, #{match[:column]}): #{match[:description]}") + end + end + if match = s.match(error_pattern) + file = Pathname(match[:path]).relative_path_from(Pathname(Dir.pwd)).to_s + if git.diff_for_file(file) + fail("#{match[:description]}", file: file, line: match[:line].to_i) + else + fail("#{file}: (#{match[:line]}, #{match[:column]}): #{match[:description]}") + end + end + } +end + +# +# ktlint +# +checkstyle_format.base_path = Dir.pwd +Dir.glob('**/build/reports/ktlint/ktlint*Check.xml') do |file| + checkstyle_format.report file +end + +# +# JUnit test results +# + +tests = [] +failures = [] +errors = [] +skipped = [] + +Dir.glob("**/build/test-results/**/TEST-*.xml") do |file| + junit.parse file + tests.concat(junit.tests) + failures.concat(junit.failures) + errors.concat(junit.errors) + skipped.concat(junit.skipped) +end +junit.tests = tests +junit.failures = failures +junit.errors = errors +junit.skipped = skipped + +junit.report diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..2b62d4d --- /dev/null +++ b/Gemfile @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +source "https://rubygems.org" +git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } + +gem 'danger' +gem 'danger-junit' +gem 'danger-checkstyle_format' diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..2bede9e --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,70 @@ +GEM + remote: https://rubygems.org/ + specs: + addressable (2.7.0) + public_suffix (>= 2.0.2, < 5.0) + claide (1.0.3) + claide-plugins (0.9.2) + cork + nap + open4 (~> 1.3) + colored2 (3.1.2) + cork (0.3.0) + colored2 (~> 3.1) + danger (7.0.0) + claide (~> 1.0) + claide-plugins (>= 0.9.2) + colored2 (~> 3.1) + cork (~> 0.1) + faraday (>= 0.9.0, < 2.0) + faraday-http-cache (~> 2.0) + git (~> 1.6) + kramdown (~> 2.0) + kramdown-parser-gfm (~> 1.0) + no_proxy_fix + octokit (~> 4.7) + terminal-table (~> 1) + danger-checkstyle_format (0.1.1) + danger-plugin-api (~> 1.0) + ox (~> 2.0) + danger-junit (1.0.0) + danger (> 2.0) + ox (~> 2.0) + danger-plugin-api (1.0.0) + danger (> 2.0) + faraday (1.0.1) + multipart-post (>= 1.2, < 3) + faraday-http-cache (2.2.0) + faraday (>= 0.8) + git (1.6.0) + rchardet (~> 1.8) + kramdown (2.1.0) + kramdown-parser-gfm (1.1.0) + kramdown (~> 2.0) + multipart-post (2.1.1) + nap (1.1.0) + no_proxy_fix (0.1.2) + octokit (4.18.0) + faraday (>= 0.9) + sawyer (~> 0.8.0, >= 0.5.3) + open4 (1.3.4) + ox (2.13.2) + public_suffix (4.0.4) + rchardet (1.8.0) + sawyer (0.8.2) + addressable (>= 2.3.5) + faraday (> 0.8, < 2.0) + terminal-table (1.8.0) + unicode-display_width (~> 1.1, >= 1.1.1) + unicode-display_width (1.7.0) + +PLATFORMS + ruby + +DEPENDENCIES + danger + danger-checkstyle_format + danger-junit + +BUNDLED WITH + 2.1.4