Browse Source

Add ability to append [ci skip] to commit messages

Appending `[ci skip]` to a commit message will instruct CI to skip the
build for that commit, at least in these systems:

 - https://circleci.com/docs/1.0/skip-a-build/
 - https://docs.travis-ci.com/user/customizing-the-build/#Skipping-a-build
 - https://docs.gitlab.com/ee/ci/yaml/#skipping-jobs

So: I've added a flag which switches that will do exactly that, to all
commits fluxd creates. To account for other CI systems that have a
similar mechanism but don't use that convention, the flag
`--git-ci-skip-message` lets you supply the text to append.
Michael Bridgen 1 year ago
parent
commit
e9e6195849
9 changed files with 107 additions and 35 deletions
  1. 19
    11
      cmd/fluxd/main.go
  2. 2
    2
      daemon/daemon.go
  3. 1
    1
      daemon/loop_test.go
  4. 19
    9
      git/gittest/repo.go
  5. 50
    2
      git/gittest/repo_test.go
  6. 1
    1
      git/operations.go
  7. 12
    8
      git/working.go
  8. 2
    0
      site/daemon.md
  9. 1
    1
      sync/sync_test.go

+ 19
- 11
cmd/fluxd/main.go View File

@@ -48,8 +48,9 @@ const (
48 48
 
49 49
 	// There are running systems that assume these defaults (by not
50 50
 	// supplying a value for one or both). Don't change them.
51
-	defaultGitSyncTag  = "flux-sync"
52
-	defaultGitNotesRef = "flux"
51
+	defaultGitSyncTag     = "flux-sync"
52
+	defaultGitNotesRef    = "flux"
53
+	defaultGitSkipMessage = "\n\n[ci skip]"
53 54
 )
54 55
 
55 56
 func optionalVar(fs *pflag.FlagSet, value ssh.OptionalValue, name, usage string) ssh.OptionalValue {
@@ -81,8 +82,10 @@ func main() {
81 82
 		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.")
82 83
 		gitLabel     = fs.String("git-label", "", "label to keep track of sync progress; overrides both --git-sync-tag and --git-notes-ref")
83 84
 		// Old git config; still used if --git-label is not supplied, but --git-label is preferred.
84
-		gitSyncTag  = fs.String("git-sync-tag", defaultGitSyncTag, "tag to use to mark sync progress for this cluster")
85
-		gitNotesRef = fs.String("git-notes-ref", defaultGitNotesRef, "ref to use for keeping commit annotations in git notes")
85
+		gitSyncTag     = fs.String("git-sync-tag", defaultGitSyncTag, "tag to use to mark sync progress for this cluster")
86
+		gitNotesRef    = fs.String("git-notes-ref", defaultGitNotesRef, "ref to use for keeping commit annotations in git notes")
87
+		gitSkip        = fs.Bool("git-ci-skip", false, `append "[ci skip]" to commit messages so that CI will skip builds`)
88
+		gitSkipMessage = fs.String("git-ci-skip-message", "", "additional text for commit messages, useful for skipping builds in CI. Use this to supply specific text, or set --git-ci-skip")
86 89
 
87 90
 		gitPollInterval = fs.Duration("git-poll-interval", 5*time.Minute, "period at which to poll git repo for new commits")
88 91
 		// registry
@@ -145,6 +148,10 @@ func main() {
145 148
 		}
146 149
 	}
147 150
 
151
+	if *gitSkipMessage == "" && *gitSkip {
152
+		*gitSkipMessage = defaultGitSkipMessage
153
+	}
154
+
148 155
 	if len(*gitPath) > 0 && (*gitPath)[0] == '/' {
149 156
 		logger.Log("err", "git subdirectory (--git-path) should not have leading forward slash")
150 157
 		os.Exit(1)
@@ -357,13 +364,14 @@ func main() {
357 364
 
358 365
 	gitRemote := git.Remote{URL: *gitURL}
359 366
 	gitConfig := git.Config{
360
-		Path:      *gitPath,
361
-		Branch:    *gitBranch,
362
-		SyncTag:   *gitSyncTag,
363
-		NotesRef:  *gitNotesRef,
364
-		UserName:  *gitUser,
365
-		UserEmail: *gitEmail,
366
-		SetAuthor: *gitSetAuthor,
367
+		Path:        *gitPath,
368
+		Branch:      *gitBranch,
369
+		SyncTag:     *gitSyncTag,
370
+		NotesRef:    *gitNotesRef,
371
+		UserName:    *gitUser,
372
+		UserEmail:   *gitEmail,
373
+		SetAuthor:   *gitSetAuthor,
374
+		SkipMessage: *gitSkipMessage,
367 375
 	}
368 376
 
369 377
 	repo := git.NewRepo(gitRemote)

+ 2
- 2
daemon/daemon.go View File

@@ -281,7 +281,7 @@ func (d *Daemon) updatePolicy(spec update.Spec, updates policy.Updates) DaemonJo
281 281
 		if d.GitConfig.SetAuthor {
282 282
 			commitAuthor = spec.Cause.User
283 283
 		}
284
-		commitAction := &git.CommitAction{Author: commitAuthor, Message: policyCommitMessage(updates, spec.Cause)}
284
+		commitAction := git.CommitAction{Author: commitAuthor, Message: policyCommitMessage(updates, spec.Cause)}
285 285
 		if err := working.CommitAndPush(ctx, commitAction, &git.Note{JobID: jobID, Spec: spec}); err != nil {
286 286
 			// On the chance pushing failed because it was not
287 287
 			// possible to fast-forward, ask for a sync so the
@@ -320,7 +320,7 @@ func (d *Daemon) release(spec update.Spec, c release.Changes) DaemonJobFunc {
320 320
 			if d.GitConfig.SetAuthor {
321 321
 				commitAuthor = spec.Cause.User
322 322
 			}
323
-			commitAction := &git.CommitAction{Author: commitAuthor, Message: commitMsg}
323
+			commitAction := git.CommitAction{Author: commitAuthor, Message: commitMsg}
324 324
 			if err := working.CommitAndPush(ctx, commitAction, &git.Note{JobID: jobID, Spec: spec, Result: result}); err != nil {
325 325
 				// On the chance pushing failed because it was not
326 326
 				// possible to fast-forward, ask the repo to fetch

+ 1
- 1
daemon/loop_test.go View File

@@ -240,7 +240,7 @@ func TestDoSync_WithNewCommit(t *testing.T) {
240 240
 			return err
241 241
 		}
242 242
 
243
-		commitAction := &git.CommitAction{Author: "", Message: "test commit"}
243
+		commitAction := git.CommitAction{Author: "", Message: "test commit"}
244 244
 		err = checkout.CommitAndPush(ctx, commitAction, nil)
245 245
 		if err != nil {
246 246
 			return err

+ 19
- 9
git/gittest/repo.go View File

@@ -59,33 +59,43 @@ func Repo(t *testing.T) (*git.Repo, func()) {
59 59
 	}), cleanup
60 60
 }
61 61
 
62
-func Checkout(t *testing.T) (*git.Checkout, func()) {
62
+// CheckoutWithConfig makes a standard repo, clones it, and returns
63
+// the clone, the original repo, and a cleanup function.
64
+func CheckoutWithConfig(t *testing.T, config git.Config) (*git.Checkout, *git.Repo, func()) {
63 65
 	repo, cleanup := Repo(t)
64 66
 	shutdown, wg := make(chan struct{}), &sync.WaitGroup{}
65 67
 	wg.Add(1)
66 68
 	go repo.Start(shutdown, wg)
67 69
 	WaitForRepoReady(repo, t)
68 70
 
69
-	config := git.Config{
70
-		Branch:    "master",
71
-		UserName:  "example",
72
-		UserEmail: "example@example.com",
73
-		SyncTag:   "flux-test",
74
-		NotesRef:  "fluxtest",
75
-	}
76 71
 	co, err := repo.Clone(context.Background(), config)
77 72
 	if err != nil {
78 73
 		close(shutdown)
79 74
 		cleanup()
80 75
 		t.Fatal(err)
81 76
 	}
82
-	return co, func() {
77
+	return co, repo, func() {
83 78
 		close(shutdown)
84 79
 		co.Clean()
85 80
 		cleanup()
86 81
 	}
87 82
 }
88 83
 
84
+var TestConfig git.Config = git.Config{
85
+	Branch:    "master",
86
+	UserName:  "example",
87
+	UserEmail: "example@example.com",
88
+	SyncTag:   "flux-test",
89
+	NotesRef:  "fluxtest",
90
+}
91
+
92
+// Checkout makes a standard repo, clones it, and returns the clone
93
+// with a cleanup function.
94
+func Checkout(t *testing.T) (*git.Checkout, func()) {
95
+	checkout, _, cleanup := CheckoutWithConfig(t, TestConfig)
96
+	return checkout, cleanup
97
+}
98
+
89 99
 func execCommand(cmd string, args ...string) error {
90 100
 	c := exec.Command(cmd, args...)
91 101
 	c.Stderr = ioutil.Discard

+ 50
- 2
git/gittest/repo_test.go View File

@@ -6,6 +6,7 @@ import (
6 6
 	"reflect"
7 7
 	"sync"
8 8
 	"testing"
9
+	"time"
9 10
 
10 11
 	"context"
11 12
 
@@ -16,6 +17,53 @@ import (
16 17
 	"github.com/weaveworks/flux/update"
17 18
 )
18 19
 
20
+func TestCommit(t *testing.T) {
21
+	config := TestConfig
22
+	config.SkipMessage = " **SKIP**"
23
+	checkout, repo, cleanup := CheckoutWithConfig(t, config)
24
+	defer cleanup()
25
+
26
+	for file, _ := range testfiles.Files {
27
+		path := filepath.Join(checkout.ManifestDir(), file)
28
+		if err := ioutil.WriteFile(path, []byte("FIRST CHANGE"), 0666); err != nil {
29
+			t.Fatal(err)
30
+		}
31
+		break
32
+	}
33
+
34
+	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
35
+	defer cancel()
36
+
37
+	commitAction := git.CommitAction{Message: "Changed file"}
38
+	if err := checkout.CommitAndPush(ctx, commitAction, nil); err != nil {
39
+		t.Fatal(err)
40
+	}
41
+
42
+	err := repo.Refresh(ctx)
43
+	if err != nil {
44
+		t.Error(err)
45
+	}
46
+
47
+	commits, err := repo.CommitsBefore(ctx, "HEAD", "")
48
+
49
+	if err != nil {
50
+		t.Fatal(err)
51
+	}
52
+	if len(commits) < 1 {
53
+		t.Fatal("expected at least one commit")
54
+	}
55
+	if msg := commits[0].Message; msg != commitAction.Message+config.SkipMessage {
56
+		t.Errorf(`expected commit message to be:
57
+
58
+%s
59
+
60
+    but it was
61
+
62
+%s
63
+`, commitAction.Message+config.SkipMessage, msg)
64
+	}
65
+}
66
+
19 67
 func TestCheckout(t *testing.T) {
20 68
 	repo, cleanup := Repo(t)
21 69
 	defer cleanup()
@@ -64,7 +112,7 @@ func TestCheckout(t *testing.T) {
64 112
 		changedFile = file
65 113
 		break
66 114
 	}
67
-	commitAction := &git.CommitAction{Author: "", Message: "Changed file"}
115
+	commitAction := git.CommitAction{Author: "", Message: "Changed file"}
68 116
 	if err := checkout.CommitAndPush(ctx, commitAction, nil); err != nil {
69 117
 		t.Fatal(err)
70 118
 	}
@@ -88,7 +136,7 @@ func TestCheckout(t *testing.T) {
88 136
 			},
89 137
 		},
90 138
 	}
91
-	commitAction = &git.CommitAction{Author: "", Message: "Changed file again"}
139
+	commitAction = git.CommitAction{Author: "", Message: "Changed file again"}
92 140
 	if err := checkout.CommitAndPush(ctx, commitAction, &expectedNote); err != nil {
93 141
 		t.Fatal(err)
94 142
 	}

+ 1
- 1
git/operations.go View File

@@ -68,7 +68,7 @@ func checkPush(ctx context.Context, workingDir, upstream string) error {
68 68
 	return execGitCmd(ctx, workingDir, nil, "push", "--delete", upstream, "tag", CheckPushTag)
69 69
 }
70 70
 
71
-func commit(ctx context.Context, workingDir string, commitAction *CommitAction) error {
71
+func commit(ctx context.Context, workingDir string, commitAction CommitAction) error {
72 72
 	commitAuthor := commitAction.Author
73 73
 	if commitAuthor != "" {
74 74
 		if err := execGitCmd(ctx,

+ 12
- 8
git/working.go View File

@@ -9,13 +9,14 @@ import (
9 9
 // Config holds some values we use when working in the working clone of
10 10
 // a repo.
11 11
 type Config struct {
12
-	Branch    string // branch we're syncing to
13
-	Path      string // path within the repo containing files we care about
14
-	SyncTag   string
15
-	NotesRef  string
16
-	UserName  string
17
-	UserEmail string
18
-	SetAuthor bool
12
+	Branch      string // branch we're syncing to
13
+	Path        string // path within the repo containing files we care about
14
+	SyncTag     string
15
+	NotesRef    string
16
+	UserName    string
17
+	UserEmail   string
18
+	SetAuthor   bool
19
+	SkipMessage string
19 20
 }
20 21
 
21 22
 // Checkout is a local working clone of the remote repo. It is
@@ -96,10 +97,13 @@ func (c *Checkout) ManifestDir() string {
96 97
 
97 98
 // CommitAndPush commits changes made in this checkout, along with any
98 99
 // extra data as a note, and pushes the commit and note to the remote repo.
99
-func (c *Checkout) CommitAndPush(ctx context.Context, commitAction *CommitAction, note *Note) error {
100
+func (c *Checkout) CommitAndPush(ctx context.Context, commitAction CommitAction, note *Note) error {
100 101
 	if !check(ctx, c.dir, c.config.Path) {
101 102
 		return ErrNoChanges
102 103
 	}
104
+
105
+	commitAction.Message += c.config.SkipMessage
106
+
103 107
 	if err := commit(ctx, c.dir, commitAction); err != nil {
104 108
 		return err
105 109
 	}

+ 2
- 0
site/daemon.md View File

@@ -45,6 +45,8 @@ fluxd requires setup and offers customization though a multitude of flags.
45 45
 |**Git repo & key etc.** |                              ||
46 46
 |--git-url               |                               | URL of git repo with Kubernetes manifests; e.g., `git@github.com:weaveworks/flux-example`|
47 47
 |--git-branch            | `master`                        | branch of git repo to use for Kubernetes manifests|
48
+|--git-ci-skip           | false   | when set, fluxd will append `\n\n[ci skip]` to its commit messages |
49
+|--git-ci-skip-message   | `""`    | if provided, fluxd will append this to commit messages (overrides --git-ci-skip`) |
48 50
 |--git-path              |                               | path within git repo to locate Kubernetes manifests (relative path)|
49 51
 |--git-user              | `Weave Flux`                    | username to use as git committer|
50 52
 |--git-email             | `support@weave.works`           | email to use as git committer|

+ 1
- 1
sync/sync_test.go View File

@@ -48,7 +48,7 @@ func TestSync(t *testing.T) {
48 48
 		if err := execCommand("rm", res[0]); err != nil {
49 49
 			t.Fatal(err)
50 50
 		}
51
-		commitAction := &git.CommitAction{Author: "", Message: "deleted " + res[0]}
51
+		commitAction := git.CommitAction{Author: "", Message: "deleted " + res[0]}
52 52
 		if err := checkout.CommitAndPush(context.Background(), commitAction, nil); err != nil {
53 53
 			t.Fatal(err)
54 54
 		}

Loading…
Cancel
Save