Skip to content
This repository has been archived by the owner on Nov 1, 2022. It is now read-only.

Commit

Permalink
Merge pull request #735 from weaveworks/695b-include-user-in-git-commit
Browse files Browse the repository at this point in the history
695b include user in git commit
  • Loading branch information
tamarakaufler authored Sep 14, 2017
2 parents 6b1640e + 9f73679 commit 953c51c
Show file tree
Hide file tree
Showing 9 changed files with 312 additions and 28 deletions.
67 changes: 60 additions & 7 deletions cmd/fluxctl/args.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package main

import (
"os/user"
"bytes"
"fmt"
"os/exec"
"strings"

"github.com/spf13/cobra"

Expand All @@ -16,11 +19,61 @@ func parseServiceOption(s string) (update.ServiceSpec, error) {
}

func AddCauseFlags(cmd *cobra.Command, opts *update.Cause) {
username := ""
user, err := user.Current()
if err == nil {
username = user.Username
}
authorInfo := getUserGitconfig()
username := getCommitAuthor(authorInfo)

cmd.Flags().StringVarP(&opts.Message, "message", "m", "", "attach a message to the update")
cmd.Flags().StringVar(&opts.User, "user", username, "override the user reported as initating the update")
cmd.Flags().StringVar(&opts.User, "user", username, "override the user reported as initiating the update")
}

func getCommitAuthor(authorInfo map[string]string) string {
userName := authorInfo["user.name"]
userEmail := authorInfo["user.email"]

switch {
case userName != "" && userEmail != "":
return fmt.Sprintf("%s <%s>", userName, userEmail)
case userEmail != "":
return userEmail
case userName != "":
return userName
}
return ""
}

func getUserGitconfig() map[string]string {
var out bytes.Buffer
userGitconfig := make(map[string]string)
cmd := exec.Command("git", "config", "--list")
cmd.Stdout = &out

err := cmd.Run()
if err != nil {
return userGitconfig
}
res := out.String()
return userGitconfigMap(res)
}

func userGitconfigMap(s string) map[string]string {
c := make(map[string]string)
lines := splitList(s)
for _, l := range lines {
if l == "" {
continue
}
prop := strings.SplitN(l, "=", 2)
p := strings.TrimSpace(prop[0])
v := strings.TrimSpace(prop[1])
c[p] = v
}
return c
}

func splitList(s string) []string {
outStr := strings.TrimSpace(s)
if outStr == "" {
return []string{}
}
return strings.Split(outStr, "\n")
}
188 changes: 188 additions & 0 deletions cmd/fluxctl/args_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
package main

import (
"reflect"
"testing"
)

func TestUserGitconfigMap_EmptyString(t *testing.T) {
d := ""
userGitconfigInfo := userGitconfigMap(d)
if len(userGitconfigInfo) != 0 {
t.Fatal("expected map with no keys")
}
}

func TestUserGitconfigMap(t *testing.T) {
d := `push.default=simple
merge.conflictstyle=diff3
pull.ff=only
core.repositoryformatversion=0
core.filemode=true
core.bare=false`
expected := map[string]string{
"push.default": "simple",
"merge.conflictstyle": "diff3",
"pull.ff": "only",
"core.repositoryformatversion": "0",
"core.filemode": "true",
"core.bare": "false",
}

userGitconfigInfo := userGitconfigMap(d)
if len(userGitconfigInfo) != 6 {
t.Fatal("got map with unexpected number of keys")
}
if !reflect.DeepEqual(userGitconfigInfo, expected) {
t.Fatal("result does not match expected structure")
}
}

func TestUserGitconfigMap_WithEmptyLines(t *testing.T) {
d := `
user.name=Jane Doe
push.default=simple
merge.conflictstyle=diff3
pull.ff=only
core.repositoryformatversion=0
core.filemode=true
core.bare=false
`
expected := map[string]string{
"user.name": "Jane Doe",
"push.default": "simple",
"merge.conflictstyle": "diff3",
"pull.ff": "only",
"core.repositoryformatversion": "0",
"core.filemode": "true",
"core.bare": "false",
}
userGitconfigInfo := userGitconfigMap(d)

if len(userGitconfigInfo) != 7 {
t.Fatal("got map with unexpected number of keys")
}
if !reflect.DeepEqual(userGitconfigInfo, expected) {
t.Fatal("result does not match expected structure")
}
}

func TestUserGitconfigMap_WithNoKeys(t *testing.T) {
d := `
`
expected := make(map[string]string)

userGitconfigInfo := userGitconfigMap(d)
if len(userGitconfigInfo) != 0 {
t.Fatal("expected map with no keys")
}
if !reflect.DeepEqual(userGitconfigInfo, expected) {
t.Fatal("result does not match expected structure")
}
}

func TestGetCommitAuthor_BothNameAndEmail(t *testing.T) {
input := map[string]string{
"user.name": "Jane Doe",
"user.email": "jd@j.d",
"push.default": "simple",
"merge.conflictstyle": "diff3",
"pull.ff": "only",
"core.repositoryformatversion": "0",
"core.filemode": "true",
"core.bare": "false",
}
expected := "Jane Doe <jd@j.d>"
author := getCommitAuthor(input)
if author != expected {
t.Fatal("author did not match expected value")
}
}

func TestGetCommitAuthor_OnlyName(t *testing.T) {
input := map[string]string{
"user.name": "Jane Doe",
"push.default": "simple",
"merge.conflictstyle": "diff3",
"pull.ff": "only",
"core.repositoryformatversion": "0",
"core.filemode": "true",
"core.bare": "false",
}
expected := "Jane Doe"
author := getCommitAuthor(input)
if author != expected {
t.Fatal("author did not match expected value")
}
}

func TestGetCommitAuthor_OnlyEmail(t *testing.T) {
input := map[string]string{
"user.email": "jd@j.d",
"push.default": "simple",
"merge.conflictstyle": "diff3",
"pull.ff": "only",
"core.repositoryformatversion": "0",
"core.filemode": "true",
"core.bare": "false",
}
expected := "jd@j.d"
author := getCommitAuthor(input)
if author != expected {
t.Fatal("author did not match expected value")
}
}

func TestGetCommitAuthor_NoNameNoEmail(t *testing.T) {
input := map[string]string{
"push.default": "simple",
"merge.conflictstyle": "diff3",
"pull.ff": "only",
"core.repositoryformatversion": "0",
"core.filemode": "true",
"core.bare": "false",
}
expected := ""
author := getCommitAuthor(input)
if author != expected {
t.Fatal("author did not match expected value")
}
}

func TestGetCommitAuthor_NameAndEmptyEmail(t *testing.T) {
input := map[string]string{
"user.name": "Jane Doe",
"user.email": "",
"push.default": "simple",
"merge.conflictstyle": "diff3",
"pull.ff": "only",
"core.repositoryformatversion": "0",
"core.filemode": "true",
"core.bare": "false",
}
expected := "Jane Doe"
author := getCommitAuthor(input)
if author != expected {
t.Fatal("author did not match expected value")
}
}

func TestGetCommitAuthor_EmailAndEmptyName(t *testing.T) {
input := map[string]string{
"user.name": "",
"user.email": "jd@j.d",
"push.default": "simple",
"merge.conflictstyle": "diff3",
"pull.ff": "only",
"core.repositoryformatversion": "0",
"core.filemode": "true",
"core.bare": "false",
}
expected := "jd@j.d"
author := getCommitAuthor(input)
if author != expected {
t.Fatal("author did not match expected value")
}
}
18 changes: 11 additions & 7 deletions cmd/fluxd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"k8s.io/client-go/1.5/rest"

"context"

"github.com/weaveworks/flux"
"github.com/weaveworks/flux/cluster"
"github.com/weaveworks/flux/cluster/kubernetes"
Expand Down Expand Up @@ -71,12 +72,13 @@ func main() {
kubernetesKubectl = fs.String("kubernetes-kubectl", "", "Optional, explicit path to kubectl tool")
versionFlag = fs.Bool("version", false, "Get version number")
// Git repo & key etc.
gitURL = fs.String("git-url", "", "URL of git repo with Kubernetes manifests; e.g., git@github.com:weaveworks/flux-example")
gitBranch = fs.String("git-branch", "master", "branch of git repo to use for Kubernetes manifests")
gitPath = fs.String("git-path", "", "path within git repo to locate Kubernetes manifests (relative path)")
gitUser = fs.String("git-user", "Weave Flux", "username to use as git committer")
gitEmail = fs.String("git-email", "support@weave.works", "email to use as git committer")
gitLabel = fs.String("git-label", "", "label to keep track of sync progress; overrides both --git-sync-tag and --git-notes-ref")
gitURL = fs.String("git-url", "", "URL of git repo with Kubernetes manifests; e.g., git@github.com:weaveworks/flux-example")
gitBranch = fs.String("git-branch", "master", "branch of git repo to use for Kubernetes manifests")
gitPath = fs.String("git-path", "", "path within git repo to locate Kubernetes manifests (relative path)")
gitUser = fs.String("git-user", "Weave Flux", "username to use as git committer")
gitEmail = fs.String("git-email", "support@weave.works", "email to use as git committer")
gitSetAuthor = fs.Bool("git-set-author", false, "If set, the author of git commits will reflect the user who initiated the commit and will differ from the git committer.")
gitLabel = fs.String("git-label", "", "label to keep track of sync progress; overrides both --git-sync-tag and --git-notes-ref")
// Old git config; still used if --git-label is not supplied, but --git-label is preferred.
gitSyncTag = fs.String("git-sync-tag", defaultGitSyncTag, "tag to use to mark sync progress for this cluster")
gitNotesRef = fs.String("git-notes-ref", defaultGitNotesRef, "ref to use for keeping commit annotations in git notes")
Expand Down Expand Up @@ -360,6 +362,7 @@ func main() {
NotesRef: *gitNotesRef,
UserName: *gitUser,
UserEmail: *gitEmail,
SetAuthor: *gitSetAuthor,
}

for checkout == nil {
Expand All @@ -382,7 +385,8 @@ func main() {
"user", *gitUser,
"email", *gitEmail,
"sync-tag", *gitSyncTag,
"notes-ref", *gitNotesRef)
"notes-ref", *gitNotesRef,
"set-author", *gitSetAuthor)
checkout = working
}
}
Expand Down
17 changes: 14 additions & 3 deletions daemon/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/pkg/errors"

"context"

"github.com/weaveworks/flux"
"github.com/weaveworks/flux/cluster"
// fluxerr "github.com/weaveworks/flux/errors"
Expand Down Expand Up @@ -252,7 +253,12 @@ func (d *Daemon) updatePolicy(spec update.Spec, updates policy.Updates) DaemonJo
return metadata, nil
}

if err := working.CommitAndPush(ctx, policyCommitMessage(updates, spec.Cause), &git.Note{JobID: jobID, Spec: spec}); err != nil {
commitAuthor := ""
if d.Checkout.Config.SetAuthor {
commitAuthor = spec.Cause.User
}
commitAction := &git.CommitAction{Author: commitAuthor, Message: policyCommitMessage(updates, spec.Cause)}
if err := working.CommitAndPush(ctx, commitAction, &git.Note{JobID: jobID, Spec: spec}); err != nil {
// On the chance pushing failed because it was not
// possible to fast-forward, ask for a sync so the
// next attempt is more likely to succeed.
Expand Down Expand Up @@ -286,7 +292,12 @@ func (d *Daemon) release(spec update.Spec, c release.Changes) DaemonJobFunc {
if commitMsg == "" {
commitMsg = c.CommitMessage()
}
if err := working.CommitAndPush(ctx, commitMsg, &git.Note{JobID: jobID, Spec: spec, Result: result}); err != nil {
commitAuthor := ""
if d.Checkout.Config.SetAuthor {
commitAuthor = spec.Cause.User
}
commitAction := &git.CommitAction{Author: commitAuthor, Message: commitMsg}
if err := working.CommitAndPush(ctx, commitAction, &git.Note{JobID: jobID, Spec: spec, Result: result}); err != nil {
// On the chance pushing failed because it was not
// possible to fast-forward, ask for a sync so the
// next attempt is more likely to succeed.
Expand Down Expand Up @@ -315,7 +326,7 @@ func (d *Daemon) SyncNotify() error {
return nil
}

// Ask the daemon how far it's got committing things; in particular, is the job
// JobStatus - Ask the daemon how far it's got committing things; in particular, is the job
// queued? running? committed? If it is done, the commit ref is returned.
func (d *Daemon) JobStatus(jobID job.ID) (job.Status, error) {
ctx, cancel := context.WithTimeout(context.Background(), defaultHandlerTimeout)
Expand Down
5 changes: 4 additions & 1 deletion daemon/loop_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/go-kit/kit/log"

"context"

"github.com/weaveworks/flux"
"github.com/weaveworks/flux/cluster"
"github.com/weaveworks/flux/cluster/kubernetes"
Expand Down Expand Up @@ -206,7 +207,9 @@ func TestDoSync_WithNewCommit(t *testing.T) {
}); err != nil {
t.Fatal(err)
}
if err := d.Checkout.CommitAndPush(context.Background(), "test commit", nil); err != nil {

commitAction := &git.CommitAction{Author: "", Message: "test commit"}
if err := d.Checkout.CommitAndPush(context.Background(), commitAction, nil); err != nil {
t.Fatal(err)
}
newRevision, err := d.Checkout.HeadRevision(context.Background())
Expand Down
Loading

0 comments on commit 953c51c

Please sign in to comment.