|
1
|
/* |
|
2
|
** Copyright (c) 2023 Preben Guldberg |
|
3
|
** |
|
4
|
** This program is free software; you can redistribute it and/or |
|
5
|
** modify it under the terms of the Simplified BSD License (also |
|
6
|
** known as the "2-Clause License" or "FreeBSD License".) |
|
7
|
|
|
8
|
** This program is distributed in the hope that it will be useful, |
|
9
|
** but without any warranty; without even the implied warranty of |
|
10
|
** merchantability or fitness for a particular purpose. |
|
11
|
** |
|
12
|
** Author contact information: |
|
13
|
** [email protected] |
|
14
|
** http://www.hwaci.com/drh/ |
|
15
|
** |
|
16
|
******************************************************************************* |
|
17
|
** |
|
18
|
** This file contains code implementing a warning policy for different events. |
|
19
|
*/ |
|
20
|
#include "config.h" |
|
21
|
#include "warnpolicy.h" |
|
22
|
|
|
23
|
/* |
|
24
|
** SETTING: warning-policy width=40 block-text propagating default={} |
|
25
|
** Policy for showing warnings under certain conditions. |
|
26
|
** |
|
27
|
** The policy is a JSON object where the following names are recognised: |
|
28
|
** |
|
29
|
** commit: Used when committing. A list of objects with names in |
|
30
|
** (message, branch, except-branch, users, except-users). |
|
31
|
** merge: Used when merging. A List of objects with names in |
|
32
|
** (message, branch, except-branch, from, except-from, |
|
33
|
** users, except-users, unpublished). |
|
34
|
** match-style: If "regexp", patterns use REGEXP, otherwise GLOB. |
|
35
|
** |
|
36
|
** Meaning of names used in lists above: |
|
37
|
** |
|
38
|
** message: MESSAGE Required: Message to show. |
|
39
|
** branch: PATTERN Apply branch match PATTERN (default any). |
|
40
|
** except-branch: PATTERN Exclude when in a branch matching PATTERN. |
|
41
|
** from: PATTERN Apply if merging from PATTERN (default any). |
|
42
|
** except-from: PATTERN Exclude when merging from PATTERN. |
|
43
|
** unpublished: true If true, only show when merging from a private |
|
44
|
** branch into a public branch. |
|
45
|
** users: LIST Show only for users in LIST (default any). |
|
46
|
** except-users: LIST Users in LIST will not be shown the messages. |
|
47
|
** |
|
48
|
** Example: |
|
49
|
** |
|
50
|
** { |
|
51
|
** "commit": [ |
|
52
|
** { "message": "Release pending, proceed with caution.", |
|
53
|
** "branch": "trunk", |
|
54
|
** "except-users": [ "owner", "admin" ] } |
|
55
|
** ], |
|
56
|
** "merge": [ |
|
57
|
** { "message": "Please use 'fossil publish' before merging private to public", |
|
58
|
** "except-branch": "rebased-branch-*", |
|
59
|
** "unpublished": true }, |
|
60
|
** { "message": "Updates to release branches should be merged from rc.", |
|
61
|
** "branch": "release-*", |
|
62
|
** "except-from": "rc-*" } |
|
63
|
** ] |
|
64
|
** } |
|
65
|
*/ |
|
66
|
|
|
67
|
/* |
|
68
|
** Fetch the match-style for warning-policy. |
|
69
|
*/ |
|
70
|
static const char *warning_policy_match_style(void){ |
|
71
|
int isRegexp = db_int(0, |
|
72
|
"SELECT 1" |
|
73
|
" FROM config" |
|
74
|
" WHERE name='warning-policy'" |
|
75
|
" AND json_error_position(value)=0" |
|
76
|
" AND value->>'match-style'='regexp'"); |
|
77
|
return isRegexp ? "REGEXP" : "GLOB"; |
|
78
|
} |
|
79
|
|
|
80
|
/* |
|
81
|
** Common part of issuing warnings. |
|
82
|
*/ |
|
83
|
static int print_policy_warnings(Blob *pSql){ |
|
84
|
Stmt q; |
|
85
|
int nWarnings = 0; |
|
86
|
|
|
87
|
db_prepare(&q, "%s)", blob_sql_text(pSql)); |
|
88
|
while( db_step(&q)==SQLITE_ROW ){ |
|
89
|
if( nWarnings==0 ) fossil_warning("Policy warnings:"); |
|
90
|
fossil_warning(" %s", db_column_text(&q, 0)); |
|
91
|
nWarnings++; |
|
92
|
} |
|
93
|
db_finalize(&q); |
|
94
|
return nWarnings; |
|
95
|
} |
|
96
|
|
|
97
|
/* |
|
98
|
** Print commit specific warnings from the warning-policy. |
|
99
|
*/ |
|
100
|
int issue_commit_warnings( |
|
101
|
const char *zBranch /* The branch we are committing to */ |
|
102
|
){ |
|
103
|
Blob sql = empty_blob; |
|
104
|
const char *zMatch; |
|
105
|
int nWarnings = 0; |
|
106
|
|
|
107
|
assert(zBranch!=0); |
|
108
|
if( g.zLogin==0) user_select(); |
|
109
|
zMatch = warning_policy_match_style(); |
|
110
|
blob_append_sql(&sql, |
|
111
|
"WITH list AS (" |
|
112
|
" SELECT value AS elm" |
|
113
|
" FROM json_each((" |
|
114
|
" SELECT json_extract(value, '$.commit')" |
|
115
|
" FROM config" |
|
116
|
" WHERE name='warning-policy' AND json_error_position(value)=0)))" |
|
117
|
" SELECT elm->>'message' FROM list" |
|
118
|
" WHERE (" |
|
119
|
" (elm->>'branch' IS NULL" |
|
120
|
" OR %Q %S elm->>'branch')" |
|
121
|
" AND (elm->>'except-branch' IS NULL" |
|
122
|
" OR NOT %Q %S elm->>'except-branch')" |
|
123
|
" AND (elm->>'users' IS NULL" |
|
124
|
" OR %Q IN (SELECT value FROM json_each(elm->>'users')))" |
|
125
|
" AND NOT %Q IN (SELECT value FROM json_each(elm->>'except-users')" |
|
126
|
" )", |
|
127
|
zBranch, zMatch, zBranch, zMatch, g.zLogin, g.zLogin |
|
128
|
); |
|
129
|
nWarnings = print_policy_warnings(&sql); |
|
130
|
blob_reset(&sql); |
|
131
|
return nWarnings; |
|
132
|
} |
|
133
|
|
|
134
|
/* |
|
135
|
** Print merge specific warnings from the warning-policy. |
|
136
|
*/ |
|
137
|
int issue_merge_warnings( |
|
138
|
const char *zBranch, /* The branch we are merging into */ |
|
139
|
const char *zFrom, /* The branch we are merging from */ |
|
140
|
int historyLoss /* Merging a private branch into a public branch */ |
|
141
|
){ |
|
142
|
Blob sql = empty_blob; |
|
143
|
const char *zMatch; |
|
144
|
int nWarnings = 0; |
|
145
|
|
|
146
|
assert(zBranch!=0); |
|
147
|
assert(zFrom!=0); |
|
148
|
if( g.zLogin==0) user_select(); |
|
149
|
zMatch = warning_policy_match_style(); |
|
150
|
blob_append_sql(&sql, |
|
151
|
"WITH list AS (" |
|
152
|
" SELECT value AS elm" |
|
153
|
" FROM json_each((" |
|
154
|
" SELECT json_extract(value, '$.merge')" |
|
155
|
" FROM config" |
|
156
|
" WHERE name='warning-policy' AND json_error_position(value)=0)))" |
|
157
|
" SELECT elm->>'message' FROM list" |
|
158
|
" WHERE (" |
|
159
|
" (elm->>'branch' IS NULL" |
|
160
|
" OR %Q %S elm->>'branch')" |
|
161
|
" AND (elm->>'except-branch' IS NULL" |
|
162
|
" OR NOT %Q %S elm->>'except-branch')" |
|
163
|
" AND (elm->>'from' IS NULL" |
|
164
|
" OR %Q %S elm->>'from')" |
|
165
|
" AND (elm->>'except-from' IS NULL" |
|
166
|
" OR NOT %Q %S elm->>'except-from')" |
|
167
|
" AND (elm->>'users' IS NULL" |
|
168
|
" OR %Q IN (SELECT value FROM json_each(elm->>'users')))" |
|
169
|
" AND NOT %Q IN (SELECT value FROM json_each(elm->>'except-users'))", |
|
170
|
zBranch, zMatch, zBranch, zMatch, |
|
171
|
zFrom, zMatch, zFrom, zMatch, |
|
172
|
g.zLogin, g.zLogin |
|
173
|
); |
|
174
|
if( !historyLoss ){ |
|
175
|
blob_append_sql(&sql, |
|
176
|
" AND (elm->>'unpublished' IS NULL" |
|
177
|
" OR NOT elm->>'unpublished')" |
|
178
|
); |
|
179
|
} |
|
180
|
nWarnings = print_policy_warnings(&sql); |
|
181
|
blob_reset(&sql); |
|
182
|
return nWarnings; |
|
183
|
} |
|
184
|
|
|
185
|
/* |
|
186
|
** COMMAND: test-warning-policy |
|
187
|
** Usage: %fossil test-warning-policy EVENT ?OPTIONS? |
|
188
|
** |
|
189
|
** Test what messages would be shown for a specific scenario. |
|
190
|
** Use the global -U|--user option to test for a specific user. |
|
191
|
** |
|
192
|
** Options: |
|
193
|
** --json JSON |
|
194
|
** |
|
195
|
** Options for "commit" event: |
|
196
|
** -b|--branch BRANCH Test commit to BRANCH. |
|
197
|
** |
|
198
|
** Options for "merge" event: |
|
199
|
** -b|--branch BRANCH Test merge to BRANCH. |
|
200
|
** -f|--from BRANCH Test merge from BRANCH. |
|
201
|
** -u|--unpublished Test merging from a private to a public branch. |
|
202
|
*/ |
|
203
|
void test_warning_policy_cmd(void){ |
|
204
|
const char *zEvent; |
|
205
|
const char *zJSON; |
|
206
|
|
|
207
|
if( g.argc<3 ) fossil_fatal("EVENT is required"); |
|
208
|
zEvent = g.argv[2]; |
|
209
|
db_must_be_within_tree(); |
|
210
|
|
|
211
|
zJSON = find_option("json", 0, 1); |
|
212
|
if( zJSON ){ |
|
213
|
db_begin_transaction(); |
|
214
|
db_set("warning-policy", zJSON, 0); |
|
215
|
} |
|
216
|
|
|
217
|
switch( db_int(-1, |
|
218
|
"SELECT json_error_position(value)=0 FROM config WHERE name='warning-policy'") |
|
219
|
){ |
|
220
|
case -1: fossil_fatal("The warning-policy setting is not set"); |
|
221
|
case 0: fossil_fatal("The warning-policy setting is not valid JSON"); |
|
222
|
default: break; |
|
223
|
} |
|
224
|
|
|
225
|
if( fossil_strcmp(zEvent, "commit")==0 ){ |
|
226
|
const char *zBranch = find_option("branch", "b", 1); |
|
227
|
if( zBranch==0 ) fossil_fatal("%s: missing --branch option", zEvent); |
|
228
|
verify_all_options(); |
|
229
|
issue_commit_warnings(zBranch); |
|
230
|
}else if( fossil_strcmp(zEvent, "merge")==0 ){ |
|
231
|
const char *zBranch = find_option("branch", "b", 1); |
|
232
|
const char *zFrom = find_option("from", "f", 1); |
|
233
|
int historyLoss = find_option("unpublished", "u", 0)!=0; |
|
234
|
if( zBranch==0 ) fossil_fatal("%s: missing --branch option", zEvent); |
|
235
|
if( zFrom==0 ) fossil_fatal("%s: missing --from option", zEvent); |
|
236
|
verify_all_options(); |
|
237
|
issue_merge_warnings(zBranch, zFrom, historyLoss); |
|
238
|
}else{ |
|
239
|
fossil_fatal("Unknown POLICY: %s", g.argv[2]); |
|
240
|
} |
|
241
|
|
|
242
|
if( zJSON ) db_end_transaction(1); |
|
243
|
} |
|
244
|
|
|
245
|
|