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 )