Skip to content

Conversation

@SteelMorgan
Copy link

@SteelMorgan SteelMorgan commented Nov 28, 2025

Описание

Добавлена поддержка частичной загрузки конфигурации для ускорения сборки.
Вместо загрузки всех файлов загружаются только измененные через параметры -partial -listFile.

Вдохновлено проектом: https://github.com/Nikolay-Shirokov/partial-load-config

Изменения

  • PartialLoadListGenerator — генерация списка файлов с BSL→XML маппингом
  • Расширен BuildAction методом runPartial()
  • Реализация для DESIGNER (-partial -listFile) и IBCMD (config import files --partial)
  • Настраиваемый порог: app.build.partial-load-threshold (по умолчанию 20)
  • Улучшена обработка ошибок MapDB

Результат

Сборка 2 файлов: ~60 сек вместо ~15 мин 🚀

Summary by CodeRabbit

Примечания к выпуску

  • Новые возможности

    • Добавлена поддержка частичной загрузки при построении проектов для повышения производительности при изменении небольшого количества файлов.
    • Новый параметр конфигурации partialLoadThreshold (по умолчанию: 20) для управления режимом загрузки.
  • Тесты

    • Добавлено полное покрытие тестами функциональности частичной загрузки с различными сценариями.
  • Улучшения

    • Улучшена обработка ошибок при работе с хранилищем файлов.
    • Обновлена конфигурация проекта для поддержки локальных параметров.

✏️ Tip: You can customize this high-level summary in your review settings.

- Add PartialLoadListGenerator for generating file list with BSL->XML mapping
- Extend BuildAction interface with runPartial() method
- Implement partial loading in AbstractBuildAction with automatic mode selection
- Add loadConfigurationPartial() to DesignerBuildAction using -partial -listFile
- Add loadConfigurationPartial() to IbcmdBuildAction using config import files --partial
- Integrate partial loading in LauncherService
- Add configurable threshold via app.build.partial-load-threshold (default: 20)
- Improve MapDB error handling to avoid log pollution
- Add unit tests for PartialLoadListGenerator

Closes: partial load feature inspired by https://github.com/Nikolay-Shirokov/partial-load-config
@coderabbitai
Copy link

coderabbitai bot commented Nov 28, 2025

Walkthrough

Добавлена поддержка частичной загрузки конфигураций при сборке: введена новая утилита PartialLoadListGenerator для анализа изменённых файлов, добавлены методы runPartial() и executeBuildDslPartial() в действия сборки, интегрирована пороговая логика в ApplicationProperties, и обновлены компоненты запуска для использования оптимизированного пути загрузки на основе количества и типа изменений.

Changes

Cohort / File(s) Описание
Конфигурация .gitignore
.gitignore
Добавлен раздел локальной конфигурации с игнорированием: mcp-configs/, mcp-example.json, DEPLOYMENT.md
Новые свойства конфигурации
src/main/kotlin/io/github/alkoleft/mcp/configuration/properties/ApplicationProperties.kt, src/main/kotlin/io/github/alkoleft/mcp/configuration/properties/BuildProperties.kt
Добавлено поле build: BuildProperties в ApplicationProperties и новый класс BuildProperties с пороговым значением partialLoadThreshold: Int = 20
Поддержка частичной загрузки в действиях сборки
src/main/kotlin/io/github/alkoleft/mcp/application/actions/build/AbstractBuildAction.kt, src/main/kotlin/io/github/alkoleft/mcp/application/actions/build/DesignerBuildAction.kt, src/main/kotlin/io/github/alkoleft/mcp/application/actions/build/IbcmdBuildAction.kt
Добавлена зависимость PartialLoadListGenerator, новые методы runPartial() и executeBuildDslPartial(), абстрактные методы loadConfigurationPartial() и loadExtensionPartial() с конкретными реализациями в подклассах
API действий сборки
src/main/kotlin/io/github/alkoleft/mcp/application/actions/common/Actions.kt
Добавлена новая функция runPartial() для интерфейса BuildAction с поддержкой карты изменений исходных наборов
Интеграция в сервис запуска
src/main/kotlin/io/github/alkoleft/mcp/application/services/LauncherService.kt
Обновлён метод updateIB() для принятия параметра sourceSetChanges и использования buildAction.runPartial() вместо run()
Утилита анализа файлов
src/main/kotlin/io/github/alkoleft/mcp/infrastructure/utility/PartialLoadListGenerator.kt
Новый Spring компонент для генерации списка файлов при частичной загрузке с логикой обработки BSL/XML файлов, обнаружением изменений Configuration.xml и решением о использовании частичной загрузки
Изменения обработки ошибок хранилища
src/main/kotlin/io/github/alkoleft/mcp/infrastructure/storage/FileBuildStateManager.kt, src/main/kotlin/io/github/alkoleft/mcp/infrastructure/storage/MapDbHashStorage.kt
Удалена обработка исключений в FileBuildStateManager.updateHashes(), заменена на нефатальный путь в MapDbHashStorage с помощью нового вспомогательного метода tryRollback() вместо немедленного выброса исключения
Тестирование
src/test/kotlin/io/github/alkoleft/mcp/RealTests.kt, src/test/kotlin/io/github/alkoleft/mcp/infrastructure/utility/PartialLoadListGeneratorTest.kt
Обновлён конструктор в тесте RealTests с передачей PartialLoadListGenerator, добавлен комплексный набор тестов PartialLoadListGeneratorTest (10+ тестовых методов) для валидации логики частичной загрузки

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Области, требующие особого внимания:

  • PartialLoadListGenerator.kt — сложная логика обработки путей, определение типов метаданных, рекурсивный обход каталогов и валидация Configuration.xml изменений требуют тщательного разбора алгоритма и граничных случаев
  • AbstractBuildAction.kt и подклассы — взаимодействие между новыми методами runPartial() и executeBuildDslPartial() с пороговой логикой должно быть проверено на логическую корректность потока выполнения
  • Изменения в обработке ошибок (MapDbHashStorage и FileBuildStateManager) — переход от выброса исключений к нефатальной обработке может повлиять на восстанавливаемость при ошибках БД
  • Тесты PartialLoadListGeneratorTest — обширный набор тестовых сценариев должен быть проверен на полноту покрытия граничных случаев и корректность ожидаемого поведения

Possibly related PRs

Suggested labels

enhancement

Poem

🐰 Загрузка проще, файлы светлей,
Порог волшебный — двадцать путей!
Когда конфиг спит без забот,
Частичный груз в беде спасает кот.
Лист файлов создан, ошибки прощены,
Все изменения разумно взвешены! ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 47.27% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Заголовок PR точно описывает основное изменение — добавление поддержки частичной загрузки конфигурации, что соответствует сути всех внесённых изменений.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

📝 Customizable high-level summaries are now available in beta!

You can now customize how CodeRabbit generates the high-level summary in your pull requests — including its content, structure, tone, and formatting.

  • Provide your own instructions using the high_level_summary_instructions setting.
  • Format the summary however you like (bullet lists, tables, multi-section layouts, contributor stats, etc.).
  • Use high_level_summary_in_walkthrough to move the summary from the description to the walkthrough section.

Example instruction:

"Divide the high-level summary into five sections:

  1. 📝 Description — Summarize the main change in 50–60 words, explaining what was done.
  2. 📓 References — List relevant issues, discussions, documentation, or related PRs.
  3. 📦 Dependencies & Requirements — Mention any new/updated dependencies, environment variable changes, or configuration updates.
  4. 📊 Contributor Summary — Include a Markdown table showing contributions:
    | Contributor | Lines Added | Lines Removed | Files Changed |
  5. ✔️ Additional Notes — Add any extra reviewer context.
    Keep each section concise (under 200 words) and use bullet or numbered lists for clarity."

Note: This feature is currently in beta for Pro-tier users, and pricing will be announced later.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (9)
src/main/kotlin/io/github/alkoleft/mcp/infrastructure/storage/MapDbHashStorage.kt (2)

112-115: Нефатальные ошибки batchUpdate выглядят согласованно с идеей «кэш можно перестроить»

Логирование warn с количеством файлов + подробности в debug и безопасный rollback дают понятное поведение: сборка не падает, а кэш будет пересчитан при следующем запуске. Это выглядит разумным компромиссом для ускорения сборок, когда MapDB ведёт себя нестабильно.

В качестве необязательного улучшения можно сразу логировать исключение на уровне warn, чтобы стек не терялся в проде, где debug обычно отключён:

-        } catch (e: Exception) {
-            logger.warn { "Не удалось сохранить хеши файлов (${updates.size} шт.) - кэш будет перестроен при следующей сборке" }
-            logger.debug(e) { "Детали ошибки сохранения хешей" }
-            tryRollback()
-        }
+        } catch (e: Exception) {
+            logger.warn(e) {
+                "Не удалось сохранить хеши файлов (${updates.size} шт.) - кэш будет перестроен при следующей сборке"
+            }
+            tryRollback()
+        }

141-155: Единообразная обработка ошибок и отдельный tryRollback — хорошо; можно чуть доработать сигнатуру и лог

Поведение storeTimestamp теперь симметрично batchUpdate: при ошибке пишется warn и выполняется безопасный rollback через отдельный helper — это делает фейлы MapDB нефатальными и предсказуемыми для вызывающего кода.

Для нового tryRollback можно слегка уточнить типизацию и лог, чтобы следовать гайдам и не терять стек-трейс:

-    private fun tryRollback() {
-        try {
-            db.rollback()
-        } catch (e: Exception) {
-            logger.debug { "Rollback не выполнен: ${e.message}" }
-        }
-    }
+    private fun tryRollback(): Unit {
+        try {
+            db.rollback()
+        } catch (e: Exception) {
+            logger.debug(e) { "Rollback не выполнен" }
+        }
+    }

Так мы явно указываем возвращаемый тип Unit и логируем исключение вместе со стеком. As per coding guidelines, ...

src/main/kotlin/io/github/alkoleft/mcp/infrastructure/utility/PartialLoadListGenerator.kt (2)

136-148: Рассмотрите упрощение через when-expression

Можно упростить логику, используя when-expression с присваиванием:

-    when {
-        absolutePath.extension.equals("bsl", ignoreCase = true) -> {
+    when (absolutePath.extension.lowercase()) {
+        "bsl" -> {
             processBslFile(sourceSetPath, relativePath, filesToLoad)
         }
-        absolutePath.extension.equals("xml", ignoreCase = true) -> {
+        "xml" -> {
             filesToLoad.add(relativePathStr)
             logger.trace { "Добавлен XML: $relativePathStr" }
         }
         else -> {
             filesToLoad.add(relativePathStr)
             logger.trace { "Добавлен файл: $relativePathStr" }
         }
     }

256-268: Оптимизируйте проверку Configuration.xml

Текущая реализация всегда вычисляет relativePath даже для файлов вне sourceSetPath. Можно оптимизировать:

 fun hasConfigurationXmlChanges(
     sourceSetPath: Path,
     changedFiles: Set<Path>,
 ): Boolean {
-    return changedFiles.any { file ->
-        val relativePath = if (file.startsWith(sourceSetPath)) {
-            sourceSetPath.relativize(file)
-        } else {
-            file
-        }
-        relativePath.name.equals("Configuration.xml", ignoreCase = true)
-    }
+    return changedFiles.any { file ->
+        file.name.equals("Configuration.xml", ignoreCase = true) &&
+            file.startsWith(sourceSetPath)
+    }
 }
src/main/kotlin/io/github/alkoleft/mcp/application/actions/build/DesignerBuildAction.kt (1)

85-99: Дублирование логики между loadConfigurationPartial() и loadExtensionPartial()

Методы различаются только вызовом extension(name). Рассмотрите извлечение общей логики.

+private fun loadPartial(
+    name: String,
+    path: Path,
+    changedFiles: Set<Path>,
+    isExtension: Boolean,
+): ProcessResult {
+    val listFile = partialLoadListGenerator.generate(path, changedFiles)
+    return actionDsl.loadConfigFromFiles {
+        fromPath(path)
+        if (isExtension) extension(name)
+        partial()
+        listFile(listFile)
+        updateConfigDumpInfo()
+        updateDBCfg()
+    }
+}
+
 override fun loadConfigurationPartial(
     name: String,
     path: Path,
     changedFiles: Set<Path>,
-): ProcessResult {
-    val listFile = partialLoadListGenerator.generate(path, changedFiles)
-    return actionDsl.loadConfigFromFiles {
-        fromPath(path)
-        partial()
-        listFile(listFile)
-        updateConfigDumpInfo()
-        updateDBCfg()
-    }
-}
+): ProcessResult = loadPartial(name, path, changedFiles, false)

 override fun loadExtensionPartial(
     name: String,
     path: Path,
     changedFiles: Set<Path>,
-): ProcessResult {
-    val listFile = partialLoadListGenerator.generate(path, changedFiles)
-    return actionDsl.loadConfigFromFiles {
-        fromPath(path)
-        extension(name)
-        partial()
-        listFile(listFile)
-        updateConfigDumpInfo()
-        updateDBCfg()
-    }
-}
+): ProcessResult = loadPartial(name, path, changedFiles, true)
src/main/kotlin/io/github/alkoleft/mcp/application/actions/build/IbcmdBuildAction.kt (1)

59-75: Дублирование кода между методами частичной загрузки

Аналогично DesignerBuildAction, здесь также наблюдается дублирование логики между loadConfigurationPartial() и loadExtensionPartial().

+private fun loadPartial(
+    name: String,
+    path: Path,
+    extensionName: String? = null,
+): ProcessResult {
+    lateinit var result: ProcessResult
+    actionDsl.config {
+        result = import(path) {
+            extensionName?.let { extension = it }
+            importSubCommand = "files"
+            partial = true
+            baseDir = path.toString()
+        }
+    }
+    return result
+}
+
 override fun loadConfigurationPartial(
     name: String,
     path: Path,
     changedFiles: Set<Path>,
-): ProcessResult {
-    // Для ibcmd используем подкоманду "files" и параметр --partial
-    // ibcmd infobase config import files --partial <path>
-    lateinit var result: ProcessResult
-    actionDsl.config {
-        result = import(path) {
-            importSubCommand = "files"
-            partial = true
-            baseDir = path.toString()
-        }
-    }
-    return result
-}
+): ProcessResult = loadPartial(name, path)

 override fun loadExtensionPartial(
     name: String,
     path: Path,
     changedFiles: Set<Path>,
-): ProcessResult {
-    // Для ibcmd используем подкоманду "files" и параметр --partial с расширением
-    lateinit var result: ProcessResult
-    actionDsl.config {
-        result = import(path) {
-            extension = name
-            importSubCommand = "files"
-            partial = true
-            baseDir = path.toString()
-        }
-    }
-    return result
-}
+): ProcessResult = loadPartial(name, path, name)

Also applies to: 91-107

src/test/kotlin/io/github/alkoleft/mcp/infrastructure/utility/PartialLoadListGeneratorTest.kt (1)

33-284: Несоответствия локальным правилам код-стайла для тестов (типы, имена, пустые строки)

В текущем виде класс тестов не полностью следует выданным правилам:

  • Локальные переменные и параметры функций не имеют явных типов (val sourceSetPath = ..., val changedFiles = ... и т.п.), хотя по гайдам типы должны быть задекларированы явно.
  • Внутри тестовых функций довольно много пустых строк между выражениями, что противоречит правилу «Don’t leave blank lines within a Kotlin function».
  • Имена тестовых переменных (sourceSetPath, changedFiles, content и т.д.) не следуют схеме inputX/mockX/actualX/expectedX, рекомендованной для файлов под src/test/**.

Функционально это не мешает, но для выравнивания со стилем проекта можно постепенно привести новые тесты к этим правилам (например, начиная с явных типов в ключевых местах и более строгого AAA‑разделения без лишних пустых строк).

As per coding guidelines, ...

src/main/kotlin/io/github/alkoleft/mcp/application/actions/build/AbstractBuildAction.kt (2)

145-158: Проверить соответствие ключей в sourceSetChanges и то, что в changedFiles попадают только файлы конкретного source set

В executeBuildDslPartial вы берёте изменения по имени конфигурации/расширения:

val changes = sourceSetChanges[configuration.name] // / extension.name
...
shouldUsePartialLoad(changes, threshold, configPath/extensionPath)

и далее пробрасываете changes.changedFiles в PartialLoadListGenerator. Для корректной работы важно, чтобы:

  • карта sourceSetChanges действительно формировалась с ключами configuration.name/extension.name, а не, например, по путям;
  • в каждом SourceSetChanges.changedFiles содержались только файлы, относящиеся к соответствующей конфигурации или расширению (иначе порог и проверка Configuration.xml будут считаться по «лишним» файлам).

Если это уже гарантируется логикой построения sourceSetChanges, всё ок; если нет — имеет смысл либо фильтровать список по sourceSet.basePath.resolve(...).normalize(), либо документировать допущение.

Also applies to: 165-183, 194-207


73-84: Отклонения от локальных Kotlin-правил (явные типы локальных переменных, пустые строки внутри функций)

Во вновь добавленных/изменённых методах (measureExecutionTime, executeBuildDsl, executeBuildDslPartial и т.п.) есть несколько расхождений с правилами:

  • Локальные переменные объявлены без явных типов (val startTime = ..., val state = BuildActionState(), val threshold = properties.build.partialLoadThreshold, val configPath = ..., val changes = ... и т.д.), хотя по гайдам «Always declare the type of each variable».
  • Внутри функций довольно много пустых строк между логическими блоками (включая новые участки кода), что формально противоречит правилу «Don't leave blank lines within a Kotlin function».

Это не влияет на поведение и читаемость здесь в целом нормальная, но для единообразия со стилем проекта можно в будущем при правках этого файла постепенно добавлять явные типы локальных переменных и немного уплотнить функции, убирая лишние пустые строки.

As per coding guidelines, ...

Also applies to: 90-129, 134-192

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4603d94 and 9f4c937.

📒 Files selected for processing (13)
  • .gitignore (1 hunks)
  • src/main/kotlin/io/github/alkoleft/mcp/application/actions/build/AbstractBuildAction.kt (5 hunks)
  • src/main/kotlin/io/github/alkoleft/mcp/application/actions/build/DesignerBuildAction.kt (4 hunks)
  • src/main/kotlin/io/github/alkoleft/mcp/application/actions/build/IbcmdBuildAction.kt (4 hunks)
  • src/main/kotlin/io/github/alkoleft/mcp/application/actions/common/Actions.kt (2 hunks)
  • src/main/kotlin/io/github/alkoleft/mcp/application/services/LauncherService.kt (3 hunks)
  • src/main/kotlin/io/github/alkoleft/mcp/configuration/properties/ApplicationProperties.kt (1 hunks)
  • src/main/kotlin/io/github/alkoleft/mcp/configuration/properties/BuildProperties.kt (1 hunks)
  • src/main/kotlin/io/github/alkoleft/mcp/infrastructure/storage/FileBuildStateManager.kt (1 hunks)
  • src/main/kotlin/io/github/alkoleft/mcp/infrastructure/storage/MapDbHashStorage.kt (2 hunks)
  • src/main/kotlin/io/github/alkoleft/mcp/infrastructure/utility/PartialLoadListGenerator.kt (1 hunks)
  • src/test/kotlin/io/github/alkoleft/mcp/RealTests.kt (2 hunks)
  • src/test/kotlin/io/github/alkoleft/mcp/infrastructure/utility/PartialLoadListGeneratorTest.kt (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.kt

📄 CodeRabbit inference engine (.cursor/rules/kotlin.mdc)

**/*.kt: Always declare the type of each variable and function (parameters and return value) in Kotlin; avoid using Any; create necessary types
Don't leave blank lines within a Kotlin function
Use PascalCase for Kotlin classes
Use camelCase for Kotlin variables, functions, and methods
Avoid magic numbers; define constants
Start each function name with a verb
Use verb-based names for boolean variables (e.g., isLoading, hasError, canDelete)
Use complete words instead of abbreviations (except standard ones like API, URL, and i/j, err, ctx, req/res/next)
Write short Kotlin functions with a single purpose (less than 20 instructions)
Name boolean-returning functions as isX/hasX/canX; name void-like functions as executeX/saveX
Avoid nesting by using early returns and extracting utility functions
Use higher-order functions (map, filter, reduce, etc.) to avoid nesting; use expression bodies for simple functions; use named functions for non-simple ones
Use default parameter values instead of checking for null
Reduce function parameters and results using RO-RO (objects for input and output) and declare necessary types
Maintain a single level of abstraction within functions
Use data classes for data representations
Avoid abusing primitive types; encapsulate data in composite types
Avoid data validations in functions; prefer classes with internal validation
Prefer immutability; use val for data that doesn't change
Declare interfaces to define contracts
Write small classes with a single purpose (less than 200 instructions, <10 public methods, <10 properties)
Use exceptions to handle unexpected errors; catch exceptions only to fix expected problems or add context; otherwise use a global handler

Files:

  • src/main/kotlin/io/github/alkoleft/mcp/configuration/properties/ApplicationProperties.kt
  • src/main/kotlin/io/github/alkoleft/mcp/application/actions/build/IbcmdBuildAction.kt
  • src/main/kotlin/io/github/alkoleft/mcp/infrastructure/storage/FileBuildStateManager.kt
  • src/test/kotlin/io/github/alkoleft/mcp/infrastructure/utility/PartialLoadListGeneratorTest.kt
  • src/main/kotlin/io/github/alkoleft/mcp/application/services/LauncherService.kt
  • src/main/kotlin/io/github/alkoleft/mcp/application/actions/build/DesignerBuildAction.kt
  • src/main/kotlin/io/github/alkoleft/mcp/application/actions/common/Actions.kt
  • src/main/kotlin/io/github/alkoleft/mcp/infrastructure/storage/MapDbHashStorage.kt
  • src/main/kotlin/io/github/alkoleft/mcp/configuration/properties/BuildProperties.kt
  • src/main/kotlin/io/github/alkoleft/mcp/application/actions/build/AbstractBuildAction.kt
  • src/main/kotlin/io/github/alkoleft/mcp/infrastructure/utility/PartialLoadListGenerator.kt
  • src/test/kotlin/io/github/alkoleft/mcp/RealTests.kt
src/{test,androidTest}/**/*.kt

📄 CodeRabbit inference engine (.cursor/rules/kotlin.mdc)

src/{test,androidTest}/**/*.kt: Follow Arrange-Act-Assert in tests and name test variables as inputX, mockX, actualX, expectedX
Write unit tests for each public function; use test doubles for dependencies (except inexpensive third-party dependencies)

Files:

  • src/test/kotlin/io/github/alkoleft/mcp/infrastructure/utility/PartialLoadListGeneratorTest.kt
  • src/test/kotlin/io/github/alkoleft/mcp/RealTests.kt
🧠 Learnings (1)
📚 Learning: 2025-08-11T22:22:35.393Z
Learnt from: CR
Repo: alkoleft/mcp-onec-test-runner PR: 0
File: .cursor/rules/kotlin.mdc:0-0
Timestamp: 2025-08-11T22:22:35.393Z
Learning: Applies to src/androidTest/**/*.kt : Use integration tests for each API module

Applied to files:

  • src/test/kotlin/io/github/alkoleft/mcp/infrastructure/utility/PartialLoadListGeneratorTest.kt
  • src/test/kotlin/io/github/alkoleft/mcp/RealTests.kt
🧬 Code graph analysis (3)
src/main/kotlin/io/github/alkoleft/mcp/application/actions/build/IbcmdBuildAction.kt (1)
src/main/kotlin/io/github/alkoleft/mcp/infrastructure/platform/dsl/ibcmd/commands/config/ConfigCommandBuilder.kt (1)
  • import (76-79)
src/main/kotlin/io/github/alkoleft/mcp/application/actions/build/DesignerBuildAction.kt (1)
src/main/kotlin/io/github/alkoleft/mcp/infrastructure/platform/dsl/designer/commands/LoadConfigFromFilesCommand.kt (4)
  • fromPath (57-59)
  • partial (73-75)
  • listFile (77-79)
  • updateConfigDumpInfo (85-87)
src/main/kotlin/io/github/alkoleft/mcp/application/actions/build/AbstractBuildAction.kt (3)
src/main/kotlin/io/github/alkoleft/mcp/application/actions/build/DesignerBuildAction.kt (6)
  • initDsl (44-51)
  • loadConfigurationPartial (61-74)
  • loadConfiguration (53-59)
  • loadExtensionPartial (85-99)
  • loadExtension (76-83)
  • updateDb (101-101)
src/main/kotlin/io/github/alkoleft/mcp/application/actions/build/IbcmdBuildAction.kt (6)
  • initDsl (44-48)
  • loadConfigurationPartial (59-75)
  • loadConfiguration (50-57)
  • loadExtensionPartial (91-107)
  • loadExtension (77-89)
  • updateDb (109-113)
src/main/kotlin/io/github/alkoleft/mcp/infrastructure/utility/PartialLoadListGenerator.kt (1)
  • shouldUsePartialLoad (278-295)
🔇 Additional comments (15)
.gitignore (1)

49-52: Убедитесь, что mcp-example.json и DEPLOYMENT.md - это локальные файлы, а не примеры/документация для разработчиков.

Правила для mcp-configs/ логичны для локальной конфигурации. Однако mcp-example.json может быть шаблоном или примером, который новые разработчики должны скопировать и использовать. Если это так, файл следует поместить в репо с расширением типа .template или .example, а не игнорировать полностью. Аналогичная ситуация с DEPLOYMENT.md — если это общая документация по развертыванию, она должна быть доступна в репо.

Пожалуйста, проверьте:

  • Является ли mcp-example.json рабочим локальным файлом (можно игнорировать) или примером конфигурации для разработчиков (должен быть в репо)?
  • Является ли DEPLOYMENT.md локальным документом развертывания (можно игнорировать) или общей документацией проекта (должна быть в репо)?
src/main/kotlin/io/github/alkoleft/mcp/infrastructure/storage/FileBuildStateManager.kt (1)

93-95: Упрощение updateHashes за счёт делегации в hashStorage выглядит удачным

С учётом того, что MapDbHashStorage.batchUpdate теперь сам обрабатывает ошибки и делает безопасный rollback, прямой вызов без локального try/catch делает код чище и не дублирует обработку ошибок.

src/main/kotlin/io/github/alkoleft/mcp/infrastructure/utility/PartialLoadListGenerator.kt (4)

49-94: LGTM: Полный набор типов метаданных 1С

Список типов метаданных охватывает все основные типы объектов конфигурации 1С, что обеспечивает корректную работу с различными элементами конфигурации.


128-131: LGTM: Корректная валидация принадлежности файла

Проверка startsWith(sourceSetPath) с предупреждением и ранним выходом корректно обрабатывает файлы вне source set.


278-296: LGTM: Корректная логика принятия решения

Логика shouldUsePartialLoad() правильно проверяет оба условия (порог и Configuration.xml) с соответствующим логированием.


158-191: Based on my research into 1C metadata structure standards, I can now assess the review comment. The web search confirms key findings that are relevant to the concerns raised:

Findings from 1C Metadata Standards:

  1. CommonModules structure (per official 1C documentation): Files are stored as CommonModules/<Name>.bsl directly—without an Ext subdirectory. This differs from other metadata types.

  2. Other metadata types (Catalogs, DataProcessors, Reports, etc.): Use the pattern ObjectType/<ObjectName>/Ext/<ModuleType>.bsl

  3. Forms and Commands within objects: Typically stored as:

    • ObjectType/<ObjectName>/Forms/<FormName>/Module.bsl
    • ObjectType/<ObjectName>/Commands/<CommandName>/Module.bsl

However, I was unable to verify the actual implementation of the processBslFile() function, findMetadataInfo(), and addAllFilesFromDirectory() due to repository clone failures. Without access to the code, I cannot confirm:

  • Whether findMetadataInfo() correctly distinguishes between CommonModules and other metadata types
  • Whether the logic properly handles BSL files in various locations (Forms, Commands, etc.)
  • What the actual behavior would be for edge cases

Верифицируйте обработку вложенных BSL-файлов в соответствии со стандартной структурой 1C

На основе стандартов 1C:

  1. CommonModules хранят BSL-файлы напрямую: CommonModules/<Name>.bsl (БЕЗ подкаталога Ext)
  2. Другие типы метаданных используют: ObjectType/<ObjectName>/Ext/<ModuleType>.bsl
  3. BSL-файлы в Forms и Commands расположены внутри структуры объектов

Требуется проверить, что findMetadataInfo() корректно обрабатывает все эти варианты размещения файлов.

src/main/kotlin/io/github/alkoleft/mcp/application/actions/common/Actions.kt (1)

58-74: LGTM: Чёткое API для частичной загрузки

Новый метод runPartial() корректно расширяет интерфейс BuildAction:

  • Параметр sourceSetChanges необходим для передачи информации об изменениях
  • KDoc ясно описывает логику автоматического выбора режима загрузки
  • Сигнатура согласуется с существующим методом run()
src/test/kotlin/io/github/alkoleft/mcp/RealTests.kt (1)

88-88: LGTM: Корректное обновление конструктора в тесте

Добавление PartialLoadListGenerator() соответствует новой сигнатуре конструктора DesignerBuildAction.

src/main/kotlin/io/github/alkoleft/mcp/configuration/properties/ApplicationProperties.kt (1)

42-42: LGTM: Последовательное расширение конфигурации

Добавление поля build соответствует существующему паттерну вложенных свойств конфигурации (connection, tools).

src/main/kotlin/io/github/alkoleft/mcp/application/services/LauncherService.kt (1)

147-161: LGTM: Корректная интеграция частичной загрузки

Обновление метода updateIB():

  • Новый параметр sourceSetChanges необходим для принятия решения о режиме загрузки
  • Вызов runPartial() вместо run() активирует логику автоматического выбора
  • KDoc ясно описывает новое поведение
src/main/kotlin/io/github/alkoleft/mcp/application/actions/build/DesignerBuildAction.kt (2)

38-41: LGTM: Корректное внедрение зависимости

Конструктор правильно принимает PartialLoadListGenerator и передаёт его в суперкласс.


61-74: Consider making updateConfigDumpInfo() conditional for partial configuration loads

The 1C Designer documentation confirms that -updateConfigDumpInfo is an optional flag for partial loads. When omitted, the partial load still functions correctly but does not synchronize ConfigDumpInfo.xml. Since this method updates XML metadata on disk with each partial load—even when only a few files change—it may introduce unnecessary I/O overhead.

Recommendation: Evaluate whether ConfigDumpInfo.xml must be synchronized immediately after every partial load or if it can be:

  • Regenerated lazily when needed
  • Updated only after batches of changes are complete
  • Skipped entirely for partial loads where consistency is not critical

If ConfigDumpInfo synchronization is essential, document the performance implications and consider batching updates.

src/main/kotlin/io/github/alkoleft/mcp/application/actions/build/IbcmdBuildAction.kt (1)

59-75: Based on the web search results, I can now provide a conclusive verification:

The baseDir parameter is correctly implemented and compatible with ibcmd.

The ibcmd documentation confirms:

  • --base-dir is the correct parameter name for the "files" import mode with --partial flag
  • baseDir = path.toString() properly converts the Path object to a String, which matches the CLI parameter format
  • The path can be either absolute or relative (this is typically determined by the file list contents, not the baseDir parameter itself)
  • The implementation aligns with the documented ibcmd syntax: ibcmd infobase config import files --base-dir <path> --partial

The original review comment's concerns about absolute vs. relative paths and compatibility with the partial parameter are both addressed by the ibcmd documentation, which confirms this is the standard and correct approach.


No issues with the baseDir parameter setting. The implementation is correct and follows ibcmd documentation.

The parameter is properly configured for partial configuration imports using the "files" subcommand, regardless of whether the paths in the file list are absolute or relative.

src/test/kotlin/io/github/alkoleft/mcp/infrastructure/utility/PartialLoadListGeneratorTest.kt (1)

33-284: Хорошее и детальное покрытие сценариев PartialLoadListGenerator

Набор тестов хорошо проверяет ключевые случаи: простые XML, изменения BSL с добавлением связанного XML, «разворот» всего каталога/документа при изменении одного модуля, учёт Configuration.xml, порог partial‑load, CommonModules, DataProcessors, несколько объектов и игнорирование файлов вне source set. С точки зрения функциональности и Arrange‑Act‑Assert структура выглядит корректной.

src/main/kotlin/io/github/alkoleft/mcp/application/actions/build/AbstractBuildAction.kt (1)

44-46: Корректная интеграция поддержки частичной загрузки в AbstractBuildAction

Интеграция PartialLoadListGenerator выглядит аккуратной:

  • runPartial симметричен существующему run и использует тот же measureExecutionTime, что сохраняет единый подсчёт длительности и обработку ошибок.
  • executeBuildDslPartial повторяет общий pipeline полной сборки, но для конфигурации и расширений принимает решение через shouldUsePartialLoad(...) и, при необходимости, вызывает loadConfigurationPartial/loadExtensionPartial.
  • Порог берётся из properties.build.partialLoadThreshold, а проверка Configuration.xml делегируется генератору, что концентрирует доменную логику в одном месте.
  • После частичных/полных загрузок по‑прежнему используется BuildActionState и общий updateDb() + финальный toResult, как и в полной сборке, так что поведение наружу остаётся единообразным.

С учётом предоставленных сниппетов реализаций в DesignerBuildAction и IbcmdBuildAction общий дизайн выглядит целостным и расширяемым.

Also applies to: 55-69, 131-192, 194-207

Comment on lines +59 to +75
override fun loadConfigurationPartial(
name: String,
path: Path,
changedFiles: Set<Path>,
): ProcessResult {
// Для ibcmd используем подкоманду "files" и параметр --partial
// ibcmd infobase config import files --partial <path>
lateinit var result: ProcessResult
actionDsl.config {
result = import(path) {
importSubCommand = "files"
partial = true
baseDir = path.toString()
}
}
return result
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Неиспользуемый параметр changedFiles

Оба метода loadConfigurationPartial() и loadExtensionPartial() принимают параметр changedFiles: Set<Path>, но не используют его. В отличие от DesignerBuildAction, здесь не вызывается partialLoadListGenerator.generate().

Возможные проблемы:

  1. Некорректная реализация: Если ibcmd не поддерживает файл списка (listFile), как DESIGNER, то частичная загрузка может не работать корректно
  2. Неполная реализация: Возможно, для ibcmd требуется другой механизм передачи списка изменённых файлов

Проверьте документацию ibcmd и уточните:

  • Поддерживает ли ibcmd config import files --partial автоматическое определение изменённых файлов?
  • Или требуется явная передача списка файлов?
ibcmd config import files --partial changed files list

Also applies to: 91-107

🤖 Prompt for AI Agents
In
src/main/kotlin/io/github/alkoleft/mcp/application/actions/build/IbcmdBuildAction.kt
around lines 59-75 and 91-107, the changedFiles parameter is accepted but never
used; verify ibcmd's behavior and either (A) if ibcmd supports passing an
explicit changed-files list for partial imports, call the
partialLoadListGenerator to generate the list from changedFiles and pass that
list path (e.g., set the import/listFile parameter or equivalent) into the
actionDsl.import call, or (B) if ibcmd auto-detects changed files or does not
support a list, remove the unused changedFiles parameter and/or add a clear
comment explaining why it is unused; implement the chosen path consistently in
both loadConfigurationPartial and loadExtensionPartial.

Comment on lines +27 to +35
data class BuildProperties(
/**
* Порог количества файлов для переключения на частичную загрузку.
* Если количество измененных файлов меньше этого значения,
* будет использована частичная загрузка (partial load).
* По умолчанию: 20 файлов
*/
val partialLoadThreshold: Int = 20,
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Добавьте валидацию порогового значения

Значение partialLoadThreshold может быть установлено в некорректное значение (≤0 или чрезмерно большое) через конфигурацию, что приведёт к неожиданному поведению.

Добавьте валидацию:

 data class BuildProperties(
     /**
      * Порог количества файлов для переключения на частичную загрузку.
      * Если количество измененных файлов меньше этого значения,
      * будет использована частичная загрузка (partial load).
      * По умолчанию: 20 файлов
      */
     val partialLoadThreshold: Int = 20,
-)
+) {
+    init {
+        require(partialLoadThreshold > 0) {
+            "partialLoadThreshold должен быть больше 0, получено: $partialLoadThreshold"
+        }
+    }
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
data class BuildProperties(
/**
* Порог количества файлов для переключения на частичную загрузку.
* Если количество измененных файлов меньше этого значения,
* будет использована частичная загрузка (partial load).
* По умолчанию: 20 файлов
*/
val partialLoadThreshold: Int = 20,
)
data class BuildProperties(
/**
* Порог количества файлов для переключения на частичную загрузку.
* Если количество измененных файлов меньше этого значения,
* будет использована частичная загрузка (partial load).
* По умолчанию: 20 файлов
*/
val partialLoadThreshold: Int = 20,
) {
init {
require(partialLoadThreshold > 0) {
"partialLoadThreshold должен быть больше 0, получено: $partialLoadThreshold"
}
}
}
🤖 Prompt for AI Agents
In
src/main/kotlin/io/github/alkoleft/mcp/configuration/properties/BuildProperties.kt
around lines 27 to 35, add runtime validation for partialLoadThreshold to reject
non-positive or unreasonably large values; implement an init block (or use
require) to ensure partialLoadThreshold is within an acceptable range (e.g.
1..10_000) and throw an IllegalArgumentException with a clear message if
violated, so invalid configuration values (≤0 or too large) are rejected early.

Comment on lines +103 to +118
fun generate(
sourceSetPath: Path,
changedFiles: Set<Path>,
): Path {
logger.debug { "Генерация списка файлов для частичной загрузки: ${changedFiles.size} файлов" }

val filesToLoad = mutableSetOf<String>()

changedFiles.forEach { absolutePath ->
processChangedFile(sourceSetPath, absolutePath, filesToLoad)
}

logger.info { "Подготовлено ${filesToLoad.size} файлов для частичной загрузки" }

return createListFile(filesToLoad)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Проверьте очистку временных файлов

Метод создаёт временный файл через createListFile(), но не обеспечивает его удаление. В случае многократных вызовов это может привести к накоплению временных файлов.

Рекомендация: добавьте механизм очистки временных файлов после использования или используйте deleteOnExit():

 private fun createListFile(files: Set<String>): Path {
     val listFile = Files.createTempFile("partial-load-", ".txt")
+    listFile.toFile().deleteOnExit()
     Files.write(listFile, files.sorted())
     logger.debug { "Создан файл списка: $listFile с ${files.size} записями" }
     return listFile
 }

Либо измените API так, чтобы вызывающий код отвечал за очистку, явно документировав это.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In
src/main/kotlin/io/github/alkoleft/mcp/infrastructure/utility/PartialLoadListGenerator.kt
around lines 103-118, the generate() method calls createListFile(filesToLoad)
which creates a temporary file but never ensures its deletion; modify the
implementation so temporary files are cleaned up: either (A) call createListFile
to mark the file for deletion (e.g., invoke deleteOnExit() on the created
Path/File or register a JVM shutdown hook) before returning, or (B) change the
API so generate() returns a managed resource (e.g., a Closeable/AutoCloseable
wrapper or provide an explicit delete() method) and document that the caller is
responsible for deletion; implement one approach consistently and update method
javadoc to state who owns cleanup responsibility.

Comment on lines +226 to +236
try {
directory.walk()
.filter { Files.isRegularFile(it) }
.forEach { file ->
val relativePath = sourceSetPath.relativize(file).toString().replace("\\", "/")
filesToLoad.add(relativePath)
logger.trace { "Добавлен связанный файл: $relativePath" }
}
} catch (e: Exception) {
logger.warn(e) { "Ошибка при сканировании каталога: $directory" }
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Улучшите обработку ошибок при сканировании каталогов

При ошибке сканирования выводится предупреждение, но процесс продолжается. Это может привести к неполному списку файлов и неожиданным результатам сборки.

Рекомендации:

  1. Логируйте конкретный тип исключения для диагностики
  2. Рассмотрите прерывание генерации при критических ошибках доступа
 } catch (e: Exception) {
-    logger.warn(e) { "Ошибка при сканировании каталога: $directory" }
+    logger.error(e) { "Критическая ошибка при сканировании каталога $directory: ${e.javaClass.simpleName}" }
+    throw IllegalStateException("Не удалось собрать полный список файлов для частичной загрузки", e)
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
try {
directory.walk()
.filter { Files.isRegularFile(it) }
.forEach { file ->
val relativePath = sourceSetPath.relativize(file).toString().replace("\\", "/")
filesToLoad.add(relativePath)
logger.trace { "Добавлен связанный файл: $relativePath" }
}
} catch (e: Exception) {
logger.warn(e) { "Ошибка при сканировании каталога: $directory" }
}
try {
directory.walk()
.filter { Files.isRegularFile(it) }
.forEach { file ->
val relativePath = sourceSetPath.relativize(file).toString().replace("\\", "/")
filesToLoad.add(relativePath)
logger.trace { "Добавлен связанный файл: $relativePath" }
}
} catch (e: Exception) {
logger.error(e) { "Критическая ошибка при сканировании каталога $directory: ${e.javaClass.simpleName}" }
throw IllegalStateException("Не удалось собрать полный список файлов для частичной загрузки", e)
}
🤖 Prompt for AI Agents
In
src/main/kotlin/io/github/alkoleft/mcp/infrastructure/utility/PartialLoadListGenerator.kt
around lines 226 to 236, the catch block currently logs a generic warning and
continues, which can mask the specific error and allow generation to proceed
with incomplete file lists; update the catch to log the exception type and
message (e.g., include e::class.simpleName and e.message) and decide on failing
fast for critical I/O errors by rethrowing a wrapped RuntimeException or
specific custom exception after logging so the generation is aborted when
access/permission errors occur; non-critical exceptions can still be logged and
skipped, but ensure the log includes full diagnostic details (exception class,
message, and stacktrace) to aid troubleshooting.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant