Show More
@@ -1,120 +1,120 b'' | |||
|
1 | 1 | # Example to validate pushed files names and size using some sort of rules |
|
2 | 2 | |
|
3 | 3 | |
|
4 | 4 | |
|
5 | 5 | @has_kwargs({ |
|
6 | 6 | 'server_url': 'url of instance that triggered this hook', |
|
7 | 7 | 'config': 'path to .ini config used', |
|
8 | 8 | 'scm': 'type of version control "git", "hg", "svn"', |
|
9 | 9 | 'username': 'username of actor who triggered this event', |
|
10 | 10 | 'ip': 'ip address of actor who triggered this hook', |
|
11 | 11 | 'action': '', |
|
12 | 12 | 'repository': 'repository name', |
|
13 | 13 | 'repo_store_path': 'full path to where repositories are stored', |
|
14 | 14 | 'commit_ids': 'pre transaction metadata for commit ids', |
|
15 | 15 | 'hook_type': '', |
|
16 | 16 | 'user_agent': 'Client user agent, e.g git or mercurial CLI version', |
|
17 | 17 | }) |
|
18 | 18 | def _pre_push_hook(*args, **kwargs): |
|
19 | 19 | """ |
|
20 | 20 | Post push hook |
|
21 | 21 | To stop version control from storing the transaction and send a message to user |
|
22 | 22 | use non-zero HookResponse with a message, e.g return HookResponse(1, 'Not allowed') |
|
23 | 23 | |
|
24 | 24 | This message will be shown back to client during PUSH operation |
|
25 | 25 | |
|
26 | 26 | Commit ids might look like that:: |
|
27 | 27 | |
|
28 | 28 | [{u'hg_env|git_env': ..., |
|
29 | 29 | u'multiple_heads': [], |
|
30 | 30 | u'name': u'default', |
|
31 | 31 | u'new_rev': u'd0b2ae0692e722e01d5677f27a104631cf798b69', |
|
32 | 32 | u'old_rev': u'd0b1ae0692e722e01d5677f27a104631cf798b69', |
|
33 | 33 | u'ref': u'', |
|
34 | 34 | u'total_commits': 2, |
|
35 | 35 | u'type': u'branch'}] |
|
36 | 36 | """ |
|
37 | 37 | import fnmatch |
|
38 | 38 | from .helpers import extra_fields, extract_pre_files |
|
39 | 39 | from .utils import str2bool, aslist |
|
40 | 40 | from rhodecode.lib.helpers import format_byte_size_binary |
|
41 | 41 | |
|
42 | 42 | # returns list of dicts with key-val fetched from extra fields |
|
43 | 43 | repo_extra_fields = extra_fields.run(**kwargs) |
|
44 | 44 | |
|
45 | 45 | # optionally use 'extra fields' to control the logic per repo |
|
46 | 46 | # e.g store a list of patterns to be forbidden e.g `*.exe, *.dump` |
|
47 | 47 | forbid_files = extra_fields.get_field(repo_extra_fields, key='forbid_files_glob', |
|
48 | 48 | convert_type=False, default=[]) |
|
49 | forbid_files = aslist(forbid_files) | |
|
49 | forbid_files = aslist(forbid_files, sep=',') | |
|
50 | 50 | |
|
51 | 51 | # forbid_files = ['*'] # example pattern |
|
52 | 52 | |
|
53 | 53 | # optionally get bytes limit for a single file, e.g 1024 for 1KB |
|
54 | 54 | forbid_size_over = extra_fields.get_field(repo_extra_fields, key='forbid_size_over', |
|
55 | 55 | convert_type=False, default=0) |
|
56 | 56 | |
|
57 | 57 | forbid_size_over = int(forbid_size_over or 0) |
|
58 | 58 | |
|
59 | 59 | # forbid_size_over = 1024 # example 1024 |
|
60 | 60 | |
|
61 | 61 | def validate_file_name_and_size(file_data, forbidden_files=None, size_limit=None): |
|
62 | 62 | """ |
|
63 | 63 | This function validates comited files against some sort of rules. |
|
64 | 64 | It should return a valid boolean, and a reason for failure |
|
65 | 65 | |
|
66 | 66 | file_data =[ |
|
67 | 67 | 'raw_diff', 'old_revision', 'stats', 'original_filename', 'is_limited_diff', |
|
68 | 68 | 'chunks', 'new_revision', 'operation', 'exceeds_limit', 'filename' |
|
69 | 69 | ] |
|
70 | 70 | file_data['ops'] = { |
|
71 | 71 | # is file binary |
|
72 | 72 | 'binary': False, |
|
73 | 73 | |
|
74 | 74 | # lines |
|
75 | 75 | 'added': 32, |
|
76 | 76 | 'deleted': 0 |
|
77 | 77 | |
|
78 | 78 | 'ops': {3: 'modified file'}, |
|
79 | 79 | 'new_mode': '100644', |
|
80 | 80 | 'old_mode': None |
|
81 | 81 | } |
|
82 | 82 | """ |
|
83 | 83 | file_name = file_data['filename'] |
|
84 | 84 | operation = file_data['operation'] # can be A(dded), M(odified), D(eleted) |
|
85 | 85 | |
|
86 | 86 | # check files names |
|
87 | 87 | if forbidden_files: |
|
88 | 88 | reason = 'File {} is forbidden to be pushed'.format(file_name) |
|
89 | 89 | for forbidden_pattern in forbid_files: |
|
90 | 90 | # here we can also filter for operation, e.g if check for only ADDED files |
|
91 | 91 | # if operation == 'A': |
|
92 | 92 | if fnmatch.fnmatch(file_name, forbidden_pattern): |
|
93 | 93 | return False, reason |
|
94 | 94 | |
|
95 | 95 | # validate A(dded) files and size |
|
96 | 96 | if size_limit and operation == 'A': |
|
97 | 97 | if 'file_size' in file_data: |
|
98 | 98 | size = file_data['file_size'] |
|
99 | 99 | else: |
|
100 | 100 | size = len(file_data['raw_diff']) |
|
101 | 101 | |
|
102 | 102 | reason = 'File {} size of {} bytes exceeds limit {}'.format( |
|
103 | 103 | file_name, format_byte_size_binary(size), |
|
104 | 104 | format_byte_size_binary(size_limit)) |
|
105 | 105 | if size > size_limit: |
|
106 | 106 | return False, reason |
|
107 | 107 | |
|
108 | 108 | return True, '' |
|
109 | 109 | |
|
110 | 110 | if forbid_files or forbid_size_over: |
|
111 | 111 | # returns list of dicts with key-val fetched from extra fields |
|
112 | 112 | file_list = extract_pre_files.run(**kwargs) |
|
113 | 113 | |
|
114 | 114 | for file_data in file_list: |
|
115 | 115 | file_valid, reason = validate_file_name_and_size( |
|
116 | 116 | file_data, forbid_files, forbid_size_over) |
|
117 | 117 | if not file_valid: |
|
118 | 118 | return HookResponse(1, reason) |
|
119 | 119 | |
|
120 | 120 | return HookResponse(0, '') |
General Comments 0
You need to be logged in to leave comments.
Login now