plugin:install
Register a third-party dowel plugin into the consumer project. After adding a
package to pubspec.yaml and running flutter pub get, plugin:install wires
the plugin's DowelServiceProvider without requiring manual edits to
bin/dispatcher.dart or lib/app/_plugins.g.dart.
On this page
Basic Usage#
dart run dowel plugin:install <name>
<name> is the plugin's pubspec package name (e.g. magic_logger). The
package must be listed in pubspec.yaml and resolved via flutter pub get
before this command runs.
Pre-flight checks: the package must appear in pubspec.yaml, and
.dart_tool/package_config.json must contain the package name. Any failure
exits with code 1 before any write is attempted. A plugin without an
install.yaml also needs the scaffold from dart run dowel install
(lib/app/_plugins.g.dart); without it the command exits with code 1 and
tells you to run install first.
Synopsis#
Full signature as declared in
lib/src/commands/plugin_install_command.dart:
plugin:install
{name : Plugin pubspec package name (e.g. magic_logger)}
{--bootstrap-command= : Plugin install sub-command to chain after registration}
{--use-yaml-only : Fail if install.yaml not found instead of registering the plugin directly}
{--force : Bypass conflict detection (inherited from DowelInstallCommand)}
{--dry-run : Print staged ops without writing (inherited)}
{--non-interactive : Skip prompts, use defaults (inherited)}
{--no-bootstrap : Skip post-install bootstrap hint message (inherited)}
The four inherited flags come from DowelInstallCommand.baseFlags and are
available on every install command, not only plugin:install.
| Flag | Effect |
|---|---|
--bootstrap-command=cmd |
Overrides the post-registration bootstrap hint (e.g. logger:install). |
--use-yaml-only |
Error out when no install.yaml found; no direct registration (Mode 2). |
--force |
Bypass conflict detection; overwrite user-modified files. |
--dry-run |
Print every staged op without touching disk. |
--non-interactive |
Skip interactive prompts; use defaults. Required in non-TTY environments. |
--no-bootstrap |
Suppress the post-install bootstrap hint message. |
Two Routing Modes#
The core of handle dispatches through two code paths. Mode 1 wins when an
install.yaml manifest is present; Mode 2 runs otherwise, and needs the
scaffold barrel from dart run dowel install.
Mode 1: install.yaml Manifest Flow#
Condition: resolveInstallYaml(name) returns a non-null path. The resolver
checks <plugin_root>/install.yaml first, then <plugin_root>/assets/install.yaml
(resolveInstallYaml in lib/src/commands/plugin_install_command.dart).
ManifestParser.parseFile parses the YAML into an InstallManifest.
ManifestInstaller stages InstallOperation objects on an InstallTransaction.
InstallTransaction.commit writes every file to a .tmp path first; on full
success, all .tmp files are renamed atomically. Any .tmp write failure deletes
all staged temps and returns Error(rolledBack: true). On Success, the plugin
entry is written to .dowel/plugins.json and PluginsRefreshCommand regenerates
lib/app/_plugins.g.dart in-process.
Preferred authoring path: declarative, conflict-checked, dry-run-capable, and
partially reversible via plugin:uninstall (V1 limitations; see Reversibility).
Mode 2: Direct Registration#
Condition: No install.yaml found AND lib/app/_plugins.g.dart exists at
the project root. Without that barrel the command exits with code 1 and asks
you to run dart run dowel install first.
_registerDowelProvider runs directly: writes a PluginEntry to
.dowel/plugins.json and regenerates _plugins.g.dart. No wrapper edit
occurs. Covers plugins without a manifest. Naming convention: my_plugin maps
to package:my_plugin/cli.dart and class MyPluginDowelProvider. Pass
--use-yaml-only to error out instead of reaching this path.
Examples#
1. Manifest-based plugin install (Mode 1)#
flutter pub add magic_logger
dart run dowel plugin:install magic_logger
Output:
Success: applied 4 ops; record at .dowel/installed/magic_logger.json
Bootstrap with: dowel logger:install
2. Canonical scaffold, no install.yaml (Mode 2)#
flutter pub add awesome_plugin
dart run dowel plugin:install awesome_plugin
Output:
Registered "awesome_plugin" via the dowel scaffold (no install.yaml needed).
3. Dry-run preview#
dart run dowel plugin:install magic_logger --dry-run
Output:
DryRun: previewed 4 ops; no changes written
WriteFile lib/config/logger.dart
InjectImport lib/main.dart
InjectAfterPattern lib/app/providers/app_service_provider.dart
AddPubspecAsset assets/logging/
Idempotency#
Re-running plugin:install for the same plugin is safe in both modes.
PluginsRegistryFile.addPlugin replaces by name so .dowel/plugins.json
always contains exactly one entry per plugin. ConfigEditor.addImportToFile
and insertCodeAfterPattern skip injection when the target content is already
present.
All InstallTransaction file writes use .tmp + atomic rename: concurrent
readers never observe partial state. A mid-commit .tmp write failure deletes
all staged temps and returns Error(rolledBack: true) before any rename occurs.
Reversibility#
plugin:uninstall <name> is the complement to plugin:install. A Mode 1
manifest commit writes .dowel/installed/<name>.json listing every op and
content hash. V1 reversibility is partial: WriteFile ops are fully reversed
(tamper-check then delete); InjectImport, InjectBeforePattern, and
InjectAfterPattern ops are logged as [skipped] (no anchor-bracketed markers
in V1; operator must reverse by hand). Mode 2 installs write no record, so
plugin:uninstall cannot auto-reverse them. Full bidirectional
reversibility is planned for V1.1.
Related Documentation#
- plugin:uninstall: removes a plugin registered via
plugin:install, using the.dowel/installed/<name>.jsonrecord. - plugins:refresh: regenerates
lib/app/_plugins.g.dartfrom.dowel/plugins.jsonwhen the codegen barrel drifts out of sync. - install.yaml schema: full reference for the declarative manifest format consumed by Mode 1.