github.com/yogeshkumararora/slsa-github-generator@v1.10.1-0.20240520161934-11278bd5afb4/internal/builders/go/pkg/build_test.go (about)

     1  // Copyright 2022 SLSA Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package pkg
    16  
    17  import (
    18  	"errors"
    19  	"fmt"
    20  	"os"
    21  	"os/exec"
    22  	"testing"
    23  
    24  	"github.com/google/go-cmp/cmp"
    25  	"github.com/google/go-cmp/cmp/cmpopts"
    26  )
    27  
    28  func errEnvVariableNameEmptyFunc(t *testing.T, got error) {
    29  	want := errEnvVariableNameEmpty
    30  	if !errors.Is(got, want) {
    31  		t.Fatalf("unexpected error: %v", cmp.Diff(got, want, cmpopts.EquateErrors()))
    32  	}
    33  }
    34  
    35  func errUnsupportedArgumentsFunc(t *testing.T, got error) {
    36  	want := errUnsupportedArguments
    37  	if !errors.Is(got, want) {
    38  		t.Fatalf("unexpected error: %v", cmp.Diff(got, want, cmpopts.EquateErrors()))
    39  	}
    40  }
    41  
    42  func errInvalidEnvArgumentFunc(t *testing.T, got error) {
    43  	want := errInvalidEnvArgument
    44  	if !errors.Is(got, want) {
    45  		t.Fatalf("unexpected error: %v", cmp.Diff(got, want, cmpopts.EquateErrors()))
    46  	}
    47  }
    48  
    49  func errEnvVariableNameNotAllowedFunc(t *testing.T, got error) {
    50  	want := errEnvVariableNameNotAllowed
    51  	if !errors.Is(got, want) {
    52  		t.Fatalf("unexpected error: %v", cmp.Diff(got, want, cmpopts.EquateErrors()))
    53  	}
    54  }
    55  
    56  func errInvalidFilenameFunc(t *testing.T, got error) {
    57  	want := errInvalidFilename
    58  	if !errors.Is(got, want) {
    59  		t.Fatalf("unexpected error: %v", cmp.Diff(got, want, cmpopts.EquateErrors()))
    60  	}
    61  }
    62  
    63  func Test_isAllowedEnvVariable(t *testing.T) {
    64  	t.Parallel()
    65  
    66  	tests := []struct {
    67  		name     string
    68  		variable string
    69  		expected bool
    70  	}{
    71  		{
    72  			name:     "BLA variable",
    73  			variable: "BLA",
    74  			expected: false,
    75  		},
    76  		{
    77  			name:     "random variable",
    78  			variable: "random",
    79  			expected: false,
    80  		},
    81  		{
    82  			name:     "GOSOMETHING variable",
    83  			variable: "GOSOMETHING",
    84  			expected: true,
    85  		},
    86  		{
    87  			name:     "CGO_SOMETHING variable",
    88  			variable: "CGO_SOMETHING",
    89  			expected: true,
    90  		},
    91  	}
    92  	for _, tt := range tests {
    93  		tt := tt // Re-initializing variable so it is not changed while executing the closure below
    94  		t.Run(tt.name, func(t *testing.T) {
    95  			t.Parallel()
    96  
    97  			r := isAllowedEnvVariable(tt.variable)
    98  			if !cmp.Equal(r, tt.expected) {
    99  				t.Errorf(cmp.Diff(r, tt.expected))
   100  			}
   101  		})
   102  	}
   103  }
   104  
   105  func Test_getOutputBinaryPath(t *testing.T) {
   106  	t.Parallel()
   107  
   108  	tests := []struct {
   109  		err  func(*testing.T, error)
   110  		name string
   111  		path string
   112  	}{
   113  		{
   114  			name: "empty output",
   115  			path: "",
   116  			err:  errInvalidFilenameFunc,
   117  		},
   118  		{
   119  			name: "not absolute",
   120  			path: "./some/path/to/binary",
   121  			err:  errInvalidFilenameFunc,
   122  		},
   123  		{
   124  			name: "absolute path",
   125  			path: "/to/absolute/path/to/binary",
   126  		},
   127  	}
   128  	for _, tt := range tests {
   129  		tt := tt // Re-initializing variable so it is not changed while executing the closure below
   130  		t.Run(tt.name, func(t *testing.T) {
   131  			t.Parallel()
   132  
   133  			r, err := getOutputBinaryPath(tt.path)
   134  			if tt.err != nil {
   135  				tt.err(t, err)
   136  			}
   137  
   138  			if err != nil {
   139  				return
   140  			}
   141  
   142  			if !cmp.Equal(r, tt.path) {
   143  				t.Errorf(cmp.Diff(r, tt.path))
   144  			}
   145  		})
   146  	}
   147  }
   148  
   149  func Test_isAllowedArg(t *testing.T) {
   150  	t.Parallel()
   151  
   152  	type test struct {
   153  		name     string
   154  		argument string
   155  		expected bool
   156  	}
   157  
   158  	var tests []test
   159  
   160  	for k := range allowedBuildArgs {
   161  		tests = append(tests, test{
   162  			name:     fmt.Sprintf("%s argument", k),
   163  			argument: k,
   164  			expected: true,
   165  		}, test{
   166  			name:     fmt.Sprintf("%sbla argument", k),
   167  			argument: fmt.Sprintf("%sbla", k),
   168  			expected: true,
   169  		}, test{
   170  			name:     fmt.Sprintf("bla %s argument", k),
   171  			argument: fmt.Sprintf("bla%s", k),
   172  			expected: false,
   173  		}, test{
   174  			name:     fmt.Sprintf("space %s argument", k),
   175  			argument: fmt.Sprintf(" %s", k),
   176  			expected: false,
   177  		})
   178  	}
   179  	for _, tt := range tests {
   180  		tt := tt // Re-initializing variable so it is not changed while executing the closure below
   181  		t.Run(tt.name, func(t *testing.T) {
   182  			t.Parallel()
   183  
   184  			r := isAllowedArg(tt.argument)
   185  			if !cmp.Equal(r, tt.expected) {
   186  				t.Errorf(cmp.Diff(r, tt.expected))
   187  			}
   188  		})
   189  	}
   190  }
   191  
   192  func Test_generateOutputFilename(t *testing.T) {
   193  	// Disable to avoid env clobbering between tests.
   194  	// t.Parallel()
   195  
   196  	tests := []struct {
   197  		name     string
   198  		filename string
   199  		goos     string
   200  		goarch   string
   201  		envs     map[string]string
   202  		argEnv   string
   203  		expected struct {
   204  			err func(*testing.T, error)
   205  			fn  string
   206  		}
   207  	}{
   208  		{
   209  			name:     "invalid filename",
   210  			filename: "../filename",
   211  			expected: struct {
   212  				err func(*testing.T, error)
   213  				fn  string
   214  			}{
   215  				err: errInvalidFilenameFunc,
   216  			},
   217  		},
   218  		{
   219  			name:     "valid filename",
   220  			filename: "",
   221  			expected: struct {
   222  				err func(*testing.T, error)
   223  				fn  string
   224  			}{
   225  				err: errInvalidFilenameFunc,
   226  			},
   227  		},
   228  		{
   229  			name:     "filename arch",
   230  			filename: "name-{{ .Arch }}",
   231  			expected: struct {
   232  				err func(*testing.T, error)
   233  				fn  string
   234  			}{
   235  				err: errEnvVariableNameEmptyFunc,
   236  			},
   237  		},
   238  		{
   239  			name:     "filename os",
   240  			filename: "name-{{ .Os }}",
   241  			expected: struct {
   242  				err func(*testing.T, error)
   243  				fn  string
   244  			}{
   245  				err: errEnvVariableNameEmptyFunc,
   246  			},
   247  		},
   248  		{
   249  			name:     "filename invalid letter ^",
   250  			filename: "Name-AB^",
   251  			goarch:   "amd64",
   252  			expected: struct {
   253  				err func(*testing.T, error)
   254  				fn  string
   255  			}{
   256  				err: errInvalidFilenameFunc,
   257  			},
   258  		},
   259  		{
   260  			filename: "filename invalid letter $",
   261  			expected: struct {
   262  				err func(*testing.T, error)
   263  				fn  string
   264  			}{
   265  				err: errInvalidFilenameFunc,
   266  			},
   267  		},
   268  		{
   269  			name:     "filename os",
   270  			filename: "name-{{ .Os }}",
   271  			expected: struct {
   272  				err func(*testing.T, error)
   273  				fn  string
   274  			}{
   275  				err: errEnvVariableNameEmptyFunc,
   276  			},
   277  		},
   278  		{
   279  			name:     "filename linux os",
   280  			filename: "name-{{ .Os }}",
   281  			goos:     "linux",
   282  			expected: struct {
   283  				err func(*testing.T, error)
   284  				fn  string
   285  			}{
   286  				err: nil,
   287  				fn:  "name-linux",
   288  			},
   289  		},
   290  		{
   291  			name:     "filename amd64 arch",
   292  			filename: "name-{{ .Arch }}",
   293  			goarch:   "amd64",
   294  			expected: struct {
   295  				err func(*testing.T, error)
   296  				fn  string
   297  			}{
   298  				err: nil,
   299  				fn:  "name-amd64",
   300  			},
   301  		},
   302  		{
   303  			name:     "filename capital letter",
   304  			filename: "Name-{{ .Arch }}",
   305  			goarch:   "amd64",
   306  			expected: struct {
   307  				err func(*testing.T, error)
   308  				fn  string
   309  			}{
   310  				err: nil,
   311  				fn:  "Name-amd64",
   312  			},
   313  		},
   314  		{
   315  			name:     "filename amd64/linux arch",
   316  			filename: "name-{{ .Os }}-{{ .Arch }}",
   317  			goarch:   "amd64",
   318  			goos:     "linux",
   319  			expected: struct {
   320  				err func(*testing.T, error)
   321  				fn  string
   322  			}{
   323  				err: nil,
   324  				fn:  "name-linux-amd64",
   325  			},
   326  		},
   327  		{
   328  			name:     "filename invalid arch",
   329  			filename: "name-{{ .Arch }}",
   330  			goarch:   "something/../../",
   331  			expected: struct {
   332  				err func(*testing.T, error)
   333  				fn  string
   334  			}{
   335  				err: errInvalidFilenameFunc,
   336  			},
   337  		},
   338  		{
   339  			name:     "filename invalid not supported",
   340  			filename: "name-{{ .Bla }}",
   341  			goarch:   "something/../../",
   342  			expected: struct {
   343  				err func(*testing.T, error)
   344  				fn  string
   345  			}{
   346  				err: errInvalidEnvArgumentFunc,
   347  			},
   348  		},
   349  		{
   350  			name:     "filename amd64/linux v1.2.3",
   351  			filename: "name-{{ .Os }}-{{ .Arch }}-{{ .Tag }}",
   352  			goarch:   "amd64",
   353  			goos:     "linux",
   354  			envs: map[string]string{
   355  				"GITHUB_REF_NAME": "v1.2.3",
   356  			},
   357  			expected: struct {
   358  				err func(*testing.T, error)
   359  				fn  string
   360  			}{
   361  				err: nil,
   362  				fn:  "name-linux-amd64-v1.2.3",
   363  			},
   364  		},
   365  		{
   366  			name:     "filename twice v1.2.3",
   367  			filename: "name-{{ .Tag }}-{{ .Tag }}",
   368  			goarch:   "amd64",
   369  			goos:     "linux",
   370  			envs: map[string]string{
   371  				"GITHUB_REF_NAME": "v1.2.3",
   372  			},
   373  			expected: struct {
   374  				err func(*testing.T, error)
   375  				fn  string
   376  			}{
   377  				err: nil,
   378  				fn:  "name-v1.2.3-v1.2.3",
   379  			},
   380  		},
   381  		{
   382  			name:     "filename twice empty versions",
   383  			filename: "name-{{ .Tag }}-{{ .Tag }}",
   384  			goarch:   "amd64",
   385  			goos:     "linux",
   386  			envs: map[string]string{
   387  				"GITHUB_REF_NAME": "",
   388  			},
   389  			expected: struct {
   390  				err func(*testing.T, error)
   391  				fn  string
   392  			}{
   393  				err: nil,
   394  				fn:  fmt.Sprintf("name-%s-%s", unknownTag, unknownTag),
   395  			},
   396  		},
   397  		{
   398  			name:     "invalid name with version",
   399  			filename: "name-{{ .Tag }}/../bla",
   400  			goarch:   "amd64",
   401  			goos:     "linux",
   402  			envs: map[string]string{
   403  				"GITHUB_REF_NAME": "v1.2.3",
   404  			},
   405  			expected: struct {
   406  				err func(*testing.T, error)
   407  				fn  string
   408  			}{
   409  				err: errInvalidFilenameFunc,
   410  			},
   411  		},
   412  		{
   413  			name:     "filename twice unset versions",
   414  			filename: "name-{{ .Tag }}-{{ .Tag }}",
   415  			goarch:   "amd64",
   416  			goos:     "linux",
   417  			expected: struct {
   418  				err func(*testing.T, error)
   419  				fn  string
   420  			}{
   421  				err: nil,
   422  				fn:  fmt.Sprintf("name-%s-%s", unknownTag, unknownTag),
   423  			},
   424  		},
   425  		{
   426  			name:     "filename envs",
   427  			filename: "name-{{ .Env.VAR1 }}-{{ .Os }}-{{ .Arch }}-{{ .Env.VAR2 }}-{{ .Tag }}-end",
   428  			goarch:   "amd64",
   429  			goos:     "linux",
   430  			envs: map[string]string{
   431  				"GITHUB_REF_NAME": "v1.2.3",
   432  			},
   433  			argEnv: "VAR1:var1, VAR2:var2",
   434  			expected: struct {
   435  				err func(*testing.T, error)
   436  				fn  string
   437  			}{
   438  				err: nil,
   439  				fn:  "name-var1-linux-amd64-var2-v1.2.3-end",
   440  			},
   441  		},
   442  	}
   443  
   444  	for _, tt := range tests {
   445  		tt := tt // Re-initializing variable so it is not changed while executing the closure below
   446  		t.Run(tt.name, func(t *testing.T) {
   447  			// Note: disable parallelism to avoid env variable clobbering between tests.
   448  			// t.Parallel()
   449  
   450  			cfg := goReleaserConfigFile{
   451  				Binary:  tt.filename,
   452  				Version: 1,
   453  				Goos:    tt.goos,
   454  				Goarch:  tt.goarch,
   455  			}
   456  			c, err := fromConfig(&cfg)
   457  			if err != nil {
   458  				t.Errorf("fromConfig: %v", err)
   459  			}
   460  
   461  			// Unset env variables, in case the workflow environment sets them.
   462  			for _, k := range []string{"GITHUB_REF_NAME"} {
   463  				os.Unsetenv(k)
   464  			}
   465  
   466  			// Set env variables.
   467  			for k, v := range tt.envs {
   468  				t.Setenv(k, v)
   469  			}
   470  
   471  			b := GoBuildNew("go compiler", c)
   472  
   473  			err = b.SetArgEnvVariables(tt.argEnv)
   474  			if err != nil {
   475  				t.Errorf("SetArgEnvVariables: %v", err)
   476  			}
   477  
   478  			fn, err := b.generateOutputFilename()
   479  			if tt.expected.err != nil {
   480  				tt.expected.err(t, err)
   481  			}
   482  
   483  			// Unset env variables, so that they don't
   484  			// affect other tests.
   485  			for k := range tt.envs {
   486  				os.Unsetenv(k)
   487  			}
   488  
   489  			if err != nil {
   490  				return
   491  			}
   492  
   493  			if fn != tt.expected.fn {
   494  				t.Errorf(cmp.Diff(fn, tt.expected.fn))
   495  			}
   496  		})
   497  	}
   498  }
   499  
   500  func Test_SetArgEnvVariables(t *testing.T) {
   501  	t.Parallel()
   502  
   503  	tests := []struct {
   504  		expected struct {
   505  			env map[string]string
   506  			err func(*testing.T, error)
   507  		}
   508  		name   string
   509  		argEnv string
   510  	}{
   511  		{
   512  			name:   "valid arg envs",
   513  			argEnv: "VAR1:value1, VAR2:value2",
   514  			expected: struct {
   515  				env map[string]string
   516  				err func(*testing.T, error)
   517  			}{
   518  				env: map[string]string{"VAR1": "value1", "VAR2": "value2"},
   519  				err: nil,
   520  			},
   521  		},
   522  		{
   523  			name:   "empty arg envs",
   524  			argEnv: "",
   525  			expected: struct {
   526  				env map[string]string
   527  				err func(*testing.T, error)
   528  			}{
   529  				env: map[string]string{},
   530  				err: nil,
   531  			},
   532  		},
   533  		{
   534  			name:   "valid arg envs not space",
   535  			argEnv: "VAR1:value1,VAR2:value2",
   536  			expected: struct {
   537  				env map[string]string
   538  				err func(*testing.T, error)
   539  			}{
   540  				env: map[string]string{"VAR1": "value1", "VAR2": "value2"},
   541  				err: nil,
   542  			},
   543  		},
   544  		{
   545  			name:   "invalid arg empty 2 values",
   546  			argEnv: "VAR1:value1,",
   547  			expected: struct {
   548  				env map[string]string
   549  				err func(*testing.T, error)
   550  			}{
   551  				err: errInvalidEnvArgumentFunc,
   552  			},
   553  		},
   554  		{
   555  			name:   "invalid arg empty 3 values",
   556  			argEnv: "VAR1:value1,, VAR3:value3",
   557  			expected: struct {
   558  				env map[string]string
   559  				err func(*testing.T, error)
   560  			}{
   561  				err: errInvalidEnvArgumentFunc,
   562  			},
   563  		},
   564  		{
   565  			name:   "invalid arg uses equal",
   566  			argEnv: "VAR1=value1",
   567  			expected: struct {
   568  				env map[string]string
   569  				err func(*testing.T, error)
   570  			}{
   571  				err: errInvalidEnvArgumentFunc,
   572  			},
   573  		},
   574  		{
   575  			name:   "valid single arg",
   576  			argEnv: "VAR1:value1",
   577  			expected: struct {
   578  				env map[string]string
   579  				err func(*testing.T, error)
   580  			}{
   581  				env: map[string]string{"VAR1": "value1"},
   582  				err: nil,
   583  			},
   584  		},
   585  		{
   586  			name:   "invalid valid single arg with empty",
   587  			argEnv: "VAR1:value1:",
   588  			expected: struct {
   589  				env map[string]string
   590  				err func(*testing.T, error)
   591  			}{
   592  				err: errInvalidEnvArgumentFunc,
   593  			},
   594  		},
   595  	}
   596  
   597  	for _, tt := range tests {
   598  		tt := tt // Re-initializing variable so it is not changed while executing the closure below
   599  		t.Run(tt.name, func(t *testing.T) {
   600  			t.Parallel()
   601  
   602  			cfg := goReleaserConfigFile{
   603  				Version: 1,
   604  			}
   605  			c, err := fromConfig(&cfg)
   606  			if err != nil {
   607  				t.Errorf("fromConfig: %v", err)
   608  			}
   609  			b := GoBuildNew("go compiler", c)
   610  
   611  			err = b.SetArgEnvVariables(tt.argEnv)
   612  			if tt.expected.err != nil {
   613  				tt.expected.err(t, err)
   614  			}
   615  
   616  			if err != nil {
   617  				return
   618  			}
   619  
   620  			sorted := cmpopts.SortSlices(func(a, b string) bool { return a < b })
   621  			if !cmp.Equal(b.argEnv, tt.expected.env, sorted) {
   622  				t.Errorf(cmp.Diff(b.argEnv, tt.expected.env))
   623  			}
   624  		})
   625  	}
   626  }
   627  
   628  func Test_generateEnvVariables(t *testing.T) {
   629  	t.Parallel()
   630  
   631  	tests := []struct {
   632  		name     string
   633  		goos     string
   634  		goarch   string
   635  		env      []string
   636  		expected struct {
   637  			err   func(*testing.T, error)
   638  			flags []string
   639  		}
   640  	}{
   641  		{
   642  			name:   "empty flags",
   643  			goos:   "linux",
   644  			goarch: "x86",
   645  			expected: struct {
   646  				err   func(*testing.T, error)
   647  				flags []string
   648  			}{
   649  				flags: []string{"GOOS=linux", "GOARCH=x86"},
   650  				err:   nil,
   651  			},
   652  		},
   653  		{
   654  			name:   "empty goos",
   655  			goarch: "x86",
   656  			expected: struct {
   657  				err   func(*testing.T, error)
   658  				flags []string
   659  			}{
   660  				flags: []string{},
   661  				err:   errEnvVariableNameEmptyFunc,
   662  			},
   663  		},
   664  		{
   665  			name: "empty goarch",
   666  			goos: "windows",
   667  			expected: struct {
   668  				err   func(*testing.T, error)
   669  				flags []string
   670  			}{
   671  				flags: []string{},
   672  				err:   errEnvVariableNameEmptyFunc,
   673  			},
   674  		},
   675  		{
   676  			name:   "invalid flags",
   677  			goos:   "windows",
   678  			goarch: "amd64",
   679  			env:    []string{"VAR1=value1", "VAR2=value2"},
   680  			expected: struct {
   681  				err   func(*testing.T, error)
   682  				flags []string
   683  			}{
   684  				err: errEnvVariableNameNotAllowedFunc,
   685  			},
   686  		},
   687  		{
   688  			name:   "invalid flags",
   689  			goos:   "windows",
   690  			goarch: "amd64",
   691  			env:    []string{"GOVAR1=value1", "GOVAR2=value2", "CGO_VAR1=val1", "CGO_VAR2=val2"},
   692  			expected: struct {
   693  				err   func(*testing.T, error)
   694  				flags []string
   695  			}{
   696  				flags: []string{
   697  					"GOOS=windows", "GOARCH=amd64",
   698  					"GOVAR1=value1", "GOVAR2=value2",
   699  					"CGO_VAR1=val1", "CGO_VAR2=val2",
   700  				},
   701  				err: nil,
   702  			},
   703  		},
   704  	}
   705  
   706  	for _, tt := range tests {
   707  		tt := tt // Re-initializing variable so it is not changed while executing the closure below
   708  		t.Run(tt.name, func(t *testing.T) {
   709  			t.Parallel()
   710  
   711  			cfg := goReleaserConfigFile{
   712  				Version: 1,
   713  				Goos:    tt.goos,
   714  				Goarch:  tt.goarch,
   715  				Env:     tt.env,
   716  			}
   717  			c, err := fromConfig(&cfg)
   718  			if err != nil {
   719  				t.Errorf("fromConfig: %v", err)
   720  			}
   721  			b := GoBuildNew("go compiler", c)
   722  
   723  			flags, err := b.generateCommandEnvVariables()
   724  
   725  			if tt.expected.err != nil {
   726  				tt.expected.err(t, err)
   727  			}
   728  			if err != nil {
   729  				return
   730  			}
   731  			expectedFlags := tt.expected.flags
   732  			sorted := cmpopts.SortSlices(func(a, b string) bool { return a < b })
   733  			if !cmp.Equal(flags, expectedFlags, sorted) {
   734  				t.Errorf(cmp.Diff(flags, expectedFlags))
   735  			}
   736  		})
   737  	}
   738  }
   739  
   740  func Test_generateLdflags(t *testing.T) {
   741  	// Disable to avoid env clobbering between tests.
   742  	// t.Parallel()
   743  
   744  	tests := []struct {
   745  		githubEnv  map[string]string
   746  		name       string
   747  		argEnv     string
   748  		outldflags string
   749  		err        func(*testing.T, error)
   750  		inldflags  []string
   751  	}{
   752  		{
   753  			name:       "version ldflags",
   754  			argEnv:     "VERSION_LDFLAGS:value1",
   755  			inldflags:  []string{"{{ .Env.VERSION_LDFLAGS }}"},
   756  			outldflags: "value1",
   757  		},
   758  		{
   759  			name:       "one value with text",
   760  			argEnv:     "VAR1:value1, VAR2:value2",
   761  			inldflags:  []string{"name-{{ .Env.VAR1 }}"},
   762  			outldflags: "name-value1",
   763  		},
   764  		{
   765  			name:       "two values with text",
   766  			argEnv:     "VAR1:value1, VAR2:value2",
   767  			inldflags:  []string{"name-{{ .Env.VAR1 }}-{{ .Env.VAR2 }}"},
   768  			outldflags: "name-value1-value2",
   769  		},
   770  		{
   771  			name:       "two values with text and not space between env",
   772  			argEnv:     "VAR1:value1,VAR2:value2",
   773  			inldflags:  []string{"name-{{ .Env.VAR1 }}-{{ .Env.VAR2 }}"},
   774  			outldflags: "name-value1-value2",
   775  		},
   776  		{
   777  			name:       "same two values with text",
   778  			argEnv:     "VAR1:value1, VAR2:value2",
   779  			inldflags:  []string{"name-{{ .Env.VAR1 }}-{{ .Env.VAR1 }}"},
   780  			outldflags: "name-value1-value1",
   781  		},
   782  		{
   783  			name:       "same value extremeties",
   784  			argEnv:     "VAR1:value1, VAR2:value2",
   785  			inldflags:  []string{"{{ .Env.VAR1 }}-name-{{ .Env.VAR1 }}"},
   786  			outldflags: "value1-name-value1",
   787  		},
   788  		{
   789  			name:       "two different value extremeties",
   790  			argEnv:     "VAR1:value1, VAR2:value2",
   791  			inldflags:  []string{"{{ .Env.VAR1 }}-name-{{ .Env.VAR2 }}"},
   792  			outldflags: "value1-name-value2",
   793  		},
   794  		{
   795  			name:      "undefined env variable",
   796  			argEnv:    "VAR2:value2",
   797  			inldflags: []string{"{{ .Env.VAR1 }}-name-{{ .Env.VAR1 }}"},
   798  			err:       errEnvVariableNameEmptyFunc,
   799  		},
   800  		{
   801  			name:      "undefined env variable 1",
   802  			argEnv:    "VAR2:value2",
   803  			inldflags: []string{"{{ .Env.VAR2 }}-name-{{ .Env.VAR1 }}"},
   804  			err:       errEnvVariableNameEmptyFunc,
   805  		},
   806  		{
   807  			name:      "empty env variable",
   808  			argEnv:    "",
   809  			inldflags: []string{"{{ .Env.VAR1 }}-name-{{ .Env.VAR1 }}"},
   810  			err:       errEnvVariableNameEmptyFunc,
   811  		},
   812  		{
   813  			name:   "several ldflags",
   814  			argEnv: "VAR1:value1, VAR2:value2, VAR3:value3",
   815  			inldflags: []string{
   816  				"{{ .Env.VAR1 }}-name-{{ .Env.VAR2 }}",
   817  				"{{ .Env.VAR1 }}-name-{{ .Env.VAR3 }}",
   818  				"{{ .Env.VAR3 }}-name-{{ .Env.VAR1 }}",
   819  				"{{ .Env.VAR3 }}-name-{{ .Env.VAR2 }}",
   820  			},
   821  			outldflags: "value1-name-value2 value1-name-value3 value3-name-value1 value3-name-value2",
   822  		},
   823  		{
   824  			name:   "several ldflags with start/end",
   825  			argEnv: "VAR1:value1, VAR2:value2, VAR3:value3",
   826  			inldflags: []string{
   827  				"start-{{ .Env.VAR1 }}-name-{{ .Env.VAR2 }}-end",
   828  				"start-{{ .Env.VAR1 }}-name-{{ .Env.VAR3 }}-end",
   829  				"start-{{ .Env.VAR3 }}-name-{{ .Env.VAR1 }}-end",
   830  				"start-{{ .Env.VAR3 }}-name-{{ .Env.VAR2 }}-end",
   831  			},
   832  			outldflags: "start-value1-name-value2-end start-value1-name-value3-end " +
   833  				"start-value3-name-value1-end start-value3-name-value2-end",
   834  		},
   835  		{
   836  			name:   "several ldflags and tag",
   837  			argEnv: "VAR1:value1, VAR2:value2, VAR3:value3",
   838  			githubEnv: map[string]string{
   839  				"GITHUB_REF_NAME": "v1.2.3",
   840  			},
   841  			inldflags: []string{
   842  				"start-{{ .Env.VAR1 }}-name-{{ .Env.VAR2 }}-{{ .Tag }}-end",
   843  				"{{ .Env.VAR1 }}-name-{{ .Env.VAR3 }}",
   844  				"{{ .Env.VAR3 }}-name-{{ .Env.VAR1 }}-{{ .Tag }}-{{ .Tag }}",
   845  				"{{ .Env.VAR3 }}-name-{{ .Env.VAR2 }}-{{ .Tag }}-end",
   846  			},
   847  			outldflags: "start-value1-name-value2-v1.2.3-end value1-name-value3 " +
   848  				"value3-name-value1-v1.2.3-v1.2.3 value3-name-value2-v1.2.3-end",
   849  		},
   850  		{
   851  			name:   "several ldflags and Arch and Os",
   852  			argEnv: "VAR1:value1, VAR2:value2, VAR3:value3",
   853  			githubEnv: map[string]string{
   854  				"GITHUB_REF_NAME": "v1.2.3",
   855  			},
   856  			inldflags: []string{
   857  				"start-{{ .Env.VAR1 }}-name-{{ .Env.VAR2 }}-{{ .Tag }}-{{ .Arch }}-end",
   858  				"{{ .Env.VAR1 }}-name-{{ .Env.VAR3 }}-{{ .Os }}-end",
   859  			},
   860  			outldflags: "start-value1-name-value2-v1.2.3-amd64-end value1-name-value3-linux-end",
   861  		},
   862  	}
   863  
   864  	for _, tt := range tests {
   865  		tt := tt // Re-initializing variable so it is not changed while executing the closure below
   866  		t.Run(tt.name, func(t *testing.T) {
   867  			// Disable to avoid env clobbering between tests.
   868  			// t.Parallel()
   869  
   870  			cfg := goReleaserConfigFile{
   871  				Version: 1,
   872  				Ldflags: tt.inldflags,
   873  				Goarch:  "amd64",
   874  				Goos:    "linux",
   875  			}
   876  			c, err := fromConfig(&cfg)
   877  			if err != nil {
   878  				t.Errorf("fromConfig: %v", err)
   879  			}
   880  
   881  			// Set GitHub env variables.
   882  			for k, v := range tt.githubEnv {
   883  				t.Setenv(k, v)
   884  			}
   885  
   886  			b := GoBuildNew("go compiler", c)
   887  
   888  			err = b.SetArgEnvVariables(tt.argEnv)
   889  			if err != nil {
   890  				t.Errorf("SetArgEnvVariables: %v", err)
   891  			}
   892  			ldflags, err := b.generateLdflags()
   893  
   894  			// Unset env variables, so that they don't
   895  			// affect other tests.
   896  			for k := range tt.githubEnv {
   897  				os.Unsetenv(k)
   898  			}
   899  
   900  			if tt.err != nil {
   901  				tt.err(t, err)
   902  			}
   903  			if err != nil {
   904  				return
   905  			}
   906  			// Note: generated env variables contain the process's env variables too.
   907  			if !cmp.Equal(ldflags, tt.outldflags) {
   908  				t.Errorf(cmp.Diff(ldflags, tt.outldflags))
   909  			}
   910  		})
   911  	}
   912  }
   913  
   914  func Test_generateFlags(t *testing.T) {
   915  	t.Parallel()
   916  
   917  	tests := []struct {
   918  		name  string
   919  		err   func(*testing.T, error)
   920  		flags []string
   921  	}{
   922  		{
   923  			name:  "valid flags",
   924  			flags: []string{"-race", "-x"},
   925  			err:   nil,
   926  		},
   927  		{
   928  			name:  "invalid -mod flags",
   929  			flags: []string{"-mod=whatever", "-x"},
   930  			err:   errUnsupportedArgumentsFunc,
   931  		},
   932  		{
   933  			name: "invalid random flags",
   934  			flags: []string{
   935  				"-a", "-race", "-msan", "-asan",
   936  				"-v", "-x", "-buildinfo", "-buildmode",
   937  				"-buildvcs", "-compiler", "-gccgoflags",
   938  				"-gcflags", "-ldflags", "-linkshared",
   939  				"-tags", "-trimpath", "bla",
   940  			},
   941  			err: errUnsupportedArgumentsFunc,
   942  		},
   943  		{
   944  			name: "valid all flags",
   945  			flags: []string{
   946  				"-a", "-race", "-msan", "-asan",
   947  				"-v", "-x", "-buildinfo", "-buildmode",
   948  				"-buildvcs", "-compiler", "-gccgoflags",
   949  				"-gcflags", "-ldflags", "-linkshared",
   950  				"-tags", "-trimpath",
   951  			},
   952  			err: nil,
   953  		},
   954  	}
   955  
   956  	for _, tt := range tests {
   957  		tt := tt // Re-initializing variable so it is not changed while executing the closure below
   958  		t.Run(tt.name, func(t *testing.T) {
   959  			t.Parallel()
   960  
   961  			cfg := goReleaserConfigFile{
   962  				Version: 1,
   963  				Flags:   tt.flags,
   964  			}
   965  			c, err := fromConfig(&cfg)
   966  			if err != nil {
   967  				t.Errorf("fromConfig: %v", err)
   968  			}
   969  			b := GoBuildNew("gocompiler", c)
   970  
   971  			flags, err := b.generateFlags()
   972  			expectedFlags := append([]string{"gocompiler", "build", "-mod=vendor"}, tt.flags...)
   973  
   974  			if tt.err != nil {
   975  				tt.err(t, err)
   976  			}
   977  			if err != nil {
   978  				return
   979  			}
   980  			// Note: generated env variables contain the process's env variables too.
   981  			sorted := cmpopts.SortSlices(func(a, b string) bool { return a < b })
   982  			if !cmp.Equal(flags, expectedFlags, sorted) {
   983  				t.Errorf(cmp.Diff(flags, expectedFlags))
   984  			}
   985  		})
   986  	}
   987  }
   988  
   989  func Test_generateCommand(t *testing.T) {
   990  	t.Parallel()
   991  
   992  	tests := []struct {
   993  		name  string
   994  		flags []string
   995  	}{
   996  		{
   997  			name:  "some flags",
   998  			flags: []string{"-race", "-x"},
   999  		},
  1000  		{
  1001  			name:  "some other flags",
  1002  			flags: []string{"-x"},
  1003  		},
  1004  		{
  1005  			name: "other random flags",
  1006  			flags: []string{
  1007  				"-a", "-race", "-msan", "-asan",
  1008  				"-v", "-x", "-buildinfo", "-buildmode",
  1009  				"-buildvcs", "-compiler", "-gccgoflags",
  1010  				"-gcflags", "-ldflags", "-linkshared",
  1011  				"-tags", "-trimpath", "bla",
  1012  			},
  1013  		},
  1014  		{
  1015  			name: "even more flags",
  1016  			flags: []string{
  1017  				"-a", "-race", "-msan", "-asan",
  1018  				"-v", "-x", "-buildinfo", "-buildmode",
  1019  				"-buildvcs", "-compiler", "-gccgoflags",
  1020  				"-gcflags", "-ldflags", "-linkshared",
  1021  				"-tags", "-trimpath",
  1022  			},
  1023  		},
  1024  	}
  1025  
  1026  	for _, tt := range tests {
  1027  		tt := tt // Re-initializing variable so it is not changed while executing the closure below
  1028  		t.Run(tt.name, func(t *testing.T) {
  1029  			t.Parallel()
  1030  
  1031  			cfgs := []*goReleaserConfigFile{
  1032  				{
  1033  					Version: 1,
  1034  					Flags:   tt.flags,
  1035  					Main:    asPointer("./some/path/main.go"),
  1036  				},
  1037  				{
  1038  					Version: 1,
  1039  					Flags:   tt.flags,
  1040  				},
  1041  			}
  1042  
  1043  			for _, cfg := range cfgs {
  1044  				c, err := fromConfig(cfg)
  1045  				if err != nil {
  1046  					t.Errorf("fromConfig: %v", err)
  1047  				}
  1048  				b := GoBuildNew("gocompiler", c)
  1049  
  1050  				command := b.generateCommand(tt.flags, "out-binary")
  1051  				expectedCommand := tt.flags
  1052  				expectedCommand = append(expectedCommand, "-o", "out-binary")
  1053  				if cfg.Main != nil {
  1054  					expectedCommand = append(expectedCommand, *cfg.Main)
  1055  				}
  1056  
  1057  				sorted := cmpopts.SortSlices(func(a, b string) bool { return a < b })
  1058  				if !cmp.Equal(command, expectedCommand, sorted) {
  1059  					t.Errorf(cmp.Diff(command, expectedCommand))
  1060  				}
  1061  			}
  1062  		})
  1063  	}
  1064  }
  1065  
  1066  func asPointer(s string) *string {
  1067  	return &s
  1068  }
  1069  
  1070  func TestGoBuild_Run(t *testing.T) {
  1071  	type fields struct {
  1072  		cfg    *GoReleaserConfig
  1073  		argEnv map[string]string
  1074  		goc    string
  1075  	}
  1076  	type args struct {
  1077  		dry bool
  1078  	}
  1079  	tests := []struct {
  1080  		name    string
  1081  		err     func(*testing.T, error)
  1082  		fields  fields
  1083  		args    args
  1084  		wantErr bool
  1085  	}{
  1086  		{
  1087  			name: "dry run valid flags",
  1088  			fields: fields{
  1089  				cfg: &GoReleaserConfig{
  1090  					Goos:   "linux",
  1091  					Goarch: "amd64",
  1092  					Binary: "binary",
  1093  					Main:   asPointer("../builders/go/main.go"),
  1094  					Dir:    asPointer("../builders/go"),
  1095  					Ldflags: []string{
  1096  						"-X main.version=1.0.0",
  1097  					},
  1098  				},
  1099  			},
  1100  			args: args{
  1101  				dry: true,
  1102  			},
  1103  		},
  1104  		{
  1105  			name: "non-dry valid flags",
  1106  			fields: fields{
  1107  				cfg: &GoReleaserConfig{
  1108  					Goos:   "linux",
  1109  					Goarch: "amd64",
  1110  					Binary: "/tmp/binary",
  1111  					Main:   asPointer("main.go"),
  1112  					Dir:    asPointer("./testdata/go"),
  1113  					Ldflags: []string{
  1114  						"-X main.version=1.0.0",
  1115  					},
  1116  				},
  1117  			},
  1118  			args: args{
  1119  				dry: false,
  1120  			},
  1121  		},
  1122  		{
  1123  			name: "slash in the binary name",
  1124  			fields: fields{
  1125  				cfg: &GoReleaserConfig{
  1126  					Goos:   "linux",
  1127  					Goarch: "amd64",
  1128  					Binary: "tmp/binary",
  1129  					Main:   asPointer("../builders/go/main.go"),
  1130  					Dir:    asPointer("../builders/go"),
  1131  				},
  1132  			},
  1133  			args: args{
  1134  				dry: true,
  1135  			},
  1136  			wantErr: true,
  1137  			err:     errInvalidFilenameFunc,
  1138  		},
  1139  		{
  1140  			name: "dry run - invalid flags",
  1141  			fields: fields{
  1142  				cfg: &GoReleaserConfig{
  1143  					Goos:    "linux",
  1144  					Goarch:  "amd64",
  1145  					Binary:  "binary",
  1146  					Main:    asPointer("../builders/go/main.go"),
  1147  					Dir:     asPointer("../builders/go"),
  1148  					Ldflags: []string{},
  1149  				},
  1150  			},
  1151  			args: args{
  1152  				dry: true,
  1153  			},
  1154  			wantErr: false,
  1155  		},
  1156  	}
  1157  	for _, tt := range tests {
  1158  		t.Run(tt.name, func(t *testing.T) {
  1159  			b := &GoBuild{
  1160  				cfg:    tt.fields.cfg,
  1161  				goc:    tt.fields.goc,
  1162  				argEnv: tt.fields.argEnv,
  1163  			}
  1164  			t.Setenv("OUTPUT_BINARY", tt.fields.cfg.Binary)
  1165  			// if the test is not dry run , then code has to look for golang binary
  1166  			if !tt.args.dry {
  1167  				path, err := exec.LookPath("go")
  1168  				if err != nil {
  1169  					t.Errorf("exec.LookPath: %v", err)
  1170  				}
  1171  				b.goc = path
  1172  			}
  1173  			err := b.Run(tt.args.dry)
  1174  			if err != nil != tt.wantErr {
  1175  				t.Errorf("Run() error = %v, wantErr %v", err, tt.wantErr)
  1176  			}
  1177  			if tt.err != nil {
  1178  				if err == nil {
  1179  					t.Errorf("Run() error = nil, but wanted error")
  1180  				} else if tt.err != nil {
  1181  					tt.err(t, err)
  1182  				}
  1183  			}
  1184  		})
  1185  	}
  1186  }