#!/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()