github.com/GoogleContainerTools/skaffold@v1.39.18/pkg/skaffold/tag/git_commit_test.go (about)

     1  //go:build !windows
     2  // +build !windows
     3  
     4  /*
     5  Copyright 2019 The Skaffold Authors
     6  
     7  Licensed under the Apache License, Version 2.0 (the "License");
     8  you may not use this file except in compliance with the License.
     9  You may obtain a copy of the License at
    10  
    11      http://www.apache.org/licenses/LICENSE-2.0
    12  
    13  Unless required by applicable law or agreed to in writing, software
    14  distributed under the License is distributed on an "AS IS" BASIS,
    15  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    16  See the License for the specific language governing permissions and
    17  limitations under the License.
    18  */
    19  
    20  package tag
    21  
    22  import (
    23  	"context"
    24  	"io/ioutil"
    25  	"os"
    26  	"path/filepath"
    27  	"strings"
    28  	"testing"
    29  	"time"
    30  
    31  	git "github.com/go-git/go-git/v5"
    32  	"github.com/go-git/go-git/v5/plumbing"
    33  	"github.com/go-git/go-git/v5/plumbing/object"
    34  
    35  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
    36  	"github.com/GoogleContainerTools/skaffold/testutil"
    37  )
    38  
    39  // These tests do not run on windows
    40  // See: https://github.com/src-d/go-git/issues/378
    41  func TestGitCommit_GenerateTag(t *testing.T) {
    42  	tests := []struct {
    43  		description            string
    44  		variantTags            string
    45  		variantCommitSha       string
    46  		variantAbbrevCommitSha string
    47  		variantTreeSha         string
    48  		variantAbbrevTreeSha   string
    49  		variantBranches        string
    50  		createGitRepo          func(string)
    51  		subDir                 string
    52  		shouldErr              bool
    53  		ignoreChanges          bool
    54  	}{
    55  		{
    56  			description:            "clean worktree without tag",
    57  			variantTags:            "eefe1b9",
    58  			variantCommitSha:       "eefe1b9c44eb0aa87199c9a079f2d48d8eb8baed",
    59  			variantAbbrevCommitSha: "eefe1b9",
    60  			variantTreeSha:         "3bed02ca656e336307e4eb4d80080d7221cba62c",
    61  			variantAbbrevTreeSha:   "3bed02c",
    62  			variantBranches:        "master",
    63  			createGitRepo: func(dir string) {
    64  				gitInit(t, dir).
    65  					write("source.go", "code").
    66  					add("source.go").
    67  					commit("initial")
    68  			},
    69  		},
    70  		{
    71  			description:            "clean worktree with tag containing a slash",
    72  			variantTags:            "v_2",
    73  			variantCommitSha:       "aea33bcc86b5af8c8570ff45d8a643202d63c808",
    74  			variantAbbrevCommitSha: "aea33bc",
    75  			variantTreeSha:         "bc69d50cda6897a6f2054e64b9059f038dc6fb0e",
    76  			variantAbbrevTreeSha:   "bc69d50",
    77  			variantBranches:        "master",
    78  			createGitRepo: func(dir string) {
    79  				gitInit(t, dir).
    80  					write("source.go", "code").
    81  					add("source.go").
    82  					commit("initial").
    83  					tag("v/1").
    84  					write("other.go", "other").
    85  					add("other.go").
    86  					commit("second commit").
    87  					tag("v/2")
    88  			},
    89  		},
    90  		{
    91  			description:            "clean worktree with a branch",
    92  			variantTags:            "589c4dc",
    93  			variantCommitSha:       "589c4dcf52df66d058c705db39f871dceacf6471",
    94  			variantAbbrevCommitSha: "589c4dc",
    95  			variantTreeSha:         "3bed02ca656e336307e4eb4d80080d7221cba62c",
    96  			variantAbbrevTreeSha:   "3bed02c",
    97  			variantBranches:        "branch",
    98  			createGitRepo: func(dir string) {
    99  				gitInit(t, dir).
   100  					write("source.go", "code").
   101  					add("source.go").
   102  					commit("initial").
   103  					branch("branch").
   104  					commit("second")
   105  			},
   106  		},
   107  		{
   108  			description:            "clean worktree with a branch containing a slash",
   109  			variantTags:            "eefe1b9",
   110  			variantCommitSha:       "eefe1b9c44eb0aa87199c9a079f2d48d8eb8baed",
   111  			variantAbbrevCommitSha: "eefe1b9",
   112  			variantTreeSha:         "3bed02ca656e336307e4eb4d80080d7221cba62c",
   113  			variantAbbrevTreeSha:   "3bed02c",
   114  			variantBranches:        "branch_withslash",
   115  			createGitRepo: func(dir string) {
   116  				gitInit(t, dir).
   117  					write("source.go", "code").
   118  					add("source.go").
   119  					commit("initial").
   120  					branch("branch/withslash")
   121  			},
   122  		},
   123  		{
   124  			description:            "dirty worktree with tag containing a slash",
   125  			variantTags:            "v_2-dirty",
   126  			variantCommitSha:       "aea33bcc86b5af8c8570ff45d8a643202d63c808-dirty",
   127  			variantAbbrevCommitSha: "aea33bc-dirty",
   128  			variantTreeSha:         "bc69d50cda6897a6f2054e64b9059f038dc6fb0e-dirty",
   129  			variantAbbrevTreeSha:   "bc69d50-dirty",
   130  			variantBranches:        "master-dirty",
   131  			createGitRepo: func(dir string) {
   132  				gitInit(t, dir).
   133  					write("source.go", "code").
   134  					add("source.go").
   135  					commit("initial").
   136  					tag("v/1").
   137  					write("other.go", "other").
   138  					add("other.go").
   139  					commit("second commit").
   140  					tag("v/2").
   141  					write("other.go", "updated code")
   142  			},
   143  		},
   144  		{
   145  			description:            "dirty worktree with ignore changes flag",
   146  			variantTags:            "v_2",
   147  			variantCommitSha:       "aea33bcc86b5af8c8570ff45d8a643202d63c808",
   148  			variantAbbrevCommitSha: "aea33bc",
   149  			variantTreeSha:         "bc69d50cda6897a6f2054e64b9059f038dc6fb0e",
   150  			variantAbbrevTreeSha:   "bc69d50",
   151  			variantBranches:        "master",
   152  			createGitRepo: func(dir string) {
   153  				gitInit(t, dir).
   154  					write("source.go", "code").
   155  					add("source.go").
   156  					commit("initial").
   157  					tag("v/1").
   158  					write("other.go", "other").
   159  					add("other.go").
   160  					commit("second commit").
   161  					tag("v/2").
   162  					write("other.go", "updated code")
   163  			},
   164  			ignoreChanges: true,
   165  		},
   166  		{
   167  			description:            "clean worktree with tags",
   168  			variantTags:            "v2",
   169  			variantCommitSha:       "aea33bcc86b5af8c8570ff45d8a643202d63c808",
   170  			variantAbbrevCommitSha: "aea33bc",
   171  			variantTreeSha:         "bc69d50cda6897a6f2054e64b9059f038dc6fb0e",
   172  			variantAbbrevTreeSha:   "bc69d50",
   173  			variantBranches:        "master",
   174  			createGitRepo: func(dir string) {
   175  				gitInit(t, dir).
   176  					write("source.go", "code").
   177  					add("source.go").
   178  					commit("initial").
   179  					tag("v1").
   180  					write("other.go", "other").
   181  					add("other.go").
   182  					commit("second commit").
   183  					tag("v2")
   184  			},
   185  		},
   186  		{
   187  			description:            "clean worktree with ignore changes flag",
   188  			variantTags:            "v2",
   189  			variantCommitSha:       "aea33bcc86b5af8c8570ff45d8a643202d63c808",
   190  			variantAbbrevCommitSha: "aea33bc",
   191  			variantTreeSha:         "bc69d50cda6897a6f2054e64b9059f038dc6fb0e",
   192  			variantAbbrevTreeSha:   "bc69d50",
   193  			variantBranches:        "master",
   194  			createGitRepo: func(dir string) {
   195  				gitInit(t, dir).
   196  					write("source.go", "code").
   197  					add("source.go").
   198  					commit("initial").
   199  					tag("v1").
   200  					write("other.go", "other").
   201  					add("other.go").
   202  					commit("second commit").
   203  					tag("v2")
   204  			},
   205  			ignoreChanges: true,
   206  		},
   207  		{
   208  			description:            "treeSha only considers current tree content",
   209  			variantTags:            "v1",
   210  			variantCommitSha:       "b2f7a7d62794237ac293eb07c6bcae3736b96231",
   211  			variantAbbrevCommitSha: "b2f7a7d",
   212  			variantTreeSha:         "3bed02ca656e336307e4eb4d80080d7221cba62c",
   213  			variantAbbrevTreeSha:   "3bed02c",
   214  			variantBranches:        "master",
   215  			createGitRepo: func(dir string) {
   216  				gitInit(t, dir).
   217  					write("source.go", "other code").
   218  					add("source.go").
   219  					commit("initial").
   220  					write("source.go", "code").
   221  					add("source.go").
   222  					commit("updated code").
   223  					tag("v1")
   224  			},
   225  		},
   226  		{
   227  			description:            "dirty worktree without tag",
   228  			variantTags:            "eefe1b9-dirty",
   229  			variantCommitSha:       "eefe1b9c44eb0aa87199c9a079f2d48d8eb8baed-dirty",
   230  			variantAbbrevCommitSha: "eefe1b9-dirty",
   231  			variantTreeSha:         "3bed02ca656e336307e4eb4d80080d7221cba62c-dirty",
   232  			variantAbbrevTreeSha:   "3bed02c-dirty",
   233  			variantBranches:        "master-dirty",
   234  			createGitRepo: func(dir string) {
   235  				gitInit(t, dir).
   236  					write("source.go", "code").
   237  					add("source.go").
   238  					commit("initial").
   239  					write("source.go", "updated code")
   240  			},
   241  		},
   242  		{
   243  			description:            "dirty worktree with tag",
   244  			variantTags:            "v1-dirty",
   245  			variantCommitSha:       "eefe1b9c44eb0aa87199c9a079f2d48d8eb8baed-dirty",
   246  			variantAbbrevCommitSha: "eefe1b9-dirty",
   247  			variantTreeSha:         "3bed02ca656e336307e4eb4d80080d7221cba62c-dirty",
   248  			variantAbbrevTreeSha:   "3bed02c-dirty",
   249  			variantBranches:        "master-dirty",
   250  			createGitRepo: func(dir string) {
   251  				gitInit(t, dir).
   252  					write("source.go", "code").
   253  					add("source.go").
   254  					commit("initial").
   255  					tag("v1").
   256  					write("source.go", "updated code")
   257  			},
   258  		},
   259  		{
   260  			description:            "untracked",
   261  			variantTags:            "eefe1b9-dirty",
   262  			variantCommitSha:       "eefe1b9c44eb0aa87199c9a079f2d48d8eb8baed-dirty",
   263  			variantAbbrevCommitSha: "eefe1b9-dirty",
   264  			variantTreeSha:         "3bed02ca656e336307e4eb4d80080d7221cba62c-dirty",
   265  			variantAbbrevTreeSha:   "3bed02c-dirty",
   266  			variantBranches:        "master-dirty",
   267  			createGitRepo: func(dir string) {
   268  				gitInit(t, dir).
   269  					write("source.go", "code").
   270  					add("source.go").
   271  					commit("initial").
   272  					write("new.go", "new code")
   273  			},
   274  		},
   275  		{
   276  			description:            "tag plus one commit",
   277  			variantTags:            "v1-1-g3cec6b9",
   278  			variantCommitSha:       "3cec6b950895704a8a69b610a199b242a3bd370f",
   279  			variantAbbrevCommitSha: "3cec6b9",
   280  			variantTreeSha:         "81eea360f7f81bc5c187498a8d6c4337e0361374",
   281  			variantAbbrevTreeSha:   "81eea36",
   282  			variantBranches:        "master",
   283  			createGitRepo: func(dir string) {
   284  				gitInit(t, dir).
   285  					write("source.go", "code").
   286  					add("source.go").
   287  					commit("initial").
   288  					tag("v1").
   289  					write("source.go", "updated code").
   290  					add("source.go").
   291  					commit("changes")
   292  			},
   293  		},
   294  		{
   295  			description:            "deleted file",
   296  			variantTags:            "279d53f-dirty",
   297  			variantCommitSha:       "279d53fcc3ae34503aec382a49a41f6db6de9a66-dirty",
   298  			variantAbbrevCommitSha: "279d53f-dirty",
   299  			variantTreeSha:         "039c20a072ceb72fb72d5883315df91659bb8ae4-dirty",
   300  			variantAbbrevTreeSha:   "039c20a-dirty",
   301  			variantBranches:        "master-dirty",
   302  			createGitRepo: func(dir string) {
   303  				gitInit(t, dir).
   304  					write("source1.go", "code1").
   305  					write("source2.go", "code2").
   306  					add("source1.go", "source2.go").
   307  					commit("initial").
   308  					delete("source1.go")
   309  			},
   310  		},
   311  		{
   312  			description:            "rename",
   313  			variantTags:            "eefe1b9-dirty",
   314  			variantCommitSha:       "eefe1b9c44eb0aa87199c9a079f2d48d8eb8baed-dirty",
   315  			variantAbbrevCommitSha: "eefe1b9-dirty",
   316  			variantTreeSha:         "3bed02ca656e336307e4eb4d80080d7221cba62c-dirty",
   317  			variantAbbrevTreeSha:   "3bed02c-dirty",
   318  			variantBranches:        "master-dirty",
   319  			createGitRepo: func(dir string) {
   320  				gitInit(t, dir).
   321  					write("source.go", "code").
   322  					add("source.go").
   323  					commit("initial").
   324  					rename("source.go", "source2.go")
   325  			},
   326  		},
   327  		{
   328  			description:            "clean artifact1 in tagged repo",
   329  			variantTags:            "v1",
   330  			variantCommitSha:       "b610928dc27484cc56990bc77622aab0dbd67131",
   331  			variantAbbrevCommitSha: "b610928",
   332  			variantTreeSha:         "3bed02ca656e336307e4eb4d80080d7221cba62c",
   333  			variantAbbrevTreeSha:   "3bed02c",
   334  			variantBranches:        "master",
   335  			createGitRepo: func(dir string) {
   336  				gitInit(t, dir).
   337  					mkdir("artifact1").write("artifact1/source.go", "code").
   338  					mkdir("artifact2").write("artifact2/source.go", "other code").
   339  					add("artifact1/source.go", "artifact2/source.go").
   340  					commit("initial").tag("v1")
   341  			},
   342  			subDir: "artifact1/",
   343  		},
   344  		{
   345  			description:            "clean artifact2 in tagged repo",
   346  			variantTags:            "v1",
   347  			variantCommitSha:       "b610928dc27484cc56990bc77622aab0dbd67131",
   348  			variantAbbrevCommitSha: "b610928",
   349  			variantTreeSha:         "36651c832d8bf5ca1e84c6dc23bb8678fa51cf3e",
   350  			variantAbbrevTreeSha:   "36651c8",
   351  			variantBranches:        "master",
   352  			createGitRepo: func(dir string) {
   353  				gitInit(t, dir).
   354  					mkdir("artifact1").write("artifact1/source.go", "code").
   355  					mkdir("artifact2").write("artifact2/source.go", "other code").
   356  					add("artifact1/source.go", "artifact2/source.go").
   357  					commit("initial").tag("v1")
   358  			},
   359  			subDir: "artifact2",
   360  		},
   361  		{
   362  			description:            "clean artifact in dirty repo",
   363  			variantTags:            "v1",
   364  			variantCommitSha:       "b610928dc27484cc56990bc77622aab0dbd67131",
   365  			variantAbbrevCommitSha: "b610928",
   366  			variantTreeSha:         "3bed02ca656e336307e4eb4d80080d7221cba62c",
   367  			variantAbbrevTreeSha:   "3bed02c",
   368  			variantBranches:        "master",
   369  			createGitRepo: func(dir string) {
   370  				gitInit(t, dir).
   371  					mkdir("artifact1").write("artifact1/source.go", "code").
   372  					mkdir("artifact2").write("artifact2/source.go", "other code").
   373  					add("artifact1/source.go", "artifact2/source.go").
   374  					commit("initial").tag("v1").
   375  					write("artifact2/source.go", "updated code")
   376  			},
   377  			subDir: "artifact1",
   378  		},
   379  		{
   380  			description:            "updated artifact in dirty repo",
   381  			variantTags:            "v1-dirty",
   382  			variantCommitSha:       "b610928dc27484cc56990bc77622aab0dbd67131-dirty",
   383  			variantAbbrevCommitSha: "b610928-dirty",
   384  			variantTreeSha:         "36651c832d8bf5ca1e84c6dc23bb8678fa51cf3e-dirty",
   385  			variantAbbrevTreeSha:   "36651c8-dirty",
   386  			variantBranches:        "master-dirty",
   387  			createGitRepo: func(dir string) {
   388  				gitInit(t, dir).
   389  					mkdir("artifact1").write("artifact1/source.go", "code").
   390  					mkdir("artifact2").write("artifact2/source.go", "other code").
   391  					add("artifact1/source.go", "artifact2/source.go").
   392  					commit("initial").tag("v1").
   393  					write("artifact2/source.go", "updated code")
   394  			},
   395  			subDir: "artifact2",
   396  		},
   397  		{
   398  			description:            "additional commit in other artifact",
   399  			variantTags:            "0d16f59",
   400  			variantCommitSha:       "0d16f59900bd63dd39425d6085d3f1333b66804f",
   401  			variantAbbrevCommitSha: "0d16f59",
   402  			variantTreeSha:         "3bed02ca656e336307e4eb4d80080d7221cba62c",
   403  			variantAbbrevTreeSha:   "3bed02c",
   404  			variantBranches:        "master",
   405  			createGitRepo: func(dir string) {
   406  				gitInit(t, dir).
   407  					mkdir("artifact1").write("artifact1/source.go", "code").
   408  					mkdir("artifact2").write("artifact2/source.go", "other code").
   409  					add("artifact1/source.go", "artifact2/source.go").
   410  					commit("initial").
   411  					write("artifact2/source.go", "updated code").
   412  					add("artifact2/source.go").
   413  					commit("update artifact2")
   414  			},
   415  			subDir: "artifact1",
   416  		},
   417  		{
   418  			description: "non git repo",
   419  			createGitRepo: func(dir string) {
   420  				ioutil.WriteFile(filepath.Join(dir, "source.go"), []byte("code"), os.ModePerm)
   421  			},
   422  			shouldErr: true,
   423  		},
   424  		{
   425  			description:     "git repo with no commit",
   426  			variantBranches: "master",
   427  			createGitRepo: func(dir string) {
   428  				gitInit(t, dir)
   429  			},
   430  			shouldErr: true,
   431  		},
   432  	}
   433  
   434  	for _, test := range tests {
   435  		test := test
   436  		testutil.Run(t, test.description, func(t *testutil.T) {
   437  			t.Parallel()
   438  
   439  			tmpDir := t.NewTempDir()
   440  			test.createGitRepo(tmpDir.Root())
   441  
   442  			image := latest.Artifact{
   443  				ImageName: "test",
   444  				Workspace: tmpDir.Path(test.subDir),
   445  			}
   446  
   447  			for variant, expectedTag := range map[string]string{
   448  				"Tags":            test.variantTags,
   449  				"CommitSha":       test.variantCommitSha,
   450  				"AbbrevCommitSha": test.variantAbbrevCommitSha,
   451  				"TreeSha":         test.variantTreeSha,
   452  				"AbbrevTreeSha":   test.variantAbbrevTreeSha,
   453  				"Branches":        test.variantBranches,
   454  			} {
   455  				tagger, err := NewGitCommit("", variant, test.ignoreChanges)
   456  				t.CheckNoError(err)
   457  
   458  				tag, err := tagger.GenerateTag(context.Background(), image)
   459  
   460  				// If we explicitly expect a tag for a variant, it should not err
   461  				shouldErr := test.shouldErr && expectedTag == ""
   462  				t.CheckErrorAndDeepEqual(shouldErr, err, expectedTag, tag)
   463  			}
   464  		})
   465  	}
   466  }
   467  
   468  func TestGitCommit_GenerateFullyQualifiedImageName(t *testing.T) {
   469  	tests := []struct {
   470  		description            string
   471  		variantTags            string
   472  		variantCommitSha       string
   473  		variantAbbrevCommitSha string
   474  		variantTreeSha         string
   475  		variantAbbrevTreeSha   string
   476  		variantBranches        string
   477  		createGitRepo          func(string)
   478  		subDir                 string
   479  		shouldErr              bool
   480  	}{
   481  		{
   482  			description:            "clean worktree without tag",
   483  			variantTags:            "test:eefe1b9",
   484  			variantCommitSha:       "test:eefe1b9c44eb0aa87199c9a079f2d48d8eb8baed",
   485  			variantAbbrevCommitSha: "test:eefe1b9",
   486  			variantTreeSha:         "test:3bed02ca656e336307e4eb4d80080d7221cba62c",
   487  			variantAbbrevTreeSha:   "test:3bed02c",
   488  			variantBranches:        "test:master",
   489  			createGitRepo: func(dir string) {
   490  				gitInit(t, dir).
   491  					write("source.go", "code").
   492  					add("source.go").
   493  					commit("initial")
   494  			},
   495  		},
   496  		{
   497  			description: "non git repo",
   498  			createGitRepo: func(dir string) {
   499  				ioutil.WriteFile(filepath.Join(dir, "source.go"), []byte("code"), os.ModePerm)
   500  			},
   501  			shouldErr: true,
   502  		},
   503  	}
   504  	for _, test := range tests {
   505  		test := test
   506  		testutil.Run(t, test.description, func(t *testutil.T) {
   507  			tmpDir := t.NewTempDir()
   508  			test.createGitRepo(tmpDir.Root())
   509  			image := latest.Artifact{
   510  				ImageName: "test",
   511  				Workspace: tmpDir.Path(test.subDir),
   512  			}
   513  
   514  			for variant, expectedTag := range map[string]string{
   515  				"Tags":            test.variantTags,
   516  				"CommitSha":       test.variantCommitSha,
   517  				"AbbrevCommitSha": test.variantAbbrevCommitSha,
   518  				"TreeSha":         test.variantTreeSha,
   519  				"AbbrevTreeSha":   test.variantAbbrevTreeSha,
   520  				"Branches":        test.variantBranches,
   521  			} {
   522  				tagger, err := NewGitCommit("", variant, false)
   523  				t.CheckNoError(err)
   524  
   525  				tag, err := GenerateFullyQualifiedImageName(context.Background(), tagger, image)
   526  
   527  				t.CheckErrorAndDeepEqual(test.shouldErr, err, expectedTag, tag)
   528  			}
   529  		})
   530  	}
   531  }
   532  
   533  func TestGitCommit_CustomTemplate(t *testing.T) {
   534  	gitCommitExample, _ := NewGitCommit("", "CommitSha", false)
   535  	tests := []struct {
   536  		description   string
   537  		template      string
   538  		customMap     map[string]Tagger
   539  		expected      string
   540  		createGitRepo func(string)
   541  		subDir        string
   542  	}{
   543  		{
   544  			description: "gitCommit component",
   545  			template:    "{{.FOO}}",
   546  			customMap:   map[string]Tagger{"FOO": gitCommitExample},
   547  			expected:    "eefe1b9c44eb0aa87199c9a079f2d48d8eb8baed",
   548  			createGitRepo: func(dir string) {
   549  				gitInit(t, dir).
   550  					write("source.go", "code").
   551  					add("source.go").
   552  					commit("initial")
   553  			},
   554  		},
   555  		{
   556  			description: "gitCommit default component",
   557  			template:    "{{.GIT}}",
   558  			expected:    "eefe1b9",
   559  			createGitRepo: func(dir string) {
   560  				gitInit(t, dir).
   561  					write("source.go", "code").
   562  					add("source.go").
   563  					commit("initial")
   564  			},
   565  		},
   566  	}
   567  	for _, test := range tests {
   568  		test := test
   569  		testutil.Run(t, test.description, func(t *testutil.T) {
   570  			tmpDir := t.NewTempDir()
   571  			test.createGitRepo(tmpDir.Root())
   572  			image := latest.Artifact{
   573  				ImageName: "test",
   574  				Workspace: tmpDir.Path(test.subDir),
   575  			}
   576  
   577  			c, err := NewCustomTemplateTagger(test.template, test.customMap)
   578  
   579  			t.CheckNoError(err)
   580  
   581  			tag, err := c.GenerateTag(context.Background(), image)
   582  
   583  			t.CheckNoError(err)
   584  			t.CheckDeepEqual(test.expected, tag)
   585  		})
   586  	}
   587  }
   588  
   589  func TestGitCommitSubDirectory(t *testing.T) {
   590  	testutil.Run(t, "", func(t *testutil.T) {
   591  		tmpDir := t.NewTempDir()
   592  		gitInit(t.T, tmpDir.Root()).mkdir("sub/sub").commit("initial")
   593  		image := latest.Artifact{
   594  			ImageName: "test",
   595  			Workspace: tmpDir.Path("sub/sub"),
   596  		}
   597  
   598  		tagger, err := NewGitCommit("", "Tags", false)
   599  		t.CheckNoError(err)
   600  		tag, err := tagger.GenerateTag(context.Background(), image)
   601  		t.CheckNoError(err)
   602  		t.CheckDeepEqual("a7b32a6", tag)
   603  
   604  		tagger, err = NewGitCommit("", "CommitSha", false)
   605  		t.CheckNoError(err)
   606  		tag, err = tagger.GenerateTag(context.Background(), image)
   607  		t.CheckNoError(err)
   608  		t.CheckDeepEqual("a7b32a69335a6daa51bd89cc1bf30bd31df228ba", tag)
   609  
   610  		tagger, err = NewGitCommit("", "AbbrevCommitSha", false)
   611  		t.CheckNoError(err)
   612  		tag, err = tagger.GenerateTag(context.Background(), image)
   613  		t.CheckNoError(err)
   614  		t.CheckDeepEqual("a7b32a6", tag)
   615  
   616  		tagger, err = NewGitCommit("", "TreeSha", false)
   617  		t.CheckNoError(err)
   618  		_, err = tagger.GenerateTag(context.Background(), image)
   619  		t.CheckErrorAndDeepEqual(true, err, "a7b32a6", tag)
   620  
   621  		tagger, err = NewGitCommit("", "AbbrevTreeSha", false)
   622  		t.CheckNoError(err)
   623  		_, err = tagger.GenerateTag(context.Background(), image)
   624  		t.CheckErrorAndDeepEqual(true, err, "a7b32a6", tag)
   625  	})
   626  }
   627  
   628  func TestPrefix(t *testing.T) {
   629  	testutil.Run(t, "", func(t *testutil.T) {
   630  		tmpDir := t.NewTempDir()
   631  		gitInit(t.T, tmpDir.Root()).commit("initial")
   632  		image := latest.Artifact{
   633  			ImageName: "test",
   634  			Workspace: tmpDir.Path("."),
   635  		}
   636  
   637  		tagger, err := NewGitCommit("tag-", "Tags", false)
   638  		t.CheckNoError(err)
   639  		tag, err := tagger.GenerateTag(context.Background(), image)
   640  		t.CheckNoError(err)
   641  		t.CheckDeepEqual("tag-a7b32a6", tag)
   642  
   643  		tagger, err = NewGitCommit("commit-", "CommitSha", false)
   644  		t.CheckNoError(err)
   645  		tag, err = tagger.GenerateTag(context.Background(), image)
   646  		t.CheckNoError(err)
   647  		t.CheckDeepEqual("commit-a7b32a69335a6daa51bd89cc1bf30bd31df228ba", tag)
   648  	})
   649  }
   650  
   651  func TestInvalidVariant(t *testing.T) {
   652  	testutil.Run(t, "", func(t *testutil.T) {
   653  		_, err := NewGitCommit("", "Invalid", false)
   654  
   655  		t.CheckErrorContains("\"Invalid\" is not a valid git tagger variant", err)
   656  	})
   657  }
   658  
   659  func TestSanitizeTag(t *testing.T) {
   660  	testutil.Run(t, "valid tags", func(t *testutil.T) {
   661  		t.CheckDeepEqual("abcdefghijklmnopqrstuvwxyz", sanitizeTag("abcdefghijklmnopqrstuvwxyz"))
   662  		t.CheckDeepEqual("ABCDEFGHIJKLMNOPQRSTUVWXYZ", sanitizeTag("ABCDEFGHIJKLMNOPQRSTUVWXYZ"))
   663  		t.CheckDeepEqual("0123456789-_.", sanitizeTag("0123456789-_."))
   664  		t.CheckDeepEqual("_v1", sanitizeTag("_v1"))
   665  	})
   666  
   667  	testutil.Run(t, "sanitized tags", func(t *testutil.T) {
   668  		t.CheckDeepEqual("v_1", sanitizeTag("v/1"))
   669  		t.CheckDeepEqual("v____1", sanitizeTag("v%$@!1"))
   670  		t.CheckDeepEqual("__v1", sanitizeTag("--v1"))
   671  		t.CheckDeepEqual("__v1", sanitizeTag("..v1"))
   672  		t.CheckDeepEqual(128, len(sanitizeTag(strings.Repeat("0123456789", 20))))
   673  	})
   674  }
   675  
   676  // gitRepo deals with test git repositories
   677  type gitRepo struct {
   678  	dir      string
   679  	repo     *git.Repository
   680  	workTree *git.Worktree
   681  	t        *testing.T
   682  }
   683  
   684  func gitInit(t *testing.T, dir string) *gitRepo {
   685  	repo, err := git.PlainInit(dir, false)
   686  	failNowIfError(t, err)
   687  
   688  	w, err := repo.Worktree()
   689  	failNowIfError(t, err)
   690  
   691  	return &gitRepo{
   692  		dir:      dir,
   693  		repo:     repo,
   694  		workTree: w,
   695  		t:        t,
   696  	}
   697  }
   698  
   699  func (g *gitRepo) mkdir(folder string) *gitRepo {
   700  	err := os.MkdirAll(filepath.Join(g.dir, folder), os.ModePerm)
   701  	failNowIfError(g.t, err)
   702  	return g
   703  }
   704  
   705  func (g *gitRepo) write(file string, content string) *gitRepo {
   706  	err := ioutil.WriteFile(filepath.Join(g.dir, file), []byte(content), os.ModePerm)
   707  	failNowIfError(g.t, err)
   708  	return g
   709  }
   710  
   711  func (g *gitRepo) rename(file, to string) *gitRepo {
   712  	err := os.Rename(filepath.Join(g.dir, file), filepath.Join(g.dir, to))
   713  	failNowIfError(g.t, err)
   714  	return g
   715  }
   716  
   717  func (g *gitRepo) delete(files ...string) *gitRepo {
   718  	for _, file := range files {
   719  		err := os.Remove(filepath.Join(g.dir, file))
   720  		failNowIfError(g.t, err)
   721  	}
   722  	return g
   723  }
   724  
   725  func (g *gitRepo) add(files ...string) *gitRepo {
   726  	for _, file := range files {
   727  		_, err := g.workTree.Add(file)
   728  		failNowIfError(g.t, err)
   729  	}
   730  	return g
   731  }
   732  
   733  func (g *gitRepo) commit(msg string) *gitRepo {
   734  	now, err := time.Parse("Jan 2, 2006 at 15:04:05 -0700 MST", "Feb 3, 2013 at 19:54:00 -0700 MST")
   735  	failNowIfError(g.t, err)
   736  
   737  	_, err = g.workTree.Commit(msg, &git.CommitOptions{
   738  		AllowEmptyCommits: true,
   739  		Author: &object.Signature{
   740  			Name:  "John Doe",
   741  			Email: "john@doe.org",
   742  			When:  now,
   743  		},
   744  	})
   745  	failNowIfError(g.t, err)
   746  
   747  	return g
   748  }
   749  
   750  func (g *gitRepo) tag(tag string) *gitRepo {
   751  	head, err := g.repo.Head()
   752  	failNowIfError(g.t, err)
   753  
   754  	n := plumbing.ReferenceName("refs/tags/" + tag)
   755  	t := plumbing.NewHashReference(n, head.Hash())
   756  
   757  	err = g.repo.Storer.SetReference(t)
   758  	failNowIfError(g.t, err)
   759  
   760  	return g
   761  }
   762  
   763  func (g *gitRepo) branch(newBranchName string) *gitRepo {
   764  	refName := plumbing.NewBranchReferenceName(newBranchName)
   765  	headRef, err := g.repo.Head()
   766  	failNowIfError(g.t, err)
   767  	ref := plumbing.NewHashReference(refName, headRef.Hash())
   768  	err = g.repo.Storer.SetReference(ref)
   769  	failNowIfError(g.t, err)
   770  
   771  	err = g.workTree.Checkout(&git.CheckoutOptions{Branch: ref.Name()})
   772  	failNowIfError(g.t, err)
   773  
   774  	return g
   775  }
   776  
   777  func failNowIfError(t *testing.T, err error) {
   778  	if err != nil {
   779  		t.Fatal(err)
   780  	}
   781  }