sapling/hooks
Stanislau Hlebik 6ddf4d16eb mononoke: revert D14143772
Summary:
Running hook test locally results in flaky errors - the new output just
disappears.

This is the error I'm getting

```
ERROR: test-hook-deny-files.t output changed
 --- test-hook-conflict-markers.t
+++ test-hook-conflict-markers.t.err
@@ -70,18 +70,6 @@
   searching for changes
   remote: Results of running hooks
   remote:   948f2cea 1
-  remote:     FAILED file hooks:
-  remote:       - conflict_markers on 1: Conflict markers were found in file '1'
-  remote:     no changeset hooks to run
-  remote:     1 of 1 file hooks failed
-  remote:     REJECTED
-  remote: Command failed
-  remote:   Error:
-  remote:     hooks failed
-  remote:   Root cause:
-  remote:     ErrorMessage {
-  remote:         msg: "hooks failed"
-  remote:     }
   abort: stream ended unexpectedly (got 0 bytes, expected 4)
   [255]

```

Apparently the output is not flushed properly

Reviewed By: HarveyHunt

Differential Revision: D14163914

fbshipit-source-id: b2acda3514ada0f720444f32f93997e148eed5f6
2019-02-21 05:47:31 -08:00
..
content-stores/src hooks: use lower level API of blobrepo to access file type and size without fetching content 2019-02-12 04:37:39 -08:00
hooks-tests/src hooks: enable configuring hooks for all bookmarks matching a given regex 2019-02-13 09:07:18 -08:00
src mononoke: revert D14143772 2019-02-21 05:47:31 -08:00
Cargo.toml Add Cargo.toml files to crates. (#7) 2018-07-09 19:52:27 -07:00
README.md hooks: pass configs down to Lua hooks via ctx 2019-02-01 05:52:20 -08:00

Commit Hooks

This document explains how to author and configure commit hooks for Mononoke.

Overview

Two types of hooks are supported (note that both are pre-commit hooks):

  • PerChangeset runs once per changeset.
  • PerAddedOrModifiedFile runs once per added or modified file per changeset.

Individual hooks are declared as follows in the server.toml file that contains the configuration for a repo:

[[hooks]]
# The name of your hook. Must be unique within this file.
name="my_hook"

# Path to the Lua script that implements your hook.
# Relative paths are resolved relative to the root of the config repo.
path="my_hook.lua"

# This must be either "PerChangeset" or "PerAddedOrModifiedFile".
hook_type="PerAddedOrModifiedFile"

# The following property is optional.
# If specified and there is a line in the commit message that matches
# the specified value, then the hook is not run for that commit.
bypass_commit_string="@ignore-conflict-markers"

# The following property is optional.
# If specified, the hook can be bypassed by specifying `--pushvars KEY=VALUE`
# when running `hg push`.
bypass_pushvar="KEY=VALUE"

Enabled hooks must be declared in [[bookmark.hooks]]:

[[bookmarks.hooks]]
# Note how this matches the "name" property in [[hooks]].
hook_name="my_hook"

Here is an example of a hook to prevent Perl files from being checked in:

-- file my_hook.lua

function hook (ctx)
  if ctx.file.path:match("\\.perl$") then
    return false, "Boo: scary Perl!"
  else
    return true
end

Lua API

Your hook must be implemented in Lua. The entry point to your hook must be a global function named hook(). This function should return up to three values:

  • success (boolean) This should be true if the hook was satisfied and false if it was not. If this is true, then description and long_description must be nil.
  • description (string or nil) If the hook was not satisfied, this should provide a short description of the failure.
  • long_description (string or nil) If the hook was not satisfied, this should provide a long description of the failure.

Here are some common properties of hooks:

The ctx argument passed to the hook function always has at least the following fields:

key description
config_strings (table of string) Contains string configs defined per repository config
config_ints (table of int) Contains int configs defined per repository config
regex_match(regex, string) (function) Returns a boolean indicating whether the string matches the supplied regex

The type file is a table with the following fields:

key description
path (string) Path to the file relative to the repo root.
is_added() Returns a boolean indicating whether the file was added as part of the changeset
is_deleted() Returns a boolean indicating whether the file was deleted as part of the changeset
is_modified() Returns a boolean indicating whether the file was modified as part of the changeset
contains_string(needle) Returns a boolean indicating whether the specified string needle is present in this file. (Only present if is_deleted() returns false.)
len() Returns a number that is the length of the file in bytes. (Only present if is_deleted() returns false.)
content() Returns a string containing the contents of the file. (Only present if is_deleted() returns false.)
path_regex_match(regex) Returns a boolean indicating whether the file's path matches the supplied regex

PerChangeset

Your hook() function receives a single ctx argument, which is a table with the following additional fields:

key description
info (table) Described below.
files (table) List of objects of type file, described above.
file_content(path) (function) Takes the relative path to a file in the repo and returns its contents.
parse_commit_msg() (function) Returns a table with phabricator tags parsed.
is_valid_reviewer(user) (function) Returns whether a user can review the commit.

ctx.info is a table with the following fields:

key description
author (string) The author of the changeset. This should be something like "Stanislau Hlebik <stash@fb.com>".
comments (string) The commit message.
parent1_hash (string or nil) p1 for the commit as a hex string, if it exists.
parent2_hash (string or nil) p2 for the commit as a hex string, if it exists.

PerAddedOrModifiedFile

Your hook() function receives a single ctx argument, which is a table with the following additional fields:

key description
file (table) Object of type file, described above. (Note is_deleted() will return false.)