Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Get author from blame #60

Merged
merged 12 commits into from
Jan 17, 2024
8 changes: 4 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ COPY . /app

ENV GOFLAGS="-mod=vendor"

# Statically compile our app for use in a distroless container
# Statically compile our app for use in the final container
RUN CGO_ENABLED=0 go build -ldflags="-w -s" -v -o app .

# A distroless container image with some basics like SSL certificates
# https://github.com/GoogleContainerTools/distroless
FROM gcr.io/distroless/static
FROM alpine:3.14

COPY --from=builder /app/app /app

RUN apk update && apk --no-cache add git

ENTRYPOINT ["/app"]
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ You can use this action together with [parent issue updater](https://github.com/
| `CLOSE_LIMIT` | Upper cap on the number of issues to close (defaults to `0` - unlimited) |
| `COMMENT_ON_ISSUES` | Leave a comment in which commit the issue was closed (defaults to `0` - do not comment) |
| `CONCURRENCY` | How many files to process in parallel (defaults to `128`) |
| `ASSIGN_FROM_BLAME` | Get the author of the comment via git API from the commit hash of the comment and assign to the issue created (defaults to '0' - do not use) |

> **NOTE:** Keep in mind that you have to escape slashes in regex patterns when putting them to yaml

Expand Down
3 changes: 3 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ inputs:
COMMENT_ON_ISSUES:
description: "Create commit reference in the comments before closing the issue"
default: "0"
ASSIGN_FROM_BLAME:
description: "Get the author of the comment via git API from the commit hash of the comment and assign to the issue created"
default: "0"
runs:
using: "docker"
image: "Dockerfile"
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ go 1.14
require (
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect
github.com/google/go-github/v49 v49.1.0
gitlab.com/ribtoks/tdg v0.0.3
gitlab.com/ribtoks/tdg v0.0.4
golang.org/x/oauth2 v0.15.0
)
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -862,8 +862,8 @@ github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN
github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
github.com/zieckey/goini v0.0.0-20180118150432-0da17d361d26 h1:E0lEWrifmR0ACbGf5PLji1XbW6rtIXLHCXO/YOqi0AE=
github.com/zieckey/goini v0.0.0-20180118150432-0da17d361d26/go.mod h1:TQpdgg7I9+PFIkatlx/dnZyZb4iZyCUx1HJj4rXi3+E=
gitlab.com/ribtoks/tdg v0.0.3 h1:FqsgHQAMxc8Tt7T0TCEErszNFa9of2JFYLnhCkmqC4c=
gitlab.com/ribtoks/tdg v0.0.3/go.mod h1:XDYfJNbUbnnCW6lGCC2TLumDieEI04L0VtjVbgDKz1w=
gitlab.com/ribtoks/tdg v0.0.4 h1:oFb+EAclMvwA1DbsL/ibdl7TgJm6shpQ35Y299FZkXk=
gitlab.com/ribtoks/tdg v0.0.4/go.mod h1:XDYfJNbUbnnCW6lGCC2TLumDieEI04L0VtjVbgDKz1w=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
Expand Down
89 changes: 79 additions & 10 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,17 @@ type env struct {
extendedLabels bool
dryRun bool
commentIssue bool
assignFromBlame bool
}

type service struct {
ctx context.Context
client *github.Client
env *env
wg sync.WaitGroup
ctx context.Context
client *github.Client
env *env
wg sync.WaitGroup
newIssuesMap map[string]*github.Issue
issueTitleToAssigneeMap map[string]string
commitToAuthorCache map[string]string
}

func (e *env) sourceRoot() string {
Expand Down Expand Up @@ -98,6 +102,7 @@ func environment() *env {
extendedLabels: flagToBool(os.Getenv("INPUT_EXTENDED_LABELS")),
closeOnSameBranch: flagToBool(os.Getenv("INPUT_CLOSE_ON_SAME_BRANCH")),
commentIssue: flagToBool(os.Getenv("INPUT_COMMENT_ON_ISSUES")),
assignFromBlame: flagToBool(os.Getenv("INPUT_ASSIGN_FROM_BLAME")),
}

var err error
Expand Down Expand Up @@ -275,15 +280,16 @@ func (s *service) openNewIssues(issueMap map[string]*github.Issue, comments []*t
count := 0

for _, c := range comments {
_, ok := issueMap[c.Title]
if !ok {
if _, ok := issueMap[c.Title]; !ok {
body := c.Body + "\n\n"
if c.Issue > 0 {
body += fmt.Sprintf("Parent issue: #%v\n", c.Issue)
}

if len(c.Author) > 0 {
body += fmt.Sprintf("Author: @%s\n", c.Author)
} else if len(c.CommitterEmail) > 0 {
body += fmt.Sprintf("Author: %s\n", c.CommitterEmail)
}

body += fmt.Sprintf("Line: %v\n%s", c.Line, s.createFileLink(c))
Expand All @@ -308,6 +314,7 @@ func (s *service) openNewIssues(issueMap map[string]*github.Issue, comments []*t
continue
}

s.newIssuesMap[c.Title] = issue
log.Printf("Created an issue. title=%v issue=%v", c.Title, issue.GetID())

if s.env.projectColumnID != -1 {
Expand All @@ -325,6 +332,56 @@ func (s *service) openNewIssues(issueMap map[string]*github.Issue, comments []*t
log.Printf("Created new issues. count=%v", count)
}

func (s *service) assignNewIssues() {
log.Printf("Adding assignees to %v newly created issues...", len(s.issueTitleToAssigneeMap))
for title, assignee := range s.issueTitleToAssigneeMap {
issue := s.newIssuesMap[title]
issueNumber := issue.GetNumber()
req := &github.IssueRequest{
Assignees: &[]string{assignee},
}
if _, _, err := s.client.Issues.Edit(s.ctx, s.env.owner, s.env.repo, issueNumber, req); err != nil {
log.Printf("Error while assigning %v to issue %v. err=%v", assignee, issueNumber, err)
} else {
log.Printf("Successfully assigned %v to issue %v.", assignee, issueNumber)
}
}
}

func (s *service) retrieveCommitAuthor(commitHash string, title string) {
// First check cache to see if this commit was already retrieved before
if commitAuthor, ok := s.commitToAuthorCache[commitHash]; ok {
s.issueTitleToAssigneeMap[title] = commitAuthor
return
}

commit, _, err := s.client.Repositories.GetCommit(s.ctx, s.env.owner, s.env.repo, commitHash, &github.ListOptions{})
ribtoks marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
log.Printf("Error while getting commit from commit hash. err=%v", err)
} else if commit != nil && commit.Author != nil && len(*commit.Author.Login) > 0 {
s.issueTitleToAssigneeMap[title] = *commit.Author.Login
s.commitToAuthorCache[commitHash] = *commit.Author.Login
} else {
ribtoks marked this conversation as resolved.
Show resolved Hide resolved
log.Printf("Error: No author mentioned in commit '%v'", commitHash)
}
}

func (s *service) retrieveNewIssueAssignees(issueMap map[string]*github.Issue, comments []*tdglib.ToDoComment) {
defer s.wg.Done()

totalNewIssues := 0
for _, c := range comments {
ribtoks marked this conversation as resolved.
Show resolved Hide resolved
if _, ok := issueMap[c.Title]; !ok {
totalNewIssues++
if len(c.CommitHash) > 0 {
s.retrieveCommitAuthor(c.CommitHash, c.Title)
}
}
}

log.Printf("Got assignees for %v of %v new issues.", len(s.issueTitleToAssigneeMap), totalNewIssues)
}

func (s *service) canCloseIssue(issue *github.Issue) bool {
if !s.env.closeOnSameBranch {
return true
Expand Down Expand Up @@ -437,9 +494,12 @@ func main() {
tc := oauth2.NewClient(ctx, ts)

svc := &service{
ctx: ctx,
client: github.NewClient(tc),
env: env,
ctx: ctx,
client: github.NewClient(tc),
env: env,
newIssuesMap: make(map[string]*github.Issue),
issueTitleToAssigneeMap: make(map[string]string),
commitToAuthorCache: make(map[string]string),
}

env.debugPrint()
Expand All @@ -462,7 +522,7 @@ func main() {
td := tdglib.NewToDoGenerator(env.sourceRoot(),
includePatterns,
excludePatterns,
false /*blame*/,
env.assignFromBlame,
env.minWords,
env.minChars,
env.concurrency)
Expand All @@ -485,8 +545,17 @@ func main() {
svc.wg.Add(1)
go svc.openNewIssues(issueMap, comments)

if env.assignFromBlame {
svc.wg.Add(1)
go svc.retrieveNewIssueAssignees(issueMap, comments)
}

log.Printf("Waiting for issues management to finish")
svc.wg.Wait()

if env.assignFromBlame && len(svc.newIssuesMap) > 0 {
svc.assignNewIssues()
}

fmt.Println(fmt.Sprintf(`::set-output name=scannedIssues::%s`, "1"))
}
26 changes: 26 additions & 0 deletions vendor/gitlab.com/ribtoks/tdg/pkg/tdglib/todogenerator.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion vendor/modules.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ github.com/google/go-github/v49/github
github.com/google/go-querystring/query
# github.com/zieckey/goini v0.0.0-20180118150432-0da17d361d26
github.com/zieckey/goini
# gitlab.com/ribtoks/tdg v0.0.3
# gitlab.com/ribtoks/tdg v0.0.4
## explicit
gitlab.com/ribtoks/tdg/pkg/tdglib
# golang.org/x/crypto v0.16.0
Expand Down