ADD: added issue class
This commit is contained in:
parent
c57dea44a3
commit
60010249ee
@ -1,6 +0,0 @@
|
|||||||
package Git::Issue::Manager;
|
|
||||||
use strict;
|
|
||||||
use warnings;
|
|
||||||
|
|
||||||
|
|
||||||
1;
|
|
442
lib/Git/IssueManager.pm
Normal file
442
lib/Git/IssueManager.pm
Normal file
@ -0,0 +1,442 @@
|
|||||||
|
package Git::IssueManager;
|
||||||
|
#ABSTRACT: Module for managing issues in a git branch within your repository
|
||||||
|
use Moose;
|
||||||
|
use Git::RepositoryHL;
|
||||||
|
use DateTime;
|
||||||
|
use DateTime::TimeZone;
|
||||||
|
use Data::Dumper;
|
||||||
|
|
||||||
|
our $VERSION = "0.1";
|
||||||
|
|
||||||
|
=attr repository
|
||||||
|
|
||||||
|
Git::Repository object on which to do the issue management
|
||||||
|
|
||||||
|
=cut
|
||||||
|
has 'repository' => (is =>'ro', isa => 'Git::RepositoryHL', required => 1);
|
||||||
|
|
||||||
|
|
||||||
|
=method ready
|
||||||
|
|
||||||
|
validates if everything is in place for issue management
|
||||||
|
|
||||||
|
=cut
|
||||||
|
sub ready
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
# return false if issue branch does not exist
|
||||||
|
return 0 unless $self->repository->hasBranch('heads/issues');
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
=method version
|
||||||
|
|
||||||
|
returns the version number of the issue system within the issue branch
|
||||||
|
|
||||||
|
=cut
|
||||||
|
sub version
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
return unless $self->ready();
|
||||||
|
|
||||||
|
my @tree = $self->repository->getTree("issues");
|
||||||
|
|
||||||
|
for my $t (@tree)
|
||||||
|
{
|
||||||
|
if ($t->{name} eq ".version")
|
||||||
|
{
|
||||||
|
my $version = $self->repository->getBlob($t->{ref});
|
||||||
|
chomp($version);
|
||||||
|
return $version;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
=method tag
|
||||||
|
|
||||||
|
returns the issue tag to prepend in front of all issue ids
|
||||||
|
|
||||||
|
=cut
|
||||||
|
sub tag
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
return unless $self->ready();
|
||||||
|
|
||||||
|
my @tree = $self->repository->getTree("issues");
|
||||||
|
|
||||||
|
for my $t (@tree)
|
||||||
|
{
|
||||||
|
if ($t->{name} eq ".tag")
|
||||||
|
{
|
||||||
|
my $tag = $self->repository->getBlob($t->{ref});
|
||||||
|
chomp($tag);
|
||||||
|
return $tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
=method init
|
||||||
|
|
||||||
|
initialize the repository for managing issues
|
||||||
|
|
||||||
|
=cut
|
||||||
|
sub init
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
my $issue_tag = shift;
|
||||||
|
|
||||||
|
return unless ! $self->ready();
|
||||||
|
|
||||||
|
die("no issue tag given") unless $issue_tag;
|
||||||
|
|
||||||
|
# create a version file in the issues branch, also creating the branch with this
|
||||||
|
my $filehash = $self->repository->createFileObject("0.1");
|
||||||
|
|
||||||
|
# add the version file to a git tree
|
||||||
|
my $t = {
|
||||||
|
mode => "100644",
|
||||||
|
type => "blob",
|
||||||
|
ref => $filehash,
|
||||||
|
path => ".version"
|
||||||
|
};
|
||||||
|
my @tree;
|
||||||
|
push(@tree,$t);
|
||||||
|
|
||||||
|
# create a tag file in the issues branch to identify the issue tag to prepend
|
||||||
|
$filehash = $self->repository->createFileObject($issue_tag);
|
||||||
|
my $t2 = {
|
||||||
|
mode => "100644",
|
||||||
|
type => "blob",
|
||||||
|
ref => $filehash,
|
||||||
|
path => ".tag"
|
||||||
|
};
|
||||||
|
push(@tree,$t2);
|
||||||
|
|
||||||
|
# create the tree
|
||||||
|
my $tree_hash=$self->repository->createTree(\@tree);
|
||||||
|
|
||||||
|
# commit the created tree
|
||||||
|
my $commit=$self->repository->createTreeCommit($tree_hash, "1", "initialized issue manager for this repository");
|
||||||
|
|
||||||
|
# now update/create the branch refs/heads/issues
|
||||||
|
|
||||||
|
$self->repository->updateRef("refs/heads/issues",$commit);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
=method _recreate - internal method, do not call directly
|
||||||
|
|
||||||
|
method used to recreate the tree structure received by getTree to something like createTree can use
|
||||||
|
|
||||||
|
=cut
|
||||||
|
sub recreate
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
my $array = shift;
|
||||||
|
|
||||||
|
for my $a (@{$array})
|
||||||
|
{
|
||||||
|
$a->{path} = $a->{name};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
=method add
|
||||||
|
|
||||||
|
add an issue to the repository
|
||||||
|
|
||||||
|
=cut
|
||||||
|
sub add
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
my $issue = shift;
|
||||||
|
die("IssueManager not initialized") unless $self->ready();
|
||||||
|
die("no Git::IssueManager::Issue object given") unless ref($issue) eq "Git::IssueManager::Issue";
|
||||||
|
|
||||||
|
my @root = $self->repository->getTree("issues");
|
||||||
|
my @open = $self->repository->getTree("issues","open/");
|
||||||
|
my @closed = $self->repository->getTree("issues","closed/");
|
||||||
|
my @assigned = $self->repository->getTree("issues","assigned/");
|
||||||
|
my @inprogress = $self->repository->getTree("issues","inprogress");
|
||||||
|
|
||||||
|
$self->recreate(\@open);
|
||||||
|
$self->recreate(\@closed);
|
||||||
|
$self->recreate(\@assigned);
|
||||||
|
$self->recreate(\@inprogress);
|
||||||
|
|
||||||
|
my $issues;
|
||||||
|
if ($issue->status eq "open")
|
||||||
|
{
|
||||||
|
$issues=\@open;
|
||||||
|
}
|
||||||
|
elsif ($issue->status eq "closed")
|
||||||
|
{
|
||||||
|
$issues=\@closed;
|
||||||
|
}
|
||||||
|
elsif ($issue->status eq "assigned")
|
||||||
|
{
|
||||||
|
$issues=\@assigned;
|
||||||
|
}
|
||||||
|
elsif ($issue->status eq "inprogress")
|
||||||
|
{
|
||||||
|
$issues=\@inprogress;
|
||||||
|
}
|
||||||
|
|
||||||
|
# check if issues already exist, only checks the subject !!!
|
||||||
|
for my $i (@{$issues})
|
||||||
|
{
|
||||||
|
if ($i->{name} eq $issue->subject)
|
||||||
|
{
|
||||||
|
die("issue already exist");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# append the issue to the tree
|
||||||
|
my $hash=$issue->createIssue($self->repository);
|
||||||
|
push (@{$issues},{
|
||||||
|
path => $issue->subject,
|
||||||
|
ref => $hash,
|
||||||
|
type => "tree",
|
||||||
|
mode => "040000"
|
||||||
|
});
|
||||||
|
|
||||||
|
# now recreate tree structure
|
||||||
|
my $openhash = $self->repository->createTree(\@open);
|
||||||
|
my $closedhash = $self->repository->createTree(\@closed);
|
||||||
|
my $assignedhash = $self->repository->createTree(\@assigned);
|
||||||
|
my $inprogresshash= $self->repository->createTree(\@inprogress);
|
||||||
|
|
||||||
|
my $openfound=0;
|
||||||
|
my $closedfound=0;
|
||||||
|
my $assignedfound=0;
|
||||||
|
my $inprogressfound=0;
|
||||||
|
# now recreate the root tree
|
||||||
|
for my $r (@root)
|
||||||
|
{
|
||||||
|
if ($r->{name} eq "open")
|
||||||
|
{
|
||||||
|
$r->{ref}=$openhash;
|
||||||
|
$openfound=1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($r->{name} eq "closed")
|
||||||
|
{
|
||||||
|
$r->{ref}=$closedhash;
|
||||||
|
$closedfound=1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($r->{name} eq "assigned")
|
||||||
|
{
|
||||||
|
$r->{ref}=$assignedhash;
|
||||||
|
$assignedfound=1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($r->{name} eq "inprogess")
|
||||||
|
{
|
||||||
|
$r->{ref}=$inprogresshash;
|
||||||
|
$inprogressfound=1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!$openfound && defined($openhash))
|
||||||
|
{
|
||||||
|
my $t = {
|
||||||
|
path => "open",
|
||||||
|
ref => $openhash,
|
||||||
|
type => "tree",
|
||||||
|
mode => "040000"
|
||||||
|
};
|
||||||
|
push(@root, $t);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$closedfound && defined($closedhash))
|
||||||
|
{
|
||||||
|
my $t = {
|
||||||
|
path => "closed",
|
||||||
|
ref => $closedhash,
|
||||||
|
type => "tree",
|
||||||
|
mode => "040000"
|
||||||
|
};
|
||||||
|
push(@root, $t);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$assignedfound && defined($assignedhash))
|
||||||
|
{
|
||||||
|
my $t = {
|
||||||
|
path => "assigned",
|
||||||
|
ref => $assignedhash,
|
||||||
|
type => "tree",
|
||||||
|
mode => "040000"
|
||||||
|
};
|
||||||
|
push(@root, $t);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$inprogressfound && defined($inprogresshash))
|
||||||
|
{
|
||||||
|
my $t = {
|
||||||
|
path => "inprogress",
|
||||||
|
ref => $inprogresshash,
|
||||||
|
type => "tree",
|
||||||
|
mode => "040000"
|
||||||
|
};
|
||||||
|
push(@root, $t);
|
||||||
|
}
|
||||||
|
|
||||||
|
my $roothash = $self->repository->createTree(\@root);
|
||||||
|
|
||||||
|
# commit the issue
|
||||||
|
my $commit=$self->repository->createTreeCommit($roothash,$self->repository->getBranchRef("heads/issues") || "start", "ADD: " . $issue->subject);
|
||||||
|
|
||||||
|
#now update branch refs/heads/issues
|
||||||
|
$self->repository->updateRef("refs/heads/issues",$commit);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
=method _to_issue - internal method, do not call directly
|
||||||
|
|
||||||
|
returns the issue converted from a tree hash entry
|
||||||
|
|
||||||
|
=cut
|
||||||
|
sub _to_issue
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
my $i = shift;
|
||||||
|
|
||||||
|
my $issue = Git::IssueManager::Issue->new(subject => $i->{name});
|
||||||
|
|
||||||
|
$issue->priority($self->repository->getBlob($self->repository->getFileRef("issues",$i->{path}."/priority")));
|
||||||
|
$issue->severity($self->repository->getBlob($self->repository->getFileRef("issues",$i->{path}."/severity")));
|
||||||
|
$issue->type($self->repository->getBlob($self->repository->getFileRef("issues",$i->{path}."/type")));
|
||||||
|
$issue->description($self->repository->getBlob($self->repository->getFileRef("issues",$i->{path}."/description")));
|
||||||
|
$issue->comment($self->repository->getBlob($self->repository->getFileRef("issues",$i->{path}."/comment")));
|
||||||
|
$issue->id($self->repository->getBlob($self->repository->getFileRef("issues",".tag")) . "-" . substr($i->{ref},0,8));
|
||||||
|
|
||||||
|
my $worker = $self->repository->getBlob($self->repository->getFileRef("issues",$i->{path}."/worker"));
|
||||||
|
if (defined($worker))
|
||||||
|
{
|
||||||
|
$worker=~/^(.*)<(.*)>$/;
|
||||||
|
$issue->worker($1 || "");
|
||||||
|
$issue->worker_email($2 || "");
|
||||||
|
}
|
||||||
|
|
||||||
|
my $taglist= $self->repository->getBlob($self->repository->getFileRef("issues",$i->{path}."/tags"));
|
||||||
|
if (defined($taglist) && length($taglist)>0)
|
||||||
|
{
|
||||||
|
my @tags=split /\n/,$taglist;
|
||||||
|
$issue->tags(\@tags);
|
||||||
|
}
|
||||||
|
|
||||||
|
my $tz=DateTime::TimeZone->new( name => 'local' );
|
||||||
|
my @commit = $self->repository->getFileLog("issues",$i->{path}."/subject",1);
|
||||||
|
$issue->creation_date(DateTime->from_epoch(epoch => $commit[0]->{date},time_zone=>$tz ));
|
||||||
|
$issue->author($commit[0]->{author}->{name});
|
||||||
|
$issue->author_email($commit[0]->{author}->{email});
|
||||||
|
|
||||||
|
@commit = $self->repository->getFileLog("issues",$i->{path},1);
|
||||||
|
$issue->last_change_date(DateTime->from_epoch(epoch => $commit[0]->{date},time_zone=>$tz ));
|
||||||
|
|
||||||
|
$issue->closed_date(DateTime->from_epoch(epoch => $commit[0]->{date},time_zone=>$tz ));
|
||||||
|
|
||||||
|
return $issue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
=method get
|
||||||
|
|
||||||
|
return the issue with the given id
|
||||||
|
|
||||||
|
=cut
|
||||||
|
sub get
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
my $id = shift;
|
||||||
|
|
||||||
|
die("no id given") unless defined($id);
|
||||||
|
|
||||||
|
$id=~/^[A-Z]+-(.*)$/;
|
||||||
|
my $hash=$1;
|
||||||
|
|
||||||
|
# first search for the id
|
||||||
|
my @open = $self->repository->getTree("issues","open/");
|
||||||
|
for my $i (@open)
|
||||||
|
{
|
||||||
|
if (substr($i->{ref},0,8) eq $hash)
|
||||||
|
{
|
||||||
|
return $self->_to_issue($i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
my @assigned = $self->repository->getTree("issues","assigned/");
|
||||||
|
for my $i (@assigned)
|
||||||
|
{
|
||||||
|
if (substr($i->{ref},0,8) eq $hash)
|
||||||
|
{
|
||||||
|
return $self->_to_issue($i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
my @inprogress = $self->repository->getTree("issues","inprogress");
|
||||||
|
for my $i (@inprogress)
|
||||||
|
{
|
||||||
|
if (substr($i->{ref},0,8) eq $hash)
|
||||||
|
{
|
||||||
|
return $self->_to_issue($i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
my @closed = $self->repository->getTree("issues","closed/");
|
||||||
|
for my $i (@closed)
|
||||||
|
{
|
||||||
|
if (substr($i->{ref},0,8) eq $hash)
|
||||||
|
{
|
||||||
|
return $self->_to_issue($i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
=method list
|
||||||
|
|
||||||
|
list all issues of the repository
|
||||||
|
|
||||||
|
=cut
|
||||||
|
sub list
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
my $filter = shift;
|
||||||
|
my @issues =$self->repository->getTree("issues","open/");
|
||||||
|
my @ret;
|
||||||
|
|
||||||
|
if (!defined($filter) || $filter eq "open" )
|
||||||
|
{
|
||||||
|
@issues=$self->repository->getTree("issues","open/");
|
||||||
|
}
|
||||||
|
|
||||||
|
for my $i (@issues)
|
||||||
|
{
|
||||||
|
my $issue = $self->_to_issue($i);
|
||||||
|
|
||||||
|
push(@ret, $issue);
|
||||||
|
}
|
||||||
|
|
||||||
|
# now sort by creation date
|
||||||
|
@ret = sort {DateTime->compare($a->creation_date, $b->creation_date)} @ret;
|
||||||
|
|
||||||
|
return @ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
1;
|
530
lib/Git/IssueManager/Issue.pm
Normal file
530
lib/Git/IssueManager/Issue.pm
Normal file
@ -0,0 +1,530 @@
|
|||||||
|
package Git::IssueManager::Issue;
|
||||||
|
#ABSTRACT: class representing an Issue
|
||||||
|
use Moose;
|
||||||
|
use File::Basename;
|
||||||
|
|
||||||
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
B<Git::IssueManager::Issue> represents an issue within the Git::IssueManager module. Issues can be
|
||||||
|
added, removed, modified and listed.
|
||||||
|
|
||||||
|
Make sure that you understand all the attributes before adding issues to your repository.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
|
||||||
|
=attr subject
|
||||||
|
|
||||||
|
The subject/ title of the issue
|
||||||
|
|
||||||
|
At most 50 chars allowed.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
has 'subject' => (is => 'rw', isa => 'Str', required => 1, trigger => sub {
|
||||||
|
my ($self, $new, $old) = @_;
|
||||||
|
|
||||||
|
die("subject exceeds 50 chars") unless length($new) < 51;
|
||||||
|
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
=attr priority
|
||||||
|
|
||||||
|
The priority of the issue. Possible values are:
|
||||||
|
|
||||||
|
=over
|
||||||
|
|
||||||
|
=item I<urgent> - the most highes level of priority
|
||||||
|
|
||||||
|
=item I<high>
|
||||||
|
|
||||||
|
=item I<medium>
|
||||||
|
|
||||||
|
=item I<low>
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
The default value is B<low>.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
has 'priority' => (is => 'rw', isa => 'Str', default => 'low', trigger => sub {
|
||||||
|
my ($self, $new, $old) = @_;
|
||||||
|
|
||||||
|
die("unknown value (" . $new . ")") unless lc($new) eq "urgent" || lc($new) eq "high" ||
|
||||||
|
lc($new) eq "medium" || lc($new) eq "low";
|
||||||
|
});
|
||||||
|
|
||||||
|
=attr severity
|
||||||
|
|
||||||
|
The severity of the issue. Possible values are:
|
||||||
|
|
||||||
|
=over
|
||||||
|
|
||||||
|
=item I<critical>
|
||||||
|
|
||||||
|
=item I<high>
|
||||||
|
|
||||||
|
=item I<medium>
|
||||||
|
|
||||||
|
=item I<low>
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
The default value is B<low>
|
||||||
|
|
||||||
|
=cut
|
||||||
|
has 'severity' => (is => 'rw', isa => 'Str', default => 'low',trigger => sub {
|
||||||
|
my ($self, $new, $old) = @_;
|
||||||
|
|
||||||
|
die("unknown value (" . $new . ")") unless lc($new) eq "critical" || lc($new) eq "high" ||
|
||||||
|
lc($new) eq "medium" || lc($new) eq "low";
|
||||||
|
});
|
||||||
|
|
||||||
|
=attr type
|
||||||
|
|
||||||
|
The type of the issue. Possible values are:
|
||||||
|
|
||||||
|
=over
|
||||||
|
|
||||||
|
=item I<bug> - a problem within the code, preventing the correct working of the software
|
||||||
|
|
||||||
|
=item I<security-bug> - a security related problem within the code, preventing the correct working of the software
|
||||||
|
|
||||||
|
=item I<improvement> - an enhancement to an already existing feature
|
||||||
|
|
||||||
|
=item I<feature> - a completly new feature
|
||||||
|
|
||||||
|
=item I<task> - a simple task, which should be done (please use rarely)
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
The default values is B<bug>.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
has 'type' => (is => 'rw', isa => 'Str', default => 'bug', trigger => sub {
|
||||||
|
my ($self, $new, $old) = @_;
|
||||||
|
|
||||||
|
die("unknown value (" . $new . ")") unless lc($new) eq "bug" || lc($new) eq "security-bug" ||
|
||||||
|
lc($new) eq "improvement" || lc($new) eq "feature" ||
|
||||||
|
lc($new) eq "task";
|
||||||
|
});
|
||||||
|
|
||||||
|
=attr status
|
||||||
|
|
||||||
|
The status of the issue. Possible values are:
|
||||||
|
|
||||||
|
=over
|
||||||
|
|
||||||
|
=item I<open> - nothing has been done yet
|
||||||
|
|
||||||
|
=item I<assigned> - the issue has been assigned to a developer
|
||||||
|
|
||||||
|
=item I<inprogess> - somebody is working on the issue
|
||||||
|
|
||||||
|
=item I<closed> - the issue is closed
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
The default value is B<open>.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
has 'status' => (is => 'rw', isa => 'Str', default => 'open', trigger => sub {
|
||||||
|
my ($self, $new, $old) = @_;
|
||||||
|
|
||||||
|
die("unknown value (" . $new . ")") unless lc($new) eq "open" || lc($new) eq "assigned" ||
|
||||||
|
lc($new) eq "inprogress" || lc($new) eq "closed";
|
||||||
|
});
|
||||||
|
|
||||||
|
=attr substatus
|
||||||
|
|
||||||
|
A substatus to the actual status. Possible values are:
|
||||||
|
|
||||||
|
=over
|
||||||
|
|
||||||
|
=item I<none> - there is no substatus
|
||||||
|
|
||||||
|
=item I<fixed> - the bug was fixed
|
||||||
|
|
||||||
|
=item I<wontfix> - the issue has been closed but it will never be fixed
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
The default value is B<none>.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
has 'substatus' => (is => 'rw', isa => 'Str', default => 'none', trigger => sub {
|
||||||
|
my ($self, $new, $old) = @_;
|
||||||
|
|
||||||
|
die("unknown value (" . $new . ")") unless lc($new) eq "none" || lc($new) eq "fixed" ||
|
||||||
|
lc($new) eq "wontfix";
|
||||||
|
});
|
||||||
|
|
||||||
|
=attr comment
|
||||||
|
|
||||||
|
A comment to the current status of the issue.
|
||||||
|
|
||||||
|
Only Plain Text is allowed.
|
||||||
|
|
||||||
|
Default value is the empty string.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
has 'comment' => (is => 'rw', isa=>'Str', default => "");
|
||||||
|
|
||||||
|
|
||||||
|
=attr description
|
||||||
|
|
||||||
|
The full description of the issue.
|
||||||
|
|
||||||
|
Only Plain Text and Markdown are allowed.
|
||||||
|
|
||||||
|
B<no HTML>
|
||||||
|
|
||||||
|
The default value is the empty string.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
has 'description' => (is => 'rw', isa => 'Str', default => "");
|
||||||
|
|
||||||
|
=attr tags
|
||||||
|
|
||||||
|
An arrayref of tags/ keywords for better identifying the issue.
|
||||||
|
|
||||||
|
Maximum length of one tag is B<20> characters.
|
||||||
|
|
||||||
|
Maximum number of tags is B<10>.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
has 'tags' => (is => 'rw', isa => 'ArrayRef[Str]', default => sub {return[];});
|
||||||
|
|
||||||
|
=attr attachments
|
||||||
|
|
||||||
|
An arrayref of files attached to this issue, for example documentation or text files presenting
|
||||||
|
error messages, screenshots, etc.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
has 'attachements' => (is => 'rw', isa=> 'ArrayRef[Str]', default => sub{return [];});
|
||||||
|
|
||||||
|
|
||||||
|
=attr author
|
||||||
|
|
||||||
|
The author of the issue, can be the name or an anomynized nickname
|
||||||
|
|
||||||
|
=cut
|
||||||
|
has 'author' => (is=> 'rw', isa => 'Str', default => "");
|
||||||
|
|
||||||
|
=attr author_email
|
||||||
|
|
||||||
|
The authors email for sending status changes of the issue
|
||||||
|
|
||||||
|
=cut
|
||||||
|
has 'author_email' => (is => 'rw', isa => 'Str', default => "");
|
||||||
|
|
||||||
|
=attr worker
|
||||||
|
|
||||||
|
The persons name working on solving the issue
|
||||||
|
|
||||||
|
=cut
|
||||||
|
has 'worker' => (is => 'rw', isa => 'Str', default => "");
|
||||||
|
|
||||||
|
=attr worker_email
|
||||||
|
|
||||||
|
The email address of the person working on this issue
|
||||||
|
|
||||||
|
=cut
|
||||||
|
has 'worker_email' => ( is => 'rw', isa => 'Str', default =>"");
|
||||||
|
|
||||||
|
=attr creation_date
|
||||||
|
|
||||||
|
A datetime object representing the date/time the issue was created
|
||||||
|
|
||||||
|
=cut
|
||||||
|
has 'creation_date' => (is => 'rw', isa=>'DateTime',default => sub{return DateTime->now();});
|
||||||
|
|
||||||
|
=attr closed_date
|
||||||
|
|
||||||
|
A datetime object representing the date/time the issue was closed, only valid if status is closed
|
||||||
|
|
||||||
|
=cut
|
||||||
|
has 'closed_date' => (is => 'rw', isa=>'DateTime',default => sub{return DateTime->now();});
|
||||||
|
|
||||||
|
=attr last_change_date
|
||||||
|
|
||||||
|
A datetime object representing the date/time the issue was last modified
|
||||||
|
|
||||||
|
=cut
|
||||||
|
has 'last_change_date' => (is => 'rw', isa=>'DateTime',default => sub{return DateTime->now();});
|
||||||
|
|
||||||
|
=attr id
|
||||||
|
|
||||||
|
id of the issue
|
||||||
|
|
||||||
|
=cut
|
||||||
|
has 'id' => (is => 'rw', isa => 'Str', default => "");
|
||||||
|
|
||||||
|
|
||||||
|
=method addTag
|
||||||
|
|
||||||
|
add another tag to the issue.
|
||||||
|
|
||||||
|
B<Example:>
|
||||||
|
|
||||||
|
$issue->addTag("File");
|
||||||
|
|
||||||
|
=over
|
||||||
|
|
||||||
|
=item B<1. Parameter:> Tag to add to the issue
|
||||||
|
|
||||||
|
=back
|
||||||
|
=cut
|
||||||
|
sub addTag
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
my $tag = shift;
|
||||||
|
|
||||||
|
die("no tag given") unless defined($tag);
|
||||||
|
die("too many tags") unless @{$self->tags}<11;
|
||||||
|
die("tag exceeds 20 chars") unless length($tag) < 21;
|
||||||
|
|
||||||
|
push (@{$self->tags}, $tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
=method delTag
|
||||||
|
|
||||||
|
del a tag from the issue
|
||||||
|
|
||||||
|
B<Example:>
|
||||||
|
|
||||||
|
$issue->delTag("File");
|
||||||
|
|
||||||
|
=over
|
||||||
|
|
||||||
|
=item B<1. Parameter> Tag to remove from issue
|
||||||
|
|
||||||
|
=back
|
||||||
|
=cut
|
||||||
|
sub delTag
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
my $tag = shift;
|
||||||
|
|
||||||
|
die("no tag given") unless defined($tag);
|
||||||
|
|
||||||
|
my $i = 0;
|
||||||
|
|
||||||
|
for my $t (@{$self->tags})
|
||||||
|
{
|
||||||
|
if ($t eq $tag)
|
||||||
|
{
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
$i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
splice(@{$self->tags},$i,1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
=method addAttachment
|
||||||
|
|
||||||
|
Add another attachment to the issue.
|
||||||
|
|
||||||
|
B<Example:>
|
||||||
|
|
||||||
|
$issue->addAttachement("/tmp/test.txt");
|
||||||
|
|
||||||
|
=over
|
||||||
|
|
||||||
|
=item B<1. Parameter> path to the attachment to add
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
Make sure the attachment exist at the given path and stays there until the issue has been
|
||||||
|
added.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
sub addAttachement
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
my $attachment = shift;
|
||||||
|
|
||||||
|
die("no attachment given") unless defined($attachment);
|
||||||
|
die("file does not exist") unless -e $attachment;
|
||||||
|
|
||||||
|
|
||||||
|
push (@{$self->attachements}, $attachment);
|
||||||
|
}
|
||||||
|
|
||||||
|
=method delAttachement
|
||||||
|
|
||||||
|
Remove an attachment from the issue.
|
||||||
|
|
||||||
|
B<Example:>
|
||||||
|
|
||||||
|
$issue->delAttachement("/tmp/test");
|
||||||
|
|
||||||
|
=over
|
||||||
|
|
||||||
|
=item B<1. Parameter> Attachment path to remove from issue
|
||||||
|
|
||||||
|
=back
|
||||||
|
=cut
|
||||||
|
sub delAttachment
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
my $attachment = shift;
|
||||||
|
|
||||||
|
die("no attachment given") unless defined($attachment);
|
||||||
|
|
||||||
|
my $i = 0;
|
||||||
|
|
||||||
|
for my $a (@{$self->attachments})
|
||||||
|
{
|
||||||
|
if ($a eq $attachment)
|
||||||
|
{
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
$i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
splice(@{$self->attachments},$i,1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
=method _createAttachmentTree - internal method, do not call directly
|
||||||
|
|
||||||
|
creates a git repository tree object from the attachment array and return the hash of the object
|
||||||
|
|
||||||
|
=over
|
||||||
|
=item B<1. Parameter> reference to a Git::RepositoryHL object
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=cut
|
||||||
|
sub _createAttachmentTree
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
my $repository = shift;
|
||||||
|
my @tree;
|
||||||
|
|
||||||
|
for my $a (@{$self->attachments})
|
||||||
|
{
|
||||||
|
my $hash = $repository->createFileObjectFromFile($a);
|
||||||
|
my $t = {
|
||||||
|
ref => $hash,
|
||||||
|
path => fileparse($a),
|
||||||
|
mode => "100644",
|
||||||
|
type => "blob"
|
||||||
|
};
|
||||||
|
push(@tree, $t);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return $repository->createTree(\@tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
=method createIssue
|
||||||
|
|
||||||
|
Creates the issue inside the given git repository and commits these changes to the issues branch
|
||||||
|
|
||||||
|
=over
|
||||||
|
|
||||||
|
=item B<1. Parameter> reference to a Git::RepositoryHL object
|
||||||
|
|
||||||
|
=back
|
||||||
|
=cut
|
||||||
|
sub createIssue
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
my $repository = shift;
|
||||||
|
my @tree;
|
||||||
|
|
||||||
|
die("No Git::RepositoryHL object given") unless ref($repository) eq "Git::RepositoryHL";
|
||||||
|
|
||||||
|
my $subject = {
|
||||||
|
path => "subject",
|
||||||
|
ref => $repository->createFileObject($self->subject),
|
||||||
|
type => "blob",
|
||||||
|
mode => "100644"
|
||||||
|
};
|
||||||
|
push(@tree, $subject);
|
||||||
|
|
||||||
|
my $priority = {
|
||||||
|
path => "priority",
|
||||||
|
ref => $repository->createFileObject($self->priority),
|
||||||
|
type => "blob",
|
||||||
|
mode => "100644"
|
||||||
|
};
|
||||||
|
push(@tree, $priority);
|
||||||
|
|
||||||
|
my $severity = {
|
||||||
|
path => "severity",
|
||||||
|
ref => $repository->createFileObject($self->severity),
|
||||||
|
type => "blob",
|
||||||
|
mode => "100644"
|
||||||
|
};
|
||||||
|
push(@tree, $severity);
|
||||||
|
|
||||||
|
my $type = {
|
||||||
|
path => "type",
|
||||||
|
ref => $repository->createFileObject($self->type),
|
||||||
|
type => "blob",
|
||||||
|
mode => "100644"
|
||||||
|
};
|
||||||
|
push(@tree, $type);
|
||||||
|
|
||||||
|
my $substatus = {
|
||||||
|
path => "substatus",
|
||||||
|
ref => $repository->createFileObject($self->substatus),
|
||||||
|
type => "blob",
|
||||||
|
mode => "100644"
|
||||||
|
};
|
||||||
|
|
||||||
|
my $comment = {
|
||||||
|
path => "comment",
|
||||||
|
ref => $repository->createFileObject($self->comment),
|
||||||
|
type => "blob",
|
||||||
|
mode => "100644"
|
||||||
|
};
|
||||||
|
push(@tree, $comment);
|
||||||
|
|
||||||
|
my $description = {
|
||||||
|
path => "description",
|
||||||
|
ref => $repository->createFileObject($self->description),
|
||||||
|
type => "blob",
|
||||||
|
mode => "100644"
|
||||||
|
};
|
||||||
|
push(@tree, $description);
|
||||||
|
|
||||||
|
my $worker = {
|
||||||
|
path => "worker",
|
||||||
|
ref => $repository->createFileObject($self->worker . "<" . $self->worker_email . ">"),
|
||||||
|
type => "blob",
|
||||||
|
mode => "100644"
|
||||||
|
};
|
||||||
|
push(@tree, $worker);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
my $tags = {
|
||||||
|
path => "tags",
|
||||||
|
ref => $repository->createFileObject(join "\n", @{$self->tags}),
|
||||||
|
type => "blob",
|
||||||
|
mode => "100644"
|
||||||
|
};
|
||||||
|
push(@tree, $tags);
|
||||||
|
|
||||||
|
if (@{$self->attachements} > 0)
|
||||||
|
{
|
||||||
|
my $attachments={
|
||||||
|
path => "attachments",
|
||||||
|
ref => $self->_createAttachmentTree($repository),
|
||||||
|
type => "tree",
|
||||||
|
mode => "040000"
|
||||||
|
};
|
||||||
|
push(@tree, $attachments);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $repository->createTree(\@tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
Loading…
Reference in New Issue
Block a user