diff --git a/webtools/tinderbox2/src/default_conf/ReqData.pm b/webtools/tinderbox2/src/default_conf/ReqData.pm new file mode 100644 index 000000000000..bf5f888c9a6a --- /dev/null +++ b/webtools/tinderbox2/src/default_conf/ReqData.pm @@ -0,0 +1,242 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- + +# ReqData.pm - the configuration file which describes the local +# configuration for the Req Bug Tracking system +# (http://www.draga.com/~jwise/minireq/) and its relationship to the +# tinderbox trees. + + +# $Revision: 1.1 $ +# $Date: 2001/11/16 20:43:27 $ +# $Author: kestes%walrus.com $ +# $Source: /home/hwine/cvs_conversion/cvsroot/mozilla/webtools/tinderbox2/src/default_conf/ReqData.pm,v $ +# $Name: $ + + +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/NPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Tinderbox build tool. +# +# The Initial Developer of the Original Code is Netscape Communications +# Corporation. Portions created by Netscape are +# Copyright (C) 1998 Netscape Communications Corporation. All +# Rights Reserved. +# + +# complete rewrite by Ken Estes: +# kestes@staff.mail.com Old work. +# kestes@tradinglinx.com New work. +# kestes@walrus.com Home. +# Contributor(s): + + +# This package is used for configuring the Generic Bug Tracking system +# module. I belive that this will handle most bug tracking systems. +# We assume that bugs are stored in a database (each bug has a known +# list of field names) and that a subset of the fields will be of +# interest to the tinderbox users (tinderbox is not the correct place +# to display any sort of long comment field). Tinderbox will display +# the bug number and a popup window showing relevant fields discribing +# this bug. If the users wishes more information they may click on +# the link and be taken directly to the bugtracking system for more +# information about the bug. Bugs have their state change as the bug +# is worked on and users of tinderbox wish to see information about +# the bug as the state changes. We divide state changes into two +# types: 'Progress', 'Slippage'. Most important is to get a feel for +# the number of bugs which move into bad/backward states ('REOPENED'). + +# Users will certainly need to configure the tables in this module for +# their needs. Additionally users need to define how to convert +# information about each bug to the correct tinderbox tree that this +# bug belongs. This is handled by defining the update2tree() function +# as appropriate. + + + +package ReqData; + +# This package must not use any tinderbox specific libraries. It is +# intended to be a base class. + + +$VERSION = '#tinder_version#'; + + + +$REQ_URL = ($TinderConfig::REQ_URL || + 'http://buildweb.reefedge.com/cgi-bin/req'); + +$REQ_HOME = ($TinderConfig::REQ_HOME || + "/home/req"); + +$REQ_URL = "http://buildweb.reefedge.com/cgi-bin/"; + + +$REQ_HOME = "/home/req"; + +# the name of the bug tracking field which shows bug_id + +$BUGID_FIELD_NAME = 'Ticket_Num'; + +$STATUS_FIELD_NAME = 'Action'; + + +# The values of the status field wich denote that the ticket is moving +# forward. Notice that this list may not be complete as we are only +# interested in displaying Developer progress. If the ticket is moving +# through QA tinderbox may not be the correct place to see that +# change. In particular newly opend tickets are not particularly +# interesting when monitoring the development process. + +# All status values are converted to lower case for ease of +# processing. Each value of this table corresponds to a bug column in +# the tinderbox status page. You may have as many bug columns as you +# like. If you wish to indicate that certain states are possible but +# should not be displayed then indicate the state with a null string. + +%STATUS_PROGRESS = ( + 'commented' => 'Progress', + 'created' => 'Progress', + 'given' => 'Progress', + 'killed' => 'Progress', + 'notified' => 'Progress', + 'opened' => 'Progress', + 'resolved' => 'Progress', + 'stalled' => 'Progress', + 'subject_changed' => 'Progress', + 'taken' => 'Progress', + 'untaken' => 'Progress', + 'user_set' => 'Progress', + ); + + +# Uncomment only the fields you wish displayed in the popup window, +# The fields will be displayed in the order they are listed here. +# Only uncomment fields which are interesting. Fields which are empty +# will still be displayed. + +@DISPLAY_FIELDS = ( + 'Subject', + 'Ticket_Num', + 'Complete_Action', + 'Author', + ); + + +# turn a tree name into a Req queue name. + +sub tree2queue { + my ($tree_name) = @_; + + my $queue_name = lc($tree_name); + $queue_name =~ s/^b-//; + + return $queue_name; +} + + +# turn a tree name into the name of a its file. + +sub tree2logfile { + my ($tree_name) = @_; + + my $queue_name = tree2queue($tree_name); + my $req_log = "$REQ_HOME/releng-${queue_name}/etc/req-log"; + + return $req_log; +} + + +# Given a pointer to a bug update hash, return the name of the tree to +# which this bug report belongs. Typically this will be the contents +# of a field like 'Product', (if you have one tinderbox page for each +# product in your bug database) however some projects may be more +# compicated. + +# One example of a complex function to determine tree name would be if +# each of the product product types listed in the bug tracking data +# base refers to one development project, except for a particular +# feature/platform of one particular project which is being developed +# by a separate group of developers. So the version control notion of +# trees (a set of modules on a branch) may not have a direct map into +# the bug tracking database at all times. + +# This function should return the null list '()' if the bug report +# should be ignored by the tinderbox server. The function returns a +# list of trees which should display the data about this bug update. + +sub update2tree { + my ($tinderbox_ref) = @_; + + my ($out); + + my @out = ( + 'ALL', + $tinderbox_ref->{'Tree'}, + ); + + # It might be a good idea to call TreeData::tree_exists() and ensure + # that this tree is valid, but this would make it harder for testing + # using genbugs. + + return (@out); +} + + + + + +# Given a bug id return a URL ('href') to the bug. +# If the bug tracker does not support URL's to a bug number, +# return a 'mailto: ' to someone who cares about the bug. + +sub bug_id2bug_url { + my ($tinderbox_ref) = @_; + + + $url = ( + $REQ_URL. + "/". + $tinderbox_ref->{'Queue'}. + "-req/req.cgi/show/". + $tinderbox_ref->{'Ticket_Num'}. + ""); + + return $url; +} + + +sub get_all_progress_states { + + my (@progress_states) = main::uniq( values %BTData::STATUS_PROGRESS ); + + # If the first element is null ignore it. + ($progress_states[0]) || + (shift @progress_states); + + return @progress_states; +} + + +sub is_status_valid { + my ($status) = @_; + + + # hard code for now + return 1; + + my $out = defined($STATUS_PROGRESS{$status}); + + return $out; +} + +1; + diff --git a/webtools/tinderbox2/src/lib/TinderDB/BT_Req.pm b/webtools/tinderbox2/src/lib/TinderDB/BT_Req.pm new file mode 100644 index 000000000000..d38dedb068fa --- /dev/null +++ b/webtools/tinderbox2/src/lib/TinderDB/BT_Req.pm @@ -0,0 +1,485 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# + +# TinderDB::BT_Req - A modified version of BT_Generic to allow +# tinderbox to interface with the Req bug tracking system. +# (http://www.draga.com/~jwise/minireq/) eventually the common code +# should be factrored out into separate classes but I do not have time +# now and the files are only about 400 lines long. The main +# difference is that Req requires us to parse a log file for each +# tree. Thus we pull the data into our datastructures instead of +# having the mail system push it in. + +# The current design of these BT modules does not allow me to have two +# copies of the same module (ie two colums both of thehm BT but +# configured to use different bug tracking system. This is due to +# global variables in the module and the way we configure the system +# in TinderConfig and BTData. + +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/NPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Tinderbox build tool. +# +# The Initial Developer of the Original Code is Netscape Communications +# Corporation. Portions created by Netscape are +# Copyright (C) 1998 Netscape Communications Corporation. All +# Rights Reserved. +# + +# complete rewrite by Ken Estes: +# kestes@staff.mail.com Old work. +# kestes@reefedge.com New work. +# kestes@walrus.com Home. +# Contributor(s): + + + +package TinderDB::BT_Req; + + +# We store the hash of all names who modified the tree at a +# particular time as follows: + +# $DATABASE{$tree}{$timenow}{$status}{$bug_id} = $record; + +# Where $rec is an anoymous hash of name vaule pairs from the bug +# tracking system. + +# we also store information in the metadata structure + +# $METADATA{$tree}{'updates_since_trim'} += 1; +# + +# Load standard perl libraries +use Time::Local; + +# Load Tinderbox libraries + +use lib '#tinder_libdir#'; + +use HTMLPopUp; +use MailProcess; +use TinderDB::BasicTxtDB; +use TreeData; +use ReqData; +use Utils; +use VCDisplay; + + + +$VERSION = ( qw $Revision: 1.1 $ )[1]; + +@ISA = qw(TinderDB::BasicTxtDB); + +# Add an empty object, of this DB subclass, to end of the set of all +# HTML columns. This registers the subclass with TinderDB and defines +# the order of the HTML columns. + +push @TinderDB::HTML_COLUMNS, TinderDB::BT_Req->new(); + + +# name of the bug tracking system +$REQ_NAME = $TinderConfig::REQ_NAME || "Req"; + +# Return the oldest time we have data for. +# This should not be older then the time we would keep if we +# were to trim the database now. + +sub find_last_data { + my ($tree) = @_; + + my $oldest_allowed_time = $main::TIME - $TinderDB::TRIM_SECONDS; + + my $last_tree_data = (sort keys %{ $DATABASE{$tree} })[0]; + + ($last_tree_data < $oldest_allowed_time) && + ($last_tree_data = $oldest_allowed_time); + + return $last_tree_data; +} + + +# untaint the variable and values parsed from trouble ticket mail. + +sub clean_bug_input { + my ($var, $value) = @_; + + # untaint data for safety + + $var = main::extract_printable_chars($var); + + # the input is HTML so it is a good idea to escape it + + $value = main::extract_html_chars($value); + $value = HTMLPopUp::escapeHTML($value); + + # remove spaces at beginning and end of lines + + $value =~ s/\s+$//; + $value =~ s/^\s+//; + + $var =~ s/\s+$//; + $var =~ s/^\s+//; + + + # There are spaces in some of the variable names, + # convert these to underlines to make processing easier. + + $var =~ s/\s+/_/g; + + return ($var, $value); +} + + +# remove all records from the database which are older then last_time. + +sub trim_db_history { + my ($self, $tree,) = (@_); + + my ($last_time) = $main::TIME - $TinderDB::TRIM_SECONDS; + + # sort numerically ascending + my (@times) = sort {$a <=> $b} keys %{ $DATABASE{$tree} }; + foreach $time (@times) { + ($time >= $last_time) && last; + + delete $DATABASE{$tree}{$time}; + } + + return ; +} + + + +# get the recent data from the Req log file. There is one log file +# per tree. + + +sub apply_db_updates { + my ($self, $tree, ) = @_; + + my $queue_name = ReqData::tree2queue($tree); + my $req_log = ReqData::tree2logfile($tree); + my $added_lines = 0; + my $last_tree_data = find_last_data($tree); + my $year = 1900 + (localtime(time()))[5]; + + # We may not have req running on every branch so the log files may + # not exist. + + (-r $req_log) || + next; + + open (REQ_LOG, "<$req_log") || + die("Could not open req logfile: $req_log\n"); + + foreach $line () { + + + # the log file looks like this. + + # FW: CVS update: reef/distrib/sets (#2) created via mail by Rich@reefedge.com. 12:32:37, Nov 15 + # FW: CVS update: reef/distrib/sets (#2) given to rich by jim. 13:46:13, Nov 15 + + + if ( $line =~ m! + ^(.+) # the subject can contain spaces + \s+\(\#(\d+)\)\s+ # bug number in parentheses + (.*) # the action taken, can contain spaces + \s+by\s+ # + ([A-Za-z0-9\.\@]+)\.\s+ # author (may contain '@', '.',) + # with a period terminator + (\d+)\:(\d+)\:(\d+)\,\s # time in colon delimited format + # with coma terminator + (\w+)\s(\d+)\n$ # three letter month and month day + !x ) { + + my %tinderbox = ( + 'Subject' => $1, + 'Ticket_Num' => $2, + 'Complete_Action' => $3, + 'Action' => $3, + 'Author' => $4, + 'Hour' => $5, + 'Minute' => $6, + 'Second' => $7, + 'Month' => $8, + 'Month_Day' => $9, + + 'Tree' => $tree, + 'Queue' => $queue_name, + ); + + my $month = MailProcess::monthstr2mon($tinderbox{'Month'}); + + my ($timenow) = timelocal + ( + $tinderbox{'Second'}, + $tinderbox{'Minute'}, + $tinderbox{'Hour'}, + $tinderbox{'Month_Day'}, + $month, + $year, + ); + + $tinderbox{'Timenow'} = $timenow; + + # we wish for all actions to be listed in our progress table + # so they can not encode user names. + # Examples of actions as parsed above: + # given to rich + # user set to jim@reefedge.com + + $tinderbox{'Action'} =~ s/ to .*$//; + $tinderbox{'Action'} =~ s/ via mail//; + + # skip records which are already in the database. + + ($timenow >= $last_tree_data) || + next; + + # remove special characters and convert any spaces to '_' + + foreach $key (keys %tinderbox) { + $value = $tinderbox{$key}; + ($key, $value) = clean_bug_input($key, $value); + $tinderbox{$key} = $value; + } + + $tinderbox{'tinderbox_timenow'} = $timenow; + $tinderbox{'tinderbox_status'} = $tinderbox{'Action'}; + $tinderbox{'tinderbox_bug_id'} = $tinderbox{'Ticket_Num'}; + + $DATABASE + {$tree} + {$timenow} + {$tinderbox{'Action'}} + {$tinderbox{'Ticket_Num'}} = \%tinderbox; + + $added_lines++; + + } # if matches regexp + + } # foreach $line + + close(REQ_LOG) || + die("Could not close req logfile: $req_log\n"); + + ($added_lines) || + return 0; + + $METADATA{$tree}{'updates_since_trim'}+= $added_lines; + + if ( ($METADATA{$tree}{'updates_since_trim'} > + $TinderDB::MAX_UPDATES_SINCE_TRIM) + ) { + $METADATA{$tree}{'updates_since_trim'}=0; + trim_db_history(@_); + } + + $self->savetree_db($tree); + + return $added_lines; +} + + +sub status_table_legend { + my ($out)=''; + + # I am not sure the best way to explain this to our users + + return ($out); +} + + +sub status_table_header { + my $out = ''; + + my (@progress_states) = ReqData::get_all_progress_states(); + + foreach $progress (@progress_states) { + $out .= "\t$REQ_NAME $progress\n"; + } + + return ($out); +} + + + +# clear data structures in preparation for printing a new table + +sub status_table_start { + my ($self, $row_times, $tree, ) = @_; + + # create an ordered list of all times which any data is stored + + # sort numerically descending + @DB_TIMES = sort {$b <=> $a} keys %{ $DATABASE{$tree} }; + + # adjust the $NEXT_DB to skip data which came after the first cell + # at the top of the page. We make the first cell bigger then the + # rest to allow for some overlap between pages. + + my ($first_cell_seconds) = 2*($row_times->[0] - $row_times->[1]); + my ($earliest_data) = $row_times->[0] + $first_cell_seconds; + + $NEXT_DB = 0; + while ( ($DB_TIMES[$NEXT_DB] > $earliest_data) && + ($NEXT_DB < $#DB_TIMES) ) { + $NEXT_DB++ + } + + return ; +} + + + + + +sub status_table_row { + my ($self, $row_times, $row_index, $tree, ) = @_; + + my (@outrow) = (); + + # find all the bug_ids which changed at any point in this cell. + + my (%bug_ids) = (); + + while (1) { + my ($time) = $DB_TIMES[$NEXT_DB]; + + # find the DB entries which are needed for this cell + ($time < $row_times->[$row_index]) && last; + + $NEXT_DB++; + + foreach $status (keys %{ $DATABASE{$tree}{$time} }) { + + # do not display bugs whos status_progres is null, these have + # been deemed uninteresting. + + ($ReqData::STATUS_PROGRESS{$status}) || + next; + + my ($query_links) = ''; + foreach $bug_id (sort keys %{ $DATABASE{$tree}{$time}{$status} }) { + + my ($table) = ''; + my ($num_rows) = 0; + my ($max_length) = 0; + my ($rec) = $DATABASE{$tree}{$time}{$status}{$bug_id}; + + # display all the interesting fields + + foreach $field (@ReqData::DISPLAY_FIELDS) { + + my ($value) = $rec->{$field}; + + # many fields tend to be empty because it diffs the bug + # reports and only reports the lines which change and a few + # lines of context. + + ($value) || + next; + + # $max_length = main::max($max_length , length($value)); + $num_rows++; + $table .= ( + "\t". + "$field". + ": ". + $value. + "
\n". + ""); + } # foreach $field + + ($table) || + next; + + $table = ( + "Ticket updated at: ". + localtime($time). + "
\n". + $table. + ""); + + # fix the size so that long summaries do not cause our window + # to get too large. + + $max_length = 40; + + # a link to the cgibin page which displays the bug + + my ($href) = ReqData::bug_id2bug_url($rec); + my ($window_title) = "$REQ_NAME Info bug_id: $bug_id"; + + # we display the list of names in 'teletype font' so that the + # names do not bunch together. It seems to make a difference if + # there is a between each link or not, but it does make a + # difference if we close the for each author or only for + # the group of links. + my ($query_link) = + HTMLPopUp::Link( + "linktxt" => "$bug_id", + "href" => $href, + + "windowtxt" => $table, + "windowtitle" => $window_title, + "windowheight" => ($num_rows * 25) + 100, + "windowwidth" => ($max_length * 15) + 100, + ); + + # put each link on its own line and add good comments so we + # can debug the HTML. + + $query_link = "\t\t".$query_link."\n"; + + $query_links .= ( + "\t\t\n". + ""); + + $query_links .= $query_link; + } # foreach $bug_id + + my ($progress) = $ReqData::STATUS_PROGRESS{$status}; + $bug_ids{$progress} .= $query_links; + } # foreach $status + } # while (1) + + + my (@progress_states) = ReqData::get_all_progress_states(); + + foreach $progress (@progress_states) { + + if ($bug_ids{$progress}) { + push @outrow, ( + "\t\n". + $bug_ids{$progress}. + "\t\n". + ""); + } else { + + push @outrow, ("\t". + "$HTMLPopUp::EMPTY_TABLE_CELL\n"); + } + + } + + return @outrow; +} + +1;