Skip to content

Commit

Permalink
Custom Commit Status URL (#30)
Browse files Browse the repository at this point in the history
* return err as second value

* Make executeTemplate more general by extracting template path join

* Generate target url with dynamic value (CommitTime)

* Inject /etc/telefonistka-gh-app-config/ for mirrord

This will be used for CUSTOM_COMMIT_STATUS_URL_TEMPLATE_PATH

* Add doc

* Add test

* Fix lint issues

* Make targetURL const

* Change the level to Debug

See: #30 (comment)
  • Loading branch information
yzdann authored Oct 23, 2024
1 parent caaf793 commit a43d3d1
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 13 deletions.
6 changes: 6 additions & 0 deletions docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,12 @@ Environment variables for the webhook process:

`TEMPLATES_PATH` Telefonistka uses Go templates to format GitHub PR comments, the variable override the default templates path("templates/"), useful for environments where the container workdir is overridden(like GitHub Actions) or when custom templates are desired.

`CUSTOM_COMMIT_STATUS_URL_TEMPLATE_PATH` allows you to set a custom [commit status](https://docs.github.com/en/rest/commits/statuses?apiVersion=2022-11-28#about-commit-statuses) target URL using Go templates. The commit time will be passed as a dynamic parameter to the template. Here is an example:

```console
https://custom-url.com?time={{.CommitTime}}
```

`ARGOCD_SERVER_ADDR` Hostname and port of the ArgoCD API endpoint, like `argocd-server.argocd.svc.cluster.local:443`, default is `localhost:8080"`

`ARGOCD_TOKEN` ArgoCD authentication token.
Expand Down
55 changes: 44 additions & 11 deletions internal/pkg/githubapi/github.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"net/http"
"os"
"path"
"path/filepath"
"regexp"
"sort"
"strings"
Expand Down Expand Up @@ -238,7 +239,7 @@ func HandlePREvent(eventPayload *github.PullRequestEvent, ghPrClientDetails GhPr
}

func generateArgoCdDiffComments(diffCommentData DiffCommentData, githubCommentMaxSize int) (comments []string, err error) {
err, templateOutput := executeTemplate("argoCdDiff", "argoCD-diff-pr-comment.gotmpl", diffCommentData)
templateOutput, err := executeTemplate("argoCdDiff", defaultTemplatesFullPath("argoCD-diff-pr-comment.gotmpl"), diffCommentData)
if err != nil {
return nil, fmt.Errorf("failed to generate ArgoCD diff comment template: %w", err)
}
Expand All @@ -255,7 +256,7 @@ func generateArgoCdDiffComments(diffCommentData DiffCommentData, githubCommentMa
componentTemplateData := diffCommentData
componentTemplateData.DiffOfChangedComponents = []argocd.DiffResult{singleComponentDiff}
componentTemplateData.Header = fmt.Sprintf("Component %d/%d: %s (Split for comment size)", i+1, totalComponents, singleComponentDiff.ComponentPath)
err, templateOutput := executeTemplate("argoCdDiff", "argoCD-diff-pr-comment.gotmpl", componentTemplateData)
templateOutput, err := executeTemplate("argoCdDiff", defaultTemplatesFullPath("argoCD-diff-pr-comment.gotmpl"), componentTemplateData)
if err != nil {
return nil, fmt.Errorf("failed to generate ArgoCD diff comment template: %w", err)
}
Expand All @@ -268,7 +269,7 @@ func generateArgoCdDiffComments(diffCommentData DiffCommentData, githubCommentMa
}

// now we don't have much choice, this is the saddest path, we'll use the concise template
err, templateOutput = executeTemplate("argoCdDiffConcise", "argoCD-diff-pr-comment-concise.gotmpl", componentTemplateData)
templateOutput, err = executeTemplate("argoCdDiffConcise", defaultTemplatesFullPath("argoCD-diff-pr-comment-concise.gotmpl"), componentTemplateData)
if err != nil {
return comments, fmt.Errorf("failed to generate ArgoCD diff comment template: %w", err)
}
Expand Down Expand Up @@ -497,25 +498,29 @@ func handleCommentPrEvent(ghPrClientDetails GhPrClientDetails, ce *github.IssueC
}

func commentPlanInPR(ghPrClientDetails GhPrClientDetails, promotions map[string]PromotionInstance) {
err, templateOutput := executeTemplate("dryRunMsg", "dry-run-pr-comment.gotmpl", promotions)
templateOutput, err := executeTemplate("dryRunMsg", defaultTemplatesFullPath("dry-run-pr-comment.gotmpl"), promotions)
if err != nil {
ghPrClientDetails.PrLogger.Errorf("Failed to generate dry-run comment template: err=%s\n", err)
return
}
_ = commentPR(ghPrClientDetails, templateOutput)
}

func executeTemplate(templateName string, templateFile string, data interface{}) (error, string) {
func executeTemplate(templateName string, templateFile string, data interface{}) (string, error) {
var templateOutput bytes.Buffer
messageTemplate, err := template.New(templateName).ParseFiles(getEnv("TEMPLATES_PATH", "templates/") + templateFile)
messageTemplate, err := template.New(templateName).ParseFiles(templateFile)
if err != nil {
return fmt.Errorf("failed to parse template: %w", err), ""
return "", fmt.Errorf("failed to parse template: %w", err)
}
err = messageTemplate.ExecuteTemplate(&templateOutput, templateName, data)
if err != nil {
return fmt.Errorf("failed to execute template: %w", err), ""
return "", fmt.Errorf("failed to execute template: %w", err)
}
return nil, templateOutput.String()
return templateOutput.String(), nil
}

func defaultTemplatesFullPath(templateFile string) string {
return filepath.Join(getEnv("TEMPLATES_PATH", "templates/") + templateFile)
}

func commentPR(ghPrClientDetails GhPrClientDetails, commentBody string) error {
Expand Down Expand Up @@ -644,7 +649,7 @@ func handleMergedPrEvent(ghPrClientDetails GhPrClientDetails, prApproverGithubCl
templateData := map[string]interface{}{
"prNumber": *pull.Number,
}
err, templateOutput := executeTemplate("autoMerge", "auto-merge-comment.gotmpl", templateData)
templateOutput, err := executeTemplate("autoMerge", defaultTemplatesFullPath("auto-merge-comment.gotmpl"), templateData)
if err != nil {
return err
}
Expand Down Expand Up @@ -817,7 +822,7 @@ func SetCommitStatus(ghPrClientDetails GhPrClientDetails, state string) {
context := "telefonistka"
avatarURL := "https://avatars.githubusercontent.com/u/1616153?s=64"
description := "Telefonistka GitOps Bot"
targetURL := "https://github.com/wayfair-incubator/telefonistka"
targetURL := commitStatusTargetURL(time.Now())

commitStatus := &github.RepoStatus{
TargetURL: &targetURL,
Expand Down Expand Up @@ -1211,3 +1216,31 @@ func GetFileContent(ghPrClientDetails GhPrClientDetails, branch string, filePath
}
return fileContentString, resp.StatusCode, nil
}

// commitStatusTargetURL generates a target URL based on an optional
// template file specified by the environment variable CUSTOM_COMMIT_STATUS_URL_TEMPLATE_PATH.
// If the template file is not found or an error occurs during template execution,
// it returns a default URL.
// passed parameter commitTime can be used in the template as .CommitTime
func commitStatusTargetURL(commitTime time.Time) string {
const targetURL string = "https://github.com/wayfair-incubator/telefonistka"

tmplFile := os.Getenv("CUSTOM_COMMIT_STATUS_URL_TEMPLATE_PATH")
tmplName := filepath.Base(tmplFile)

// dynamic parameters to be used in the template
p := struct {
CommitTime time.Time
}{
CommitTime: commitTime,
}
renderedURL, err := executeTemplate(tmplName, tmplFile, p)
if err != nil {
log.Debugf("Failed to render target URL template: %v", err)
return targetURL
}

// trim any leading/trailing whitespace
renderedURL = strings.TrimSpace(renderedURL)
return renderedURL
}
49 changes: 49 additions & 0 deletions internal/pkg/githubapi/github_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"
"os"
"testing"
"time"

"github.com/stretchr/testify/assert"
)
Expand Down Expand Up @@ -289,3 +290,51 @@ func TestGhPrClientDetailsGetBlameURLPrefix(t *testing.T) {
assert.Equal(t, tc.ExpectURL, blameURLPrefix)
}
}

func TestCommitStatusTargetURL(t *testing.T) {
t.Parallel()

tests := map[string]struct {
expectedURL string
templateFile string
validTemplate bool
}{
"Default URL when no env var is set": {
expectedURL: "https://github.com/wayfair-incubator/telefonistka",
templateFile: "",
validTemplate: false,
},
"Custom URL from template": {
expectedURL: "https://custom-url.com?time=%d&calculated_time=%d",
templateFile: "./testdata/custom_commit_status_valid_template.gotmpl",
validTemplate: true,
},
"Invalid template": {
expectedURL: "https://github.com/wayfair-incubator/telefonistka",
templateFile: "./testdata/custom_commit_status_invalid_template.gotmpl",
validTemplate: false,
},
}

for name, tc := range tests {
t.Run(name, func(t *testing.T) {
t.Parallel()
now := time.Now()

expectedURL := tc.expectedURL
if tc.templateFile != "" {
os.Setenv("CUSTOM_COMMIT_STATUS_URL_TEMPLATE_PATH", tc.templateFile)
defer os.Unsetenv("CUSTOM_COMMIT_STATUS_URL_TEMPLATE_PATH")

if tc.validTemplate {
expectedURL = fmt.Sprintf(expectedURL, now.UnixMilli(), now.Add(-10*time.Minute).UnixMilli())
}
}

result := commitStatusTargetURL(now)
if result != expectedURL {
t.Errorf("%s: Expected URL to be %q, got %q", name, expectedURL, result)
}
})
}
}
2 changes: 1 addition & 1 deletion internal/pkg/githubapi/promotion.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ func DetectDrift(ghPrClientDetails GhPrClientDetails) error {
}
}
if len(diffOutputMap) != 0 {
err, templateOutput := executeTemplate("driftMsg", "drift-pr-comment.gotmpl", diffOutputMap)
templateOutput, err := executeTemplate("driftMsg", defaultTemplatesFullPath("drift-pr-comment.gotmpl"), diffOutputMap)
if err != nil {
return err
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
https://custom-url.com?time={{.InvalidField}}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{{ $calculated_time := .CommitTime.Add -600000000000 }}https://custom-url.com?time={{.CommitTime.UnixMilli}}&calculated_time={{$calculated_time.UnixMilli}}
2 changes: 1 addition & 1 deletion mirrord.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"fs": {
"mode": "read",
"read_write": ".+\\.json" ,
"read_only": [ "^/etc/telefonistka-gh-app-creds/.*" ]
"read_only": [ "^/etc/telefonistka-gh-app-creds/.*", "^/etc/telefonistka-gh-app-config/.*" ]
},
"network": {
"incoming": "steal",
Expand Down

0 comments on commit a43d3d1

Please sign in to comment.