diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a43f7ab..2a45d66 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ jobs: strategy: matrix: dart: [3.3, 3.6] - package: [cli_tools] + package: [cli_tools, config] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -30,7 +30,7 @@ jobs: strategy: matrix: dart: [3.3, 3.6] - package: [cli_tools] + package: [cli_tools, config] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -48,7 +48,7 @@ jobs: strategy: matrix: dart: [3.3, 3.6] - package: [cli_tools] + package: [cli_tools, config] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-cli_tools.yaml b/.github/workflows/publish-cli_tools.yaml index fcaa11c..97342f5 100644 --- a/.github/workflows/publish-cli_tools.yaml +++ b/.github/workflows/publish-cli_tools.yaml @@ -1,4 +1,4 @@ -name: Publish CLI Tools +name: Publish cli_tools package on: push: diff --git a/.github/workflows/publish-config.yaml b/.github/workflows/publish-config.yaml new file mode 100644 index 0000000..8e33bdd --- /dev/null +++ b/.github/workflows/publish-config.yaml @@ -0,0 +1,15 @@ +name: Publish config package + +on: + push: + tags: + # Matches tags like config-v1.2.3 and config-v1.2.3-pre.1 + - 'config-v[0-9]+.[0-9]+.[0-9]+*' + +jobs: + publish: + permissions: + id-token: write + uses: dart-lang/setup-dart/.github/workflows/publish.yml@v1 + with: + working-directory: packages/config diff --git a/README.md b/README.md index 37ec7b5..ecb7d9d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![Serverpod banner](https://github.com/serverpod/serverpod/raw/main/misc/images/github-header.webp) +[![Serverpod banner](https://github.com/serverpod/serverpod/raw/main/misc/images/github-header.webp)](https://github.com/serverpod/serverpod) The cli_tools repository contains open-source packages that help you build CLI commands. @@ -10,6 +10,13 @@ They are actively maintained and used by the Serverpod team. The [cli_tools package](packages/cli_tools/README.md) offers several utilities for CLI development, for example: terminal logging, user-input prompting, and usage-analytics collection. +### config + +The [config package](packages/config/README.md) provides comprehensive +configuration ingestion and validation, including typed command line options, +environment variables, and configuration files as input, and better error +reporting. + ## Contributing Guidelines diff --git a/packages/cli_tools/CHANGELOG.md b/packages/cli_tools/CHANGELOG.md index 04d5cc4..57522b3 100644 --- a/packages/cli_tools/CHANGELOG.md +++ b/packages/cli_tools/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +## 0.7.0-beta.2 +- refactor!: Moved out the `config` library from the `cli_tools` package and into its own package, to be published as `config` on pub.dev. + ## 0.7.0-beta.1 - refactor: Reorganized files in new subdir packages/cli_tools - fix!: Removed unused static options list in BetterCommandRunner diff --git a/packages/cli_tools/README.md b/packages/cli_tools/README.md index 32e5f35..2dbc96f 100644 --- a/packages/cli_tools/README.md +++ b/packages/cli_tools/README.md @@ -1,4 +1,4 @@ -![Serverpod banner](https://github.com/serverpod/serverpod/raw/main/misc/images/github-header.webp) +[![Serverpod banner](https://github.com/serverpod/serverpod/raw/main/misc/images/github-header.webp)](https://github.com/serverpod/serverpod) # CLI Tools @@ -7,11 +7,7 @@ These tools were developed for the Serverpod CLI but can be used in any Dart pro ## Config library -The config library is a significant extension to the args package and enables -typed options, environment variables and configuration files as input, and -better error reporting. - -[Config README](#config) +The `config` library has been moved into its own package, published on pub.dev as [config](https://pub.dev/packages/config). ## Contributing to the Project @@ -27,395 +23,4 @@ We are happy to accept contributions. To contribute, please do the following: Tests are required to accept any pull requests. -# Config - -The config library is a significant extension to the Dart args package. - -The main features are: - -- Typed arg options: `int`, `DateTime`, `Duration`, user-defined `Enums`. - - Automatic parsing and user-friendly error messages. - - Type-specific constraints, such as min/max for all `Comparable` option types. - - Multivalued options are typed, e.g. `List`. - - Custom types can easily be added and combined with the existing ones. - - See [Supported option types](#supported-option-types) for the complete list. - -- Equal support for positional arguments, with proper validation. - - Arguments can be both positional and named, making the --name optional. - -- Equal support for environment variables. - - Options can be specified both via arguments and environment variables. - - Environment variables have the same typed values support as args. - -- Options can be fetched from [configuration files](#using-configuration-files) as well. - - YAML/JSON configuration file support. - -- Options can have custom value-providing callbacks. - -- Named option groups are supported. - - A group can specify mutually exclusive options. - - A group can be mandatory in that at least one of its options is set. - -- Tracability - the information on an option's value source is retained. - -- The error handling is consistent, in contrast to the args package. - - Fail-fast, all validation is performed up-front. - - All errors are collected, avoiding the poor UX of fix-one-and-then-get-the-next-error. - - Well-defined exception behavior. - -These tools were developed for the Serverpod CLI but can be used in any Dart project. - -## Drop-in replacement - -The `ConfigParser` class is designed as a drop-in replacement for `ArgParser` -from the `args` package. Its purpose is to make it easy to transition -to the config library - just replace the name `ArgParser` with `ConfigParser`. - -It maintains almost complete compatibility with the original package while -enabling direct use of the new features. - -It achieves complete compatibility with the original package with the exception -of addCommand(), which you can replace with -[`BetterCommandRunner`](lib/src/better_command_runner/better_command_runner.dart). - -- **Compatibility**: The `ConfigParser` implements the same interface as - `ArgParser`, and returns a `ConfigResults` object that implements `ArgResults`. -- **Usage**: You can directly replace `ArgParser` in your existing code: - ```dart - final parser = ConfigParser(); // instead of ArgParser() - parser.addFlag('verbose', abbr: 'v'); - parser.addOption('port', defaultsTo: '8080'); - parser.addOption('host', envName: 'HOST'); // using env feature - final results = parser.parse(['--verbose', '--port', '3000']); - ``` - -- **Key Differences**: - - The `addCommand()` method is not supported - (see [`BetterCommandRunner`](lib/src/better_command_runner/better_command_runner.dart) instead) - - All validation is performed up-front with consistent error messages - - The parser supports additional configuration sources (environment variables, config files) - -- **Migration Path**: You can start using `ConfigParser` as a direct replacement -for `ArgParser` and gradually adopt its additional features as needed. - -## Usage - -_For transitioning existing code from ArgParser, see the drop-in replacement -section above._ - -This library emphasizes a declarative style of defining options. -Here is a real-life example, from a _show logs_ command, -that shows how to create a set of options for a particular command as an _enum_. - -```dart -import 'package:cli_tools/config.dart'; - -enum LogOption implements OptionDefinition { - limit(IntOption( - argName: 'limit', - helpText: 'The maximum number of log records to fetch.', - defaultsTo: 50, - min: 0, - )), - utc(FlagOption( - argName: 'utc', - argAbbrev: 'u', - helpText: 'Display timestamps in UTC timezone instead of local.', - defaultsTo: false, - envName: 'DISPLAY_UTC', - )), - recent(DurationOption( - argName: 'recent', - argAbbrev: 'r', - argPos: 0, - helpText: - 'Fetch records from the recent period. ' - 'Can also be specified as the first argument.', - min: Duration.zero, - )), - before(DateTimeOption( - argName: 'before', - helpText: 'Fetch records from before this timestamp.', - )); - - const LogOption(this.option); - - @override - final ConfigOptionBase option; -} -``` - -The enum form enables constant initialization, typed `Configuration`, -and easy reference. - -```dart - Future runWithConfig( - final Configuration commandConfig, - ) async { - final limit = commandConfig.value(LogOption.limit); - final inUtc = commandConfig.value(LogOption.utc); - final recentOpt = commandConfig.optionalValue(LogOption.recent); - final beforeOpt = commandConfig.optionalValue(LogOption.before); - ... - } -``` - -It is also possible to create them as a List: - -```dart -abstract final class _ProjectOptions { - static const name = StringOption( - argName: 'name', - mandatory: true - ); - static const enable = FlagOption( - argName: 'enable', - defaultsTo: false, - ); - - static createOptions = [ - name, - enable, - ]; -} -... - - Future runWithConfig( - final Configuration commandConfig, - ) async { - final name = commandConfig.value(_ProjectOptions.name); - final enable = commandConfig.value(_ProjectOptions.enable); - ... - } -``` - -> Note that options that are mandatory or have a default value have a guaranteed value. -They return a non-nullable type, while "optional" options return a nullable type. - -### Main classes - -An instance of the [OptionDefinition](lib/src/config/configuration.dart) class -defines an option. -This is an abstract class and implemented by option Enum types -as well as the base option class `ConfigOptionBase`. -The latter is typically -not used directly, instead the typed subclasses are used such as `StringOption` -or `IntOption`. - -An instance of the [Configuration](lib/src/config/configuration.dart) class -holds a configuration, i.e. the values for a set of option definitions. - -### Resolution order - -The configuration library resolves each option value in a specific order, with earlier sources taking precedence over later ones. - -1. **Command-line arguments** - - Named arguments (e.g., `--verbose` or `-v`) have top precedence - - Positional arguments are resolved after named - - Specified using `argName`, `argAbbrev`, and `argPos` - -2. **Environment variables** - - Environment variables have second precedence after CLI arguments - - Variable name is specified using `envName` - -3. **Configuration files** - - Values from configuration files (e.g. YAML/JSON) - - Lookup key is specified using `configKey` - -4. **Custom value providers** - - Values from custom callbacks - - Callbacks are allowed to depend on other option values - (option definition order is significant in this case) - - Callback is specified using `fromCustom` - -5. **Default values** - - A default value guarantees that an option has a value - - Const values are specified using `defaultsTo` - - Non-const values are specifed with a callback using `fromDefault` - -This order ensures that: -- Command-line arguments always take precedence, allowing users to override any other settings -- Environment variables can be used for values used across multiple command invocations, - or to override other configuration sources -- Configuration files provide persistent settings -- Custom providers enable complex logic and integration with external systems -- Default values serve as a fallback when no other value is specified - -### Resolution sources - -Only the value sources provided to the `Configuration.resolve` constructor are -actually included. This means that any precedence tiers can be skipped, -regardless of what the option definitions say. - -This enables flexible inclusion of sources depending on context -and helps constructing specific test cases. - -### Supported option types - -The library provides a rich set of typed options out of the box. All option types support the common arguments like `argName`, `helpText`, `mandatory`, etc. Below are the additional type-specific arguments: - -| Value Type | Option Class | Additional Settings | Description | -|------|-------|---------------------|-------------| -| String | `StringOption` | None | String values | -| Boolean | `FlagOption` | `negatable` | Whether the flag can be negated | -| Integer | `IntOption` | `min`
`max` | Minimum allowed value
Maximum allowed value | -| DateTime | `DateTimeOption` | `min`
`max` | Minimum allowed date/time
Maximum allowed date/time | -| Duration | `DurationOption` | `min`
`max` | Minimum allowed duration
Maximum allowed duration | -| Any Enum | `EnumOption` | None | Typed enum values | -| File | `FileOption` | `mode` | Whether the file must exist, must not exist, or may exist | -| Directory | `DirOption` | `mode` | Whether the directory must exist, must not exist, or may exist | -| String List | `MultiStringOption` | `splitCommas` | Whether to split input on commas | -| Any List | `MultiOption` | `multiParser` | Parser for the element type | - -It is easy to add custom option types, and to reuse the parsing code from existing option types. Just copy code from existing options and modify as needed. - -#### Common option features - -All option types support: -- Command-line arguments (full name, abbreviated name, and positional) -- Environment variables -- Configuration file values -- Custom value-providing callback -- Default values -- Allowed values list validation -- Aliases -- Custom validation -- Help text and value descriptions -- Mandatory -- Hidden -- Option groups - -### Resolving a Configuration - -This is an overview of how a Configuration is resolved. - -```mermaid -sequenceDiagram - participant User Code - participant OptionDefinitions - participant Configuration - participant ArgParser - participant EnvVariables - participant ConfigSourceProvider - participant ConfigurationSource - - User Code->>OptionDefinitions: Define options - User Code->>Configuration: Resolve with context (args, env, config, etc) - Configuration->>ArgParser: Parse command-line args - Configuration->>EnvVariables: Lookup environment variables - Configuration->>ConfigSourceProvider: (if needed) getConfigSource(cfg) - ConfigSourceProvider->>ConfigurationSource: Provide config data - ConfigurationSource-->>Configuration: Value for key - Configuration-->>User Code: Typed Configuration (with errors if any) -``` - -## Integration with commands - -In Dart, commands are often implemented using `Command` and `CommandRunner` -from the `args` package. - -To use the config library with these, they need to be subclassed -to modify the use of `ArgParser` and introduce `Configuration`. This has -already been done for you, with the `BetterCommand` and `BetterCommandRunner` -classes in the `better_command_runner` library in this package. - -See the full example [example/config_simple_example.dart](example/config_simple_example.dart). - -## Using configuration files - -To use configuration files as a source of option values, -a `ConfigurationBroker` needs to be provided when resolving the -`Configuration`. - -```dart - Configuration.resolve( - options: options, - argResults: argResults, - env: envVariables, - configBroker: FileConfigBroker(), - ); -``` - -A file-reading ConfigurationBroker can be implemented like this: - -```dart -class FileConfigBroker implements ConfigurationBroker { - ConfigurationSource? _configSource; - - FileConfigBroker(); - - @override - String? valueOrNull(final String key, final Configuration cfg) { - // By lazy-loading the config, the file path can depend on another option - _configSource ??= ConfigurationParser.fromFile( - cfg.value(TimeSeriesOption.configFile).path, - ); - final value = _configSource?.valueOrNull(key); - return value is String ? value : null; - } -} -``` - -To reference a value from the configuration broker in an option definition, -specify the `configKey`. In this example, the configuration file is a JSON or -YAML file and the JSON pointer syntax is used. - -```dart - interval(DurationOption( - argName: 'interval', - argAbbrev: 'i', - configKey: '/interval', // JSON pointer - )); -``` - -See the full example in [example/config_file_example.dart](example/config_file_example.dart). - -### Multiple configuration sources - -By using the `MultiDomainConfigBroker`, configuration sources -from multiple providers can be used, called configuration *domains*. - -They are distinguished by the format used in the configKey, -which needs to specify a so-called *qualified key* - -qualifying the key with the domain it is found in. - -Domains are matched using `Pattern`, e.g. string prefixes or regular expressions. -For example, a simple prefix and colon syntax can be used: - -```dart - dir(DirOption( - argName: 'dir', - configKey: 'local:/dir', - helpText: 'the local directory', - )), - host(StringOption( - argName: 'host', - configKey: 'remote:/host', - helpText: 'the remote host name', - )); -``` - -The first domain that matches the qualified key is used to retrieve the value. -This means that the order of the domains is significant if the matching patterns -overlap. - -#### RegExp domains - -Advanced pattern matching is supported via `RegExp`, enabling complex qualifiers -including paths and URLs, such that the key pattern qualifies the domain. - -When using regular expressions to identify the domain, the value key is derived -from the qualified key depending on the capturing groups in the regex. - -- If the regex has no capturing groups: - - If the regex matches a shorter string than the qualified key, the value key is the remainder after the match.\ - This makes prefix matching simple. - - If the regex matches the entire qualified key, the value key is the entire qualified key.\ - This can be used for specific syntaxes like URLs. - -- If the regex has one or more capturing groups:\ - The value key is the string captured by the first group. - -For more information, see the -[`MultiDomainConfigBroker`](lib/src/config/multi_config_source.dart) -source documentation. +See also [CONTRIBUTING](/CONTRIBUTING.md). diff --git a/packages/cli_tools/example/config_simple_example.dart b/packages/cli_tools/example/config_simple_example.dart deleted file mode 100644 index af803bd..0000000 --- a/packages/cli_tools/example/config_simple_example.dart +++ /dev/null @@ -1,49 +0,0 @@ -import 'package:cli_tools/cli_tools.dart'; - -Future main(List args) async { - var commandRunner = BetterCommandRunner( - 'example', - 'Example CLI command', - ); - commandRunner.addCommand(TimeSeriesCommand()); - - try { - await commandRunner.run(args); - } on UsageException catch (e) { - print(e); - return 1; - } - return 0; -} - -enum TimeSeriesOption implements OptionDefinition { - interval(DurationOption( - argName: 'interval', - argAbbrev: 'i', - helpText: 'The interval between the series elements', - mandatory: true, - min: Duration(seconds: 1), - max: Duration(days: 1), - )); - - const TimeSeriesOption(this.option); - - @override - final ConfigOptionBase option; -} - -class TimeSeriesCommand extends BetterCommand { - TimeSeriesCommand() : super(options: TimeSeriesOption.values); - - @override - String get name => 'series'; - - @override - String get description => 'Generate a series of time stamps'; - - @override - void runWithConfig(Configuration commandConfig) { - var interval = commandConfig.value(TimeSeriesOption.interval); - print('interval: $interval'); - } -} diff --git a/packages/cli_tools/example/main.dart b/packages/cli_tools/example/main.dart index 737510c..f968a7c 100644 --- a/packages/cli_tools/example/main.dart +++ b/packages/cli_tools/example/main.dart @@ -1,4 +1,5 @@ import 'package:cli_tools/cli_tools.dart'; +import 'package:config/config.dart'; Future main(List args) async { var commandRunner = BetterCommandRunner( diff --git a/packages/cli_tools/example/simple_command_example.dart b/packages/cli_tools/example/simple_command_example.dart new file mode 100644 index 0000000..d61b547 --- /dev/null +++ b/packages/cli_tools/example/simple_command_example.dart @@ -0,0 +1,63 @@ +import 'package:cli_tools/better_command_runner.dart'; +import 'package:config/config.dart'; + +/// Example of using [BetterCommandRunner] to create subcommands +/// together with the `config` package to ingest configuration +/// from the command line or environment variables. +/// +/// Run this example program like so: +/// ```sh +/// dart run example/simple_command_example.dart show --interval=1s +/// ``` +/// or +/// ```sh +/// INTERVAL=1s dart run example/simple_command_example.dart show +/// ``` +Future main(List args) async { + var commandRunner = BetterCommandRunner( + 'example', + 'Example CLI command', + ); + commandRunner.addCommand(ShowCommand()); + + try { + await commandRunner.run(args); + } on UsageException catch (e) { + print(e); + return 1; + } + return 0; +} + +enum ShowOption implements OptionDefinition { + interval(DurationOption( + argName: 'interval', + argAbbrev: 'i', + envName: 'INTERVAL', + helpText: 'The time interval', + mandatory: true, + min: Duration(seconds: 1), + max: Duration(days: 1), + )); + + const ShowOption(this.option); + + @override + final ConfigOptionBase option; +} + +class ShowCommand extends BetterCommand { + ShowCommand() : super(options: ShowOption.values); + + @override + String get name => 'show'; + + @override + String get description => 'Show the configured interval'; + + @override + void runWithConfig(Configuration commandConfig) { + var interval = commandConfig.value(ShowOption.interval); + print('interval: $interval'); + } +} diff --git a/packages/cli_tools/lib/cli_tools.dart b/packages/cli_tools/lib/cli_tools.dart index e3e969b..160540c 100644 --- a/packages/cli_tools/lib/cli_tools.dart +++ b/packages/cli_tools/lib/cli_tools.dart @@ -1,6 +1,5 @@ export 'analytics.dart'; export 'better_command_runner.dart'; -export 'config.dart'; export 'docs_generator.dart'; export 'local_storage_manager.dart'; export 'logger.dart'; diff --git a/packages/cli_tools/lib/src/better_command_runner/better_command.dart b/packages/cli_tools/lib/src/better_command_runner/better_command.dart index ebf7beb..641c963 100644 --- a/packages/cli_tools/lib/src/better_command_runner/better_command.dart +++ b/packages/cli_tools/lib/src/better_command_runner/better_command.dart @@ -3,8 +3,7 @@ import 'dart:io' show Platform; import 'package:args/args.dart'; import 'package:args/command_runner.dart'; -import 'package:cli_tools/config.dart'; -import 'package:cli_tools/src/config/output_formatting.dart'; +import 'package:config/config.dart'; import 'better_command_runner.dart'; diff --git a/packages/cli_tools/lib/src/better_command_runner/better_command_runner.dart b/packages/cli_tools/lib/src/better_command_runner/better_command_runner.dart index e18c565..7c8242c 100644 --- a/packages/cli_tools/lib/src/better_command_runner/better_command_runner.dart +++ b/packages/cli_tools/lib/src/better_command_runner/better_command_runner.dart @@ -3,8 +3,7 @@ import 'dart:io' show Platform; import 'package:args/args.dart'; import 'package:args/command_runner.dart'; -import 'package:cli_tools/config.dart'; -import 'package:cli_tools/src/config/output_formatting.dart'; +import 'package:config/config.dart'; /// A function type for executing code before running a command. typedef OnBeforeRunCommand = Future Function(BetterCommandRunner runner); diff --git a/packages/cli_tools/lib/src/documentation_generator/documentation_generator.dart b/packages/cli_tools/lib/src/documentation_generator/documentation_generator.dart index 3988e9f..1613b5e 100644 --- a/packages/cli_tools/lib/src/documentation_generator/documentation_generator.dart +++ b/packages/cli_tools/lib/src/documentation_generator/documentation_generator.dart @@ -1,4 +1,4 @@ -import '../../better_command_runner.dart'; +import '../../better_command_runner.dart' show BetterCommandRunner; class CommandDocumentationGenerator { final BetterCommandRunner commandRunner; diff --git a/packages/cli_tools/lib/src/prompts/select.dart b/packages/cli_tools/lib/src/prompts/select.dart index ece1e34..4015df2 100644 --- a/packages/cli_tools/lib/src/prompts/select.dart +++ b/packages/cli_tools/lib/src/prompts/select.dart @@ -1,7 +1,8 @@ import 'dart:io'; import 'package:cli_tools/cli_tools.dart'; -import 'package:cli_tools/src/prompts/key_codes.dart'; + +import 'key_codes.dart'; /// Object that represents an option in a select prompt. class Option { diff --git a/packages/cli_tools/pubspec.lock b/packages/cli_tools/pubspec.lock index 5ff8eb7..3d54f97 100644 --- a/packages/cli_tools/pubspec.lock +++ b/packages/cli_tools/pubspec.lock @@ -58,13 +58,20 @@ packages: source: hosted version: "0.1.0" collection: - dependency: "direct main" + dependency: transitive description: name: collection sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted version: "1.18.0" + config: + dependency: "direct main" + description: + path: "../config" + relative: true + source: path + version: "0.7.0-beta.2" convert: dependency: transitive description: @@ -202,7 +209,7 @@ packages: source: hosted version: "0.12.16+1" meta: - dependency: "direct main" + dependency: transitive description: name: meta sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c @@ -282,7 +289,7 @@ packages: source: hosted version: "1.3.0" rfc_6901: - dependency: "direct main" + dependency: transitive description: name: rfc_6901 sha256: df1bbfa3d023009598f19636d6114c6ac1e0b7bb7bf6a260f0e6e6ce91416820 @@ -490,7 +497,7 @@ packages: source: hosted version: "1.2.1" yaml: - dependency: "direct main" + dependency: transitive description: name: yaml sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" diff --git a/packages/cli_tools/pubspec.yaml b/packages/cli_tools/pubspec.yaml index a1cc997..6383452 100644 --- a/packages/cli_tools/pubspec.yaml +++ b/packages/cli_tools/pubspec.yaml @@ -1,5 +1,5 @@ name: cli_tools -version: 0.7.0-beta.1 +version: 0.7.0-beta.2 description: A collection of tools for building great command-line interfaces. repository: https://github.com/serverpod/cli_tools homepage: https://serverpod.dev @@ -17,17 +17,14 @@ environment: dependencies: args: ^2.7.0 ci: ^0.1.0 - collection: ^1.18.0 + config: ^0.7.0-beta.2 http: '>=0.13.0 <2.0.0' - meta: ^1.11.0 path: ^1.8.2 pub_api_client: '>=2.4.0 <4.0.0' pub_semver: ^2.1.4 - rfc_6901: ^0.2.0 super_string: ^1.0.3 - yaml: ^3.1.2 dev_dependencies: - test: ^1.24.0 serverpod_lints: '>=1.2.6' test_descriptor: ^2.0.2 + test: ^1.24.0 diff --git a/packages/cli_tools/pubspec_overrides.yaml b/packages/cli_tools/pubspec_overrides.yaml new file mode 100644 index 0000000..598045e --- /dev/null +++ b/packages/cli_tools/pubspec_overrides.yaml @@ -0,0 +1,3 @@ +dependency_overrides: + config: + path: ../config diff --git a/packages/cli_tools/test/better_command_runner/analytics_test.dart b/packages/cli_tools/test/better_command_runner/analytics_test.dart index da4523e..af39b01 100644 --- a/packages/cli_tools/test/better_command_runner/analytics_test.dart +++ b/packages/cli_tools/test/better_command_runner/analytics_test.dart @@ -4,7 +4,7 @@ import 'package:args/command_runner.dart'; import 'package:cli_tools/better_command_runner.dart'; import 'package:test/test.dart'; -import '../test_utils/test_utils.dart'; +import '../test_utils/test_utils.dart' show flushEventQueue; class MockCommand extends Command { static String commandName = 'mock-command'; diff --git a/packages/cli_tools/test/better_command_test.dart b/packages/cli_tools/test/better_command_runner/better_command_test.dart similarity index 99% rename from packages/cli_tools/test/better_command_test.dart rename to packages/cli_tools/test/better_command_runner/better_command_test.dart index 6c5ba0c..f7b530a 100644 --- a/packages/cli_tools/test/better_command_test.dart +++ b/packages/cli_tools/test/better_command_runner/better_command_test.dart @@ -1,7 +1,7 @@ import 'dart:async'; import 'package:cli_tools/better_command_runner.dart'; -import 'package:cli_tools/config.dart'; +import 'package:config/config.dart'; import 'package:test/test.dart'; enum BespokeGlobalOption implements OptionDefinition { diff --git a/packages/cli_tools/test/documentation_generator/generate_markdown_test.dart b/packages/cli_tools/test/documentation_generator/generate_markdown_test.dart index 9effcde..421099a 100644 --- a/packages/cli_tools/test/documentation_generator/generate_markdown_test.dart +++ b/packages/cli_tools/test/documentation_generator/generate_markdown_test.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:args/command_runner.dart'; import 'package:cli_tools/cli_tools.dart'; +import 'package:config/config.dart' show Configuration, OptionDefinition; import 'package:test/test.dart'; class AddSpiceCommand extends Command { diff --git a/packages/cli_tools/test/prompts/multiple_select_test.dart b/packages/cli_tools/test/prompts/multiple_select_test.dart index b1d241e..086f053 100644 --- a/packages/cli_tools/test/prompts/multiple_select_test.dart +++ b/packages/cli_tools/test/prompts/multiple_select_test.dart @@ -2,9 +2,10 @@ // ignore_for_file: unused_local_variable import 'package:cli_tools/cli_tools.dart'; -import 'package:cli_tools/src/prompts/key_codes.dart'; -import 'package:cli_tools/src/prompts/select.dart'; +import 'package:cli_tools/src/prompts/key_codes.dart' show KeyCodes; +import 'package:cli_tools/src/prompts/select.dart' show underline; import 'package:test/test.dart'; + import '../test_utils/io_helper.dart'; import '../test_utils/prompts/key_code_sequence.dart'; import '../test_utils/prompts/option_matcher.dart'; diff --git a/packages/cli_tools/test/prompts/select_test.dart b/packages/cli_tools/test/prompts/select_test.dart index d887ca3..6eed48e 100644 --- a/packages/cli_tools/test/prompts/select_test.dart +++ b/packages/cli_tools/test/prompts/select_test.dart @@ -2,9 +2,10 @@ // ignore_for_file: unused_local_variable import 'package:cli_tools/cli_tools.dart'; -import 'package:cli_tools/src/prompts/key_codes.dart'; -import 'package:cli_tools/src/prompts/select.dart'; +import 'package:cli_tools/src/prompts/key_codes.dart' show KeyCodes; +import 'package:cli_tools/src/prompts/select.dart' show underline; import 'package:test/test.dart'; + import '../test_utils/io_helper.dart'; import '../test_utils/prompts/key_code_sequence.dart'; import '../test_utils/prompts/option_matcher.dart'; diff --git a/packages/config/.gitignore b/packages/config/.gitignore new file mode 100644 index 0000000..3a85790 --- /dev/null +++ b/packages/config/.gitignore @@ -0,0 +1,3 @@ +# https://dart.dev/guides/libraries/private-files +# Created by `dart pub` +.dart_tool/ diff --git a/packages/config/CHANGELOG.md b/packages/config/CHANGELOG.md new file mode 100644 index 0000000..1864e80 --- /dev/null +++ b/packages/config/CHANGELOG.md @@ -0,0 +1,4 @@ +# Changelog + +## 0.7.0-beta.2 +- feat: Moved out the `config` library from the `cli_tools` package and into its own package, to be published as `config` on pub.dev. diff --git a/packages/config/LICENSE b/packages/config/LICENSE new file mode 100644 index 0000000..654d51d --- /dev/null +++ b/packages/config/LICENSE @@ -0,0 +1,28 @@ +BSD 3-Clause License + +Copyright (c) 2025, Serverpod + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/config/README.md b/packages/config/README.md new file mode 100644 index 0000000..23aadcf --- /dev/null +++ b/packages/config/README.md @@ -0,0 +1,416 @@ +[![Serverpod banner](https://github.com/serverpod/serverpod/raw/main/misc/images/github-header.webp)](https://github.com/serverpod/serverpod) + +# Config + +The config package is a significant extension to the args package and enables +typed options, environment variables and configuration files as input, and +better error reporting. + +## Contributing to the Project + +We are happy to accept contributions. To contribute, please do the following: + +1. Fork the repository +2. Create a feature branch +3. Commit your changes +4. Push to the branch +5. Create a pull request +6. Discuss and modify the pull request as necessary +7. The pull request will be accepted and merged by the repository owner + +Tests are required to accept any pull requests. + +See also [CONTRIBUTING](/CONTRIBUTING.md). + +## Overview + +The config library is a significant extension to the Dart args package. + +The main features are: + +- Typed arg options: `int`, `DateTime`, `Duration`, user-defined `Enums`. + - Automatic parsing and user-friendly error messages. + - Type-specific constraints, such as min/max for all `Comparable` option types. + - Multivalued options are typed, e.g. `List`. + - Custom types can easily be added and combined with the existing ones. + - See [Supported option types](#supported-option-types) for the complete list. + +- Equal support for positional arguments, with proper validation. + - Arguments can be both positional and named, making the --name optional. + +- Equal support for environment variables. + - Options can be specified both via arguments and environment variables. + - Environment variables have the same typed values support as args. + +- Options can be fetched from [configuration files](#using-configuration-files) as well. + - YAML/JSON configuration file support. + +- Options can have custom value-providing callbacks. + +- Named option groups are supported. + - A group can specify mutually exclusive options. + - A group can be mandatory in that at least one of its options is set. + +- Tracability - the information on an option's value source is retained. + +- The error handling is consistent, in contrast to the args package. + - Fail-fast, all validation is performed up-front. + - All errors are collected, avoiding the poor UX of fix-one-and-then-get-the-next-error. + - Well-defined exception behavior. + +These tools were developed for the Serverpod CLI but can be used in any Dart project. + +## Drop-in replacement + +The `ConfigParser` class is designed as a drop-in replacement for `ArgParser` +from the `args` package. Its purpose is to make it easy to transition +to the config library - just replace the name `ArgParser` with `ConfigParser`. + +It maintains almost complete compatibility with the original package while +enabling direct use of the new features. + +It achieves complete compatibility with the original package with the exception +of addCommand(), which you can replace with +[`BetterCommandRunner`](lib/src/better_command_runner/better_command_runner.dart). + +- **Compatibility**: The `ConfigParser` implements the same interface as + `ArgParser`, and returns a `ConfigResults` object that implements `ArgResults`. +- **Usage**: You can directly replace `ArgParser` in your existing code: + ```dart + final parser = ConfigParser(); // instead of ArgParser() + parser.addFlag('verbose', abbr: 'v'); + parser.addOption('port', defaultsTo: '8080'); + parser.addOption('host', envName: 'HOST'); // using env feature + final results = parser.parse(['--verbose', '--port', '3000']); + ``` + +- **Key Differences**: + - The `addCommand()` method is not supported + (see [`BetterCommandRunner`](lib/src/better_command_runner/better_command_runner.dart) instead) + - All validation is performed up-front with consistent error messages + - The parser supports additional configuration sources (environment variables, config files) + +- **Migration Path**: You can start using `ConfigParser` as a direct replacement +for `ArgParser` and gradually adopt its additional features as needed. + +## Usage + +_For transitioning existing code from ArgParser, see the drop-in replacement +section above._ + +This library emphasizes a declarative style of defining options. +Here is a real-life example, from a _show logs_ command, +that shows how to create a set of options for a particular command as an _enum_. + +```dart +import 'package:config/config.dart'; + +enum LogOption implements OptionDefinition { + limit(IntOption( + argName: 'limit', + helpText: 'The maximum number of log records to fetch.', + defaultsTo: 50, + min: 0, + )), + utc(FlagOption( + argName: 'utc', + argAbbrev: 'u', + helpText: 'Display timestamps in UTC timezone instead of local.', + defaultsTo: false, + envName: 'DISPLAY_UTC', + )), + recent(DurationOption( + argName: 'recent', + argAbbrev: 'r', + argPos: 0, + helpText: + 'Fetch records from the recent period. ' + 'Can also be specified as the first argument.', + min: Duration.zero, + )), + before(DateTimeOption( + argName: 'before', + helpText: 'Fetch records from before this timestamp.', + )); + + const LogOption(this.option); + + @override + final ConfigOptionBase option; +} +``` + +The enum form enables constant initialization, typed `Configuration`, +and easy reference. + +```dart + Future runWithConfig( + final Configuration commandConfig, + ) async { + final limit = commandConfig.value(LogOption.limit); + final inUtc = commandConfig.value(LogOption.utc); + final recentOpt = commandConfig.optionalValue(LogOption.recent); + final beforeOpt = commandConfig.optionalValue(LogOption.before); + ... + } +``` + +It is also possible to create them as a List: + +```dart +abstract final class _ProjectOptions { + static const name = StringOption( + argName: 'name', + mandatory: true + ); + static const enable = FlagOption( + argName: 'enable', + defaultsTo: false, + ); + + static createOptions = [ + name, + enable, + ]; +} +... + + Future runWithConfig( + final Configuration commandConfig, + ) async { + final name = commandConfig.value(_ProjectOptions.name); + final enable = commandConfig.value(_ProjectOptions.enable); + ... + } +``` + +> Note that options that are mandatory or have a default value have a guaranteed value. +They return a non-nullable type, while "optional" options return a nullable type. + +### Main classes + +An instance of the [OptionDefinition](lib/src/config/configuration.dart) class +defines an option. +This is an abstract class and implemented by option Enum types +as well as the base option class `ConfigOptionBase`. +The latter is typically +not used directly, instead the typed subclasses are used such as `StringOption` +or `IntOption`. + +An instance of the [Configuration](lib/src/config/configuration.dart) class +holds a configuration, i.e. the values for a set of option definitions. + +### Resolution order + +The configuration library resolves each option value in a specific order, with earlier sources taking precedence over later ones. + +1. **Command-line arguments** + - Named arguments (e.g., `--verbose` or `-v`) have top precedence + - Positional arguments are resolved after named + - Specified using `argName`, `argAbbrev`, and `argPos` + +2. **Environment variables** + - Environment variables have second precedence after CLI arguments + - Variable name is specified using `envName` + +3. **Configuration files** + - Values from configuration files (e.g. YAML/JSON) + - Lookup key is specified using `configKey` + +4. **Custom value providers** + - Values from custom callbacks + - Callbacks are allowed to depend on other option values + (option definition order is significant in this case) + - Callback is specified using `fromCustom` + +5. **Default values** + - A default value guarantees that an option has a value + - Const values are specified using `defaultsTo` + - Non-const values are specifed with a callback using `fromDefault` + +This order ensures that: +- Command-line arguments always take precedence, allowing users to override any other settings +- Environment variables can be used for values used across multiple command invocations, + or to override other configuration sources +- Configuration files provide persistent settings +- Custom providers enable complex logic and integration with external systems +- Default values serve as a fallback when no other value is specified + +### Resolution sources + +Only the value sources provided to the `Configuration.resolve` constructor are +actually included. This means that any precedence tiers can be skipped, +regardless of what the option definitions say. + +This enables flexible inclusion of sources depending on context +and helps constructing specific test cases. + +### Supported option types + +The library provides a rich set of typed options out of the box. All option types support the common arguments like `argName`, `helpText`, `mandatory`, etc. Below are the additional type-specific arguments: + +| Value Type | Option Class | Additional Settings | Description | +|------|-------|---------------------|-------------| +| String | `StringOption` | None | String values | +| Boolean | `FlagOption` | `negatable` | Whether the flag can be negated | +| Integer | `IntOption` | `min`
`max` | Minimum allowed value
Maximum allowed value | +| DateTime | `DateTimeOption` | `min`
`max` | Minimum allowed date/time
Maximum allowed date/time | +| Duration | `DurationOption` | `min`
`max` | Minimum allowed duration
Maximum allowed duration | +| Any Enum | `EnumOption` | None | Typed enum values | +| File | `FileOption` | `mode` | Whether the file must exist, must not exist, or may exist | +| Directory | `DirOption` | `mode` | Whether the directory must exist, must not exist, or may exist | +| String List | `MultiStringOption` | `splitCommas` | Whether to split input on commas | +| Any List | `MultiOption` | `multiParser` | Parser for the element type | + +It is easy to add custom option types, and to reuse the parsing code from existing option types. Just copy code from existing options and modify as needed. + +#### Common option features + +All option types support: +- Command-line arguments (full name, abbreviated name, and positional) +- Environment variables +- Configuration file values +- Custom value-providing callback +- Default values +- Allowed values list validation +- Aliases +- Custom validation +- Help text and value descriptions +- Mandatory +- Hidden +- Option groups + +### Resolving a Configuration + +This is an overview of how a Configuration is resolved. + +```mermaid +sequenceDiagram + participant User Code + participant OptionDefinitions + participant Configuration + participant ArgParser + participant EnvVariables + participant ConfigSourceProvider + participant ConfigurationSource + + User Code->>OptionDefinitions: Define options + User Code->>Configuration: Resolve with context (args, env, config, etc) + Configuration->>ArgParser: Parse command-line args + Configuration->>EnvVariables: Lookup environment variables + Configuration->>ConfigSourceProvider: (if needed) getConfigSource(cfg) + ConfigSourceProvider->>ConfigurationSource: Provide config data + ConfigurationSource-->>Configuration: Value for key + Configuration-->>User Code: Typed Configuration (with errors if any) +``` + +## Integration with commands + +In Dart, commands are often implemented using `Command` and `CommandRunner` +from the `args` package. + +To use the config library with these, they need to be subclassed +to modify the use of `ArgParser` and introduce `Configuration`. This has +already been done for you, with the `BetterCommand` and `BetterCommandRunner` +classes in the `better_command_runner` library in this package. + +See the full example [example/config_simple_example.dart](example/config_simple_example.dart). + +## Using configuration files + +To use configuration files as a source of option values, +a `ConfigurationBroker` needs to be provided when resolving the +`Configuration`. + +```dart + Configuration.resolve( + options: options, + argResults: argResults, + env: envVariables, + configBroker: FileConfigBroker(), + ); +``` + +A file-reading ConfigurationBroker can be implemented like this: + +```dart +class FileConfigBroker implements ConfigurationBroker { + ConfigurationSource? _configSource; + + FileConfigBroker(); + + @override + String? valueOrNull(final String key, final Configuration cfg) { + // By lazy-loading the config, the file path can depend on another option + _configSource ??= ConfigurationParser.fromFile( + cfg.value(TimeSeriesOption.configFile).path, + ); + final value = _configSource?.valueOrNull(key); + return value is String ? value : null; + } +} +``` + +To reference a value from the configuration broker in an option definition, +specify the `configKey`. In this example, the configuration file is a JSON or +YAML file and the JSON pointer syntax is used. + +```dart + interval(DurationOption( + argName: 'interval', + argAbbrev: 'i', + configKey: '/interval', // JSON pointer + )); +``` + +See the full example in [example/config_file_example.dart](example/config_file_example.dart). + +### Multiple configuration sources + +By using the `MultiDomainConfigBroker`, configuration sources +from multiple providers can be used, called configuration *domains*. + +They are distinguished by the format used in the configKey, +which needs to specify a so-called *qualified key* - +qualifying the key with the domain it is found in. + +Domains are matched using `Pattern`, e.g. string prefixes or regular expressions. +For example, a simple prefix and colon syntax can be used: + +```dart + dir(DirOption( + argName: 'dir', + configKey: 'local:/dir', + helpText: 'the local directory', + )), + host(StringOption( + argName: 'host', + configKey: 'remote:/host', + helpText: 'the remote host name', + )); +``` + +The first domain that matches the qualified key is used to retrieve the value. +This means that the order of the domains is significant if the matching patterns +overlap. + +#### RegExp domains + +Advanced pattern matching is supported via `RegExp`, enabling complex qualifiers +including paths and URLs, such that the key pattern qualifies the domain. + +When using regular expressions to identify the domain, the value key is derived +from the qualified key depending on the capturing groups in the regex. + +- If the regex has no capturing groups: + - If the regex matches a shorter string than the qualified key, the value key is the remainder after the match.\ + This makes prefix matching simple. + - If the regex matches the entire qualified key, the value key is the entire qualified key.\ + This can be used for specific syntaxes like URLs. + +- If the regex has one or more capturing groups:\ + The value key is the string captured by the first group. + +For more information, see the +[`MultiDomainConfigBroker`](lib/src/config/multi_config_source.dart) +source documentation. diff --git a/packages/config/analysis_options.yaml b/packages/config/analysis_options.yaml new file mode 100644 index 0000000..65336e6 --- /dev/null +++ b/packages/config/analysis_options.yaml @@ -0,0 +1,5 @@ +include: package:serverpod_lints/cli.yaml + +analyzer: + errors: + unnecessary_final: false diff --git a/packages/cli_tools/example/config.yaml b/packages/config/example/config.yaml similarity index 100% rename from packages/cli_tools/example/config.yaml rename to packages/config/example/config.yaml diff --git a/packages/cli_tools/example/config_file_example.dart b/packages/config/example/config_file_example.dart similarity index 53% rename from packages/cli_tools/example/config_file_example.dart rename to packages/config/example/config_file_example.dart index a87803c..887c8d0 100644 --- a/packages/cli_tools/example/config_file_example.dart +++ b/packages/config/example/config_file_example.dart @@ -1,27 +1,49 @@ -import 'dart:io' show File; - -import 'package:args/args.dart' show ArgResults; -import 'package:cli_tools/cli_tools.dart'; - +import 'dart:io' show File, Platform; + +import 'package:config/config.dart'; + +/// Example of accessing a configuration file as fallback if the option +/// is not specified on the command line. +/// It also shows how to make the configuration file itself +/// configurable via the command line or environment variable. +/// +/// Run this example program like so: +/// ```sh +/// dart run example/config_file_example.dart --config=example/config.yaml +/// ``` +/// or +/// ```sh +/// CONFIG_FILE=example/config.yaml dart run example/config_file_example.dart +/// ``` +/// Command line usage: +/// ```sh +/// --config The path to the config file +/// (defaults to "File: 'example/config.yaml'") +/// -i, --interval= The interval between the series elements +/// ``` Future main(List args) async { - var commandRunner = BetterCommandRunner( - 'example', - 'Example CLI command', - ); - commandRunner.addCommand(TimeSeriesCommand()); - + final Configuration config; try { - await commandRunner.run(args); + config = Configuration.resolve( + options: TimeSeriesOption.values, + args: args, + env: Platform.environment, + configBroker: FileConfigBroker(), + ); } on UsageException catch (e) { print(e); return 1; } + + var interval = config.optionalValue(TimeSeriesOption.interval); + print('interval: $interval'); return 0; } enum TimeSeriesOption implements OptionDefinition { configFile(FileOption( argName: 'config', + envName: 'CONFIG_FILE', helpText: 'The path to the config file', fromDefault: _defaultConfigFilePath, mode: PathExistMode.mustExist, @@ -43,32 +65,6 @@ enum TimeSeriesOption implements OptionDefinition { File _defaultConfigFilePath() => File('example/config.yaml'); -class TimeSeriesCommand extends BetterCommand { - TimeSeriesCommand({super.env}) : super(options: TimeSeriesOption.values); - - @override - String get name => 'series'; - - @override - String get description => 'Generate a series of time stamps'; - - @override - void runWithConfig(Configuration commandConfig) { - var interval = commandConfig.optionalValue(TimeSeriesOption.interval); - print('interval: $interval'); - } - - @override - Configuration resolveConfiguration(ArgResults? argResults) { - return Configuration.resolveNoExcept( - options: options, - argResults: argResults, - env: envVariables, - configBroker: FileConfigBroker(), - ); - } -} - class FileConfigBroker implements ConfigurationBroker { ConfigurationSource? _configSource; diff --git a/packages/config/example/main.dart b/packages/config/example/main.dart new file mode 100644 index 0000000..eea0689 --- /dev/null +++ b/packages/config/example/main.dart @@ -0,0 +1,100 @@ +import 'dart:io' show Platform; + +import 'package:config/config.dart'; + +/// Example of using the [Configuration] class to resolve command line arguments and environment variables. +/// +/// Run this example program like so: +/// ```sh +/// dart run example/main.dart --until=2025-08-08 --length=10 +/// ``` +/// or +/// ```sh +/// SERIES_UNTIL=2025-08-08 SERIES_LENGTH=10 dart run example/main.dart +/// ``` +/// +/// Command line usage: +/// ```sh +/// --until= The end timestamp of the series +/// (defaults to "2025-08-08 12:06:49.611906") +/// -l, --length= The number of elements in the series +/// -i, --interval= The interval between the series elements +/// ``` +int main(List args) { + final Configuration config; + try { + config = Configuration.resolve( + options: TimeSeriesOption.values, + args: args, + env: Platform.environment, + ); + } on UsageException catch (e) { + print(e); + return 1; + } + + generateTimeSeries(config); + return 0; +} + +/// Options are defineable as enums as well as regular lists. +/// +/// The enum approach is more distinct and type safe. +/// The list approach is more dynamic and permits non-const initialization. +enum TimeSeriesOption implements OptionDefinition { + until(DateTimeOption( + argName: 'until', + envName: 'SERIES_UNTIL', // can also be specified as environment variable + fromDefault: _defaultUntil, + helpText: 'The end timestamp of the series', + )), + length(IntOption( + argName: 'length', + argAbbrev: 'l', + argPos: 0, // can also be specified as positional argument + helpText: 'The number of elements in the series', + min: 1, + max: 100, + group: _granularityGroup, + )), + interval(DurationOption( + argName: 'interval', + argAbbrev: 'i', + helpText: 'The interval between the series elements', + min: Duration(seconds: 1), + max: Duration(days: 1), + group: _granularityGroup, + )); + + const TimeSeriesOption(this.option); + + @override + final ConfigOptionBase option; +} + +/// Exactly one of the options in this group must be set. +const _granularityGroup = MutuallyExclusive( + 'Granularity', + mode: MutuallyExclusiveMode.mandatory, +); + +/// A function can be used as a const initializer. +DateTime _defaultUntil() => DateTime.now().add(const Duration(days: 1)); + +void generateTimeSeries(Configuration config) { + var start = DateTime.now(); + var until = config.value(TimeSeriesOption.until); + + // exactly one of these options is set + var length = config.optionalValue(TimeSeriesOption.length); + var interval = config.optionalValue(TimeSeriesOption.interval); + interval ??= (until.difference(start) ~/ length!); + if (interval < const Duration(milliseconds: 1)) { + interval = const Duration(milliseconds: 1); + } + + while (start.isBefore(until)) { + print(start); + start = start.add(interval); + } +} diff --git a/packages/cli_tools/lib/config.dart b/packages/config/lib/config.dart similarity index 100% rename from packages/cli_tools/lib/config.dart rename to packages/config/lib/config.dart diff --git a/packages/cli_tools/lib/src/config/config.dart b/packages/config/lib/src/config/config.dart similarity index 87% rename from packages/cli_tools/lib/src/config/config.dart rename to packages/config/lib/src/config/config.dart index 086c40a..10542e8 100644 --- a/packages/cli_tools/lib/src/config/config.dart +++ b/packages/config/lib/src/config/config.dart @@ -10,4 +10,5 @@ export 'multi_config_source.dart'; export 'option_groups.dart'; export 'option_types.dart'; export 'options.dart'; +export 'output_formatting.dart' show formatConfigError; export 'source_type.dart'; diff --git a/packages/cli_tools/lib/src/config/config_parser.dart b/packages/config/lib/src/config/config_parser.dart similarity index 100% rename from packages/cli_tools/lib/src/config/config_parser.dart rename to packages/config/lib/src/config/config_parser.dart diff --git a/packages/cli_tools/lib/src/config/config_source.dart b/packages/config/lib/src/config/config_source.dart similarity index 100% rename from packages/cli_tools/lib/src/config/config_source.dart rename to packages/config/lib/src/config/config_source.dart diff --git a/packages/cli_tools/lib/src/config/config_source_provider.dart b/packages/config/lib/src/config/config_source_provider.dart similarity index 100% rename from packages/cli_tools/lib/src/config/config_source_provider.dart rename to packages/config/lib/src/config/config_source_provider.dart diff --git a/packages/cli_tools/lib/src/config/configuration.dart b/packages/config/lib/src/config/configuration.dart similarity index 99% rename from packages/cli_tools/lib/src/config/configuration.dart rename to packages/config/lib/src/config/configuration.dart index b1089b0..b8070c3 100644 --- a/packages/cli_tools/lib/src/config/configuration.dart +++ b/packages/config/lib/src/config/configuration.dart @@ -15,7 +15,7 @@ import 'source_type.dart'; /// and implement the configuration like so: /// ```dart /// import 'dart:io' show Platform; -/// import 'package:cli_tools/config.dart'; +/// import 'package:config/config.dart'; /// /// enum MyAppOption implements OptionDefinition { /// username(StringOption( diff --git a/packages/cli_tools/lib/src/config/configuration_broker.dart b/packages/config/lib/src/config/configuration_broker.dart similarity index 100% rename from packages/cli_tools/lib/src/config/configuration_broker.dart rename to packages/config/lib/src/config/configuration_broker.dart diff --git a/packages/cli_tools/lib/src/config/configuration_parser.dart b/packages/config/lib/src/config/configuration_parser.dart similarity index 100% rename from packages/cli_tools/lib/src/config/configuration_parser.dart rename to packages/config/lib/src/config/configuration_parser.dart diff --git a/packages/cli_tools/lib/src/config/exceptions.dart b/packages/config/lib/src/config/exceptions.dart similarity index 88% rename from packages/cli_tools/lib/src/config/exceptions.dart rename to packages/config/lib/src/config/exceptions.dart index f1f6950..9a12055 100644 --- a/packages/cli_tools/lib/src/config/exceptions.dart +++ b/packages/config/lib/src/config/exceptions.dart @@ -1,4 +1,4 @@ -import 'package:cli_tools/src/config/options.dart'; +import 'options.dart'; /// Indicates that the option definition is invalid. class OptionDefinitionError extends Error { diff --git a/packages/cli_tools/lib/src/config/file_system_options.dart b/packages/config/lib/src/config/file_system_options.dart similarity index 100% rename from packages/cli_tools/lib/src/config/file_system_options.dart rename to packages/config/lib/src/config/file_system_options.dart diff --git a/packages/cli_tools/lib/src/config/json_yaml_document.dart b/packages/config/lib/src/config/json_yaml_document.dart similarity index 100% rename from packages/cli_tools/lib/src/config/json_yaml_document.dart rename to packages/config/lib/src/config/json_yaml_document.dart diff --git a/packages/cli_tools/lib/src/config/multi_config_source.dart b/packages/config/lib/src/config/multi_config_source.dart similarity index 100% rename from packages/cli_tools/lib/src/config/multi_config_source.dart rename to packages/config/lib/src/config/multi_config_source.dart diff --git a/packages/cli_tools/lib/src/config/option_groups.dart b/packages/config/lib/src/config/option_groups.dart similarity index 100% rename from packages/cli_tools/lib/src/config/option_groups.dart rename to packages/config/lib/src/config/option_groups.dart diff --git a/packages/cli_tools/lib/src/config/option_resolution.dart b/packages/config/lib/src/config/option_resolution.dart similarity index 100% rename from packages/cli_tools/lib/src/config/option_resolution.dart rename to packages/config/lib/src/config/option_resolution.dart diff --git a/packages/cli_tools/lib/src/config/option_types.dart b/packages/config/lib/src/config/option_types.dart similarity index 100% rename from packages/cli_tools/lib/src/config/option_types.dart rename to packages/config/lib/src/config/option_types.dart diff --git a/packages/cli_tools/lib/src/config/options.dart b/packages/config/lib/src/config/options.dart similarity index 99% rename from packages/cli_tools/lib/src/config/options.dart rename to packages/config/lib/src/config/options.dart index ca6e248..ce28994 100644 --- a/packages/cli_tools/lib/src/config/options.dart +++ b/packages/config/lib/src/config/options.dart @@ -1,6 +1,6 @@ import 'package:args/args.dart'; import 'package:args/command_runner.dart'; -import 'package:cli_tools/src/config/exceptions.dart'; +import 'exceptions.dart'; import 'package:collection/collection.dart'; import 'package:meta/meta.dart'; diff --git a/packages/cli_tools/lib/src/config/output_formatting.dart b/packages/config/lib/src/config/output_formatting.dart similarity index 100% rename from packages/cli_tools/lib/src/config/output_formatting.dart rename to packages/config/lib/src/config/output_formatting.dart diff --git a/packages/cli_tools/lib/src/config/source_type.dart b/packages/config/lib/src/config/source_type.dart similarity index 100% rename from packages/cli_tools/lib/src/config/source_type.dart rename to packages/config/lib/src/config/source_type.dart diff --git a/packages/config/pubspec.lock b/packages/config/pubspec.lock new file mode 100644 index 0000000..93d8b4c --- /dev/null +++ b/packages/config/pubspec.lock @@ -0,0 +1,421 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7" + url: "https://pub.dev" + source: hosted + version: "67.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + sha256: "37577842a27e4338429a1cbc32679d508836510b056f1eedf0c8d20e39c1383d" + url: "https://pub.dev" + source: hosted + version: "6.4.1" + args: + dependency: "direct main" + description: + name: args + sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 + url: "https://pub.dev" + source: hosted + version: "2.7.0" + async: + dependency: transitive + description: + name: async + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" + source: hosted + version: "2.11.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + collection: + dependency: "direct main" + description: + name: collection + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + url: "https://pub.dev" + source: hosted + version: "1.18.0" + convert: + dependency: transitive + description: + name: convert + sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" + url: "https://pub.dev" + source: hosted + version: "3.1.1" + coverage: + dependency: transitive + description: + name: coverage + sha256: "3945034e86ea203af7a056d98e98e42a5518fff200d6e8e6647e1886b07e936e" + url: "https://pub.dev" + source: hosted + version: "1.8.0" + crypto: + dependency: transitive + description: + name: crypto + sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab + url: "https://pub.dev" + source: hosted + version: "3.0.3" + file: + dependency: transitive + description: + name: file + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 + url: "https://pub.dev" + source: hosted + version: "7.0.1" + flutter_lints: + dependency: transitive + description: + name: flutter_lints + sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c" + url: "https://pub.dev" + source: hosted + version: "4.0.0" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 + url: "https://pub.dev" + source: hosted + version: "4.0.0" + glob: + dependency: transitive + description: + name: glob + sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" + url: "https://pub.dev" + source: hosted + version: "3.2.1" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + url: "https://pub.dev" + source: hosted + version: "4.0.2" + io: + dependency: transitive + description: + name: io + sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" + url: "https://pub.dev" + source: hosted + version: "1.0.4" + js: + dependency: transitive + description: + name: js + sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf + url: "https://pub.dev" + source: hosted + version: "0.7.1" + lints: + dependency: transitive + description: + name: lints + sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235" + url: "https://pub.dev" + source: hosted + version: "4.0.0" + logging: + dependency: transitive + description: + name: logging + sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + url: "https://pub.dev" + source: hosted + version: "0.12.16+1" + meta: + dependency: "direct main" + description: + name: meta + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + url: "https://pub.dev" + source: hosted + version: "1.16.0" + mime: + dependency: transitive + description: + name: mime + sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + node_preamble: + dependency: transitive + description: + name: node_preamble + sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db" + url: "https://pub.dev" + source: hosted + version: "2.0.2" + package_config: + dependency: transitive + description: + name: package_config + sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + path: + dependency: "direct main" + description: + name: path + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + url: "https://pub.dev" + source: hosted + version: "1.9.0" + pool: + dependency: transitive + description: + name: pool + sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" + url: "https://pub.dev" + source: hosted + version: "1.5.1" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + rfc_6901: + dependency: "direct main" + description: + name: rfc_6901 + sha256: df1bbfa3d023009598f19636d6114c6ac1e0b7bb7bf6a260f0e6e6ce91416820 + url: "https://pub.dev" + source: hosted + version: "0.2.0" + serverpod_lints: + dependency: "direct dev" + description: + name: serverpod_lints + sha256: "216d982e83068f7b134eee3f277492ba037fe7d4aa047c4c5f420819a437b4a7" + url: "https://pub.dev" + source: hosted + version: "2.2.2" + shelf: + dependency: transitive + description: + name: shelf + sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 + url: "https://pub.dev" + source: hosted + version: "1.4.1" + shelf_packages_handler: + dependency: transitive + description: + name: shelf_packages_handler + sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + shelf_static: + dependency: transitive + description: + name: shelf_static + sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3 + url: "https://pub.dev" + source: hosted + version: "1.1.3" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + sha256: cc36c297b52866d203dbf9332263c94becc2fe0ceaa9681d07b6ef9807023b67 + url: "https://pub.dev" + source: hosted + version: "2.0.1" + source_map_stack_trace: + dependency: transitive + description: + name: source_map_stack_trace + sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b + url: "https://pub.dev" + source: hosted + version: "2.1.2" + source_maps: + dependency: transitive + description: + name: source_maps + sha256: "708b3f6b97248e5781f493b765c3337db11c5d2c81c3094f10904bfa8004c703" + url: "https://pub.dev" + source: hosted + version: "0.10.12" + source_span: + dependency: transitive + description: + name: source_span + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.dev" + source: hosted + version: "1.10.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + url: "https://pub.dev" + source: hosted + version: "1.11.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + url: "https://pub.dev" + source: hosted + version: "2.1.2" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "0bd04f5bb74fcd6ff0606a888a30e917af9bd52820b178eaa464beb11dca84b6" + url: "https://pub.dev" + source: hosted + version: "1.4.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + test: + dependency: "direct dev" + description: + name: test + sha256: "7ee44229615f8f642b68120165ae4c2a75fe77ae2065b1e55ae4711f6cf0899e" + url: "https://pub.dev" + source: hosted + version: "1.25.7" + test_api: + dependency: transitive + description: + name: test_api + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" + url: "https://pub.dev" + source: hosted + version: "0.7.2" + test_core: + dependency: transitive + description: + name: test_core + sha256: "55ea5a652e38a1dfb32943a7973f3681a60f872f8c3a05a14664ad54ef9c6696" + url: "https://pub.dev" + source: hosted + version: "0.6.4" + test_descriptor: + dependency: "direct dev" + description: + name: test_descriptor + sha256: "9ce468c97ae396e8440d26bb43763f84e2a2a5331813ee5a397cb4da481aaf9a" + url: "https://pub.dev" + source: hosted + version: "2.0.2" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + url: "https://pub.dev" + source: hosted + version: "1.3.2" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14" + url: "https://pub.dev" + source: hosted + version: "14.3.1" + watcher: + dependency: transitive + description: + name: watcher + sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + web: + dependency: transitive + description: + name: web + sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" + url: "https://pub.dev" + source: hosted + version: "0.5.1" + web_socket: + dependency: transitive + description: + name: web_socket + sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83" + url: "https://pub.dev" + source: hosted + version: "0.1.6" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: "9f187088ed104edd8662ca07af4b124465893caf063ba29758f97af57e61da8f" + url: "https://pub.dev" + source: hosted + version: "3.0.1" + webkit_inspection_protocol: + dependency: transitive + description: + name: webkit_inspection_protocol + sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572" + url: "https://pub.dev" + source: hosted + version: "1.2.1" + yaml: + dependency: "direct main" + description: + name: yaml + sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" + url: "https://pub.dev" + source: hosted + version: "3.1.2" +sdks: + dart: ">=3.3.0 <4.0.0" diff --git a/packages/config/pubspec.yaml b/packages/config/pubspec.yaml new file mode 100644 index 0000000..66cfcd6 --- /dev/null +++ b/packages/config/pubspec.yaml @@ -0,0 +1,29 @@ +name: config +version: 0.7.0-beta.2 +description: A package for parsing command-line arguments and configuration files. +repository: https://github.com/serverpod/cli_tools +issue_tracker: https://github.com/serverpod/cli_tools/issues +documentation: https://github.com/serverpod/cli_tools/packages/config/README.md + +topics: + - cli + - args + - command + - configuration + - yaml + +environment: + sdk: ^3.3.0 + +dependencies: + args: ^2.7.0 + collection: ^1.18.0 + meta: ^1.11.0 + path: ^1.8.2 + rfc_6901: ^0.2.0 + yaml: ^3.1.2 + +dev_dependencies: + serverpod_lints: '>=1.2.6' + test_descriptor: ^2.0.2 + test: ^1.24.0 diff --git a/packages/cli_tools/test/config/args_compatibility/allow_anything_test.dart b/packages/config/test/config/args_compatibility/allow_anything_test.dart similarity index 100% rename from packages/cli_tools/test/config/args_compatibility/allow_anything_test.dart rename to packages/config/test/config/args_compatibility/allow_anything_test.dart diff --git a/packages/cli_tools/test/config/args_compatibility/args_test.dart b/packages/config/test/config/args_compatibility/args_test.dart similarity index 99% rename from packages/cli_tools/test/config/args_compatibility/args_test.dart rename to packages/config/test/config/args_compatibility/args_test.dart index 926c4fe..ed24a70 100644 --- a/packages/cli_tools/test/config/args_compatibility/args_test.dart +++ b/packages/config/test/config/args_compatibility/args_test.dart @@ -7,7 +7,7 @@ import 'package:args/args.dart'; import 'package:test/test.dart'; -import 'package:cli_tools/config.dart'; +import 'package:config/config.dart'; import 'test_utils.dart'; diff --git a/packages/cli_tools/test/config/args_compatibility/command_parse_test.dart b/packages/config/test/config/args_compatibility/command_parse_test.dart similarity index 100% rename from packages/cli_tools/test/config/args_compatibility/command_parse_test.dart rename to packages/config/test/config/args_compatibility/command_parse_test.dart diff --git a/packages/cli_tools/test/config/args_compatibility/command_runner_test.dart b/packages/config/test/config/args_compatibility/command_runner_test.dart similarity index 100% rename from packages/cli_tools/test/config/args_compatibility/command_runner_test.dart rename to packages/config/test/config/args_compatibility/command_runner_test.dart diff --git a/packages/cli_tools/test/config/args_compatibility/command_test.dart b/packages/config/test/config/args_compatibility/command_test.dart similarity index 100% rename from packages/cli_tools/test/config/args_compatibility/command_test.dart rename to packages/config/test/config/args_compatibility/command_test.dart diff --git a/packages/cli_tools/test/config/args_compatibility/parse_performance_test.dart b/packages/config/test/config/args_compatibility/parse_performance_test.dart similarity index 98% rename from packages/cli_tools/test/config/args_compatibility/parse_performance_test.dart rename to packages/config/test/config/args_compatibility/parse_performance_test.dart index 0a25f92..9322671 100644 --- a/packages/cli_tools/test/config/args_compatibility/parse_performance_test.dart +++ b/packages/config/test/config/args_compatibility/parse_performance_test.dart @@ -5,7 +5,7 @@ import 'package:args/args.dart'; import 'package:test/test.dart'; -import 'package:cli_tools/config.dart'; +import 'package:config/config.dart'; void main() { group('ConfigParser.parse() is fast', () { diff --git a/packages/cli_tools/test/config/args_compatibility/parse_test.dart b/packages/config/test/config/args_compatibility/parse_test.dart similarity index 99% rename from packages/cli_tools/test/config/args_compatibility/parse_test.dart rename to packages/config/test/config/args_compatibility/parse_test.dart index c1948f5..63396cd 100644 --- a/packages/cli_tools/test/config/args_compatibility/parse_test.dart +++ b/packages/config/test/config/args_compatibility/parse_test.dart @@ -6,7 +6,7 @@ import 'package:test/test.dart'; -import 'package:cli_tools/config.dart'; +import 'package:config/config.dart'; import 'test_utils.dart'; diff --git a/packages/cli_tools/test/config/args_compatibility/test_utils.dart b/packages/config/test/config/args_compatibility/test_utils.dart similarity index 100% rename from packages/cli_tools/test/config/args_compatibility/test_utils.dart rename to packages/config/test/config/args_compatibility/test_utils.dart diff --git a/packages/cli_tools/test/config/args_compatibility/trailing_options_test.dart b/packages/config/test/config/args_compatibility/trailing_options_test.dart similarity index 98% rename from packages/cli_tools/test/config/args_compatibility/trailing_options_test.dart rename to packages/config/test/config/args_compatibility/trailing_options_test.dart index 391585c..f8f26e7 100644 --- a/packages/cli_tools/test/config/args_compatibility/trailing_options_test.dart +++ b/packages/config/test/config/args_compatibility/trailing_options_test.dart @@ -4,7 +4,7 @@ import 'package:test/test.dart'; -import 'package:cli_tools/config.dart'; +import 'package:config/config.dart'; import 'test_utils.dart'; diff --git a/packages/cli_tools/test/config/args_compatibility/usage_test.dart b/packages/config/test/config/args_compatibility/usage_test.dart similarity index 99% rename from packages/cli_tools/test/config/args_compatibility/usage_test.dart rename to packages/config/test/config/args_compatibility/usage_test.dart index fed8e42..5c1fbf9 100644 --- a/packages/cli_tools/test/config/args_compatibility/usage_test.dart +++ b/packages/config/test/config/args_compatibility/usage_test.dart @@ -4,7 +4,7 @@ import 'package:test/test.dart'; -import 'package:cli_tools/config.dart'; +import 'package:config/config.dart'; void main() { group('ConfigParser.usage', () { diff --git a/packages/cli_tools/test/config/args_compatibility/utils_test.dart b/packages/config/test/config/args_compatibility/utils_test.dart similarity index 100% rename from packages/cli_tools/test/config/args_compatibility/utils_test.dart rename to packages/config/test/config/args_compatibility/utils_test.dart diff --git a/packages/cli_tools/test/config/config_source_test.dart b/packages/config/test/config/config_source_test.dart similarity index 99% rename from packages/cli_tools/test/config/config_source_test.dart rename to packages/config/test/config/config_source_test.dart index 3c80674..6acff18 100644 --- a/packages/cli_tools/test/config/config_source_test.dart +++ b/packages/config/test/config/config_source_test.dart @@ -1,6 +1,6 @@ import 'package:test/test.dart'; -import 'package:cli_tools/config.dart'; +import 'package:config/config.dart'; void main() { group( diff --git a/packages/cli_tools/test/config/configuration_test.dart b/packages/config/test/config/configuration_test.dart similarity index 99% rename from packages/cli_tools/test/config/configuration_test.dart rename to packages/config/test/config/configuration_test.dart index c1b1476..635e330 100644 --- a/packages/cli_tools/test/config/configuration_test.dart +++ b/packages/config/test/config/configuration_test.dart @@ -1,7 +1,7 @@ import 'package:args/args.dart'; import 'package:test/test.dart'; -import 'package:cli_tools/config.dart'; +import 'package:config/config.dart'; void main() async { group('Given invalid configuration abbrevation without full name', () { diff --git a/packages/cli_tools/test/config/configuration_type_test.dart b/packages/config/test/config/configuration_type_test.dart similarity index 99% rename from packages/cli_tools/test/config/configuration_type_test.dart rename to packages/config/test/config/configuration_type_test.dart index 77c1662..06c0be2 100644 --- a/packages/cli_tools/test/config/configuration_type_test.dart +++ b/packages/config/test/config/configuration_type_test.dart @@ -1,6 +1,6 @@ import 'package:test/test.dart'; -import 'package:cli_tools/config.dart'; +import 'package:config/config.dart'; enum AnimalEnum { cat, diff --git a/packages/cli_tools/test/config/date_parsing_test.dart b/packages/config/test/config/date_parsing_test.dart similarity index 98% rename from packages/cli_tools/test/config/date_parsing_test.dart rename to packages/config/test/config/date_parsing_test.dart index 2f5d63c..7e191b4 100644 --- a/packages/cli_tools/test/config/date_parsing_test.dart +++ b/packages/config/test/config/date_parsing_test.dart @@ -1,6 +1,6 @@ import 'package:test/test.dart'; -import 'package:cli_tools/config.dart'; +import 'package:config/config.dart'; void main() { group('Given a DateTimeParser', () { diff --git a/packages/cli_tools/test/config/duration_parsing_test.dart b/packages/config/test/config/duration_parsing_test.dart similarity index 99% rename from packages/cli_tools/test/config/duration_parsing_test.dart rename to packages/config/test/config/duration_parsing_test.dart index d26d029..07e1793 100644 --- a/packages/cli_tools/test/config/duration_parsing_test.dart +++ b/packages/config/test/config/duration_parsing_test.dart @@ -1,6 +1,6 @@ import 'package:test/test.dart'; -import 'package:cli_tools/config.dart'; +import 'package:config/config.dart'; void main() { group('Given a DurationParser with normal default unit "s"', () { diff --git a/packages/cli_tools/test/config/file_options_test.dart b/packages/config/test/config/file_options_test.dart similarity index 99% rename from packages/cli_tools/test/config/file_options_test.dart rename to packages/config/test/config/file_options_test.dart index 628199e..e2257c3 100644 --- a/packages/cli_tools/test/config/file_options_test.dart +++ b/packages/config/test/config/file_options_test.dart @@ -2,7 +2,7 @@ import 'package:path/path.dart' as p; import 'package:test/test.dart'; import 'package:test_descriptor/test_descriptor.dart' as d; -import 'package:cli_tools/config.dart'; +import 'package:config/config.dart'; void main() async { group('Given a DirOption', () {