diff --git a/api/internal/git/cloner.go b/api/internal/git/cloner.go index 78f4f32cbb..d15fa75903 100644 --- a/api/internal/git/cloner.go +++ b/api/internal/git/cloner.go @@ -22,15 +22,11 @@ func ClonerUsingGitExec(repoSpec *RepoSpec) error { if err = r.run("init"); err != nil { return err } - if err = r.run( - "remote", "add", "origin", repoSpec.CloneSpec()); err != nil { - return err - } ref := "HEAD" if repoSpec.Ref != "" { ref = repoSpec.Ref } - if err = r.run("fetch", "--depth=1", "origin", ref); err != nil { + if err = r.run("fetch", "--depth=1", repoSpec.CloneSpec(), ref); err != nil { return err } if err = r.run("checkout", "FETCH_HEAD"); err != nil { diff --git a/api/internal/git/gitrunner.go b/api/internal/git/gitrunner.go index fcc7130cdf..c6d12d0156 100644 --- a/api/internal/git/gitrunner.go +++ b/api/internal/git/gitrunner.go @@ -46,9 +46,9 @@ func (r gitRunner) run(args ...string) error { cmd.String(), r.duration, func() error { - _, err := cmd.CombinedOutput() + out, err := cmd.CombinedOutput() if err != nil { - return errors.Wrapf(err, "git cmd = '%s'", cmd.String()) + return errors.Wrapf(err, "failed to run '%s': %s", cmd.String(), string(out)) } return err }) diff --git a/api/internal/git/repospec.go b/api/internal/git/repospec.go index c38f8081ae..a1c29e6c71 100644 --- a/api/internal/git/repospec.go +++ b/api/internal/git/repospec.go @@ -119,8 +119,12 @@ func parseGitURL(n string) ( return } host, n = parseHostSpec(n) - gitSuff = gitSuffix + isLocal := strings.HasPrefix(host, "file://") + if !isLocal { + gitSuff = gitSuffix + } if strings.Contains(n, gitSuffix) { + gitSuff = gitSuffix index := strings.Index(n, gitSuffix) orgRepo = n[0:index] n = n[index+len(gitSuffix):] @@ -131,6 +135,19 @@ func parseGitURL(n string) ( return } + if isLocal { + if idx := strings.Index(n, "//"); idx > 0 { + orgRepo = n[:idx] + n = n[idx+2:] + path, gitRef, gitTimeout, gitSubmodules = peelQuery(n) + return + } + path, gitRef, gitTimeout, gitSubmodules = peelQuery(n) + orgRepo = path + path = "" + return + } + i := strings.Index(n, "/") if i < 1 { path, gitRef, gitTimeout, gitSubmodules = peelQuery(n) @@ -200,7 +217,7 @@ func parseHostSpec(n string) (string, string) { // Start accumulating the host part. for _, p := range []string{ // Order matters here. - "git::", "gh:", "ssh://", "https://", "http://", + "git::", "gh:", "ssh://", "https://", "http://", "file://", "git@", "github.com:", "github.com/"} { if len(p) < len(n) && strings.ToLower(n[:len(p)]) == p { n = n[len(p):] diff --git a/api/internal/git/repospec_test.go b/api/internal/git/repospec_test.go index 384209cfff..ad63b4aa02 100644 --- a/api/internal/git/repospec_test.go +++ b/api/internal/git/repospec_test.go @@ -11,6 +11,7 @@ import ( "time" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestNewRepoSpecFromUrl_Permute(t *testing.T) { @@ -61,6 +62,7 @@ func TestNewRepoSpecFromUrl_Permute(t *testing.T) { rs, err := NewRepoSpecFromURL(uri) if err != nil { t.Errorf("problem %v", err) + continue } if rs.Host != hostSpec { bad = append(bad, []string{"host", uri, rs.Host, hostSpec}) @@ -120,6 +122,7 @@ func TestNewRepoSpecFromUrl_Smoke(t *testing.T) { repoSpec RepoSpec cloneSpec string absPath string + skip string }{ { name: "t1", @@ -285,11 +288,134 @@ func TestNewRepoSpecFromUrl_Smoke(t *testing.T) { GitSuffix: ".git", }, }, + { + name: "t15", + input: "https://github.com/kubernetes-sigs/kustomize//examples/multibases/dev/?ref=v1.0.6", + cloneSpec: "https://github.com/kubernetes-sigs/kustomize.git", + absPath: notCloned.Join("/examples/multibases/dev"), + repoSpec: RepoSpec{ + Host: "https://github.com/", + OrgRepo: "kubernetes-sigs/kustomize", + Path: "/examples/multibases/dev/", + Ref: "v1.0.6", + GitSuffix: ".git", + }, + }, + { + name: "t16", + input: "file://a/b/c/someRepo.git/somepath?ref=someBranch", + cloneSpec: "file://a/b/c/someRepo.git", + absPath: notCloned.Join("somepath"), + repoSpec: RepoSpec{ + Host: "file://", + OrgRepo: "a/b/c/someRepo", + Path: "somepath", + Ref: "someBranch", + GitSuffix: ".git", + }, + }, + { + name: "t17", + input: "file://a/b/c/someRepo//somepath?ref=someBranch", + cloneSpec: "file://a/b/c/someRepo", + absPath: notCloned.Join("somepath"), + repoSpec: RepoSpec{ + Host: "file://", + OrgRepo: "a/b/c/someRepo", + Path: "somepath", + Ref: "someBranch", + }, + }, + { + name: "t18", + input: "file://a/b/c/someRepo?ref=someBranch", + cloneSpec: "file://a/b/c/someRepo", + absPath: notCloned.String(), + repoSpec: RepoSpec{ + Host: "file://", + OrgRepo: "a/b/c/someRepo", + Ref: "someBranch", + }, + }, + { + name: "t19", + input: "file:///a/b/c/someRepo?ref=someBranch", + cloneSpec: "file:///a/b/c/someRepo", + absPath: notCloned.String(), + repoSpec: RepoSpec{ + Host: "file://", + OrgRepo: "/a/b/c/someRepo", + Ref: "someBranch", + }, + }, + { + name: "t20", + input: "ssh://git@github.com/kubernetes-sigs/kustomize//examples/multibases/dev?ref=v1.0.6", + cloneSpec: "git@github.com:kubernetes-sigs/kustomize.git", + absPath: notCloned.Join("examples/multibases/dev"), + repoSpec: RepoSpec{ + Host: "git@github.com:", + OrgRepo: "kubernetes-sigs/kustomize", + Path: "/examples/multibases/dev", + Ref: "v1.0.6", + GitSuffix: ".git", + }, + }, + { + name: "t21", + input: "file:///a/b/c/someRepo", + cloneSpec: "file:///a/b/c/someRepo", + absPath: notCloned.String(), + repoSpec: RepoSpec{ + Host: "file://", + OrgRepo: "/a/b/c/someRepo", + }, + }, + { + name: "t22", + input: "file:///", + cloneSpec: "file:///", + absPath: notCloned.String(), + repoSpec: RepoSpec{ + Host: "file://", + OrgRepo: "/", + }, + }, + { + name: "t23", + skip: "the `//` repo separator does not work", + input: "https://fake-git-hosting.org/path/to/repo//examples/multibases/dev", + cloneSpec: "https://fake-git-hosting.org/path/to.git", + absPath: notCloned.Join("/examples/multibases/dev"), + repoSpec: RepoSpec{ + Host: "https://fake-git-hosting.org/", + OrgRepo: "path/to/repo", + Path: "/examples/multibases/dev", + GitSuffix: ".git", + }, + }, + { + name: "t24", + skip: "the `//` repo separator does not work", + input: "ssh://alice@acme.co/path/to/repo//examples/multibases/dev", + cloneSpec: "ssh://alice@acme.co/path/to/repo.git", + absPath: notCloned.Join("/examples/multibases/dev"), + repoSpec: RepoSpec{ + Host: "ssh://alice@acme.co", + OrgRepo: "path/to/repo", + Path: "/examples/multibases/dev", + GitSuffix: ".git", + }, + }, } for _, tc := range testcases { t.Run(tc.name, func(t *testing.T) { + if tc.skip != "" { + t.Skip(tc.skip) + } + rs, err := NewRepoSpecFromURL(tc.input) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, tc.cloneSpec, rs.CloneSpec(), "cloneSpec mismatch") assert.Equal(t, tc.absPath, rs.AbsPath(), "absPath mismatch") // some values have defaults. Clear them here so test cases remain compact. diff --git a/api/krusty/originannotation_test.go b/api/krusty/originannotation_test.go index 7adbc9f54c..39c32edff5 100644 --- a/api/krusty/originannotation_test.go +++ b/api/krusty/originannotation_test.go @@ -817,7 +817,7 @@ buildMetadata: [originAnnotations] metadata: annotations: config.kubernetes.io/origin: | - repo: https://github.com/kubernetes-sigs/kustomize + repo: https://github.com/kubernetes-sigs/kustomize.git ref: v1.0.6 configuredIn: examples/ldap/base/kustomization.yaml configuredBy: diff --git a/api/krusty/remoteload_test.go b/api/krusty/remoteload_test.go deleted file mode 100644 index e28c4365a9..0000000000 --- a/api/krusty/remoteload_test.go +++ /dev/null @@ -1,434 +0,0 @@ -// Copyright 2019 The Kubernetes Authors. -// SPDX-License-Identifier: Apache-2.0 - -package krusty_test - -import ( - "bytes" - "encoding/base64" - "fmt" - "io" - "net/http" - "os" - "path/filepath" - "strconv" - "strings" - "testing" - - "github.com/stretchr/testify/require" - "sigs.k8s.io/kustomize/api/krusty" - "sigs.k8s.io/kustomize/api/loader" - "sigs.k8s.io/kustomize/api/resmap" - "sigs.k8s.io/kustomize/kyaml/filesys" - "sigs.k8s.io/kustomize/kyaml/yaml" -) - -const resourcesField = `resources: -- %s` -const resourceErrorFormat = "accumulating resources: accumulation err='accumulating resources from '%s': " -const fileError = "evalsymlink failure" -const repoFindError = "URL is a git repository" - -const multibaseDevExampleBuild = `apiVersion: v1 -kind: Pod -metadata: - labels: - app: myapp - name: dev-myapp-pod -spec: - containers: - - image: nginx:1.7.9 - name: nginx -` - -type remoteResourceCase struct { - local bool // only run locally; doesn't behave as expected on server - kustomization string - error bool - expected string // if error, expected is error string -} - -func createKustDir(content string, require *require.Assertions) (filesys.FileSystem, filesys.ConfirmedDir) { - fSys := filesys.MakeFsOnDisk() - tmpDir, err := filesys.NewTmpConfirmedDir() - require.NoError(err) - require.NoError(fSys.WriteFile(filepath.Join(tmpDir.String(), "kustomization.yaml"), []byte(content))) - return fSys, tmpDir -} - -func checkYaml(actual resmap.ResMap, expected string, require *require.Assertions) { - yml, err := actual.AsYaml() - require.NoError(err) - require.Equal(expected, string(yml)) -} - -func testRemoteResource(require *require.Assertions, test *remoteResourceCase) { - fSys, tmpDir := createKustDir(test.kustomization, require) - - b := krusty.MakeKustomizer(krusty.MakeDefaultOptions()) - m, err := b.Run( - fSys, - tmpDir.String()) - - if test.error { - require.Error(err) - require.Contains(err.Error(), test.expected) - } else { - require.NoError(err) - checkYaml(m, test.expected, require) - } - - require.NoError(fSys.RemoveAll(tmpDir.String())) -} - -func isLocalEnv(require *require.Assertions) bool { - // make variable that determines whether to run local-only tests - if value, exists := os.LookupEnv("IS_LOCAL"); exists { - isLocal, err := strconv.ParseBool(strings.TrimSpace(value)) - require.NoError(err) - return isLocal - } - return false -} - -func runResourceTests(t *testing.T, cases map[string]*remoteResourceCase) { - t.Helper() - - req := require.New(t) - for name, test := range cases { - savedTest := test // test assignment changes; need assignment in this scope (iteration) of range - t.Run(name, func(t *testing.T) { - if savedTest.local && !isLocalEnv(req) { - t.SkipNow() - } - configureGitSSHCommand(t) - testRemoteResource(req, test) - }) - } -} - -func configureGitSSHCommand(t *testing.T) { - t.Helper() - - // This contains a read-only Deploy Key for the kustomize repo. - node, err := yaml.ReadFile("testdata/repo_read_only_ssh_key.yaml") - require.NoError(t, err) - keyB64, err := node.GetString("key") - require.NoError(t, err) - key, err := base64.StdEncoding.DecodeString(keyB64) - require.NoError(t, err) - - // Write the key to a temp file and use it in SSH - f, err := os.CreateTemp("", "kustomize_ssh") - require.NoError(t, err) - _, err = io.Copy(f, bytes.NewReader(key)) - require.NoError(t, err) - cmd := fmt.Sprintf("ssh -i %s", f.Name()) - const SSHCommandKey = "GIT_SSH_COMMAND" - t.Setenv(SSHCommandKey, cmd) - t.Cleanup(func() { - _ = os.Remove(f.Name()) - }) -} - -func TestRemoteLoad(t *testing.T) { - req := require.New(t) - - fSys := filesys.MakeFsOnDisk() - b := krusty.MakeKustomizer(krusty.MakeDefaultOptions()) - - m, err := b.Run( - fSys, - "github.com/kubernetes-sigs/kustomize/examples/multibases/dev/?ref=v1.0.6") - req.NoError(err) - checkYaml(m, multibaseDevExampleBuild, req) -} - -func TestRemoteResourceHttps(t *testing.T) { - tests := map[string]*remoteResourceCase{ - "basic": { - kustomization: ` -resources: -- https://github.com/kubernetes-sigs/kustomize//examples/multibases/dev/?ref=v1.0.6`, - expected: multibaseDevExampleBuild, - }, - ".git repo suffix, no slash suffix": { - kustomization: ` -resources: -- https://github.com/kubernetes-sigs/kustomize.git//examples/multibases/dev?ref=v1.0.6`, - expected: multibaseDevExampleBuild, - }, - "repo": { - kustomization: ` -resources: -- https://github.com/annasong20/kustomize-test.git?ref=main`, - expected: multibaseDevExampleBuild, - }, - "raw remote file": { - kustomization: ` -resources: -- https://raw.githubusercontent.com/kubernetes-sigs/kustomize/v3.1.0/examples/multibases/base/pod.yaml -namePrefix: dev-`, - expected: multibaseDevExampleBuild, - }, - } - - runResourceTests(t, tests) -} - -func TestRemoteResourceSsh(t *testing.T) { - tests := map[string]*remoteResourceCase{ - "scp shorthand": { - kustomization: ` -resources: -- git@github.com:kubernetes-sigs/kustomize//examples/multibases/dev/?ref=v1.0.6`, - expected: multibaseDevExampleBuild, - }, - "full ssh, no ending slash": { - kustomization: ` -resources: -- ssh://git@github.com/kubernetes-sigs/kustomize//examples/multibases/dev?ref=v1.0.6`, - expected: multibaseDevExampleBuild, - }, - "repo": { - local: true, - kustomization: ` -resources: -- ssh://git@github.com/annasong20/kustomize-test.git?ref=main`, - expected: multibaseDevExampleBuild, - }, - } - - runResourceTests(t, tests) -} - -func TestRemoteResourcePort(t *testing.T) { - sshURL := "ssh://git@github.com:22/kubernetes-sigs/kustomize//examples/multibases/dev/?ref=v1.0.6" - httpsURL := "https://github.com:443/kubernetes-sigs/kustomize//examples/multibases/dev/?ref=v1.0.6" - - // TODO: ports not currently supported; implement in future - tests := map[string]*remoteResourceCase{ - "ssh": { - local: true, - kustomization: fmt.Sprintf(resourcesField, sshURL), - error: true, - expected: fmt.Sprintf(resourceErrorFormat+fileError, sshURL), - }, - "https": { - kustomization: fmt.Sprintf(resourcesField, httpsURL), - error: true, - expected: fmt.Sprintf(resourceErrorFormat+repoFindError, httpsURL), - }, - } - - runResourceTests(t, tests) -} - -func TestRemoteResourceRepo(t *testing.T) { - tests := map[string]*remoteResourceCase{ - "https, no ref": { - // TODO: fix flaky test that sporadically throws errors on server - local: true, - kustomization: ` -resources: -- https://github.com/annasong20/kustomize-test.git`, - expected: multibaseDevExampleBuild, - }, - "ssh, no ref": { - local: true, - kustomization: ` -resources: -- git@github.com:annasong20/kustomize-test.git`, - expected: multibaseDevExampleBuild, - }, - } - - runResourceTests(t, tests) -} - -func TestRemoteResourceParameters(t *testing.T) { - httpsNoParam := "https://github.com/kubernetes-sigs/kustomize//examples/multibases/dev/" - httpsMasterBranch := "https://github.com/kubernetes-sigs/kustomize//examples/multibases/dev?ref=master" - sshNoParams := "git@github.com:kubernetes-sigs/kustomize//examples/multibases/dev" - - // TODO: cases with expected errors are query parameter combinations that aren't supported yet; implement in future - // TODO: fix flaky tests (non-ssh tests that we skip) that sporadically fail on server - tests := map[string]*remoteResourceCase{ - "https no params": { - local: true, - kustomization: fmt.Sprintf(resourcesField, httpsNoParam), - error: true, - expected: fmt.Sprintf(resourceErrorFormat+repoFindError, httpsNoParam), - }, - "https master": { - local: true, - kustomization: fmt.Sprintf(resourcesField, httpsMasterBranch), - error: true, - expected: fmt.Sprintf(resourceErrorFormat+repoFindError, httpsMasterBranch), - }, - "https master and no submodules": { - local: true, - kustomization: ` -resources: -- https://github.com/kubernetes-sigs/kustomize//examples/multibases/dev?ref=master&submodules=false`, - expected: multibaseDevExampleBuild, - }, - "https all params": { - local: true, - kustomization: ` -resources: -- https://github.com/kubernetes-sigs/kustomize//examples/multibases/dev?ref=v1.0.6&timeout=10&submodules=true`, - expected: multibaseDevExampleBuild, - }, - "ssh no params": { - local: true, - kustomization: fmt.Sprintf(resourcesField, sshNoParams), - error: true, - expected: fmt.Sprintf(resourceErrorFormat+fileError, sshNoParams), - }, - "ssh all params": { - local: true, - kustomization: ` -resources: -- ssh://git@github.com/annasong20/kustomize-test.git?ref=main&timeout=10&submodules=true`, - expected: multibaseDevExampleBuild, - }, - } - - runResourceTests(t, tests) -} - -func TestRemoteResourceGoGetter(t *testing.T) { - // TODO: fix flaky tests (the ones that we skip) that fail sporadically on server - tests := map[string]*remoteResourceCase{ - "git detector with / subdirectory separator": { - local: true, - kustomization: ` -resources: -- github.com/kubernetes-sigs/kustomize/examples/multibases/dev/?ref=v1.0.6`, - expected: multibaseDevExampleBuild, - }, - "git detector for repo": { - local: true, - kustomization: ` -resources: -- github.com/annasong20/kustomize-test`, - expected: multibaseDevExampleBuild, - }, - "https with / subdirectory separator": { - local: true, - kustomization: ` -resources: -- https://github.com/kubernetes-sigs/kustomize/examples/multibases/dev/?ref=v1.0.6`, - expected: multibaseDevExampleBuild, - }, - "git forced protocol": { - kustomization: ` -resources: -- git::https://github.com/kubernetes-sigs/kustomize//examples/multibases/dev/?ref=v1.0.6`, - expected: multibaseDevExampleBuild, - }, - "git forced protocol with / subdirectory separator": { - local: true, - kustomization: ` -resources: -- git::https://github.com/kubernetes-sigs/kustomize/examples/multibases/dev/?ref=v1.0.6`, - expected: multibaseDevExampleBuild, - }, - } - - runResourceTests(t, tests) -} - -func TestRemoteResourceWithHttpError(t *testing.T) { - req := require.New(t) - - url404 := "https://github.com/thisisa404.yaml" - fSys, tmpDir := createKustDir(fmt.Sprintf(resourcesField, url404), req) - b := krusty.MakeKustomizer(krusty.MakeDefaultOptions()) - - _, err := b.Run(fSys, tmpDir.String()) - - httpErr := fmt.Errorf("%w: status code %d (%s)", loader.ErrHTTP, 404, http.StatusText(404)) - accuFromErr := fmt.Errorf("accumulating resources from '%s': %w", url404, httpErr) - expectedErr := fmt.Errorf("accumulating resources: %w", accuFromErr) - req.EqualErrorf(err, expectedErr.Error(), url404) - - req.NoError(fSys.RemoveAll(tmpDir.String())) -} - -func TestRemoteResourceAnnoOrigin(t *testing.T) { - test := remoteResourceCase{ - kustomization: ` -resources: -- github.com/kubernetes-sigs/kustomize/examples/multibases/dev/?ref=v1.0.6 -buildMetadata: [originAnnotations] -`, - expected: `apiVersion: v1 -kind: Pod -metadata: - annotations: - config.kubernetes.io/origin: | - path: examples/multibases/base/pod.yaml - repo: https://github.com/kubernetes-sigs/kustomize - ref: v1.0.6 - labels: - app: myapp - name: dev-myapp-pod -spec: - containers: - - image: nginx:1.7.9 - name: nginx -`, - } - - testRemoteResource(require.New(t), &test) -} - -func TestRemoteResourceAsBaseWithAnnoOrigin(t *testing.T) { - req := require.New(t) - - fSys := filesys.MakeFsOnDisk() - b := krusty.MakeKustomizer(krusty.MakeDefaultOptions()) - tmpDir, err := filesys.NewTmpConfirmedDir() - req.NoError(err) - base := filepath.Join(tmpDir.String(), "base") - prod := filepath.Join(tmpDir.String(), "prod") - req.NoError(fSys.Mkdir(base)) - req.NoError(fSys.Mkdir(prod)) - req.NoError(fSys.WriteFile(filepath.Join(base, "kustomization.yaml"), []byte(` -resources: -- github.com/kubernetes-sigs/kustomize/examples/multibases/dev/?ref=v1.0.6 -`))) - req.NoError(fSys.WriteFile(filepath.Join(prod, "kustomization.yaml"), []byte(` -resources: -- ../base -namePrefix: prefix- -buildMetadata: [originAnnotations] -`))) - - m, err := b.Run( - fSys, - prod) - req.NoError(err) - - expected := `apiVersion: v1 -kind: Pod -metadata: - annotations: - config.kubernetes.io/origin: | - path: examples/multibases/base/pod.yaml - repo: https://github.com/kubernetes-sigs/kustomize - ref: v1.0.6 - labels: - app: myapp - name: prefix-dev-myapp-pod -spec: - containers: - - image: nginx:1.7.9 - name: nginx -` - checkYaml(m, expected, req) - - req.NoError(fSys.RemoveAll(tmpDir.String())) -} diff --git a/api/krusty/remoteloader_test.go b/api/krusty/remoteloader_test.go new file mode 100644 index 0000000000..e5ee3ded6f --- /dev/null +++ b/api/krusty/remoteloader_test.go @@ -0,0 +1,443 @@ +// Copyright 2022 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package krusty_test + +import ( + "bytes" + "encoding/base64" + "fmt" + "io" + "os" + "os/exec" + "path/filepath" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "sigs.k8s.io/kustomize/api/krusty" + "sigs.k8s.io/kustomize/api/loader" + "sigs.k8s.io/kustomize/api/resmap" + "sigs.k8s.io/kustomize/kyaml/filesys" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +func TestRemoteLoad_LocalProtocol(t *testing.T) { + type testRepos struct { + root string + simple string + noSuffix string + multiBaseDev string + withSubmodule string + } + + // creates git repos under a root temporary directory with the following structure + // root/ + // simple.git/ - base with just a pod + // nosuffix/ - same as simple.git/ without the .git suffix + // multibase.git/ - base with a dev overlay + // with-submodule.git/ - includes `simple` as a submodule + // submodule/ - the submodule referencing `simple + createGitRepos := func(t *testing.T) testRepos { + t.Helper() + + bash := func(script string) { + cmd := exec.Command("sh", "-c", script) + o, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("error running %v\nerr: %v\n%s", script, err, string(o)) + } + } + root := t.TempDir() + bash(fmt.Sprintf(` +set -eux + +export ROOT="%s" +export GIT_AUTHOR_EMAIL=nobody@kustomize.io +export GIT_AUTHOR_NAME=Nobody +export GIT_COMMITTER_EMAIL=nobody@kustomize.io +export GIT_COMMITTER_NAME=Nobody + +cp -r testdata/remoteload/simple $ROOT/simple.git +( + cd $ROOT/simple.git + git init --initial-branch=main + git add . + git commit -m "import" + git checkout -b change-image + cat >>kustomization.yaml <