diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml index 9a48110..10570ba 100644 --- a/.github/dependabot.yaml +++ b/.github/dependabot.yaml @@ -7,3 +7,7 @@ updates: interval: monthly labels: - autosubmit + - package-ecosystem: pub + directory: / + schedule: + interval: monthly diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 3110a84..2ef331b 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -8,10 +8,13 @@ Fixes #(issue number) Please delete options that are not relevant. -- [ ] Bug fix (non-breaking change which fixes an issue) -- [ ] New feature (non-breaking change which adds functionality) -- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) -- [ ] This change requires a documentation update +- [ ] ๐Ÿ› ๏ธ Bug fix (non-breaking change which fixes an issue) +- [ ] โœจ New feature (non-breaking change which adds functionality) +- [ ] โŒ Breaking change (fix or feature that would cause existing functionality to not work as expected) +- [ ] ๐Ÿ“ This change requires a documentation update +- [ ] ๐Ÿงน Code refactor +- [ ] โœ… Build configuration change +- [ ] ๐Ÿ—‘๏ธ Chore ## Checklist: diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index bc5d776..03579cb 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -28,7 +28,7 @@ jobs: - name: ๐ŸŽฏ Set up Dart SDK uses: dart-lang/setup-dart@v1 with: - sdk: 3.0.4 + sdk: 3.5.1 - name: ๐Ÿ“ฆ Install Dart Dependencies run: dart pub get diff --git a/.github/workflows/pull_request.yaml b/.github/workflows/pull_request.yaml index 4067c4b..2fb980b 100644 --- a/.github/workflows/pull_request.yaml +++ b/.github/workflows/pull_request.yaml @@ -16,7 +16,7 @@ jobs: - name: ๐ŸŽฏ Set up Dart SDK uses: dart-lang/setup-dart@v1 with: - sdk: 3.0.4 + sdk: 3.5.1 - name: ๐Ÿ“ฆ Install Dart Dependencies run: dart pub get diff --git a/.gitignore b/.gitignore index 3a85790..703a643 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,16 @@ # https://dart.dev/guides/libraries/private-files # Created by `dart pub` + +# 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/README.md b/README.md index bc54b2c..c56ebc8 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,45 @@ # Dart Hooks +>Automating the integration and management of Git Hooks in Dart projects. + +_Social buttons_ + +[![errorempire/darthooks](https://img.shields.io/static/v1?label=errorempire&message=DartHooks&color=yellow&logo=github)](https://github.com/errorempire/DartHooks "Go to GitHub repo") +[![stars - DartHooks](https://img.shields.io/github/stars/errorempire/DartHooks?style=social)](https://github.com/errorempire/DartHooks) +[![forks - DartHooks](https://img.shields.io/github/forks/errorempire/DartHooks?style=social)](https://github.com/errorempire/DartHooks) + +_Repo metadata_ + +[![GitHub tag](https://img.shields.io/github/tag/errorempire/DartHooks?include_prereleases=&sort=semver&color=yellow)](https://github.com/errorempire/DartHooks/releases/) +[![License](https://img.shields.io/badge/License-MIT-yellow)](#license) +[![issues - DartHooks](https://img.shields.io/github/issues/errorempire/DartHooks)](https://github.com/errorempire/DartHooks/issues) + - [Dart Hooks](#dart-hooks) - - [Installation](#installation) + - [Getting Started ๐Ÿš€](#getting-started-) + - [Usage ๐Ÿ”จ](#usage-) + - [Features ๐ŸŽ‰](#features-) + - [Running grinder tasks ๐Ÿงช](#running-grinder-tasks-) + - [License](#license) + +## Getting Started ๐Ÿš€ -Automating the integration and management of Git Hooks in Dart projects. +If the CLI application is available on [pub.dev](https://pub.dev), install via: + +```sh +dart pub add dart_hooks --dev +``` + +then: + +```sh +dart pub get +``` + +## Usage ๐Ÿ”จ Check out the [examples](https://github.com/errorempire/DartHooks/tree/main/example). +## Features ๐ŸŽ‰ Supported SDK: - **Dart** - ~~Flutter~~ @@ -15,12 +48,16 @@ Supported hooks: - `pre-commit` - `pre-push` -## Installation +## Running grinder tasks ๐Ÿงช -```sh -dart pub add dart_hooks --dev -``` +Check out the [Grinder](https://pub.dev/packages/grinder) documentation for installation. + +To see all available tasks: ```sh -dart pub get +grind --help ``` + +## License + +Released under [MIT](/LICENSE) by [@bcsizmadia](https://github.com/bcsizmadia). \ No newline at end of file diff --git a/bin/apply.dart b/bin/apply.dart deleted file mode 100644 index 76d044b..0000000 --- a/bin/apply.dart +++ /dev/null @@ -1,32 +0,0 @@ -import "dart:io"; - -import "package:dart_hooks/checks.dart"; -import "package:dart_hooks/command_executor.dart"; -import "package:dart_hooks/globals.dart"; - -void main() async { - await checkHooksDirectory(); - await removeHooks(); - await _applyHooks(); -} - -Future _applyHooks() async { - final Iterable keys = yamlMap.keys.cast(); - for (String key in keys) { - final String cmd = "${key.substring(0, 3)}_${key.substring(4)}"; - - await _updateGitHook(cmd, key); - } - logger.info("Hooks have been updated"); -} - -Future _updateGitHook(String cmd, String key) async { - final templateScript = """ -#!/usr/bin/env sh - -dart run dart_hooks:$cmd -"""; - - await File(hooksDirectory(key)).writeAsString(templateScript); - await Process.run("chmod", ["755", hooksDirectory(key)]); -} diff --git a/bin/dart_hooks.dart b/bin/dart_hooks.dart new file mode 100644 index 0000000..8878181 --- /dev/null +++ b/bin/dart_hooks.dart @@ -0,0 +1,11 @@ +import 'package:args/command_runner.dart'; +import 'package:dart_hooks/commands/exports.dart'; + +void main(List args) async { + CommandRunner('dart_hooks', + 'DartHooks - Automating the integration and management of Git Hooks in Dart projects') + ..addCommand(InitCommand()) + ..addCommand(ApplyCommand()) + ..addCommand(DestroyCommand()) + ..run(args); +} diff --git a/bin/destroy.dart b/bin/destroy.dart deleted file mode 100644 index fe6254f..0000000 --- a/bin/destroy.dart +++ /dev/null @@ -1,16 +0,0 @@ -import "package:dart_hooks/command_executor.dart"; -import "package:dart_hooks/globals.dart"; - -void main() async { - await removeHooks(); - await _removeConfiguration(); -} - -Future _removeConfiguration() async { - if (await config.exists()) { - await config.delete(); - logger.info("Configuration file has been deleted"); - } else { - logger.error("Configuration file does not exist"); - } -} diff --git a/bin/init.dart b/bin/init.dart deleted file mode 100644 index 64de5c3..0000000 --- a/bin/init.dart +++ /dev/null @@ -1,22 +0,0 @@ -import "dart:io"; - -import "package:dart_hooks/classes.dart"; -import "package:dart_hooks/globals.dart"; - -void main() async { - await _createConfigFile(); -} - -Future _createConfigFile() async { - const Stage data = Stage(stages: { - "pre-commit": Command(commands: { - "commands": ["dart --version", "flutter --version"] - }), - "pre-push": Command(commands: { - "commands": ["dart --version", "flutter --version"] - }) - }); - - File(configFile).writeAsStringSync(YamlConverter().toYaml(data)); - logger.info("Configuration file have been created"); -} diff --git a/bin/pre_commit.dart b/bin/pre_commit.dart deleted file mode 100644 index cd9cecc..0000000 --- a/bin/pre_commit.dart +++ /dev/null @@ -1,7 +0,0 @@ -import "package:dart_hooks/checks.dart"; -import "package:dart_hooks/command_executor.dart"; - -void main() async { - await checkConfigurationFile(); - await applyHooks("pre-commit"); -} diff --git a/bin/pre_push.dart b/bin/pre_push.dart deleted file mode 100644 index 2a9d7a6..0000000 --- a/bin/pre_push.dart +++ /dev/null @@ -1,7 +0,0 @@ -import "package:dart_hooks/checks.dart"; -import "package:dart_hooks/command_executor.dart"; - -void main() async { - await checkConfigurationFile(); - await applyHooks("pre-push"); -} diff --git a/example/README.md b/example/README.md index f741ef0..819f1f3 100644 --- a/example/README.md +++ b/example/README.md @@ -1,31 +1,66 @@ -# How to Use +# How to Use ๐Ÿคท -- [How to Use](#how-to-use) - - [Initialize the Config](#initialize-the-config) - - [Applying the Hooks](#applying-the-hooks) -- [Example Config File](#example-config-file) -- [How to Uninstall](#how-to-uninstall) +- [How to Use ๐Ÿคท](#how-to-use-) + - [How to install ๐Ÿ“ฆ](#how-to-install-) + - [Install the package via pub.dev:](#install-the-package-via-pubdev) + - [Development Installation ๐Ÿ› ๏ธ](#development-installation-๏ธ) + - [Initialize the Config โš™๏ธ](#initialize-the-config-๏ธ) + - [Applying the Hooks ๐Ÿช](#applying-the-hooks-) + - [Example Config File ๐Ÿ“](#example-config-file-) + - [How to Uninstall ๐Ÿ—‘๏ธ](#how-to-uninstall-๏ธ) +## How to install ๐Ÿ“ฆ -## Initialize the Config +### Install the package via [pub.dev](https://pub.dev/packages/dart_hooks): + +```sh +dart pub add dart_hooks --dev +``` + +### Development Installation ๐Ÿ› ๏ธ + +For development purposes, you can install the package from the local directory. + +Clone the repository and checkout the desired branch: + +```sh +git clone git@github.com:errorempire/DartHooks.git +``` + +Add the following to your `pubspec.yaml` file: + +```yaml +--- +dev_dependencies: + dart_hooks: + path: /DartHooks +``` + +Run the following command to install the package: + +```sh +dart pub get +``` + +## Initialize the Config โš™๏ธ Execute the following command to create the `dart_hooks.yaml` file: ```sh -dart run dart_hooks:init +dart run dart_hooks init ``` -## Applying the Hooks +## Applying the Hooks ๐Ÿช You should modify the list of hooks in the `dart_hooks.yaml` file (i.e., by adding or deleting a hook), you will need to apply the updated hook. -Note that if you are only changing the commands list, executing the following command is not necessary. +Note that if you are only changing the commands list, executing the following command is necessary. ```sh -dart run dart_hooks:apply +dart run dart_hooks apply ``` -# Example Config File +## Example Config File ๐Ÿ“ ```yaml --- @@ -35,20 +70,24 @@ pre-commit: pre-push: commands: - dart analyze +``` +OR: + +```yaml --- pre-commit: - commands: dart analyze + commands: [dart analyze] pre-push: - commands: dart analyze + commands: [dart analyze] ``` -# How to Uninstall +## How to Uninstall ๐Ÿ—‘๏ธ To remove the hooks and configuration file, execute the command below: ```sh -dart run dart_hooks:destroy +dart run dart_hooks destroy ``` Subsequently, you can remove the dependency by running: diff --git a/lib/checks.dart b/lib/checks.dart deleted file mode 100644 index bf52399..0000000 --- a/lib/checks.dart +++ /dev/null @@ -1,27 +0,0 @@ -import "dart:io"; - -import "globals.dart"; - -Future checkConfigurationFile() async { - if (!await File(configFile).exists()) { - logger.error("No configuration file found"); - exit(1); - } -} - -Future checkStagedChanges() async { - final ProcessResult result = await Process.run( - "git", ["diff-index", "--cached", "--quiet", "HEAD", "--"]); - - if (result.exitCode != 0) { - logger.error("No changes to be committed"); - exit(result.exitCode); - } -} - -Future checkHooksDirectory() async { - if (!await Directory(hooksDirectory()).exists()) { - logger.error("Directory does not appear to be a git repository"); - exit(1); - } -} diff --git a/lib/classes.dart b/lib/classes.dart deleted file mode 100644 index 05f078f..0000000 --- a/lib/classes.dart +++ /dev/null @@ -1,84 +0,0 @@ -class CommandResult { - final String command; - final String stdout; - final String stderr; - final int exitCode; - - const CommandResult({ - required this.command, - required this.stdout, - this.stderr = "", - this.exitCode = 0, - }); -} - -class Colorize { - static const _red = "\x1B[31m"; - static const _green = "\x1B[32m"; - static const _blue = "\x1B[34m"; - static const _yellow = "\x1B[33m"; - static const _reset = "\x1B[0m"; - - static String red(dynamic input) => "$_red$input$_reset"; - static String green(dynamic input) => "$_green$input$_reset"; - static String blue(dynamic input) => "$_blue$input$_reset"; - static String yellow(dynamic input) => "$_yellow$input$_reset"; -} - -class Logger { - void info(String message) { - print(""); - print(Colorize.blue("[ INFO ] ") + Colorize._reset + message); - print(""); - } - - void warning(String message) { - print(""); - print(Colorize.yellow("[ WARNING ] ") + Colorize._reset + message); - print(""); - } - - void error(String message) { - print(""); - print(Colorize.red("[ ERROR ] ") + Colorize._reset + message); - print(""); - } - - void cmdSuccess(String message, {String? type = ""}) { - if (type != "") type = "$type: "; - print(Colorize.blue(type) + Colorize.green(message)); - } - - void cmdError(String type, dynamic message) { - print(Colorize.blue("$type: ") + Colorize.red(message)); - } -} - -class Command { - final Map> commands; - const Command({required this.commands}); -} - -class Stage { - final Map stages; - const Stage({required this.stages}); -} - -class YamlConverter { - String toYaml(dynamic data, [int indentLevel = 0]) { - final indent = " " * indentLevel; - - if (data is Command) { - return data.commands.entries.map((e) { - final value = e.value.map((v) => "$indent - $v").join("\n"); - return "$indent${e.key}:\n$value"; - }).join("\n"); - } else if (data is Stage) { - return data.stages.entries.map((e) { - final value = toYaml(e.value, indentLevel + 2); - return "$indent${e.key}:\n$value"; - }).join("\n"); - } - throw ArgumentError("Unsupported data type for YAML conversion"); - } -} diff --git a/lib/command_executor.dart b/lib/command_executor.dart deleted file mode 100644 index 241c101..0000000 --- a/lib/command_executor.dart +++ /dev/null @@ -1,77 +0,0 @@ -import "dart:convert"; -import "dart:io"; - -import "classes.dart"; -import "globals.dart"; -import "package:yaml/yaml.dart"; - -Future startProcess(String command, String hookType) async { - // if (hookType == "pre-commit") await checkStagedChanges(); - - final List parts = command.split(" "); - final String executable = parts[0]; - final List arguments = parts.skip(1).toList(); - - final Process process = await Process.start(executable, arguments); - - return CommandResult( - command: command, - stdout: await process.stdout.transform(utf8.decoder).join(), - stderr: await process.stderr.transform(utf8.decoder).join(), - exitCode: await process.exitCode, - ); -} - -void printInfo(List results, String type) { - print(""); - print("--------------------------"); - print(" Dart Hooks "); - print("--------------------------"); - print(""); - - for (var result in results) { - if (result.stderr.isNotEmpty && result.exitCode != 0) { - printError(result, type); - } else { - printSuccess(result, type); - } - } -} - -void printError(CommandResult result, String type) { - logger - ..cmdError("Type", type) - ..cmdError("Command", result.command) - ..cmdError("Exit Code", result.exitCode) - ..cmdError("Error", result.stderr); - exit(result.exitCode); -} - -void printSuccess(CommandResult result, String type) { - logger - ..cmdSuccess(type, type: "Type") - ..cmdSuccess(result.command, type: "Command") - ..cmdSuccess(result.stdout); -} - -Future applyHooks(String hookType) async { - final Map yamlFile = - loadYaml(await File(configFile).readAsString()) as Map; - final commands = yamlFile[hookType]["commands"]; - - final List results = []; - - if (commands is String) results.add(await startProcess(commands, hookType)); - - if (commands is YamlList && commands.isNotEmpty) { - for (var command in commands) { - results.add(await startProcess(command as String, hookType)); - } - } - printInfo(results, hookType); -} - -Future removeHooks() async { - await Directory(hooksDirectory()).delete(recursive: true); - await Directory(hooksDirectory()).create(); -} diff --git a/lib/commands/_apply_command.dart b/lib/commands/_apply_command.dart new file mode 100644 index 0000000..d82e809 --- /dev/null +++ b/lib/commands/_apply_command.dart @@ -0,0 +1,27 @@ +import 'dart:async'; + +import 'package:args/command_runner.dart'; + +import '../mixins/exports.dart'; + +class ApplyCommand extends Command + with + Logger, + HooksConfigurationAnalyzer, + HooksConfigurationLoader, + HooksConfigurationWriter, + HooksConfigurationApplier { + @override + String get description => 'Apply hooks configuration'; + + @override + String get name => 'apply'; + + @override + FutureOr? run() async { + await checkGitDirectory(); + await applyHooks(); + // NOTE: Delete old hooks if removed from the configuration file + return ''; + } +} diff --git a/lib/commands/_destroy_command.dart b/lib/commands/_destroy_command.dart new file mode 100644 index 0000000..b874d64 --- /dev/null +++ b/lib/commands/_destroy_command.dart @@ -0,0 +1,26 @@ +import 'dart:async'; + +import 'package:args/command_runner.dart'; + +import '../mixins/exports.dart'; + +class DestroyCommand extends Command + with + Logger, + HooksConfigurationAnalyzer, + HooksConfigurationLoader, + HooksConfigurationDestroyer { + @override + String get description => 'Remove configuration'; + + @override + String get name => 'destroy'; + + @override + FutureOr? run() async { + await checkGitDirectory(); + await removeConfigurationFile(); + await removeAppliedHooks(); + return ''; + } +} diff --git a/lib/commands/_init_command.dart b/lib/commands/_init_command.dart new file mode 100644 index 0000000..0b6a814 --- /dev/null +++ b/lib/commands/_init_command.dart @@ -0,0 +1,25 @@ +import 'dart:async'; + +import 'package:args/command_runner.dart'; + +import '../mixins/exports.dart'; + +class InitCommand extends Command + with + Logger, + HooksConfigurationAnalyzer, + HooksConfigurationWriter, + HooksConfigurationLoader { + @override + String get description => 'Initialize configuration'; + + @override + String get name => 'init'; + + @override + FutureOr? run() async { + await checkGitDirectory(); + await createConfigurationFile(); + return ''; + } +} diff --git a/lib/commands/exports.dart b/lib/commands/exports.dart new file mode 100644 index 0000000..31038cb --- /dev/null +++ b/lib/commands/exports.dart @@ -0,0 +1,3 @@ +export "_apply_command.dart"; +export "_destroy_command.dart"; +export "_init_command.dart"; diff --git a/lib/extensions/colored_string.dart b/lib/extensions/colored_string.dart new file mode 100644 index 0000000..b38527b --- /dev/null +++ b/lib/extensions/colored_string.dart @@ -0,0 +1,14 @@ +extension ColoredStringExtension on String { + static const _red = "\x1B[31m"; + static const _green = "\x1B[32m"; + static const _blue = "\x1B[34m"; + static const _yellow = "\x1B[33m"; + static const _reset = "\x1B[0m"; + static const _magenta = "\x1B[35m"; + + String get blue => '$_blue$this$_reset'; + String get green => '$_green$this$_reset'; + String get magenta => '$_magenta$this$_reset'; + String get red => '$_red$this$_reset'; + String get yellow => '$_yellow$this$_reset'; +} diff --git a/lib/globals.dart b/lib/globals.dart deleted file mode 100644 index 31f4449..0000000 --- a/lib/globals.dart +++ /dev/null @@ -1,15 +0,0 @@ -import "dart:io"; - -import "classes.dart"; -import "package:yaml/yaml.dart"; - -const String configFile = "dart_hooks.yaml"; -final Map yamlMap = loadYaml(File(configFile).readAsStringSync()) as Map; - -File config = File(configFile); -Logger logger = Logger(); - -String hooksDirectory([String? key]) { - if (key == null) return ".git/hooks"; - return ".git/hooks/$key"; -} diff --git a/lib/mixins/_logger.dart b/lib/mixins/_logger.dart new file mode 100644 index 0000000..8703095 --- /dev/null +++ b/lib/mixins/_logger.dart @@ -0,0 +1,28 @@ +import '../extensions/colored_string.dart'; + +mixin Logger { + void error(String message) { + _log("ERROR".red, message); + } + + void info(String message) { + _log("INFO".blue, message); + } + + void question(String message) { + _log("QUESTION".magenta, message); + } + + void success(String message) { + _log("SUCCESS".green, message); + } + + void warning(String message) { + _log("WARNING".yellow, message); + } + + void _log(String? type, String message) { + final typeString = type != null ? "$type: " : ''; + print("$typeString$message"); + } +} diff --git a/lib/mixins/exports.dart b/lib/mixins/exports.dart new file mode 100644 index 0000000..f48293b --- /dev/null +++ b/lib/mixins/exports.dart @@ -0,0 +1,6 @@ +export "_logger.dart"; +export "hooks_configuration/_analyzer.dart"; +export "hooks_configuration/_applier.dart"; +export "hooks_configuration/_destroyer.dart"; +export "hooks_configuration/_loader.dart"; +export "hooks_configuration/_writer.dart"; diff --git a/lib/mixins/hooks_configuration/_analyzer.dart b/lib/mixins/hooks_configuration/_analyzer.dart new file mode 100644 index 0000000..63e7291 --- /dev/null +++ b/lib/mixins/hooks_configuration/_analyzer.dart @@ -0,0 +1,24 @@ +import 'dart:io'; + +import '../exports.dart'; + +mixin HooksConfigurationAnalyzer on Logger { + final File configFile = File("dart_hooks.yaml"); + + Future checkDartHooksConfigFile() async { + if (configFile.existsSync()) { + success("Configuration file already exists."); + return true; + } else { + warning("Configuration file does not exist."); + return false; + } + } + + Future checkGitDirectory() async { + if (!Directory(".git").existsSync()) { + warning("Directory does not appear to be a git repository."); + exit(1); + } + } +} diff --git a/lib/mixins/hooks_configuration/_applier.dart b/lib/mixins/hooks_configuration/_applier.dart new file mode 100644 index 0000000..79e9e8a --- /dev/null +++ b/lib/mixins/hooks_configuration/_applier.dart @@ -0,0 +1,30 @@ +import 'dart:io'; + +import '../exports.dart'; + +mixin HooksConfigurationApplier on Logger, HooksConfigurationLoader { + Future applyHooks() async { + if (await checkDartHooksConfigFile()) { + final List loadedConfig = await loadConfigurationFile(); + + for (var entry in loadedConfig) { + final String key = entry.keys.first as String; + final List commands = entry[key] as List; + + await File(hooksDirectory(key)).writeAsString(""" +#!/usr/bin/env sh + +${commands.join("\n")} +"""); + await Process.run("chmod", ["755", hooksDirectory(key)]); + + info("[$key] Hooks have been updated."); + } + } else { + error("Configuration file is empty or does not exist."); + } + } + + static String hooksDirectory([String? key]) => + key == null ? ".git/hooks" : ".git/hooks/$key"; +} diff --git a/lib/mixins/hooks_configuration/_destroyer.dart b/lib/mixins/hooks_configuration/_destroyer.dart new file mode 100644 index 0000000..7af709e --- /dev/null +++ b/lib/mixins/hooks_configuration/_destroyer.dart @@ -0,0 +1,38 @@ +import 'dart:io'; + +import '../../extensions/colored_string.dart'; +import '../exports.dart'; + +mixin HooksConfigurationDestroyer + on Logger, HooksConfigurationAnalyzer, HooksConfigurationLoader { + Future removeAppliedHooks() async { + final List loadedConfig = await loadConfigurationFile(); + + for (var hookCommands in loadedConfig) { + final hook = hookCommands.keys.first; + + final hookFile = File(".git/hooks/$hook"); + + if (hookFile.existsSync()) { + try { + hookFile.deleteSync(); + success("$hook hook file removed successfully."); + } catch (e) { + error("Failed to remove $hook hook file: $e"); + } + } else { + info("$hook hook file not found,${"skipping...".yellow}"); + } + } + } + + Future removeConfigurationFile() async { + if (await checkDartHooksConfigFile()) { + await configFile.delete().then((_) { + success("Configuration file has been deleted."); + }).catchError((e) { + error("Failed to delete configuration file: $e"); + }); + } + } +} diff --git a/lib/mixins/hooks_configuration/_loader.dart b/lib/mixins/hooks_configuration/_loader.dart new file mode 100644 index 0000000..5707b5e --- /dev/null +++ b/lib/mixins/hooks_configuration/_loader.dart @@ -0,0 +1,29 @@ +import 'dart:io'; + +import 'package:yaml/yaml.dart'; + +import '../exports.dart'; + +mixin HooksConfigurationLoader on HooksConfigurationAnalyzer { + Future loadConfigurationFile() async { + final List entries = []; + + final String yamlString = configFile.readAsStringSync(); + + if (yamlString.isEmpty) { + error("Configuration file is empty."); + exit(1); + } + + final dynamic yamlData = loadYaml(yamlString); + + if (yamlData is Map) { + yamlData.forEach((dynamic key, dynamic value) { + final String stageName = key as String; + final List commandsList = value["commands"] as List; + entries.add({stageName: commandsList}); + }); + } + return entries; + } +} diff --git a/lib/mixins/hooks_configuration/_writer.dart b/lib/mixins/hooks_configuration/_writer.dart new file mode 100644 index 0000000..65ce057 --- /dev/null +++ b/lib/mixins/hooks_configuration/_writer.dart @@ -0,0 +1,43 @@ +import 'dart:io'; + +import 'package:yaml_writer/yaml_writer.dart'; + +import '../exports.dart'; + +mixin HooksConfigurationWriter on Logger, HooksConfigurationAnalyzer { + Future createConfigurationFile() async { + if (await checkDartHooksConfigFile()) { + if (_promptOverwriteConfigFile()) { + info("Creating configuration file..."); + _writeYamlConfiguration(); + success("Configuration file has been created."); + } + } else { + info("Creating configuration file..."); + _writeYamlConfiguration(); + success("Configuration file has been created."); + } + } + + bool _promptOverwriteConfigFile() { + question("Do you want to overwrite it the configuration file? (y/n)"); + + if (stdin.readLineSync() == "y") return true; + + info("Configuration file has not been overwritten"); + return false; + } + + void _writeYamlConfiguration() { + final Map>> exampleConfig = { + "pre-commit": { + "commands": ["dart --version", "flutter --version"] + }, + "pre-push": { + "commands": ["dart --version", "flutter --version"] + } + }; + + configFile.writeAsStringSync(YamlWriter().write(exampleConfig)); + } +} diff --git a/pubspec.lock b/pubspec.lock deleted file mode 100644 index baf95f1..0000000 --- a/pubspec.lock +++ /dev/null @@ -1,77 +0,0 @@ -# Generated by pub -# See https://dart.dev/tools/pub/glossary#lockfile -packages: - collection: - dependency: transitive - description: - name: collection - sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf - url: "https://pub.dev" - source: hosted - version: "1.19.0" - json2yaml: - dependency: "direct main" - description: - name: json2yaml - sha256: da94630fbc56079426fdd167ae58373286f603371075b69bf46d848d63ba3e51 - url: "https://pub.dev" - source: hosted - version: "3.0.1" - lints: - dependency: "direct dev" - description: - name: lints - sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235" - url: "https://pub.dev" - source: hosted - version: "4.0.0" - meta: - dependency: transitive - description: - name: meta - sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 - url: "https://pub.dev" - source: hosted - version: "1.15.0" - path: - dependency: transitive - description: - name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" - url: "https://pub.dev" - source: hosted - version: "1.9.0" - source_span: - dependency: transitive - description: - name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" - url: "https://pub.dev" - source: hosted - version: "1.10.0" - string_scanner: - dependency: transitive - description: - name: string_scanner - sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" - url: "https://pub.dev" - source: hosted - version: "1.3.0" - term_glyph: - dependency: transitive - description: - name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 - 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.5.1 <4.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index f26a5bb..fb9a121 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -9,16 +9,12 @@ issue_tracker: https://github.com/errorempire/DartHooks/issues environment: sdk: ^3.5.1 -executables: - init: - pre_commit: - pre_push: - apply: - destroy: - dependencies: + args: ^2.5.0 json2yaml: ^3.0.1 yaml: ^3.1.2 + yaml_writer: ^2.0.0 dev_dependencies: lints: ^4.0.0 + grinder: ^0.9.5 diff --git a/tool/grind.dart b/tool/grind.dart new file mode 100644 index 0000000..b459dba --- /dev/null +++ b/tool/grind.dart @@ -0,0 +1,58 @@ +import 'dart:io'; + +import 'package:grinder/grinder.dart'; + +main(List args) => grind(args); + +@Task("Compile dart code to exe") +compile() { + Process.run("dart", ["compile", "exe", "bin/dart_hooks.dart"]).then((result) { + print(result.stdout); + print(result.stderr); + }); +} + +@Task("Get dependencies") +deps() => Pub.get(); + +@Task('Run gh action pull_request') +pullRequest() async { + try { + await Process.run("act", ["-l"]).then((_) async { + final process = await Process.start("act", ['pull_request']); + + process.stdout + .transform(const SystemEncoding().decoder) + .listen((data) => print(data)); + + process.stderr + .transform(const SystemEncoding().decoder) + .listen((data) => print('ERROR: $data')); + + print('Process exited with code: ${await process.exitCode}'); + }); + } catch (e) { + print("'act' not found, trying 'gh act'"); + } + + try { + await Process.run("gh", ["act", "-l"]).then((_) async { + final process = await Process.start("gh", ['act', 'pull_request']); + + process.stdout + .transform(const SystemEncoding().decoder) + .listen((data) => print(data)); + + process.stderr + .transform(const SystemEncoding().decoder) + .listen((data) => print('ERROR: $data')); + + print('Process exited with code: ${await process.exitCode}'); + }); + } catch (e) { + print("Please install act or gh act"); + } +} + +@Task("Update dependencies") +update() => Pub.upgrade();