Skip to content

Commit

Permalink
feat: birth.
Browse files Browse the repository at this point in the history
  • Loading branch information
ldez committed Feb 18, 2019
1 parent 2662b08 commit 829c878
Show file tree
Hide file tree
Showing 7 changed files with 307 additions and 0 deletions.
85 changes: 85 additions & 0 deletions action.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package main

import (
"context"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"log"
"os"
"strings"

"github.com/google/go-github/v24/github"
"golang.org/x/oauth2"
)

const (
// https://developer.github.com/v3/activity/events/types/#pullrequestevent
pullRequestEventName = "pull_request"
)

func action(dryRun bool) error {
gitHubToken := os.Getenv("GITHUB_TOKEN")
eventName := os.Getenv("GITHUB_EVENT_NAME")
eventPath := os.Getenv("GITHUB_EVENT_PATH")

if len(gitHubToken) == 0 {
return errors.New("GITHUB_TOKEN is required")
}

if eventName != pullRequestEventName {
return fmt.Errorf("invalid event type: %q", eventName)
}

event := &github.PullRequestEvent{}
err := readEvent(eventPath, event)
if err != nil {
return err
}

action := event.GetAction()

if action != "closed" || !event.PullRequest.GetMerged() {
log.Printf("skip: %q merge %v", action, event.PullRequest.GetMerged())
return nil
}

ctx := context.Background()
client := newGitHubClient(ctx, gitHubToken)

owner, repoName := getRepoInfo()

return closeRelatedIssues(ctx, client, owner, repoName, event.PullRequest, dryRun)
}

func newGitHubClient(ctx context.Context, token string) *github.Client {
var client *github.Client
if len(token) == 0 {
client = github.NewClient(nil)
} else {
ts := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: token},
)
tc := oauth2.NewClient(ctx, ts)
client = github.NewClient(tc)
}
return client
}

func readEvent(eventPath string, event interface{}) error {
content, err := ioutil.ReadFile(eventPath)
if err != nil {
return err
}

return json.Unmarshal(content, event)
}

func getRepoInfo() (string, string) {
githubRepository := os.Getenv("GITHUB_REPOSITORY")

parts := strings.SplitN(githubRepository, "/", 2)

return parts[0], parts[1]
}
7 changes: 7 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module github.com/ldez/gha-mjolnir

require (
github.com/google/go-github v17.0.0+incompatible
github.com/google/go-github/v24 v24.0.0
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be
)
14 changes: 14 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-github/v24 v24.0.0 h1:11gKs9XbwcUO5yEgg+GAbtg6z6CH7tK77wDM3mnAnXc=
github.com/google/go-github/v24 v24.0.0/go.mod h1:CRqaW1Uns1TCkP0wqTpxYyRxRjxwvKU/XSS44u6X74M=
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
golang.org/x/crypto v0.0.0-20180820150726-614d502a4dac/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d h1:g9qWBGx4puODJTMVyoPrpoxPFgVGd+z1DZwjfRu4d0I=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sys v0.0.0-20180824143301-4910a1d54f87/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
12 changes: 12 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package main

import "log"

func main() {
displayVersion()

err := action(true)
if err != nil {
log.Fatal(err)
}
}
95 changes: 95 additions & 0 deletions mjolnir.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package main

import (
"context"
"fmt"
"log"
"regexp"
"strconv"
"strings"

"github.com/google/go-github/v24/github"
)

var (
globalFixesIssueRE = regexp.MustCompile(`(?i)(?:close|closes|closed|fix|fixes|fixed|resolve|resolves|resolved)((?:[\s]+#[\d]+)(?:[\s,]+#[\d]+)*(?:[\n\r\s,]|$))`)
fixesIssueRE = regexp.MustCompile(`[\s,]+#`)
cleanNumberRE = regexp.MustCompile(`[\n\r\s,]`)
)

// closeRelatedIssues Closes issues listed in the PR description.
func closeRelatedIssues(ctx context.Context, client *github.Client, owner string, repositoryName string, pr *github.PullRequest, dryRun bool) error {
issueNumbers := parseIssueFixes(pr.GetBody())

for _, issueNumber := range issueNumbers {
log.Printf("PR #%d: closes issue #%d, add milestones %s", pr.GetNumber(), issueNumber, pr.Milestone.GetTitle())
if !dryRun {
err := closeIssue(ctx, client, owner, repositoryName, pr, issueNumber)
if err != nil {
return fmt.Errorf("unable to close issue #%d: %v", issueNumber, err)
}
}

// Add comment if needed
if pr.Base.GetRef() != "master" {
message := fmt.Sprintf("Closed by #%d.", pr.GetNumber())

log.Printf("PR #%d: issue #%d, add comment: %s", pr.GetNumber(), issueNumber, message)

if !dryRun {
err := addComment(ctx, client, owner, repositoryName, issueNumber, message)
if err != nil {
return fmt.Errorf("unable to add comment on issue #%d: %v", issueNumber, err)
}
}
}
}

return nil
}

func closeIssue(ctx context.Context, client *github.Client, owner string, repositoryName string, pr *github.PullRequest, issueNumber int) error {

var milestone *int
if pr.Milestone != nil {
milestone = pr.Milestone.Number
}

issueRequest := &github.IssueRequest{
Milestone: milestone,
State: github.String("closed"),
}

_, _, err := client.Issues.Edit(ctx, owner, repositoryName, issueNumber, issueRequest)
return err
}

func addComment(ctx context.Context, client *github.Client, owner string, repositoryName string, issueNumber int, message string) error {
issueComment := &github.IssueComment{
Body: github.String(message),
}
_, _, err := client.Issues.CreateComment(ctx, owner, repositoryName, issueNumber, issueComment)
return err
}

func parseIssueFixes(text string) []int {
var issueNumbers []int

submatch := globalFixesIssueRE.FindStringSubmatch(strings.Replace(text, ":", "", -1))

if len(submatch) != 0 {
issuesRaw := fixesIssueRE.Split(submatch[1], -1)

for _, issueRaw := range issuesRaw {
cleanIssueRaw := cleanNumberRE.ReplaceAllString(issueRaw, "")
if len(cleanIssueRaw) != 0 {
numb, err := strconv.ParseInt(cleanIssueRaw, 10, 16)
if err != nil {
log.Println(err)
}
issueNumbers = append(issueNumbers, int(numb))
}
}
}
return issueNumbers
}
70 changes: 70 additions & 0 deletions mjolnir_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package main

import (
"reflect"
"testing"
)

func Test_parseIssueFixes(t *testing.T) {
testCases := []struct {
name string
text string
expectedNumbers []int
}{
{
name: "only letters",
text: `
Fixes dlsqj
`,
expectedNumbers: []int{},
},
{
name: "valid issue numbers coma",
text: `
Fixes #13 #14, #15,#16,
`,
expectedNumbers: []int{13, 14, 15, 16},
},
{
name: "valid issue numbers space",
text: `
Fixes #13 #14 #15 #16
`,
expectedNumbers: []int{13, 14, 15, 16},
},
{
name: "invalid pattern",
text: `
Fixes #13#14,#15,#16,
`,
expectedNumbers: []int{},
},
{
name: "french style",
text: `
Fixes : #13,#14,#15,#16,
`,
expectedNumbers: []int{13, 14, 15, 16},
},
{
name: "valid issue numbers coma and :",
text: `
Fixes: #13,#14,#15,#16,
`,
expectedNumbers: []int{13, 14, 15, 16},
},
}

for _, test := range testCases {
test := test
t.Run(test.name, func(t *testing.T) {
t.Parallel()

issueNumbers := parseIssueFixes(test.text)

if (len(issueNumbers) != 0 || len(test.expectedNumbers) != 0) && !reflect.DeepEqual(issueNumbers, test.expectedNumbers) {
t.Errorf("Got %v, expected %v", issueNumbers, test.expectedNumbers)
}
})
}
}
24 changes: 24 additions & 0 deletions version.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package main

import (
"fmt"
"runtime"
)

var (
version = "dev"
commit = "I don't remember exactly"
date = "I don't remember exactly"
)

// displayVersion DisplayVersion version.
func displayVersion() {
fmt.Printf(`mjolnir:
version : %s
commit : %s
build date : %s
go version : %s
go compiler : %s
platform : %s/%s
`, version, commit, date, runtime.Version(), runtime.Compiler, runtime.GOOS, runtime.GOARCH)
}

0 comments on commit 829c878

Please sign in to comment.