From 9b2e706b0f335b1adb7a37ae2cc43855a9c29532 Mon Sep 17 00:00:00 2001 From: Lukas Matt Date: Sat, 17 Mar 2018 12:51:20 +0100 Subject: [PATCH 1/5] Implement opt-in/out for pull requests --- frontend.go | 6 ++++++ repo.go | 1 + templates/auth.html | 10 ++++++++++ webhook.go | 41 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 58 insertions(+) diff --git a/frontend.go b/frontend.go index 5c694af..316dcd6 100644 --- a/frontend.go +++ b/frontend.go @@ -104,12 +104,18 @@ func resultPage(w http.ResponseWriter, r *http.Request) { return } + var optIn bool + if strings.ToUpper(r.URL.Query().Get("optin")) == "ON" { + optIn = true + } + secret := Secret(16) repo := Repo{ Project: project, Slug: repo, Token: accessToken, Secret: secret, + OptIn: optIn, } name := "web" diff --git a/repo.go b/repo.go index c7c2f85..2e14d0a 100644 --- a/repo.go +++ b/repo.go @@ -27,6 +27,7 @@ type Repo struct { Slug string Token string Secret string + OptIn bool } type Repos []Repo diff --git a/templates/auth.html b/templates/auth.html index ba62653..b15865f 100644 --- a/templates/auth.html +++ b/templates/auth.html @@ -15,6 +15,16 @@ Your project slug is the Github user- and repository name separated by a slash e.g. ganggo/federation +
+ + + + That would mean that tests are triggered if you specify [ci] in the PR title, message + or if the PR was tagged with ci. If you leave the box unchecked + tests will be triggered on every pull-request. However you can skip them by writing [ci skip] in the PR title, + message or if the PR was tagged with ci skip. + +
diff --git a/webhook.go b/webhook.go index a9bec60..9bf351f 100644 --- a/webhook.go +++ b/webhook.go @@ -25,6 +25,7 @@ import ( "encoding/json" "github.com/jinzhu/gorm" _ "github.com/jinzhu/gorm/dialects/sqlite" + "strings" ) func webhook(w http.ResponseWriter, r *http.Request) { @@ -76,6 +77,46 @@ func webhook(w http.ResponseWriter, r *http.Request) { // return //} + var flagExists = false + var buildFlag = "ci skip" + if repo.OptIn { + buildFlag = "ci" + } + + // check PR title and body for [ci] or [ci skip] flag + if pr.Title != nil && pr.Body != nil && + strings.Contains(strings.ToLower(*pr.Title), + fmt.Sprintf("[%s]", buildFlag)) && + strings.Contains(strings.ToLower(*pr.Body), + fmt.Sprintf("[%s]", buildFlag)) { + flagExists = true + } + + if !flagExists { + // check labels for build flag if we haven't already found it + for _, label := range pr.Labels { + if label.Name != nil && strings.Contains( + strings.ToLower(*label.Name), buildFlag) { + flagExists = true + break + } + } + } + + // ignoring pull-request! Repository is set + // to opt-in and no build flag was found + if repo.OptIn && !flagExists { + fmt.Fprintf(w, `{}`) + return + } + + // ignoring pull-request! Repository is set + // to opt-out and a skip flag was found + if !repo.OptIn && flagExists { + fmt.Fprintf(w, `{}`) + return + } + build := Build{ RepoID: repo.ID, Matrix: fmt.Sprintf( From 936695e6a01edee4b6244261d3817c4a0e95fd06 Mon Sep 17 00:00:00 2001 From: Lukas Matt Date: Sat, 17 Mar 2018 16:50:41 +0100 Subject: [PATCH 2/5] Check commit message for opt-in/out as well --- webhook.go | 59 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 42 insertions(+), 17 deletions(-) diff --git a/webhook.go b/webhook.go index 9bf351f..5dc08d5 100644 --- a/webhook.go +++ b/webhook.go @@ -26,6 +26,7 @@ import ( "github.com/jinzhu/gorm" _ "github.com/jinzhu/gorm/dialects/sqlite" "strings" + "context" ) func webhook(w http.ResponseWriter, r *http.Request) { @@ -55,16 +56,18 @@ func webhook(w http.ResponseWriter, r *http.Request) { } // skip all events except for open PRs - if *pr.State != "open" { + if pr.GetState() != "open" { logger.Println("Ignore closed pull request") fmt.Fprintf(w, `{}`) return } var repo Repo - err = db.Where("slug = ?", *pr.Base.Repo.FullName).Find(&repo).Error + err = db.Where("slug = ?", + pr.GetBase().GetRepo().GetFullName(), + ).Find(&repo).Error if err != nil { - logger.Println(err, *pr.Base.Repo.FullName) + logger.Println(err, pr.GetBase().GetRepo().GetFullName()) fmt.Fprintf(w, `{"error":"repo not registered"}`) return } @@ -84,28 +87,47 @@ func webhook(w http.ResponseWriter, r *http.Request) { } // check PR title and body for [ci] or [ci skip] flag - if pr.Title != nil && pr.Body != nil && - strings.Contains(strings.ToLower(*pr.Title), - fmt.Sprintf("[%s]", buildFlag)) && - strings.Contains(strings.ToLower(*pr.Body), + if pr.Title != nil && strings.Contains(strings.ToLower(*pr.Title), fmt.Sprintf("[%s]", buildFlag)) { flagExists = true - } - - if !flagExists { + } else if pr.Body != nil && strings.Contains(strings.ToLower(*pr.Body), + fmt.Sprintf("[%s]", buildFlag)) { + flagExists = true + } else { // check labels for build flag if we haven't already found it for _, label := range pr.Labels { if label.Name != nil && strings.Contains( - strings.ToLower(*label.Name), buildFlag) { + strings.ToLower(label.GetName()), buildFlag) { flagExists = true break } } + + if !flagExists { + // last but not least check the commit message for flags + var prService github.PullRequestsService + commits, _, err := prService.ListCommits(context.Background(), + pr.GetHead().GetUser().GetLogin(), + pr.GetHead().GetRepo().GetName(), + pr.GetNumber(), &github.ListOptions{}) + + if err == nil && len(commits) > 0 { + // check only last commit since older ones are irrelevant + commitMsg := strings.ToLower(commits[0].GetCommit().GetMessage()) + flagExists = strings.Contains( + commitMsg, fmt.Sprintf("[%s]", buildFlag)) + } else { + logger.Printf("Commits is empty or an error occurred: %+v\n", err) + } + } } // ignoring pull-request! Repository is set // to opt-in and no build flag was found if repo.OptIn && !flagExists { + logger.Printf( + "Ignore optin=%t buildFlag=%s flagExists=%t\n", + repo.OptIn, buildFlag, flagExists) fmt.Fprintf(w, `{}`) return } @@ -113,6 +135,9 @@ func webhook(w http.ResponseWriter, r *http.Request) { // ignoring pull-request! Repository is set // to opt-out and a skip flag was found if !repo.OptIn && flagExists { + logger.Printf( + "Ignore optin=%t buildFlag=%s flagExists=%t\n", + repo.OptIn, buildFlag, flagExists) fmt.Fprintf(w, `{}`) return } @@ -122,19 +147,19 @@ func webhook(w http.ResponseWriter, r *http.Request) { Matrix: fmt.Sprintf( `"PROJECT=%s PRREPO=%s PRSHA=%s"`, repo.Project, - *pr.Head.Repo.CloneURL, - *pr.Head.SHA, + pr.GetHead().GetRepo().GetCloneURL(), + pr.GetHead().GetSHA(), ), - PRUser: *pr.Head.User.Login, - PRRepo: *pr.Head.Repo.Name, - PRSha: *pr.Head.SHA, + PRUser: pr.GetHead().GetUser().GetLogin(), + PRRepo: pr.GetHead().GetRepo().GetName(), + PRSha: pr.GetHead().GetSHA(), } + err = db.Create(&build).Error if err != nil { logger.Println(err) fmt.Fprintf(w, `{"error":"database error"}`) return } - fmt.Fprintf(w, `{}`) } From 2cfaa0db6515cb5ce1f6752bd60bc3f6b6eb0646 Mon Sep 17 00:00:00 2001 From: Lukas Matt Date: Sat, 17 Mar 2018 17:32:15 +0100 Subject: [PATCH 3/5] Add custom build triggers to repositories --- frontend.go | 19 +++++++++++++------ repo.go | 2 ++ templates/auth.html | 19 ++++++++++++++++--- webhook.go | 4 ++-- 4 files changed, 33 insertions(+), 11 deletions(-) diff --git a/frontend.go b/frontend.go index 316dcd6..2d77045 100644 --- a/frontend.go +++ b/frontend.go @@ -104,18 +104,25 @@ func resultPage(w http.ResponseWriter, r *http.Request) { return } - var optIn bool - if strings.ToUpper(r.URL.Query().Get("optin")) == "ON" { - optIn = true - } - secret := Secret(16) repo := Repo{ Project: project, Slug: repo, Token: accessToken, Secret: secret, - OptIn: optIn, + } + + // set the repo to opt-in + if strings.ToUpper(r.URL.Query().Get("optin")) == "ON" { + repo.OptIn = true + } + // define custom opt-in flag + if r.URL.Query().Get("optinFlag") != "" { + repo.OptInFlag = r.URL.Query().Get("optinFlag") + } + // define custom opt-out flag + if r.URL.Query().Get("optoutFlag") != "" { + repo.OptOutFlag = r.URL.Query().Get("optoutFlag") } name := "web" diff --git a/repo.go b/repo.go index 2e14d0a..911e73f 100644 --- a/repo.go +++ b/repo.go @@ -28,6 +28,8 @@ type Repo struct { Token string Secret string OptIn bool + OptInFlag string `gorm:"default:'ci'"` + OptOutFlag string `gorm:"default:'ci skip'"` } type Repos []Repo diff --git a/templates/auth.html b/templates/auth.html index b15865f..27daf07 100644 --- a/templates/auth.html +++ b/templates/auth.html @@ -19,11 +19,24 @@ - That would mean that tests are triggered if you specify [ci] in the PR title, message + That would mean that tests are triggered if you specify [ci] in the PR title, body, commit-message or if the PR was tagged with ci. If you leave the box unchecked - tests will be triggered on every pull-request. However you can skip them by writing [ci skip] in the PR title, - message or if the PR was tagged with ci skip. + tests will be triggered on every pull-request. However you can skip them by using [ci skip] + in the mentioned fields or tag it with ci skip. +
+ + + + If you want to use custom build triggers for opt-in you can specify it here. + +
+
+ + + If you want to use custom build triggers for opt-out you can specify it here. + +
diff --git a/webhook.go b/webhook.go index 5dc08d5..e1563d0 100644 --- a/webhook.go +++ b/webhook.go @@ -81,9 +81,9 @@ func webhook(w http.ResponseWriter, r *http.Request) { //} var flagExists = false - var buildFlag = "ci skip" + var buildFlag = repo.OptOutFlag if repo.OptIn { - buildFlag = "ci" + buildFlag = repo.OptInFlag } // check PR title and body for [ci] or [ci skip] flag From 8df848c5c92886a14babccdae0fd78b27ff95be7 Mon Sep 17 00:00:00 2001 From: Lukas Matt Date: Sat, 17 Mar 2018 17:53:12 +0100 Subject: [PATCH 4/5] Fix nil pointer; use github client for listing commits --- webhook.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webhook.go b/webhook.go index e1563d0..ba90d51 100644 --- a/webhook.go +++ b/webhook.go @@ -105,8 +105,8 @@ func webhook(w http.ResponseWriter, r *http.Request) { if !flagExists { // last but not least check the commit message for flags - var prService github.PullRequestsService - commits, _, err := prService.ListCommits(context.Background(), + client := github.NewClient(nil) + commits, _, err := client.PullRequests.ListCommits(context.Background(), pr.GetHead().GetUser().GetLogin(), pr.GetHead().GetRepo().GetName(), pr.GetNumber(), &github.ListOptions{}) From 02ae6c5a1f7dee56e474d0e9d7fefd65b31535bc Mon Sep 17 00:00:00 2001 From: Lukas Matt Date: Sat, 17 Mar 2018 18:05:11 +0100 Subject: [PATCH 5/5] Fix case-sensitive build flags and use last commit instead of first --- webhook.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/webhook.go b/webhook.go index ba90d51..31373f1 100644 --- a/webhook.go +++ b/webhook.go @@ -87,17 +87,16 @@ func webhook(w http.ResponseWriter, r *http.Request) { } // check PR title and body for [ci] or [ci skip] flag - if pr.Title != nil && strings.Contains(strings.ToLower(*pr.Title), + if pr.Title != nil && strings.Contains(pr.GetTitle(), fmt.Sprintf("[%s]", buildFlag)) { flagExists = true - } else if pr.Body != nil && strings.Contains(strings.ToLower(*pr.Body), + } else if pr.Body != nil && strings.Contains(pr.GetBody(), fmt.Sprintf("[%s]", buildFlag)) { flagExists = true } else { // check labels for build flag if we haven't already found it for _, label := range pr.Labels { - if label.Name != nil && strings.Contains( - strings.ToLower(label.GetName()), buildFlag) { + if label.Name != nil && strings.Contains(label.GetName(), buildFlag) { flagExists = true break } @@ -106,14 +105,15 @@ func webhook(w http.ResponseWriter, r *http.Request) { if !flagExists { // last but not least check the commit message for flags client := github.NewClient(nil) - commits, _, err := client.PullRequests.ListCommits(context.Background(), + commits, _, err := client.PullRequests.ListCommits( + context.Background(), pr.GetHead().GetUser().GetLogin(), pr.GetHead().GetRepo().GetName(), pr.GetNumber(), &github.ListOptions{}) if err == nil && len(commits) > 0 { // check only last commit since older ones are irrelevant - commitMsg := strings.ToLower(commits[0].GetCommit().GetMessage()) + commitMsg := commits[len(commits)-1].GetCommit().GetMessage() flagExists = strings.Contains( commitMsg, fmt.Sprintf("[%s]", buildFlag)) } else {