add support for the Req Ticketing system.

This commit is contained in:
kestes%walrus.com 2001-11-16 20:43:30 +00:00
parent 930b59df79
commit 6bc80bec3b
2 changed files with 727 additions and 0 deletions

View File

@ -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;

View File

@ -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 (<REQ_LOG>) {
# 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<th>$REQ_NAME $progress</th>\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".
"<tt>$field</tt>".
": ".
$value.
"<br>\n".
"");
} # foreach $field
($table) ||
next;
$table = (
"Ticket updated at: ".
localtime($time).
"<br>\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 <cr> between each link or not, but it does make a
# difference if we close the <tt> for each author or only for
# the group of links.
my ($query_link) =
HTMLPopUp::Link(
"linktxt" => "<tt>$bug_id</tt>",
"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<!-- Req: ".("bug_id: $bug_id, ".
"Time: '".localtime($time)."', ".
"Progress: $progress, ".
"Status: $status, ".
"Tree: $tree, ".
"").
" -->\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<td align=center>\n".
$bug_ids{$progress}.
"\t</td>\n".
"");
} else {
push @outrow, ("\t<!-- skipping: Req: Progress: $progress tree: $tree -->".
"<td align=center>$HTMLPopUp::EMPTY_TABLE_CELL</td>\n");
}
}
return @outrow;
}
1;