feat: redis, fileserver, httpclient 런타임 시점 구현 추가
This commit is contained in:
+132
-4
@@ -1,5 +1,6 @@
|
||||
import groovy.json.JsonSlurper
|
||||
import org.gradle.api.artifacts.dsl.LockMode
|
||||
import org.gradle.api.artifacts.component.ModuleComponentIdentifier
|
||||
import org.gradle.api.tasks.bundling.AbstractArchiveTask
|
||||
import org.gradle.api.tasks.bundling.Jar
|
||||
|
||||
@@ -232,8 +233,13 @@ configure(subprojects.findAll { it.childProjects.isEmpty() }) {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||
testImplementation 'org.springframework.boot:spring-boot-starter-webmvc-test'
|
||||
if (project.path in [':domain-core', ':application-core', ':shared-contract']) {
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter'
|
||||
testImplementation 'org.assertj:assertj-core'
|
||||
} else {
|
||||
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||
testImplementation 'org.springframework.boot:spring-boot-starter-webmvc-test'
|
||||
}
|
||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
||||
|
||||
spotbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.14.0' // D4 code-level security
|
||||
@@ -555,7 +561,7 @@ tasks.register('verifyCleanArchitectureDependencies') {
|
||||
group = 'verification'
|
||||
description = 'Verifies Clean Architecture project dependency direction.'
|
||||
|
||||
File moduleRegistryFile = new File(rootProject.projectDir.parentFile, '.harness/project/modules.yaml')
|
||||
File moduleRegistryFile = new File(rootProject.projectDir, 'config/architecture/modules.json')
|
||||
inputs.file(moduleRegistryFile)
|
||||
|
||||
doLast {
|
||||
@@ -619,18 +625,140 @@ tasks.register('verifyCleanArchitectureDependencies') {
|
||||
}
|
||||
.toSet()
|
||||
|
||||
if (moduleName != 'sample-portfolio' && actual.contains('sample-portfolio')) {
|
||||
throw new GradleException(
|
||||
"Module ':${moduleName}' has a forbidden production dependency on " +
|
||||
"':sample-portfolio'. The sample module may only be consumed through " +
|
||||
"non-production fixture configurations."
|
||||
)
|
||||
}
|
||||
|
||||
Set<String> forbidden = actual - allowed
|
||||
if (!forbidden.isEmpty()) {
|
||||
throw new GradleException(
|
||||
"Module ':${moduleName}' has forbidden project dependencies ${forbidden}. " +
|
||||
"Allowed dependencies are ${allowed}. " +
|
||||
"Production modules must not depend on ':sample-ticket', and adapter modules must not depend on each other."
|
||||
"Production modules must not depend on ':sample-portfolio'; " +
|
||||
"all project edges must be explicitly registered."
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def verifyApplicationCoreDependencyPurity = tasks.register('verifyApplicationCoreDependencyPurity') {
|
||||
group = 'verification'
|
||||
description = 'Verifies application-core has only project production dependencies and no diagnostic frameworks on application classpaths.'
|
||||
|
||||
doLast {
|
||||
Project application = project(':application-core')
|
||||
List<String> violations = []
|
||||
|
||||
['api', 'implementation', 'compileOnly', 'runtimeOnly'].each { configurationName ->
|
||||
def configuration = application.configurations.findByName(configurationName)
|
||||
if (configuration == null) {
|
||||
return
|
||||
}
|
||||
configuration.dependencies.each { dependency ->
|
||||
if (!(dependency instanceof ProjectDependency)) {
|
||||
violations << "${configurationName}: non-project production dependency " +
|
||||
"${dependency.group ?: '<no-group>'}:${dependency.name}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Closure<Boolean> forbiddenGroup = { String groupName ->
|
||||
groupName != null && (
|
||||
groupName.startsWith('org.springframework') ||
|
||||
groupName == 'org.slf4j' ||
|
||||
groupName == 'ch.qos.logback' ||
|
||||
groupName == 'org.apache.logging.log4j' ||
|
||||
groupName == 'io.micrometer')
|
||||
}
|
||||
['compileClasspath', 'runtimeClasspath', 'testCompileClasspath', 'testRuntimeClasspath']
|
||||
.each { configurationName ->
|
||||
def configuration = application.configurations.getByName(configurationName)
|
||||
configuration.incoming.resolutionResult.allComponents.each { component ->
|
||||
if (component.id instanceof ModuleComponentIdentifier &&
|
||||
forbiddenGroup(component.id.group)) {
|
||||
violations << "${configurationName}: forbidden resolved dependency " +
|
||||
"${component.id.group}:${component.id.module}:${component.id.version}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!violations.isEmpty()) {
|
||||
throw new GradleException(
|
||||
"verifyApplicationCoreDependencyPurity: ${violations.size()} violation(s):\n " +
|
||||
violations.toSorted().join('\n '))
|
||||
}
|
||||
logger.lifecycle(
|
||||
'verifyApplicationCoreDependencyPurity: OK — application-core production declarations are project-only and application classpaths contain no Spring/logging/metrics frameworks.')
|
||||
}
|
||||
}
|
||||
|
||||
project(':application-core').tasks.named('check') {
|
||||
dependsOn verifyApplicationCoreDependencyPurity
|
||||
}
|
||||
|
||||
def verifyConfigurationPropertiesProcessor = tasks.register('verifyConfigurationPropertiesProcessor') {
|
||||
group = 'verification'
|
||||
description = 'Verifies every registered leaf declares the Spring configuration processor exactly when its main source owns @ConfigurationProperties.'
|
||||
|
||||
File moduleRegistryFile = new File(rootProject.projectDir, 'config/architecture/modules.json')
|
||||
inputs.file(moduleRegistryFile)
|
||||
|
||||
doLast {
|
||||
def moduleRegistry = new JsonSlurper().parse(moduleRegistryFile)
|
||||
List<String> violations = []
|
||||
def processorDeclaration = ~/^\s*annotationProcessor\s+['"]org\.springframework\.boot:spring-boot-configuration-processor['"]\s*$/
|
||||
|
||||
moduleRegistry.modules.each { module ->
|
||||
File leafDirectory = rootProject.projectDir.parentFile.toPath()
|
||||
.resolve(module.source_path as String)
|
||||
.normalize()
|
||||
.toFile()
|
||||
File mainSource = new File(leafDirectory, 'src/main')
|
||||
File buildFile = new File(leafDirectory, 'build.gradle')
|
||||
|
||||
int propertyAnnotationCount = 0
|
||||
if (mainSource.isDirectory()) {
|
||||
mainSource.eachFileRecurse { File sourceFile ->
|
||||
if (sourceFile.name.endsWith('.java')) {
|
||||
propertyAnnotationCount += sourceFile.text.count('@ConfigurationProperties(')
|
||||
}
|
||||
}
|
||||
}
|
||||
int processorCount = buildFile.readLines().count { String line ->
|
||||
processorDeclaration.matcher(line).matches()
|
||||
}
|
||||
|
||||
boolean ownsConfigurationProperties = propertyAnnotationCount > 0
|
||||
if (ownsConfigurationProperties && processorCount != 1) {
|
||||
violations << "${module.id}: ${propertyAnnotationCount} @ConfigurationProperties occurrence(s), " +
|
||||
"but ${processorCount} configuration-processor declaration(s)"
|
||||
} else if (!ownsConfigurationProperties && processorCount != 0) {
|
||||
violations << "${module.id}: no @ConfigurationProperties occurrence, but " +
|
||||
"${processorCount} configuration-processor declaration(s)"
|
||||
}
|
||||
}
|
||||
|
||||
if (!violations.isEmpty()) {
|
||||
throw new GradleException(
|
||||
"verifyConfigurationPropertiesProcessor: ${violations.size()} parity violation(s):\n " +
|
||||
violations.toSorted().join('\n '))
|
||||
}
|
||||
logger.lifecycle(
|
||||
"verifyConfigurationPropertiesProcessor: OK — all ${moduleRegistry.modules.size()} registered leaves have exact configuration-processor parity.")
|
||||
}
|
||||
}
|
||||
|
||||
configure(subprojects.findAll { it.childProjects.isEmpty() }) {
|
||||
tasks.named('check') {
|
||||
dependsOn verifyConfigurationPropertiesProcessor
|
||||
}
|
||||
}
|
||||
|
||||
// verifyOneTypePerFile — one public top-level type per file, file name == type name
|
||||
// (code-conventions I6). Rationale in README.md.
|
||||
tasks.register('verifyOneTypePerFile') {
|
||||
|
||||
Reference in New Issue
Block a user