5b51e1c049717f065f0f8bce64c2d7a541eabd67
[todo.git] / poll_bugzilla.ml
1 (* Poll Bugzilla, find new tasks, and add them to the database.
2  * This runs from a cron job.
3  *)
4
5 open CalendarLib
6 open Unix
7 open Printf
8
9 let quote = Filename.quote
10
11 let products = [
12   "Red Hat Enterprise Linux 6",
13   Printer.Date.from_string "2016-11-14";
14   "Red Hat Enterprise Linux 7",
15   Printer.Date.from_string "2017-04-03";
16 ]
17
18 let assigned_to = "rjones@redhat.com"
19 let states = ["NEW";"ASSIGNED";"ON_DEV";"POST";"MODIFIED"]
20
21 let () =
22   let rex = Pcre.regexp "^(\\d+) (\\S+) (.*)$" in
23   let errors = ref 0 in
24
25   let dbh = PGOCaml.connect ~database:"todo" () in
26
27   List.iter (
28     fun (product, my_deadline) ->
29       (* Query bugzilla. *)
30       let lines =
31         let cmd =
32           sprintf "bugzilla query -p %s -a %s -t %s --outputformat='%%{bug_id} %%{component} %%{short_desc}'"
33                   (quote product) (quote assigned_to)
34                   (quote (String.concat "," states)) in
35         let chan = open_process_in cmd in
36         let lines = ref [] in
37         (try while true do lines := input_line chan :: !lines done
38          with End_of_file -> ());
39         let stat = close_process_in chan in
40         (match stat with
41          | WEXITED 0 -> ()
42          | WEXITED i ->
43             eprintf "error: bugzilla command: exited with error %d\n%!" i;
44             incr errors
45          | WSIGNALED i ->
46             eprintf "error: bugzilla command: killed by signal %d\n%!" i;
47             incr errors
48          | WSTOPPED i ->
49             eprintf "error: bugzilla command: stopped by signal %d\n%!" i;
50             incr errors
51         );
52         List.rev !lines in
53
54       if lines <> [] then (
55         (* The output is <BUG> <COMPONENT> <DESCRIPTION>.
56          * The component is turned into a tag.
57          *)
58         List.iter (
59           fun line ->
60             try
61               let subs = Pcre.exec ~rex line in
62               let bugid = Int32.of_string (Pcre.get_substring subs 1) in
63               let component = Pcre.get_substring subs 2 in
64               let description = Pcre.get_substring subs 3 in
65
66               PGOCaml.begin_work dbh;
67
68               (* If the bug doesn't exist in the tasks table, add it. *)
69               let rows = PGSQL(dbh) "select id from tasks
70                                       where rhbz = $bugid" in
71               let taskid =
72                 match rows with
73                 | [id] -> id
74                 | [] ->
75                    printf "new task: RHBZ#%ld %s %s\n%!"
76                           bugid component description;
77                    PGSQL(dbh) "insert into tasks (description, rhbz)
78                                           values ($description, $bugid)";
79                    PGOCaml.serial4 dbh "tasks_id_seq"
80                 | _ -> assert false in
81
82               (* If the component doesn't exist as a tag, create it. *)
83               let rows =
84                 PGSQL(dbh) "select id from tags where name = $component" in
85               let tagid =
86                 match rows with
87                 | [id] -> id
88                 | [] ->
89                    printf "new tag: %s\n%!" component;
90                    PGSQL(dbh) "insert into tags (name, colour)
91                                          values ($component, 'blue')";
92                    PGOCaml.serial4 dbh "tags_id_seq"
93                 | _ -> assert false in
94
95               (* If the bug is not tagged with the component, tag it.
96                * If the bug changes component, this creates a second
97                * tag, which is intentional.
98                *)
99               let rows =
100                 PGSQL(dbh) "select 1 from tags_tasks, tasks, tags
101                              where tags_tasks.taskid = tasks.id
102                                and tags_tasks.tagid = tags.id
103                                and tasks.rhbz = $bugid
104                                and tags.name = $component" in
105               if rows <> [Some 1_l] then
106                 PGSQL(dbh) "insert into tags_tasks (tagid, taskid)
107                                             values ($tagid, $taskid)";
108
109               (* Add the bug to the todo table with the appropriate
110                * deadline.
111                *)
112               let rows =
113                 PGSQL(dbh) "select 1 from todo where taskid = $taskid" in
114               if rows <> [Some 1_l] then
115                 PGSQL(dbh) "insert into todo (taskid, deadline)
116                                       values ($taskid, $my_deadline::date)";
117
118               PGOCaml.commit dbh
119             with
120               Not_found ->
121                 eprintf "error: line did not match regular expression: %s\n%!"
122                         line;
123                 incr errors
124         ) lines
125       )
126   ) products;
127
128   if !errors > 0 then
129     exit 1