Skip to content

Commit b3a7e54

Browse files
authored
Merge pull request #81 from pipedrive/update-status-with-remote
Update status with remote
2 parents 1507658 + 14c6c65 commit b3a7e54

File tree

8 files changed

+250
-29
lines changed

8 files changed

+250
-29
lines changed

.vscode/launch.json

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
// Use IntelliSense to learn about possible attributes.
3+
// Hover to view descriptions of existing attributes.
4+
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5+
"version": "0.2.0",
6+
"configurations": [
7+
{
8+
"name": "Launch",
9+
"type": "go",
10+
"request": "launch",
11+
"mode": "debug",
12+
"program": "${workspaceRoot}",
13+
"buildFlags": "-ldflags=-X=main.version=0.0.28",
14+
"env": {
15+
"GITHUB_API_TOKEN":"x"
16+
},
17+
"cwd": "x",
18+
"args": [
19+
"sync"
20+
],
21+
"port": 8080,
22+
"host": "127.0.0.1"
23+
}
24+
]
25+
}

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# changelog
22

3+
2021-05-14 - v0.0.29
4+
5+
- Adds - Command `mp sync` to sync local status with remote status [#81](https://github.com/Clever/microplane/pull/81)
6+
- Changes - Command `mp status` has flag `--sync` to sync local status with remote status [#81](https://github.com/Clever/microplane/pull/81)
7+
38
2021-03-22 - v0.0.28
49

510
- Adds MVP support for Github enterprise (testing pending). [#74](https://github.com/Clever/microplane/pull/74)

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.0.28
1+
0.0.29

cmd/root.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ func init() {
4747
pushCmd.Flags().StringSliceVarP(&pushFlagLabels, "labels", "l", nil, "labels to attach to PR")
4848

4949
rootCmd.AddCommand(statusCmd)
50+
statusCmd.Flags().BoolP("sync", "s", false, "Sync workflow status with repo origin")
51+
52+
rootCmd.AddCommand(syncCmd)
5053

5154
rootCmd.AddCommand(initCmd)
5255

cmd/status.go

Lines changed: 23 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99

1010
"github.com/Clever/microplane/clone"
1111
"github.com/Clever/microplane/initialize"
12+
"github.com/Clever/microplane/lib"
1213
"github.com/Clever/microplane/merge"
1314
"github.com/Clever/microplane/plan"
1415
"github.com/Clever/microplane/push"
@@ -33,30 +34,22 @@ var statusCmd = &cobra.Command{
3334
log.Fatalf("error loading init.json: %s\n", err.Error())
3435
}
3536

36-
singleRepo, err := cmd.Flags().GetString("repo")
37-
if err == nil && singleRepo != "" {
38-
valid := false
39-
validRepoNames := []string{}
40-
for _, r := range initOutput.Repos {
41-
if r.Name == singleRepo {
42-
valid = true
43-
break
44-
}
45-
validRepoNames = append(validRepoNames, r.Name)
46-
}
47-
if !valid {
48-
log.Fatalf("%s not a targeted repo name (valid target repos are: %s)", singleRepo, strings.Join(validRepoNames, ", "))
49-
}
50-
isSingleRepo = true
37+
repos, err := whichRepos(cmd)
38+
if err != nil {
39+
log.Fatal(err)
5140
}
52-
53-
repos := []string{}
54-
for _, r := range initOutput.Repos {
55-
if singleRepo != "" && r.Name != singleRepo {
56-
continue
41+
sync, err := cmd.Flags().GetBool("sync")
42+
if err != nil {
43+
log.Fatal(err)
44+
}
45+
if sync {
46+
err = parallelize(repos, syncOneRepo)
47+
if err != nil {
48+
// TODO: dig into errors and display them with more detail
49+
log.Fatal(err)
5750
}
58-
repos = append(repos, r.Name)
5951
}
52+
6053
printStatus(repos)
6154
},
6255
}
@@ -76,7 +69,7 @@ func joinWithTab(s ...string) string {
7669
return strings.Join(s, "\t")
7770
}
7871

79-
func printStatus(repos []string) {
72+
func printStatus(repos []lib.Repo) {
8073
out := tabWriterWithDefaults()
8174
fmt.Fprintln(out, joinWithTab("REPO", "STATUS", "DETAILS"))
8275
for _, r := range repos {
@@ -86,19 +79,20 @@ func printStatus(repos []string) {
8679
if len(d3) > 150 {
8780
d3 = d3[:150] + "..."
8881
}
89-
fmt.Fprintln(out, joinWithTab(r, status, d3))
82+
fmt.Fprintln(out, joinWithTab(r.Name, status, d3))
9083
}
9184
out.Flush()
9285
}
9386

94-
func getRepoStatus(repo string) (status, details string) {
87+
func getRepoStatus(repo lib.Repo) (status, details string) {
88+
repoName := repo.Name
9589
status = "initialized"
9690
details = ""
9791
var cloneOutput struct {
9892
clone.Output
9993
Error string
10094
}
101-
if !(loadJSON(outputPath(repo, "clone"), &cloneOutput) == nil && cloneOutput.Success) {
95+
if !(loadJSON(outputPath(repoName, "clone"), &cloneOutput) == nil && cloneOutput.Success) {
10296
if cloneOutput.Error != "" {
10397
details = color.RedString("(clone error) ") + cloneOutput.Error
10498
}
@@ -110,7 +104,7 @@ func getRepoStatus(repo string) (status, details string) {
110104
plan.Output
111105
Error string
112106
}
113-
if !(loadJSON(outputPath(repo, "plan"), &planOutput) == nil && planOutput.Success) {
107+
if !(loadJSON(outputPath(repoName, "plan"), &planOutput) == nil && planOutput.Success) {
114108
if planOutput.Error != "" {
115109
details = color.RedString("(plan error) ") + planOutput.Error
116110
}
@@ -129,7 +123,7 @@ func getRepoStatus(repo string) (status, details string) {
129123
push.Output
130124
Error string
131125
}
132-
if !(loadJSON(outputPath(repo, "push"), &pushOutput) == nil && pushOutput.Success) {
126+
if !(loadJSON(outputPath(repoName, "push"), &pushOutput) == nil && pushOutput.Success) {
133127
if pushOutput.Error != "" {
134128
details = color.RedString("(push error) ") + pushOutput.Error
135129
}
@@ -142,7 +136,8 @@ func getRepoStatus(repo string) (status, details string) {
142136
merge.Output
143137
Error string
144138
}
145-
if !(loadJSON(outputPath(repo, "merge"), &mergeOutput) == nil && mergeOutput.Success) {
139+
// check PR was merged
140+
if !(loadJSON(outputPath(repoName, "merge"), &mergeOutput) == nil && mergeOutput.Success) {
146141
if mergeOutput.Error != "" {
147142
details = color.RedString("(merge error) ") + mergeOutput.Error
148143
}

cmd/sync.go

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package cmd
2+
3+
import (
4+
"context"
5+
"log"
6+
"os"
7+
"path/filepath"
8+
9+
"github.com/Clever/microplane/initialize"
10+
"github.com/Clever/microplane/lib"
11+
"github.com/Clever/microplane/merge"
12+
"github.com/Clever/microplane/push"
13+
"github.com/Clever/microplane/sync"
14+
"github.com/spf13/cobra"
15+
)
16+
17+
var syncCmd = &cobra.Command{
18+
Use: "sync",
19+
Short: "Sync workflow status with remote repo",
20+
Run: func(cmd *cobra.Command, args []string) {
21+
// find files and folders to explain the status of each repo
22+
initPath := outputPath("", "init")
23+
24+
if _, err := os.Stat(initPath); os.IsNotExist(err) {
25+
log.Fatalf("must run init first: %s\n", err.Error())
26+
}
27+
28+
var initOutput initialize.Output
29+
if err := loadJSON(initPath, &initOutput); err != nil {
30+
log.Fatalf("error loading init.json: %s\n", err.Error())
31+
}
32+
33+
repos, err := whichRepos(cmd)
34+
if err != nil {
35+
log.Fatal(err)
36+
}
37+
38+
err = parallelize(repos, syncOneRepo)
39+
if err != nil {
40+
// TODO: dig into errors and display them with more detail
41+
log.Fatal(err)
42+
}
43+
},
44+
}
45+
46+
func syncOneRepo(r lib.Repo, ctx context.Context) error {
47+
log.Printf("syncing: %s/%s", r.Owner, r.Name)
48+
repoName := r.Name
49+
50+
var pushOutput struct {
51+
push.Output
52+
Error string
53+
}
54+
55+
if !(loadJSON(outputPath(repoName, "push"), &pushOutput) == nil && pushOutput.Success) {
56+
return nil
57+
}
58+
output, err := syncPush(r, ctx, pushOutput.Output)
59+
if err != nil {
60+
return err
61+
}
62+
63+
var mergeOutput struct {
64+
merge.Output
65+
Error string
66+
}
67+
68+
if loadJSON(outputPath(repoName, "merge"), &mergeOutput) == nil && mergeOutput.Success {
69+
return nil
70+
}
71+
72+
if err := syncMerge(r, ctx, output); err != nil {
73+
return err
74+
}
75+
76+
log.Printf("synced: %s/%s", r.Owner, r.Name)
77+
return nil
78+
}
79+
80+
func syncPush(r lib.Repo, ctx context.Context, pushOutput push.Output) (sync.Output, error) {
81+
82+
var output sync.Output
83+
var err error
84+
if r.IsGitlab() {
85+
output, err = sync.GitlabSyncPush(ctx, r, pushOutput, repoLimiter)
86+
} else if r.IsGithub() {
87+
output, err = sync.GithubSyncPush(ctx, r, pushOutput, repoLimiter)
88+
}
89+
if err != nil {
90+
return sync.Output{}, err
91+
}
92+
pushOutput.CommitSHA = output.CommitSHA
93+
pushOutput.PullRequestCombinedStatus = output.PullRequestCombinedStatus
94+
95+
writeJSON(pushOutput, outputPath(r.Name, "push"))
96+
return output, nil
97+
}
98+
func syncMerge(r lib.Repo, ctx context.Context, output sync.Output) error {
99+
if !output.Merged {
100+
return nil
101+
}
102+
103+
mergeOutputPath := outputPath(r.Name, "merge")
104+
mergeWorkDir := filepath.Dir(mergeOutputPath)
105+
if err := os.MkdirAll(mergeWorkDir, 0755); err != nil {
106+
return err
107+
}
108+
109+
writeJSON(merge.Output{
110+
Success: true,
111+
MergeCommitSHA: output.MergeCommitSHA,
112+
}, mergeOutputPath)
113+
return nil
114+
}

sync/syncGithub.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package sync
2+
3+
import (
4+
"context"
5+
"time"
6+
7+
"github.com/Clever/microplane/lib"
8+
"github.com/Clever/microplane/push"
9+
)
10+
11+
type Output struct {
12+
CommitSHA string
13+
PullRequestCombinedStatus string
14+
MergeCommitSHA string
15+
Merged bool
16+
}
17+
18+
func GithubSyncPush(ctx context.Context, r lib.Repo, po push.Output, repoLimiter *time.Ticker) (Output, error) {
19+
// Create Github Client
20+
p := lib.NewProviderFromConfig(r.ProviderConfig)
21+
client, err := p.GithubClient(ctx)
22+
if err != nil {
23+
return Output{}, err
24+
}
25+
26+
pr, _, err := client.PullRequests.Get(ctx, r.Owner, r.Name, po.PullRequestNumber)
27+
if err != nil {
28+
return Output{}, err
29+
}
30+
31+
<-repoLimiter.C
32+
cs, _, err := client.Repositories.GetCombinedStatus(ctx, r.Owner, r.Name, *pr.Head.SHA, nil)
33+
if err != nil {
34+
return Output{}, err
35+
}
36+
37+
return Output{
38+
CommitSHA: *pr.Head.SHA,
39+
PullRequestCombinedStatus: *cs.State,
40+
MergeCommitSHA: *pr.MergeCommitSHA,
41+
Merged: *pr.Merged,
42+
}, nil
43+
}

sync/syncGitlab.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package sync
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"time"
7+
8+
"github.com/Clever/microplane/lib"
9+
"github.com/Clever/microplane/push"
10+
"github.com/xanzy/go-gitlab"
11+
)
12+
13+
func GitlabSyncPush(ctx context.Context, r lib.Repo, po push.Output, repoLimiter *time.Ticker) (Output, error) {
14+
// Create client
15+
p := lib.NewProviderFromConfig(r.ProviderConfig)
16+
client, err := p.GitlabClient()
17+
if err != nil {
18+
return Output{}, err
19+
}
20+
pid := fmt.Sprintf("%s/%s", r.Owner, r.Name)
21+
mr, _, err := client.MergeRequests.GetMergeRequest(pid, po.PullRequestNumber, nil)
22+
if err != nil {
23+
return Output{}, err
24+
}
25+
pipelineStatus, err := push.GetPipelineStatus(client, r.Owner, r.Name, &gitlab.ListProjectPipelinesOptions{SHA: &mr.SHA})
26+
if err != nil {
27+
return Output{}, err
28+
}
29+
30+
return Output{
31+
CommitSHA: mr.SHA,
32+
PullRequestCombinedStatus: pipelineStatus,
33+
MergeCommitSHA: mr.MergeCommitSHA,
34+
Merged: mr.MergeStatus == "merged",
35+
}, nil
36+
}

0 commit comments

Comments
 (0)