Sidekick CLIs allow you to automate repetitive tasks in your Flutter/Dart projects. It is handy for mono-repositories where developers work across multiple packages. For example, calling pub get
for all packages or releasing your app to Firebase in a language you’re familiar with: Dart!
Creating a sidekick CLI
The initial setup is done with the global sidekick
CLI. Install it on your system with pub global activate
.
dart pub global activate sidekick
Then use the sidekick init
command to generate the CLI for your Flutter app by following the instructions.
sidekick init
Only the person generating the CLI for the first time requires the global sidekick
package. Once generated, use the generated CLI.
What makes a sidekick CLI
The following diagram shows the layout of a sidekick CLI called amanda
. (The name is up to you).
Every sidekick CLI has two key components:
entryPoint is a shell script that is used to execute the CLI. It also marks the root of the project projectRoot
. It’s usually the root of your repository.
sidekickPackage contains the complete source code of the Dart CLI, including shell tooling to download dependencies automatically and the embedded Dart runtime.
The sidekickPackage can be located anywhere inside projectRoot
.
Access the fixed points
The projectRoot
and sidekickPackage
location together with the cliName
is everything required to generate the CLI. Those locations are fixed points and can be accessed in your CLI via SidekickContext
. All three are guaranteed to always exist.
// The directory where the entryPoint is located
SidekickContext.projectRoot;
// The location of the entryPoint, the shell script
// that is used to execute the cli.
SidekickContext.entryPoint;
// Source code and tooling of the sidekick CLI
SidekickContext.sidekickPackage;
// The name of the CLI
SidekickContext.cliName;
Inside the sidekick CLI package
The sidekickPackage
(amanda_sidekick
) is a normal Dart package with a pubspec.yaml
for dependencies and the full source code in lib/
. The CLI can be executed and debugged like any other CLI with dart bin/main.dart
.
Sidekick CLIs are self-executable
What makes a sidekick CLI special is the bash tooling in /tool
which allows the CLI to be self-executable when executed with the entryPoint
.
When called via entryPoint
the bash scripts do the following:
- Download a Dart runtime with the version defined in
pubspec.yaml
(environment.sdk (lowerBound)
) tobuild/cache/dart-sdk
. This is the embedded Dart runtime. - Checks for changes in the package to eventually recompile the
.exe
- Compiles the CLI to a native executable (
.exe
) for the given system (saved in/build/cli.exe
)
The embedded Dart runtime
Sidekick downloads its own Dart runtime where which is used to compile the sidekick CLI. The SDKs of your various sidekick CLIs are cached in ~/.dart/sdk/cache/<dart-version>
.
Which Dart runtime is used is read from the lower-bound environment.sdk
in pubspec.yaml
.
environment:
sdk: ">=2.19.0 <3.0.0"
This configuration will download the Dart SDK 2.19.
For now, the entire Dart SDK is downloaded. But that might change in the future to reduce download sizes. Do not trust
The embedded Dart SDK is never accessible to your CLI. It is not used for the
<sidekick-cli> dart
command or any other command.The
dart
command reads the Dart SDK fromflutterSdkPath
ordartSdkPath
which might, or might not link to your local Dart SDK (systemDartSdkPath()
)
Add new commands
New Commands can be added with plugins or manually. Check out our favorite sidekick plugins to see what’s possible.
For plugins use amanda sidekick plugins install <plugin>
To create your own Command create a new file echo_text_command.dart
in lib/src/commands/
.
class EchoTextCommand extends Command {
@override
String get name => 'echo-text';
@override
String get description => 'Echos the text';
EchoTextCommand() {
argParser.addOption('text');
}
@override
Future<void> run() async {
final cliName = argResults!['text'] as String?;
print('echo $cliName');
}
}
Then register the command at the runner in the registry lib/amanda_sidekick.dart
.
final runner = initializeSidekick();
runner
//.. more commands
..addCommand(FlutterCommand())
..addCommand(EchoTextCommand()); // <-- Register your own command
<cli> sidekick update
To make sure that you’re always using the latest and greatest run amanda sidekick update
. Sidekick will automatically check for new sidekick_core
updates and will automatically migrate your code to match the new version.
This migration mechanism is especially useful for changes in the bash scripts to guarantee compatibility with all operating systems.
For feedback, issues and feature requests head to the sidekick repository