How do I set up a hook in HG / Mercurial that gets dictated by the repository?
Asked Answered
L

4

3

I have a need for a hook to run after update (this will build the solution they have updated) and I don't want to have to add that hook manually for each person that clones my central repository.

When someone first clones my central repository, is it possible to include hooks into that clone? It seems that the .hgrc file doesn't get cloned automatically.

I did read about site-wide hooks, but as far as I understand it, they work on each created repository, where I only want to have the hooks on some repos.

Laureate answered 18/9, 2011 at 14:34 Comment(0)
H
10

As Rudi already said, this is (thankfully) not possible for security reasons.

However, you can reduce the per-clone workload to set up hooks manually: Ship the hook scripts as part of your repository, e.g. in a directory .hghooks, and additionally include a script in your repo which sets up these hooks in a clone's hgrc. Each coworker now only needs to call the setup script once per clone.

Hillis answered 18/9, 2011 at 18:33 Comment(1)
This is exactly what I did, after first clone from the repo, devs will have to run an installer anyway. It creates an IIS site, adds a hostname to the hosts file and now also changes their hgrc file, I never thought of adding it there, so easy!Laureate
N
4

This is not possible, since that hooks do not propagate to clones is a security measure. If this were possible, one could set up a rouge repository, which runs arbitrary commands on any machine where the repo is cloned.

See http://hgbook.red-bean.com/read/handling-repository-events-with-hooks.html#id402330 for more details.

Northeast answered 18/9, 2011 at 16:42 Comment(0)
C
3

This will allow for centralised per-repo hooks, with a single setup step per user. It will however cause problems for users who are disconnected from the network. An alternative if you tend to have disconnected developers (or ones over high-latency/low bandwidth links) would be to have a repo containing the hooks, and set up each user's global hgrc to point into that repo (and require regular pulls from a central hook repo).

Note that I treat the ID of the first commit as the "repo ID" - this assumes that the first commit in each repository is unique in some way - contents or commit message. If this is not the case you could do the same thing but applying it over the first N commits - but you would then have to account for repos that have fewer than N commits - can't just take repo[:5] for example as newer commits would then change the repo ID. I'd personally suggest that the first commit should probably be a standard .ignore file with a commit message unique to that repo.

  1. Have a central shared_hgrc file, accessible from a network share (or in a hook repo).

  2. Each user's global hgrc has:

    %include /path/to/shared_hgrc
    
  3. Create a shared repository of python hook modules. The hooks must be written in python.

  4. Create your hook functions. In each function, check which repo the hook has been called on by checking the ID of the first commit:

    # hooktest.py
    
    import mercurial.util
    
    FOOBAR_REPO = 'b88c69276866d73310be679b6a4b40d875e26d84'
    
    ALLOW_PRECOMMIT_REPOS = set((
        FOOBAR_REPO,
    ))
    
    def precommit_deny_if_wrong_repo(ui, repo, **kwargs):
        """Aborts if the repo is not allowed to do this.
        The repo ID is the ID of the first commit to the repo."""
    
        repo_id = repo[0].hex().lower()
    
        if repo_id not in ALLOW_PRECOMMIT_REPOS:
            raise mercurial.util.Abort('Repository denied: %s' % (repo_id,))
    
        ui.status('Repository allowed: %s\n' % (repo_id,))
    
    def precommit_skip_if_wrong_repo(ui, repo, **kwargs):
        """Skips the hook if the repo is not allowed to do this.
        The repo ID is the ID of the first commit to the repo."""
    
        repo_id = repo[0].hex().lower()
    
        if repo_id not in ALLOW_PRECOMMIT_REPOS:
            ui.debug('Repository hook skipped: %s\n' % (repo_id,))
            return
    
        ui.status('Repository hook allowed: %s\n' % (repo_id,))
    
  5. In the shared_hgrc file, set up the hooks you need (make sure you qualify the hook names to prevent conflicts):

    [hooks]
    pre-commit.00_skip = python:/path/to/hooktest.py:precommit_skip_if_wrong_repo
    pre-commit.01_deny = python:/path/to/hooktest.py:precommit_deny_if_wrong_repo
    
Constrict answered 18/9, 2011 at 23:6 Comment(0)
I
1

As @Rudi said first, it can't be done for security reasons.

With some prior setup you can make it so that hooks are run on clone, but putting a hook with a repo-relative path in /etc/mercurial or in each user's ~/.hgrc, which in a corporate setting can be done via your system management tools or by building a custom Mercurial installer. In a non-corporate setting follow @Oben's advice and provide the scripts and a readme.

Interviewer answered 18/9, 2011 at 19:13 Comment(2)
It's worth mentioning that hooks defined in /etc/mercurial can be overriden by users (if they wish to)Apostrophize
A great point. You can help people to no forget to config a hook, but you can't make them run one on their own machine.Interviewer

© 2022 - 2024 — McMap. All rights reserved.