summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrancois Marier <francois@debian.org>2008-06-29 21:51:31 +1200
committerFrancois Marier <francois@debian.org>2008-06-29 21:51:31 +1200
commit03a60521d41962fb3d36e8e8002e9bba51796ff6 (patch)
tree36150e8a410695002674e5ad202c425d1796b0c9
parent42280f662d3ce4affb00eb68a22a081dfb951395 (diff)
Imported Upstream version v0.3.0upstream/v0.3.0
-rw-r--r--.cil30
-rw-r--r--INSTALLATION2
-rw-r--r--README20
-rwxr-xr-xbin/cil425
-rw-r--r--debian-etch/changelog12
-rw-r--r--debian-etch/control2
-rw-r--r--debian-etch/install1
-rw-r--r--debian-lenny/changelog12
-rw-r--r--debian-lenny/control2
-rw-r--r--debian-lenny/install1
-rw-r--r--etc/bash_completion.d/cil79
-rw-r--r--issues/c_02a9bb68.cil13
-rw-r--r--issues/c_4b71d0c3.cil6
-rw-r--r--issues/c_4edba98c.cil16
-rw-r--r--issues/c_7eb313cd.cil11
-rw-r--r--issues/c_81dc204c.cil9
-rw-r--r--issues/c_d8dd779f.cil8
-rw-r--r--issues/c_e9cc10a9.cil9
-rw-r--r--issues/i_02ee35bd.cil4
-rw-r--r--issues/i_5c88cb30.cil8
-rw-r--r--issues/i_6baa8555.cil7
-rw-r--r--issues/i_85eceee9.cil39
-rw-r--r--issues/i_98203ce8.cil26
-rw-r--r--issues/i_99d9dd74.cil31
-rw-r--r--issues/i_b19d5ada.cil24
-rw-r--r--issues/i_c6a8d865.cil17
-rw-r--r--issues/i_cbb43db9.cil4
-rw-r--r--issues/i_fb79b2e8.cil6
-rw-r--r--lib/CIL.pm100
-rw-r--r--lib/CIL/Base.pm47
-rw-r--r--lib/CIL/Comment.pm8
-rw-r--r--lib/CIL/Issue.pm63
-rw-r--r--lib/CIL/Utils.pm23
33 files changed, 970 insertions, 95 deletions
diff --git a/.cil b/.cil
index e69de29..c1fef1f 100644
--- a/.cil
+++ b/.cil
@@ -0,0 +1,30 @@
+StatusStrict: 1
+StatusAllowedList: New
+StatusAllowedList: InProgress
+StatusAllowedList: Fixed
+StatusAllowedList: OnHold
+StatusAllowedList: Duplicate
+StatusAllowedList: Finished
+StatusOpenList: New
+StatusOpenList: InProgress
+StatusOpenList: Fixed
+StatusClosedList: OnHold
+StatusClosedList: Duplicate
+StatusClosedList: Finished
+LabelStrict: 1
+LabelAllowedList: Release-v0.1.0
+LabelAllowedList: Release-v0.2.0
+LabelAllowedList: Release-v0.2.1
+LabelAllowedList: Type-Enhancement
+LabelAllowedList: Type-Defect
+LabelAllowedList: Type-Task
+LabelAllowedList: Type-Patch
+LabelAllowedList: Type-Other
+LabelAllowedList: Milestone-v0.1
+LabelAllowedList: Milestone-v0.2
+LabelAllowedList: Milestone-v0.3
+LabelAllowedList: Milestone-Future
+LabelAllowedList: Priority-Critical
+LabelAllowedList: Priority-High
+LabelAllowedList: Priority-Medium
+LabelAllowedList: Priority-Low
diff --git a/INSTALLATION b/INSTALLATION
index 0da7be5..cf172ab 100644
--- a/INSTALLATION
+++ b/INSTALLATION
@@ -43,7 +43,7 @@ Debian paackage:
which can then be installed with:
- $ sudo dpkg -i ../cil_0.2.1_all.deb
+ $ sudo dpkg -i ../cil_0.3.0_all.deb
or added to a repository you are using for easier installation with apt-get or
aptitude.
diff --git a/README b/README
index 607b470..c0f3d5e 100644
--- a/README
+++ b/README
@@ -32,15 +32,27 @@ list all the issues by using the 'list' command:
$ cil list
+When listing the issues, they can also be filtered:
+
+ $ cil list --status=New
+ $ cil list --label=Type-Enhancement
+ $ cil list --is-open
+ $ cil list --label=Milestone-v0.3 --is-open
+
You can see what the issue name is by looking at the 'Issue' title. Imagine it
is 'cafebabe' (which by default is the time from epoch). To see your issue
again, use the 'show' command:
$ cil show cafebabe
-Another reporting command is 'summary':
+Another reporting command is 'summary' for which all the filter options also
+apply:
$ cil summary
+ $ cil summary --status=New
+ $ cil summary --label=Type-Enhancement
+ $ cil summary --is-open
+ $ cil summary --label=Milestone-v0.3 --is-open
The columns show 'Name', 'Status', 'CreatedBy' and 'Summary'.
@@ -69,6 +81,12 @@ it from the issue:
$ cil extract decaf7ea --filename=mycore
+Finally, because the cil issue files reside on the filesystem in flat files,
+there needs to be a way to check the integrity of the issues, therefore you can
+run this to do checks regarding the whole issue list:
+
+ $ cil fsck
+
That's it for now. As you can see, if you've played with any kind of bug/issue
tracker before, 'cil' is straightforward.
diff --git a/bin/cil b/bin/cil
index d5a0af1..b8bcd20 100755
--- a/bin/cil
+++ b/bin/cil
@@ -20,8 +20,6 @@
use strict;
use warnings;
-use Data::Dumper;
-
use Getopt::Mixed "nextOption";
use Digest::MD5 qw(md5_hex);
use File::Touch;
@@ -36,42 +34,42 @@ use CIL::Attachment;
## ----------------------------------------------------------------------------
# constants
-use constant VERSION => '0.2.1';
+my $y = 'y';
+
+use constant VERSION => '0.3.0';
my @IN_OPTS = (
+ # strings
'p=s', # p = path
'path>p', # for 'add'
'f=s', # f = filename
- 'filename=f', # for 'extract'
-
+ 'filename>f', # for 'extract'
+ 's=s', # s = status
+ 'status>s', # for 'summary', 'list'
+ 'l=s', # l = label
+ 'label>l', # for 'summary, 'list'
+ 'c=s', # c = created-by
+ 'created-by>c', # for 'summary', 'list'
+ 'a=s', # a = assigned_to
+ 'assigned-to>a',# for 'summary', 'list'
+
+ # booleans
+ 'is-open', # for 'summary', 'list'
+ 'is-closed', # for 'summary', 'list'
'help',
'version',
);
my %BOOLEAN_ARGS = (
- help => 1,
- version => 1,
+ 'help' => 1,
+ 'version' => 1,
+ 'is-open' => 1,
+ 'is-closed' => 1,
);
my $gan = $ENV{GIT_AUTHOR_NAME} || 'Your Name';
my $gae = $ENV{GIT_AUTHOR_EMAIL} || 'you@example.org';
-my $new_issue_text = <<"EOF";
-Summary :
-Status : New
-CreatedBy : $gan <$gae>
-AssignedTo : $gan <$gae>
-Label :
-
-Description...
-EOF
-
-my $add_comment_text = <<"EOF";
-CreatedBy : $gan <$gae>
-
-Description...
-EOF
-
## ----------------------------------------------------------------------------
# main program
@@ -100,6 +98,7 @@ EOF
}
my $cil = CIL->new();
+ $cil->read_config_file( '.cil' );
&{"cmd_$command"}($cil, $args, @ARGV);
}
@@ -142,7 +141,7 @@ sub cmd_init {
# add a README.txt so people know what this is about
unless ( -f "$issues_dir/README.txt" ) {
- write_file("issues_dir/README.txt", <<README);
+ write_file("$issues_dir/README.txt", <<'README');
This directory is used by CIL to track issues and feature requests.
The home page for CIL is at http://kapiti.geek.nz/software/cil.html
@@ -154,12 +153,13 @@ README
}
sub cmd_list {
- my ($cil) = @_;
+ my ($cil, $args) = @_;
check_paths($cil);
# find all the issues
my $issues = $cil->get_issues();
+ $issues = filter_issues( $cil, $issues, $args );
if ( @$issues ) {
foreach my $issue ( sort { $a->Inserted cmp $b->Inserted } @$issues ) {
separator();
@@ -173,12 +173,13 @@ sub cmd_list {
}
sub cmd_summary {
- my ($cil) = @_;
+ my ($cil, $args) = @_;
check_paths($cil);
# find all the issues
my $issues = $cil->get_issues();
+ $issues = filter_issues( $cil, $issues, $args );
if ( @$issues ) {
separator();
foreach my $issue ( @$issues ) {
@@ -225,11 +226,37 @@ sub cmd_status {
sub cmd_add {
my ($cil, undef, $issue_name) = @_;
- # read in the new issue text
CIL::Utils->ensure_interactive();
- my $fh = CIL::Utils->solicit( $new_issue_text );
- my $issue = CIL::Issue->new_from_fh( 'tmp', $fh );
+ my $issue = CIL::Issue->new('tmpname');
+ $issue->Status('New');
+ $issue->CreatedBy("$gan <$gae>");
+ $issue->AssignedTo("$gan <$gae>");
+ $issue->Description("Description ...");
+
+ my $edit = $y;
+
+ # keep going until we get a valid issue or we want to quit
+ while ( $edit eq $y ) {
+ # read in the new issue text
+ my $fh = CIL::Utils->solicit( $issue->as_output );
+ $issue = CIL::Issue->new_from_fh( 'tmp', $fh );
+
+ # check if the issue is valid
+ if ( $issue->is_valid($cil) ) {
+ $edit = 'n';
+ }
+ else {
+ msg($_) foreach @{ $issue->errors };
+ print 'Would you like to re-edit (y/n): ';
+ $edit = <STDIN>;
+ chomp $edit;
+ print "\n";
+ }
+ }
+
+ # if the issue is still invalid, they quit without correcting it
+ return unless $issue->is_valid( $cil );
# we've got the issue, so let's name it
my $unique_str = $issue->Inserted . $issue->Summary . $issue->Description;
@@ -246,20 +273,35 @@ sub cmd_edit {
fatal("Couldn't load issue '$issue_name'");
}
- # create the ini file, then edit it
- my $edit_issue_text = $issue->as_output;
-
- # read in the new issue text
CIL::Utils->ensure_interactive();
- my $fh = CIL::Utils->solicit( join('', @$edit_issue_text) );
- my $issue_edited = CIL::Issue->new_from_fh( $issue->name, $fh );
- unless ( defined $issue_edited ) {
- fatal("couldn't create issue (program error)");
+ my $edit = $y;
+
+ # keep going until we get a valid issue or we want to quit
+ while ( $edit eq $y ) {
+ # read in the new issue text
+ my $fh = CIL::Utils->solicit( $issue->as_output );
+ $issue = CIL::Issue->new_from_fh( $issue->name, $fh );
+
+ # check if the issue is valid
+ if ( $issue->is_valid($cil) ) {
+ $edit = 'n';
+ }
+ else {
+ msg($_) foreach @{ $issue->errors };
+ print 'Would you like to re-edit (y/n): ';
+ $edit = <STDIN>;
+ chomp $edit;
+ print "\n";
+ }
}
- $issue_edited->save($cil);
- display_issue($cil, $issue_edited);
+ # if the issue is still invalid, they quit without correcting it
+ return unless $issue->is_valid( $cil );
+
+ # save it
+ $issue->save($cil);
+ display_issue($cil, $issue);
}
sub cmd_comment {
@@ -270,21 +312,43 @@ sub cmd_comment {
fatal("couldn't load issue '$issue_name'");
}
- # read in the new issue text
CIL::Utils->ensure_interactive();
- my $fh = CIL::Utils->solicit( $add_comment_text );
- my $comment = CIL::Comment->new_from_fh( 'tmp', $fh );
- unless ( defined $comment ) {
- fatal("could not create new comment");
+ # create the new comment
+ my $comment = CIL::Comment->new('tmpname');
+ $comment->Issue( $issue->name );
+ $comment->CreatedBy("$gan <$gae>");
+ $comment->Description("Description ...");
+
+ my $edit = $y;
+
+ # keep going until we get a valid issue or we want to quit
+ while ( $edit eq $y ) {
+ # read in the new comment text
+ my $fh = CIL::Utils->solicit( $comment->as_output );
+ $comment = CIL::Comment->new_from_fh( 'tmp', $fh );
+
+ # check if the comment is valid
+ if ( $comment->is_valid($cil) ) {
+ $edit = 'n';
+ }
+ else {
+ msg($_) foreach @{ $issue->errors };
+ print 'Would you like to re-edit (y/n): ';
+ $edit = <STDIN>;
+ chomp $edit;
+ print "\n";
+ }
}
+ # if the comment is still invalid, they quit without correcting it
+ return unless $comment->is_valid( $cil );
+
# we've got the comment, so let's name it
my $unique_str = $comment->Inserted . $issue->Description;
$comment->set_name( substr(md5_hex($unique_str), 0, 8) );
- # finally, tell it who it's parent is and then save
- $comment->Issue( $issue->name );
+ # finally, save it
$comment->save($cil);
# add the comment to the issue, update it's timestamp and save it out
@@ -347,7 +411,7 @@ EOF
}
sub cmd_extract {
- my ($cil, undef, $attachment_name, $args) = @_;
+ my ($cil, $args, $attachment_name) = @_;
my $attachment = CIL::Attachment->new_from_name($cil, $attachment_name);
unless ( defined $attachment ) {
@@ -358,15 +422,184 @@ sub cmd_extract {
write_file( $filename, $attachment->as_binary );
}
+sub cmd_fsck {
+ my ($cil, $args) = @_;
+
+ # this looks at all the issues it can find and checks for:
+ # * validity
+ # * all the comments are there
+ # * all the attachments are there
+ # then it checks each individual comment/attachment for:
+ # * ToDo: validity
+ # * it's parent exists
+
+ check_paths($cil);
+
+ # find all the issues, comments and attachments
+ my $issues = $cil->get_issues();
+ my $issue = {};
+ foreach my $i ( @$issues ) {
+ $issue->{$i->name} = $i;
+ }
+ my $comments = $cil->get_comments();
+ my $comment = {};
+ foreach my $c ( @$comments ) {
+ $comment->{$c->name} = $c;
+ }
+ my $attachments = $cil->get_attachments();
+ my $attachment = {};
+ foreach my $a ( @$attachments ) {
+ $attachment->{$a->name} = $a;
+ }
+
+ my $errors = {};
+
+ if ( @$issues ) {
+ foreach my $i ( sort { $a->Inserted cmp $b->Inserted } @$issues ) {
+ my $name = $i->name;
+
+ unless ( $i->is_valid($cil) ) {
+ foreach ( @{ $i->errors } ) {
+ push @{$errors->{$name}}, $_;
+ }
+ }
+
+ # check that all it's comments are there and that they have this parent
+ my $comments = $i->Comments;
+ foreach my $c ( @$comments ) {
+ # see if this comment exists at all
+ if ( exists $comment->{$c} ) {
+ # check the parent is this issue
+ push @{$errors->{$name}}, "comment '$c' is listed under issue '" . $i->name . "' but does not reciprocate"
+ unless $comment->{$c}->Issue eq $i->name;
+ }
+ else {
+ push @{$errors->{$name}}, "comment '$c' listed in issue '" . $i->name . "' does not exist";
+ }
+ }
+
+ # check that all it's attachments are there and that they have this parent
+ my $attachments = $i->Attachments;
+ foreach my $a ( @$attachments ) {
+ # see if this attachment exists at all
+ if ( exists $attachment->{$a} ) {
+ # check the parent is this issue
+ push @{$errors->{$name}}, "attachment '$a' is listed under issue '" . $i->name . "' but does not reciprocate"
+ unless $attachment->{$a}->Issue eq $i->name;
+ }
+ else {
+ push @{$errors->{$name}}, "attachment '$a' listed in issue '" . $i->name . "' does not exist";
+ }
+ }
+ }
+ }
+
+ print_fsck_errors('Issue', $errors);
+
+ # comments
+ $errors = {};
+
+ # loop through all the comments
+ if ( @$comments ) {
+ # check that their parent issues exist
+ foreach my $c ( sort { $a->Inserted cmp $b->Inserted } @$comments ) {
+ # check that the parent of each comment exists
+ unless ( exists $issue->{$c->Issue} ) {
+ push @{$errors->{$c->name}}, "comment '" . $c->name . "' refers to issue '" . $c->Issue . "' but issue does not exist";
+ }
+ }
+ }
+
+ print_fsck_errors('Comment', $errors);
+
+ # attachments
+ $errors = {};
+
+ # loop through all the attachments
+ if ( @$attachments ) {
+ # check that their parent issues exist
+ foreach my $a ( sort { $a->Inserted cmp $b->Inserted } @$attachments ) {
+ # check that the parent of each attachment exists
+ unless ( exists $issue->{$a->Issue} ) {
+ push @{$errors->{$a->name}}, "attachment '" . $a->name . "' refers to issue '" . $a->Issue . "' but issue does not exist";
+ }
+ }
+ }
+
+ print_fsck_errors('Attachment', $errors);
+
+ # nothing more to do
+ separator();
+}
+
+sub print_fsck_errors {
+ my ($entity, $errors) = @_;
+
+ separator();
+ foreach my $issue_name ( keys %$errors ) {
+ title( "$entity $issue_name ");
+ foreach my $error ( @{$errors->{$issue_name}} ) {
+ msg("* $error");
+ }
+ }
+}
+
## ----------------------------------------------------------------------------
sub check_paths {
my ($cil) = @_;
# make sure an issue directory is available
- unless ( -d $cil->issue_dir ) {
- fatal("couldn't find '" . $cil->issue_dir . "' directory");
+ unless ( -d $cil->IssueDir ) {
+ fatal("couldn't find '" . $cil->IssueDir . "' directory");
+ }
+}
+
+sub filter_issues {
+ my ($cil, $issues, $args) = @_;
+
+ # don't filter if we haven't been given anything
+ return $issues unless %$args;
+
+ # take a copy of the whole lot first (so we don't destroy the input list)
+ my @new_issues = @$issues;
+
+ # firstly, get out the Statuses we want
+ if ( defined $args->{s} ) {
+ @new_issues = grep { $_->Status eq $args->{s} } @new_issues;
+ }
+
+ # then see if we want a particular label (could be a bit nicer)
+ if ( defined $args->{l} ) {
+ my @tmp;
+ foreach my $issue ( @new_issues ) {
+ push @tmp, $issue
+ if grep { $_ eq $args->{l} } @{$issue->Labels};
+ }
+ @new_issues = @tmp;
+ }
+
+ # filter out dependent on open/closed
+ if ( defined $args->{'is-open'} ) {
+ # just get the open issues
+ @new_issues = grep { $_->is_open($cil) } @new_issues;
+ }
+ if ( defined $args->{'is-closed'} ) {
+ # just get the closed issues
+ @new_issues = grep { $_->is_closed($cil) } @new_issues;
}
+
+ # filter out 'created by'
+ if ( defined $args->{c} ) {
+ @new_issues = grep { $args->{c} eq $_->created_by_email } @new_issues;
+ }
+
+ # filter out 'assigned to'
+ if ( defined $args->{a} ) {
+ @new_issues = grep { $args->{a} eq $_->assigned_to_email } @new_issues;
+ }
+
+ return \@new_issues;
}
## ----------------------------------------------------------------------------
@@ -535,14 +768,15 @@ Usage: $0 COMMAND [options]
Commands:
init [--path=PATH]
add
- summary
- list
+ summary [--status=STATUS] [--label=LABEL] [--is-open] [--is-closed]
+ list [--status=STATUS] [--label=LABEL] [--is-open] [--is-closed]
show ISSUE
status ISSUE NEW_STATUS
edit ISSUE
comment ISSUE
attach ISSUE FILENAME
extract ATTACHMENT [--filename=FILENAME]
+ fsck
See <http://kapiti.geek.nz/software/cil.html> for further information.
Report bugs to <andychilton -at- gmail -dot- com>.
@@ -560,6 +794,9 @@ cil - the command-line issue list
$ cil init
$ cil summary
$ cil list
+ $ cil list --status=New
+ $ cil list --label=Release-v0.1
+ $ cil list --is-open
$ cil add
... added issue 'cafebabe' ...
@@ -576,6 +813,8 @@ cil - the command-line issue list
$ cil extract decaf7ea
$ cil extract decaf7ea --filename=other_filename.txt
+ $ cil fsck
+
=head1 DESCRIPTION
Cil is a small but useful command-line issue list. It saves issues, comments
@@ -588,13 +827,15 @@ and attachments as local files which you can check in to your repository.
Creates a local '.cil' file and an 'issues' directory. If PATH is specified,
the config file and directory will be created in the destination directory.
-=item summary
+=item summary [--status=STATUS] [--label=LABEL] [--is-open] [--is-closed]
-Displays a one line summary for each issue.
+Displays a one line summary for each issue. You may filter on both the Status
+and Label fields.
-=item list
+=item list [--status=STATUS] [--label=LABEL] [--is-open] [--is-closed]
-Shows each issue with more information.
+Shows each issue with more information. You may filter on both the Status and
+Label fields.
=item add
@@ -628,19 +869,83 @@ otherwise it will use the original one saved along with the attachment.
=back
+=head1 .cil
+
+The C<.cil> file is used to configure bits and pieces within cil for this
+particular issue list. The following options are available and where stated,
+may be declared multiple times:
+
+The C<.cil> file is fairly simple and an example can be seen here:
+
+ StatusStrict: 1
+ StatusAllowedList: New
+ StatusAllowedList: InProgress
+ StatusAllowedList: Finished
+ StatusOpenList: New
+ StatusOpenList: InProgress
+ StatusClosedList: Finished
+ LabelStrict: 1
+ LabelAllowedList: Type-Enhancement
+ LabelAllowedList: Type-Defect
+ LabelAllowedList: Priority-High
+ LabelAllowedList: Priority-Medium
+ LabelAllowedList: Priority-Low
+
+=over
+
+=item StatusStrict
+
+Default: 0, Type: Boolean (0/1)
+
+If this is set to a true value then cil checks that the status you enter into
+an issue (after adding or editing) is also in the allowed list (see
+StatusAllowedList).
+
+=item StatusAllowedList
+
+Default: empty, Type: List
+
+This list is checked against when adding or editing issues but only if you have
+StatusStrict on.
+
+=item StatusOpenList
+
+Default: empty, Type: List
+
+This list is checked against when filtering with --is-open.
+
+=item StatusClosedList
+
+Default: empty, Type: List
+
+This list is checked against when filtering with --is-closed.
+
+=item LabelStrict
+
+Default: 0, Type: Boolean (0/1)
+
+This determines that labels you enter are checked against LabelAllowedList. Set
+to 1 if you require this feature.
+
+=item LabelAllowedList
+
+Default: empty, Type: List
+
+This determines which labels are allowed if you have turned on LabelStrict.
+
+=back
+
=head1 BUGS
Probably. Let me know :-)
=head1 TODO
-There is a number of things to do. High on the list are:
-
-* the ability to set Statuses from the command line
+To get a ToDo list for cil, clone the repo, find the issues/ dir and type:
-* set where you want your issues (from a .cil file)
+ $ cil --is-open
-* simple search first, proper search and indexing second
+This gives the current outstanding issues in cil.
=head1 AUTHOR
diff --git a/debian-etch/changelog b/debian-etch/changelog
index f42576f..16920ad 100644
--- a/debian-etch/changelog
+++ b/debian-etch/changelog
@@ -1,3 +1,15 @@
+cil (0.3.0) unstable; urgency=low
+
+ * Added filters for the listing commands
+ * Added ability to read .cil config file
+ * Can now set which Statuses are valid
+ * Can set which Labels are valid
+ * Added 'fsck' command
+ * Added bash completion for options, commands and entities
+ * Ready for 0.3.0 release
+
+ -- Andrew Chilton <andychilton@gmail.com> Sat, 28 Jun 2008 12:06:19 +1200
+
cil (0.2.1) unstable; urgency=low
* Ready for 0.2.1 release
diff --git a/debian-etch/control b/debian-etch/control
index ebac134..ded0c34 100644
--- a/debian-etch/control
+++ b/debian-etch/control
@@ -11,7 +11,7 @@ Package: cil
Section: perl
Priority: optional
Architecture: all
-Depends: ${perl:Depends}, libgetopt-mixed-perl, libfile-touch-perl, libfile-slurp-perl, libclass-accessor-perl, libdatetime-perl
+Depends: ${perl:Depends}, libgetopt-mixed-perl, libfile-touch-perl, libfile-slurp-perl, libclass-accessor-perl, libdatetime-perl, libemail-find-perl
Description: command line issue tracker
'cil' allows easy command-line creation of an issue tracker. It saves each
issue locally and in plain text. Commands are given such that these issues can
diff --git a/debian-etch/install b/debian-etch/install
index 6d9e803..dc23abd 100644
--- a/debian-etch/install
+++ b/debian-etch/install
@@ -1,2 +1,3 @@
lib/* usr/share/perl5
bin/cil usr/bin
+etc/bash_completion.d/cil etc/bash_completion.d
diff --git a/debian-lenny/changelog b/debian-lenny/changelog
index f42576f..16920ad 100644
--- a/debian-lenny/changelog
+++ b/debian-lenny/changelog
@@ -1,3 +1,15 @@
+cil (0.3.0) unstable; urgency=low
+
+ * Added filters for the listing commands
+ * Added ability to read .cil config file
+ * Can now set which Statuses are valid
+ * Can set which Labels are valid
+ * Added 'fsck' command
+ * Added bash completion for options, commands and entities
+ * Ready for 0.3.0 release
+
+ -- Andrew Chilton <andychilton@gmail.com> Sat, 28 Jun 2008 12:06:19 +1200
+
cil (0.2.1) unstable; urgency=low
* Ready for 0.2.1 release
diff --git a/debian-lenny/control b/debian-lenny/control
index ebac134..ded0c34 100644
--- a/debian-lenny/control
+++ b/debian-lenny/control
@@ -11,7 +11,7 @@ Package: cil
Section: perl
Priority: optional
Architecture: all
-Depends: ${perl:Depends}, libgetopt-mixed-perl, libfile-touch-perl, libfile-slurp-perl, libclass-accessor-perl, libdatetime-perl
+Depends: ${perl:Depends}, libgetopt-mixed-perl, libfile-touch-perl, libfile-slurp-perl, libclass-accessor-perl, libdatetime-perl, libemail-find-perl
Description: command line issue tracker
'cil' allows easy command-line creation of an issue tracker. It saves each
issue locally and in plain text. Commands are given such that these issues can
diff --git a/debian-lenny/install b/debian-lenny/install
index 6d9e803..dc23abd 100644
--- a/debian-lenny/install
+++ b/debian-lenny/install
@@ -1,2 +1,3 @@
lib/* usr/share/perl5
bin/cil usr/bin
+etc/bash_completion.d/cil etc/bash_completion.d
diff --git a/etc/bash_completion.d/cil b/etc/bash_completion.d/cil
new file mode 100644
index 0000000..3a57087
--- /dev/null
+++ b/etc/bash_completion.d/cil
@@ -0,0 +1,79 @@
+#
+# Bash Completion support for cil.
+#
+# Copyright (c) 2008 Andrew Chilton <andychilton@gmail.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+__issues()
+{
+ ls -1 issues/i_*.cil 2>/dev/null | sed -e 's/^issues\/i_//; s/\.cil$//'
+}
+
+__comments()
+{
+ ls -1 issues/c_*.cil 2>/dev/null | sed -e 's/^issues\/c_//; s/\.cil$//'
+}
+
+__attachments()
+{
+ ls -1 issues/a_*.cil 2>/dev/null | sed -e 's/^issues\/a_//; s/\.cil$//'
+}
+
+_cil()
+{
+ local cur prev opts
+
+ # constants
+ opts="--help --version --path --status --label --filename --is-open --is-closed --assigned-to --created-by"
+ commands="init add summary list show status edit comment attach extract"
+
+ COMPREPLY=()
+ cur=${COMP_WORDS[COMP_CWORD]}
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ # easy options first
+ if [[ ${cur} == -* ]] ; then
+ COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
+ return 0
+ fi
+
+ # check for a command first
+ if [[ ${prev} == 'cil' ]] ; then
+ COMPREPLY=( $(compgen -W "${commands}" -- ${cur}) )
+ return 0
+ fi
+
+ # check for the various commands
+ case "${prev}" in
+ show|status|edit|comment|attach)
+ local issues=$( __issues )
+ COMPREPLY=( $(compgen -W "${issues}" -- ${cur}) )
+ return 0
+ ;;
+ extract)
+ local attachments=$( __attachments )
+ COMPREPLY=( $(compgen -W "${attachments}" -- ${cur}) )
+ return 0
+ ;;
+ *)
+ ;;
+ esac
+
+ # for debugging - comment in/out when necessary
+ #COMPREPLY=( $(compgen -W "prev=${prev} cur=${cur}" -- ) )
+ #return 0
+}
+complete -F _cil cil
diff --git a/issues/c_02a9bb68.cil b/issues/c_02a9bb68.cil
new file mode 100644
index 0000000..554d4a3
--- /dev/null
+++ b/issues/c_02a9bb68.cil
@@ -0,0 +1,13 @@
+Issue: 85eceee9
+CreatedBy: Andrew Chilton <andychilton@gmail.com>
+Inserted: 2008-06-27T13:18:47
+Updated: 2008-06-27T13:20:01
+
+Added the --created-by and --assigned-to filters.
+
+These work just on the email address and not the whole field. E.g.
+
+ $ cil list --created-by=andy@example.com
+ $ cil list --assigned-to=andy@example.org
+
+The short options for these are -c and -a.
diff --git a/issues/c_4b71d0c3.cil b/issues/c_4b71d0c3.cil
new file mode 100644
index 0000000..fdba844
--- /dev/null
+++ b/issues/c_4b71d0c3.cil
@@ -0,0 +1,6 @@
+Issue: 85eceee9
+CreatedBy: Andrew Chilton <andychilton@gmail.com>
+Inserted: 2008-06-27T12:40:51
+Updated: 2008-06-27T12:40:51
+
+Now added --is-open and --is-closed.
diff --git a/issues/c_4edba98c.cil b/issues/c_4edba98c.cil
new file mode 100644
index 0000000..c58c7ed
--- /dev/null
+++ b/issues/c_4edba98c.cil
@@ -0,0 +1,16 @@
+Issue: 6baa8555
+CreatedBy: Andrew Chilton <andychilton@gmail.com>
+Inserted: 2008-06-26T12:11:09
+Updated: 2008-06-26T12:12:41
+
+Added the following options:
+
+* StatusStrict
+* StatusAllowedList
+* StatusOpenList
+* StatusClosedList
+
+* LabelStrict
+* LabelAllowedList
+
+The lists are only checked if the strict variables are on.
diff --git a/issues/c_7eb313cd.cil b/issues/c_7eb313cd.cil
new file mode 100644
index 0000000..8d97144
--- /dev/null
+++ b/issues/c_7eb313cd.cil
@@ -0,0 +1,11 @@
+Issue: 85eceee9
+CreatedBy: Andrew Chilton <andychilton@gmail.com>
+Inserted: 2008-06-23T12:07:44
+Updated: 2008-06-23T12:07:44
+
+Added both --status and --label.
+
+In the case of Status, it tests for equality.
+
+In the case of Label, it tests that the issue has at least one label of the one
+specified.
diff --git a/issues/c_81dc204c.cil b/issues/c_81dc204c.cil
new file mode 100644
index 0000000..7d3b708
--- /dev/null
+++ b/issues/c_81dc204c.cil
@@ -0,0 +1,9 @@
+Issue: c6a8d865
+CreatedBy: Andrew Chilton <andychilton@gmail.com>
+Inserted: 2008-06-27T23:39:55
+Updated: 2008-06-27T23:40:49
+
+Added the command 'fsck'. Currently it works okay but it might need looking at
+in the future for functionality and possible refactoring.
+
+Closing issue.
diff --git a/issues/c_d8dd779f.cil b/issues/c_d8dd779f.cil
new file mode 100644
index 0000000..f18b4e9
--- /dev/null
+++ b/issues/c_d8dd779f.cil
@@ -0,0 +1,8 @@
+Issue: 5c88cb30
+CreatedBy: Andrew Chilton <andychilton@gmail.com>
+Inserted: 2008-06-23T12:36:04
+Updated: 2008-06-23T12:36:04
+
+Closing this issue as WontFix since the current solution is good (ie. the 8
+char hex number). Will reopen if demand says so and I'll try and get a patch
+off whoever wants it :-)
diff --git a/issues/c_e9cc10a9.cil b/issues/c_e9cc10a9.cil
new file mode 100644
index 0000000..b6d8839
--- /dev/null
+++ b/issues/c_e9cc10a9.cil
@@ -0,0 +1,9 @@
+Issue: 85eceee9
+CreatedBy: Andrew Chilton <andychilton@gmail.com>
+Inserted: 2008-06-27T13:30:08
+Updated: 2008-06-27T13:30:45
+
+Not going to do --has-comments or --has-attachments until someone says that
+it's actually useful.
+
+Closing this but as finished.
diff --git a/issues/i_02ee35bd.cil b/issues/i_02ee35bd.cil
index 21546ec..85596e3 100644
--- a/issues/i_02ee35bd.cil
+++ b/issues/i_02ee35bd.cil
@@ -2,11 +2,11 @@ Summary: Labels should be allowed to have a 'required' set
Status: New
CreatedBy: Andrew Chilton <andychilton@gmail.com>
AssignedTo: Andrew Chilton <andychilton@gmail.com>
-Label: Release-v0.1
Label: Priority-Medium
+Label: Release-v0.1.0
Label: Type-Enhancement
Inserted: 2008-05-05T12:53:38
-Updated: 2008-06-21T10:50:05
+Updated: 2008-06-26T11:53:27
In the .cil config file, there should a field which determines a
'required' set of labels.
diff --git a/issues/i_5c88cb30.cil b/issues/i_5c88cb30.cil
index 003dfdc..51cb0f4 100644
--- a/issues/i_5c88cb30.cil
+++ b/issues/i_5c88cb30.cil
@@ -1,14 +1,14 @@
Summary: Options for issues names needs to be added
-Status: New
+Status: OnHold
CreatedBy: Andrew Chilton <andychilton@gmail.com>
AssignedTo: Andrew Chilton <andychilton@gmail.com>
-Label: Milestone-v0.2
+Label: Milestone-Future
Label: Priority-Low
-Label: Release-v0.1
Label: Type-Enhancement
+Comment: d8dd779f
Comment: feb65ae7
Inserted: 2008-05-05T12:33:19
-Updated: 2008-06-21T10:57:47
+Updated: 2008-06-26T12:10:32
When issues are created, they are given the epoch as it's name.
Instead of just using the epoch, the user should be able to configure
diff --git a/issues/i_6baa8555.cil b/issues/i_6baa8555.cil
index 5352e93..701e18e 100644
--- a/issues/i_6baa8555.cil
+++ b/issues/i_6baa8555.cil
@@ -1,12 +1,13 @@
Summary: Do checking on input fields after adding or editing issue
-Status: New
+Status: InProgress
CreatedBy: Andrew Chilton <andychilton@gmail.com>
AssignedTo: Andrew Chilton <andychilton@gmail.com>
Label: Priority-Medium
-Label: Release-v0.1
+Label: Release-v0.1.0
Label: Type-Enhancement
+Comment: 4edba98c
Inserted: 2008-05-05T12:46:58
-Updated: 2008-06-21T10:59:52
+Updated: 2008-06-26T12:12:41
For example, if there is a config item in .cil called
'allowed_statuses', all input values in the 'Status' field should be
diff --git a/issues/i_85eceee9.cil b/issues/i_85eceee9.cil
new file mode 100644
index 0000000..59707ce
--- /dev/null
+++ b/issues/i_85eceee9.cil
@@ -0,0 +1,39 @@
+Summary: Ability to filter the issues lists (summary and list)
+Status: Finished
+CreatedBy: Andrew Chilton <andychilton@gmail.com>
+AssignedTo: Andrew Chilton <andychilton@gmail.com>
+Label: Milestone-v0.3
+Label: Release-v0.2.0
+Label: Type-Enhancement
+Comment: 02a9bb68
+Comment: 4b71d0c3
+Comment: 7eb313cd
+Comment: e9cc10a9
+Inserted: 2008-06-23T12:05:33
+Updated: 2008-06-27T13:32:59
+
+The ability to filter on various things would be nice. For example using these
+list commands, though summary should work the same way:
+
+ $ cil list --status=New
+ $ cil list --status=Assigned
+ $ cil list --status=Finished
+
+ $ cil list --label=Release-v0.1
+ $ cil list --label=Type-Enhancement
+
+And combining them should be easy:
+
+ $ cil list --status=Assigned --label=Type-Defect
+
+Other options include:
+
+ $ cil list --assigned-to=andychilton@gmail.com
+ $ cil list --has-attachments --has-comments
+ $ cil list --is-open/closed
+
+There may be other options so this issue will be closed when we think we've had
+enough.
+
+Note: whilst filtering on lists is good, actual searching is still better for a
+lot of things you'd like to do. This will be added as a separate issue.
diff --git a/issues/i_98203ce8.cil b/issues/i_98203ce8.cil
new file mode 100644
index 0000000..f913c1f
--- /dev/null
+++ b/issues/i_98203ce8.cil
@@ -0,0 +1,26 @@
+Summary: Search and indexing should be added to help aid finding issues
+Status: New
+CreatedBy: Andrew Chilton <andychilton@gmail.com>
+AssignedTo: Andrew Chilton <andychilton@gmail.com>
+Label: Milestone-Future
+Label: Release-v0.2.1
+Label: Type-Enhancement
+Inserted: 2008-06-23T12:20:45
+Updated: 2008-06-26T11:54:39
+
+Some sort of indexing and search should be added to cil so that it is easier to
+find issues in amongst a long list of them.
+
+There have been some suggestions in the past but we want something that is
+local, doesn't have to be set up and will just create a file right there.
+
+Using it should be a matter of just searching (and the re-indexing will happen
+automatically). Here are some example commands which might be good:
+
+ # shouldn't be necessary but there should be a way to do it
+ $ cil reindex
+
+ $ cil search 'Project Blah refactoring'
+ $ cil search 'screenshot table formatting'
+
+The search should list results in either the 'summary' or 'list' view.
diff --git a/issues/i_99d9dd74.cil b/issues/i_99d9dd74.cil
new file mode 100644
index 0000000..208d1b9
--- /dev/null
+++ b/issues/i_99d9dd74.cil
@@ -0,0 +1,31 @@
+Summary: Add a filter '--grep=regex'
+Status: New
+CreatedBy: Andrew Chilton <andychilton@gmail.com>
+AssignedTo: Andrew Chilton <andychilton@gmail.com>
+Label: Milestone-Future
+Label: Release-v0.2.1
+Label: Type-Enhancement
+Inserted: 2008-06-24T12:16:57
+Updated: 2008-06-27T23:59:48
+
+Having a 'grep' filter would be quite powerful. It could be used in the 'list'
+and 'summary' commands. It would be good if it accepts Perl regexes and might
+work a little something like this:
+
+Find all issues with 'screenshot' in them:
+
+ $ cil list --grep=screenshot
+
+Find any issue that mentions one of the following:
+
+ $ cil list --grep=css|html|javascript
+
+This one would be directed at the 'CreatedBy' and 'AssignedTo' fields:
+
+ $ cil list --grep=andychilton@
+
+This one would probably be targeted at the 'Label' field:
+
+ $ cil list --grep=^Release-v0.2
+
+So accepting Perl regexes would be excellent.
diff --git a/issues/i_b19d5ada.cil b/issues/i_b19d5ada.cil
new file mode 100644
index 0000000..7eb3b56
--- /dev/null
+++ b/issues/i_b19d5ada.cil
@@ -0,0 +1,24 @@
+Summary: Add a 'cil check-in-git' command
+Status: New
+CreatedBy: Andrew Chilton <andychilton@gmail.com>
+AssignedTo: Andrew Chilton <andychilton@gmail.com>
+Label: Milestone-Future
+Label: Release-v0.2.1
+Label: Type-Enhancement
+Inserted: 2008-06-24T03:24:14
+Updated: 2008-06-27T13:29:00
+
+The ability to check whether issues/comments/attachments are tracked by Git is
+important. Or indeed any VCS system.
+
+My thoughts are that this command would do something like the following:
+
+* internally, it runs "git ls-files issues/" and makes a list of files in that
+ directory
+* makes a list of issues that are currently contained in the directory
+* complains if it finds something Git doesn't know about
+
+Also, when it runs through each issue, it should do the same for all comments
+and attachments. It could either output something which tells you you have
+untracked files or it could give you example git commands which add them to
+those being tracked.
diff --git a/issues/i_c6a8d865.cil b/issues/i_c6a8d865.cil
new file mode 100644
index 0000000..9e46130
--- /dev/null
+++ b/issues/i_c6a8d865.cil
@@ -0,0 +1,17 @@
+Summary: Add a 'fsck' command
+Status: Finished
+CreatedBy: Andrew Chilton <andychilton@gmail.com>
+AssignedTo: Andrew Chilton <andychilton@gmail.com>
+Label: Milestone-v0.3
+Label: Type-Enhancement
+Comment: 81dc204c
+Inserted: 2008-06-27T13:39:48
+Updated: 2008-06-27T23:40:49
+
+When we add issues and their comments and attachments, there is the possibility
+that you don't actually check in the correct files at the same time. This
+command will help to figure out whether you have all the files around.
+
+This command will also check each command for validity. For example, if you
+change the 'StatusAllowedList' in your '.cil' file, then this will help
+determine if there are any issues set to now invalid statuses.
diff --git a/issues/i_cbb43db9.cil b/issues/i_cbb43db9.cil
index 3490199..61123ef 100644
--- a/issues/i_cbb43db9.cil
+++ b/issues/i_cbb43db9.cil
@@ -3,11 +3,11 @@ Status: Finished
CreatedBy: Andrew Chilton <andychilton@gmail.com>
AssignedTo: Andrew Chilton <andychilton@gmail.com>
Label: Priority-Medium
-Label: Release-v0.1
+Label: Release-v0.1.0
Label: Type-Enhancement
Comment: 2b92ef13
Inserted: 2008-05-05T12:20:28
-Updated: 2008-06-22T03:54:13
+Updated: 2008-06-26T11:55:42
'cil' currently has no way of adding attachments to issues.
diff --git a/issues/i_fb79b2e8.cil b/issues/i_fb79b2e8.cil
index b218517..e865d07 100644
--- a/issues/i_fb79b2e8.cil
+++ b/issues/i_fb79b2e8.cil
@@ -2,12 +2,12 @@ Summary: Ability to set Issue Status' from the command line
Status: Finished
CreatedBy: Andrew Chilton <andychilton@gmail.com>
AssignedTo: Andrew Chilton <andychilton@gmail.com>
-Label: Mileston-v0.2
-Label: Release-v0.1
+Label: Milestone-v0.2
+Label: Release-v0.1.0
Label: Type-Enhancement
Comment: 792a4acf
Inserted: 2008-06-22T04:00:20
-Updated: 2008-06-22T04:02:46
+Updated: 2008-06-26T11:56:03
The ability to do something like the following would be good.
diff --git a/lib/CIL.pm b/lib/CIL.pm
index b85f2fb..e2be890 100644
--- a/lib/CIL.pm
+++ b/lib/CIL.pm
@@ -26,12 +26,20 @@ use warnings;
use File::Glob qw(:glob);
use base qw(Class::Accessor);
-__PACKAGE__->mk_accessors(qw(issue_dir));
+__PACKAGE__->mk_accessors(qw(
+ IssueDir
+ StatusStrict StatusAllowed StatusOpen StatusClosed
+ LabelStrict LabelAllowed
+));
my $defaults = {
- issue_dir => 'issues',
+ IssueDir => 'issues', # the dir to save the issues in
+ StatusStrict => 0, # whether to complain if a status is invalid
+ LabelStrict => 0, # whether to complain if a label is invalid
};
+my @config_hashes = qw(StatusAllowed StatusOpen StatusClosed LabelAllowed);
+
## ----------------------------------------------------------------------------
sub new {
@@ -50,21 +58,39 @@ sub new {
return $self;
}
-sub list_issues {
- my ($self) = @_;
+sub list_entities {
+ my ($self, $prefix) = @_;
- my $globpath = $self->issue_dir . "/i_*.cil";
+ my $globpath = $self->IssueDir . "/${prefix}_*.cil";
my @filenames = bsd_glob($globpath);
- my @issues;
+ my @entities;
foreach my $filename ( sort @filenames ) {
- my ($name) = $filename =~ m{/i_(.*)\.cil$}xms;
- push @issues, {
+ my ($name) = $filename =~ m{/${prefix}_(.*)\.cil$}xms;
+ push @entities, {
name => $name,
filename => $filename,
};
}
- return \@issues;
+ return \@entities;
+}
+
+sub list_issues {
+ my ($self) = @_;
+
+ return $self->list_entities('i');
+}
+
+sub list_comments {
+ my ($self) = @_;
+
+ return $self->list_entities('c');
+}
+
+sub list_attachments {
+ my ($self) = @_;
+
+ return $self->list_entities('a');
}
sub get_issues {
@@ -79,6 +105,30 @@ sub get_issues {
return \@issues;
}
+sub get_comments {
+ my ($self) = @_;
+
+ my $comment_list = $self->list_comments();
+
+ my @comments;
+ foreach my $comment ( @$comment_list ) {
+ push @comments, CIL::Comment->new_from_name( $self, $comment->{name} );
+ }
+ return \@comments;
+}
+
+sub get_attachments {
+ my ($self) = @_;
+
+ my $attachment_list = $self->list_attachments();
+
+ my @attachments;
+ foreach my $attachment ( @$attachment_list ) {
+ push @attachments, CIL::Attachment->new_from_name( $self, $attachment->{name} );
+ }
+ return \@attachments;
+}
+
sub get_comments_for {
my ($self, $issue) = @_;
@@ -109,6 +159,38 @@ sub get_attachments_for {
return \@attachments;
}
+sub read_config_file {
+ my ( $self, $filename ) = @_;
+
+ my $cfg = CIL::Utils->parse_cil_file( $filename );
+
+ # set some defaults if we don't have any of these
+ foreach my $key ( keys %$defaults ) {
+ $cfg->{$key} ||= $defaults->{$key};
+ }
+
+ # for some things, make a hash out of them
+ foreach my $hash_name ( @config_hashes ) {
+ my $h = {};
+ foreach my $thing ( @{$cfg->{"${hash_name}List"}} ) {
+ $h->{$thing} = 1;
+ }
+ $cfg->{$hash_name} = $h;
+ undef $cfg->{"${hash_name}List"};
+ }
+
+ # set each config item
+ $self->IssueDir( $cfg->{IssueDir} );
+
+ $self->StatusStrict( $cfg->{StatusStrict} );
+ $self->StatusAllowed( $cfg->{StatusAllowed} );
+ $self->StatusOpen( $cfg->{StatusOpen} );
+ $self->StatusClosed( $cfg->{StatusClosed} );
+
+ $self->LabelStrict( $cfg->{LabelStrict} );
+ $self->LabelAllowed( $cfg->{LabelAllowed} );
+}
+
## ----------------------------------------------------------------------------
1;
## ----------------------------------------------------------------------------
diff --git a/lib/CIL/Base.pm b/lib/CIL/Base.pm
index ed5c3a8..f9b932f 100644
--- a/lib/CIL/Base.pm
+++ b/lib/CIL/Base.pm
@@ -25,6 +25,7 @@ use strict;
use warnings;
use Carp;
use DateTime;
+use CIL::Utils;
use base qw(Class::Accessor);
__PACKAGE__->mk_accessors(qw(CreatedBy Inserted Updated));
@@ -87,6 +88,30 @@ sub new_from_fh {
return $class->new_from_data( $name, $data );
}
+sub set_data {
+ my ($self, $data) = @_;
+
+ # loop through all the allowed fields
+ my $fields = $self->fields();
+ my $array_fields = $self->array_fields();
+
+ # save each field
+ foreach my $field ( @$fields ) {
+ next unless defined $data->{$field};
+
+ # make it an array if it should be one
+ if ( exists $array_fields->{$field} and ref $data->{$field} ne 'ARRAY' ) {
+ $data->{$field} = [ $data->{$field} ];
+ }
+
+ # modify the data directly, otherwise Updated will kick in
+ $self->set_no_update($field, $data->{$field});
+ }
+ $self->set_no_update('Changed', 1);
+
+ $self->{data} = $data;
+}
+
sub save {
my ($self, $cil) = @_;
@@ -96,12 +121,18 @@ sub save {
CIL::Utils->write_cil_file( $filename, $self->{data}, @$fields );
}
+sub as_output {
+ my ($self) = @_;
+ my $fields = $self->fields();
+ return CIL::Utils->format_data_as_output( $self->{data}, @$fields );
+}
+
sub create_filename {
my ($class, $cil, $name) = @_;
# create the filename from it's parts
my $prefix = $class->prefix();
- my $issue_dir = $cil->issue_dir;
+ my $issue_dir = $cil->IssueDir;
my $filename = "${issue_dir}/${prefix}_${name}.cil";
return $filename;
@@ -125,7 +156,7 @@ sub set {
# finish if both are defined and they're the same
if ( defined $orig and defined $value ) {
- return if eval { $orig eq $value };
+ return if $orig eq $value;
}
# finish if neither are defined
@@ -147,7 +178,7 @@ sub set_no_update {
sub set_inserted_now {
my ($self) = @_;
- my $time = DateTime->now;
+ my $time = DateTime->now->iso8601;
$self->{data}{Inserted} = $time;
$self->{data}{Updated} = $time;
$self->{Changed} = 1;
@@ -155,7 +186,7 @@ sub set_inserted_now {
sub set_updated_now {
my ($self) = @_;
- my $time = DateTime->now;
+ my $time = DateTime->now->iso8601;
$self->{data}{Updated} = $time;
$self->{Changed} = 1;
}
@@ -184,6 +215,14 @@ sub name {
return $self->{name};
}
+sub errors {
+ my $self = shift;
+ if( @_ ) {
+ $self->{errors} = $_[0];
+ }
+ return $self->{errors};
+}
+
## ----------------------------------------------------------------------------
1;
## ----------------------------------------------------------------------------
diff --git a/lib/CIL/Comment.pm b/lib/CIL/Comment.pm
index 9d0398c..362f094 100644
--- a/lib/CIL/Comment.pm
+++ b/lib/CIL/Comment.pm
@@ -75,6 +75,14 @@ sub last_field {
return 'Description';
}
+sub is_valid {
+ # ToDo:
+ # * check that the issue is valid
+ # * Inserted and Updated are valid
+ # * Description has something in it
+ return 1;
+}
+
## ----------------------------------------------------------------------------
1;
## ----------------------------------------------------------------------------
diff --git a/lib/CIL/Issue.pm b/lib/CIL/Issue.pm
index 0dfaf53..d44626e 100644
--- a/lib/CIL/Issue.pm
+++ b/lib/CIL/Issue.pm
@@ -90,6 +90,37 @@ sub last_field {
return 'Description';
}
+sub is_valid {
+ my ($self, $cil) = @_;
+
+ my @errors;
+
+ # issues should have a Summary
+ unless ( defined defined $self->Summary and length $self->Summary ) {
+ push @errors, 'Issue does not have a summary';
+ }
+
+ # see if we only allow certain Statuses
+ if ( $cil->StatusStrict ) {
+ unless ( exists $cil->StatusAllowed()->{$self->Status} ) {
+ push @errors, "StatusStrict is turned on but this issue has an invalid status '" . $self->Status . "'";
+ }
+ }
+
+ # see if we only allow certain Labels
+ if ( $cil->LabelStrict ) {
+ my @labels = @{$self->Labels};
+ foreach my $label ( @labels ) {
+ unless ( exists $cil->LabelAllowed()->{$label} ) {
+ push @errors, "LabelStrict is turned on but this issue has an invalid label '$label'";
+ }
+ }
+ }
+
+ $self->errors( \@errors );
+ return @errors ? 0 : 1;
+}
+
sub add_label {
my ($self, $label) = @_;
@@ -124,9 +155,9 @@ sub add_attachment {
$self->flag_as_updated();
}
-sub as_output {
+sub Labels {
my ($self) = @_;
- return CIL::Utils->format_data_as_output( $self->{data}, @FIELDS );
+ return $self->{data}{Label};
}
sub Comments {
@@ -139,6 +170,34 @@ sub Attachments {
return $self->{data}{Attachment};
}
+sub is_open {
+ my ($self, $cil) = @_;
+
+ # check against the list of Open Statuses
+ my $open = $cil->StatusOpen();
+ return exists $open->{$self->Status};
+}
+
+sub is_closed {
+ my ($self, $cil) = @_;
+
+ # check against the list of Closed Statuses
+ my $closed = $cil->StatusClosed();
+ return exists $closed->{$self->Status};
+}
+
+sub assigned_to_email {
+ my ($self) = @_;
+
+ return CIL::Utils->extract_email_address( $self->AssignedTo );
+}
+
+sub created_by_email {
+ my ($self) = @_;
+
+ return CIL::Utils->extract_email_address( $self->CreatedBy );
+}
+
## ----------------------------------------------------------------------------
1;
## ----------------------------------------------------------------------------
diff --git a/lib/CIL/Utils.pm b/lib/CIL/Utils.pm
index a0e165c..e9611cf 100644
--- a/lib/CIL/Utils.pm
+++ b/lib/CIL/Utils.pm
@@ -26,6 +26,7 @@ use warnings;
use Carp;
use File::Slurp;
use File::Temp qw(tempfile);
+use Email::Find;
use POSIX qw(getpgrp tcgetpgrp);
use Fcntl qw(:DEFAULT :flock);
@@ -76,8 +77,9 @@ sub parse_from_lines {
}
}
- # now read everything that's left into the $last_field field
- $data->{$last_field} = join("\n", @lines);
+ # now read everything that's left into the $last_field field (if there is one)
+ $data->{$last_field} = join("\n", @lines)
+ if defined $last_field;
return $data;
}
@@ -128,6 +130,8 @@ sub write_cil_file {
sub solicit {
my ($class, $message) = @_;
+ $message = join('', @$message) if ref $message eq 'ARRAY';
+
# when calling this, assume we're already interactive
File::Temp->safe_level(File::Temp::HIGH);
@@ -179,6 +183,21 @@ sub ensure_interactive {
return;
}
+sub extract_email_address {
+ my ($class, $text) = @_;
+
+ my $email_address;
+ my $num_found = find_emails(
+ $text,
+ sub {
+ my ($mail_address, $text_email) = @_;
+ $email_address = $text_email;
+ }
+ );
+
+ return $email_address;
+}
+
## ----------------------------------------------------------------------------
1;
## ----------------------------------------------------------------------------