1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
14 "git.arvados.org/arvados.git/sdk/go/arvados"
15 "gopkg.in/src-d/go-billy.v4/osfs"
16 git "gopkg.in/src-d/go-git.v4"
17 git_config "gopkg.in/src-d/go-git.v4/config"
18 git_plumbing "gopkg.in/src-d/go-git.v4/plumbing"
19 git_http "gopkg.in/src-d/go-git.v4/plumbing/transport/http"
20 "gopkg.in/src-d/go-git.v4/storage/memory"
23 type gitMount arvados.Mount
26 sha1re = regexp.MustCompile(`^[0-9a-f]{40}$`)
27 repoUUIDre = regexp.MustCompile(`^[0-9a-z]{5}-s0uqq-[0-9a-z]{15}$`)
30 func (gm gitMount) validate() error {
31 if gm.Path != "" && gm.Path != "/" {
32 return fmt.Errorf("cannot mount git_tree with path %q -- only \"/\" is supported", gm.Path)
34 if !sha1re.MatchString(gm.Commit) {
35 return fmt.Errorf("cannot mount git_tree with commit %q -- must be a 40-char SHA1", gm.Commit)
37 if gm.RepositoryName != "" || gm.GitURL != "" {
38 return fmt.Errorf("cannot mount git_tree -- repository_name and git_url must be empty")
40 if !repoUUIDre.MatchString(gm.UUID) {
41 return fmt.Errorf("cannot mount git_tree with uuid %q -- must be a repository UUID", gm.UUID)
44 return fmt.Errorf("writable git_tree mount is not supported")
49 // ExtractTree extracts the specified tree into dir, which is an
50 // existing empty local directory.
51 func (gm gitMount) extractTree(ac *arvados.Client, dir string, token string) error {
56 dd, err := ac.DiscoveryDocument()
58 return fmt.Errorf("error getting discovery document: %w", err)
60 u, err := url.Parse(dd.GitURL)
62 return fmt.Errorf("parse gitUrl %q: %s", dd.GitURL, err)
64 u, err = u.Parse("/" + gm.UUID + ".git")
66 return fmt.Errorf("build git url from %q, %q: %s", dd.GitURL, gm.UUID, err)
68 store := memory.NewStorage()
69 repo, err := git.Init(store, osfs.New(dir))
71 return fmt.Errorf("init repo: %s", err)
73 _, err = repo.CreateRemote(&git_config.RemoteConfig{
75 URLs: []string{u.String()},
78 return fmt.Errorf("create remote %q: %s", u.String(), err)
80 err = repo.Fetch(&git.FetchOptions{
82 Auth: &git_http.BasicAuth{
88 return fmt.Errorf("git fetch %q: %s", u.String(), err)
90 wt, err := repo.Worktree()
92 return fmt.Errorf("worktree failed: %s", err)
94 err = wt.Checkout(&git.CheckoutOptions{
95 Hash: git_plumbing.NewHash(gm.Commit),
98 return fmt.Errorf("checkout failed: %s", err)
100 err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
104 // copy user rx bits to group and other, in case
105 // prevailing umask is more restrictive than 022
107 mode = mode | ((mode >> 3) & 050) | ((mode >> 6) & 5)
108 return os.Chmod(path, mode)
111 return fmt.Errorf("chmod -R %q: %s", dir, err)