App-GHGen

 view release on metacpan or  search on metacpan

lib/App/GHGen/Reporter.pm  view on Meta::CPAN

        $comment .= "    create-pr: true\n";
        $comment .= "```\n\n";
    }

    # Add savings estimate
    my $savings = estimate_savings($issues);
    if ($savings->{minutes} > 0) {
        $comment .= "### 💰 Potential Savings\n\n";
        $comment .= "By fixing these issues:\n";
        $comment .= "- ⏱️ Save **~$savings->{minutes} CI minutes/month**\n";

        if ($savings->{cost} > 0) {
            $comment .= "- 💵 Save **~\$$savings->{cost}/month** (private repos)\n";
        }

        $comment .= "\n";
    }

    $comment .= "---\n";
    $comment .= "*Analysis by [GHGen](https://github.com/your-org/ghgen)*\n";

    return $comment;
}

=head2 estimate_savings($issues)

Estimate CI-minute savings and associated cost reduction from fixing a set of issues.

=head3 Purpose

For each C<performance> (caching) or C<cost> (concurrency, triggers) issue,
add a fixed minute estimate to the running total and compute the equivalent
USD saving at the GitHub private-repo rate of $0.008/minute.

=head3 Arguments

=over 4

=item C<$issues> (ArrayRef[HashRef], required)

Issue hashes with at least C<type> and C<message>.

=back

=head3 Returns

A hash reference:

    {
        minutes => Int,   # total estimated minutes saved per month; 0 when no savings
        cost    => Int,   # floor(minutes * 0.008); 0 when no savings
    }

=head3 Side Effects

None.  Pure function.

=head3 Usage Example

    my $s = estimate_savings(\@issues);
    say "Save $s->{minutes} min/month";

=head3 API SPECIFICATION

=head4 Input

    { issues => { type => 'arrayref', required => 1 } }

=head4 Output

    {
        type => 'hashref',
        keys => {
            minutes => { type => 'scalar' },
            cost    => { type => 'scalar' },
        },
    }

=head3 FORMAL SPECIFICATION

    estimate_savings : seq Issue → { minutes: ℕ, cost: ℕ }

    RATE ≔ 0.008
    savings(i) ≔
        i.type = performance ∧ i.message =~ /caching/     → 500
        i.type = cost        ∧ i.message =~ /concurrency/ → 50
        i.type = cost        ∧ i.message =~ /triggers/    → 100
        otherwise                                          → 0

    total  ≔ ∑ { savings(i) ∣ i ∈ issues }
    result ≔ { minutes ↦ total, cost ↦ floor(total × RATE) }

=cut

sub estimate_savings($issues) {
	my %savings = (
		minutes => 0,
		cost => 0,
	);

	for my $issue (@$issues) {
	# Estimate savings by issue type
		if ($issue->{type} eq 'performance') {
			# Caching saves ~5 minutes per workflow run
			# Assume 100 runs/month
			$savings{minutes} += 500 if $issue->{message} =~ /caching/;
		} elsif ($issue->{type} eq 'cost') {
			if ($issue->{message} =~ /concurrency/) {
				# Concurrency saves ~50 minutes/month by canceling old runs
				$savings{minutes} += 50;
			}
			if ($issue->{message} =~ /triggers/) {
				# Trigger filters save ~100 minutes/month
				$savings{minutes} += 100;
			}
		}
	}

	# Private repo pricing: ~$0.008 per minute
	$savings{cost} = int($savings{minutes} * 0.008);



( run in 1.278 second using v1.01-cache-2.11-cpan-9169edd2b0e )