Skip to main content
info

Documentation is currently for moon v2 and latest proto. Documentation for moon v1 has been frozen and can be found here.

VCS hooks

v1.9.0

VCS hooks (most popular with Git) are a mechanism for running scripts at pre-defined phases in the VCS's lifecycle, most commonly pre-commit, pre-push, or pre-merge. With moon, we provide a built-in solution for managing hooks, and syncing them across developers and machines.

Defining hooks

Hooks can be configured with the vcs.hooks setting in .moon/workspace.*. This setting requires a map of hook names (in the format required by your VCS), to a list of arbitrary commands to run within the hook script. Commands are used as-is and are not formatted or interpolated in any way.

To demonstrate this, let's configure a pre-commit hook that runs a moon lint task for affected projects, and also verifies that the commit message abides by a specified format (using pre-commit and the commitlint hook, for example).

.moon/workspace.yml
vcs:
hooks:
pre-commit:
- 'pre-commit run'
- 'moon run :lint --affected'
commit-msg:
- 'pre-commit run --hook-stage commit-msg --commit-msg-filename $ARG1'
info

All commands are executed from the repository root (not moon's workspace root) and must exist on PATH. If moon is installed locally, you can execute it using a repository relative path, like ./node_modules/@moonrepo/cli/moon.

Accessing argumentsv1.40.3

To ease interoperability between operating systems and terminal shells, we set passed arguments as environment variables.

In your hook commands, you can access these arguments using the $ARG<n> format, where <n> is the 1-indexed position of the argument. For example, to access the first argument, you would use $ARG1, the second argument would be $ARG2, and so on. $ARG0 exists and points to the current script.

Enabling hooks

Hooks are a divisive subject, as some developers love them, and others hate them. Finding a viable solution for everyone can be difficult, so with moon, we opted to support 2 distinct options, but only 1 can be used at a time. Choose the option that works best for your project, team, or company!

Automatically for everyone

If you'd like hooks to be enforced for every contributor of the repository, then simply enable the vcs.sync setting in .moon/workspace.*. This will automatically generate hook scripts and link them with the local VCS checkout, everytime a task is ran.

.moon/workspace.yml
vcs:
hooks: [...]
sync: true
caution

Automatically activating hooks on everyone's computer is considered a sensitive action, because it enables the execution of arbitrary code on the computers of the team members. Be careful about the hook commands you define in the .moon/workspace.* file.

Manually by each developer

If you'd prefer contributors to have a choice in whether or not they want to use hooks, then simply do nothing, and guide them to run the moon sync hooks command. This command will generate hook scripts and link them with the local VCS checkout.

$ moon sync hooks

Disabling hooks

If you choose to stop using hooks, you'll need to cleanup the previously generated hook scripts, and reset the VCS checkout. To start, disable the vcs.sync setting.

.moon/workspace.yml
vcs:
sync: false

And then run the following command, which will delete files from your local filesystem. Every developer that is using hooks will need to run this command.

$ moon sync hooks --clean

How it works

When hooks are enabled, the following processes will take place.

  1. The configured hooks will be generated as individual script files in the .moon/hooks directory. Whether or not you commit or ignore these script files is your choice. They are written to the .moon directory so that they can be reviewed, audited, and easily tested, but are required.

  2. We then sync these generated hook scripts with the current VCS. For Git, we set core.hooksPath to point to the .moon/hooks directory, which tells Git to look for hook scripts in that directory instead of the default .git/hooks directory.

info

The .moon/hooks scripts are generated as Bash scripts (use a .sh file extension) on Unix, and PowerShell scripts (use a .ps1 file extension) on Windows.

Examples

Pre-commit

A perfect use case for the pre-commit hook is to check linting and formatting of the files being committed. If either of these tasks fail, the commit will abort until they are fixed. Be sure to use the --affected option so that we only run on changed projects!

.moon/workspace.yml
vcs:
hooks:
pre-commit:
- 'moon run :lint :format --affected --status=staged'

By default this will run on the entire project (all files). If you want to filter it to only the changed files, enable the affectedFiles task option.