| # Copyright (c) 2011 elego Software Solutions GmbH <info@elego.de> |
| # |
| # Permission to use, copy, modify, and distribute this software for any |
| # purpose with or without fee is hereby granted, provided that the above |
| # copyright notice and this permission notice appear in all copies. |
| # |
| # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
| # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
| # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
| # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| |
| # Python module helpful for writing hook scripts. |
| # |
| # This is mostly an example for calling 'svnlook' and 'svn' from Python hooks |
| # and for reading simple configuration files. While it's an example, this may |
| # also be useful in a production environment as is. |
| # |
| # To use this, place this file next to your python hook scripts (e.g. in |
| # 'repos/hooks/'). To be able use these functions, you may simply write: |
| # from hook_toolbox import * |
| |
| # NOTE: Adjust the BIN_PATH according to your operating system, to get valid |
| # paths to your binaries (e.g. '/usr/bin/svnlook'): |
| BIN_PATH = '/usr/bin' |
| |
| |
| import sys, os, subprocess, shlex |
| |
| |
| def get_log_message(*args): |
| '''Synopsis: |
| get_log_message(repos, '-t', transaction) (from pre-commit hook) |
| get_log_message(repos, '-r', revision) (from post-commit hook) |
| Returns the log message as a string. |
| ''' |
| cmd = [os.path.join(BIN_PATH, 'svnlook'), 'propget', '--revprop'] |
| cmd.extend(args) |
| cmd.append('svn:log') |
| return run(*cmd) |
| |
| |
| def get_changed_paths(*args): |
| '''Synopsis: |
| get_changed_paths(repos, '-t', transaction) (from pre-commit hook) |
| get_changed_paths(repos, '-r', revision) (from post-commit hook) |
| Returns the list of changed paths, relative to the repository root. |
| ''' |
| cmd = [os.path.join(BIN_PATH, 'svnlook'), 'changed'] |
| cmd.extend(args) |
| changes = run(*cmd) |
| #print changes |
| |
| # First four chars of each svnlook output line show the kind of change. |
| # The rest of the line is the complete repository path. See: |
| # http://svnbook.red-bean.com/nightly/en/svn.ref.svnlook.c.changed.html |
| changed_paths = [ line[4:] for line in changes.split('\n') if len(line) > 4 ] |
| |
| #print '%s:\n '%(' '.join(args)), '\n '.join(changed_paths) |
| return changed_paths |
| |
| |
| def has_path_changed(path, changed_paths): |
| '''Returns True if any changed path begins with the given path. |
| path: A path relative to the repos root, without leading slash. |
| changed_paths: A list obtained from the function get_changed_paths().''' |
| |
| for changed_path in changed_paths: |
| if changed_path.startswith(path): |
| return True |
| return False |
| |
| |
| def read_config(repos, filename, expected_tokens_per_line=-1): |
| '''Reads the file <repos>/conf/<filename> line wise and tokenizes each |
| line according to shell syntax rules. For example, a file like |
| |
| # comments & blank lines ignored |
| |
| aaa bb |
| cccc "d d d" ee |
| ff |
| |
| would return a list |
| |
| [ ['aaa', 'bb'], |
| ['cccc', 'd d d', 'ee'], |
| ['ff'] ] |
| |
| If expected_tokens_per_line is > 0, then only the lines matching the |
| given number of tokens are returned. In above example, passing |
| expected_tokens_per_line=2 would yield just: |
| |
| [ ['aaa', 'bb'] ] |
| |
| Returns an empty list if no such config file exists. |
| ''' |
| path = os.path.join(repos, 'conf', filename) |
| if not os.path.exists(path): |
| print('Not present:', path) |
| return [] |
| |
| config_lines = open(path).readlines() |
| |
| tokenized_lines = [ shlex.split(line, True) for line in config_lines ] |
| tokenized_lines = [ tokens for tokens in tokenized_lines if tokens ] |
| |
| if expected_tokens_per_line < 1: |
| return tokenized_lines |
| |
| matching_lines = [ tokens for tokens in tokenized_lines |
| if len(tokens) == expected_tokens_per_line ] |
| |
| if len(matching_lines) < len(tokenized_lines): |
| print('*** %d syntax errors in %s' % ( |
| len(tokenized_lines) - len(matching_lines), |
| path)) |
| |
| return matching_lines |
| |
| |
| def update_working_copy(wc_path): |
| if not os.path.exists(wc_path): |
| print('--> *** Cannot find working copy', wc_path) |
| return None |
| return run(os.path.join(BIN_PATH, 'svn'), 'update', wc_path) |
| |
| |
| def run(*cmd): |
| '''Call the given command & args and return what it printed to stdout. |
| e.g. result = run('/usr/bin/svn', 'info', wc_dir_path) ''' |
| print('-->', ' '.join(cmd)) |
| stdout = subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()[0] |
| print(stdout.strip()) |
| return stdout |
| |