Skip to content

Commit

Permalink
WIP release 3.0.0:
Browse files Browse the repository at this point in the history
- Add test coverage
- Code cleanup
- Fix init and create project task
  • Loading branch information
buijs-dev committed May 4, 2024
1 parent e2834ef commit 4bc04b5
Show file tree
Hide file tree
Showing 17 changed files with 357 additions and 83 deletions.
8 changes: 5 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
## 2.1.0
* Uses klutter_ui 1.1.0.
* Uses Klutter Gradle v2024.1.1.beta with support for protobuf.
## 3.0.0
* Use klutter_ui 1.1.0.
* Use Klutter Gradle v2024.1.1.beta with support for protobuf.
* Move post-build tasks in root/platform build.gradle.kts to gradle plugin.
* Remove producer and consumer scripts.
* Add kradle script.

## 2.0.0
* Uses AGP 8.0.2 in projects.
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ Add the Klutter library to dependencies in the pubspec.yaml:

```yaml
dev_dependencies:
klutter: ^2.1.0
klutter: ^3.0.0
```
Then run:
Expand Down
1 change: 1 addition & 0 deletions lib/src/cli/cli.dart
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import "../common/utilities.dart";
import "context.dart";
import "task_service.dart" as service;

export "flutter.dart";
export "option.dart";
export "task.dart";
export "task_add.dart";
Expand Down
20 changes: 20 additions & 0 deletions lib/src/cli/context.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,26 @@ class Context {
final Map<TaskOption, String> taskOptions;
}

/// Copy utilities for [Context] objects.
extension CopyContext on Context {
/// Create a new [Context] instance where existing
/// fields are overwritten with the given data.
Context copyWith({
Directory? workingDirectory,
TaskName? taskName,
Map<TaskOption, String> taskOptions = const {},
}) {
this.taskOptions.forEach((key, value) {
taskOptions.putIfAbsent(key, () => value);
});

return Context(
workingDirectory: workingDirectory ?? this.workingDirectory,
taskName: taskName ?? this.taskName,
taskOptions: taskOptions);
}
}

/// Parse user input and return the [Context] or null if input is invalid.
Context? toContextOrNull(Directory workingDirectory, List<String> arguments) {
if (arguments.isEmpty) {
Expand Down
48 changes: 48 additions & 0 deletions lib/src/cli/flutter.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright (c) 2021 - 2024 Buijs Software
//
// 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.

import "dart:io";

import "../common/common.dart";

/// Create a new flutter project.
Future<Directory> createFlutterProjectOrThrow({
required Executor executor,
required String pathToFlutter,
required String pathToRoot,
required String name,
required String group,
}) async {
executor
..executable = pathToFlutter
..workingDirectory = Directory(pathToRoot)
..arguments = [
"create",
name,
"--org",
group,
"--template=plugin",
"--platforms=android,ios",
]
..run();

return Directory(pathToRoot.normalize).resolveFolder(name)
..verifyFolderExists;
}
69 changes: 57 additions & 12 deletions lib/src/cli/option.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2021 - 2023 Buijs Software
// Copyright (c) 2021 - 2024 Buijs Software
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
Expand All @@ -23,52 +23,73 @@ import "dart:io";
import "../common/common.dart";
import "context.dart";

/// An option containing a String value which should be declared by the user.
///
/// For this option no defaults can be set.
class RequiredUserInputString extends RequiredUserInput<String> {
/// Construct a new [RequiredUserInputString] instance.
const RequiredUserInputString(this.description);

@override
final String description;

@override
String convertOrThrow(String value) => value;
}

/// An option containing a bool value which has a default value.
class UserInputBooleanOrDefault extends UserInputOrDefault<bool> {
/// Construct a new [RequiredUserInputString] instance.
const UserInputBooleanOrDefault(this.description, super.defaultValue);

@override
final String description;

bool convertOrThrow(String value) {
switch (value.trim().toUpperCase()) {
case "TRUE":
return true;
case "FALSE":
return false;
default:
throw InputException("expected a bool value but received: $value");
}
}
static const _map = {"TRUE": true, "FALSE": false};

@override
bool convertOrThrow(String value) =>
_map[value.trim().toUpperCase()] ??
(throw InputException("expected a bool value but received: $value"));
}

/// An option containing a value which should be declared by the user.
///
/// For this option no defaults can be set.
abstract class RequiredUserInput<T> extends Input<T> {
/// Construct a new [RequiredUserInput] instance.
const RequiredUserInput();
}

/// An option containing a [T] value which has a default value.
abstract class UserInputOrDefault<T> extends Input<T> {
/// Construct a new [UserInputOrDefault] instance.
const UserInputOrDefault(this.defaultValue);

/// Default value [T] which is returned when user did not specify the option.
final T defaultValue;

/// Get the [T] defaultValue as String.
///
/// Can be overwritten by extending class to apply custom serialization.
String get defaultValueToString => defaultValue.toString();
}

/// An option containing a [T] value.
abstract class Input<T> {
/// Construct a new [Input] instance.
const Input();

/// Throws [InputException].
/// Convert a String value to type [T] or throw [InputException].
T convertOrThrow(String value);

/// A description for this option.
String get description;
}

/// Exception indicating an error in the user input.
///
/// This exception occurs when the user input can not be mapped to [Input].
class InputException implements Exception {
/// Create instance of [InputException] with a message [cause].
InputException(this.cause);
Expand Down Expand Up @@ -98,7 +119,11 @@ class RootDirectoryInput
String get description => "the klutter project root directory";
}

/// Option specifying the flutter version to use.
///
/// Containing a [VerifiedFlutterVersion] value which defaults to flutter 3.10.6.
class FlutterVersionOption extends UserInputOrDefault<VerifiedFlutterVersion> {
/// Construct a new [FlutterVersionOption] instance with default value of 3.10.6.
FlutterVersionOption() : super("3.10.6".verifyFlutterVersion!);

@override
Expand All @@ -115,20 +140,30 @@ class FlutterVersionOption extends UserInputOrDefault<VerifiedFlutterVersion> {
"invalid flutter version (supported versions are: ${supportedFlutterVersions.keys}): $value"));
}

/// Option to allow overwriting existing entities which defaults to false.
class OverwriteOption extends UserInputBooleanOrDefault {
/// Construct a new [OverwriteOption] instance.
OverwriteOption()
: super("overwrite existing distribution when found", false);
}

/// Option to skip download actions which defaults to false.
///
/// This options is used for testing download actions.
class DryRunOption extends UserInputBooleanOrDefault {
/// Construct a new [DryRunOption] instance.
DryRunOption() : super("skip downloading of libraries", false);
}

/// Option to specify a klutter library name which has no default.
class LibraryName extends RequiredUserInputString {
/// Construct a new [LibraryName] instance.
const LibraryName() : super("name of the library to add");
}

/// Option to specify squint_json version which defaults to [squintPubVersion].
class SquintPubVersion extends UserInputOrDefault<String> {
/// Construct a new [SquintPubVersion] instance with default value [squintPubVersion].
const SquintPubVersion() : super(squintPubVersion);

@override
Expand All @@ -138,7 +173,9 @@ class SquintPubVersion extends UserInputOrDefault<String> {
String convertOrThrow(String value) => value;
}

/// Option to specify klutter (dart) version which defaults to [klutterPubVersion].
class KlutterPubVersion extends UserInputOrDefault<String> {
/// Construct a new [KlutterPubVersion] instance with default value [klutterPubVersion].
const KlutterPubVersion() : super(klutterPubVersion);

@override
Expand All @@ -148,7 +185,9 @@ class KlutterPubVersion extends UserInputOrDefault<String> {
String convertOrThrow(String value) => value;
}

/// Option to specify klutter_ui (dart) version which defaults to [klutterUIPubVersion].
class KlutteruiPubVersion extends UserInputOrDefault<String> {
/// Construct a new [KlutteruiPubVersion] instance with default value [klutterUIPubVersion].
const KlutteruiPubVersion() : super(klutterUIPubVersion);

@override
Expand All @@ -158,7 +197,9 @@ class KlutteruiPubVersion extends UserInputOrDefault<String> {
String convertOrThrow(String value) => value;
}

/// Option to specify klutter (gradle) version which defaults to [klutterGradleVersion].
class KlutterGradleVersionOption extends UserInputOrDefault<String> {
/// Construct a new [KlutterGradleVersionOption] instance with default value [klutterGradleVersion].
const KlutterGradleVersionOption() : super(klutterGradleVersion);

@override
Expand All @@ -170,7 +211,9 @@ class KlutterGradleVersionOption extends UserInputOrDefault<String> {
(throw InputException("not a valid bom version: $value"));
}

/// Option to specify plugin_name which defaults to my_plugin.
class PluginNameOption extends UserInputOrDefault<String> {
/// Construct a new [PluginNameOption] instance with default value my_plugin.
const PluginNameOption() : super("my_plugin");

@override
Expand All @@ -187,7 +230,9 @@ class PluginNameOption extends UserInputOrDefault<String> {
}
}

/// Option to specify group_name which defaults to dev.buijs.klutter.example.
class GroupNameOption extends UserInputOrDefault<String> {
/// Construct a new [GroupNameOption] instance with default value dev.buijs.klutter.example.
GroupNameOption() : super("dev.buijs.klutter.example");

@override
Expand Down
12 changes: 11 additions & 1 deletion lib/src/cli/task.dart
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,13 @@ enum TaskName {
create,

/// Build a klutter project.
build
build,

/// Run flutter commands using the cached flutter distribution.
flutter,

/// Run gradle commands using the local gradlew distribution.
gradle,
}

/// Convert a String value to a [TaskName].
Expand All @@ -159,6 +165,10 @@ extension TaskNameParser on String? {
return TaskName.get;
case "INIT":
return TaskName.init;
case "GRADLE":
return TaskName.gradle;
case "FLUTTER":
return TaskName.flutter;
default:
return null;
}
Expand Down
1 change: 0 additions & 1 deletion lib/src/cli/task_build.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ class BuildProject extends Task {
BuildProject({Executor? executor})
: super(TaskName.build, {
TaskOption.root: RootDirectoryInput(),
// TODO add skiptest option
}) {
_executor = executor ?? Executor();
}
Expand Down
Loading

0 comments on commit 4bc04b5

Please sign in to comment.