forked from runatlantis/atlantis
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgit_cred_writer.go
141 lines (127 loc) · 4.51 KB
/
git_cred_writer.go
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
package vcs
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"github.com/pkg/errors"
"github.com/runatlantis/atlantis/server/logging"
)
// WriteGitCreds generates a .git-credentials file containing the username and token
// used for authenticating with git over HTTPS
// It will create the file in home/.git-credentials
// If ghAccessToken is true we will look for a line starting with https://x-access-token and ending with gitHostname and replace it.
func WriteGitCreds(gitUser string, gitToken string, gitHostname string, home string, logger logging.SimpleLogging, ghAccessToken bool) error {
const credsFilename = ".git-credentials"
credsFile := filepath.Join(home, credsFilename)
credsFileContentsPattern := `https://%s:%s@%s` // nolint: gosec
config := fmt.Sprintf(credsFileContentsPattern, gitUser, gitToken, gitHostname)
// If the file doesn't exist, write it.
if _, err := os.Stat(credsFile); err != nil {
if err := os.WriteFile(credsFile, []byte(config), 0600); err != nil {
return errors.Wrapf(err, "writing generated %s file with user, token and hostname to %s", credsFilename, credsFile)
}
logger.Info("wrote git credentials to %s", credsFile)
} else {
hasLine, err := fileHasLine(config, credsFile)
if err != nil {
return err
}
if hasLine {
logger.Debug("git credentials file has expected contents, not modifying")
return nil
}
if ghAccessToken {
hasGHToken, err := fileHasGHToken(gitUser, gitHostname, credsFile)
if err != nil {
return err
}
if hasGHToken {
// Need to replace the line.
if err := fileLineReplace(config, gitUser, gitHostname, credsFile); err != nil {
return errors.Wrap(err, "replacing git credentials line for github app")
}
logger.Info("updated git credentials in %s", credsFile)
} else {
if err := fileAppend(config, credsFile); err != nil {
return err
}
logger.Info("wrote git credentials to %s", credsFile)
}
} else {
// Otherwise we need to append the line.
if err := fileAppend(config, credsFile); err != nil {
return err
}
logger.Info("wrote git credentials to %s", credsFile)
}
}
credentialCmd := exec.Command("git", "config", "--global", "credential.helper", "store")
if out, err := credentialCmd.CombinedOutput(); err != nil {
return errors.Wrapf(err, "There was an error running %s: %s", strings.Join(credentialCmd.Args, " "), string(out))
}
logger.Info("successfully ran %s", strings.Join(credentialCmd.Args, " "))
urlCmd := exec.Command("git", "config", "--global", fmt.Sprintf("url.https://%s@%s.insteadOf", gitUser, gitHostname), fmt.Sprintf("ssh://git@%s", gitHostname)) // nolint: gosec
if out, err := urlCmd.CombinedOutput(); err != nil {
return errors.Wrapf(err, "There was an error running %s: %s", strings.Join(urlCmd.Args, " "), string(out))
}
logger.Info("successfully ran %s", strings.Join(urlCmd.Args, " "))
return nil
}
func fileHasLine(line string, filename string) (bool, error) {
currContents, err := os.ReadFile(filename) // nolint: gosec
if err != nil {
return false, errors.Wrapf(err, "reading %s", filename)
}
for _, l := range strings.Split(string(currContents), "\n") {
if l == line {
return true, nil
}
}
return false, nil
}
func fileAppend(line string, filename string) error {
currContents, err := os.ReadFile(filename) // nolint: gosec
if err != nil {
return err
}
if len(currContents) > 0 && !strings.HasSuffix(string(currContents), "\n") {
line = "\n" + line
}
return os.WriteFile(filename, []byte(string(currContents)+line), 0600)
}
func fileLineReplace(line, user, host, filename string) error {
currContents, err := os.ReadFile(filename) // nolint: gosec
if err != nil {
return err
}
prevLines := strings.Split(string(currContents), "\n")
var newLines []string
for _, l := range prevLines {
if strings.HasPrefix(l, "https://"+user) && strings.HasSuffix(l, host) {
newLines = append(newLines, line)
} else {
newLines = append(newLines, l)
}
}
toWrite := strings.Join(newLines, "\n")
// there was nothing to replace so we need to append the creds
if toWrite == "" {
return fileAppend(line, filename)
}
return os.WriteFile(filename, []byte(toWrite), 0600)
}
func fileHasGHToken(user, host, filename string) (bool, error) {
currContents, err := os.ReadFile(filename) // nolint: gosec
if err != nil {
return false, err
}
prevLines := strings.Split(string(currContents), "\n")
for _, l := range prevLines {
if strings.HasPrefix(l, "https://"+user) && strings.HasSuffix(l, host) {
return true, nil
}
}
return false, nil
}