+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
## ----------------------------------------------------------------------------
# constants
+my $y = 'y';
+
use constant VERSION => '0.2.1';
my @IN_OPTS = (
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
}
my $cil = CIL->new();
+ $cil->read_config_file( '.cil' );
&{"cmd_$command"}($cil, $args, @ARGV);
}
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 {
+ print $issue->error(), "\n";
+ 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;
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 {
+ print $issue->error(), "\n";
+ 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 {
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 {
+ print $comment->error(), "\n";
+ 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
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");
}
}
--- /dev/null
+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.
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.
Summary: Options for issues names needs to be added
-Status: WontFix
+Status: OnHold
CreatedBy: Andrew Chilton <andychilton@gmail.com>
AssignedTo: Andrew Chilton <andychilton@gmail.com>
Label: Milestone-v0.2
Label: Priority-Low
-Label: Release-v0.1
+Label: Release-v0.1.0
Label: Type-Enhancement
Comment: d8dd779f
Comment: feb65ae7
Inserted: 2008-05-05T12:33:19
-Updated: 2008-06-23T12:36:04
+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
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
CreatedBy: Andrew Chilton <andychilton@gmail.com>
AssignedTo: Andrew Chilton <andychilton@gmail.com>
Label: Milestone-v0.3
-Label: Release-v0.2
+Label: Release-v0.2.0
Label: Type-Enhancement
Comment: 7eb313cd
Inserted: 2008-06-23T12:05:33
-Updated: 2008-06-23T12:08:05
+Updated: 2008-06-26T11:54:29
The ability to filter on various things would be nice. For example using these
list commands, though summary should work the same way:
Label: Release-v0.2.1
Label: Type-Enhancement
Inserted: 2008-06-23T12:20:45
-Updated: 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.
Status: New
CreatedBy: Andrew Chilton <andychilton@gmail.com>
AssignedTo: Andrew Chilton <andychilton@gmail.com>
-Label: Milestone-v0.3.0
+Label: Milestone-v0.3
Label: Release-v0.2.1
Label: Type-Enhancement
Inserted: 2008-06-24T12:16:57
-Updated: 2008-06-24T12:16:57
+Updated: 2008-06-26T11:54:57
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
Status: New
CreatedBy: Andrew Chilton <andychilton@gmail.com>
AssignedTo: Andrew Chilton <andychilton@gmail.com>
-Label: Mileston-v0.3.0
+Label: Milestone-v0.3
Label: Release-v0.2.1
Label: Type-Enhancement
Inserted: 2008-06-24T03:24:14
-Updated: 2008-06-24T03:24:14
+Updated: 2008-06-26T11:55:28
The ability to check whether issues/comments/attachments are tracked by Git is
important. Or indeed any VCS system.
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.
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.
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 {
sub list_issues {
my ($self) = @_;
- my $globpath = $self->issue_dir . "/i_*.cil";
+ my $globpath = $self->IssueDir . "/i_*.cil";
my @filenames = bsd_glob($globpath);
my @issues;
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;
## ----------------------------------------------------------------------------
use warnings;
use Carp;
use DateTime;
+use CIL::Utils;
use base qw(Class::Accessor);
__PACKAGE__->mk_accessors(qw(CreatedBy Inserted Updated));
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) = @_;
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;
return $self->{name};
}
+sub error {
+ my $self = shift;
+ if( @_ ) {
+ $self->{error} = $_[0];
+ }
+ return $self->{error};
+}
+
## ----------------------------------------------------------------------------
1;
## ----------------------------------------------------------------------------
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;
## ----------------------------------------------------------------------------
return 'Description';
}
+sub is_valid {
+ my ($self, $cil) = @_;
+
+ # issues should have a Summary
+ unless ( defined defined $self->Summary and length $self->Summary ) {
+ $self->error( 'You must provide a summary.' );
+ return;
+ }
+
+ # see if we only allow certain Statuses
+ if ( $cil->StatusStrict ) {
+ unless ( exists $cil->StatusAllowed()->{$self->Status} ) {
+ $self->error( "You have 'StatusStrict' turned on but this status (" . $self->Status . ") is not in the 'StatusAllowedList'" );
+ return;
+ }
+ }
+
+ # see if we only allow certain Labels
+ if ( $cil->LabelStrict ) {
+ my @labels = @{$self->Labels};
+ foreach my $label ( @labels ) {
+ unless ( exists $cil->LabelAllowed()->{$label} ) {
+ $self->error( "You have 'LabelStrict' turned on but this label ($label) is not in the 'LabelAllowedList'" );
+ return;
+ }
+ }
+ }
+
+ return 1;
+}
+
sub add_label {
my ($self, $label) = @_;
$self->flag_as_updated();
}
-sub as_output {
- my ($self) = @_;
- return CIL::Utils->format_data_as_output( $self->{data}, @FIELDS );
-}
-
sub Labels {
my ($self) = @_;
return $self->{data}{Label};
}
}
- # 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;
}
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);