File: git.go

package info (click to toggle)
vagrant 2.3.7%2Bgit20230731.5fc64cde%2Bdfsg-3
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 17,616 kB
  • sloc: ruby: 111,820; sh: 462; makefile: 123; ansic: 34; lisp: 1
file content (142 lines) | stat: -rw-r--r-- 3,388 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
package datasource

import (
	"bytes"
	"context"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"

	"github.com/hashicorp/go-hclog"
	"github.com/hashicorp/hcl/v2"
	"github.com/hashicorp/hcl/v2/gohcl"
	"github.com/mitchellh/mapstructure"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"

	"github.com/hashicorp/vagrant-plugin-sdk/terminal"
	"github.com/hashicorp/vagrant/internal/server/proto/vagrant_server"
)

type GitSource struct{}

func newGitSource() Sourcer { return &GitSource{} }

func (s *GitSource) ProjectSource(body hcl.Body, ctx *hcl.EvalContext) (*vagrant_server.Job_DataSource, error) {
	// Decode
	var cfg gitConfig
	if diag := gohcl.DecodeBody(body, ctx, &cfg); len(diag) > 0 {
		return nil, diag
	}

	// Return the data source
	return &vagrant_server.Job_DataSource{
		Source: &vagrant_server.Job_DataSource_Git{
			Git: &vagrant_server.Job_Git{
				Url:  cfg.Url,
				Path: cfg.Path,
			},
		},
	}, nil
}

func (s *GitSource) Override(raw *vagrant_server.Job_DataSource, m map[string]string) error {
	src := raw.Source.(*vagrant_server.Job_DataSource_Git).Git

	var md mapstructure.Metadata
	if err := mapstructure.DecodeMetadata(m, src, &md); err != nil {
		return err
	}

	if len(md.Unused) > 0 {
		return fmt.Errorf("invalid override keys: %v", md.Unused)
	}

	return nil
}

func (s *GitSource) Get(
	ctx context.Context,
	log hclog.Logger,
	ui terminal.UI,
	raw *vagrant_server.Job_DataSource,
	baseDir string,
) (string, func() error, error) {
	source := raw.Source.(*vagrant_server.Job_DataSource_Git)

	// Some quick validation
	if p := source.Git.Path; p != "" {
		if filepath.IsAbs(p) {
			return "", nil, status.Errorf(codes.FailedPrecondition,
				"git path must be relative")
		}

		for _, part := range filepath.SplitList(p) {
			if part == ".." {
				return "", nil, status.Errorf(codes.FailedPrecondition,
					"git path may not contain '..'")
			}
		}
	}

	// Create a temporary directory where we will store the cloned data.
	td, err := ioutil.TempDir(baseDir, "vagrant")
	if err != nil {
		return "", nil, err
	}
	closer := func() error {
		return os.RemoveAll(td)
	}

	// Output
	ui.Output("Cloning data from Git", terminal.WithHeaderStyle())
	ui.Output("URL: %s", source.Git.Url, terminal.WithInfoStyle())
	if source.Git.Ref != "" {
		ui.Output("Ref: %s", source.Git.Ref, terminal.WithInfoStyle())
	}

	// Clone
	var output bytes.Buffer
	cmd := exec.CommandContext(ctx, "git", "clone", source.Git.Url, td)
	cmd.Stdout = &output
	cmd.Stderr = &output
	cmd.Stdin = nil
	if err := cmd.Run(); err != nil {
		closer()
		return "", nil, status.Errorf(codes.Aborted,
			"Git clone failed: %s", output.String())
	}

	// Checkout if we have a ref. If we don't have a ref we use the
	// default of whatever we got.
	if ref := source.Git.Ref; ref != "" {
		output.Reset()
		cmd := exec.CommandContext(ctx, "git", "checkout", ref)
		cmd.Dir = td
		cmd.Stdout = &output
		cmd.Stderr = &output
		cmd.Stdin = nil
		if err := cmd.Run(); err != nil {
			closer()
			return "", nil, status.Errorf(codes.Aborted,
				"Git checkout failed: %s", output.String())
		}
	}

	// If we have a path, set it.
	result := td
	if p := source.Git.Path; p != "" {
		result = filepath.Join(result, p)
	}

	return result, closer, nil
}

type gitConfig struct {
	Url  string `hcl:"url,attr"`
	Path string `hcl:"path,optional"`
}

var _ Sourcer = (*GitSource)(nil)