feat(config): ConfigWatcher tracks HOCON include files#610
feat(config): ConfigWatcher tracks HOCON include files#610dsudomoin wants to merge 11 commits intokora-projects:1.0from
Conversation
… paths Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- ContainerConfigOrigin(List) now flattens nested containers (consistency with 2-arg constructor) - ConfigWatcher removes stale files from watch list after refresh - Add comment explaining "merge of" filter origin Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ConfigFactory Move extractIncludedFiles, collectFileOrigins, enrichOriginWithIncludes from the interface to the factory class where they belong. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace config tree walking approach with TrackingConfigIncluder that intercepts include file() calls during HOCON parsing. This is more correct because: - Catches all includes, even empty files or overridden values - No fragile "merge of" string filtering - Uses the proper Typesafe Config extension point Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… HoconConfigFactory Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…luder
Handle include "filename" (not just include file("...")) by resolving
the path via ConfigIncludeContext.relativeTo() and recording it if it
points to a file on disk.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
| closeable.close(); | ||
| return ConfigFactory.parseFile(file.path().toFile()); | ||
| } else if (origin instanceof ContainerConfigOrigin container) { | ||
| for (var o : container.origins()) { |
There was a problem hiding this comment.
classpath отвалится.
Это надо сделать так: ContainerConfigOrigin сделать интерфейсом и брать у него в вотчере список.
Для хокона сделать ConfigOrigin, который реализует этот интерфейс и имеет какой-то openInputStream или что-то такое, чтобы считать то, что является источником.
There was a problem hiding this comment.
Поправил: ContainerConfigOrigin теперь интерфейс с origins(). Для HOCON сделал HoconConfigOrigin, который реализует его и имеет openInputStream() — работает и с файлами, и с classpath. applicationUnresolved() упрощён, больше нет pattern-matching по типам origin. TrackingConfigIncluder теперь фильтрует не-файлы через Files.isRegularFile().
| * <p> | ||
| * Delegates actual include resolution to the fallback (default) includer. | ||
| */ | ||
| final class TrackingConfigIncluder implements ConfigIncluder, ConfigIncluderFile { |
There was a problem hiding this comment.
Тут кажется надо фильтрануть не файлы.
There was a problem hiding this comment.
Добавил проверку Files.isRegularFile(path) в обоих методах include() и includeFile() — теперь classpath ресурсы, URL и несуществующие пути отсеиваются.
…, HoconConfigOrigin with openInputStream - Convert ContainerConfigOrigin from class to interface with origins() method - Add SimpleContainerConfigOrigin for general use (MergeConfigFactory) - Add HoconConfigOrigin implementing ContainerConfigOrigin with openInputStream() to support both file and classpath sources polymorphically - Simplify HoconConfigModule.applicationUnresolved() — no more pattern-matching on origin types, just uses HoconConfigOrigin.openInputStream() - Filter non-file includes in TrackingConfigIncluder (Files.isRegularFile check) to ignore classpath resources and non-existent paths Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Summary
includedirectivesTrackingConfigIncluder(ConfigIncluder+ConfigIncluderFile) to intercept include calls during parsing and record file pathsinclude file("path")and heuristicinclude "name"(resolved viaConfigIncludeContext.relativeTo())ConfigFactory.parseReader()toConfigFactory.parseFile()/parseURL()for correct include resolutionMotivation
When
application.confincludes external files (e.g.include file("/opt/app/config/override.conf")), changes to those files were invisible to ConfigWatcher. This is a common pattern in Kubernetes whereapplication.confstays in resources and per-environment overrides are mounted separately.Changes
ContainerConfigOrigin— addedList<ConfigOrigin>constructor with nested container flatteningTrackingConfigIncluder— new class implementingConfigIncluder+ConfigIncluderFile, intercepts include calls and records file pathsHoconConfigFactory— addedparseAndEnrichOrigin()andenrichOriginWithIncludes()for building enrichedConfigOriginwith all discovered include filesHoconConfigModule—applicationConfigOrigin()usesHoconConfigFactory.parseAndEnrichOrigin();applicationUnresolved()usesparseFile()/parseURL()ConfigWatcher— recalculates watched files after each refreshTest plan
include file("...")— included file path recordedinclude file()(main → level1 → level2) — all paths recordedinclude "name"— resolved file path recorded🤖 Generated with Claude Code