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