Skip to content

Commit 06fbc29

Browse files
committed
Refactoring and some cleanup
Return values need to be changed ASAP (Sorry)
1 parent eb93b4b commit 06fbc29

File tree

1 file changed

+91
-129
lines changed

1 file changed

+91
-129
lines changed

controllers/git/git.go

Lines changed: 91 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -1,201 +1,163 @@
1-
/*
2-
Package git holds every implemention needed
3-
to do various git operations.
4-
This is a interface-first implemention.
5-
*/
61
package git
72

83
import (
94
"net/url"
105
"os"
11-
"strings"
126

137
"github.com/go-git/go-git/v5"
148
"github.com/go-git/go-git/v5/config"
159
"github.com/go-git/go-git/v5/plumbing"
16-
"github.com/go-git/go-git/v5/plumbing/transport"
17-
"github.com/go-git/go-git/v5/plumbing/transport/ssh"
1810
"github.com/go-logr/logr"
19-
gossh "golang.org/x/crypto/ssh"
2011
)
2112

2213
// GitOperator defines the interface for git operations.
2314
type GitOperator interface {
24-
Clone(repoURL string, secretRef []byte, directory string, logger logr.Logger) (err error)
25-
Poll(repo string, secretRef []byte, branch string, directory string, logger logr.Logger) (changes bool, err error)
26-
Hash(repo string, secretRef []byte, branch string, logger logr.Logger) (hash string, err error)
15+
Clone(repoURL string, secretRef []byte, directory string, logger logr.Logger) error
16+
Poll(repoURL string, secretRef []byte, branch string, directory string, logger logr.Logger) (bool, error)
17+
Hash(repo string, secretRef []byte, branch string, logger logr.Logger) (string, error)
2718
}
2819

2920
// GitImplementer implements the GitOperator interface.
3021
type GitImplementer struct{}
3122

32-
// Clone clones the given repository to a local directory.
33-
func (g *GitImplementer) Clone(repoURL string, secretRef []byte, directory string, logger logr.Logger) (err error) {
34-
var auth transport.AuthMethod
23+
// getSSHAuth is a helper function to create an ssh.PublicKeys AuthMethod.
24+
func getSSHAuth(secretRef []byte) (transport.AuthMethod, error) {
25+
if len(secretRef) == 0 {
26+
return nil, fmt.Errorf("SSH secret reference is empty")
27+
}
3528

36-
logger.Info("Creating directory")
37-
err = os.Mkdir(directory, 0755)
29+
auth, err := ssh.NewPublicKeys("git", secretRef, "")
3830
if err != nil {
39-
logger.Error(err, "Failed to create directory", "directory", directory)
40-
41-
return err
31+
return nil, fmt.Errorf("failed to create public keys from secret: %w", err)
4232
}
4333

44-
// Check if repo and directory are empty.
45-
logger.Info("Checking if repo and or directory is empty")
46-
if empty(repoURL, directory) {
47-
logger.Error(err, "repo and or directory is empty", "repoURL", repoURL, "directory", directory)
34+
// Insecurely ignore host key
35+
// ToDO: Use a proper host key verification callback.
36+
auth.HostKeyCallback = gossh.InsecureIgnoreHostKey()
4837

49-
return err
38+
return auth, nil
39+
}
40+
41+
// Clone clones the given repository to a local directory.
42+
func (g *GitImplementer) Clone(repoURL string, secretRef []byte, directory string, logger logr.Logger) error {
43+
if repoURL == "" || directory == "" {
44+
return fmt.Errorf("repoURL and directory must not be empty")
5045
}
5146

52-
logger.Info("Retrieving auth Token")
53-
auth, err = ssh.NewPublicKeys("git", secretRef, "")
54-
if err != nil {
55-
logger.Error(err, "Failed on retrieve the token from the tokenContent")
47+
logger.Info("Creating directory", "directory", directory)
48+
if err := os.MkdirAll(directory, 0755); err != nil {
49+
return fmt.Errorf("failed to create directory %s: %w", directory, err)
50+
}
5651

52+
logger.Info("Retrieving auth method")
53+
auth, err := getSSHAuth(secretRef)
54+
if err != nil {
5755
return err
5856
}
5957

60-
logger.Info("Plain Cloning Repo")
58+
logger.Info("Cloning repository", "repoURL", repoURL)
6159
_, err = git.PlainClone(directory, false, &git.CloneOptions{
62-
URL: repoURL,
63-
Auth: auth,
64-
Depth: 1,
60+
URL: repoURL,
61+
Auth: auth,
62+
Progress: os.Stdout, // Optional: show progress
63+
Depth: 1,
6564
})
6665
if err != nil {
67-
logger.Error(err, "Failed to clone repo", "repo", repoURL)
68-
69-
return err
66+
return fmt.Errorf("failed to clone repo %s: %w", repoURL, err)
7067
}
7168

72-
return err
69+
return nil
7370
}
7471

75-
// Poll polls for changes for the given remote git repository. Returns true, if current local commit hash and remote hash are not equal.
76-
func (g *GitImplementer) Poll(repo string, secretRef []byte, branch string, directory string, logger logr.Logger) (changes bool, err error) {
77-
// Defaults to false. We only change to true if there is a difference between the hashes.
78-
changes = false
79-
80-
// Check if repo and directory are empty.
81-
if empty(repo, directory) {
82-
logger.Error(err, "repo and or directory is empty", "repo", repo, "directory", directory)
83-
84-
return changes, err
72+
// Poll polls for changes for the given remote git repository.
73+
// Returns true if current local commit hash and remote hash are different.
74+
func (g *GitImplementer) Poll(repoURL string, secretRef []byte, branch string, directory string, logger logr.Logger) (bool, error) {
75+
if repoURL == "" || directory == "" {
76+
return false, fmt.Errorf("repoURL and directory must not be empty")
8577
}
8678

8779
// Get hash from local repo.
88-
localHash, err := g.Hash(directory, secretRef, branch, logger)
80+
localHash, err := g.Hash(directory, nil, "", logger) // No secret or branch needed for local HEAD
8981
if err != nil {
90-
logger.Error(err, "Failed to get hash", "repo", repo, "directory", directory)
91-
92-
return changes, err
82+
return false, fmt.Errorf("failed to get local hash from %s: %w", directory, err)
9383
}
9484

9585
// Get Hash from remote repo
96-
remoteHash, err := g.Hash(repo, secretRef, branch, logger)
86+
remoteHash, err := g.Hash(repoURL, secretRef, branch, logger)
9787
if err != nil {
98-
logger.Error(err, "Failed to get hash", "repo", repo, "directory", directory)
99-
100-
return changes, err
88+
return false, fmt.Errorf("failed to get remote hash for %s: %w", repoURL, err)
10189
}
10290

103-
if localHash != remoteHash {
104-
changes = true
105-
}
91+
changes := localHash != remoteHash
92+
logger.Info("Polling result", "localHash", localHash, "remoteHash", remoteHash, "changes", changes)
10693

107-
return changes, err
94+
return changes, nil
10895
}
10996

11097
// Hash retrieves the hash of the given repository.
111-
func (g *GitImplementer) Hash(repo string, secretRef []byte, branch string, logger logr.Logger) (hash string, err error) {
112-
var auth transport.AuthMethod
98+
func (g *GitImplementer) Hash(repo string, secretRef []byte, branch string, logger logr.Logger) (string, error) {
99+
if isURL(repo) {
100+
return g.remoteHash(repo, secretRef, branch, logger)
101+
}
102+
return g.localHash(repo, logger)
103+
}
113104

114-
auth, err = ssh.NewPublicKeys("git", secretRef, "")
105+
// localHash retrieves the HEAD commit hash from a local repository.
106+
func (g *GitImplementer) localHash(path string, logger logr.Logger) (string, error) {
107+
localRepo, err := git.PlainOpen(path)
115108
if err != nil {
116-
logger.Error(err, "Failed to retrieve the token from the tokenContent")
117-
118-
return hash, err
109+
return "", fmt.Errorf("failed to open local repo at %s: %w", path, err)
119110
}
120111

121-
pkAuth, ok := auth.(*ssh.PublicKeys)
122-
if !ok {
123-
logger.Error(err, "pkAuth error")
112+
headRef, err := localRepo.Head()
113+
if err != nil {
114+
return "", fmt.Errorf("failed to get HEAD for local repo at %s: %w", path, err)
124115
}
125116

126-
pkAuth.HostKeyCallback = gossh.InsecureIgnoreHostKey()
127-
128-
switch {
129-
case isURL(repo):
130-
remoterepo := git.NewRemote(nil, &config.RemoteConfig{
131-
URLs: []string{repo},
132-
Name: "origin",
133-
})
134-
135-
refs, err := remoterepo.List(&git.ListOptions{
136-
Auth: auth,
137-
})
138-
if err != nil {
139-
logger.Error(err, "Failed to list remote repo", "repo", repo)
140-
141-
return hash, err
142-
}
143-
144-
refName := plumbing.NewBranchReferenceName(branch)
145-
for _, ref := range refs {
146-
if ref.Name() == refName {
147-
return ref.Hash().String(), err
148-
}
149-
}
150-
151-
return hash, err
152-
case !isURL(repo):
153-
localRepo, err := git.PlainOpen(repo)
154-
if err != nil {
155-
logger.Error(err, "Failed to open local repo", "repo", repo)
117+
return headRef.Hash().String(), nil
118+
}
156119

157-
return hash, err
158-
}
120+
// remoteHash retrieves the commit hash for a specific branch from a remote repository.
121+
func (g *GitImplementer) remoteHash(repoURL string, secretRef []byte, branch string, logger logr.Logger) (string, error) {
122+
if branch == "" {
123+
return "", fmt.Errorf("branch must be specified for remote repository")
124+
}
159125

160-
headRef, err := localRepo.Head()
161-
if err != nil {
162-
logger.Error(err, "failed to get head for local git repo", "repo", repo)
126+
auth, err := getSSHAuth(secretRef)
127+
if err != nil {
128+
return "", err
129+
}
163130

164-
return hash, err
165-
}
131+
remoterepo := git.NewRemote(nil, &config.RemoteConfig{
132+
URLs: []string{repoURL},
133+
})
166134

167-
hash = headRef.Hash().String()
168-
if hash == "" {
169-
logger.Error(err, "failed to get hash for local git repo", "repo", repo)
135+
refs, err := remoterepo.List(&git.ListOptions{
136+
Auth: auth,
137+
})
138+
if err != nil {
139+
return "", fmt.Errorf("failed to list remote repo %s: %w", repoURL, err)
140+
}
170141

171-
return hash, err
142+
refName := plumbing.NewBranchReferenceName(branch)
143+
for _, ref := range refs {
144+
if ref.Name() == refName {
145+
return ref.Hash().String(), nil
172146
}
173-
174-
return hash, err
175147
}
176148

177-
return hash, err
149+
return "", fmt.Errorf("branch '%s' not found in remote repository %s", branch, repoURL)
178150
}
179151

180-
// isURL checks if the given string is a valid URL.
181-
func isURL(repo string) bool {
182-
if repo == "" {
183-
return false
184-
}
185-
parsedURL, err := url.ParseRequestURI(repo)
186-
if err == nil && parsedURL.Scheme != "" {
152+
// isUrl checks if the given string is a valid URL.
153+
func isURL(str string) bool {
154+
// A simple check for SCP-like syntax or a scheme.
155+
// https://en.wikipedia.org/wiki/Secure_copy_protocol :-)
156+
if strings.Contains(str, "@") && strings.Contains(str, ":") {
187157
return true
188158
}
189-
190-
if strings.Contains(repo, "@") && strings.Contains(repo, ":") {
191-
return true
192-
}
193-
// if parsedURL.Scheme != "" {
194-
// return true
195-
// } else {
196-
// return false
197-
// }
198-
return false
159+
u, err := url.ParseRequestURI(str)
160+
return err == nil && u.Scheme != "" && u.Host != ""
199161
}
200162

201163
// empty checks if the repo and directory strings are empty.

0 commit comments

Comments
 (0)