Skip to content

Commit

Permalink
feat!: move lifecycle hooks to command sections
Browse files Browse the repository at this point in the history
Fixes #460
  • Loading branch information
blaugold committed Feb 6, 2023
1 parent 60b39c9 commit 5bbb5c8
Show file tree
Hide file tree
Showing 14 changed files with 400 additions and 203 deletions.
10 changes: 6 additions & 4 deletions docs/commands/bootstrap.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -84,19 +84,21 @@ example:
melos bootstrap --diff="main"
```

## Adding a postbootstrap lifecycle script
## Adding a post bootstrap lifecycle script

Melos supports various command [lifecycle hooks](/configuration/scripts#hooks)
that can be defined in your `melos.yaml`.

For example, if you needed to run something such as a build runner automatically
after `melos bootstrap` is run, you can add a `postBootstrap` script:
after `melos bootstrap` is run, you can add a `post` hook script:

```yaml
# melos.yaml
# ...
scripts:
postBootstrap: dart pub run build_runner build
command:
bootstrap:
hooks:
post: dart pub run build_runner build
# ...
```

Expand Down
20 changes: 11 additions & 9 deletions docs/commands/clean.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,16 @@ Melos supports various command lifecycle hooks that can be defined in your
`melos.yaml`.

For example, if you needed to cleanup any generated files, e.g. from a build
runner, automatically after `melos clean` is ran, you can add a `postClean`
runner, automatically after `melos clean` is ran, you can add a `post` hook
script.

```yaml
# melos.yaml
# ...
scripts:
postClean: rm packages/foo/lib/src/generated_file.g.dart
command:
clean:
hooks:
post: rm packages/foo/lib/src/generated_file.g.dart
# ...
```

Expand All @@ -63,11 +65,11 @@ Another common use case is to run `flutter clean` in all Flutter packages;
```yaml
# melos.yaml
# ...
scripts:
# Runs "flutter clean" in all Flutter packages (`--flutter`)
# with a concurrency of 3 at a time (`--concurrency=3`).
postClean: >
melos exec --flutter --concurrency=3 -- "flutter clean"
command:
clean:
hooks:
# Runs "flutter clean" in all Flutter packages (`--flutter`)
# with a concurrency of 3 at a time (`--concurrency=3`).
post: melos exec --flutter --concurrency=3 -- "flutter clean"
# ...
```
15 changes: 12 additions & 3 deletions docs/commands/version.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,18 @@ melos version
The `version` command supports the following hooks in addition to the
[common hooks](/configuration/scripts#hooks):

- `preVersionCommit`: Runs before the version commit is created. Allows you to
make your own changes as part of versioning. You need to stage changes that
you make yourself.
- `preCommit`: Runs before the version commit is created. Allows you to make
your own changes as part of versioning. You need to stage changes that you
make yourself.

```yaml
command:
version:
hooks:
preCommit: |
# Make changes to the version commit here.
# You need to stage changes that you make yourself.
```
## --prerelease (-p)
Expand Down
19 changes: 11 additions & 8 deletions docs/configuration/scripts.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ description:

# Workspace Scripts

Workspace scripts can be executed with `melos run` or will be executed as hooks
before/after some specific Melos commands.
Workspace scripts can be executed with `melos run` or will be executed as
lifecycle hooks of some specific Melos commands.

With the simple syntax, only the name of the script and the command to execute
needs to be specified:
Expand Down Expand Up @@ -138,14 +138,17 @@ the script in all packages, use the `--no-select` option.
Certain Melos commands support running scripts before and after the command is
executed, as well as at other interstring points in the command's execution.

All commands that support hooks at least support `pre` and `post` hook. Pre
hooks have the captialized name of the command prefixed with `pre`. Post hooks
have the captialized name of the command prefixed with `post`:
All commands that support hooks, support at least the `pre` and `post` hook.

Hooks are configured in the `hooks` section of a command's configuration in the
`melos.yaml` file.

```yaml
scripts:
preBootstrap: echo `bootstrap command is running...`
postBootstrap: echo `bootstrap command is done`
command:
bootstrap:
hooks:
pre: echo `bootstrap command is running...`
post: echo `bootstrap command is done`
```
Currently, the following Melos commands support hooks:
Expand Down
5 changes: 3 additions & 2 deletions docs/guides/automated-releases.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,9 @@ This behavior can be disabled by passing `--no-git-tag-version`.
#### Hook

After updating `pubspec.yaml` files and changelogs but before creating the
version commit, the `preVersionCommit` script in `melos.yaml` is executed. This
allows you to run custom code before the version commit is created.
version commit, the `command/version/hooks/preCommit` script in `melos.yaml` is
executed. This allows you to run custom code before the version commit is
created.

Note that you have to stage changes that you make in the hook and want to have
included in the version commit yourself.
Expand Down
32 changes: 17 additions & 15 deletions docs/guides/migrations.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -80,24 +80,26 @@ The following options are now enabled by default:

### Lifecycle hooks

The lifecycle `pre` and `post` hooks have been renamed. Previously, the `pre`
hook had the same name as the command, and the `post` hook had the name of the
command with `post` appended. Now, both the `pre` and `post` hook names are the
capitalized name of the command, prefixed with `pre` or `post` respectively.

- `bootstrap` -> `preBootstrap`
- `postbootstrap` -> `postBootstrap`
- `clean` -> `preClean`
- `postclean` -> `postClean`
- `version` -> `preVersion`
- `postversion` -> `postVersion`
Lifecycle hooks have been moved to the respective command configurations.
Previously, hooks were defined in `scripts`. The `pre` hook had the same name as
the command, and the `post` hook had the name of the command with `post`
appended. Now, hooks are defined in `command/<name>/hooks`.

- `bootstrap` -> `command/bootstrap/hooks/pre`
- `postbootstrap` -> `command/bootstrap/hooks/post`
- `clean` -> `command/clean/hooks/pre`
- `postclean` -> `command/clean/hooks/post`
- `version` -> `command/version/hooks/preCommit`
- `postversion` -> `command/version/hooks/post`

#### Version command hooks

The `version` hook was previously executed during versioning, before the version
commit was created. The `preVersion` hook is now executed before the `version`
command, like for all other commands.
commit was created. The `pre` hook is now executed before the `version` command,
like for all other commands.

The new `preVersionCommmit` hook is executed before the version commit is
created and is the equivalent of the previous `version` hook.
The new `preCommit` hook is executed before the version commit is created and is
the equivalent of the previous `version` hook.

### Package filters

Expand Down
8 changes: 4 additions & 4 deletions melos.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ command:
branch: main
# Additionally build a changelog at the root of the workspace.
workspaceChangelog: true
hooks:
preCommit: |
dart run scripts/generate_version.dart && \
git add packages/melos/lib/version.g.dart
ide:
intellij: true
Expand Down Expand Up @@ -42,10 +46,6 @@ scripts:
env:
MELOS_TEST: true

preVersionCommit: |
dart run scripts/generate_version.dart && \
git add packages/melos/lib/version.g.dart
activate:
description: Activate the local version of melos for development.
run:
Expand Down
2 changes: 1 addition & 1 deletion packages/melos/lib/src/commands/bootstrap.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ mixin _BootstrapMixin on _CleanMixin {

return _runLifecycle(
workspace,
ScriptLifecycle.bootstrap,
_CommandWithLifecycle.bootstrap,
() async {
final pubCommandForLogging = [
...pubCommandExecArgs(
Expand Down
2 changes: 1 addition & 1 deletion packages/melos/lib/src/commands/clean.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ mixin _CleanMixin on _Melos {

return _runLifecycle(
workspace,
ScriptLifecycle.clean,
_CommandWithLifecycle.clean,
() async {
logger.log('Cleaning workspace...');

Expand Down
24 changes: 11 additions & 13 deletions packages/melos/lib/src/commands/run.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,22 @@ mixin _RunMixin on _Melos {
);
}

final scriptSourceCode =
targetStyle(script.effectiveRun.replaceAll('\n', ''));

logger.command('melos run ${script.name}');
logger.child(scriptSourceCode).child(runningLabel).newLine();

final exitCode = await _runScript(
script,
config,
global: global,
noSelect: noSelect,
extraArgs: extraArgs,
);

logger.newLine();
logger.command('melos run ${script.name}');
final resultLogger =
logger.child(targetStyle(script.effectiveRun.replaceAll('\n', '')));
final resultLogger = logger.child(scriptSourceCode);

if (exitCode != 0) {
resultLogger.child(failedLabel);
Expand Down Expand Up @@ -68,11 +72,11 @@ mixin _RunMixin on _Melos {
return scripts[selectedScriptIndex].name;
}

@override
Future<int> _runScript(
Script script,
MelosWorkspaceConfig config, {
Script script, {
GlobalOptions? global,
required bool noSelect,
bool noSelect = false,
List<String> extraArgs = const [],
}) async {
final workspace = await MelosWorkspace.fromConfig(
Expand Down Expand Up @@ -151,12 +155,6 @@ mixin _RunMixin on _Melos {
final scriptSource = script.effectiveRun;
final scriptParts = scriptSource.split(' ');

logger.command('melos run ${script.name}');
logger
.child(targetStyle(scriptSource.replaceAll('\n', '')))
.child(runningLabel)
.newLine();

return startCommand(
scriptParts..addAll(extraArgs),
logger: logger,
Expand Down Expand Up @@ -216,6 +214,6 @@ class ScriptException implements MelosException {

@override
String toString() {
return 'ScriptException: The script $scriptName failed to execute';
return 'ScriptException: The script $scriptName failed to execute.';
}
}
54 changes: 41 additions & 13 deletions packages/melos/lib/src/commands/runner.dart
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ part 'publish.dart';
part 'run.dart';
part 'version.dart';

enum ScriptLifecycle {
enum _CommandWithLifecycle {
bootstrap,
clean,
version,
Expand Down Expand Up @@ -108,36 +108,64 @@ abstract class _Melos {

Future<void> _runLifecycle(
MelosWorkspace workspace,
ScriptLifecycle lifecycle,
_CommandWithLifecycle command,
FutureOr<void> Function() cb,
) async {
final hooks = workspace.config.scripts.lifecycleHooksFor(lifecycle);
final hooks = workspace.config.commands.lifecycleHooksFor(command);
final preScript = hooks.pre;
final postScript = hooks.post;

if (preScript != null) {
logger
..log('Running ${preScript.name} lifecycle script...')
..newLine();

await run(scriptName: preScript.name);
await _runLifecycleScript(preScript, command: command);
logger.newLine();
}

try {
await cb();
} finally {
if (postScript != null) {
logger
..log('Running ${postScript.name} lifecycle script...')
..newLine();

await run(scriptName: postScript.name);
logger.newLine();
await _runLifecycleScript(postScript, command: command);
}
}
}

Future<void> _runLifecycleScript(
Script script, {
required _CommandWithLifecycle command,
}) async {
logger
..command('melos ${command.name} [${script.name}]')
..child(targetStyle(script.effectiveRun.replaceAll('\n', '')))
..newLine();

final exitCode = await _runScript(script, noSelect: true);

if (exitCode != 0) {
throw ScriptException._('command/${command.name}/hooks/${script.name}');
}
}

Future<void> run({
String? scriptName,
bool noSelect = false,
});

Future<int> _runScript(
Script script, {
bool noSelect = false,
});
}

extension on CommandConfigs {
LifecycleHooks lifecycleHooksFor(_CommandWithLifecycle command) {
switch (command) {
case _CommandWithLifecycle.bootstrap:
return bootstrap.hooks;
case _CommandWithLifecycle.clean:
return clean.hooks;
case _CommandWithLifecycle.version:
return version.hooks;
}
}
}
Loading

0 comments on commit 5bbb5c8

Please sign in to comment.