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

Add pr-status command #67

Merged
merged 1 commit into from
May 18, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@ BIN_DIR := $(CURDIR)/bin
$(BIN_DIR)/golangci-lint: $(BIN_DIR)/golangci-lint-${GOLANGCI_VERSION}
@ln -sf golangci-lint-${GOLANGCI_VERSION} $(BIN_DIR)/golangci-lint
$(BIN_DIR)/golangci-lint-${GOLANGCI_VERSION}:
@curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | BINARY=golangci-lint bash -s -- v${GOLANGCI_VERSION}
@curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | BINARY=golangci-lint bash -s -- v${GOLANGCI_VERSION}
@mv $(BIN_DIR)/golangci-lint $@

mod:
150 changes: 150 additions & 0 deletions cmd/pr_status/pr_status.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
/*
* Copyright 2021 Skyscanner Limited.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

package pr_status

import (
"fmt"
"github.com/fatih/color"
"github.com/rodaine/table"
"github.com/skyscanner/turbolift/internal/campaign"
"github.com/skyscanner/turbolift/internal/github"
"github.com/skyscanner/turbolift/internal/logging"
"github.com/spf13/cobra"
"os"
"path"
"strings"
)

var reactionsOrder = []string{
"THUMBS_UP",
"THUMBS_DOWN",
"LAUGH",
"HOORAY",
"CONFUSED",
"HEART",
"ROCKET",
"EYES",
}

var reactionsMapping = map[string]string{
"THUMBS_UP": "👍",
"THUMBS_DOWN": "👎",
"LAUGH": "😆",
"HOORAY": "🎉",
"CONFUSED": "😕",
"HEART": "❤️",
"ROCKET": "🚀",
"EYES": "👀",
}

var gh github.GitHub = github.NewRealGitHub()

var list bool

func NewPrStatusCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "pr-status",
Short: "Displays the status of PRs",
Run: run,
}
cmd.Flags().BoolVar(&list, "list", false, "Displays a listing by PR")

return cmd
}

func run(c *cobra.Command, _ []string) {
logger := logging.NewLogger(c)

readCampaignActivity := logger.StartActivity("Reading campaign data")
dir, err := campaign.OpenCampaign()
if err != nil {
readCampaignActivity.EndWithFailure(err)
return
}
readCampaignActivity.EndWithSuccess()

statuses := make(map[string]int)
reactions := make(map[string]int)

detailsTable := table.New("Repository", "State", "Reviews", "URL")
detailsTable.WithHeaderFormatter(color.New(color.Underline).SprintfFunc())
detailsTable.WithFirstColumnFormatter(color.New(color.FgCyan).SprintfFunc())
detailsTable.WithWriter(logger.Writer())

for _, repo := range dir.Repos {
repoDirPath := path.Join("work", repo.OrgName, repo.RepoName) // i.e. work/org/repo

checkStatusActivity := logger.StartActivity("Checking PR status for %s", repo.FullRepoName)

// skip if the working copy does not exist
if _, err = os.Stat(repoDirPath); os.IsNotExist(err) {
checkStatusActivity.EndWithWarningf("Directory %s does not exist - has it been cloned?", repoDirPath)
statuses["SKIPPED"]++
continue
}

prStatus, err := gh.GetPR(checkStatusActivity.Writer(), repoDirPath, dir.Name)
if err != nil {
checkStatusActivity.EndWithFailuref("No PR found: %v", err)
statuses["NO_PR"]++
continue
}

statuses[prStatus.State]++

for _, reaction := range prStatus.ReactionGroups {
reactions[reaction.Content] += reaction.Users.TotalCount
}

detailsTable.AddRow(repo.FullRepoName, prStatus.State, prStatus.ReviewDecision, prStatus.Url)

checkStatusActivity.EndWithSuccess()
}

logger.Successf("turbolift pr-status completed\n")

logger.Println()

if list {
detailsTable.Print()
logger.Println()
}

summaryTable := table.New("State", "Count")
summaryTable.WithHeaderFormatter(color.New(color.Underline).SprintfFunc())
summaryTable.WithFirstColumnFormatter(color.New(color.FgCyan).SprintfFunc())
summaryTable.WithWriter(logger.Writer())

summaryTable.AddRow("Merged", statuses["MERGED"])
summaryTable.AddRow("Open", statuses["OPEN"])
summaryTable.AddRow("Closed", statuses["CLOSED"])
summaryTable.AddRow("Skipped", statuses["SKIPPED"])
summaryTable.AddRow("No PR Found", statuses["NO_PR"])

summaryTable.Print()

logger.Println()

var reactionsOutput []string
for _, key := range reactionsOrder {
if reactions[key] > 0 {
reactionsOutput = append(reactionsOutput, fmt.Sprintf("%s %d", reactionsMapping[key], reactions[key]))
}
}
if len(reactionsOutput) > 0 {
logger.Println("Reactions:", strings.Join(reactionsOutput, " "))
}
}
171 changes: 171 additions & 0 deletions cmd/pr_status/pr_status_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
/*
* Copyright 2021 Skyscanner Limited.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

package pr_status

import (
"bytes"
"errors"
"io"
"os"
"testing"

"github.com/skyscanner/turbolift/internal/github"
"github.com/skyscanner/turbolift/internal/testsupport"
"github.com/stretchr/testify/assert"
)

func init() {
// disable output colouring so that strings we want to do 'Contains' checks on do not have ANSI escape sequences in IDEs
_ = os.Setenv("NO_COLOR", "1")
}

func TestItLogsSummaryInformation(t *testing.T) {
prepareFakeResponses()

testsupport.PrepareTempCampaign(true, "org/repo1", "org/repo2", "org/repo3")

out, err := runCommand(false)
assert.NoError(t, err)
assert.Contains(t, out, "Checking PR status for org/repo1")
assert.Contains(t, out, "Checking PR status for org/repo2")
assert.Contains(t, out, "turbolift pr-status completed")
assert.Regexp(t, "Open\\s+1", out)
assert.Regexp(t, "Merged\\s+1", out)
assert.Regexp(t, "Closed\\s+1", out)

assert.Regexp(t, "Reactions: 👍 4 👎 3 🚀 1", out)

// Shouldn't show 'list' detailed info
assert.NotRegexp(t, "org/repo1\\s+OPEN", out)
assert.NotRegexp(t, "org/repo2\\s+MERGED", out)
}

func TestItLogsDetailedInformation(t *testing.T) {
prepareFakeResponses()

testsupport.PrepareTempCampaign(true, "org/repo1", "org/repo2", "org/repo3")

out, err := runCommand(true)
assert.NoError(t, err)
// Should still show summary info
assert.Regexp(t, "Open\\s+1", out)
assert.Regexp(t, "👍\\s+4", out)

assert.Regexp(t, "org/repo1\\s+OPEN\\s+REVIEW_REQUIRED", out)
assert.Regexp(t, "org/repo2\\s+MERGED\\s+APPROVED", out)
assert.Regexp(t, "org/repo3\\s+CLOSED", out)
}

func TestItSkipsUnclonedRepos(t *testing.T) {
prepareFakeResponses()

testsupport.PrepareTempCampaign(true, "org/repo1", "org/repo2")
_ = os.Remove("work/org/repo2")

out, err := runCommand(true)
assert.NoError(t, err)
// Should still show summary info
assert.Regexp(t, "Open\\s+1", out)
assert.Regexp(t, "Merged\\s+0", out)
assert.Regexp(t, "Skipped\\s+1", out)
assert.Regexp(t, "No PR Found\\s+0", out)

assert.Regexp(t, "org/repo1\\s+OPEN", out)
assert.NotRegexp(t, "org/repo2\\s+MERGED", out)
}

func TestItNotesReposWhereNoPrCanBeFound(t *testing.T) {
prepareFakeResponses()

testsupport.PrepareTempCampaign(true, "org/repo1", "org/repo2", "org/repoWithError")

out, err := runCommand(true)
assert.NoError(t, err)
// Should still show summary info
assert.Regexp(t, "Open\\s+1", out)
assert.Regexp(t, "Merged\\s+1", out)
assert.Regexp(t, "Skipped\\s+0", out)
assert.Regexp(t, "No PR Found\\s+1", out)

assert.Regexp(t, "org/repo1\\s+OPEN", out)
}

func runCommand(showList bool) (string, error) {
cmd := NewPrStatusCmd()
list = showList
outBuffer := bytes.NewBufferString("")
cmd.SetOut(outBuffer)
err := cmd.Execute()

if err != nil {
return outBuffer.String(), err
}
return outBuffer.String(), nil
}

func prepareFakeResponses() {
dummyData := map[string]*github.PrStatus{
"work/org/repo1": {
State: "OPEN",
ReactionGroups: []github.ReactionGroup{
{
Content: "THUMBS_UP",
Users: github.ReactionGroupUsers{
TotalCount: 3,
},
},
{
Content: "ROCKET",
Users: github.ReactionGroupUsers{
TotalCount: 1,
},
},
},
ReviewDecision: "REVIEW_REQUIRED",
},
"work/org/repo2": {
State: "MERGED",
ReactionGroups: []github.ReactionGroup{
{
Content: "THUMBS_UP",
Users: github.ReactionGroupUsers{
TotalCount: 1,
},
},
},
ReviewDecision: "APPROVED",
},
"work/org/repo3": {
State: "CLOSED",
ReactionGroups: []github.ReactionGroup{
{
Content: "THUMBS_DOWN",
Users: github.ReactionGroupUsers{
TotalCount: 3,
},
},
},
},
}
fakeGitHub := github.NewFakeGitHub(nil, func(output io.Writer, workingDir string) (interface{}, error) {
if workingDir == "work/org/repoWithError" {
return nil, errors.New("Synthetic error")
} else {
return dummyData[workingDir], nil
}
})
gh = fakeGitHub
}
3 changes: 3 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
@@ -27,6 +27,8 @@ import (
initCmd "github.com/skyscanner/turbolift/cmd/init"
updatePrsCmd "github.com/skyscanner/turbolift/cmd/update_prs"
"github.com/spf13/cobra"

prStatusCmd "github.com/skyscanner/turbolift/cmd/pr_status"
)

var (
@@ -52,6 +54,7 @@ func init() {
rootCmd.AddCommand(initCmd.NewInitCmd())
rootCmd.AddCommand(foreachCmd.NewForeachCmd())
rootCmd.AddCommand(updatePrsCmd.NewUpdatePRsCmd())
rootCmd.AddCommand(prStatusCmd.NewPrStatusCmd())
}

func Execute() {
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -5,8 +5,9 @@ go 1.16
require (
github.com/briandowns/spinner v1.15.0
github.com/fatih/color v1.12.0
github.com/manifoldco/promptui v0.9.0 // indirect
github.com/manifoldco/promptui v0.9.0
github.com/mattn/go-isatty v0.0.13 // indirect
github.com/rodaine/table v1.0.1
github.com/spf13/cobra v1.1.3
github.com/stretchr/testify v1.7.0
golang.org/x/sys v0.0.0-20210603125802-9665404d3644 // indirect
13 changes: 7 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
@@ -23,14 +23,14 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
github.com/briandowns/spinner v1.12.0 h1:72O0PzqGJb6G3KgrcIOtL/JAGGZ5ptOMCn9cUHmqsmw=
github.com/briandowns/spinner v1.12.0/go.mod h1:QOuQk7x+EaDASo80FEXwlwiA+j/PPIcX3FScO+3/ZPQ=
github.com/briandowns/spinner v1.15.0 h1:L0jR0MYN7OAeMwpTzDZWIeqyDLXtTeJFxqoq+sL0VQM=
github.com/briandowns/spinner v1.15.0/go.mod h1:QOuQk7x+EaDASo80FEXwlwiA+j/PPIcX3FScO+3/ZPQ=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
@@ -44,7 +44,6 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc=
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
@@ -120,16 +119,16 @@ github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czP
github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA=
github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA=
github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
@@ -160,6 +159,8 @@ github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rodaine/table v1.0.1 h1:U/VwCnUxlVYxw8+NJiLIuCxA/xa6jL38MY3FYysVWWQ=
github.com/rodaine/table v1.0.1/go.mod h1:UVEtfBsflpeEcD56nF4F5AocNFta0ZuolpSVdPtlmP4=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@@ -184,6 +185,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
@@ -254,7 +256,6 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 h1:HyfiK1WMnHj5FXFXatD+Qs1A/xC2Run6RzeW1SyHxpc=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
29 changes: 24 additions & 5 deletions internal/github/fake_github.go
Original file line number Diff line number Diff line change
@@ -24,8 +24,9 @@ import (
)

type FakeGitHub struct {
handler func(output io.Writer, workingDir string, fullRepoName string) (bool, error)
calls [][]string
handler func(output io.Writer, workingDir string, fullRepoName string) (bool, error)
returningHandler func(output io.Writer, workingDir string) (interface{}, error)
calls [][]string
}

func (f *FakeGitHub) CreatePullRequest(output io.Writer, workingDir string, metadata PullRequest) (didCreate bool, err error) {
@@ -53,37 +54,55 @@ func (f *FakeGitHub) ClosePullRequest(output io.Writer, workingDir string, branc
return err
}

func (f *FakeGitHub) GetPR(output io.Writer, workingDir string, _ string) (*PrStatus, error) {
f.calls = append(f.calls, []string{workingDir})
result, err := f.returningHandler(output, workingDir)
if result == nil {
return nil, err
}
return result.(*PrStatus), err
}

func (f *FakeGitHub) AssertCalledWith(t *testing.T, expected [][]string) {
assert.Equal(t, expected, f.calls)
}

func NewFakeGitHub(h func(output io.Writer, workingDir string, fullRepoName string) (bool, error)) *FakeGitHub {
func NewFakeGitHub(h func(output io.Writer, workingDir string, fullRepoName string) (bool, error), r func(output io.Writer, workingDir string) (interface{}, error)) *FakeGitHub {
return &FakeGitHub{
handler: h,
calls: [][]string{},
handler: h,
returningHandler: r,
calls: [][]string{},
}
}

func NewAlwaysSucceedsFakeGitHub() *FakeGitHub {
return NewFakeGitHub(func(output io.Writer, workingDir string, fullRepoName string) (bool, error) {
return true, nil
}, func(output io.Writer, workingDir string) (interface{}, error) {
return PrStatus{}, nil
})
}

func NewAlwaysFailsFakeGitHub() *FakeGitHub {
return NewFakeGitHub(func(output io.Writer, workingDir string, fullRepoName string) (bool, error) {
return false, errors.New("synthetic error")
}, func(output io.Writer, workingDir string) (interface{}, error) {
return nil, errors.New("synthetic error")
})
}

func NewAlwaysThrowNoPRFound() *FakeGitHub {
return NewFakeGitHub(func(output io.Writer, workingDir string, branchName string) (bool, error) {
return false, &NoPRFoundError{Path: workingDir, BranchName: branchName}
}, func(output io.Writer, workingDir string) (interface{}, error) {
panic("should not be invoked")
})
}

func NewAlwaysReturnsFalseFakeGitHub() *FakeGitHub {
return NewFakeGitHub(func(output io.Writer, workingDir string, fullRepoName string) (bool, error) {
return false, nil
}, func(output io.Writer, workingDir string) (interface{}, error) {
return PrStatus{}, nil
})
}
14 changes: 8 additions & 6 deletions internal/github/github.go
Original file line number Diff line number Diff line change
@@ -27,17 +27,19 @@ import (
var execInstance executor.Executor = executor.NewRealExecutor()

type PullRequest struct {
Title string
Body string
UpstreamRepo string
IsDraft bool
Title string
Body string
UpstreamRepo string
IsDraft bool
ReviewDecision string
}

type GitHub interface {
ForkAndClone(output io.Writer, workingDir string, fullRepoName string) error
Clone(output io.Writer, workingDir string, fullRepoName string) error
CreatePullRequest(output io.Writer, workingDir string, metadata PullRequest) (didCreate bool, err error)
ClosePullRequest(output io.Writer, workingDir string, branchName string) error
GetPR(output io.Writer, workingDir string, branchName string) (*PrStatus, error)
}

type RealGitHub struct{}
@@ -138,13 +140,13 @@ func (r *RealGitHub) GetPR(output io.Writer, workingDir string, branchName strin

// if the user has write permissions on the repo,
// the PR should be under _CurrentBranch_.
if prr.CurrentBranch != nil && !prr.CurrentBranch.Closed {
if prr.CurrentBranch != nil {
return prr.CurrentBranch, nil
}

// If not, then it's a forked PR. The headRefName is as such: `username:branchName`
for _, pr := range prr.CreatedBy {
if strings.HasSuffix(pr.HeadRefName, branchName) && !pr.Closed {
if strings.HasSuffix(pr.HeadRefName, branchName) {
return pr, nil
}
}
4 changes: 4 additions & 0 deletions internal/logging/logging.go
Original file line number Diff line number Diff line change
@@ -84,3 +84,7 @@ func (log *Logger) StartActivity(format string, args ...interface{}) *Activity {
verbose: log.verbose,
}
}

func (log *Logger) Writer() io.Writer {
return log.writer
}