File Coverage

File:lib/CheckSpelling/SummaryTables.pm
Coverage:84.2%

linestmtbrancondsubtimecode
1#! -*-perl-*-
2package CheckSpelling::SummaryTables;
3
4
1
1
1
102838
1
2
use utf8;
5
1
1
1
15
0
18
use Cwd 'abs_path';
6
1
1
1
5
1
23
use File::Basename;
7
1
1
1
3
2
23
use File::Temp qw/ tempfile tempdir /;
8
1
1
1
1
0
33
use JSON::PP;
9
1
1
1
160
1
17
use CheckSpelling::Util;
10
1
1
1
174
1
533
use CheckSpelling::GitSources;
11
12
1
1
1
2
1
16
unless (eval 'use URI::Escape; 1') {
13    eval 'use URI::Escape::XS qw/uri_escape/';
14}
15
16my $pull_base;
17my $pull_head;
18
19sub file_ref {
20
2
151
    my ($file, $line) = @_;
21
2
4
    $file =~ s/ /%20/g;
22
2
9
    return "$file:$line";
23}
24
25sub github_blame {
26
18
30786
    my ($file, $line) = @_;
27
18
13
    our (%git_roots, %github_urls, $pull_base, $pull_head);
28
29
18
33
    return file_ref($file, $line) if ($file =~ m{^https?://});
30
31
17
20
    my ($parsed_file, $git_base_dir, $prefix, $remote_url, $rev) = CheckSpelling::GitSources::git_source_and_rev($file);
32
17
15
    return file_ref($file, $line) unless defined $prefix;
33
17
22
    my $line_delimiter = $prefix =~ m<https?://> ? '#L' : ':';
34
35
17
20
    $file = uri_escape($parsed_file, "^A-Za-z0-9\-\._~/");
36
17
166
    return "$prefix$file$line_delimiter$line";
37}
38
39sub main {
40
4
8153
    binmode(STDOUT, ":encoding(UTF-8)");
41
4
59
    my $budget = CheckSpelling::Util::get_val_from_env("summary_budget", "");
42
4
66
    print STDERR "Summary Tables budget: $budget\n";
43
4
6
    my $summary_tables = tempdir();
44
4
470
    my $table;
45    my @tables;
46
47
4
6
    my $head_ref = CheckSpelling::Util::get_file_from_env('GITHUB_HEAD_REF', "");
48
4
3
    my $github_url = CheckSpelling::Util::get_file_from_env('GITHUB_SERVER_URL', "");
49
4
3
    my $github_repository = CheckSpelling::Util::get_file_from_env('GITHUB_REPOSITORY', "");
50
4
2
    my $event_file_path = CheckSpelling::Util::get_file_from_env('GITHUB_EVENT_PATH', "");
51
4
21
    if ($head_ref && $github_url && $github_repository && $event_file_path) {
52
4
44
        if (open $event_file_handle, '<', $event_file_path) {
53
4
7
            local $/;
54
4
35
            my $json = <$event_file_handle>;
55
4
17
            close $event_file_handle;
56
4
8
            my $data = decode_json($json);
57
4
1305
            our $pull_base = "$github_url/$github_repository";
58
4
5
            our $pull_head = "$github_url/".$data->{'pull_request'}->{'head'}->{'repo'}->{'full_name'};
59
4
13
            unless ($pull_head && $pull_base && ($pull_base ne $pull_head)) {
60
2
4
                $pull_base = $pull_head = '';
61            }
62        }
63    }
64
65
4
14
    while (<>) {
66
18
88
        next unless m{^(.+):(\d+):(\d+) \.\.\. (\d+),\s(Error|Warning|Notice)\s-\s(.+)\s\(([-a-z]+)\)$};
67
15
30
        my ($file, $line, $column, $endColumn, $severity, $message, $code) = ($1, $2, $3, $4, $5, $6, $7);
68
15
8
        my $table_file = "$summary_tables/$code";
69
15
77
        push @tables, $code unless -e $table_file;
70
15
231
        open $table, ">>", $table_file;
71
15
15
        $message =~ s/\|/\\|/g;
72
15
14
        my $blame = CheckSpelling::SummaryTables::github_blame($file, $line);
73
15
29
        print $table "$message | $blame\n";
74
15
165
        close $table;
75    }
76
4
6
    return unless @tables;
77
78
3
3
    my ($details_prefix, $footer, $suffix, $need_suffix) = (
79        "<details><summary>Details 🔎</summary>\n\n",
80        "</details>\n\n",
81        "\n</details>\n\n",
82        0
83    );
84
3
1
    my $footer_length = length $footer;
85
3
3
    if ($budget) {
86
3
6
        $budget -= (length $details_prefix) + (length $suffix);
87
3
21
        print STDERR "Summary Tables budget reduced to: $budget\n";
88    }
89
3
5
    for $table_file (sort @tables) {
90
9
4
        my $header = "<details><summary>📂 $table_file</summary>\n\n".
91            "note|path\n".
92            "-|-\n";
93
9
9
        my $header_length = length $header;
94
9
5
        my $file_path = "$summary_tables/$table_file";
95
9
25
        my $cost = $header_length + $footer_length + -s $file_path;
96
9
22
        if ($budget && ($budget < $cost)) {
97
7
20
            print STDERR "::warning title=summary-table::Details for '$table_file' too big to include in Step Summary (summary-table-skipped)\n";
98
7
6
            next;
99        }
100
2
15
        open $table, "<:encoding(UTF-8)", $file_path;
101
2
39
        my @entries;
102
2
1
        my $real_cost = $header_length + $footer_length;
103
2
14
        foreach my $line (<$table>) {
104
2
12
            $real_cost += length $line;
105
2
2
            push @entries, $line;
106        }
107
2
6
        close $table;
108
2
2
        if ($real_cost > $cost) {
109
0
0
            print STDERR "budget ($real_cost > $cost)\n";
110
0
0
            if ($budget && ($budget < $real_cost)) {
111
0
0
                print STDERR "::warning title=summary-tables::budget exceeded for $table_file (summary-table-skipped)\n";
112
0
0
                next;
113            }
114        }
115
2
2
        if ($details_prefix ne '') {
116
1
12
            print $details_prefix;
117
1
1
            $details_prefix = '';
118
1
1
            $need_suffix = 1;
119        }
120
2
7
        print $header;
121
2
12
        print join ("", sort CheckSpelling::Util::case_biased @entries);
122
2
6
        print $footer;
123
2
1
        if ($budget) {
124
2
2
            $budget -= $cost;
125
2
6
            print STDERR "Summary Tables budget reduced to: $budget\n";
126        }
127    }
128
3
16
    print $suffix if $need_suffix;
129}
130
1311;