Skip to content

Commit

Permalink
Update
Browse files Browse the repository at this point in the history
  • Loading branch information
janekbaraniewski committed Jul 19, 2023
1 parent 5509f25 commit c7341d1
Show file tree
Hide file tree
Showing 3 changed files with 141 additions and 64 deletions.
7 changes: 1 addition & 6 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ go 1.20
require github.com/spf13/cobra v1.7.0

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
Expand All @@ -14,25 +13,21 @@ require (
github.com/mattn/go-colorable v0.1.2 // indirect
github.com/mattn/go-isatty v0.0.8 // indirect
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/crypto v0.11.0 // indirect
golang.org/x/net v0.12.0 // indirect
golang.org/x/sys v0.10.0 // indirect
golang.org/x/term v0.10.0 // indirect
golang.org/x/text v0.11.0 // indirect
golang.org/x/time v0.3.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

require (
github.com/AlecAivazis/survey/v2 v2.3.7
github.com/google/go-github v17.0.0+incompatible
github.com/google/go-github/v33 v33.0.0
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/testify v1.8.4
github.com/stretchr/testify v1.8.4 // indirect
github.com/xanzy/go-gitlab v0.86.0
golang.org/x/oauth2 v0.10.0
gopkg.in/yaml.v2 v2.4.0
Expand Down
15 changes: 4 additions & 11 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ=
github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo=
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s=
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI=
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
Expand All @@ -16,17 +17,15 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
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/v33 v33.0.0 h1:qAf9yP0qc54ufQxzwv+u9H0tiVOnPJxo0lI/JXqw3ZM=
github.com/google/go-github/v33 v33.0.0/go.mod h1:GMdDnVZY/2TsWgp/lkYnpSAh6TrzhANBBwm6k6TTEXg=
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=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI=
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
github.com/hashicorp/go-retryablehttp v0.7.2 h1:AcYqCvkpalPnPF2pn0KamgwamS42TqUDDYFRKq/RAd0=
github.com/hashicorp/go-retryablehttp v0.7.2/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
Expand All @@ -46,7 +45,6 @@ github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRM
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
Expand All @@ -56,17 +54,13 @@ github.com/xanzy/go-gitlab v0.86.0/go.mod h1:5ryv+MnpZStBH8I/77HuQBsMbBGANtVpLWC
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50=
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8=
golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
Expand Down Expand Up @@ -97,7 +91,6 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
Expand Down
183 changes: 136 additions & 47 deletions pkg/issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,52 +11,137 @@ import (
)

const (
GitHubApi = "https://api.github.com/"
GitHubApi = "https://api.github.com/"
errIssueIDNotFound = "issueID not found"
errIssueDoesNotExistOnGitHub = "issue does not exist on GitHub: %w"
errFailedToCloseIssue = "failed to close the issue: %w"
errFailedToGetIssue = "failed to get the issue: %w"
)

// decodeBackendToken decodes backend token from base64
func decodeBackendToken(backendConfig BackendConfig) (string, error) {
ghToken, err := base64.RawStdEncoding.DecodeString(backendConfig.Token)
if err != nil {
return "", err
}
return string(ghToken), nil
}

// getIssueBackendConfigurator prepares IssueBackend
func getIssueBackendConfigurator(backendConfig BackendConfig) (IssueBackend, error) {
ghToken, err := decodeBackendToken(backendConfig)
if err != nil {
return nil, err
}
return GetIssueBackend(&GetBackendConfig{
Type: backendConfig.Type,
GitHubApi: GitHubApi,
GitHubToken: ghToken,
}), nil
}

// getRepoBackendConfigurator prepares RepositoryBackend
func getRepoBackendConfigurator(backendConfig BackendConfig) (RepositoryBackend, error) {
ghToken, err := decodeBackendToken(backendConfig)
if err != nil {
return nil, err
}
return GetRepoBackend(&GetBackendConfig{
Type: backendConfig.Type,
GitHubApi: GitHubApi,
GitHubToken: ghToken,
}), nil
}

// StartWorkingOnIssue starts work on an issue
func StartWorkingOnIssue(config *IssuectlConfig, repositories []string, issueID IssueID) error {
profile := config.GetProfile(config.GetCurrentProfile())

for _, repoName := range repositories {
// TODO: make sure that repos are not duplicated OR overwrite instead of adding repositories
if err := profile.AddRepository((*RepoConfigName)(&repoName)); err != nil {
if err := addRepositoryToProfile(&profile, repoName); err != nil {
return err
}
}
if _, found := config.GetIssue(issueID); found {

if isIssueIdInUse(config, issueID) {
return fmt.Errorf("issueID already in use")
}

Log.Infof("Starting work on issue %v ...", issueID)

repo := config.GetRepository(profile.DefaultRepository)
backendConfig := config.GetBackend(profile.Backend)
gitUser, _ := config.GetGitUser(profile.GitUserName)
ghToken, err := base64.RawStdEncoding.DecodeString(backendConfig.Token)
issueBackend, issueDirPath, err := initializeIssueBackendAndDir(config, &profile, issueID)
if err != nil {
return err
}
issueBackend := GetIssueBackend(&GetBackendConfig{
Type: backendConfig.Type,
GitHubApi: GitHubApi,
GitHubToken: string(ghToken),
})

issue, branchName, err := getIssueAndBranchName(config, issueBackend, &profile, issueID)
if err != nil {
return err
}

newIssue, err := createAndAddRepositoriesToIssue(config, &profile, issueID, issueDirPath, branchName, issue, repositories)
if err != nil {
return err
}

if err := config.AddIssue(newIssue); err != nil {
return err
}

Log.Infof("Started working on issue %v", issueID)
return nil
}

// addRepositoryToProfile adds repository to profile
func addRepositoryToProfile(profile *Profile, repoName string) error {
if err := profile.AddRepository((*RepoConfigName)(&repoName)); err != nil {
return err
}
return nil
}

// isIssueIdInUse checks if issue ID is already in use
func isIssueIdInUse(config *IssuectlConfig, issueID IssueID) bool {
_, found := config.GetIssue(issueID)
return found
}

// initializeIssueBackendAndDir prepares IssueBackend and creates directory for issue
func initializeIssueBackendAndDir(config *IssuectlConfig, profile *Profile, issueID IssueID) (IssueBackend, string, error) {
backendConfig := config.GetBackend(profile.Backend)
issueBackend, err := getIssueBackendConfigurator(backendConfig)
if err != nil {
return nil, "", err
}

repo := config.GetRepository(profile.DefaultRepository)
exists, err := issueBackend.IssueExists(repo.Owner, repo.Name, issueID)
if err != nil || !exists {
return fmt.Errorf("issue does not exist on GitHub: %v", err)
return nil, "", fmt.Errorf(errIssueDoesNotExistOnGitHub, err)
}
Log.V(2).Infof("Creating issue work dir")

issueDirPath, err := createDirectory(profile.WorkDir, string(issueID))
if err != nil {
return err
return nil, "", err
}

return issueBackend, issueDirPath, nil
}

// getIssueAndBranchName gets issue and prepares branch name
func getIssueAndBranchName(config *IssuectlConfig, issueBackend IssueBackend, profile *Profile, issueID IssueID) (interface{}, string, error) {
repo := config.GetRepository(profile.DefaultRepository)
issue, err := issueBackend.GetIssue(repo.Owner, repo.Name, issueID)
if err != nil {
return fmt.Errorf("failed to get the issue: %v", err)
return nil, "", fmt.Errorf(errFailedToGetIssue, err)
}

branchName := fmt.Sprintf("%v-%v", issueID, strings.ReplaceAll(*issue.(*github.Issue).Title, " ", "-"))
return issue, branchName, nil
}

Log.Infof("Cloning multiple repositories: %v", profile.Repositories)
// createAndAddRepositoriesToIssue prepares issue and clones repositories to it
func createAndAddRepositoriesToIssue(config *IssuectlConfig, profile *Profile, issueID IssueID, issueDirPath string, branchName string, issue interface{}, repositories []string) (*IssueConfig, error) {
newIssue := &IssueConfig{
Name: *issue.(*github.Issue).Title,
ID: issueID,
Expand All @@ -65,46 +150,53 @@ func StartWorkingOnIssue(config *IssuectlConfig, repositories []string, issueID
Dir: issueDirPath,
Profile: profile.Name,
}
for _, repoName := range profile.Repositories {
repo := config.GetRepository(*repoName)
Log.Infof("Cloning repo %v", repo.Name)
repoDirPath, err := cloneRepo(&repo, issueDirPath, &gitUser)

for _, repoName := range repositories {
err := cloneAndAddRepositoryToIssue(config, profile, newIssue, issueDirPath, branchName, repoName)
if err != nil {
return err
return nil, err
}
Log.V(2).Infof("Creating branch")
if err := createBranch(repoDirPath, branchName, &gitUser); err != nil {
return err
}
newIssue.Repositories = append(newIssue.Repositories, repo.Name)
}
if err := config.AddIssue(newIssue); err != nil {

return newIssue, nil
}

// cloneAndAddRepositoryToIssue clones repository and adds it to issue
func cloneAndAddRepositoryToIssue(config *IssuectlConfig, profile *Profile, issue *IssueConfig, issueDirPath string, branchName string, repoName string) error {
gitUser, _ := config.GetGitUser(profile.GitUserName)
repo := config.GetRepository(RepoConfigName(repoName))

Log.Infof("Cloning repo %v", repo.Name)

repoDirPath, err := cloneRepo(&repo, issueDirPath, &gitUser)
if err != nil {
return err
}

Log.Infof("Started working on issue %v", issueID)
Log.V(2).Infof("Creating branch")
if err := createBranch(repoDirPath, branchName, &gitUser); err != nil {
return err
}

issue.Repositories = append(issue.Repositories, repo.Name)
return nil
}

// OpenPullRequest opens pull request
func OpenPullRequest(issueID IssueID) error {
config := LoadConfig()
profile := config.GetProfile(config.GetCurrentProfile())

issue, found := config.GetIssue(issueID)
if !found {
return fmt.Errorf("issueID not found")
return fmt.Errorf(errIssueIDNotFound)
}

backendConfig := config.GetBackend(profile.Backend)
ghToken, err := base64.RawStdEncoding.DecodeString(backendConfig.Token)
repoBackend, err := getRepoBackendConfigurator(config.GetBackend(profile.Backend))
if err != nil {
return err
}
repoBackend := GetRepoBackend(&GetBackendConfig{
Type: backendConfig.Type,
GitHubApi: GitHubApi,
GitHubToken: string(ghToken),
})

repo := config.GetRepository(profile.DefaultRepository)
return repoBackend.OpenPullRequest(
repo.Owner,
Expand All @@ -116,34 +208,31 @@ func OpenPullRequest(issueID IssueID) error {
)
}

// FinishWorkingOnIssue finishes work on an issue
func FinishWorkingOnIssue(issueID IssueID) error {
config := LoadConfig()
profile := config.GetProfile(config.GetCurrentProfile())
repo := config.GetRepository(profile.DefaultRepository)
backendConfig := config.GetBackend(profile.Backend)
ghToken, err := base64.RawStdEncoding.DecodeString(backendConfig.Token)

issueBackend, err := getIssueBackendConfigurator(config.GetBackend(profile.Backend))
if err != nil {
return err
}
issueBackend := GetIssueBackend(&GetBackendConfig{
Type: backendConfig.Type,
GitHubToken: string(ghToken),
GitHubApi: GitHubApi,
})

err = issueBackend.CloseIssue(
repo.Owner,
repo.Name,
issueID,
)
if err != nil {
return fmt.Errorf("failed to close the issue: %v", err)
return fmt.Errorf(errFailedToCloseIssue, err)
}

Log.Infof("Cleaning up after work on issue %v", issueID)

issue, found := config.GetIssue(issueID)
if !found {
return errors.New("Issue not found")
return errors.New(errIssueIDNotFound)
}

if err := os.RemoveAll(issue.Dir); err != nil {
Expand Down

0 comments on commit c7341d1

Please sign in to comment.