Skip to content

Commit 382302a

Browse files
authored
✨ Local config and creds. (#75)
### problem This is needed to support working with multiple repositories simultaneously. ### solution The configuration and credentials cannot be written into the _HOME_ directory. Further, each instance of the SCM needs to have their own. Add command.Command.Env support. Update ssh, git, subversion to write credentials and run with HOME=_dir_ where _dir_ is specific to each instance. The ssh.Agent writes keys in .ssh/ with identity ID prefix. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added support for customizing environment variables when running commands. * **Refactor** * Simplified and unified identity and credential handling for Git, Maven, and Subversion repositories. * Repository-specific configuration and credential files are now stored in hashed directories under the current working directory. * Improved flexibility in repository creation and configuration options with a new option mechanism. * Updated SSH agent handling and environment setup for enhanced reliability and consistent environment variables. * Enhanced Git command execution with improved environment isolation and updated command options. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: Jeff Ortel <[email protected]>
1 parent 17140aa commit 382302a

File tree

7 files changed

+187
-125
lines changed

7 files changed

+187
-125
lines changed

cmd/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@ import (
99
func main() {
1010
_ = command.New("")
1111
_ = ssh.Agent{}
12-
_, _ = repository.New("", nil, nil)
12+
_, _ = repository.New("", nil)
1313
}

command/cmd.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ type Command struct {
2929
Options Options
3030
Path string
3131
Dir string
32+
Env []string
3233
Reporter Reporter
3334
Writer Writer
3435
}
@@ -63,6 +64,7 @@ func (r *Command) RunWith(ctx context.Context) (err error) {
6364
}()
6465
cmd := exec.CommandContext(ctx, r.Path, r.Options...)
6566
cmd.Dir = r.Dir
67+
cmd.Env = r.Env
6668
cmd.Stdout = &r.Writer
6769
cmd.Stderr = &r.Writer
6870
err = cmd.Start()

repository/factory.go

Lines changed: 51 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,27 @@ import (
55

66
hub "github.com/konveyor/tackle2-hub/addon"
77
"github.com/konveyor/tackle2-hub/api"
8+
"github.com/pkg/errors"
89
)
910

1011
var (
11-
addon = hub.Addon
12-
HomeDir = ""
12+
addon = hub.Addon
13+
Dir = ""
1314
)
1415

1516
func init() {
16-
HomeDir, _ = os.UserHomeDir()
17+
Dir, _ = os.Getwd()
1718
}
1819

1920
type Remote = api.Repository
2021

2122
// New SCM repository factory.
22-
func New(destDir string, remote *Remote, identities []api.Ref) (r SCM, err error) {
23+
// Options:
24+
// - *api.Ref
25+
// - api.Ref
26+
// - *api.Identity
27+
// - api.Identity
28+
func New(destDir string, remote *Remote, option ...any) (r SCM, err error) {
2329
var insecure bool
2430
switch remote.Kind {
2531
case "subversion":
@@ -30,7 +36,6 @@ func New(destDir string, remote *Remote, identities []api.Ref) (r SCM, err error
3036
svn := &Subversion{}
3137
svn.Path = destDir
3238
svn.Remote = *remote
33-
svn.Identities = identities
3439
svn.Insecure = insecure
3540
r = svn
3641
default:
@@ -41,11 +46,19 @@ func New(destDir string, remote *Remote, identities []api.Ref) (r SCM, err error
4146
git := &Git{}
4247
git.Path = destDir
4348
git.Remote = *remote
44-
git.Identities = identities
4549
git.Insecure = insecure
4650
r = git
4751
}
4852
err = r.Validate()
53+
if err != nil {
54+
return
55+
}
56+
for _, opt := range option {
57+
err = r.Use(opt)
58+
if err != nil {
59+
return
60+
}
61+
}
4962
return
5063
}
5164

@@ -56,27 +69,47 @@ type SCM interface {
5669
Branch(ref string) (err error)
5770
Commit(files []string, msg string) (err error)
5871
Head() (commit string, err error)
72+
Use(option any) (err error)
5973
}
6074

6175
// Authenticated repository.
6276
type Authenticated struct {
63-
Identities []api.Ref
64-
Insecure bool
77+
Identity api.Identity
78+
Insecure bool
6579
}
6680

67-
// FindIdentity by kind.
68-
func (r *Authenticated) findIdentity(kind string) (matched *api.Identity, found bool, err error) {
69-
for _, ref := range r.Identities {
70-
identity, nErr := addon.Identity.Get(ref.ID)
71-
if nErr != nil {
72-
err = nErr
81+
// Use option.
82+
// Options:
83+
// - *api.Ref
84+
// - api.Ref
85+
// - *api.Identity
86+
// - api.Identity
87+
func (a *Authenticated) Use(option any) (err error) {
88+
var id *api.Identity
89+
switch opt := option.(type) {
90+
case *api.Ref:
91+
if opt == nil {
92+
return
93+
}
94+
id, err = addon.Identity.Get(opt.ID)
95+
if err != nil {
96+
return
97+
}
98+
a.Identity = *id
99+
case api.Ref:
100+
id, err = addon.Identity.Get(opt.ID)
101+
if err != nil {
73102
return
74103
}
75-
if identity.Kind == kind {
76-
found = true
77-
matched = identity
78-
break
104+
a.Identity = *id
105+
case *api.Identity:
106+
if opt != nil {
107+
a.Identity = *opt
79108
}
109+
case api.Identity:
110+
a.Identity = opt
111+
default:
112+
err = errors.Errorf("Invalid option: %T", opt)
80113
}
81114
return
82115
}

repository/git.go

Lines changed: 67 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ package repository
33
import (
44
"errors"
55
"fmt"
6+
"hash/fnv"
67
urllib "net/url"
78
"os"
89
pathlib "path"
10+
"strconv"
911
"strings"
1012

1113
liberr "github.com/jortel/go-utils/error"
@@ -42,34 +44,34 @@ func (r *Git) Validate() (err error) {
4244
// Fetch clones the repository.
4345
func (r *Git) Fetch() (err error) {
4446
url := r.URL()
47+
addon.Activity("[GIT] Home (directory): %s", r.home())
4548
addon.Activity("[GIT] Cloning: %s", url.String())
4649
_ = nas.RmDir(r.Path)
47-
id, found, err := r.findIdentity("source")
48-
if err != nil {
49-
return
50-
}
51-
if found {
50+
if r.Identity.ID != 0 {
5251
addon.Activity(
5352
"[GIT] Using credentials (id=%d) %s.",
54-
id.ID,
55-
id.Name)
56-
} else {
57-
id = &api.Identity{}
53+
r.Identity.ID,
54+
r.Identity.Name)
55+
}
56+
err = nas.MkDir(r.home(), 0755)
57+
if err != nil {
58+
err = liberr.Wrap(err)
59+
return
5860
}
5961
err = r.writeConfig()
6062
if err != nil {
6163
return
6264
}
63-
err = r.writeCreds(id)
65+
err = r.writeCreds()
6466
if err != nil {
6567
return
6668
}
6769
agent := ssh.Agent{}
68-
err = agent.Add(id, url.Host)
70+
err = agent.Add(&r.Identity, url.Host)
6971
if err != nil {
7072
return
7173
}
72-
cmd := command.New("/usr/bin/git")
74+
cmd := r.git()
7375
cmd.Options.Add("clone")
7476
cmd.Options.Add("--depth", "1")
7577
if r.Remote.Branch != "" {
@@ -87,7 +89,7 @@ func (r *Git) Fetch() (err error) {
8789

8890
// Branch creates a branch with the given name if not exist and switch to it.
8991
func (r *Git) Branch(ref string) (err error) {
90-
cmd := command.New("/usr/bin/git")
92+
cmd := r.git()
9193
cmd.Dir = r.Path
9294
cmd.Options.Add("checkout", ref)
9395
err = cmd.Run()
@@ -97,15 +99,17 @@ func (r *Git) Branch(ref string) (err error) {
9799
cmd.Options.Add("checkout", "-b", ref)
98100
}
99101
r.Remote.Branch = ref
100-
return cmd.Run()
102+
err = cmd.Run()
103+
return
101104
}
102105

103106
// addFiles adds files to staging area.
104107
func (r *Git) addFiles(files []string) (err error) {
105-
cmd := command.New("/usr/bin/git")
108+
cmd := r.git()
106109
cmd.Dir = r.Path
107110
cmd.Options.Add("add", files...)
108-
return cmd.Run()
111+
err = cmd.Run()
112+
return
109113
}
110114

111115
// Commit files and push to remote.
@@ -114,20 +118,22 @@ func (r *Git) Commit(files []string, msg string) (err error) {
114118
if err != nil {
115119
return err
116120
}
117-
cmd := command.New("/usr/bin/git")
121+
cmd := r.git()
118122
cmd.Dir = r.Path
119123
cmd.Options.Add("commit")
120-
cmd.Options.Add("--message", msg)
124+
cmd.Options.Add("--allow-empty")
125+
cmd.Options.Add("-m", msg)
121126
err = cmd.Run()
122127
if err != nil {
123128
return err
124129
}
125-
return r.push()
130+
err = r.push()
131+
return
126132
}
127133

128134
// Head returns HEAD commit.
129135
func (r *Git) Head() (commit string, err error) {
130-
cmd := command.New("/usr/bin/git")
136+
cmd := r.git()
131137
cmd.Dir = r.Path
132138
cmd.Options.Add("rev-parse")
133139
cmd.Options.Add("HEAD")
@@ -140,12 +146,25 @@ func (r *Git) Head() (commit string, err error) {
140146
return
141147
}
142148

149+
// git returns git command.
150+
func (r *Git) git() (cmd *command.Command) {
151+
cmd = command.New("/usr/bin/git")
152+
cmd.Env = append(
153+
os.Environ(),
154+
"GIT_TERMINAL_PROMPT=0",
155+
"GIT_TRACE_SETUP=1",
156+
"GIT_TRACE=1",
157+
"HOME="+r.home())
158+
return
159+
}
160+
143161
// push changes to remote.
144162
func (r *Git) push() (err error) {
145-
cmd := command.New("/usr/bin/git")
163+
cmd := r.git()
146164
cmd.Dir = r.Path
147-
cmd.Options.Add("push", "--set-upstream", "origin", r.Remote.Branch)
148-
return cmd.Run()
165+
cmd.Options.Add("push", "origin", "HEAD")
166+
err = cmd.Run()
167+
return
149168
}
150169

151170
// URL returns the parsed URL.
@@ -157,11 +176,7 @@ func (r *Git) URL() (u GitURL) {
157176

158177
// writeConfig writes config file.
159178
func (r *Git) writeConfig() (err error) {
160-
path := pathlib.Join(HomeDir, ".gitconfig")
161-
found, err := nas.Exists(path)
162-
if found || err != nil {
163-
return
164-
}
179+
path := pathlib.Join(r.home(), ".gitconfig")
165180
f, err := os.Create(path)
166181
if err != nil {
167182
err = liberr.Wrap(
@@ -170,7 +185,6 @@ func (r *Git) writeConfig() (err error) {
170185
path)
171186
return
172187
}
173-
174188
proxy, err := r.proxy()
175189
if err != nil {
176190
return
@@ -179,7 +193,9 @@ func (r *Git) writeConfig() (err error) {
179193
s += "name = Konveyor Dev\n"
180194
s += "email = [email protected]\n"
181195
s += "[credential]\n"
182-
s += "helper = store\n"
196+
s += "helper = store --file="
197+
s += pathlib.Join(r.home(), ".git-credentials")
198+
s += "\n"
183199
s += "[http]\n"
184200
s += fmt.Sprintf("sslVerify = %t\n", !r.Insecure)
185201
if proxy != "" {
@@ -198,15 +214,11 @@ func (r *Git) writeConfig() (err error) {
198214
}
199215

200216
// writeCreds writes credentials (store) file.
201-
func (r *Git) writeCreds(id *api.Identity) (err error) {
202-
if id.User == "" || id.Password == "" {
203-
return
204-
}
205-
path := pathlib.Join(HomeDir, ".git-credentials")
206-
found, err := nas.Exists(path)
207-
if found || err != nil {
217+
func (r *Git) writeCreds() (err error) {
218+
if r.Identity.User == "" || r.Identity.Password == "" {
208219
return
209220
}
221+
path := pathlib.Join(r.home(), ".git-credentials")
210222
f, err := os.Create(path)
211223
if err != nil {
212224
err = liberr.Wrap(
@@ -222,12 +234,12 @@ func (r *Git) writeCreds(id *api.Identity) (err error) {
222234
} {
223235
entry := scheme
224236
entry += "://"
225-
if id.User != "" {
226-
entry += id.User
237+
if r.Identity.User != "" {
238+
entry += r.Identity.User
227239
entry += ":"
228240
}
229-
if id.Password != "" {
230-
entry += id.Password
241+
if r.Identity.Password != "" {
242+
entry += r.Identity.Password
231243
entry += "@"
232244
}
233245
entry += url.Host
@@ -307,12 +319,25 @@ func (r *Git) checkout() (err error) {
307319
_ = os.Chdir(dir)
308320
}()
309321
_ = os.Chdir(r.Path)
310-
cmd := command.New("/usr/bin/git")
322+
cmd := r.git()
311323
cmd.Options.Add("checkout", branch)
312324
err = cmd.Run()
313325
return
314326
}
315327

328+
// home returns the Git home directory path.
329+
func (r *Git) home() (home string) {
330+
h := fnv.New32a()
331+
_, _ = h.Write([]byte(r.Remote.URL))
332+
n := h.Sum32()
333+
digest := strconv.FormatUint(uint64(n), 16)
334+
home = pathlib.Join(
335+
Dir,
336+
".git",
337+
digest)
338+
return
339+
}
340+
316341
// GitURL git clone URL.
317342
type GitURL struct {
318343
Raw string

0 commit comments

Comments
 (0)