diff options
Diffstat (limited to 'tools/hook-scripts/validate-extensions.py')
-rwxr-xr-x | tools/hook-scripts/validate-extensions.py | 110 |
1 files changed, 110 insertions, 0 deletions
diff --git a/tools/hook-scripts/validate-extensions.py b/tools/hook-scripts/validate-extensions.py new file mode 100755 index 0000000..ed0283d --- /dev/null +++ b/tools/hook-scripts/validate-extensions.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python +# +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +# + +"""\ +Check that any files pending commit into a Subversion repository have +suitable file extensions, printing an error and exiting with an +errorful value if any files fail validation. This is intended to be +used as a Subversion pre-commit hook script. + +Syntax 1: + + validate-extensions.py REPOS_PATH TXN_NAME deny EXT [...] + + Ensure that any newly added files do *not* have one of the provided + file extensions. + + +Syntax 2: + + validate-extensions.py REPOS_PATH TXN_NAME allow EXT [...] + + Ensure that any newly added files *do* have one of the provided + file extensions. (Extension-less files are disallowed.) + +""" + +import sys +import os +from svn import repos, fs, core + +def validate_added_extensions(repos_path, txn_name, extensions, action): + # Open the repository and transaction. + fs_ptr = repos.fs(repos.open(repos_path)) + txn_t = fs.open_txn(fs_ptr, txn_name) + txn_root = fs.txn_root(txn_t) + + # Fetch the changes made in this transaction. + changes = fs.svn_fs_paths_changed(txn_root) + paths = changes.keys() + + # Check the changes. + for path in paths: + change = changes[path] + + # Always allow deletions. + if change.change_kind == fs.path_change_delete: + continue + + # Always allow non-files. + kind = fs.check_path(txn_root, path) + if kind != core.svn_node_file: + continue + + # If this was a newly added (without history) file ... + if ((change.change_kind == fs.path_change_replace) \ + or (change.change_kind == fs.path_change_add)): + copyfrom_rev, copyfrom_path = fs.copied_from(txn_root, path) + if copyfrom_rev == core.SVN_INVALID_REVNUM: + + # ... then check it for a valid extension. + base, ext = os.path.splitext(path) + if ext: + ext = ext[1:].lower() + if ((ext in extensions) and (action == 'deny')) \ + or ((ext not in extensions) and (action == 'allow')): + sys.stderr.write("Path '%s' has an extension disallowed by server " + "configuration.\n" % (path)) + sys.exit(1) + +def usage_and_exit(errmsg=None): + stream = errmsg and sys.stderr or sys.stdout + stream.write(__doc__) + if errmsg: + stream.write("ERROR: " + errmsg + "\n") + sys.exit(errmsg and 1 or 0) + +def main(): + argc = len(sys.argv) + if argc < 5: + usage_and_exit("Not enough arguments.") + repos_path = sys.argv[1] + txn_name = sys.argv[2] + action = sys.argv[3] + if action not in ("allow", "deny"): + usage_and_exit("Invalid action '%s'. Expected either 'allow' or 'deny'." + % (action)) + extensions = [x.lower() for x in sys.argv[4:]] + validate_added_extensions(repos_path, txn_name, extensions, action) + +if __name__ == "__main__": + main() |