[Bps-public-commit] RT-Extension-CommandByMail branch master updated. 3.01-8-g4afefe8
BPS Git Server
git at git.bestpractical.com
Tue Dec 26 20:03:40 UTC 2023
This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "RT-Extension-CommandByMail".
The branch, master has been updated
via 4afefe852423d3874277a0d2403ff6ba8ba108da (commit)
via b271e53db4d8d4468a981fe1dfb3e0c6c937f65c (commit)
via e669929908a99b1710df53a6e8ef54a49d7a4d4d (commit)
via 3555d86b4876674a08e42ce7caa95fbcf7da04ee (commit)
via 76f6f68fc1be930e899dabc7faecc022cd9dedc1 (commit)
via ee0ae3d0175cb626dbf2d50e3984141f1734c9a4 (commit)
via 67ae57a2f3f4db0e3bfa015e7ac7aed830164ef8 (commit)
via e9a90c41f8d4142ea4abdc0700c381aad9d64b4f (commit)
from 702177074a7d60404bbecb2db8febf2c7be3f250 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit 4afefe852423d3874277a0d2403ff6ba8ba108da
Merge: 7021770 b271e53
Author: sunnavy <sunnavy at bestpractical.com>
Date: Tue Dec 26 15:00:32 2023 -0500
Merge branch 'add-custom-role-command'
commit b271e53db4d8d4468a981fe1dfb3e0c6c937f65c
Author: sunnavy <sunnavy at bestpractical.com>
Date: Tue Dec 26 14:59:48 2023 -0500
Bump rt minimal version to 4.4 where custom roles were added
diff --git a/META.yml b/META.yml
index e97b2dd..b086058 100644
--- a/META.yml
+++ b/META.yml
@@ -31,4 +31,4 @@ resources:
repository: https://github.com/bestpractical/rt-extension-commandbymail
version: '3.01'
x_module_install_rtx_version: '0.43'
-x_requires_rt: 4.0.0
+x_requires_rt: 4.4.0
diff --git a/Makefile.PL b/Makefile.PL
index 8bd1a56..979ed05 100644
--- a/Makefile.PL
+++ b/Makefile.PL
@@ -5,6 +5,7 @@ RTx ('RT-Extension-CommandByMail');
no_index package => "RT::Extension::CommandByMail::Test";
build_requires('Test::More');
build_requires('IPC::Open2');
+requires_rt('4.4.0');
repository('https://github.com/bestpractical/rt-extension-commandbymail');
diff --git a/README b/README
index c396158..593c4eb 100644
--- a/README
+++ b/README
@@ -2,7 +2,7 @@ NAME
RT::Extension::CommandByMail - Change ticket metadata via email
RT VERSION
- Works with RT 4.0, 4.2, 4.4, 5.0
+ Works with RT 4.4, 5.0
SYNOPSIS
(Send email with content that looks like the following.)
diff --git a/lib/RT/Extension/CommandByMail.pm b/lib/RT/Extension/CommandByMail.pm
index 7734ae7..ee71696 100644
--- a/lib/RT/Extension/CommandByMail.pm
+++ b/lib/RT/Extension/CommandByMail.pm
@@ -19,7 +19,7 @@ RT::Extension::CommandByMail - Change ticket metadata via email
=head1 RT VERSION
-Works with RT 4.0, 4.2, 4.4, 5.0
+Works with RT 4.4, 5.0
=head1 SYNOPSIS
commit e669929908a99b1710df53a6e8ef54a49d7a4d4d
Author: sunnavy <sunnavy at bestpractical.com>
Date: Tue Dec 26 11:37:23 2023 -0500
Update tests to catch the new "User ... not found" warning
diff --git a/xt/update.t b/xt/update.t
index 77c9d40..36781a5 100644
--- a/xt/update.t
+++ b/xt/update.t
@@ -2,6 +2,7 @@ use strict;
use warnings;
use RT::Extension::CommandByMail::Test tests => undef;
+use Test::Warn;
my $test = 'RT::Extension::CommandByMail::Test';
my $test_ticket_id;
@@ -156,9 +157,10 @@ END
diag("set watchers on update") if $ENV{'TEST_VERBOSE'};
-foreach my $field ( qw(Requestor Cc AdminCc) ) {
- my $value = 'test at localhost';
- my $text = <<END;
+warning_like {
+ foreach my $field (qw(Requestor Cc AdminCc)) {
+ my $value = 'test at localhost';
+ my $text = <<END;
Subject: [$RT::rtname #$test_ticket_id] test
From: root\@localhost
@@ -166,14 +168,15 @@ $field: $value
test
END
- my (undef, $id) = $test->send_via_mailgate( $text );
- is($id, $test_ticket_id, "updated ticket");
- my $obj = RT::Ticket->new( $RT::SystemUser );
- $obj->Load( $id );
- is($obj->id, $id, "loaded ticket");
- my $method = $field .'Addresses';
- is($obj->$method(), $value, 'set '. $field );
-}
+ my ( undef, $id ) = $test->send_via_mailgate($text);
+ is( $id, $test_ticket_id, "updated ticket" );
+ my $obj = RT::Ticket->new($RT::SystemUser);
+ $obj->Load($id);
+ is( $obj->id, $id, "loaded ticket" );
+ my $method = $field . 'Addresses';
+ is( $obj->$method(), $value, 'set ' . $field );
+ }
+} qr/User 'test\@localhost' not found/;
diag("add requestor on update") if $ENV{'TEST_VERBOSE'};
commit 3555d86b4876674a08e42ce7caa95fbcf7da04ee
Author: Ronaldo Richieri <ronaldo at bestpractical.com>
Date: Fri Dec 22 17:03:41 2023 -0300
Update POD with group support for Watchers commands
diff --git a/lib/RT/Extension/CommandByMail.pm b/lib/RT/Extension/CommandByMail.pm
index 63a40aa..7734ae7 100644
--- a/lib/RT/Extension/CommandByMail.pm
+++ b/lib/RT/Extension/CommandByMail.pm
@@ -208,6 +208,8 @@ can be used several times and/or with C<Add> and C<Del> prefixes,
for example C<Requestor> comand set requestor(s) and the current
requestors would be deleted, but C<AddRequestor> command adds
to the current list.
+For groups, you must prefix the group name with C<group:>. For example,
+C<AddAdminCc: group:MyGroupname>.
Requestor: <address> Set requestor(s) using the email address
AddRequestor: <address> Add new requestor using the email address
commit 76f6f68fc1be930e899dabc7faecc022cd9dedc1
Author: Ronaldo Richieri <ronaldo at bestpractical.com>
Date: Fri Dec 22 16:58:35 2023 -0300
Add full group support to Watchers commands
Previously, the Watchers commands such as AddRequestor, DelCc, etc. would
only work mainly with users.
We now add full group support to these commands when updating the ticket.
diff --git a/lib/RT/Extension/CommandByMail.pm b/lib/RT/Extension/CommandByMail.pm
index 29bb1e3..63a40aa 100644
--- a/lib/RT/Extension/CommandByMail.pm
+++ b/lib/RT/Extension/CommandByMail.pm
@@ -486,23 +486,66 @@ sub ProcessCommands {
$results{ $attribute }->{value} = $cmds{ lc $attribute };
}
+
+ # Parses core WATCHERS and Custom Roles related commands
+ # First, add custom roles from this queue to the list of
+ # attributes we are going to parse
+ $custom_roles->LimitToObjectId( $queue->id );
+ my %custom_role_grouptype_to_name;
+ while ( my $custom_role = $custom_roles->Next ) {
+ $custom_role_grouptype_to_name{ $custom_role->GroupType }
+ = $custom_role->Name;
+ push @WATCHER_ATTRIBUTES, $custom_role->GroupType;
+ }
+ # Then, parse the commands
foreach my $type ( @WATCHER_ATTRIBUTES ) {
- my %tmp = _ParseAdditiveCommand( \%cmds, 1, $type );
+ my $command_suffix = $type;
+ $command_suffix = "CustomRole{". $custom_role_grouptype_to_name{ $type } ."}"
+ if $type =~ /^RT::CustomRole-/;
+ my %tmp = _ParseAdditiveCommand( \%cmds, 1, $command_suffix );
next unless keys %tmp;
- $tmp{'Default'} = [ do {
- my $method = $type;
- $method .= 's' if $type eq 'Requestor';
- $args{'Ticket'}->$method->MemberEmailAddresses;
- } ];
+ # Convert values to ID so we can better compare with the existing
+ # values when we are updating
+ # %tmp can be originally something like
+ # ( 'Add' => [ 'user1 at example.com', 'group:group1', 'nonexistantuser at example.com' ] )
+ # after _ParseAdditiveCommand, found objects will be converted to
+ # PrincipalId's, so it will be turned into something like the following:
+ # ( 'Add' => [ 1, 2, 'nonexistantuser at example' ] )
+ _ReplaceUserAndGroupById( \%tmp );
+
+ my $role_group;
+ if ( $type eq 'Requestor' )
+ {
+ $role_group = $ticket_as_user->Requestors;
+ }
+ elsif ( $type =~ /^RT::CustomRole-/ )
+ {
+ $role_group = $ticket_as_user->RoleGroup($type);
+ }
+ else
+ {
+ # AdminCc, Cc
+ $role_group = $ticket_as_user->$type;
+ }
+
+ my $members = $role_group->MembersObj( Recursively => 0 );
+
+ my @res;
+ while ( my $member = $members->Next ) {
+ push @res, $member->MemberId;
+ }
+
+ $tmp{'Default'} = [ @res ];
my ($add, $del) = _CompileAdditiveForUpdate( %tmp );
+
foreach my $text ( @$del ) {
- my $user = RT::User->new($RT::SystemUser);
- $user->LoadByEmail($text) if $text =~ /\@/;
- $user->Load($text) unless $user->id;
+ # if we are removing a watcher, it is already has a user
+ # in the system, so emails will not be useful here
+ next if $text =~ /\@/;
my ( $val, $msg ) = $ticket_as_user->DeleteWatcher(
Type => $type,
- PrincipalId => $user->PrincipalId,
+ PrincipalId => $text,
);
push @{ $results{ 'Del'. $type } }, {
value => $text,
@@ -511,15 +554,9 @@ sub ProcessCommands {
};
}
foreach my $text ( @$add ) {
- my $user = RT::User->new($RT::SystemUser);
- $user->LoadByEmail($text) if $text =~ /\@/;
- $user->Load($text) unless $user->id;
my ( $val, $msg ) = $ticket_as_user->AddWatcher(
Type => $type,
- $user->id
- ? (PrincipalId => $user->PrincipalId)
- : (Email => $text)
- ,
+ $text =~ /\D/ ? (Email => $text) : (PrincipalId => $text),
);
push @{ $results{ 'Add'. $type } }, {
value => $text,
@@ -644,59 +681,6 @@ sub ProcessCommands {
}
}
- $custom_roles->LimitToObjectId( $queue->id );
- while ( my $custom_role = $custom_roles->Next ) {
- my %tmp = _ParseAdditiveCommand( \%cmds, 0, "CustomRole{". $custom_role->Name ."}" );
- next unless keys %tmp;
-
- # Convert values to ID so we can better compare with the existing
- # values when we are updating
- # %tmp can be originally something like
- # ( 'Add' => [ 'user1 at example.com', 'group:group1', 'nonexistantuser at example.com' ] )
- # after _ParseAdditiveCommand, found objects will be converted to
- # PrincipalId's, so it will be turned into something like the following:
- # ( 'Add' => [ 1, 2, 'nonexistantuser at example' ] )
- _ReplaceUserAndGroupById( \%tmp );
-
- my $role_group = $ticket_as_user->RoleGroup($custom_role->GroupType);
- my $custom_role_members = $role_group->MembersObj( Recursively => 0 );
-
- my @res;
- while ( my $member = $custom_role_members->Next ) {
- push @res, $member->MemberId;
- }
-
- $tmp{'Default'} = [ @res ];
- my ($add, $del) = _CompileAdditiveForUpdate( %tmp );
-
- foreach my $text ( @$del ) {
- # if we are removing a watcher, it is already has a user
- # in the system, so emails will not be useful here
- next if $text =~ /\@/;
- my ( $val, $msg ) = $ticket_as_user->DeleteWatcher(
- Type => $custom_role->GroupType,
- PrincipalId => $text,
- );
- push @{ $results{ 'Del'. "CustomRole{". $custom_role->Name ."}" } }, {
- value => $text,
- result => $val,
- message => $msg
- };
- }
- foreach my $text ( @$add ) {
- my ( $val, $msg ) = $ticket_as_user->AddWatcher(
- Type => $custom_role->GroupType,
- $text =~ /\D/ ? (Email => $text) : (PrincipalId => $text),
- );
- push @{ $results{ 'Add'. "CustomRole{". $custom_role->Name ."}" } }, {
- value => $text,
- result => $val,
- message => $msg
- };
-
- }
- }
-
foreach my $attribute (grep $_ eq 'Status', @REGULAR_ATTRIBUTES) {
next unless defined $cmds{ lc $attribute };
next if $ticket_as_user->$attribute() eq $cmds{ lc $attribute };
commit ee0ae3d0175cb626dbf2d50e3984141f1734c9a4
Author: Ronaldo Richieri <ronaldo at bestpractical.com>
Date: Fri Dec 22 16:58:28 2023 -0300
Update POD with Custom Role support
diff --git a/README b/README
index 9d178f3..c396158 100644
--- a/README
+++ b/README
@@ -154,10 +154,12 @@ DESCRIPTION
TimeLeft: <minutes>
Watchers
- Manage watchers: requestors, ccs and admin ccs. This commands can be
+ Manage watchers: requestors, ccs and admin ccs. These commands can be
used several times and/or with Add and Del prefixes, for example
Requestor comand set requestor(s) and the current requestors would be
- deleted, but AddRequestor command adds to the current list.
+ deleted, but AddRequestor command adds to the current list. For groups,
+ you must prefix the group name with group:. For example, AddAdminCc:
+ group:MyGroupname.
Requestor: <address> Set requestor(s) using the email address
AddRequestor: <address> Add new requestor using the email address
@@ -169,6 +171,20 @@ DESCRIPTION
AddAdminCc: <address> Add new AdminCc watcher using the email address
DelAdminCc: <address> Remove email address as AdminCc watcher
+ Custom Roles
+ Manage custom roles of the ticket. These commands can be used several
+ times and/or with Add and Del prefixes. If you have a Custom Role called
+ Customer for example, you can pass the command CustomRole.{Customer} to
+ set the members of that role. You can pass either a username or an email
+ address. For groups, you must prefix the group name with group:. For
+ example, CustomRole.{Customer}: group:MyGroupname.
+
+ CustomRole.{Customer}: set the members of the Customer Custom Role
+ AddCustomRole.{Customer}: add members to the Customer Custom Role
+ DelCustomRole.{Customer}: remove members from the Customer Custom Role
+
+ Replace Customer with the name of your Custom Role.
+
Links
Manage links. These commands are also could be used several times in one
message.
diff --git a/lib/RT/Extension/CommandByMail.pm b/lib/RT/Extension/CommandByMail.pm
index dc5ad47..29bb1e3 100644
--- a/lib/RT/Extension/CommandByMail.pm
+++ b/lib/RT/Extension/CommandByMail.pm
@@ -203,7 +203,7 @@ value.
=head3 Watchers
-Manage watchers: requestors, ccs and admin ccs. This commands
+Manage watchers: requestors, ccs and admin ccs. These commands
can be used several times and/or with C<Add> and C<Del> prefixes,
for example C<Requestor> comand set requestor(s) and the current
requestors would be deleted, but C<AddRequestor> command adds
@@ -219,6 +219,22 @@ to the current list.
AddAdminCc: <address> Add new AdminCc watcher using the email address
DelAdminCc: <address> Remove email address as AdminCc watcher
+=head3 Custom Roles
+
+Manage custom roles of the ticket.
+These commands can be used several times and/or with C<Add> and C<Del>
+prefixes. If you have a Custom Role called C<Customer> for example, you can
+pass the command C<CustomRole.{Customer}> to set the members of that role.
+You can pass either a username or an email address.
+For groups, you must prefix the group name with C<group:>. For example,
+C<CustomRole.{Customer}: group:MyGroupname>.
+
+ CustomRole.{Customer}: set the members of the Customer Custom Role
+ AddCustomRole.{Customer}: add members to the Customer Custom Role
+ DelCustomRole.{Customer}: remove members from the Customer Custom Role
+
+Replace C<Customer> with the name of your Custom Role.
+
=head3 Links
Manage links. These commands are also could be used several times in one
commit 67ae57a2f3f4db0e3bfa015e7ac7aed830164ef8
Author: Ronaldo Richieri <ronaldo at bestpractical.com>
Date: Fri Dec 22 10:15:12 2023 -0300
Add Custom Role update support
Add new commands that allows do update ticket custom roles:
- CustomRole
- AddCustomRole
- DelCustomRole
Also update minimal version of RT to 5.0.4 since it's required for
one of the new code blocks.
diff --git a/lib/RT/Extension/CommandByMail.pm b/lib/RT/Extension/CommandByMail.pm
index 88111f5..dc5ad47 100644
--- a/lib/RT/Extension/CommandByMail.pm
+++ b/lib/RT/Extension/CommandByMail.pm
@@ -420,6 +420,14 @@ sub ProcessCommands {
my $transaction;
+ # Prepare Custom Roles. We will restrict also later to the ticket
+ # queue or to the creation queue according to the operation we are
+ # doing (create / update)
+ my $custom_roles = RT::CustomRoles->new( $args{'CurrentUser'} );
+ if ( RT::Handle::cmp_version($RT::VERSION, '5.0.4') >= 0 ) {
+ $custom_roles->LimitToLookupType( 'RT::Queue-RT::Ticket' );
+ }
+
# If we're updating.
if ( $args{'Ticket'}->id ) {
$ticket_as_user->Load( $args{'Ticket'}->id );
@@ -620,6 +628,59 @@ sub ProcessCommands {
}
}
+ $custom_roles->LimitToObjectId( $queue->id );
+ while ( my $custom_role = $custom_roles->Next ) {
+ my %tmp = _ParseAdditiveCommand( \%cmds, 0, "CustomRole{". $custom_role->Name ."}" );
+ next unless keys %tmp;
+
+ # Convert values to ID so we can better compare with the existing
+ # values when we are updating
+ # %tmp can be originally something like
+ # ( 'Add' => [ 'user1 at example.com', 'group:group1', 'nonexistantuser at example.com' ] )
+ # after _ParseAdditiveCommand, found objects will be converted to
+ # PrincipalId's, so it will be turned into something like the following:
+ # ( 'Add' => [ 1, 2, 'nonexistantuser at example' ] )
+ _ReplaceUserAndGroupById( \%tmp );
+
+ my $role_group = $ticket_as_user->RoleGroup($custom_role->GroupType);
+ my $custom_role_members = $role_group->MembersObj( Recursively => 0 );
+
+ my @res;
+ while ( my $member = $custom_role_members->Next ) {
+ push @res, $member->MemberId;
+ }
+
+ $tmp{'Default'} = [ @res ];
+ my ($add, $del) = _CompileAdditiveForUpdate( %tmp );
+
+ foreach my $text ( @$del ) {
+ # if we are removing a watcher, it is already has a user
+ # in the system, so emails will not be useful here
+ next if $text =~ /\@/;
+ my ( $val, $msg ) = $ticket_as_user->DeleteWatcher(
+ Type => $custom_role->GroupType,
+ PrincipalId => $text,
+ );
+ push @{ $results{ 'Del'. "CustomRole{". $custom_role->Name ."}" } }, {
+ value => $text,
+ result => $val,
+ message => $msg
+ };
+ }
+ foreach my $text ( @$add ) {
+ my ( $val, $msg ) = $ticket_as_user->AddWatcher(
+ Type => $custom_role->GroupType,
+ $text =~ /\D/ ? (Email => $text) : (PrincipalId => $text),
+ );
+ push @{ $results{ 'Add'. "CustomRole{". $custom_role->Name ."}" } }, {
+ value => $text,
+ result => $val,
+ message => $msg
+ };
+
+ }
+ }
+
foreach my $attribute (grep $_ eq 'Status', @REGULAR_ATTRIBUTES) {
next unless defined $cmds{ lc $attribute };
next if $ticket_as_user->$attribute() eq $cmds{ lc $attribute };
@@ -681,6 +742,14 @@ sub ProcessCommands {
$create_args{ 'CustomField-' . $cf->id } = [ _CompileAdditiveForCreate(%tmp) ];
}
+ # Canonicalize custom roles
+ $custom_roles->LimitToObjectId( $queue->id );
+ while ( my $custom_role = $custom_roles->Next ) {
+ my %tmp = _ParseAdditiveCommand( \%cmds, 0, "CustomRole{". $custom_role->Name ."}" );
+ next unless keys %tmp;
+ $create_args{ $custom_role->GroupType } = [ _CompileAdditiveForCreate(%tmp) ];
+ }
+
# Canonicalize watchers
# First of all fetch default values
foreach my $type ( @WATCHER_ATTRIBUTES ) {
@@ -771,6 +840,48 @@ sub ProcessCommands {
Transaction => $transaction };
}
+sub _ReplaceUserAndGroupById {
+ my $cmds = shift;
+
+ foreach my $key (keys %$cmds) {
+ my @values = @{ $cmds->{$key} };
+ next unless @values;
+
+ my @new_values;
+ # Check each value and see if it can be a user or a group
+ foreach my $value (@values) {
+ if ($value =~ /^group\:(.*)/) {
+ my $group_name = $1;
+ $group_name =~ s/^\s+|\s+$//g;
+
+ my $group = RT::Group->new(RT->SystemUser);
+ $group->LoadUserDefinedGroup($group_name);
+ if ($group->id) {
+ push @new_values, $group->id;
+ next;
+ } else {
+ RT->Logger->error("Group '$1' not found");
+ next;
+ }
+ }
+ my $user = RT::User->new(RT->SystemUser);
+ $user->LoadByEmail($value) if $value =~ /\@/;
+ $user->Load($value) unless $user->id;
+ if ($user->id) {
+ push @new_values, $user->id;
+ next;
+ } else {
+ RT->Logger->warning("User '$value' not found");
+ next unless $value =~ /\@/;
+ }
+ # If no user or group found, keep the original value in case
+ # it contains an email address
+ push @new_values, $value;
+ }
+ $cmds->{$key} = \@new_values;
+ }
+}
+
sub _ParseAdditiveCommand {
my ($cmds, $plural_forms, $base) = @_;
my (%res);
@@ -871,6 +982,10 @@ sub _CanonicalizeCommand {
# CustomField commands
$key =~ s/^(add|del|)c(?:ustom)?-?f(?:ield)?\.?[({\[](.*)[)}\]]$/$1customfield{$2}/i;
$key =~ s/^(?:transaction|txn)c(?:ustom)?-?f(?:ield)?\.?[({\[](.*)[)}\]]$/transactioncustomfield{$1}/i;
+
+ # CustomRole commands
+ $key =~ s/^(add|del|)c(?:ustom)?-?r(?:ole)?\.?[({\[](.*)[)}\]]$/$1customrole{$2}/i;
+
return $key;
}
@@ -878,6 +993,7 @@ sub _CheckCommand {
my ($cmd, $val) = (lc shift, shift);
return 1 if $cmd =~ /^(add|del|)customfield\{.*\}$/i;
return 1 if $cmd =~ /^transactioncustomfield\{.*\}$/i;
+ return 1 if $cmd =~ /^(add|del|)customrole\{.*\}$/i;
if ( grep $cmd eq lc $_, @REGULAR_ATTRIBUTES, @TIME_ATTRIBUTES, @DATE_ATTRIBUTES ) {
return 1 unless ref $val;
return (0, "Command '$cmd' doesn't support multiple values");
-----------------------------------------------------------------------
Summary of changes:
META.yml | 6 +-
Makefile.PL | 1 +
README | 22 +++++-
inc/Module/AutoInstall.pm | 2 +-
inc/Module/Install.pm | 2 +-
inc/Module/Install/AutoInstall.pm | 2 +-
inc/Module/Install/Base.pm | 2 +-
inc/Module/Install/Can.pm | 2 +-
inc/Module/Install/Fetch.pm | 2 +-
inc/Module/Install/Include.pm | 2 +-
inc/Module/Install/Makefile.pm | 2 +-
inc/Module/Install/Metadata.pm | 12 +--
inc/Module/Install/RTx.pm | 4 +-
inc/Module/Install/Win32.pm | 2 +-
inc/Module/Install/WriteAll.pm | 2 +-
inc/YAML/Tiny.pm | 4 +-
lib/RT/Extension/CommandByMail.pm | 156 +++++++++++++++++++++++++++++++++-----
xt/update.t | 25 +++---
18 files changed, 195 insertions(+), 55 deletions(-)
hooks/post-receive
--
RT-Extension-CommandByMail
More information about the Bps-public-commit
mailing list