Skip to content

Commit

Permalink
Get author from blame (#60)
Browse files Browse the repository at this point in the history
* Add AUTHOR_FROM_BLAME flag

- Modified Dockerfile to use alpine base
- Added the new flag
- Added git command to mark directory as safe for running git cmds
- Upgraded libs

* Add assignees to git issue

* Remove trailing . and / from root path in git cmd

* correct commit API, add committerEmail to body

* Paralelly get assignees and assign to issues later

* Use new waitGroup for getting assignees go routine

* Made changes based on review

* Make calls to commitAPI linear instead of parallel

* Cache the commits retrieved

* Make changes from second review

* Upgrade tdg to v0.0.4 from v0.0.3

* Remove code to mark root dir safe for git
  • Loading branch information
sidsprasad authored Jan 17, 2024
1 parent 716c96b commit 3482957
Show file tree
Hide file tree
Showing 8 changed files with 117 additions and 18 deletions.
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{})
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 {
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 {
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

0 comments on commit 3482957

Please sign in to comment.