diff options
author | Stan Hu <stanhu@gmail.com> | 2020-09-14 09:02:44 +0300 |
---|---|---|
committer | Stan Hu <stanhu@gmail.com> | 2020-09-15 18:50:55 +0300 |
commit | 3cef6ba80866e1c5367d86d8f29fb1e87eb96a08 (patch) | |
tree | c9ac7c350c6da25c559208694af427dee353a634 | |
parent | 4805333598f9ed608652cfd28650cabf110fc646 (diff) |
Export GL_REPOSITORY and GL_PROJECT_PATH in `git archive` call
To support bundling Git LFS objects within a `git archive` call, we will
need a custom LFS smudge filter that will download the LFS data. To do
this, we will need to make an API call request to check the the
requested LFS object ID actually belongs to the LFS object network, so
we need to export `GL_REPOSITORY` to the environment. `GL_PROJECT_PATH`
may be useful for debugging purposes.
-rw-r--r-- | changelogs/unreleased/sh-export-gl-project-git-archive.yml | 5 | ||||
-rw-r--r-- | internal/command/command.go | 5 | ||||
-rw-r--r-- | internal/git/command.go | 6 | ||||
-rw-r--r-- | internal/git/safecmd.go | 10 | ||||
-rw-r--r-- | internal/git/safecmd_test.go | 32 | ||||
-rw-r--r-- | internal/gitaly/service/repository/archive.go | 8 | ||||
-rw-r--r-- | internal/gitaly/service/repository/archive_test.go | 45 | ||||
-rw-r--r-- | internal/testhelper/testhelper.go | 8 |
8 files changed, 111 insertions, 8 deletions
diff --git a/changelogs/unreleased/sh-export-gl-project-git-archive.yml b/changelogs/unreleased/sh-export-gl-project-git-archive.yml new file mode 100644 index 000000000..1b0999a7a --- /dev/null +++ b/changelogs/unreleased/sh-export-gl-project-git-archive.yml @@ -0,0 +1,5 @@ +--- +title: Export GL_REPOSITORY and GL_PROJECT_PATH in git archive call +merge_request: 2557 +author: +type: changed diff --git a/internal/command/command.go b/internal/command/command.go index 40f8844a9..5ea8ab752 100644 --- a/internal/command/command.go +++ b/internal/command/command.go @@ -458,3 +458,8 @@ func checkNullArgv(cmd *exec.Cmd) error { func (c *Command) Args() []string { return c.cmd.Args } + +// Env is an accessor for the environment variables +func (c *Command) Env() []string { + return c.cmd.Env +} diff --git a/internal/git/command.go b/internal/git/command.go index 866269424..6fb682913 100644 --- a/internal/git/command.go +++ b/internal/git/command.go @@ -9,13 +9,15 @@ import ( "gitlab.com/gitlab-org/gitaly/internal/git/repository" ) -// unsafeCmd creates a git.unsafeCmd with the given args and Repository -func unsafeCmd(ctx context.Context, repo repository.GitRepo, args ...string) (*command.Command, error) { +// unsafeCmdWithEnv creates a git.unsafeCmd with the given args, environment, and Repository +func unsafeCmdWithEnv(ctx context.Context, extraEnv []string, repo repository.GitRepo, args ...string) (*command.Command, error) { args, env, err := argsAndEnv(repo, args...) if err != nil { return nil, err } + env = append(env, extraEnv...) + return unsafeBareCmd(ctx, CmdStream{}, env, args...) } diff --git a/internal/git/safecmd.go b/internal/git/safecmd.go index a0a240123..c45aa3a8f 100644 --- a/internal/git/safecmd.go +++ b/internal/git/safecmd.go @@ -213,15 +213,21 @@ func ConvertGlobalOptions(options *gitalypb.GlobalOptions) []Option { return globals } -// SafeCmd creates a git.Command with the given args and Repository. It +// SafeCmd creates a command.Command with the given args and Repository. It // validates the arguments in the command before executing. func SafeCmd(ctx context.Context, repo repository.GitRepo, globals []Option, sc Cmd) (*command.Command, error) { + return SafeCmdWithEnv(ctx, nil, repo, globals, sc) +} + +// SafeCmdWithEnv creates a command.Command with the given args, environment, and Repository. It +// validates the arguments in the command before executing. +func SafeCmdWithEnv(ctx context.Context, env []string, repo repository.GitRepo, globals []Option, sc Cmd) (*command.Command, error) { args, err := combineArgs(globals, sc) if err != nil { return nil, err } - return unsafeCmd(ctx, repo, args...) + return unsafeCmdWithEnv(ctx, env, repo, args...) } // SafeBareCmd creates a git.Command with the given args, stream, and env. It diff --git a/internal/git/safecmd_test.go b/internal/git/safecmd_test.go index 0829d9a58..6ab199390 100644 --- a/internal/git/safecmd_test.go +++ b/internal/git/safecmd_test.go @@ -200,6 +200,11 @@ func TestSafeCmdValid(t *testing.T) { // ignore first 3 indeterministic args (executable path and repo args) require.Equal(t, tt.expectArgs, cmd.Args()[3:]) + cmd, err = git.SafeCmdWithEnv(ctx, nil, testRepo, tt.globals, tt.subCmd) + require.NoError(t, err) + // ignore first 3 indeterministic args (executable path and repo args) + require.Equal(t, tt.expectArgs, cmd.Args()[3:]) + cmd, err = git.SafeStdinCmd(ctx, testRepo, tt.globals, tt.subCmd) require.NoError(t, err) require.Equal(t, tt.expectArgs, cmd.Args()[3:]) @@ -215,6 +220,33 @@ func TestSafeCmdValid(t *testing.T) { } } +func TestSafeCmdWithEnv(t *testing.T) { + testRepo, _, cleanup := testhelper.NewTestRepo(t) + defer cleanup() + + ctx, cancel := testhelper.Context() + defer cancel() + + reenableGitCmd := disableGitCmd() + defer reenableGitCmd() + + globals := []git.Option{ + git.Flag{Name: "--aaaa-bbbb"}, + } + + subCmd := git.SubCmd{Name: "cccc"} + endOfOptions := "--end-of-options" + expectArgs := []string{"--aaaa-bbbb", "cccc", endOfOptions} + + env := []string{"TEST_VAR1=1", "TEST_VAR2=2"} + + cmd, err := git.SafeCmdWithEnv(ctx, env, testRepo, globals, subCmd) + require.NoError(t, err) + // ignore first 3 indeterministic args (executable path and repo args) + require.Equal(t, expectArgs, cmd.Args()[3:]) + require.Subset(t, cmd.Env(), env) +} + func disableGitCmd() testhelper.Cleanup { oldBinPath := config.Config.Git.BinPath config.Config.Git.BinPath = "/bin/echo" diff --git a/internal/gitaly/service/repository/archive.go b/internal/gitaly/service/repository/archive.go index e60950678..807f97ebd 100644 --- a/internal/gitaly/service/repository/archive.go +++ b/internal/gitaly/service/repository/archive.go @@ -2,6 +2,7 @@ package repository import ( "context" + "fmt" "io" "os" "os/exec" @@ -150,7 +151,12 @@ func handleArchive(ctx context.Context, writer io.Writer, in *gitalypb.GetArchiv pathspecs = append(pathspecs, ":(exclude)"+exclude) } - archiveCommand, err := git.SafeCmd(ctx, in.GetRepository(), nil, git.SubCmd{ + env := []string{ + fmt.Sprintf("GL_REPOSITORY=%s", in.GetRepository().GetGlRepository()), + fmt.Sprintf("GL_PROJECT_PATH=%s", in.GetRepository().GetGlProjectPath()), + } + + archiveCommand, err := git.SafeCmdWithEnv(ctx, env, in.GetRepository(), nil, git.SubCmd{ Name: "archive", Flags: []git.Option{git.ValueFlag{"--format", format}, git.ValueFlag{"--prefix", in.GetPrefix() + "/"}}, Args: args, diff --git a/internal/gitaly/service/repository/archive_test.go b/internal/gitaly/service/repository/archive_test.go index ffbc8540c..4f600d836 100644 --- a/internal/gitaly/service/repository/archive_test.go +++ b/internal/gitaly/service/repository/archive_test.go @@ -9,6 +9,7 @@ import ( "testing" "github.com/stretchr/testify/require" + "gitlab.com/gitlab-org/gitaly/internal/gitaly/config" "gitlab.com/gitlab-org/gitaly/internal/testhelper" "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb" "gitlab.com/gitlab-org/gitaly/streamio" @@ -369,6 +370,50 @@ func TestGetArchivePathInjection(t *testing.T) { require.Zero(t, authorizedKeysFileStat.Size()) } +func TestGetArchiveEnv(t *testing.T) { + serverSocketPath, stop := runRepoServer(t) + defer stop() + + client, conn := newRepositoryClient(t, serverSocketPath) + defer conn.Close() + + testRepo, _, cleanupFn := testhelper.NewTestRepo(t) + defer cleanupFn() + + commitID := "1a0b36b3cdad1d2ee32457c102a8c0b7056fa863" + + ctx, cancel := testhelper.Context() + defer cancel() + + req := &gitalypb.GetArchiveRequest{ + Repository: testRepo, + CommitId: commitID, + } + + tmpFile, err := ioutil.TempFile("", "archive.sh") + require.NoError(t, err) + defer os.Remove(tmpFile.Name()) + + err = tmpFile.Chmod(0755) + require.NoError(t, err) + + tmpFile.Write([]byte(`#!/bin/sh +env | grep ^GL_`)) + tmpFile.Close() + + oldBinPath := config.Config.Git.BinPath + config.Config.Git.BinPath = tmpFile.Name() + defer func() { config.Config.Git.BinPath = oldBinPath }() + + stream, err := client.GetArchive(ctx, req) + require.NoError(t, err) + + data, err := consumeArchive(stream) + require.NoError(t, err) + require.Contains(t, string(data), "GL_REPOSITORY="+testhelper.GlRepository) + require.Contains(t, string(data), "GL_PROJECT_PATH="+testhelper.GlProjectPath) +} + func compressedFileContents(t *testing.T, format gitalypb.GetArchiveRequest_Format, name string) []byte { switch format { case gitalypb.GetArchiveRequest_TAR: diff --git a/internal/testhelper/testhelper.go b/internal/testhelper/testhelper.go index fd0b23387..971963aa0 100644 --- a/internal/testhelper/testhelper.go +++ b/internal/testhelper/testhelper.go @@ -57,6 +57,7 @@ const ( testGitEnv = "testdata/git-env" GlRepository = "project-1" GlID = "user-123" + GlProjectPath = "gitlab-org/gitlab-test" ) var configureOnce sync.Once @@ -501,9 +502,10 @@ func Context(opts ...ContextOpt) (context.Context, func()) { func CreateRepo(t testing.TB, storagePath, relativePath string) *gitalypb.Repository { require.NoError(t, os.MkdirAll(filepath.Dir(storagePath), 0755), "making repo parent dir") return &gitalypb.Repository{ - StorageName: "default", - RelativePath: relativePath, - GlRepository: GlRepository, + StorageName: "default", + RelativePath: relativePath, + GlRepository: GlRepository, + GlProjectPath: GlProjectPath, } } |