xaizek / rocketgit (License: AGPLv3+) (since 2018-12-09)
Light and fast Git hosting solution suitable to serve both as a hub or as a personal code storage with its tickets, pull requests, API and much more.
Commit b6a908e44053809a8c00c14e71aaea3fee524fd9

Bulk + first version of bug tracking
Author: Catalin(ux) M. BOIE
Author date (UTC): 2012-05-15 21:18
Committer name: Catalin(ux) M. BOIE
Committer date (UTC): 2012-05-15 21:18
Parent(s): 61b4d6048dca4647c779777323e23347bc89d1c0
Signing key:
Tree: aa0bd94514908b9149e1985b5c8c253b2bac1118
File Lines added Lines deleted
TODO 20 0
hooks/update 1 1
inc/bug.inc.php 329 0
inc/struct.inc.php 34 0
tests/Makefile 4 1
tests/bug.php 66 0
File TODO changed (mode: 100644) (index c0d4ecd..c735d80)
8 8 [ ] "log" does not list last entries! More exactly, seems the owner does not update repo! [ ] "log" does not list last entries! More exactly, seems the owner does not update repo!
9 9 [ ] @@branch@@ is not defined for merge requests. Should it? Probably yes, to filter them. [ ] @@branch@@ is not defined for merge requests. Should it? Probably yes, to filter them.
10 10 [ ] Create a repo and click on it; seems we get error (gabi)! [ ] Create a repo and click on it; seems we get error (gabi)!
11 [ ] Enforce Signoff-by lines per project (a new permission)
12 = reject commits without signoff!
13 Maybe, do it generic, allow a text field to enumerate what should be in a commit!
14 Also, present a list with checkboxex: at least Signoff-by, Reported-by, Acked-by!
11 15 [ ] [ ]
16 [ ] Check if we have a way to disable merge requests per project.
17 [ ] Add real name to user info.
18 [ ] Add permission to add bug tracker to a project.
19
20 == Medium ==
21 [ ] Linus on why GitHub sucks: https://github.com/torvalds/linux/pull/17#issuecomment-5654674
22 [ ] Warn if commit messages are too long (no wrap).
23 [ ] Allow possibility to send an e-mail to mainteiner from web with a pull request
24 [ ] Check https://github.com/torvalds/linux/pull/17#issuecomment-5654674
25 [ ] Merge requests e-mail: explanation of why to pull, diffstat! Maybe also the patch if is small.
26 [ ] Check git-request-pull
12 27
13 28 == Normal priority == == Normal priority ==
29 [ ] How to sign merge requests?!
30 [ ] Signal, with red, if a key was uploaded in the last X days.
31 [ ] Store in a cookie the last uid used, and if > 0, lookup e-mail and prefill
32 forgot password e-mail field.
33 [ ] Yeah BitBucket's pricing is much better they only charge on the number of collaborators.
14 34 [ ] Permit "log" to see more rows. [ ] Permit "log" to see more rows.
15 35 [ ] Allow admin to upload keys for a user. [ ] Allow admin to upload keys for a user.
16 36 [ ] Make an option to not allow a client to upload keys. [ ] Make an option to not allow a client to upload keys.
File hooks/update changed (mode: 100755) (index f579da7..4d7ab71)
... ... $a['ip'] = getenv("ROCKETGIT_IP");
35 35 $a['namespace'] = getenv("GIT_NAMESPACE"); $a['namespace'] = getenv("GIT_NAMESPACE");
36 36
37 37 rg_log("Start " . rg_array2string($a)); rg_log("Start " . rg_array2string($a));
38 rg_log("_SERVER: " . print_r($_SERVER, TRUE));
38 rg_log("_SERVER: " . rg_array2string($_SERVER));
39 39
40 40 umask(0022); umask(0022);
41 41
File inc/bug.inc.php added (mode: 100644) (index 0000000..1fb7fe9)
1 <?php
2 require_once($INC . "/util.inc.php");
3 require_once($INC . "/log.inc.php");
4 require_once($INC . "/sql.inc.php");
5 require_once($INC . "/user.inc.php");
6 require_once($INC . "/prof.inc.php");
7
8 $rg_bug_error = "";
9
10 function rg_bug_set_error($str)
11 {
12 global $rg_bug_error;
13
14 $rg_bug_error = $str;
15 }
16
17 function rg_bug_error()
18 {
19 global $rg_bug_error;
20 return $rg_bug_error;
21 }
22
23 /*
24 * We want the bug number to be consecutive per project.
25 * TODO: Big race here, two people can insert the get the same id!
26 */
27 function rg_bug_next_id($db, $repo_id)
28 {
29 rg_prof_start("bug_next_id");
30
31 $sql = "SELECT MAX(bug_id) + 1 AS max FROM bugs"
32 . " WHERE repo_id = $repo_id";
33 $res = rg_sql_query($db, $sql);
34 if ($res === FALSE) {
35 rg_bug_set_error("cannot get max (" . rg_sql_error() . ")");
36 return $ret;
37 }
38 $row = rg_sql_fetch_array($res);
39 rg_log("DEBUG: max=" . $row['max']);
40 rg_sql_free_result($res);
41
42 rg_prof_end("bug_next_id");
43
44 return empty($row['max']) ? 1 : $row['max'];
45 }
46
47 /*
48 * Return info about a bug
49 */
50 $rg_bug_info_cache = array();
51 function rg_bug_info($db, $repo_id, $bug_id)
52 {
53 global $rg_bug_info_cache;
54
55 rg_prof_start("bug_info");
56
57 $key = $repo_id . "-" . $bug_id;
58 if (isset($rg_bug_info_cache[$key]))
59 return $rg_bug_info_cache[$key];
60
61 rg_log("bug_info: repo_id=$repo_id bug_id=$bug_id");
62
63 $ret['ok'] = 0;
64 $ret['exists'] = 0;
65
66 $rg_bug_info_cache[$key] = $ret;
67
68 $sql = "SELECT * FROM bugs"
69 . " WHERE repo_id = " . $repo_id
70 . " AND bug_id = " . $bug_id;
71 $res = rg_sql_query($db, $sql);
72 if ($res === FALSE) {
73 rg_bug_set_error("cannot query (" . rg_sql_error() . ")");
74 return $ret;
75 }
76 $ret['ok'] = 1;
77 $row = rg_sql_fetch_array($res);
78 rg_sql_free_result($res);
79 if (!isset($row['bug_id'])) {
80 rg_log("\tBug not found!");
81 return $ret;
82 }
83
84 $row['exists'] = 1;
85 $row['ok'] = 1;
86
87 $rg_bug_info_cache[$key] = $row;
88
89 rg_prof_end("bug_info");
90
91 return $row;
92 }
93
94 /*
95 * Add a bug
96 */
97 function rg_bug_add($db, $repo_id, $uid, $data)
98 {
99 rg_prof_start("bug_add");
100
101 rg_log("bug_add: repo_id=$repo_id uid=$uid"
102 . " data: " . rg_array2string($data));
103
104 // TODO: test if user is allowed to add a bug
105
106 $e_data = $data;
107 $e_data['title'] = rg_sql_escape($db, $data['title']);
108 $e_data['body'] = rg_sql_escape($db, $data['body']);
109 $e_data['labels'] = rg_sql_escape($db, $data['labels']);
110
111 $itime = time();
112 $ip = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : "";
113
114 $bug_id = rg_bug_next_id($db, $repo_id);
115 if ($bug_id == 0)
116 return FALSE;
117
118 $sql = "INSERT INTO bugs (bug_id, itime, utime, repo_id, uid, ip"
119 . ", title, body, labels, state, assigned_uid, deleted)"
120 . " VALUES ($bug_id, $itime, 0, $repo_id, $uid, '$ip'"
121 . ", '" . $e_data['title'] . "'"
122 . ", '" . $e_data['body'] . "'"
123 . ", '" . $e_data['labels'] . "'"
124 . ", " . $e_data['state']
125 . ", " . $e_data['assigned_uid']
126 . ", 0)";
127 $res = rg_sql_query($db, $sql);
128 if ($res === FALSE) {
129 rg_bug_set_error("Cannot insert bug (" . rg_sql_error() . ")");
130 return FALSE;
131 }
132 rg_sql_free_result($res);
133
134 rg_prof_end("bug_add");
135
136 return $bug_id;
137 }
138
139 /*
140 * Delete a bug
141 */
142 function rg_bug_delete($db, $repo_id, $bug_id)
143 {
144 rg_prof_start("bug_delete");
145
146 rg_log("bug_delete: $repo_id=$repo_id bug_id=$bug_id");
147
148 // TODO: Check rights
149
150 $now = time();
151
152 // Only mark it as such, deletion will happen in background
153 $sql = "UPDATE bugs SET deleted = $now"
154 . " WHERE repo_id = $repo_id"
155 . " AND bug_id = $bug_id";
156 $res = rg_sql_query($db, $sql);
157 if ($res === FALSE) {
158 rg_bug_set_error("Cannot delete bug (" . rg_sql_error() . ")");
159 return FALSE;
160 }
161 rg_sql_free_result($res);
162
163 rg_prof_end("bug_delete");
164
165 return TRUE;
166 }
167
168 /*
169 * Update a bug
170 * TODO: check rights - also for create?
171 */
172 function rg_bug_update($db, $repo_id, $bug_id, $data)
173 {
174 rg_prof_start("bug_update");
175
176 rg_log("bug_update: repo_id=$repo_id bug_id=$bug_id data: "
177 . rg_array2string($data));
178
179 // First, test if it already exists
180 $bi = rg_bug_info($db, $repoid, $bug_id);
181 if ($bi['exists'] != 1)
182 return FALSE;
183
184 $e_data = $data;
185 $e_data['title'] = rg_sql_escape($db, $data['title']);
186 $e_data['body'] = rg_sql_escape($db, $data['body']);
187 $e_data['labels'] = rg_sql_escape($db, $data['labels']);
188
189 $utime = time();
190
191 $sql = "UPDATE bugs SET utime = $now"
192 . ", title = '" . $e_data['title'] . "'"
193 . ", body = '" . $e_data['body'] . "'"
194 . ", labels = '" . $e_data['labels'] . "'"
195 . ", state = " . $e_data['state']
196 . ", assigned_uid = " . $e_data['assigned_uid']
197 . " WHERE repo_id = $repo_id"
198 . " AND bug_id = $bug_id";
199 $res = rg_sql_query($db, $sql);
200 if ($res === FALSE) {
201 rg_bug_set_error("Cannot update bug (" . rg_sql_error() . ")");
202 return FALSE;
203 }
204 rg_sql_free_result($res);
205
206 rg_prof_end("bug_update");
207
208 return TRUE;
209 }
210
211 /*
212 * List bugs
213 */
214 function rg_bug_list_query($db, $url, $sql)
215 {
216 rg_prof_start("bug_list_query");
217
218 rg_log("bug_list_query: url=$url, sql=$sql...");
219
220 $res = rg_sql_query($db, $sql);
221 if ($res === FALSE) {
222 rg_bug_set_error("cannot list by query (" . rg_sql_error() . ")");
223 return FALSE;
224 }
225
226 $d = array();
227 while (($row = rg_sql_fetch_array($res))) {
228 $_line = array();
229
230 foreach ($row as $k => $v)
231 $_line[$k] = $v;
232
233 $_ui = rg_user_info($db, $row['uid'], "", "");
234 if ($_ui['exists'] != 1)
235 $_line['owner'] = "?";
236 else
237 $_line['owner'] = $_ui['username'];
238
239 $_line['body'] = nl2br(htmlspecialchars($row['body']));
240 $_line['creation'] = gmdate("Y-m-d", $row['itime']);
241 $_line['updated'] = gmdate("Y-m-d", $row['utime']);
242
243 if ($row['assigned_uid'] == 0) {
244 $_line['assigned_to'] = "-";
245 } else {
246 $_ui = rg_user_info($db, $row['assigned_uid'], "", "");
247 if ($_ui['exists'] != 1) {
248 $_line['assigned_to'] = "?";
249 } else {
250 $_line['assigned_to'] = $_ui['username'];
251 }
252 }
253
254 $d[] = $_line;
255 }
256 rg_sql_free_result($res);
257
258 rg_prof_end("bug_list_query");
259
260 return rg_template_table("bug/list", $d, array());
261 }
262
263 /*
264 *
265 */
266 function rg_bug_list($db, $url, $repo_id)
267 {
268 rg_log("bug_list: url=$url, repo_id=$repo_id");
269
270 $sql = "SELECT * FROM bugs"
271 . " WHERE repo_id = $repo_id"
272 . " ORDER BY itime";
273
274 return rg_bug_list_query($db, $url, $sql);
275 }
276
277 /*
278 *
279 */
280 function rg_bug_search($db, $q)
281 {
282 rg_log("bug_search: q=$q...");
283
284 $e_q = rg_sql_escape($db, $q);
285
286 $sql = "SELECT * FROM bugs"
287 . " WHERE deleted = 0"
288 . " AND (title ILIKE '%$e_q%' OR body ILIKE '%$e_q%')"
289 . " ORDER BY itime"
290 . " LIMIT 10";
291
292 return rg_bug_list_query($db, "", $sql);
293 }
294
295 /*
296 * Add a note for a bug
297 */
298 function rg_bug_note_add($db, $repo_id, $bug_id, $uid, $data)
299 {
300 rg_prof_start("bug_note_add");
301
302 rg_log("bug_note_add: repo_id=$repo_id bug_id=$bug_id"
303 . " data: " . rg_array2string($data));
304
305 // TODO: test if user is allowed to add a note
306
307 $e_data = $data;
308 $e_data['note'] = rg_sql_escape($db, $data['note']);
309
310 $itime = time();
311 $ip = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : "?";
312
313 $sql = "INSERT INTO bug_notes (repo_id, bug_id, itime, uid, ip"
314 . ", note)"
315 . " VALUES ($repo_id, $bug_id, $itime, $uid, '$ip'"
316 . ", '" . $e_data['note'] . "')";
317 $res = rg_sql_query($db, $sql);
318 if ($res === FALSE) {
319 rg_bug_set_error("Cannot insert bug note (" . rg_sql_error() . ")");
320 return FALSE;
321 }
322 rg_sql_free_result($res);
323
324 rg_prof_end("bug_note_add");
325
326 return TRUE;
327 }
328
329 ?>
File inc/struct.inc.php changed (mode: 100644) (index f0e7072..886ac19)
... ... $rg_sql_struct[5]['tables'] = array(
119 119 . ")" . ")"
120 120 ); );
121 121
122 $rg_sql_struct[6] = array();
123 $rg_sql_struct[6]['tables'] = array(
124 "bugs" => "CREATE TABLE bugs ("
125 . "repo_id INT NOT NULL"
126 . ", bug_id INT NOT NULL"
127 . ", itime INT NOT NULL"
128 . ", utime INT NOT NULL"
129 . ", uid INT NOT NULL"
130 . ", ip TEXT NOT NULL"
131 . ", title TEXT NOT NULL"
132 . ", body TEXT NOT NULL"
133 . ", labels TEXT NOT NULL"
134 . ", state INT NOT NULL"
135 . ", assigned_uid INT NOT NULL"
136 . ", deleted INT NOT NULL"
137 . ")",
138 "bug_notes" => "CREATE TABLE bug_notes ("
139 . "repo_id INT NOT NULL"
140 . ", bug_id INT NOT NULL"
141 . ", note TEXT NOT NULL"
142 . ", itime INT NOT NULL"
143 . ", uid INT NOT NULL"
144 . ", ip TEXT NOT NULL"
145 . ")"
146 );
147 $rg_sql_struct[6]['other'] = array(
148 "bugs_index_repo_id_bug_id" => "CREATE UNIQUE INDEX bugs_i_repo_id_bug_id"
149 . " ON bugs (repo_id, bug_id)",
150 "bugs_index_itime" => "CREATE INDEX bugs_i_itime"
151 . " ON bugs (itime)",
152 "bug_notes_index_repo_id_bug_id" => "CREATE UNIQUE INDEX bug_notes_i_repo_id_bug_id"
153 . " ON bug_notes (repo_id, bug_id)"
154 );
155
122 156 // This must be the last line // This must be the last line
123 157 $rg_sql_schema_ver = count($rg_sql_struct); $rg_sql_schema_ver = count($rg_sql_struct);
124 158
File tests/Makefile changed (mode: 100644) (index b74c6b8..e5d6bc2)
1 tests := util db keys repo rights state user git prof hook_update hook_update_anon
1 tests := util db keys repo rights state user git prof hook_update hook_update_anon bug
2 2 .PHONY: $(tests) .PHONY: $(tests)
3 3
4 4 all: $(tests) all: $(tests)
 
... ... git:
30 30 prof: prof:
31 31 php prof.php php prof.php
32 32
33 bug:
34 php bug.php
35
33 36 hook_update: hook_update:
34 37 ./hook_update.sh ./hook_update.sh
35 38
File tests/bug.php added (mode: 100644) (index 0000000..f192688)
1 <?php
2 error_reporting(E_ALL | E_STRICT);
3 ini_set("track_errors", "On");
4
5 $INC = "../inc";
6 require_once($INC . "/bug.inc.php");
7 require_once($INC . "/sql.inc.php");
8 require_once($INC . "/struct.inc.php");
9
10 rg_log_set_file("bug.log");
11
12 $rg_sql_debug = 1;
13
14 $db = rg_sql_open("dbname=trg");
15 if ($db === FALSE) {
16 rg_log("Cannot create a database (" . rg_sql_error() . ")!");
17 exit(1);
18 }
19
20 $r = rg_sql_struct_update_if_needed($db, RG_DROP_TABLES|RG_IGNORE_ERRORS);
21 if ($r !== TRUE) {
22 rg_log("Cannot create struct (" . rg_sql_error() . ")!");
23 exit(1);
24 }
25
26 // defaults
27 $repo_id = 100;
28 $uid = 1;
29
30 $data = array("title" => "Bug title",
31 "body" => "This is the body\nof the\nbug. <>",
32 "labels" => "label1,label2,label3",
33 "state" => 1,
34 "assigned_uid" => 6);
35 $r = rg_bug_add($db, $repo_id, $uid, $data);
36 if ($r === FALSE) {
37 rg_log("Cannot insert a bug (" . rg_bug_error() . ")!");
38 exit(1);
39 }
40 $bug_id = $r;
41
42 $uid = 10;
43 $data = array("note" => "This is just a note.");
44 $r = rg_bug_note_add($db, $repo_id, $bug_id, $uid, $data);
45 if ($r === FALSE) {
46 rg_log("Cannot add a note (" . rg_bug_error() . ")!");
47 exit(1);
48 }
49
50 $r = rg_bug_info($db, $repo_id, $bug_id);
51 if ($r['exists'] != 1) {
52 rg_log("Cannot lookup a bug (" . rg_bug_error() . ")!");
53 exit(1);
54 }
55
56 // test a non existing bug
57 $r = rg_bug_info($db, $repo_id, 0);
58 if ($r['exists'] != 0) {
59 rg_log("Wrong bug number (0) returned valid data!");
60 exit(1);
61 }
62
63 rg_sql_close($db);
64
65 echo "bug: OK!\n";
66 ?>
Hints

Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://code.reversed.top/user/xaizek/rocketgit

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@code.reversed.top/user/xaizek/rocketgit

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a pull request:
... clone the repository ...
... make some changes and some commits ...
git push origin master