Skip to content

Commit 7581a6e

Browse files
authored
fix: always skip pre-existing policy failures and clean up API surface (#3)
Remove the OnlyAppliesToNewResources guard from the pre-existing failure skip logic so PR comments never show issues that existed before the PR. Move isGithub into Data as IsGithubApp and switch tagging/guardrail slices from pointers to values since go-proto returns them as non-pointers.
1 parent 06227fe commit 7581a6e

8 files changed

Lines changed: 108 additions & 34 deletions

File tree

pkg/vcs/comment/data.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ type Data struct {
2323
// Available from runner's RunMetadata.UsageAPIEnabled.
2424
UsageAPIEnabled bool
2525

26+
// IsGithubApp should be true when the repository is Github and the Github app (eg. the runner) is installed
27+
// for this repository. It makes the comment include suggestions about using @infracost help which only works
28+
// with the app in Github.
29+
IsGithubApp bool
30+
2631
// OrgSlug is the organization slug, used to build links to Infracost Cloud settings.
2732
OrgSlug string
2833

@@ -79,12 +84,12 @@ type Data struct {
7984

8085
// TaggingPolicyResults contains the aggregated tagging evaluation results across
8186
// all projects.
82-
TaggingPolicyResults []*event.TaggingPolicyResult
83-
PreviousTaggingPolicyResults []*event.TaggingPolicyResult
87+
TaggingPolicyResults []event.TaggingPolicyResult
88+
PreviousTaggingPolicyResults []event.TaggingPolicyResult
8489

8590
// GuardrailResults contains the aggregated guardrail results.
86-
GuardrailResults []*event.GuardrailResult
87-
PreviousGuardrailResults []*event.GuardrailResult
91+
GuardrailResults []event.GuardrailResult
92+
PreviousGuardrailResults []event.GuardrailResult
8893
}
8994

9095
// ResourceSummary contains aggregated resource counts across all projects.

pkg/vcs/comment/failure_index.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ type taggingFailureIndex struct {
4949
previous map[string]map[string]bool
5050
}
5151

52-
func buildTaggingFailureIndex(currentResults []*event.TaggingPolicyResult, previousResults []*event.TaggingPolicyResult) taggingFailureIndex {
52+
func buildTaggingFailureIndex(currentResults []event.TaggingPolicyResult, previousResults []event.TaggingPolicyResult) taggingFailureIndex {
5353
idx := taggingFailureIndex{
5454
current: make(map[string]map[string]bool),
5555
previous: make(map[string]map[string]bool),
@@ -72,4 +72,4 @@ func buildTaggingFailureIndex(currentResults []*event.TaggingPolicyResult, previ
7272
}
7373

7474
return idx
75-
}
75+
}

pkg/vcs/comment/fixed_issues.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ func finopsFixedIssueCounts(previousResults []*provider.FinopsPolicyResult, inde
7777

7878
// taggingFixedIssueCounts computes fixed issue counts for tagging policies.
7979
// A fixed issue is an address that was failing previously but is no longer failing.
80-
func taggingFixedIssueCounts(results []*event.TaggingPolicyResult, index taggingFailureIndex) []FixedIssueCount {
80+
func taggingFixedIssueCounts(results []event.TaggingPolicyResult, index taggingFailureIndex) []FixedIssueCount {
8181
var counts []FixedIssueCount
8282

8383
for _, result := range results {
@@ -107,4 +107,4 @@ func taggingFixedIssueCounts(results []*event.TaggingPolicyResult, index tagging
107107
}
108108

109109
return counts
110-
}
110+
}

pkg/vcs/comment/governance.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ const (
1515
GovernanceResourceLimit = 5
1616
)
1717

18-
func (data *Data) processGovernance(inputs *Inputs, isGithub bool, finopsIndex, securityIndex policyFailureIndex, taggingIndex taggingFailureIndex) bool {
18+
func (data *Data) processGovernance(inputs *Inputs, finopsIndex, securityIndex policyFailureIndex, taggingIndex taggingFailureIndex) bool {
1919
data.processPolicyResults(inputs, "FinOps policies", data.FinOpsPolicyResults, finopsIndex)
2020
data.processPolicyResults(inputs, "Cloud security policies", data.SecurityPolicyResults, securityIndex)
2121
data.processTaggingPolicyResults(inputs, taggingIndex)
@@ -39,7 +39,7 @@ func (data *Data) processGovernance(inputs *Inputs, isGithub bool, finopsIndex,
3939
}
4040
}
4141

42-
inputs.GovernanceSentence = formatGovernanceSentence(totalIssues, hasGuardrail, isGithub)
42+
inputs.GovernanceSentence = formatGovernanceSentence(totalIssues, hasGuardrail, data.IsGithubApp)
4343
return hasGuardrail
4444
}
4545

@@ -74,7 +74,7 @@ func (data *Data) processPolicyResults(inputs *Inputs, title string, results []*
7474

7575
for _, resource := range result.FailingResources {
7676
// Skip resources that were already failing before this PR.
77-
if result.OnlyAppliesToNewResources && prevFailing[resource.Id] {
77+
if prevFailing[resource.Id] {
7878
continue
7979
}
8080

@@ -252,7 +252,7 @@ func (data *Data) processGuardrailResults(inputs *Inputs) {
252252

253253
// formatGovernanceSentence returns a summary line about policy alignment.
254254
// See: dashboard/api/src/services/templates/partials/governanceOutputs.ts formatGovernanceSentence (~line 214)
255-
func formatGovernanceSentence(totalIssuesCount int, hasGuardrail bool, isGithub bool) string {
255+
func formatGovernanceSentence(totalIssuesCount int, hasGuardrail bool, isGithubApp bool) string {
256256
if totalIssuesCount == 0 {
257257
if hasGuardrail {
258258
return ""
@@ -265,7 +265,7 @@ func formatGovernanceSentence(totalIssuesCount int, hasGuardrail bool, isGithub
265265
fixing = `Consider fixing this issue, it doesn't align with your company's FinOps policies & the Well-Architected Framework.`
266266
}
267267

268-
if isGithub {
268+
if isGithubApp {
269269
return fmt.Sprintf("<p>%s <b>Add a PR comment with <code>@infracost help</code> to see how you can dismiss or snooze issues and unblock your PR.</b></p>", fixing)
270270
}
271271

@@ -439,7 +439,7 @@ func formatTagIssues(resource event.TagPolicyResultResource) []string {
439439
}
440440

441441
// formatGuardrailMessage builds the trigger description for a guardrail result.
442-
func formatGuardrailMessage(result *event.GuardrailResult) string {
442+
func formatGuardrailMessage(result event.GuardrailResult) string {
443443
var parts []string
444444

445445
if result.Increase != nil && result.Increase.GreaterThanZero() {

pkg/vcs/comment/template.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,15 @@ const truncationBuffer = 1000
3030
// Render executes the given template against the provided data and
3131
// returns the rendered string, truncating cost details if necessary
3232
// to fit within maxCommentSize.
33-
func Render(tmpl *template.Template, isGithub bool, maxCommentSize int, data Data) (string, error) {
33+
func Render(tmpl *template.Template, maxCommentSize int, data Data) (string, error) {
3434
inputs := new(Inputs)
3535
data.processProjectErrors(inputs)
3636

3737
finopsIndex := buildPolicyFailureIndex(data.FinOpsPolicyResults, data.PreviousFinOpsPolicyResults)
3838
securityIndex := buildPolicyFailureIndex(data.SecurityPolicyResults, data.PreviousSecurityPolicyResults)
3939
taggingIndex := buildTaggingFailureIndex(data.TaggingPolicyResults, data.PreviousTaggingPolicyResults)
4040

41-
hasGuardrail := data.processGovernance(inputs, isGithub, finopsIndex, securityIndex, taggingIndex)
41+
hasGuardrail := data.processGovernance(inputs, finopsIndex, securityIndex, taggingIndex)
4242
data.processFixedIssues(inputs, finopsIndex, securityIndex, taggingIndex)
4343
data.processDisplayCosts(inputs, hasGuardrail)
4444
data.processProjectCosts(inputs)
@@ -95,7 +95,7 @@ func truncateMiddleStr(s string, maxLen int) string {
9595
return sep
9696
}
9797
startChars := (charsToShow + 1) / 2 // ceil
98-
backChars := charsToShow / 2 // floor
98+
backChars := charsToShow / 2 // floor
9999
return s[:startChars] + sep + s[len(s)-backChars:]
100100
}
101101

@@ -381,7 +381,6 @@ type CostTableEntry struct {
381381
NewTotalCost string
382382
}
383383

384-
385384
type ProjectError struct {
386385
Name string
387386
Error string

pkg/vcs/comment/template_test.go

Lines changed: 58 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,26 +19,25 @@ var update = flag.Bool("update", false, "update golden files")
1919
func TestRender(t *testing.T) {
2020
tests := []struct {
2121
name string
22-
isGithub bool
2322
maxCommentSize int
2423
data Data
2524
goldenFile string
2625
}{
2726
{
2827
name: "minimal_empty",
29-
isGithub: true,
3028
maxCommentSize: 65000,
3129
data: Data{
30+
IsGithubApp: true,
3231
Currency: "USD",
3332
TotalMonthlyCost: rat.Zero,
3433
},
3534
goldenFile: "minimal_empty.md",
3635
},
3736
{
3837
name: "cost_increase",
39-
isGithub: true,
4038
maxCommentSize: 65000,
4139
data: Data{
40+
IsGithubApp: true,
4241
Currency: "USD",
4342
TotalMonthlyCost: rat.New(250),
4443
PastTotalMonthlyCost: rat.New(200),
@@ -120,9 +119,9 @@ func TestRender(t *testing.T) {
120119
},
121120
{
122121
name: "project_errors",
123-
isGithub: true,
124122
maxCommentSize: 65000,
125123
data: Data{
124+
IsGithubApp: true,
126125
Currency: "USD",
127126
TotalMonthlyCost: rat.Zero,
128127
Projects: []ProjectResult{
@@ -145,9 +144,9 @@ func TestRender(t *testing.T) {
145144
},
146145
{
147146
name: "governance_finops",
148-
isGithub: true,
149147
maxCommentSize: 65000,
150148
data: Data{
149+
IsGithubApp: true,
151150
Currency: "USD",
152151
TotalMonthlyCost: rat.New(100),
153152
RepoURL: "https://github.com/my-org/my-repo",
@@ -178,7 +177,6 @@ func TestRender(t *testing.T) {
178177
},
179178
{
180179
name: "fixed_issues",
181-
isGithub: false,
182180
maxCommentSize: 140000,
183181
data: Data{
184182
Currency: "USD",
@@ -207,9 +205,9 @@ func TestRender(t *testing.T) {
207205
},
208206
{
209207
name: "preexisting_issues",
210-
isGithub: true,
211208
maxCommentSize: 65000,
212209
data: Data{
210+
IsGithubApp: true,
213211
Currency: "USD",
214212
TotalMonthlyCost: rat.New(100),
215213
OrgSlug: "my-org",
@@ -242,15 +240,61 @@ func TestRender(t *testing.T) {
242240
},
243241
goldenFile: "preexisting_issues.md",
244242
},
243+
{
244+
name: "mixed_preexisting_and_new_issues",
245+
maxCommentSize: 65000,
246+
data: Data{
247+
IsGithubApp: true,
248+
Currency: "USD",
249+
TotalMonthlyCost: rat.New(200),
250+
RepoURL: "https://github.com/my-org/my-repo",
251+
CommitSHA: "abc123",
252+
PreviousFinOpsPolicyResults: []*provider.FinopsPolicyResult{
253+
{
254+
PolicySlug: "use-gp3",
255+
PolicyName: "Use GP3 volumes",
256+
IncludeInPullRequestComment: true,
257+
FailingResources: []*provider.FinopsPolicyFailingResource{
258+
{Id: "aws_ebs_volume.old"},
259+
},
260+
},
261+
},
262+
FinOpsPolicyResults: []*provider.FinopsPolicyResult{
263+
{
264+
PolicySlug: "use-gp3",
265+
PolicyName: "Use GP3 volumes",
266+
PolicyMessage: "Use GP3 volumes instead of GP2 for better performance.",
267+
IncludeInPullRequestComment: true,
268+
FailingResources: []*provider.FinopsPolicyFailingResource{
269+
{
270+
Id: "aws_ebs_volume.old",
271+
CauseAddress: "aws_ebs_volume.old",
272+
Issues: []*provider.FinopsResourceIssue{
273+
{Description: "This volume uses GP2, consider upgrading to GP3"},
274+
},
275+
},
276+
{
277+
Id: "aws_ebs_volume.new",
278+
CauseAddress: "aws_ebs_volume.new",
279+
Issues: []*provider.FinopsResourceIssue{
280+
{Description: "This volume uses GP2, consider upgrading to GP3"},
281+
},
282+
},
283+
},
284+
},
285+
},
286+
},
287+
goldenFile: "mixed_preexisting_and_new_issues.md",
288+
},
245289
{
246290
name: "guardrail_blocks",
247-
isGithub: true,
248291
maxCommentSize: 65000,
249292
data: Data{
293+
IsGithubApp: true,
250294
Currency: "USD",
251295
TotalMonthlyCost: rat.New(500),
252296
PastTotalMonthlyCost: rat.New(100),
253-
GuardrailResults: []*event.GuardrailResult{
297+
GuardrailResults: []event.GuardrailResult{
254298
{
255299
GuardrailID: "guardrail-1",
256300
GuardrailName: "Cost increase > $100",
@@ -299,9 +343,9 @@ func TestRender(t *testing.T) {
299343
},
300344
{
301345
name: "environmental_metrics",
302-
isGithub: true,
303346
maxCommentSize: 65000,
304347
data: Data{
348+
IsGithubApp: true,
305349
EnableEnvironmentalMetrics: true,
306350
Currency: "USD",
307351
TotalMonthlyCost: rat.New(300),
@@ -365,14 +409,14 @@ func TestRender(t *testing.T) {
365409
},
366410
{
367411
name: "governance_tagging",
368-
isGithub: true,
369412
maxCommentSize: 65000,
370413
data: Data{
414+
IsGithubApp: true,
371415
Currency: "USD",
372416
TotalMonthlyCost: rat.New(100),
373417
RepoURL: "https://github.com/my-org/my-repo",
374418
CommitSHA: "abc123",
375-
TaggingPolicyResults: []*event.TaggingPolicyResult{
419+
TaggingPolicyResults: []event.TaggingPolicyResult{
376420
{
377421
Name: "Require env tag",
378422
TagPolicyID: "tp-1",
@@ -404,7 +448,6 @@ func TestRender(t *testing.T) {
404448
},
405449
{
406450
name: "rich_cost_details",
407-
isGithub: false,
408451
maxCommentSize: 65000,
409452
data: Data{
410453
Currency: "EUR",
@@ -542,9 +585,9 @@ func TestRender(t *testing.T) {
542585
},
543586
{
544587
name: "truncated_cost_details",
545-
isGithub: true,
546588
maxCommentSize: 2800,
547589
data: Data{
590+
IsGithubApp: true,
548591
Currency: "USD",
549592
TotalMonthlyCost: rat.New(1000),
550593
PastTotalMonthlyCost: rat.New(500),
@@ -625,7 +668,7 @@ func TestRender(t *testing.T) {
625668

626669
for _, tt := range tests {
627670
t.Run(tt.name, func(t *testing.T) {
628-
got, err := Render(DefaultTemplate, tt.isGithub, tt.maxCommentSize, tt.data)
671+
got, err := Render(DefaultTemplate, tt.maxCommentSize, tt.data)
629672
if err != nil {
630673
t.Fatalf("Render() error: %v", err)
631674
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<h3>💰 Infracost report</h3>
2+
<p>Consider fixing this issue, it doesn't align with your company's FinOps policies & the Well-Architected Framework. <b>Add a PR comment with <code>@infracost help</code> to see how you can dismiss or snooze issues and unblock your PR.</b></p>
3+
4+
<table>
5+
<tr><td colspan="2" width="1000px">FinOps policies</td></tr>
6+
7+
<tr><td colspan="2" title="Failure">
8+
<details >
9+
<summary>
10+
<b>🔴 Use GP3 volumes</b>
11+
</summary><br/>
12+
13+
Use GP3 volumes instead of GP2 for better performance.
14+
</details>
15+
</td></tr>
16+
<tr><td></td><td>
17+
18+
`aws_ebs_volume.new`
19+
* This volume uses GP2, consider upgrading to GP3
20+
</td></tr>
21+
22+
</table>
23+
24+
<sub>
25+
This comment will be updated when code changes.
26+
</sub>
27+

pkg/vcs/github/github.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ func New(ctx context.Context, owner, repo, token string, prNumber int32, opts Op
7474

7575
// GenerateComment renders a PR comment from the given data.
7676
func (g *GitHub) GenerateComment(data comment.Data) (string, error) {
77-
return comment.Render(g.tmpl, true, 65000, data)
77+
return comment.Render(g.tmpl, 65000, data)
7878
}
7979

8080
// PostComment publishes a comment to the pull request using the given behavior.

0 commit comments

Comments
 (0)