SVN pre-commit hook for avoiding changes to tags subdirectories

前端 未结 11 568
陌清茗
陌清茗 2020-11-28 04:25

Is there anybody who has clear instructions on how to add a pre-commit hook that avoids changes to tags subdirectories?

I already searched the internet quite a bit.

相关标签:
11条回答
  • 2020-11-28 04:50

    I don't have enough reputation to "comment" on Raim's answer above, but his worked great, with one exception, his grep pattern is wrong.

    I simply used the below as my pre-commit hook (I didn't have an existing one, you'd need to merge in that case):

    #!/bin/sh
    
    REPOS="$1"
    TXN="$2"
    
    SVNLOOK=/opt/local/bin/svnlook
    
    # Committing to tags is not allowed
    $SVNLOOK changed -t "$TXN" "$REPOS" | grep "^U\W.*\/tags\/" && /bin/echo "Cannot commit to tags!" 1>&2 && exit 1
    
    # All checks passed, so allow the commit.
    exit 0
    

    The only problem with Raim's grep pattern is that it only matched "tags" if it was at the "root" of your repo. Since I have several projects in my repo, the script as he wrote it allowed commits on tag branches.

    Also, be sure to chmod +x as indicated, otherwise you'll think it worked b/c the commit failed, but it failed b/c it couldn't exec the pre-commit hook, not because the hook worked.

    This was really great, thanks Raim. Much better and lighter weight than all other suggestions as it has no dependencies!

    0 讨论(0)
  • 2020-11-28 04:57

    The accepted answer prevents updating files in a tag but doesn't prevent adding files to a tag. The following version handles both:

    #!/bin/sh
    
    REPOS="$1"
    TXN="$2"
    SVNLOOK="/home/staging/thirdparty/subversion-1.6.17/bin/svnlook"
    
    # Committing to tags is not allowed
    $SVNLOOK changed -t "$TXN" "$REPOS" --copy-info| grep -v "^ " | grep -P '^[AU]   \w+/tags/' && /bin/echo "Cannot update tags!" 1>&2 && exit 1
    
    # All checks passed, so allow the commit.
    exit 0
    
    0 讨论(0)
  • 2020-11-28 04:58

    Since the 1st answer didn't prevent add/suppr files, and prevented new tags creation, and many other where incomplete or buggy, I reworked it

    Here is my pre-commit hook : Goals are :

    • Disallow commits on tags (file addition/suppression/updates)
    • Don't prevent creation of tags

    --------- file "pre-commit" (put in repositories hooks folder) ---------

    #!/bin/sh
    
    REPOS="$1"
    TXN="$2"
    
    SVNLOOK=/usr/bin/svnlook
    
    #Logs
    #$SVNLOOK changed -t "$TXN" "$REPOS" > /tmp/changes
    #echo "$TXN" > /tmp/txn
    #echo "$REPOS" > /tmp/repos
    
    # Committing to tags is not allowed
    # Forbidden changes are Update/Add/Delete.  /W = non alphanum char  Redirect is necessary to get the error message, since regular output is lost.
    # BUT, we must allow tag creation / suppression
    
    $SVNLOOK changed -t "$TXN" "$REPOS" | /bin/grep "^A\W.*tags\/[0-9._-]*\/." && /bin/echo "Commit to tags are NOT allowed ! (Admin custom rule)" 1>&2 && exit 101
    $SVNLOOK changed -t "$TXN" "$REPOS" | /bin/grep "^U\W.*tags\/[0-9._-]*\/." && /bin/echo "Commit to tags are NOT allowed ! (Admin custom rule)" 1>&2 && exit 102
    $SVNLOOK changed -t "$TXN" "$REPOS" | /bin/grep "^D\W.*tags\/[0-9._-]*\/." && /bin/echo "Commit to tags are NOT allowed ! (Admin custom rule)" 1>&2 && exit 104
    
    # All checks passed, so allow the commit.
    exit 0;
    

    --------- end of file "pre-commit" ---------

    Also, I made 2 shell scripts to copy my hook in every project of my svn : One to set a repo read only :

    --------- script "setOneRepoTagsReadOnly.sh" ---------

    #!/bin/sh
    
    cd /var/svn/repos/svn
    zeFileName=$1/hooks/pre-commit
    /bin/cp ./CUSTOM_HOOKS/pre-commit $zeFileName
    chown www-data:www-data $zeFileName
    chmod +x $zeFileName
    

    --------- end of file "setOneRepoTagsReadOnly.sh" ---------

    And one calling it for every repo, to make all of my repos read only :

    --------- file "makeTagsReadOnly.sh" ---------

    #!/bin/shs/svn                                                                                                                                                                         
    #Lists all repos, and adds the pre-commit hook to protect tags on each of them
    find /var/svn/repos/svn/ -maxdepth 1 -mindepth 1 -type d -execdir '/var/svn/repos/svn/setOneRepoTagsReadOnly.sh' \{\} \;
    

    --------- end of file "makeTagsReadOnly.sh" ---------

    I execute thoses scripts directly from the svn "root" (/var/svn/repos/svn, in my case). Btw, a cron task could be set to automatically modify new repos by executing thoses scripts daily

    Hope it helps.

    0 讨论(0)
  • 2020-11-28 05:03

    The listed answers are great but none did exactly what I need. I want to allow creating tags easily, but once they are created they should be fully read-only.

    I also want to prevent the stupid situation where if you do this:

    svn copy myrepo/trunk myrepo/tags/newrelease
    

    All is well the first time. But the second time, if the tag already exists, you will end up with myrepo/tags/newrelease/trunk.

    My pre-commit hook will look for any pre-existing SVN directory matching (repo)/tags/(tag)/ and fail if it's found:

    $SVNLOOK tree -N --full-paths "$REPOS" "`$SVNLOOK changed -t "$TXN" "$REPOS" \
      | sed 's/[A-Z][[:space:]]*\([^/]*\)\/tags\/\([^/]*\)\/.*/\1\/tags\/\2\//' \
      | head -n 1`" \
      && echo "Tag already exists, commit rejected." >&2 \
      && exit 1
    
    0 讨论(0)
  • 2020-11-28 05:04

    Here is a short shell script to prevent committing to tags after they have been created:

    #!/bin/sh
    
    REPOS="$1"
    TXN="$2"
    
    SVNLOOK=/usr/bin/svnlook
    
    # Committing to tags is not allowed
    $SVNLOOK changed -t "$TXN" "$REPOS" | grep "^U\W*tags" && /bin/echo "Cannot commit to tags!" 1>&2 && exit 1
    
    # All checks passed, so allow the commit.
    exit 0
    

    Save this at hooks/pre-commit for your Subversion repository and make it executable with chmod +x.

    0 讨论(0)
  • 2020-11-28 05:04

    Pretty late to the party, however I wrote a python pre-commit hook for work which is based off the log-police.py script on http://subversion.tigris.org/.

    This script should do what you want, however it also checks that a log message exists, though that should be easy to remove from the script.

    Some caveats:

    • I'm new to Python, so it most likely could be written better
    • It has only been tested on Windows 2003 with Python 2.5 and Subversion 1.4.

    Requirements:

    • Subversion
    • Python
    • Subversion bindings for Python

    Finally, the code:

    #!/usr/bin/env python
    
    #
    # pre-commit.py:
    #
    # Performs the following:
    #  - Makes sure the author has entered in a log message.
    #  - Make sure author is only creating a tag, or if deleting a tag, author is a specific user
    #
    # Script based on http://svn.collab.net/repos/svn/trunk/tools/hook-scripts/log-police.py
    #
    # usage: pre-commit.py -t TXN_NAME REPOS
    # E.g. in pre-commit.bat (under Windows)
    #   python.exe {common_hooks_dir}\pre_commit.py -t %2 %1
    #
    
    
    import os
    import sys
    import getopt
    try:
      my_getopt = getopt.gnu_getopt
    except AttributeError:
      my_getopt = getopt.getopt
    
    import re
    
    import svn
    import svn.fs
    import svn.repos
    import svn.core
    
    #
    # Check Tags functionality
    #
    def check_for_tags(txn):
      txn_root = svn.fs.svn_fs_txn_root(txn)
      changed_paths = svn.fs.paths_changed(txn_root)
      for path, change in changed_paths.iteritems():
        if is_path_within_a_tag(path): # else go to next path
          if is_path_a_tag(path):
            if (change.change_kind == svn.fs.path_change_delete):
              if not is_txn_author_allowed_to_delete(txn):
                sys.stderr.write("\nOnly an administrator can delete a tag.\n\nContact your Subversion Administrator for details.")
                return False
            elif (change.change_kind != svn.fs.path_change_add):
              sys.stderr.write("\nUnable to modify " + path + ".\n\nIt is within a tag and tags are read-only.\n\nContact your Subversion Administrator for details.")
              return False
            # else user is adding a tag, so accept this change
          else:
            sys.stderr.write("\nUnable to modify " + path + ".\n\nIt is within a tag and tags are read-only.\n\nContact your Subversion Administrator for details.")
            return False
      return True
    
    def is_path_within_a_tag(path):
      return re.search('(?i)\/tags\/', path)
    
    def is_path_a_tag(path):
      return re.search('(?i)\/tags\/[^\/]+\/?$', path)
    
    def is_txn_author_allowed_to_delete(txn):
      author = get_txn_property(txn, 'svn:author')
      return (author == 'bob.smith')
    
    #
    # Check log message functionality
    #
    def check_log_message(txn):
      log_message = get_txn_property(txn, "svn:log")
      if log_message is None or log_message.strip() == "":
        sys.stderr.write("\nCannot enter in empty commit message.\n")
        return False
      else:
        return True
    
    def get_txn_property(txn, prop_name):
      return svn.fs.svn_fs_txn_prop(txn, prop_name)
    
    def usage_and_exit(error_msg=None):
      import os.path
      stream = error_msg and sys.stderr or sys.stdout
      if error_msg:
        stream.write("ERROR: %s\n\n" % error_msg)
      stream.write("USAGE: %s -t TXN_NAME REPOS\n"
                   % (os.path.basename(sys.argv[0])))
      sys.exit(error_msg and 1 or 0)
    
    def main(ignored_pool, argv):
      repos_path = None
      txn_name = None
    
      try:
        opts, args = my_getopt(argv[1:], 't:h?', ["help"])
      except:
        usage_and_exit("problem processing arguments / options.")
      for opt, value in opts:
        if opt == '--help' or opt == '-h' or opt == '-?':
          usage_and_exit()
        elif opt == '-t':
          txn_name = value
        else:
          usage_and_exit("unknown option '%s'." % opt)
    
      if txn_name is None:
        usage_and_exit("must provide -t argument")
      if len(args) != 1:
        usage_and_exit("only one argument allowed (the repository).")
    
      repos_path = svn.core.svn_path_canonicalize(args[0])
    
      fs = svn.repos.svn_repos_fs(svn.repos.svn_repos_open(repos_path))
      txn = svn.fs.svn_fs_open_txn(fs, txn_name)
    
      if check_log_message(txn) and check_for_tags(txn):
        sys.exit(0)
      else:
        sys.exit(1)
    
    if __name__ == '__main__':
      sys.exit(svn.core.run_app(main, sys.argv))
    
    0 讨论(0)
提交回复
热议问题