diff --git a/.github/workflows/generate_template.yaml b/.github/workflows/generate_template.yaml deleted file mode 100644 index c32d6de..0000000 --- a/.github/workflows/generate_template.yaml +++ /dev/null @@ -1,44 +0,0 @@ -name: generate_template - -on: - push: - paths: - - .github/workflows/generate_template.yaml - - tool/generator/** - - src/my_cli/** - - patches/** - branches: - - main - -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: dart-lang/setup-dart@v1 - - - name: Install Dependencies - working-directory: tool/generator - run: dart pub get - - - name: Generate Template - run: dart ./tool/generator/main.dart - - - name: Config Git User - run: | - git config user.name VGV Bot - git config user.email vgvbot@users.noreply.github.com - - - name: Create Pull Request - uses: peter-evans/create-pull-request@v5.0.2 - with: - base: main - branch: chore/generate-template - commit-message: "chore: generate template" - title: "chore: generate template" - body: Please squash and merge me! - labels: bot - author: VGV Bot - assignees: vgvbot - reviewers: felangel, renancaraujo - committer: VGV Bot diff --git a/.github/workflows/my_cli.yaml b/.github/workflows/my_cli.yaml deleted file mode 100644 index 2c06a75..0000000 --- a/.github/workflows/my_cli.yaml +++ /dev/null @@ -1,34 +0,0 @@ -name: my_cli - -on: - pull_request: - paths: - - ".github/workflows/my_cli.yaml" - - "src/my_cli/lib/**" - - "src/my_cli/test/**" - - "src/my_cli/pubspec.yaml" - - "tool/generator/**" - push: - branches: - - main - paths: - - ".github/workflows/my_cli.yaml" - - "src/my_cli/lib/**" - - "src/my_cli/test/**" - - "src/my_cli/pubspec.yaml" - - "tool/generator/**" - -jobs: - build: - uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/dart_package.yml@v1 - with: - working_directory: src/my_cli - - spell-check: - uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/spell_check.yml@v1 - with: - includes: | - **/*.md - !brick/**/*.md - .*/**/*.md - modified_files_only: false diff --git a/.github/workflows/very_good_dart_cli.yaml b/.github/workflows/very_good_dart_cli.yaml new file mode 100644 index 0000000..2ce443d --- /dev/null +++ b/.github/workflows/very_good_dart_cli.yaml @@ -0,0 +1,68 @@ +name: very_good_core + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +on: + push: + paths: + - .github/workflows/very_good_dart_cli.yaml + - "brick/**" + branches: + - main + pull_request: + paths: + - .github/workflows/very_good_dart_cli.yaml + - "brick/**" + branches: + - main + +jobs: + brick: + runs-on: ubuntu-latest + + strategy: + matrix: + dart-version: + # The minimum Dart SDK version supported by the package, + # refer to https://docs.flutter.dev/development/tools/sdk/releases. + - "3.0.0" + - "stable" + + steps: + - name: ๐Ÿ“š Git Checkout + uses: actions/checkout@v4 + + - name: ๐ŸŽฏ Setup Dart + uses: dart-lang/setup-dart@v1 + with: + sdk: ${{ matrix.dart-version }} + + - name: ๐Ÿงฑ Mason Make + run: | + dart pub global activate mason_cli + mason get + mason make very_good_dart_cli -c brick/config.json -o output --on-conflict overwrite + + - name: ๐Ÿ“ฆ Install Dependencies + run: dart pub get --directory output/test_cli + + - name: โœจ Check Formatting + run: dart format --set-exit-if-changed output/test_cli + + - name: ๐Ÿ•ต๏ธ Analyze + run: dart analyze --fatal-infos --fatal-warnings output/test_cli + + - name: ๐Ÿงช Run Tests + run: | + dart pub global activate coverage 1.2.0 + cd output/test_cli + dart test -j 4 --coverage=coverage + dart pub global run coverage:format_coverage --lcov --check-ignore --in=coverage --out=coverage/lcov.info --packages=.dart_tool/package_config.json --report-on="lib" + cd ../../ + + - name: ๐Ÿ“Š Check Code Coverage + uses: VeryGoodOpenSource/very_good_coverage@v2 + with: + path: output/test_cli/coverage/lcov.info diff --git a/.gitignore b/.gitignore index 6ac9a46..c2fd2cc 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,12 @@ pubspec.lock # Android studio and IntelliJ -.idea \ No newline at end of file +.idea + +# Files and directories created by mason +.mason/ +mason-lock.json +output/ + +# Files and directories created by MacOS +.DS_Store \ No newline at end of file diff --git a/brick/config.json b/brick/config.json new file mode 100644 index 0000000..7f364ba --- /dev/null +++ b/brick/config.json @@ -0,0 +1,6 @@ +{ + "project_name": "test_cli", + "executable_name": "very_good_ventures", + "description": "very_good_core test configuration", + "publishable": false +} diff --git a/mason.yaml b/mason.yaml new file mode 100644 index 0000000..5bea14a --- /dev/null +++ b/mason.yaml @@ -0,0 +1,3 @@ +bricks: + very_good_dart_cli: + path: brick diff --git a/patches/changelog.patch b/patches/changelog.patch deleted file mode 100644 index 3c8710a..0000000 --- a/patches/changelog.patch +++ /dev/null @@ -1,4 +0,0 @@ -diff --git a/src/my_cli/CHANGELOG.md "b/src/my_cli/{{#publishable}}CHANGELOG.md{{/publishable}}" -similarity index 100% -rename from src/my_cli/CHANGELOG.md -rename to "src/my_cli/{{#publishable}}CHANGELOG.md{{/publishable}}" diff --git a/patches/pubspec_publish.patch b/patches/pubspec_publish.patch deleted file mode 100644 index 4f8a70b..0000000 --- a/patches/pubspec_publish.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/src/my_cli/pubspec.yaml b/src/my_cli/pubspec.yaml -index 20c444b..b97bba1 100644 ---- a/src/my_cli/pubspec.yaml -+++ b/src/my_cli/pubspec.yaml -@@ -1,7 +1,7 @@ - name: my_cli - description: A Very Good CLI application - version: 0.0.1 --publish_to: none -+{{^publishable}}publish_to: none{{/publishable}} - - environment: - sdk: ">=3.0.0 <4.0.0" diff --git a/patches/workflow_pana.patch b/patches/workflow_pana.patch deleted file mode 100644 index 26761bc..0000000 --- a/patches/workflow_pana.patch +++ /dev/null @@ -1,15 +0,0 @@ -diff --git a/src/my_cli/.github/workflows/my_cli.yaml b/src/my_cli/.github/workflows/my_cli.yaml -index c0cb0e7..cf38411 100644 ---- a/src/my_cli/.github/workflows/my_cli.yaml -+++ b/src/my_cli/.github/workflows/my_cli.yaml -@@ -22,6 +22,10 @@ jobs: - - build: - uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/dart_package.yml@v1 -+{{#publishable}} -+ pana: -+ uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/pana.yml@v1 -+{{/publishable}} - - verify-version: - runs-on: ubuntu-latest diff --git a/src/my_cli/.github/ISSUE_TEMPLATE/ config.yml b/src/my_cli/.github/ISSUE_TEMPLATE/ config.yml deleted file mode 100644 index 3ba13e0..0000000 --- a/src/my_cli/.github/ISSUE_TEMPLATE/ config.yml +++ /dev/null @@ -1 +0,0 @@ -blank_issues_enabled: false diff --git a/src/my_cli/.github/ISSUE_TEMPLATE/bug_report.md b/src/my_cli/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 50a4c7b..0000000 --- a/src/my_cli/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -name: Bug Report -about: Create a report to help us improve -title: "fix: " -labels: bug ---- - -**Description** - -A clear and concise description of what the bug is. - -**Steps To Reproduce** - -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error - -**Expected Behavior** - -A clear and concise description of what you expected to happen. - -**Screenshots** - -If applicable, add screenshots to help explain your problem. - -**Additional Context** - -Add any other context about the problem here. diff --git a/src/my_cli/.github/ISSUE_TEMPLATE/feature_request.md b/src/my_cli/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index ddd2fcc..0000000 --- a/src/my_cli/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -name: Feature Request -about: A new feature to be added to the project -title: "feat: " -labels: feature ---- - -**Description** - -Clearly describe what you are looking to add. The more context the better. - -**Requirements** - -- [ ] Checklist of requirements to be fulfilled - -**Additional Context** - -Add any other context or screenshots about the feature request go here. diff --git a/src/my_cli/.github/PULL_REQUEST_TEMPLATE.md b/src/my_cli/.github/PULL_REQUEST_TEMPLATE.md deleted file mode 100644 index 6b9372e..0000000 --- a/src/my_cli/.github/PULL_REQUEST_TEMPLATE.md +++ /dev/null @@ -1,23 +0,0 @@ - - -## Description - - - -## Type of Change - - - -- [ ] โœจ New feature (non-breaking change which adds functionality) -- [ ] ๐Ÿ› ๏ธ Bug fix (non-breaking change which fixes an issue) -- [ ] โŒ Breaking change (fix or feature that would cause existing functionality to change) -- [ ] ๐Ÿงน Code refactor -- [ ] โœ… Build configuration change -- [ ] ๐Ÿ“ Documentation -- [ ] ๐Ÿ—‘๏ธ Chore diff --git a/src/my_cli/.github/cspell.json b/src/my_cli/.github/cspell.json deleted file mode 100644 index 1b78898..0000000 --- a/src/my_cli/.github/cspell.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "version": "0.2", - "$schema": "https://raw.githubusercontent.com/streetsidesoftware/cspell/main/cspell.schema.json", - "dictionaries": ["vgv_allowed", "vgv_forbidden"], - "dictionaryDefinitions": [ - { - "name": "vgv_allowed", - "path": "https://raw.githubusercontent.com/verygoodopensource/very_good_dictionaries/main/allowed.txt", - "description": "Allowed VGV Spellings" - }, - { - "name": "vgv_forbidden", - "path": "https://raw.githubusercontent.com/verygoodopensource/very_good_dictionaries/main/forbidden.txt", - "description": "Forbidden VGV Spellings" - } - ], - "useGitignore": true, - "words": [ - ] -} diff --git a/src/my_cli/.github/dependabot.yaml b/src/my_cli/.github/dependabot.yaml deleted file mode 100644 index 63b035c..0000000 --- a/src/my_cli/.github/dependabot.yaml +++ /dev/null @@ -1,11 +0,0 @@ -version: 2 -enable-beta-ecosystems: true -updates: - - package-ecosystem: "github-actions" - directory: "/" - schedule: - interval: "daily" - - package-ecosystem: "pub" - directory: "/" - schedule: - interval: "daily" diff --git a/src/my_cli/.github/workflows/my_cli.yaml b/src/my_cli/.github/workflows/my_cli.yaml deleted file mode 100644 index 75f3b5f..0000000 --- a/src/my_cli/.github/workflows/my_cli.yaml +++ /dev/null @@ -1,55 +0,0 @@ -name: my_cli - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -on: - pull_request: - paths: - - ".github/workflows/my_cli.yaml" - - "lib/**" - - "test/**" - - "pubspec.yaml" - push: - branches: - - main - paths: - - ".github/workflows/my_cli.yaml" - - "lib/**" - - "test/**" - - "pubspec.yaml" - -jobs: - semantic-pull-request: - uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/semantic_pull_request.yml@v1 - - build: - uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/dart_package.yml@v1 - - spell-check: - uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/spell_check.yml@v1 - with: - includes: | - **/*.md - !brick/**/*.md - .*/**/*.md - modified_files_only: false - - verify-version: - runs-on: ubuntu-latest - steps: - - name: ๐Ÿ“š Git Checkout - uses: actions/checkout@v2 - - - name: ๐ŸŽฏ Setup Dart - uses: dart-lang/setup-dart@v1 - with: - sdk: "stable" - - - name: ๐Ÿ“ฆ Install Dependencies - run: | - dart pub get - - - name: ๐Ÿ”Ž Verify version - run: dart run test --run-skipped -t version-verify diff --git a/src/my_cli/.gitignore b/src/my_cli/.gitignore deleted file mode 100644 index 9f6ee8a..0000000 --- a/src/my_cli/.gitignore +++ /dev/null @@ -1,15 +0,0 @@ -# See https://www.dartlang.org/guides/libraries/private-files - -# Files and directories created by pub -.dart_tool/ -.packages -build/ -pubspec.lock - -# Files generated during tests -.test_coverage.dart -coverage/ -.test_runner.dart - -# Android studio and IntelliJ -.idea \ No newline at end of file diff --git a/src/my_cli/CHANGELOG.md b/src/my_cli/CHANGELOG.md deleted file mode 100644 index 4fff97b..0000000 --- a/src/my_cli/CHANGELOG.md +++ /dev/null @@ -1,3 +0,0 @@ -# 0.0.1 - -- feat: initial commit ๐ŸŽ‰ diff --git a/src/my_cli/LICENSE b/src/my_cli/LICENSE deleted file mode 100644 index e187c84..0000000 --- a/src/my_cli/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2023 Very Good Ventures - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/src/my_cli/README.md b/src/my_cli/README.md deleted file mode 100644 index fea9d93..0000000 --- a/src/my_cli/README.md +++ /dev/null @@ -1,71 +0,0 @@ -## my_cli - -![coverage][coverage_badge] -[![style: very good analysis][very_good_analysis_badge]][very_good_analysis_link] -[![License: MIT][license_badge]][license_link] - -Generated by the [Very Good CLI][very_good_cli_link] ๐Ÿค– - -A Very Good CLI application. - ---- - -## Getting Started ๐Ÿš€ - -If the CLI application is available on [pub](https://pub.dev), activate globally via: - -```sh -dart pub global activate my_cli -``` - -Or locally via: - -```sh -dart pub global activate --source=path -``` - -## Usage - -```sh -# Sample command -$ my_executable sample - -# Sample command option -$ my_executable sample --cyan - -# Show CLI version -$ my_executable --version - -# Show usage help -$ my_executable --help -``` - -## Running Tests with coverage ๐Ÿงช - -To run all unit tests use the following command: - -```sh -$ dart pub global activate coverage 1.2.0 -$ dart test --coverage=coverage -$ dart pub global run coverage:format_coverage --lcov --in=coverage --out=coverage/lcov.info -``` - -To view the generated coverage report you can use [lcov](https://github.com/linux-test-project/lcov) -. - -```sh -# Generate Coverage Report -$ genhtml coverage/lcov.info -o coverage/ - -# Open Coverage Report -$ open coverage/index.html -``` - ---- - -[coverage_badge]: coverage_badge.svg -[license_badge]: https://img.shields.io/badge/license-MIT-blue.svg -[license_link]: https://opensource.org/licenses/MIT -[very_good_analysis_badge]: https://img.shields.io/badge/style-very_good_analysis-B22C89.svg -[very_good_analysis_link]: https://pub.dev/packages/very_good_analysis -[very_good_cli_link]: https://github.com/VeryGoodOpenSource/very_good_cli \ No newline at end of file diff --git a/src/my_cli/analysis_options.yaml b/src/my_cli/analysis_options.yaml deleted file mode 100644 index fa798a8..0000000 --- a/src/my_cli/analysis_options.yaml +++ /dev/null @@ -1,4 +0,0 @@ -include: package:very_good_analysis/analysis_options.5.1.0.yaml -linter: - rules: - public_member_api_docs: false diff --git a/src/my_cli/bin/my_executable.dart b/src/my_cli/bin/my_executable.dart deleted file mode 100644 index 50ae056..0000000 --- a/src/my_cli/bin/my_executable.dart +++ /dev/null @@ -1,18 +0,0 @@ -import 'dart:io'; - -import 'package:my_cli/src/command_runner.dart'; - -Future main(List args) async { - await _flushThenExit(await MyCLICommandRunner().run(args)); -} - -/// Flushes the stdout and stderr streams, then exits the program with the given -/// status code. -/// -/// This returns a Future that will never complete, since the program will have -/// exited already. This is useful to prevent Future chains from proceeding -/// after you've decided to exit. -Future _flushThenExit(int status) { - return Future.wait([stdout.close(), stderr.close()]) - .then((_) => exit(status)); -} diff --git a/src/my_cli/coverage_badge.svg b/src/my_cli/coverage_badge.svg deleted file mode 100644 index 88bfadf..0000000 --- a/src/my_cli/coverage_badge.svg +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - coverage - coverage - 100% - 100% - - \ No newline at end of file diff --git a/src/my_cli/dart_test.yaml b/src/my_cli/dart_test.yaml deleted file mode 100644 index 2f46c7e..0000000 --- a/src/my_cli/dart_test.yaml +++ /dev/null @@ -1,3 +0,0 @@ -tags: - version-verify: - skip: "Should only be run during pull request. Verifies if version file is updated." \ No newline at end of file diff --git a/src/my_cli/lib/my_cli.dart b/src/my_cli/lib/my_cli.dart deleted file mode 100644 index 31d3c79..0000000 --- a/src/my_cli/lib/my_cli.dart +++ /dev/null @@ -1,10 +0,0 @@ -/// my_cli, A Very Good CLI application -/// -/// ```sh -/// # activate my_cli -/// dart pub global activate my_cli -/// -/// # see usage -/// my_executable --help -/// ``` -library my_cli; diff --git a/src/my_cli/lib/src/command_runner.dart b/src/my_cli/lib/src/command_runner.dart deleted file mode 100644 index f2c794e..0000000 --- a/src/my_cli/lib/src/command_runner.dart +++ /dev/null @@ -1,144 +0,0 @@ -import 'package:args/args.dart'; -import 'package:args/command_runner.dart'; -import 'package:cli_completion/cli_completion.dart'; -import 'package:mason_logger/mason_logger.dart'; -import 'package:my_cli/src/commands/commands.dart'; -import 'package:my_cli/src/version.dart'; -import 'package:pub_updater/pub_updater.dart'; - -const executableName = 'my_executable'; -const packageName = 'my_cli'; -const description = 'A Very Good CLI application'; - -/// {@template my_cli_command_runner} -/// A [CommandRunner] for the CLI. -/// -/// ``` -/// $ my_executable --version -/// ``` -/// {@endtemplate} -class MyCLICommandRunner extends CompletionCommandRunner { - /// {@macro my_cli_command_runner} - MyCLICommandRunner({ - Logger? logger, - PubUpdater? pubUpdater, - }) : _logger = logger ?? Logger(), - _pubUpdater = pubUpdater ?? PubUpdater(), - super(executableName, description) { - // Add root options and flags - argParser - ..addFlag( - 'version', - abbr: 'v', - negatable: false, - help: 'Print the current version.', - ) - ..addFlag( - 'verbose', - help: 'Noisy logging, including all shell commands executed.', - ); - - // Add sub commands - addCommand(SampleCommand(logger: _logger)); - addCommand(UpdateCommand(logger: _logger, pubUpdater: _pubUpdater)); - } - - @override - void printUsage() => _logger.info(usage); - - final Logger _logger; - final PubUpdater _pubUpdater; - - @override - Future run(Iterable args) async { - try { - final topLevelResults = parse(args); - if (topLevelResults['verbose'] == true) { - _logger.level = Level.verbose; - } - return await runCommand(topLevelResults) ?? ExitCode.success.code; - } on FormatException catch (e, stackTrace) { - // On format errors, show the commands error message, root usage and - // exit with an error code - _logger - ..err(e.message) - ..err('$stackTrace') - ..info('') - ..info(usage); - return ExitCode.usage.code; - } on UsageException catch (e) { - // On usage errors, show the commands usage message and - // exit with an error code - _logger - ..err(e.message) - ..info('') - ..info(e.usage); - return ExitCode.usage.code; - } - } - - @override - Future runCommand(ArgResults topLevelResults) async { - // Fast track completion command - if (topLevelResults.command?.name == 'completion') { - await super.runCommand(topLevelResults); - return ExitCode.success.code; - } - - // Verbose logs - _logger - ..detail('Argument information:') - ..detail(' Top level options:'); - for (final option in topLevelResults.options) { - if (topLevelResults.wasParsed(option)) { - _logger.detail(' - $option: ${topLevelResults[option]}'); - } - } - if (topLevelResults.command != null) { - final commandResult = topLevelResults.command!; - _logger - ..detail(' Command: ${commandResult.name}') - ..detail(' Command options:'); - for (final option in commandResult.options) { - if (commandResult.wasParsed(option)) { - _logger.detail(' - $option: ${commandResult[option]}'); - } - } - } - - // Run the command or show version - final int? exitCode; - if (topLevelResults['version'] == true) { - _logger.info(packageVersion); - exitCode = ExitCode.success.code; - } else { - exitCode = await super.runCommand(topLevelResults); - } - - // Check for updates - if (topLevelResults.command?.name != UpdateCommand.commandName) { - await _checkForUpdates(); - } - - return exitCode; - } - - /// Checks if the current version (set by the build runner on the - /// version.dart file) is the most recent one. If not, show a prompt to the - /// user. - Future _checkForUpdates() async { - try { - final latestVersion = await _pubUpdater.getLatestVersion(packageName); - final isUpToDate = packageVersion == latestVersion; - if (!isUpToDate) { - _logger - ..info('') - ..info( - ''' -${lightYellow.wrap('Update available!')} ${lightCyan.wrap(packageVersion)} \u2192 ${lightCyan.wrap(latestVersion)} -Run ${lightCyan.wrap('$executableName update')} to update''', - ); - } - } catch (_) {} - } -} diff --git a/src/my_cli/lib/src/commands/commands.dart b/src/my_cli/lib/src/commands/commands.dart deleted file mode 100644 index eec317e..0000000 --- a/src/my_cli/lib/src/commands/commands.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'sample_command.dart'; -export 'update_command.dart'; diff --git a/src/my_cli/lib/src/commands/sample_command.dart b/src/my_cli/lib/src/commands/sample_command.dart deleted file mode 100644 index 2f8e37c..0000000 --- a/src/my_cli/lib/src/commands/sample_command.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'package:args/command_runner.dart'; -import 'package:mason_logger/mason_logger.dart'; - -/// {@template sample_command} -/// -/// `my_executable sample` -/// A [Command] to exemplify a sub command -/// {@endtemplate} -class SampleCommand extends Command { - /// {@macro sample_command} - SampleCommand({ - required Logger logger, - }) : _logger = logger { - argParser.addFlag( - 'cyan', - abbr: 'c', - help: 'Prints the same joke, but in cyan', - negatable: false, - ); - } - - @override - String get description => 'A sample sub command that just prints one joke'; - - @override - String get name => 'sample'; - - final Logger _logger; - - @override - Future run() async { - var output = 'Which unicorn has a cold? The Achoo-nicorn!'; - if (argResults?['cyan'] == true) { - output = lightCyan.wrap(output)!; - } - _logger.info(output); - return ExitCode.success.code; - } -} diff --git a/src/my_cli/lib/src/commands/update_command.dart b/src/my_cli/lib/src/commands/update_command.dart deleted file mode 100644 index bdd1cf3..0000000 --- a/src/my_cli/lib/src/commands/update_command.dart +++ /dev/null @@ -1,74 +0,0 @@ -import 'dart:io'; - -import 'package:args/command_runner.dart'; -import 'package:mason_logger/mason_logger.dart'; -import 'package:my_cli/src/command_runner.dart'; -import 'package:my_cli/src/version.dart'; -import 'package:pub_updater/pub_updater.dart'; - -/// {@template update_command} -/// A command which updates the CLI. -/// {@endtemplate} -class UpdateCommand extends Command { - /// {@macro update_command} - UpdateCommand({ - required Logger logger, - PubUpdater? pubUpdater, - }) : _logger = logger, - _pubUpdater = pubUpdater ?? PubUpdater(); - - final Logger _logger; - final PubUpdater _pubUpdater; - - @override - String get description => 'Update the CLI.'; - - static const String commandName = 'update'; - - @override - String get name => commandName; - - @override - Future run() async { - final updateCheckProgress = _logger.progress('Checking for updates'); - late final String latestVersion; - try { - latestVersion = await _pubUpdater.getLatestVersion(packageName); - } catch (error) { - updateCheckProgress.fail(); - _logger.err('$error'); - return ExitCode.software.code; - } - updateCheckProgress.complete('Checked for updates'); - - final isUpToDate = packageVersion == latestVersion; - if (isUpToDate) { - _logger.info('CLI is already at the latest version.'); - return ExitCode.success.code; - } - - final updateProgress = _logger.progress('Updating to $latestVersion'); - - late final ProcessResult result; - try { - result = await _pubUpdater.update( - packageName: packageName, - versionConstraint: latestVersion, - ); - } catch (error) { - updateProgress.fail(); - _logger.err('$error'); - return ExitCode.software.code; - } - - if (result.exitCode != ExitCode.success.code) { - updateProgress.fail(); - _logger.err('Error updating CLI: ${result.stderr}'); - return ExitCode.software.code; - } - - updateProgress.complete('Updated to $latestVersion'); - - return ExitCode.success.code; - } -} diff --git a/src/my_cli/lib/src/version.dart b/src/my_cli/lib/src/version.dart deleted file mode 100644 index 67a7647..0000000 --- a/src/my_cli/lib/src/version.dart +++ /dev/null @@ -1,2 +0,0 @@ -// Generated code. Do not modify. -const packageVersion = '0.0.1'; diff --git a/src/my_cli/pubspec.yaml b/src/my_cli/pubspec.yaml deleted file mode 100644 index 032788c..0000000 --- a/src/my_cli/pubspec.yaml +++ /dev/null @@ -1,24 +0,0 @@ -name: my_cli -description: A Very Good CLI application -version: 0.0.1 -publish_to: none - -environment: - sdk: ">=3.0.0 <4.0.0" - -dependencies: - args: ^2.4.1 - cli_completion: ^0.4.0 - mason_logger: ^0.2.5 - pub_updater: ^0.4.0 - -dev_dependencies: - build_runner: ^2.4.4 - build_verify: ^3.1.0 - build_version: ^2.1.1 - mocktail: ^1.0.0 - test: ^1.24.6 - very_good_analysis: ^5.1.0 - -executables: - my_executable: diff --git a/src/my_cli/test/ensure_build_test.dart b/src/my_cli/test/ensure_build_test.dart deleted file mode 100644 index 4b619ed..0000000 --- a/src/my_cli/test/ensure_build_test.dart +++ /dev/null @@ -1,9 +0,0 @@ -@Tags(['version-verify']) -library ensure_build_test; - -import 'package:build_verify/build_verify.dart'; -import 'package:test/test.dart'; - -void main() { - test('ensure_build', expectBuildClean); -} diff --git a/src/my_cli/test/src/command_runner_test.dart b/src/my_cli/test/src/command_runner_test.dart deleted file mode 100644 index fdb7ff8..0000000 --- a/src/my_cli/test/src/command_runner_test.dart +++ /dev/null @@ -1,174 +0,0 @@ -import 'dart:io'; - -import 'package:args/command_runner.dart'; -import 'package:cli_completion/cli_completion.dart'; -import 'package:mason_logger/mason_logger.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:my_cli/src/command_runner.dart'; -import 'package:my_cli/src/version.dart'; -import 'package:pub_updater/pub_updater.dart'; -import 'package:test/test.dart'; - -class _MockLogger extends Mock implements Logger {} - -class _MockProgress extends Mock implements Progress {} - -class _MockPubUpdater extends Mock implements PubUpdater {} - -const latestVersion = '0.0.0'; - -final updatePrompt = ''' -${lightYellow.wrap('Update available!')} ${lightCyan.wrap(packageVersion)} \u2192 ${lightCyan.wrap(latestVersion)} -Run ${lightCyan.wrap('$executableName update')} to update'''; - -void main() { - group('MyCLICommandRunner', () { - late PubUpdater pubUpdater; - late Logger logger; - late MyCLICommandRunner commandRunner; - - setUp(() { - pubUpdater = _MockPubUpdater(); - - when( - () => pubUpdater.getLatestVersion(any()), - ).thenAnswer((_) async => packageVersion); - - logger = _MockLogger(); - - commandRunner = MyCLICommandRunner( - logger: logger, - pubUpdater: pubUpdater, - ); - }); - - test('shows update message when newer version exists', () async { - when( - () => pubUpdater.getLatestVersion(any()), - ).thenAnswer((_) async => latestVersion); - - final result = await commandRunner.run(['--version']); - expect(result, equals(ExitCode.success.code)); - verify(() => logger.info(updatePrompt)).called(1); - }); - - test( - 'Does not show update message when the shell calls the ' - 'completion command', - () async { - when( - () => pubUpdater.getLatestVersion(any()), - ).thenAnswer((_) async => latestVersion); - - final result = await commandRunner.run(['completion']); - expect(result, equals(ExitCode.success.code)); - verifyNever(() => logger.info(updatePrompt)); - }, - ); - - test('does not show update message when using update command', () async { - when( - () => pubUpdater.getLatestVersion(any()), - ).thenAnswer((_) async => latestVersion); - when( - () => pubUpdater.update( - packageName: packageName, - versionConstraint: any(named: 'versionConstraint'), - ), - ).thenAnswer( - (_) async => ProcessResult(0, ExitCode.success.code, null, null), - ); - when( - () => pubUpdater.isUpToDate( - packageName: any(named: 'packageName'), - currentVersion: any(named: 'currentVersion'), - ), - ).thenAnswer((_) async => true); - - final progress = _MockProgress(); - final progressLogs = []; - when(() => progress.complete(any())).thenAnswer((_) { - final message = _.positionalArguments.elementAt(0) as String?; - if (message != null) progressLogs.add(message); - }); - when(() => logger.progress(any())).thenReturn(progress); - - final result = await commandRunner.run(['update']); - expect(result, equals(ExitCode.success.code)); - verifyNever(() => logger.info(updatePrompt)); - }); - - test('can be instantiated without an explicit analytics/logger instance', - () { - final commandRunner = MyCLICommandRunner(); - expect(commandRunner, isNotNull); - expect(commandRunner, isA>()); - }); - - test('handles FormatException', () async { - const exception = FormatException('oops!'); - var isFirstInvocation = true; - when(() => logger.info(any())).thenAnswer((_) { - if (isFirstInvocation) { - isFirstInvocation = false; - throw exception; - } - }); - final result = await commandRunner.run(['--version']); - expect(result, equals(ExitCode.usage.code)); - verify(() => logger.err(exception.message)).called(1); - verify(() => logger.info(commandRunner.usage)).called(1); - }); - - test('handles UsageException', () async { - final exception = UsageException('oops!', 'exception usage'); - var isFirstInvocation = true; - when(() => logger.info(any())).thenAnswer((_) { - if (isFirstInvocation) { - isFirstInvocation = false; - throw exception; - } - }); - final result = await commandRunner.run(['--version']); - expect(result, equals(ExitCode.usage.code)); - verify(() => logger.err(exception.message)).called(1); - verify(() => logger.info('exception usage')).called(1); - }); - - group('--version', () { - test('outputs current version', () async { - final result = await commandRunner.run(['--version']); - expect(result, equals(ExitCode.success.code)); - verify(() => logger.info(packageVersion)).called(1); - }); - }); - - group('--verbose', () { - test('enables verbose logging', () async { - final result = await commandRunner.run(['--verbose']); - expect(result, equals(ExitCode.success.code)); - - verify(() => logger.detail('Argument information:')).called(1); - verify(() => logger.detail(' Top level options:')).called(1); - verify(() => logger.detail(' - verbose: true')).called(1); - verifyNever(() => logger.detail(' Command options:')); - }); - - test('enables verbose logging for sub commands', () async { - final result = await commandRunner.run([ - '--verbose', - 'sample', - '--cyan', - ]); - expect(result, equals(ExitCode.success.code)); - - verify(() => logger.detail('Argument information:')).called(1); - verify(() => logger.detail(' Top level options:')).called(1); - verify(() => logger.detail(' - verbose: true')).called(1); - verify(() => logger.detail(' Command: sample')).called(1); - verify(() => logger.detail(' Command options:')).called(1); - verify(() => logger.detail(' - cyan: true')).called(1); - }); - }); - }); -} diff --git a/src/my_cli/test/src/commands/sample_command_test.dart b/src/my_cli/test/src/commands/sample_command_test.dart deleted file mode 100644 index 4bc72d8..0000000 --- a/src/my_cli/test/src/commands/sample_command_test.dart +++ /dev/null @@ -1,58 +0,0 @@ -import 'package:mason_logger/mason_logger.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:my_cli/src/command_runner.dart'; -import 'package:test/test.dart'; - -class _MockLogger extends Mock implements Logger {} - -void main() { - group('sample', () { - late Logger logger; - late MyCLICommandRunner commandRunner; - - setUp(() { - logger = _MockLogger(); - commandRunner = MyCLICommandRunner(logger: logger); - }); - - test('tells a joke', () async { - final exitCode = await commandRunner.run(['sample']); - - expect(exitCode, ExitCode.success.code); - - verify( - () => logger.info('Which unicorn has a cold? The Achoo-nicorn!'), - ).called(1); - }); - test('tells a joke in cyan', () async { - final exitCode = await commandRunner.run(['sample', '-c']); - - expect(exitCode, ExitCode.success.code); - - verify( - () => logger.info( - lightCyan.wrap('Which unicorn has a cold? The Achoo-nicorn!'), - ), - ).called(1); - }); - - test('wrong usage', () async { - final exitCode = await commandRunner.run(['sample', '-p']); - - expect(exitCode, ExitCode.usage.code); - - verify(() => logger.err('Could not find an option or flag "-p".')) - .called(1); - verify( - () => logger.info( - ''' -Usage: $executableName sample [arguments] --h, --help Print this usage information. --c, --cyan Prints the same joke, but in cyan - -Run "$executableName help" to see global options.''', - ), - ).called(1); - }); - }); -} diff --git a/src/my_cli/test/src/commands/update_command_test.dart b/src/my_cli/test/src/commands/update_command_test.dart deleted file mode 100644 index dbe38fc..0000000 --- a/src/my_cli/test/src/commands/update_command_test.dart +++ /dev/null @@ -1,185 +0,0 @@ -import 'dart:io'; - -import 'package:mason_logger/mason_logger.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:my_cli/src/command_runner.dart'; -import 'package:my_cli/src/commands/commands.dart'; -import 'package:my_cli/src/version.dart'; -import 'package:pub_updater/pub_updater.dart'; -import 'package:test/test.dart'; - -class _MockLogger extends Mock implements Logger {} - -class _MockProgress extends Mock implements Progress {} - -class _MockPubUpdater extends Mock implements PubUpdater {} - -void main() { - const latestVersion = '0.0.0'; - - group('update', () { - late PubUpdater pubUpdater; - late Logger logger; - late MyCLICommandRunner commandRunner; - - setUp(() { - final progress = _MockProgress(); - final progressLogs = []; - pubUpdater = _MockPubUpdater(); - logger = _MockLogger(); - commandRunner = MyCLICommandRunner( - logger: logger, - pubUpdater: pubUpdater, - ); - - when( - () => pubUpdater.getLatestVersion(any()), - ).thenAnswer((_) async => packageVersion); - when( - () => pubUpdater.update( - packageName: packageName, - versionConstraint: latestVersion, - ), - ).thenAnswer( - (_) async => ProcessResult(0, ExitCode.success.code, null, null), - ); - when( - () => pubUpdater.isUpToDate( - packageName: any(named: 'packageName'), - currentVersion: any(named: 'currentVersion'), - ), - ).thenAnswer((_) async => true); - when(() => progress.complete(any())).thenAnswer((_) { - final message = _.positionalArguments.elementAt(0) as String?; - if (message != null) progressLogs.add(message); - }); - when(() => logger.progress(any())).thenReturn(progress); - }); - - test('can be instantiated without a pub updater', () { - final command = UpdateCommand(logger: logger); - expect(command, isNotNull); - }); - - test( - 'handles pub latest version query errors', - () async { - when( - () => pubUpdater.getLatestVersion(any()), - ).thenThrow(Exception('oops')); - final result = await commandRunner.run(['update']); - expect(result, equals(ExitCode.software.code)); - verify(() => logger.progress('Checking for updates')).called(1); - verify(() => logger.err('Exception: oops')); - verifyNever( - () => pubUpdater.update( - packageName: any(named: 'packageName'), - versionConstraint: any(named: 'versionConstraint'), - ), - ); - }, - ); - - test( - 'handles pub update errors', - () async { - when( - () => pubUpdater.getLatestVersion(any()), - ).thenAnswer((_) async => latestVersion); - when( - () => pubUpdater.update( - packageName: any(named: 'packageName'), - versionConstraint: any(named: 'versionConstraint'), - ), - ).thenThrow(Exception('oops')); - final result = await commandRunner.run(['update']); - expect(result, equals(ExitCode.software.code)); - verify(() => logger.progress('Checking for updates')).called(1); - verify(() => logger.err('Exception: oops')); - verify( - () => pubUpdater.update( - packageName: any(named: 'packageName'), - versionConstraint: any(named: 'versionConstraint'), - ), - ).called(1); - }, - ); - - test('handles pub update process errors', () async { - const error = 'Oh no! Installing this is not possible right now!'; - - when( - () => pubUpdater.getLatestVersion(any()), - ).thenAnswer((_) async => latestVersion); - - when( - () => pubUpdater.update( - packageName: any(named: 'packageName'), - versionConstraint: any(named: 'versionConstraint'), - ), - ).thenAnswer((_) async => ProcessResult(0, 1, null, error)); - - final result = await commandRunner.run(['update']); - - expect(result, equals(ExitCode.software.code)); - verify(() => logger.progress('Checking for updates')).called(1); - verify(() => logger.err('Error updating CLI: $error')); - verify( - () => pubUpdater.update( - packageName: any(named: 'packageName'), - versionConstraint: any(named: 'versionConstraint'), - ), - ).called(1); - }); - - test( - 'updates when newer version exists', - () async { - when( - () => pubUpdater.getLatestVersion(any()), - ).thenAnswer((_) async => latestVersion); - when( - () => pubUpdater.update( - packageName: any(named: 'packageName'), - versionConstraint: any(named: 'versionConstraint'), - ), - ).thenAnswer( - (_) async => ProcessResult(0, ExitCode.success.code, null, null), - ); - when(() => logger.progress(any())).thenReturn(_MockProgress()); - final result = await commandRunner.run(['update']); - expect(result, equals(ExitCode.success.code)); - verify(() => logger.progress('Checking for updates')).called(1); - verify(() => logger.progress('Updating to $latestVersion')).called(1); - verify( - () => pubUpdater.update( - packageName: packageName, - versionConstraint: latestVersion, - ), - ).called(1); - }, - ); - - test( - 'does not update when already on latest version', - () async { - when( - () => pubUpdater.getLatestVersion(any()), - ).thenAnswer((_) async => packageVersion); - when(() => logger.progress(any())).thenReturn(_MockProgress()); - final result = await commandRunner.run(['update']); - expect(result, equals(ExitCode.success.code)); - verify( - () => logger.info('CLI is already at the latest version.'), - ).called(1); - verifyNever(() => logger.progress('Updating to $latestVersion')); - verifyNever( - () => pubUpdater.update( - packageName: any(named: 'packageName'), - versionConstraint: any(named: 'versionConstraint'), - ), - ); - }, - ); - }); -} diff --git a/tool/generator/main.dart b/tool/generator/main.dart deleted file mode 100644 index 680837d..0000000 --- a/tool/generator/main.dart +++ /dev/null @@ -1,151 +0,0 @@ -import 'dart:io'; - -import 'package:path/path.dart' as p; - -final targetPath = p.join('brick', '__brick__'); -final sourcePath = p.join('src'); - -void main() async { - // Remove Previously Generated Files - final targetDir = Directory(targetPath); - if (targetDir.existsSync()) { - await targetDir.delete(recursive: true); - } - - // Copy Project Files - await Shell.cp(sourcePath, targetPath); - - // Apply patches - await Future.wait( - Directory('patches').listSync().whereType().map( - (file) async { - final process = await Process.start('git', ['apply']); - process.stdin - .write(file.readAsStringSync().replaceAll('src', targetPath)); - await process.stdin.close(); - - await process.exitCode; - }, - ), - ); - - // Convert Values to Variables - await Future.wait( - Directory(p.join(targetPath, 'my_cli')) - .listSync(recursive: true) - .whereType() - .map((_) async { - var file = _; - - try { - if (p.basename(file.path) == 'LICENSE') { - await file.delete(recursive: true); - return; - } - - if (p.isWithin( - p.join(targetPath, 'my_cli', '.github', 'workflows'), - file.path, - )) { - final contents = file.readAsStringSync(); - file.writeAsStringSync( - contents.replaceFirst( - r'group: ${{ github.workflow }}-${{ github.ref }}', - r'group: ${{#mustacheCase}}github.workflow{{/mustacheCase}}-${{#mustacheCase}}github.ref{{/mustacheCase}}', - ), - ); - } - - final contents = await file.readAsString(); - file = await file.writeAsString( - contents - // project_name - .replaceAll('my_cli', '{{project_name.snakeCase()}}') - .replaceAll('my-cli', '{{project_name.paramCase()}}') - .replaceAll('MyCLI', '{{project_name.pascalCase()}}') - .replaceAll('myCLI', '{{project_name.camelCase()}}') - .replaceAll('MY_CLI', '{{project_name.constantCase()}}') - // executable_name - .replaceAll('my_executable', '{{executable_name.snakeCase()}}') - // description - .replaceAll('A Very Good CLI application', '{{description}}'), - ); - - final fileSegments = file.path.split('/').sublist(2); - - if (fileSegments - .any((e) => e.contains('my_cli') || e.contains('my_executable'))) { - final newPathSegment = fileSegments - .join('/') - .replaceAll( - 'my_cli', - '{{project_name.snakeCase()}}', - ) - .replaceAll( - 'my_executable', - '{{executable_name.snakeCase()}}', - ); - final newPath = p.join(targetPath, newPathSegment); - File(newPath).createSync(recursive: true); - file.renameSync(newPath); - } - } catch (_) {} - }), - ); - await Directory(p.join(targetPath, 'my_cli')).delete(recursive: true); -} - -class Shell { - static Future cp(String source, String destination) { - return _Cmd.run('cp', ['-rf', source, destination]); - } - - static Future rm(String source) { - return _Cmd.run('rm', ['-rf', source]); - } - - static Future mkdir(String destination) { - return _Cmd.run('mkdir', ['-p', destination]); - } -} - -class _Cmd { - static Future run( - String cmd, - List args, { - bool throwOnError = true, - String? processWorkingDir, - }) async { - final result = await Process.run(cmd, args, - workingDirectory: processWorkingDir, runInShell: true); - - if (throwOnError) { - _throwIfProcessFailed(result, cmd, args); - } - return result; - } - - static void _throwIfProcessFailed( - ProcessResult pr, - String process, - List args, - ) { - if (pr.exitCode != 0) { - final values = { - 'Standard out': pr.stdout.toString().trim(), - 'Standard error': pr.stderr.toString().trim() - }..removeWhere((k, v) => v.isEmpty); - - String message; - if (values.isEmpty) { - message = 'Unknown error'; - } else if (values.length == 1) { - message = values.values.single; - } else { - message = values.entries.map((e) => '${e.key}\n${e.value}').join('\n'); - } - - throw ProcessException(process, args, message, pr.exitCode); - } - } -} diff --git a/tool/generator/pubspec.yaml b/tool/generator/pubspec.yaml deleted file mode 100644 index 959cf98..0000000 --- a/tool/generator/pubspec.yaml +++ /dev/null @@ -1,8 +0,0 @@ -name: generator -description: A template generator for Very Good Dart CLI. - -environment: - sdk: ">=3.0.0 <4.0.0" - -dependencies: - path: ^1.8.3