import groovy.json.JsonSlurper plugins { id 'org.gradle.toolchains.foojay-resolver-convention' version '1.0.0' } rootProject.name = 'ca-skeleton' File repositoryRoot = settingsDir.parentFile.canonicalFile File moduleRegistryFile = new File(settingsDir, 'config/architecture/modules.json') if (!moduleRegistryFile.isFile()) { throw new GradleException("Missing module registry: ${moduleRegistryFile}") } def moduleRegistry = new JsonSlurper().parse(moduleRegistryFile) if (!(moduleRegistry instanceof Map)) { throw new GradleException("Module registry root must be a JSON object: ${moduleRegistryFile}") } Set expectedRootFields = ['runtime_compositions', 'modules'] as Set if (moduleRegistry.keySet().collect { it as String }.toSet() != expectedRootFields) { throw new GradleException( "Module registry root fields must be exactly ${expectedRootFields}: ${moduleRegistryFile}") } if (!(moduleRegistry.modules instanceof List) || moduleRegistry.modules.isEmpty()) { throw new GradleException("Module registry has no modules: ${moduleRegistryFile}") } Set expectedRuntimeCompositions = ['app-bootstrap', 'sample-portfolio'] as Set if (!(moduleRegistry.runtime_compositions instanceof List) || moduleRegistry.runtime_compositions.collect { it as String }.toSet() != expectedRuntimeCompositions || moduleRegistry.runtime_compositions.size() != expectedRuntimeCompositions.size()) { throw new GradleException( "Module registry runtime_compositions must be exactly ${expectedRuntimeCompositions}: " + moduleRegistryFile) } int expectedModuleCount = 19 if (moduleRegistry.modules.size() != expectedModuleCount) { throw new GradleException( "Module registry must contain exactly ${expectedModuleCount} modules, " + "but found ${moduleRegistry.modules.size()}: ${moduleRegistryFile}") } Set moduleIds = new LinkedHashSet<>() Set gradlePaths = new LinkedHashSet<>() Set sourceDirectoryPaths = new LinkedHashSet<>() String repositoryRootPrefix = repositoryRoot.path + File.separator List> validatedModules = moduleRegistry.modules.withIndex().collect { rawModule, index -> if (!(rawModule instanceof Map)) { throw new GradleException("Module registry entry ${index} must be a JSON object.") } Map module = rawModule as Map Set expectedModuleFields = [ 'id', 'gradle_path', 'source_path', 'allowed_dependencies', 'runtime_memberships' ] as Set if (module.keySet().collect { it as String }.toSet() != expectedModuleFields) { throw new GradleException( "Module registry entry ${index} fields must be exactly ${expectedModuleFields}.") } ['id', 'gradle_path', 'source_path'].each { field -> if (!(module[field] instanceof String) || (module[field] as String).isBlank()) { throw new GradleException( "Module registry entry ${index} needs a nonblank string '${field}'.") } } if (!(module.allowed_dependencies instanceof List)) { throw new GradleException( "Module registry entry '${module.id}' needs an 'allowed_dependencies' list.") } if (!(module.runtime_memberships instanceof List)) { throw new GradleException( "Module registry entry '${module.id}' needs a 'runtime_memberships' list.") } String id = module.id as String String gradlePath = module.gradle_path as String String sourcePath = module.source_path as String List allowedDependencies = module.allowed_dependencies.withIndex().collect { dependencyId, dependencyIndex -> if (!(dependencyId instanceof String) || (dependencyId as String).isBlank()) { throw new GradleException( "Module registry entry '${id}' has a non-string or blank allowed dependency " + "at index ${dependencyIndex}.") } dependencyId as String } List runtimeMemberships = module.runtime_memberships.withIndex().collect { membership, membershipIndex -> if (!(membership instanceof String) || (membership as String).isBlank()) { throw new GradleException( "Module registry entry '${id}' has a non-string or blank runtime membership " + "at index ${membershipIndex}.") } membership as String } if (runtimeMemberships.toSet().size() != runtimeMemberships.size()) { throw new GradleException( "Module registry entry '${id}' contains duplicate runtime memberships.") } Set unknownRuntimeMemberships = runtimeMemberships.toSet() - expectedRuntimeCompositions if (!unknownRuntimeMemberships.isEmpty()) { throw new GradleException( "Module registry entry '${id}' references unknown runtime memberships " + "${unknownRuntimeMemberships.toSorted()}.") } if (!moduleIds.add(id)) { throw new GradleException("Module registry contains duplicate module id '${id}'.") } if (!gradlePath.startsWith(':')) { throw new GradleException( "Module registry entry '${id}' has Gradle path '${gradlePath}' that does not start with ':'.") } if (!gradlePaths.add(gradlePath)) { throw new GradleException("Module registry contains duplicate Gradle path '${gradlePath}'.") } if (new File(sourcePath).isAbsolute()) { throw new GradleException( "Module registry entry '${id}' source path must be repository-root-relative: '${sourcePath}'.") } File sourceDirectory = new File(repositoryRoot, sourcePath).canonicalFile if (!sourceDirectory.path.startsWith(repositoryRootPrefix)) { throw new GradleException( "Module registry entry '${id}' source path escapes the repository root: '${sourcePath}'.") } if (!sourceDirectory.isDirectory()) { throw new GradleException( "Module registry entry '${id}' source path is not an existing directory: ${sourceDirectory}") } if (!sourceDirectoryPaths.add(sourceDirectory.path)) { throw new GradleException( "Module registry entry '${id}' resolves to duplicate or aliased canonical source directory: " + "${sourceDirectory}") } [ id : id, gradle_path : gradlePath, source_directory : sourceDirectory, allowed_dependencies: allowedDependencies, runtime_memberships : runtimeMemberships ] } expectedRuntimeCompositions.each { compositionId -> Map composition = validatedModules.find { it.id == compositionId } if (composition == null || !(composition.runtime_memberships as List).contains(compositionId)) { throw new GradleException( "Runtime composition '${compositionId}' must be registered and include itself in " + 'runtime_memberships.') } } validatedModules.each { module -> module.allowed_dependencies.each { dependencyId -> if (dependencyId == module.id) { throw new GradleException( "Module registry entry '${module.id}' must not depend on itself.") } if (module.id != 'sample-portfolio' && dependencyId == 'sample-portfolio') { throw new GradleException( "Production module registry entry '${module.id}' must not allow a dependency on " + "'sample-portfolio'.") } if (!moduleIds.contains(dependencyId)) { throw new GradleException( "Module registry entry '${module.id}' references unknown allowed dependency id " + "'${dependencyId}'.") } } } validatedModules.each { module -> include module.gradle_path project(module.gradle_path).projectDir = module.source_directory }