4
# Licensed to the Apache Software Foundation (ASF) under one
5
# or more contributor license agreements. See the NOTICE file
6
# distributed with this work for additional information
7
# regarding copyright ownership. The ASF licenses this file
8
# to you under the Apache License, Version 2.0 (the
9
# "License"); you may not use this file except in compliance
10
# with the License. You may obtain a copy of the License at
12
# http://www.apache.org/licenses/LICENSE-2.0
14
# Unless required by applicable law or agreed to in writing,
15
# software distributed under the License is distributed on an
16
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17
# KIND, either express or implied. See the License for the
18
# specific language governing permissions and limitations
24
Check that any files pending commit into a Subversion repository have
25
suitable file extensions, printing an error and exiting with an
26
errorful value if any files fail validation. This is intended to be
27
used as a Subversion pre-commit hook script.
31
validate-extensions.py REPOS_PATH TXN_NAME deny EXT [...]
33
Ensure that any newly added files do *not* have one of the provided
39
validate-extensions.py REPOS_PATH TXN_NAME allow EXT [...]
41
Ensure that any newly added files *do* have one of the provided
42
file extensions. (Extension-less files are disallowed.)
48
from svn import repos, fs, core
50
def validate_added_extensions(repos_path, txn_name, extensions, action):
51
# Open the repository and transaction.
52
fs_ptr = repos.fs(repos.open(repos_path))
53
txn_t = fs.open_txn(fs_ptr, txn_name)
54
txn_root = fs.txn_root(txn_t)
56
# Fetch the changes made in this transaction.
57
changes = fs.svn_fs_paths_changed(txn_root)
58
paths = changes.keys()
62
change = changes[path]
64
# Always allow deletions.
65
if change.change_kind == fs.path_change_delete:
68
# Always allow non-files.
69
kind = fs.check_path(txn_root, path)
70
if kind != core.svn_node_file:
73
# If this was a newly added (without history) file ...
74
if ((change.change_kind == fs.path_change_replace) \
75
or (change.change_kind == fs.path_change_add)):
76
copyfrom_rev, copyfrom_path = fs.copied_from(txn_root, path)
77
if copyfrom_rev == core.SVN_INVALID_REVNUM:
79
# ... then check it for a valid extension.
80
base, ext = os.path.splitext(path)
83
if ((ext in extensions) and (action == 'deny')) \
84
or ((ext not in extensions) and (action == 'allow')):
85
sys.stderr.write("Path '%s' has an extension disallowed by server "
86
"configuration.\n" % (path))
89
def usage_and_exit(errmsg=None):
90
stream = errmsg and sys.stderr or sys.stdout
93
stream.write("ERROR: " + errmsg + "\n")
94
sys.exit(errmsg and 1 or 0)
99
usage_and_exit("Not enough arguments.")
100
repos_path = sys.argv[1]
101
txn_name = sys.argv[2]
103
if action not in ("allow", "deny"):
104
usage_and_exit("Invalid action '%s'. Expected either 'allow' or 'deny'."
106
extensions = [x.lower() for x in sys.argv[4:]]
107
validate_added_extensions(repos_path, txn_name, extensions, action)
109
if __name__ == "__main__":