feat: Implement initialization command #31
Conversation
… configuration management
There was a problem hiding this comment.
Pull request overview
Introduces a persisted CLI configuration mechanism (shared + local) and adds an interactive init command to scaffold/update those configs, with generate now defaulting to config values unless explicitly overridden by CLI options.
Changes:
- Added
IConfigService/ConfigServiceplusSharedConfigandLocalConfigmodels for reading/writing.al2dbml/config.jsonand.al2dbml/config.local.json. - Added
initCLI command to prompt for config values, update.gitignore, and optionally install/update a git pre-commit hook. - Updated
generatecommand and CLI registration/DI wiring to use configuration defaults.
Reviewed changes
Copilot reviewed 7 out of 8 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/AL2DBML.CLI/Services/IConfigService.cs | Defines config read/write contract for shared/local config. |
| src/AL2DBML.CLI/Services/ConfigService.cs | Implements JSON-backed config persistence under .al2dbml/. |
| src/AL2DBML.CLI/Models/SharedConfig.cs | Adds shared output configuration model (path/name). |
| src/AL2DBML.CLI/Models/LocalConfig.cs | Adds local input configuration model (path). |
| src/AL2DBML.CLI/Commands/InitCommand.cs | Implements interactive initialization + gitignore/hook integration. |
| src/AL2DBML.CLI/Commands/GenerateCommand.cs | Loads config defaults when CLI options aren’t provided. |
| src/AL2DBML.CLI/Program.cs | Registers new services and init command with Spectre.Console.Cli. |
| .gitignore | Updates ignore patterns for generated outputs and config files. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| private static void WritePreCommitHook() | ||
| { | ||
| const string hookPath = ".git/hooks/pre-commit"; | ||
| var hookSection = $"{HookStartMarker}\nif command -v al2dbml > /dev/null 2>&1; then\n {HookCommand} || echo \"Warning: al2dbml generate failed, skipping DBML update.\"\nelse\n echo \"Warning: al2dbml not found, skipping DBML update.\"\nfi\n{HookEndMarker}"; | ||
|
|
||
| string content; | ||
| if (File.Exists(hookPath)) | ||
| { | ||
| content = File.ReadAllText(hookPath); | ||
| var startIdx = content.IndexOf(HookStartMarker, StringComparison.Ordinal); | ||
| var endIdx = content.IndexOf(HookEndMarker, StringComparison.Ordinal); | ||
|
|
||
| if (startIdx >= 0 && endIdx >= 0) | ||
| content = content[..startIdx] + hookSection + content[(endIdx + HookEndMarker.Length)..]; | ||
| else | ||
| content = content.TrimEnd() + $"\n\n{hookSection}\n"; | ||
| } | ||
| else | ||
| { | ||
| content = $"#!/bin/sh\n\n{hookSection}\n"; | ||
| } | ||
|
|
||
| File.WriteAllText(hookPath, content); | ||
|
|
There was a problem hiding this comment.
WritePreCommitHook() writes to .git/hooks/pre-commit but doesn’t verify that the repository is a git repo or that .git/hooks exists. Running init outside a git checkout will throw DirectoryNotFoundException. Consider detecting .git/hooks (or .git) first and either skipping hook creation with a warning or creating the directory structure where appropriate.
| app.Configure(config => | ||
| { | ||
| config.AddCommand<GenerateCommand>("generate"); | ||
| config.AddCommand<InitCommand>("init"); | ||
| }); |
There was a problem hiding this comment.
PR description still contains the placeholder closes #. The repo’s PR validation workflow requires a real closes/fixes/resolves #<issue> reference, so this will likely fail CI until it’s updated.
| return JsonSerializer.Deserialize<SharedConfig>(File.ReadAllText(path), JsonOptions); | ||
| } | ||
|
|
||
| public LocalConfig? LoadLocalConfig() | ||
| { | ||
| var path = Path.Combine(ConfigDir, LocalConfigFile); | ||
| if (!File.Exists(path)) return null; | ||
| return JsonSerializer.Deserialize<LocalConfig>(File.ReadAllText(path), JsonOptions); |
There was a problem hiding this comment.
LoadSharedConfig/LoadLocalConfig will throw (and crash the CLI) if the JSON is invalid/empty or the file is unreadable, since JsonSerializer.Deserialize and File.ReadAllText exceptions aren’t handled. Consider catching JsonException/IOException and either returning null (with a warning) or throwing a more user-friendly exception that the commands can render nicely.
| return JsonSerializer.Deserialize<SharedConfig>(File.ReadAllText(path), JsonOptions); | |
| } | |
| public LocalConfig? LoadLocalConfig() | |
| { | |
| var path = Path.Combine(ConfigDir, LocalConfigFile); | |
| if (!File.Exists(path)) return null; | |
| return JsonSerializer.Deserialize<LocalConfig>(File.ReadAllText(path), JsonOptions); | |
| try | |
| { | |
| var json = File.ReadAllText(path); | |
| if (string.IsNullOrWhiteSpace(json)) return null; | |
| return JsonSerializer.Deserialize<SharedConfig>(json, JsonOptions); | |
| } | |
| catch (IOException) | |
| { | |
| // Treat unreadable config as missing | |
| return null; | |
| } | |
| catch (JsonException) | |
| { | |
| // Treat invalid JSON config as missing | |
| return null; | |
| } | |
| } | |
| public LocalConfig? LoadLocalConfig() | |
| { | |
| var path = Path.Combine(ConfigDir, LocalConfigFile); | |
| if (!File.Exists(path)) return null; | |
| try | |
| { | |
| var json = File.ReadAllText(path); | |
| if (string.IsNullOrWhiteSpace(json)) return null; | |
| return JsonSerializer.Deserialize<LocalConfig>(json, JsonOptions); | |
| } | |
| catch (IOException) | |
| { | |
| // Treat unreadable config as missing | |
| return null; | |
| } | |
| catch (JsonException) | |
| { | |
| // Treat invalid JSON config as missing | |
| return null; | |
| } |
…ved warning messages
Description
This pull request introduces a configuration system to the CLI, enabling persistent storage of input/output paths and output file names, and adds an interactive
initcommand to simplify project setup. The changes also allow the CLI to read these configuration values by default, while still permitting command-line overrides. Additionally, the PR provides optional integration with Git pre-commit hooks and updates dependency injection and command registration accordingly.Configuration system and command updates:
ConfigService(ConfigService.cs,IConfigService.cs) to handle reading and writing shared (config.json) and local (config.local.json) configuration files under the.al2dbmldirectory. This service is registered for dependency injection. [1] [2] [3]SharedConfigfor output settings andLocalConfigfor input settings, with sensible defaults. [1] [2]GenerateCommandto use configuration values as defaults for input path, output path, and output file name, falling back to command-line arguments if provided.New CLI features:
initcommand (InitCommand) that prompts the user for configuration values, saves them, ensures.gitignoreis updated, and optionally creates or updates a Git pre-commit hook to runal2dbml generate.initcommand in the CLI application.These changes streamline project setup and make it easier to maintain consistent configuration across teams and environments.
Closes
closes #4