diff --git a/components/content-service/pkg/initializer/git.go b/components/content-service/pkg/initializer/git.go index f7fe45d396eca9..e3eaccb3f240d4 100644 --- a/components/content-service/pkg/initializer/git.go +++ b/components/content-service/pkg/initializer/git.go @@ -198,13 +198,13 @@ func (ws *GitInitializer) realizeCloneTarget(ctx context.Context) (err error) { return err } - if err := ws.Git(ctx, "checkout", "-B", branchName, "origin/"+ws.CloneTarget); err != nil { + if err := ws.Git(ctx, "-c", "core.hooksPath=/dev/null", "checkout", "-B", branchName, "origin/"+ws.CloneTarget); err != nil { log.WithError(err).WithField("remoteURI", ws.RemoteURI).WithField("branch", branchName).Error("Cannot fetch remote branch") return err } case LocalBranch: // checkout local branch based on remote HEAD - if err := ws.Git(ctx, "checkout", "-B", ws.CloneTarget, "origin/HEAD", "--no-track"); err != nil { + if err := ws.Git(ctx, "-c", "core.hooksPath=/dev/null", "checkout", "-B", ws.CloneTarget, "origin/HEAD", "--no-track"); err != nil { return err } case RemoteCommit: @@ -216,7 +216,7 @@ func (ws *GitInitializer) realizeCloneTarget(ctx context.Context) (err error) { } // checkout specific commit - if err := ws.Git(ctx, "checkout", ws.CloneTarget); err != nil { + if err := ws.Git(ctx, "-c", "core.hooksPath=/dev/null", "checkout", ws.CloneTarget); err != nil { return err } default: diff --git a/test/tests/workspace/git_hooks_test.go b/test/tests/workspace/git_hooks_test.go new file mode 100644 index 00000000000000..84f95304c4ae21 --- /dev/null +++ b/test/tests/workspace/git_hooks_test.go @@ -0,0 +1,141 @@ +// Copyright (c) 2022 Gitpod GmbH. All rights reserved. +// Licensed under the GNU Affero General Public License (AGPL). +// See License-AGPL.txt in the project root for license information. + +package workspace + +import ( + "context" + "os" + "testing" + "time" + + "sigs.k8s.io/e2e-framework/pkg/envconf" + "sigs.k8s.io/e2e-framework/pkg/features" + + agent "github.com/gitpod-io/gitpod/test/pkg/agent/workspace/api" + "github.com/gitpod-io/gitpod/test/pkg/integration" +) + +const ( + FILE_CREATED_HOOKS = "output.txt" +) + +type GitHooksTestCase struct { + Name string + ContextURL string + WorkspaceRoot string +} + +func TestGitHooks(t *testing.T) { + userToken, _ := os.LookupEnv("USER_TOKEN") + integration.SkipWithoutUsername(t, username) + integration.SkipWithoutUserToken(t, userToken) + + parallelLimiter := make(chan struct{}, 2) + + tests := []GitHooksTestCase{ + { + Name: "husky", + ContextURL: "https://github.com/gitpod-io/gitpod-test-repo/tree/husky", + WorkspaceRoot: "/workspace/gitpod-test-repo", + }, + } + + f := features.New("git hooks"). + WithLabel("component", "server"). + Assess("should run git hooks tests", func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { + ffs := []struct { + Name string + FF string + }{ + {Name: "classic"}, + {Name: "pvc", FF: "persistent_volume_claim"}, + } + + for _, ff := range ffs { + func() { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) + defer cancel() + + api := integration.NewComponentAPI(ctx, cfg.Namespace(), kubeconfig, cfg.Client()) + defer api.Done(t) + + username := username + ff.Name + userId, err := api.CreateUser(username, userToken) + if err != nil { + t.Fatal(err) + } + + if err := api.UpdateUserFeatureFlag(userId, ff.FF); err != nil { + t.Fatal(err) + } + }() + } + + for _, ff := range ffs { + for _, test := range tests { + t.Run(test.ContextURL+"_"+ff.Name, func(t *testing.T) { + t.Parallel() + + t.Logf("Waiting %s", test.ContextURL+"_"+ff.Name) + + parallelLimiter <- struct{}{} + defer func() { + <-parallelLimiter + }() + + t.Logf("Running %s", test.ContextURL+"_"+ff.Name) + + username := username + ff.Name + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) + defer cancel() + + api := integration.NewComponentAPI(ctx, cfg.Namespace(), kubeconfig, cfg.Client()) + defer api.Done(t) + + wsInfo, stopWs, err := integration.LaunchWorkspaceFromContextURL(t, ctx, test.ContextURL, username, api) + if err != nil { + t.Fatal(err) + } + + defer func() { + sctx, scancel := context.WithTimeout(context.Background(), 10*time.Minute) + defer scancel() + + sapi := integration.NewComponentAPI(sctx, cfg.Namespace(), kubeconfig, cfg.Client()) + defer sapi.Done(t) + + if _, err := stopWs(true, sapi); err != nil { + t.Fatal(err) + } + }() + rsa, closer, err := integration.Instrument(integration.ComponentWorkspace, "workspace", cfg.Namespace(), kubeconfig, cfg.Client(), integration.WithInstanceID(wsInfo.LatestInstance.ID)) + if err != nil { + t.Fatal(err) + } + defer rsa.Close() + integration.DeferCloser(t, closer) + + var ls agent.ListDirResponse + err = rsa.Call("WorkspaceAgent.ListDir", &agent.ListDirRequest{ + Dir: test.WorkspaceRoot, + }, &ls) + if err != nil { + t.Fatal(err) + } + for _, f := range ls.Files { + if f == FILE_CREATED_HOOKS { + t.Fatal("Checkout hooks are executed") + } + } + }) + } + } + return ctx + }). + Feature() + + testEnv.Test(t, f) +}