Skip to content

Commit e4b3be0

Browse files
authored
Add ability to specify the commit message (and use this message in the PR details), also enhance the default message. (#49)
1 parent 092e07d commit e4b3be0

File tree

6 files changed

+91
-38
lines changed

6 files changed

+91
-38
lines changed

cmd/services/main.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ const (
2121
toFlag = "to"
2222
serviceFlag = "service"
2323
cacheDirFlag = "cache-dir"
24+
msgFlag = "commit-message"
2425
nameFlag = "commit-name"
2526
emailFlag = "commit-email"
2627
debugFlag = "debug"
@@ -77,6 +78,12 @@ var (
7778
Required: false,
7879
EnvVars: []string{"COMMIT_EMAIL"},
7980
},
81+
&cli.StringFlag{
82+
Name: msgFlag,
83+
Usage: "the msg to use on the resultant commit and pull request",
84+
Required: false,
85+
EnvVars: []string{"COMMIT_MSG"},
86+
},
8087
&cli.BoolFlag{
8188
Name: debugFlag,
8289
Usage: "additional debug logging output",
@@ -121,6 +128,7 @@ func promoteAction(c *cli.Context) error {
121128
newBranchName := c.String(branchNameFlag)
122129
debug := c.Bool(debugFlag)
123130
keepCache := c.Bool(keepCacheFlag)
131+
msg := c.String(msgFlag)
124132

125133
cacheDir, err := homedir.Expand(c.String(cacheDirFlag))
126134
if err != nil {
@@ -131,7 +139,7 @@ func promoteAction(c *cli.Context) error {
131139
if err != nil {
132140
return fmt.Errorf("unable to establish credentials: %w", err)
133141
}
134-
return avancement.New(cacheDir, author, avancement.WithDebug(debug)).Promote(service, fromRepo, toRepo, newBranchName, keepCache)
142+
return avancement.New(cacheDir, author, avancement.WithDebug(debug)).Promote(service, fromRepo, toRepo, newBranchName, msg, keepCache)
135143
}
136144

137145
func newAuthor(c *cli.Context) (*git.Author, error) {

pkg/avancement/pull_request.go

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,28 @@ import (
1010
// TODO: OptionFuncs for Base and Title?
1111
// TODO: For the Head, should this try and determine whether or not this is a
1212
// fork ("user" of both repoURLs) and if so, simplify the Head?
13-
func makePullRequestInput(fromURL, toURL, branchName string) (*scm.PullRequestInput, error) {
14-
_, fromRepo, err := util.ExtractUserAndRepo(fromURL)
15-
if err != nil {
16-
return nil, err
17-
}
13+
func makePullRequestInput(fromLocal bool, fromURL, toURL, branchName, prBody string) (*scm.PullRequestInput, error) {
14+
var title string
15+
1816
fromUser, toRepo, err := util.ExtractUserAndRepo(toURL)
1917
if err != nil {
2018
return nil, err
2119
}
22-
title := fmt.Sprintf("promotion from %s to %s", fromRepo, toRepo)
20+
21+
if fromLocal {
22+
title = fmt.Sprintf("promotion from local filesystem directory to %s", toRepo)
23+
} else {
24+
_, fromRepo, err := util.ExtractUserAndRepo(fromURL)
25+
if err != nil {
26+
return nil, err
27+
}
28+
title = fmt.Sprintf("promotion from %s to %s", fromRepo, toRepo)
29+
}
30+
2331
return &scm.PullRequestInput{
2432
Title: title,
2533
Head: fmt.Sprintf("%s:%s", fromUser, branchName),
2634
Base: "master",
27-
Body: "this is a test body",
35+
Body: prBody,
2836
}, nil
2937
}

pkg/avancement/pull_request_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import (
99
)
1010

1111
func TestMakePullRequestInput(t *testing.T) {
12-
pr, err := makePullRequestInput("https://example.com/project/dev-env.git", "https://example.com/project/prod-env.git", "my-test-branch")
12+
pr, err := makePullRequestInput(false, "https://example.com/project/dev-env.git", "https://example.com/project/prod-env.git", "my-test-branch", "foo bar wibble")
1313
if err != nil {
1414
t.Fatal(err)
1515
}
@@ -18,7 +18,7 @@ func TestMakePullRequestInput(t *testing.T) {
1818
Title: "promotion from dev-env to prod-env",
1919
Head: "project:my-test-branch",
2020
Base: "master",
21-
Body: "this is a test body",
21+
Body: "foo bar wibble",
2222
}
2323

2424
if diff := cmp.Diff(want, pr); diff != "" {

pkg/avancement/service_manager.go

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,6 @@ type ServiceManager struct {
2626
debug bool
2727
}
2828

29-
const (
30-
defaultCommitMsg = "this is a commit"
31-
)
32-
3329
type scmClientFactory func(token string) *scm.Client
3430
type repoFactory func(url, localPath string, debug bool) (git.Repo, error)
3531
type localFactory func(localPath string, debug bool) git.Source
@@ -75,7 +71,7 @@ const fromBranch = "master"
7571
//
7672
// It uses a Git cache to checkout the code to, and will copy the environment
7773
// configuration for the `fromURL` to the `toURL` in a named branch.
78-
func (s *ServiceManager) Promote(serviceName, fromURL, toURL, newBranchName string, keepCache bool) error {
74+
func (s *ServiceManager) Promote(serviceName, fromURL, toURL, newBranchName, message string, keepCache bool) error {
7975
var source, destination git.Repo
8076
var reposToDelete []git.Repo
8177

@@ -92,7 +88,8 @@ func (s *ServiceManager) Promote(serviceName, fromURL, toURL, newBranchName stri
9288

9389
var localSource git.Source
9490
var errorSource error
95-
if fromLocalRepo(fromURL) {
91+
isLocal := fromLocalRepo(fromURL)
92+
if isLocal {
9693
localSource = s.localFactory(fromURL, s.debug)
9794
} else {
9895
source, errorSource = s.checkoutSourceRepo(fromURL, fromBranch)
@@ -113,7 +110,7 @@ func (s *ServiceManager) Promote(serviceName, fromURL, toURL, newBranchName stri
113110
reposToDelete = append(reposToDelete, destination)
114111

115112
var copied []string
116-
if fromLocalRepo(fromURL) {
113+
if isLocal {
117114
copied, err = local.CopyConfig(serviceName, localSource, destination)
118115
if err != nil {
119116
return fmt.Errorf("failed to setup local repo: %w", err)
@@ -125,18 +122,28 @@ func (s *ServiceManager) Promote(serviceName, fromURL, toURL, newBranchName stri
125122
}
126123
}
127124

125+
commitMsg := message
126+
if commitMsg == "" {
127+
if isLocal {
128+
commitMsg = fmt.Sprintf("Promotion of service `%s` from local filesystem directory `%s`.", serviceName, fromURL)
129+
} else {
130+
commitMsg = generateDefaultCommitMsg(source, serviceName, fromURL, fromBranch)
131+
}
132+
}
133+
128134
if err := destination.StageFiles(copied...); err != nil {
129135
return fmt.Errorf("failed to stage files: %w", err)
130136
}
131-
if err := destination.Commit(defaultCommitMsg, s.author); err != nil {
137+
if err := destination.Commit(commitMsg, s.author); err != nil {
132138
return fmt.Errorf("failed to commit: %w", err)
133139
}
140+
134141
if err := destination.Push(newBranchName); err != nil {
135142
return fmt.Errorf("failed to push: %w", err)
136143
}
137144

138145
ctx := context.Background()
139-
pr, err := createPullRequest(ctx, fromURL, toURL, newBranchName, s.clientFactory(s.author.Token))
146+
pr, err := createPullRequest(ctx, fromURL, toURL, newBranchName, commitMsg, s.clientFactory(s.author.Token), isLocal)
140147
if err != nil {
141148
return fmt.Errorf("failed to create a pull-request for branch %s in %v: %w", newBranchName, toURL, err)
142149
}
@@ -185,13 +192,12 @@ func (s *ServiceManager) cloneRepo(repoURL, branch string) (git.Repo, error) {
185192
return repo, nil
186193
}
187194

188-
func createPullRequest(ctx context.Context, fromURL, toURL, newBranchName string, client *scm.Client) (*scm.PullRequest, error) {
189-
prInput, err := makePullRequestInput(fromURL, toURL, newBranchName)
195+
func createPullRequest(ctx context.Context, fromURL, toURL, newBranchName, commitMsg string, client *scm.Client, fromLocal bool) (*scm.PullRequest, error) {
196+
prInput, err := makePullRequestInput(fromLocal, fromURL, toURL, newBranchName, commitMsg)
190197
if err != nil {
191198
// TODO: improve this error message
192199
return nil, err
193200
}
194-
195201
user, repo, err := util.ExtractUserAndRepo(toURL)
196202
if err != nil {
197203
// TODO: improve this error message
@@ -218,6 +224,7 @@ func addCredentialsIfNecessary(s string, a *git.Author) (string, error) {
218224
parsed.User = url.UserPassword("promotion", a.Token)
219225
return parsed.String(), nil
220226
}
227+
221228
func fromLocalRepo(s string) bool {
222229
parsed, err := url.Parse(s)
223230
if err != nil || parsed.Scheme == "" {
@@ -230,6 +237,13 @@ func generateBranch(repo git.Repo) string {
230237
uniqueString := uuid.New()
231238
runes := []rune(uniqueString.String())
232239
branchName := repo.GetName() + "-" + repo.GetCommitID() + "-" + string(runes[0:5])
233-
branchName = strings.Replace(branchName, "\n","",-1)
240+
branchName = strings.Replace(branchName, "\n", "", -1)
234241
return branchName
235242
}
243+
244+
// generateDefaultCommitMsg constructs a default commit message based on the source information.
245+
func generateDefaultCommitMsg(sourceRepo git.Repo, serviceName, from, fromBranch string) string {
246+
commit := sourceRepo.GetCommitID()
247+
msg := fmt.Sprintf("Promoting service `%s` at commit `%s` from branch `%s` in `%s`.", serviceName, commit, fromBranch, from)
248+
return msg
249+
}

pkg/avancement/service_manager_test.go

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@ package avancement
22

33
import (
44
"errors"
5+
"fmt"
56
"path"
67
"path/filepath"
8+
"regexp"
79
"strings"
810
"testing"
9-
"regexp"
1011

1112
"github.com/jenkins-x/go-scm/scm"
1213
fakescm "github.com/jenkins-x/go-scm/scm/driver/fake"
@@ -21,14 +22,18 @@ const (
2122
)
2223

2324
func TestPromoteWithSuccessKeepCacheTrue(t *testing.T) {
24-
promoteWithSuccess(t, true)
25+
promoteWithSuccess(t, true, "")
2526
}
2627

2728
func TestPromoteWithSuccessKeepCacheFalse(t *testing.T) {
28-
promoteWithSuccess(t, false)
29+
promoteWithSuccess(t, false, "")
30+
}
31+
32+
func TestPromoteWithSuccessCustomMsg(t *testing.T) {
33+
promoteLocalWithSuccess(t, true, "custom commit message here")
2934
}
3035

31-
func promoteWithSuccess(t *testing.T, keepCache bool) {
36+
func promoteWithSuccess(t *testing.T, keepCache bool, msg string) {
3237
dstBranch := "test-branch"
3338
author := &git.Author{Name: "Testing User", Email: "[email protected]", Token: "test-token"}
3439
devRepo, stagingRepo := mock.New("/dev", "master"), mock.New("/staging", "master")
@@ -46,14 +51,20 @@ func promoteWithSuccess(t *testing.T, keepCache bool) {
4651
}
4752
devRepo.AddFiles("/services/my-service/base/config/myfile.yaml")
4853

49-
err := sm.Promote("my-service", dev, staging, dstBranch, keepCache)
54+
err := sm.Promote("my-service", dev, staging, dstBranch, msg, keepCache)
5055
if err != nil {
5156
t.Fatal(err)
5257
}
5358

59+
expectedCommitMsg := msg
60+
if msg == "" {
61+
commit := devRepo.GetCommitID()
62+
expectedCommitMsg = fmt.Sprintf("Promoting service `my-service` at commit `%s` from branch `master` in `%s`.", commit, dev)
63+
}
64+
5465
stagingRepo.AssertBranchCreated(t, "master", dstBranch)
5566
stagingRepo.AssertFileCopiedInBranch(t, dstBranch, "/dev/services/my-service/base/config/myfile.yaml", "/staging/services/my-service/base/config/myfile.yaml")
56-
stagingRepo.AssertCommit(t, dstBranch, defaultCommitMsg, author)
67+
stagingRepo.AssertCommit(t, dstBranch, expectedCommitMsg, author)
5768
stagingRepo.AssertPush(t, dstBranch)
5869

5970
if keepCache {
@@ -66,14 +77,18 @@ func promoteWithSuccess(t *testing.T, keepCache bool) {
6677
}
6778

6879
func TestPromoteLocalWithSuccessKeepCacheFalse(t *testing.T) {
69-
promoteLocalWithSuccess(t, false)
80+
promoteLocalWithSuccess(t, false, "")
7081
}
7182

7283
func TestPromoteLocalWithSuccessKeepCacheTrue(t *testing.T) {
73-
promoteLocalWithSuccess(t, true)
84+
promoteLocalWithSuccess(t, true, "")
7485
}
7586

76-
func promoteLocalWithSuccess(t *testing.T, keepCache bool) {
87+
func TestPromoteLocalWithSuccessCustomMsg(t *testing.T) {
88+
promoteLocalWithSuccess(t, true, "custom commit message supplied")
89+
}
90+
91+
func promoteLocalWithSuccess(t *testing.T, keepCache bool, msg string) {
7792
dstBranch := "test-branch"
7893
author := &git.Author{Name: "Testing User", Email: "[email protected]", Token: "test-token"}
7994
stagingRepo := mock.New("/staging", "master")
@@ -93,14 +108,19 @@ func promoteLocalWithSuccess(t *testing.T, keepCache bool) {
93108
sm.debug = true
94109
devRepo.AddFiles("/config/myfile.yaml")
95110

96-
err := sm.Promote("my-service", ldev, staging, dstBranch, keepCache)
111+
err := sm.Promote("my-service", ldev, staging, dstBranch, msg, keepCache)
97112
if err != nil {
98113
t.Fatal(err)
99114
}
100115

116+
expectedCommitMsg := msg
117+
if expectedCommitMsg == "" {
118+
expectedCommitMsg = "Promotion of service `my-service` from local filesystem directory `/root/repo`."
119+
}
120+
101121
stagingRepo.AssertBranchCreated(t, "master", dstBranch)
102122
stagingRepo.AssertFileCopiedInBranch(t, dstBranch, "/dev/config/myfile.yaml", "/staging/services/my-service/base/config/myfile.yaml")
103-
stagingRepo.AssertCommit(t, dstBranch, defaultCommitMsg, author)
123+
stagingRepo.AssertCommit(t, dstBranch, expectedCommitMsg, author)
104124
stagingRepo.AssertPush(t, dstBranch)
105125

106126
if keepCache {
@@ -161,22 +181,25 @@ func TestPromoteWithCacheDeletionFailure(t *testing.T) {
161181
}
162182
devRepo.AddFiles("/services/my-service/base/config/myfile.yaml")
163183

164-
err := sm.Promote("my-service", dev, staging, dstBranch, false)
184+
err := sm.Promote("my-service", dev, staging, dstBranch, "", false)
165185
if err != nil {
166186
t.Fatal(err)
167187
}
168188

189+
commit := devRepo.GetCommitID()
190+
expectedCommitMsg := fmt.Sprintf("Promoting service `my-service` at commit `%s` from branch `master` in `%s`.", commit, dev)
191+
169192
stagingRepo.AssertBranchCreated(t, "master", dstBranch)
170193
stagingRepo.AssertFileCopiedInBranch(t, dstBranch, "/dev/services/my-service/base/config/myfile.yaml", "/staging/services/my-service/base/config/myfile.yaml")
171-
stagingRepo.AssertCommit(t, dstBranch, defaultCommitMsg, author)
194+
stagingRepo.AssertCommit(t, dstBranch, expectedCommitMsg, author)
172195
stagingRepo.AssertPush(t, dstBranch)
173196

174197
stagingRepo.AssertNotDeletedFromCache(t)
175198
devRepo.AssertDeletedFromCache(t)
176199
}
177200

178201
func TestGenerateBranchWithSuccess(t *testing.T) {
179-
repo := mock.New("/dev", "master")
202+
repo := mock.New("/dev", "master")
180203
GenerateBranchWithSuccess(t, repo)
181204
}
182205

pkg/git/mock/mock.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ func (m *Repository) AssertFileCopiedInBranch(t *testing.T, branch, from, name s
186186
// message and auth token.
187187
func (m *Repository) AssertCommit(t *testing.T, branch, msg string, a *git.Author) {
188188
if !hasString(key(branch, msg, a.Token), m.commits) {
189-
t.Fatalf("no matching commit %#v in branch %s using token %s", msg, branch, a.Token)
189+
t.Fatalf("no matching commit %#v in branch %s using token %s. Commits available: %+v", msg, branch, a.Token, m.commits)
190190
}
191191
}
192192

0 commit comments

Comments
 (0)