From 3bfcf98e21b4710302677c8a9712f2a00cfa237a Mon Sep 17 00:00:00 2001 From: gewuyou Date: Wed, 16 Jul 2025 12:16:32 +0800 Subject: [PATCH] =?UTF-8?q?ci:=20=E6=B7=BB=E5=8A=A0=20GitLab=20CI/CD=20?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E5=B9=B6=E5=AE=9E=E7=8E=B0=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E5=8C=96=E6=B5=81=E7=A8=8B-=20=E6=96=B0=E5=A2=9E=20.gitlab-ci.?= =?UTF-8?q?main.yml=E3=80=81.gitlab-ci.other.yml=20=E5=92=8C=20.gitlab-ci.?= =?UTF-8?q?test.yml=20=E6=96=87=E4=BB=B6=20-=20=E5=AE=9E=E7=8E=B0=E4=BA=86?= =?UTF-8?q?=20build=E3=80=81tag=E3=80=81publish=E3=80=81reset=20=E5=92=8C?= =?UTF-8?q?=20mirror=E7=AD=89=E9=98=B6=E6=AE=B5=E7=9A=84=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E5=8C=96=E6=B5=81=E7=A8=8B=20-=20=E6=B7=BB=E5=8A=A0=E4=BA=86?= =?UTF-8?q?=20check=20=E5=92=8C=20test=20=E5=88=86=E6=94=AF=E7=9A=84?= =?UTF-8?q?=E6=9E=84=E5=BB=BA=E5=92=8C=E5=8F=91=E5=B8=83=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=20-=20=E6=9B=B4=E6=96=B0=E4=BA=86=20build.gradle.kts=20?= =?UTF-8?q?=E5=92=8C=20settings.gradle.kts=20=E6=96=87=E4=BB=B6=EF=BC=8C?= =?UTF-8?q?=E8=B0=83=E6=95=B4=E4=BA=86=E9=A1=B9=E7=9B=AE=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=20-=20=E6=96=B0=E5=A2=9E=20MdcContextElement=20=E5=92=8C=20Cor?= =?UTF-8?q?outineMdcWebFilter=20=E7=B1=BB=EF=BC=8C=E7=94=A8=E4=BA=8E?= =?UTF-8?q?=E5=8D=8F=E7=A8=8B=E4=B8=AD=E7=9A=84=20MDC=20=E4=B8=8A=E4=B8=8B?= =?UTF-8?q?=E6=96=87=E4=BC=A0=E6=92=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 +- .gitlab-ci.yml | 10 ++ .gitlab/workflows/.gitlab-ci.main.yml | 131 ++++++++++++++++++ .gitlab/workflows/.gitlab-ci.other.yml | 44 ++++++ .gitlab/workflows/.gitlab-ci.test.yml | 24 ++++ build.gradle.kts | 35 +++-- .../forgeboot-context-impl/build.gradle.kts | 1 + .../context/impl/element/MdcContextElement.kt | 53 +++++++ .../impl/filter/CoroutineMdcWebFilter.kt | 46 ++++++ forgeboot-webmvc/spec/build.gradle.kts | 4 +- settings.gradle.kts | 72 +++++----- 11 files changed, 369 insertions(+), 53 deletions(-) create mode 100644 .gitlab-ci.yml create mode 100644 .gitlab/workflows/.gitlab-ci.main.yml create mode 100644 .gitlab/workflows/.gitlab-ci.other.yml create mode 100644 .gitlab/workflows/.gitlab-ci.test.yml create mode 100644 forgeboot-context/forgeboot-context-impl/src/main/kotlin/com/gewuyou/forgeboot/context/impl/element/MdcContextElement.kt create mode 100644 forgeboot-context/forgeboot-context-impl/src/main/kotlin/com/gewuyou/forgeboot/context/impl/filter/CoroutineMdcWebFilter.kt diff --git a/.gitignore b/.gitignore index f1e2a6d..16fd2e0 100644 --- a/.gitignore +++ b/.gitignore @@ -21,7 +21,7 @@ build/ out/ !**/src/main/**/out/ !**/src/test/**/out/ - +/temp ### Kotlin ### .kotlin diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..8e28bed --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,10 @@ +include: + - local: '/.gitlab/workflows/.gitlab-ci.test.yml' + rules: + - if: '$CI_COMMIT_BRANCH == "test"' + - local: '/.gitlab/workflows/.gitlab-ci.main.yml' + rules: + - if: '$CI_COMMIT_BRANCH == "main"' + - local: '/.gitlab/workflows/.gitlab-ci.other.yml' + rules: + - if: '$CI_COMMIT_BRANCH != "main" && $CI_COMMIT_BRANCH != "test"' \ No newline at end of file diff --git a/.gitlab/workflows/.gitlab-ci.main.yml b/.gitlab/workflows/.gitlab-ci.main.yml new file mode 100644 index 0000000..3035768 --- /dev/null +++ b/.gitlab/workflows/.gitlab-ci.main.yml @@ -0,0 +1,131 @@ +stages: + - build + - tag + - publish + - reset + - mirror + +variables: + GRADLE_USER_HOME: "$CI_PROJECT_DIR/.gradle" + +before_script: + - rm -rf $GRADLE_USER_HOME/.tmp || true + +# ✅ Build 阶段 +build: + stage: build + rules: + - if: '$CI_COMMIT_BRANCH == "main"' + cache: + key: + files: + - gradle/libs.versions.toml + - "**/*.gradle.kts" + prefix: lab-agent + paths: + - .gradle/caches/ + - .gradle/wrapper/ + - .gradle/kotlin-profile/ + - .kotlin/ + policy: pull-push + script: + - ./gradlew clean build + tags: + - java + +# 🏷️ 自动打标签 +tag: + stage: tag + needs: [ "build" ] + image: alpine:latest + rules: + - if: '$CI_COMMIT_BRANCH == "main"' + script: + - set -euo pipefail + - apk add --no-cache git + - git config --global user.email "pipeline@${GITLAB_URL}" + - git config --global user.name "Project Pipeline Bot" + - git fetch --tags + - git fetch origin main + - MAIN_COMMIT=$(git rev-parse origin/main) + - echo "🔗 main commit -> $MAIN_COMMIT" + + - LATEST_TAG=$(git tag --list '*' --sort=-v:refname | head -n1 || true) + - if [ -z "$LATEST_TAG" ]; then LATEST_TAG="0.0.0"; fi + - echo "🔖 最新 tag -> $LATEST_TAG" + + - VERSION=${LATEST_TAG#v} + - MAJOR=$(echo "$VERSION" | cut -d. -f1) + - MINOR=$(echo "$VERSION" | cut -d. -f2) + - PATCH=$(echo "$VERSION" | cut -d. -f3) + + - PATCH=$((PATCH + 1)) + - NEW_TAG="${MAJOR}.${MINOR}.${PATCH}" + - echo "🏷️ 新 tag -> $NEW_TAG" + + - if git tag --points-at "$MAIN_COMMIT" | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' > /dev/null; then + echo "⏭️ 已存在 tag,跳过创建"; + exit 0; + fi + + - git tag $NEW_TAG $MAIN_COMMIT + - git push https://oauth2:${PIPELINE_BOT_TOKEN}@${GITLAB_URL}/${CI_PROJECT_PATH}.git $NEW_TAG + - echo "✅ tag $NEW_TAG 已推送" + tags: + - java + +# 📦 发布至 GitLab Maven 仓库 +publish: + stage: publish + needs: [ "tag" ] + rules: + - if: '$CI_COMMIT_BRANCH == "main"' + script: + - ./gradlew publishMavenJavaPublicationToGitLabRepository + tags: + - java + +# 🔄 重建 test 分支 +reset: + stage: reset + needs: [ "build" ] + image: alpine:latest + rules: + - if: '$CI_COMMIT_BRANCH == "main"' + script: + - set -euo pipefail + - apk add --no-cache git + - git config --global user.email "pipeline@${GITLAB_URL}" + - git config --global user.name "Project Pipeline Bot" + - git clone --depth=1 --branch main https://oauth2:${PIPELINE_BOT_TOKEN}@${GITLAB_URL}/${CI_PROJECT_PATH}.git repo + - cd repo + - git checkout -B test + - git push origin test --force + - echo "✅ test 分支已重建完成" + tags: + - java +# Mirror to GitHub +mirror-to-github: + stage: mirror + needs: [ "build" ] + image: alpine:latest + rules: + - if: '$CI_COMMIT_BRANCH == "main"' + script: + - set -euo pipefail + - apk add --no-cache git openssh + - git config --global user.name "Project Pipeline Bot" + - git config --global user.email "pipeline@${GITLAB_URL}" + + - echo "🔄 正在 clone 当前 GitLab 仓库..." + - git clone --depth=1 --branch main https://oauth2:${PIPELINE_BOT_TOKEN}@${GITLAB_URL}/${CI_PROJECT_PATH}.git repo + - cd repo + + - echo "🔗 添加 GitHub 远程地址..." + - git remote add github https://x-access-token:${GITHUB_PUSH_TOKEN}@github.com/GeWuYou/forgeboot.git + + - echo "🚀 推送 main 分支到 GitHub..." + - git push github main --force + - echo "✅ GitHub 同步完成" + tags: + - java diff --git a/.gitlab/workflows/.gitlab-ci.other.yml b/.gitlab/workflows/.gitlab-ci.other.yml new file mode 100644 index 0000000..117bfd0 --- /dev/null +++ b/.gitlab/workflows/.gitlab-ci.other.yml @@ -0,0 +1,44 @@ +stages: + - check +before_script: + - export GRADLE_USER_HOME=$CI_PROJECT_DIR/.gradle + - rm -rf $CI_PROJECT_DIR/.gradle/.tmp || true +# ✅ check Job +check: + stage: check + cache: + key: + files: + - gradle/libs.versions.toml + - "**/build.gradle.kts" + prefix: lab-agent + paths: + - .gradle/caches/ + - .gradle/wrapper/ + - .gradle/kotlin-profile/ + - .kotlin/ + - buildSrc/.gradle/ + - buildSrc/build/ + policy: pull-push + script: + - echo "🔧 授予 gradlew 执行权限..." + - chmod +x gradlew + + - echo "🧪 开始快速编译检查(跳过打包和测试)..." + - ./gradlew compileKotlin compileJava --stacktrace || (echo "❌ 编译失败!请检查错误日志" && exit 1) + + - echo "📦 当前缓存目录:" + - ls -la .gradle/ + + - echo "📦 Gradle 缓存结构检查" + - ls -la .gradle/caches/modules-2/files-2.1 || true + - echo "📦 缓存文件数量:$(find .gradle/caches/modules-2/files-2.1 -type f | wc -l)" + + - echo "🛑 停止 Gradle 守护进程..." + - ./gradlew --stop + + - echo "🔍 当前 Java 进程:" + - ps aux | grep java || true + tags: + - docker + - java \ No newline at end of file diff --git a/.gitlab/workflows/.gitlab-ci.test.yml b/.gitlab/workflows/.gitlab-ci.test.yml new file mode 100644 index 0000000..46d99be --- /dev/null +++ b/.gitlab/workflows/.gitlab-ci.test.yml @@ -0,0 +1,24 @@ +stages: + - build + - publish + +# 🧪 test 构建 +build: + stage: build + rules: + - if: '$CI_COMMIT_BRANCH == "test"' + script: + - ./gradlew clean build + tags: + - java + +# 🧪 test 发布 SNAPSHOT 包(允许覆盖) +publish: + stage: publish + needs: ["build"] + rules: + - if: '$CI_COMMIT_BRANCH == "test"' + script: + - ./gradlew publishMavenJavaPublicationToGitLabRepository + tags: + - java diff --git a/build.gradle.kts b/build.gradle.kts index b087d10..a44ad8d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -11,13 +11,10 @@ plugins { // Kotlin kapt 支持 alias(libs.plugins.kotlin.kapt) id(libs.plugins.kotlin.jvm.get().pluginId) - alias(libs.plugins.gradleMavenPublishPlugin) -} -java { - withSourcesJar() - withJavadocJar() +// alias(libs.plugins.gradleMavenPublishPlugin) } + // 配置 SCM 版本插件 scmVersion { tag { @@ -93,27 +90,26 @@ subprojects { tasks.named("sourcesJar") { dependsOn("generateI18nKeys") } - } version = rootProject.version apply { - plugin(libs.plugins.java.get().pluginId) - plugin(libs.plugins.javaLibrary.get().pluginId) +// plugin(libs.plugins.java.get().pluginId) +// plugin(libs.plugins.javaLibrary.get().pluginId) plugin(libs.plugins.maven.publish.get().pluginId) plugin(libs.plugins.kotlin.jvm.get().pluginId) plugin(libs.plugins.axionRelease.get().pluginId) plugin(libs.plugins.kotlin.kapt.get().pluginId) - plugin(libs.plugins.gradleMavenPublishPlugin.get().pluginId) +// plugin(libs.plugins.gradleMavenPublishPlugin.get().pluginId) // 导入仓库配置 from(file("$configDir/repositories.gradle.kts")) } - // 中央仓库 - mavenPublishing { - publishToMavenCentral("DEFAULT", true) - signAllPublications() - } +// // 中央仓库 +// mavenPublishing { +// publishToMavenCentral("https://s01.oss.sonatype.org/", true) +// signAllPublications() +// } // 发布配置 publishing { repositories { @@ -131,6 +127,17 @@ subprojects { password = System.getenv("GITHUB_TOKEN") } } + maven { + name = "GitLab" + url = uri("https://gitlab.snow-lang.com/api/v4/projects/18/packages/maven") + credentials(HttpHeaderCredentials::class) { + name = "Private-Token" + value = System.getenv("PIPELINE_BOT_TOKEN") // 存储为 GitLab CI/CD Secret + } + authentication { + create("header", HttpHeaderAuthentication::class) + } + } // Gitea 仓库 val host = System.getenv("GITEA_HOST") host?.let { diff --git a/forgeboot-context/forgeboot-context-impl/build.gradle.kts b/forgeboot-context/forgeboot-context-impl/build.gradle.kts index 2b189da..cd82be8 100644 --- a/forgeboot-context/forgeboot-context-impl/build.gradle.kts +++ b/forgeboot-context/forgeboot-context-impl/build.gradle.kts @@ -6,4 +6,5 @@ dependencies { implementation(platform(libs.springBootDependencies.bom)) implementation(libs.springBoot.autoconfigure) implementation(libs.jackson.databind) + implementation(libs.kotlinxCoroutines.reactor) } diff --git a/forgeboot-context/forgeboot-context-impl/src/main/kotlin/com/gewuyou/forgeboot/context/impl/element/MdcContextElement.kt b/forgeboot-context/forgeboot-context-impl/src/main/kotlin/com/gewuyou/forgeboot/context/impl/element/MdcContextElement.kt new file mode 100644 index 0000000..ae9d022 --- /dev/null +++ b/forgeboot-context/forgeboot-context-impl/src/main/kotlin/com/gewuyou/forgeboot/context/impl/element/MdcContextElement.kt @@ -0,0 +1,53 @@ +package com.gewuyou.forgeboot.context.impl.element + +import kotlinx.coroutines.ThreadContextElement +import org.slf4j.MDC +import kotlin.coroutines.AbstractCoroutineContextElement +import kotlin.coroutines.CoroutineContext + +/** + * MDC上下文元素,用于在协程中传播MDC(Mapped Diagnostic Context)上下文。 + * + * MDC 是 SLF4J 提供的一个日志诊断工具,允许将特定信息绑定到当前线程的上下文中, + * 以便在日志输出时能够包含这些信息。由于协程可能在线程间切换,因此需要此实现来保证上下文一致性。 + * + * @param contextMap 包含 MDC 上下文键值对的不可变 Map,用于保存和恢复诊断上下文。 + * @since 2025-07-16 11:05:47 + * @author gewuyou + */ +class MdcContextElement( + private val contextMap: Map +) : ThreadContextElement>, + AbstractCoroutineContextElement(Key) { + + /** + * 协程上下文键对象,用于标识此类上下文元素的唯一性。 + */ + companion object Key : CoroutineContext.Key + + /** + * 更新当前线程的 MDC 上下文为协程指定的上下文,并返回旧的上下文状态。 + * + * 此方法会在协程切换至新线程时调用,以确保目标线程的 MDC 上下文与协程一致。 + * + * @param context 当前协程的上下文,不直接使用但保留用于扩展。 + * @return 返回更新前的 MDC 上下文状态,用于后续恢复。 + */ + override fun updateThreadContext(context: CoroutineContext): Map { + val oldState = MDC.getCopyOfContextMap() ?: emptyMap() + MDC.setContextMap(contextMap) + return oldState + } + + /** + * 恢复当前线程的 MDC 上下文至先前保存的状态。 + * + * 此方法在协程完成执行并释放线程资源时调用,确保线程可以还原其原始 MDC 上下文。 + * + * @param context 当前协程的上下文,不直接使用但保留用于扩展。 + * @param oldState 需要恢复的先前 MDC 上下文状态。 + */ + override fun restoreThreadContext(context: CoroutineContext, oldState: Map) { + MDC.setContextMap(oldState) + } +} \ No newline at end of file diff --git a/forgeboot-context/forgeboot-context-impl/src/main/kotlin/com/gewuyou/forgeboot/context/impl/filter/CoroutineMdcWebFilter.kt b/forgeboot-context/forgeboot-context-impl/src/main/kotlin/com/gewuyou/forgeboot/context/impl/filter/CoroutineMdcWebFilter.kt new file mode 100644 index 0000000..2b71165 --- /dev/null +++ b/forgeboot-context/forgeboot-context-impl/src/main/kotlin/com/gewuyou/forgeboot/context/impl/filter/CoroutineMdcWebFilter.kt @@ -0,0 +1,46 @@ +package com.gewuyou.forgeboot.context.impl.filter + +import com.gewuyou.forgeboot.context.impl.element.MdcContextElement +import kotlinx.coroutines.reactive.awaitFirstOrNull +import kotlinx.coroutines.reactor.mono +import org.springframework.web.server.ServerWebExchange +import org.springframework.web.server.WebFilter +import org.springframework.web.server.WebFilterChain +import reactor.core.publisher.Mono +import java.util.stream.Collectors + +/** + * 协程 MDC Web 过滤器 + * + * 用于在响应式编程环境下,将上下文(如 MDC)从 Reactor Context 传递到 Kotlin 协程中, + * 确保日志上下文信息能够正确传播。 + * + * @since 2025-07-16 11:07:44 + * @author gewuyou + */ +class CoroutineMdcWebFilter : WebFilter { + + /** + * 执行过滤操作的方法 + * + * @param exchange 表示当前的服务器 Web 交换信息,包含请求和响应 + * @param chain 当前的过滤器链,用于继续执行后续的过滤器或目标处理器 + * @return 返回一个 Mono,表示异步完成的过滤操作 + */ + override fun filter(exchange: ServerWebExchange, chain: WebFilterChain): Mono { + return Mono.deferContextual { ctxView -> + // 从 Reactor 上下文中提取键值对,筛选出 key 和 value 均为 String 类型的条目 + val mdcMap = ctxView.stream() + .filter { it.key is String && it.value is String } + .collect(Collectors.toMap( + { it.key as String }, + { it.value as String } + )) + + // 在带有 MDC 上下文的协程中执行过滤链 + mono(MdcContextElement(mdcMap)) { + chain.filter(exchange).awaitFirstOrNull() + } + } + } +} \ No newline at end of file diff --git a/forgeboot-webmvc/spec/build.gradle.kts b/forgeboot-webmvc/spec/build.gradle.kts index 61e6456..67e1bbb 100644 --- a/forgeboot-webmvc/spec/build.gradle.kts +++ b/forgeboot-webmvc/spec/build.gradle.kts @@ -1,6 +1,6 @@ dependencies { compileOnly(libs.springBootStarter.jpa) - implementation(project(Modules.Webmvc.DTO)) - implementation(project(Modules.Core.EXTENSION)) + api(project(Modules.Webmvc.DTO)) + api(project(Modules.Core.EXTENSION)) } diff --git a/settings.gradle.kts b/settings.gradle.kts index d26962a..6c04b5f 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -136,42 +136,42 @@ project(":forgeboot-trace:forgeboot-trace-impl").name = "forgeboot-trace-impl" project(":forgeboot-trace:forgeboot-trace-autoconfigure").name = "forgeboot-trace-autoconfigure" //endregion -//region module security -/** - * Includes and configures projects related to 'forgeboot-security' - * This module handles security-related functionality. - */ -include( - "forgeboot-security", - ":forgeboot-security:forgeboot-security-core", - - ":forgeboot-security:forgeboot-security-authenticate", - ":forgeboot-security:forgeboot-security-authenticate:api", - ":forgeboot-security:forgeboot-security-authenticate:impl", - ":forgeboot-security:forgeboot-security-authenticate:autoconfigure", - - ":forgeboot-security:forgeboot-security-authorize", - ":forgeboot-security:forgeboot-security-authorize:api", - ":forgeboot-security:forgeboot-security-authorize:impl", - ":forgeboot-security:forgeboot-security-authorize:autoconfigure" -) -project(":forgeboot-security").name = "forgeboot-security-spring-boot-starter" -project(":forgeboot-security:forgeboot-security-core").name = "forgeboot-security-core" - -project(":forgeboot-security:forgeboot-security-authenticate").name = - "forgeboot-security-authenticate-spring-boot-starter" -project(":forgeboot-security:forgeboot-security-authenticate:api").name = "forgeboot-security-authenticate-api" -project(":forgeboot-security:forgeboot-security-authenticate:impl").name = "forgeboot-security-authenticate-impl" -project(":forgeboot-security:forgeboot-security-authenticate:autoconfigure").name = - "forgeboot-security-authenticate-autoconfigure" - -project(":forgeboot-security:forgeboot-security-authorize").name = "forgeboot-security-authorize-spring-boot-starter" -project(":forgeboot-security:forgeboot-security-authorize:api").name = "forgeboot-security-authorize-api" -project(":forgeboot-security:forgeboot-security-authorize:impl").name = "forgeboot-security-authorize-impl" -project(":forgeboot-security:forgeboot-security-authorize:autoconfigure").name = - "forgeboot-security-authorize-autoconfigure" - -//endregion +////region module security +///** +// * Includes and configures projects related to 'forgeboot-security' +// * This module handles security-related functionality. +// */ +//include( +// "forgeboot-security", +// ":forgeboot-security:forgeboot-security-core", +// +// ":forgeboot-security:forgeboot-security-authenticate", +// ":forgeboot-security:forgeboot-security-authenticate:api", +// ":forgeboot-security:forgeboot-security-authenticate:impl", +// ":forgeboot-security:forgeboot-security-authenticate:autoconfigure", +// +// ":forgeboot-security:forgeboot-security-authorize", +// ":forgeboot-security:forgeboot-security-authorize:api", +// ":forgeboot-security:forgeboot-security-authorize:impl", +// ":forgeboot-security:forgeboot-security-authorize:autoconfigure" +//) +//project(":forgeboot-security").name = "forgeboot-security-spring-boot-starter" +//project(":forgeboot-security:forgeboot-security-core").name = "forgeboot-security-core" +// +//project(":forgeboot-security:forgeboot-security-authenticate").name = +// "forgeboot-security-authenticate-spring-boot-starter" +//project(":forgeboot-security:forgeboot-security-authenticate:api").name = "forgeboot-security-authenticate-api" +//project(":forgeboot-security:forgeboot-security-authenticate:impl").name = "forgeboot-security-authenticate-impl" +//project(":forgeboot-security:forgeboot-security-authenticate:autoconfigure").name = +// "forgeboot-security-authenticate-autoconfigure" +// +//project(":forgeboot-security:forgeboot-security-authorize").name = "forgeboot-security-authorize-spring-boot-starter" +//project(":forgeboot-security:forgeboot-security-authorize:api").name = "forgeboot-security-authorize-api" +//project(":forgeboot-security:forgeboot-security-authorize:impl").name = "forgeboot-security-authorize-impl" +//project(":forgeboot-security:forgeboot-security-authorize:autoconfigure").name = +// "forgeboot-security-authorize-autoconfigure" +// +////endregion //region module cache include(