From 5f323396a6104047e5a045b17ff39eb284917e89 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Wed, 16 Nov 2016 13:40:39 +0000 Subject: [PATCH] Add tool for pulling bugs from Bugzilla. --- .gitignore | 1 + Makefile.am | 19 +++++++- common-rules.mk | 2 +- configure.ac | 6 +++ poll_bugzilla.ml | 129 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ todo-schema.sql | 6 ++- 6 files changed, 158 insertions(+), 5 deletions(-) create mode 100644 poll_bugzilla.ml diff --git a/.gitignore b/.gitignore index 6cd3a5f..282de7e 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,7 @@ Makefile.in /m4/ltversion.m4 /m4/lt~obsolete.m4 /missing +/poll-bugzilla /stamp-h1 /todo /todo.spec diff --git a/Makefile.am b/Makefile.am index 7aaa7b2..80cd1ac 100644 --- a/Makefile.am +++ b/Makefile.am @@ -24,7 +24,7 @@ EXTRA_DIST = \ META.in \ README -all_sources = $(TODO_SOURCES_ML) +all_sources = $(TODO_SOURCES_ML) $(POLL_BUGZILLA_SOURCES_ML) # You must adjust this to point to the right database host. The # database itself is hard-coded as 'todo'. Setting the environment @@ -32,7 +32,7 @@ all_sources = $(TODO_SOURCES_ML) #export PGHOST=todo export PGDATABASE=todo -bin_PROGRAMS = todo +bin_PROGRAMS = todo poll-bugzilla TODO_SOURCES_ML = \ todo_config.ml \ @@ -59,6 +59,21 @@ todo_LINK = \ $(OCAMLFIND) $(OCAMLBEST) $(OCAMLFLAGS) $(OCAMLPACKAGES) \ $(OCAMLLINKFLAGS) $(TODO_OBJECTS) -o $@ +POLL_BUGZILLA_SOURCES_ML = \ + poll_bugzilla.ml +if !HAVE_OCAMLOPT +POLL_BUGZILLA_OBJECTS = $(POLL_BUGZILLA_SOURCES_ML:.ml=.cmo) +else +POLL_BUGZILLA_OBJECTS = $(POLL_BUGZILLA_SOURCES_ML:.ml=.cmx) +endif + +poll_bugzilla_SOURCES = dummy.c +poll_bugzilla_DEPENDENCIES = $(POLL_BUGZILLA_OBJECTS) $(top_srcdir)/ocaml-link.sh +poll_bugzilla_LINK = \ + $(top_srcdir)/ocaml-link.sh -- \ + $(OCAMLFIND) $(OCAMLBEST) $(OCAMLFLAGS) $(OCAMLPACKAGES) \ + $(OCAMLLINKFLAGS) $(POLL_BUGZILLA_OBJECTS) -o $@ + BUILT_SOURCES = dummy.c dummy.c: rm -f $@ diff --git a/common-rules.mk b/common-rules.mk index 853efad..704edd8 100644 --- a/common-rules.mk +++ b/common-rules.mk @@ -19,7 +19,7 @@ CLEANFILES = *~ *.cmi *.cmo *.cmx *.cma *.cmxa *.o OCAMLFLAGS = -g -warn-error CDEFLMPSUVYZX-3 OCAMLPACKAGES = \ - -package unix,calendar,pgocaml.syntax \ + -package unix,calendar,pcre,pgocaml.syntax \ -syntax camlp4o \ -I $(top_builddir) diff --git a/configure.ac b/configure.ac index 7a01621..b1f0b60 100644 --- a/configure.ac +++ b/configure.ac @@ -86,6 +86,12 @@ if test "x$OCAML_PKG_pgocaml" = "xno"; then AC_MSG_ERROR([You must install the 'ocaml-pgocaml' library]) fi +dnl Check for OCaml package PCRE (required). +AC_CHECK_OCAML_PKG(pcre) +if test "x$OCAML_PKG_pcre" = "xno"; then + AC_MSG_ERROR([You must install the 'ocaml-pcre' library]) +fi + dnl Check for POD (for manual pages). AC_CHECK_PROG(POD2MAN,pod2man,pod2man,no) AM_CONDITIONAL([HAVE_POD2MAN], diff --git a/poll_bugzilla.ml b/poll_bugzilla.ml new file mode 100644 index 0000000..5b51e1c --- /dev/null +++ b/poll_bugzilla.ml @@ -0,0 +1,129 @@ +(* Poll Bugzilla, find new tasks, and add them to the database. + * This runs from a cron job. + *) + +open CalendarLib +open Unix +open Printf + +let quote = Filename.quote + +let products = [ + "Red Hat Enterprise Linux 6", + Printer.Date.from_string "2016-11-14"; + "Red Hat Enterprise Linux 7", + Printer.Date.from_string "2017-04-03"; +] + +let assigned_to = "rjones@redhat.com" +let states = ["NEW";"ASSIGNED";"ON_DEV";"POST";"MODIFIED"] + +let () = + let rex = Pcre.regexp "^(\\d+) (\\S+) (.*)$" in + let errors = ref 0 in + + let dbh = PGOCaml.connect ~database:"todo" () in + + List.iter ( + fun (product, my_deadline) -> + (* Query bugzilla. *) + let lines = + let cmd = + sprintf "bugzilla query -p %s -a %s -t %s --outputformat='%%{bug_id} %%{component} %%{short_desc}'" + (quote product) (quote assigned_to) + (quote (String.concat "," states)) in + let chan = open_process_in cmd in + let lines = ref [] in + (try while true do lines := input_line chan :: !lines done + with End_of_file -> ()); + let stat = close_process_in chan in + (match stat with + | WEXITED 0 -> () + | WEXITED i -> + eprintf "error: bugzilla command: exited with error %d\n%!" i; + incr errors + | WSIGNALED i -> + eprintf "error: bugzilla command: killed by signal %d\n%!" i; + incr errors + | WSTOPPED i -> + eprintf "error: bugzilla command: stopped by signal %d\n%!" i; + incr errors + ); + List.rev !lines in + + if lines <> [] then ( + (* The output is . + * The component is turned into a tag. + *) + List.iter ( + fun line -> + try + let subs = Pcre.exec ~rex line in + let bugid = Int32.of_string (Pcre.get_substring subs 1) in + let component = Pcre.get_substring subs 2 in + let description = Pcre.get_substring subs 3 in + + PGOCaml.begin_work dbh; + + (* If the bug doesn't exist in the tasks table, add it. *) + let rows = PGSQL(dbh) "select id from tasks + where rhbz = $bugid" in + let taskid = + match rows with + | [id] -> id + | [] -> + printf "new task: RHBZ#%ld %s %s\n%!" + bugid component description; + PGSQL(dbh) "insert into tasks (description, rhbz) + values ($description, $bugid)"; + PGOCaml.serial4 dbh "tasks_id_seq" + | _ -> assert false in + + (* If the component doesn't exist as a tag, create it. *) + let rows = + PGSQL(dbh) "select id from tags where name = $component" in + let tagid = + match rows with + | [id] -> id + | [] -> + printf "new tag: %s\n%!" component; + PGSQL(dbh) "insert into tags (name, colour) + values ($component, 'blue')"; + PGOCaml.serial4 dbh "tags_id_seq" + | _ -> assert false in + + (* If the bug is not tagged with the component, tag it. + * If the bug changes component, this creates a second + * tag, which is intentional. + *) + let rows = + PGSQL(dbh) "select 1 from tags_tasks, tasks, tags + where tags_tasks.taskid = tasks.id + and tags_tasks.tagid = tags.id + and tasks.rhbz = $bugid + and tags.name = $component" in + if rows <> [Some 1_l] then + PGSQL(dbh) "insert into tags_tasks (tagid, taskid) + values ($tagid, $taskid)"; + + (* Add the bug to the todo table with the appropriate + * deadline. + *) + let rows = + PGSQL(dbh) "select 1 from todo where taskid = $taskid" in + if rows <> [Some 1_l] then + PGSQL(dbh) "insert into todo (taskid, deadline) + values ($taskid, $my_deadline::date)"; + + PGOCaml.commit dbh + with + Not_found -> + eprintf "error: line did not match regular expression: %s\n%!" + line; + incr errors + ) lines + ) + ) products; + + if !errors > 0 then + exit 1 diff --git a/todo-schema.sql b/todo-schema.sql index eb4492b..5c9aeb2 100644 --- a/todo-schema.sql +++ b/todo-schema.sql @@ -49,12 +49,14 @@ create table retired ( create table tags ( id serial not null primary key, name text not null, - colour text not null references colours (colour) + colour text not null references colours (colour), + unique (name) ); create table tags_tasks ( tagid integer not null references tags (id), - taskid integer not null references tasks (id) + taskid integer not null references tasks (id), + unique (tagid, taskid) ); create table colours ( -- 1.8.3.1