Devel-Cover-Report-Coveralls

 view release on metacpan or  search on metacpan

CHANGELOG.md  view on Meta::CPAN


## [1.0.0] - 2026-11-07

- Update the usage examples for GitHub Actions (with parallel builds), Travis CI and the other CI services, and document `COVERALLS_NO_FAIL` and `COVERALLS_PERL_SERVICE_NAME` (#58)
- Do not wrap the commit ID, names, e-mail addresses and message from `git log` in single quotes on Windows (#54)
- **Breaking:** `cover -report coveralls` exits with a non-zero status when the upload to Coveralls fails, so the CI job fails. Set `COVERALLS_NO_FAIL=1` to exit 0, which also covers a missing repo token, e.g. on pull requests from forks (#56)
- **Breaking:** On GitHub Actions, report as `github` with the values of the official coverage-reporter: the build number is the run (`GITHUB_RUN_ID`) instead of the commit, pull requests send their number and source branch instead of `<number>/merge...
- **Breaking:** Travis CI also needs `COVERALLS_REPO_TOKEN` (or `repo_token` in `.coveralls.yml`), as Coveralls requires it there too (#53)
- Support GitLab CI, read `BUILD_NUMBER` on Jenkins, treat `COVERALLS_PARALLEL=false` and `0` as off, and send the branch, commit and pull request that CircleCI, Jenkins and Azure Pipelines give (#53)
- Require Perl 5.20 or later, the version the current Devel::Cover needs (#57)
- Do not send credentials in git remote URLs, and send each remote once with its fetch URL (#51)

## [0.32.1] - 2023-05-19

- Fix test (thanks chylli-deriv)

## [0.32] - 2023-04-25

- Decode git log output (thanks voegelas)
- Some bug fix (thanks voegelas)

lib/Devel/Cover/Report/Coveralls.pm  view on Meta::CPAN

    my $command = "git log -1 --encoding=UTF-8 --pretty=format:$format";
    if (open(my $fh, '-|:encoding(UTF-8)', $command)) {
        $text = do { local $/; <$fh> };
        close $fh;
    }

    return $text;
}

# Turn the output of `git remote -v` into the list of remotes to send.
# Each remote is listed once (its fetch URL), and credentials are removed
# from the URL.
sub parse_git_remotes {
    my $text = shift;

    my @remotes;
    for my $line (split /\n/, defined $text ? $text : '') {
        # "<name>\t<url> (fetch)". The URL may contain spaces (a local path).
        next unless $line =~ /^(\S+)\t(.*) \(fetch\)\r?$/;
        push @remotes, +{ name => $1, url => strip_url_credentials($2) };
    }

    return \@remotes;
}

# Remove the user information from a URL with a scheme
# (https://user:pass@host/path -> https://host/path).
# scp-like URLs (git@host:path) cannot hold a password and are kept as they are.
sub strip_url_credentials {
    my $url = shift;
    $url =~ s!^([A-Za-z][A-Za-z0-9+.-]*://)[^/?#]*@!$1!;
    return $url;
}

sub get_git_info {
    my $git = {
        head => {
            id              => git_log('%H'),
            author_name     => git_log('%aN'),

t/01_basic.t  view on Meta::CPAN

    like $out, qr/^error: /m, 'still prints the error';

    local $ENV{COVERALLS_NO_FAIL} = 'false';
    $response = { success => 1, status => 200, reason => 'OK',
        content => '{"url":"https://coveralls.io/jobs/1"}' };
    ($ok, $err, $out) = $run->();
    ok $ok, 'returns on success';
    like $out, qr{^register: https://coveralls.io/jobs/1$}m, 'prints the job';
};

subtest 'strip_url_credentials' => sub {
    my %cases = (
        'https://gitlab-ci-token:SECRET@gitlab.example.com/x/y.git' => 'https://gitlab.example.com/x/y.git',
        'https://user@github.com/x/y.git'                         => 'https://github.com/x/y.git',
        'https://user:p@ss@github.com/x/y.git'                    => 'https://github.com/x/y.git',
        'ssh://git@github.com/x/y.git'                            => 'ssh://github.com/x/y.git',
        'https://github.com/x/y.git'                              => 'https://github.com/x/y.git',
        'https://github.com/x/y@z.git'                            => 'https://github.com/x/y@z.git',
        'https://github.com?x=a@b'                                => 'https://github.com?x=a@b',
        'git@github.com:x/y.git'                                  => 'git@github.com:x/y.git',
        '/path/to/repo.git'                                       => '/path/to/repo.git',
    );
    for my $url (sort keys %cases) {
        is Devel::Cover::Report::Coveralls::strip_url_credentials($url), $cases{$url}, $url;
    }
};

subtest 'parse_git_remotes' => sub {
    my $text = join '', map { "$_\n" }
        "origin\thttps://gitlab-ci-token:SECRET\@gitlab.example.com/x/y.git (fetch)",
        "origin\thttps://gitlab-ci-token:SECRET\@gitlab.example.com/x/y.git (push)",
        "upstream\tgit\@github.com:x/y.git (fetch)",
        "upstream\tgit\@github.com:x/y.git (push)",
        "local\tC:/Users/John Doe/repo (fetch)\r",
        "local\tC:/Users/John Doe/repo (push)\r";

    is_deeply Devel::Cover::Report::Coveralls::parse_git_remotes($text), [
        { name => 'origin',   url => 'https://gitlab.example.com/x/y.git' },
        { name => 'upstream', url => 'git@github.com:x/y.git' },
        { name => 'local',    url => 'C:/Users/John Doe/repo' },
    ], 'one fetch URL per remote, without credentials';

    is_deeply Devel::Cover::Report::Coveralls::parse_git_remotes(''), [], 'no remotes';
    is_deeply Devel::Cover::Report::Coveralls::parse_git_remotes(undef), [], 'git not available';
};

subtest 'get_git_info remotes' => sub {
    my $inside = `git rev-parse --is-inside-work-tree 2>&1`;
    plan skip_all => 'not in a git work tree' unless defined $inside && $inside =~ /^true/;

    my @names = split /\n/, `git remote`;



( run in 2.314 seconds using v1.01-cache-2.11-cpan-3f962061493 )