App-GHPT
view release on metacpan or search on metacpan
lib/App/GHPT/WorkSubmitter.pm view on Meta::CPAN
qw(
GH_TOKEN
GITHUB_TOKEN
)
];
}
sub _token_from_env ($self) {
my $key = first { $ENV{$_} } $self->_all_env_keys;
return $key ? $ENV{$key} : undef;
}
sub _build_pivotaltracker_token ($self) {
my $env_key = 'PIVOTALTRACKER_TOKEN';
my $key = 'submit-work.pivotaltracker.token';
return $ENV{$env_key} // $self->_config_val($key)
// $self->_require_env_or_git_config( $env_key, $key );
}
sub _build_pt_api ($self) {
return WebService::PivotalTracker->new(
token => $self->pivotaltracker_token,
);
}
sub _build_project_ids ($self) {
my $want = $self->project;
return [
map { $_->id }
grep { $want ? ( $_->name =~ /\Q$want/i ) : 1 }
$self->_pt_api->projects->@*
];
}
sub _find_project ($self) {
my $want = $self->project;
my @projects = grep { $want ? ( $_->name =~ /\Q$want/i ) : 1 }
$self->_pt_api->projects->@*;
return $projects[0] if @projects == 1;
my %project_by_name = map { $_->name => $_ } @projects;
my $name = $self->_choose( [ sort keys %project_by_name ] );
return $project_by_name{$name};
}
sub _find_requester ( $self, $project ) {
my $want = $self->requester;
my @memberships = grep { $want ? ( $_->person->name =~ /\Q$want/i ) : 1 }
$project->memberships->@*;
return $memberships[0]->person if @memberships == 1;
my %membership_by_person_name
= map { $_->person->name => $_ } @memberships;
my $name = $self->_choose( [ sort keys %membership_by_person_name ] );
return $membership_by_person_name{$name}->person;
}
before print_usage_text => sub {
say <<'EOF';
Please see POD in App::GHPT for installation and troubleshooting directions.
EOF
};
sub run ($self) {
my ( $requester, $chosen_story ) = $self->_choose_pt_story;
unless ($requester) {
die "No requester found!\n";
}
unless ($chosen_story) {
die "No started stories found!\n";
}
my $pull_request_url = $self->_create_pull_request(
$self->_append_question_answers(
$self->_confirm_story(
$self->_text_for_story( $chosen_story, $requester ),
),
),
);
$self->_update_pt_story( $chosen_story, $pull_request_url );
say $chosen_story->url;
say $pull_request_url;
return 0;
}
sub _append_question_answers ( $self, $text ) {
my $qa_markdown = App::GHPT::WorkSubmitter::AskPullRequestQuestions->new(
merge_to_branch_name => 'origin/' . $self->base,
question_namespaces => $self->_question_namespaces,
)->ask_questions;
return $text unless defined $qa_markdown and length $qa_markdown;
return join "\n\n",
$text,
'----',
$qa_markdown,
;
}
sub _choose {
my $self = shift;
return choose(@_)
|| exit 1; # user hit q or ctrl-d to quit
}
sub _choose_pt_story ($self) {
if ( $self->create_story ) {
my $project = $self->_find_project;
my $requester = $self->_find_requester($project);
my $name = $self->_get_story_name;
if ( $self->dry_run ) {
say "Would create story $name in "
. $project->name
. ' with requester '
. $requester->name
. ' but this is a dry-run.';
exit;
}
return (
$requester->name,
$self->_pt_api->create_story(
current_state => 'started',
# This is primarily intended for small changes/stories, so 0 points.
estimate => 0,
name => $name,
owner_ids => [ $self->_pt_api->me->id ],
project_id => $project->id,
requested_by_id => $requester->id,
)
);
}
my $stories = [
map {
$self->_pt_api->project_stories_where(
project_id => $_,
filter => sprintf(
'(owner:%s AND (state:started OR state:finished))',
$self->pivotaltracker_username
),
)->@*
} $self->_project_ids->@*
];
$stories = $self->_filter_chores_and_maybe_warn_user($stories);
return undef unless $stories->@*;
my %stories_lookup = map { $_->name => $_ } $stories->@*;
my $chosen_story = $self->_choose( [ sort keys %stories_lookup ] );
return (
$stories_lookup{$chosen_story}->requested_by->name,
$stories_lookup{$chosen_story}
);
}
sub _get_story_name ($self) {
my $story_name = $self->story_name;
if ( !$story_name ) {
say q{Please enter the new story's name:};
$story_name = $self->_read_line;
}
return $story_name;
}
sub _read_line ($) {
while (1) {
my $l = readline( \*STDIN );
$l =~ s/^\s+|\s+$//g;
return $l if $l;
}
}
sub _filter_chores_and_maybe_warn_user ( $self, $stories ) {
my ( $chore_stories, $non_chore_stories )
= part { $_->story_type eq 'chore' ? 0 : 1 } $stories->@*;
say 'Note: '
. ( scalar $chore_stories->@* )
. PL( ' chore', scalar $chore_stories->@* )
. PL_V( ' is', scalar $chore_stories->@* )
. ' not shown here (chores by definition do not require review).'
if $chore_stories;
return $non_chore_stories // [];
}
sub _confirm_story ( $self, $text ) {
my $result = $self->_choose(
[ 'Accept', 'Edit' ],
{ prompt => $text, clear_screen => $ENV{'SUBMIT_WORK_CLEAR'} // 0 }
);
return $text if $result eq 'Accept';
my $fh = solicit($text);
return do { local $/ = undef; <$fh> };
}
sub _text_for_story ( $self, $story, $reviewer ) {
join "\n\n",
$story->name,
$story->url,
( $story->description ? $story->description : () ),
(
$self->_include_requester_name_in_pr
? 'Reviewer: ' . $reviewer
: ()
),
;
}
sub _create_pull_request ( $self, $text ) {
if ( $self->dry_run ) {
print $text;
exit;
}
my ( $title, $body ) = split /\n\n/, $text, 2;
my $res = $self->_github_api->pull_requests->create(
data => {
base => $self->base,
body => $body,
head => $self->_git_current_branch,
title => $title,
},
);
unless ( $res->success ) {
die "Error while creating pull request:\n\n"
. _format_github_error($res) . "\n";
}
return $res->content->{html_url};
}
sub _format_github_error ($res) {
my $content = $res->content;
if ( my $msg = $content->{message} ) {
( run in 1.922 second using v1.01-cache-2.11-cpan-d7a12ab2c7f )