/
repo_handler.go
121 lines (111 loc) · 3.82 KB
/
repo_handler.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
package reposync
import (
"errors"
"fmt"
"github.com/cli/go-gh"
"github.com/rm3l/gh-org-repo-sync/internal/cli"
"github.com/rm3l/gh-org-repo-sync/internal/github"
"log"
"os"
"path/filepath"
)
// CloneProtocol indicates the Git protocol to use for cloning.
// See the constants exported in this package for further details.
type CloneProtocol string
const (
// SystemProtocol indicates whether to use the Git protocol configured in the GitHub CLI,
// e.g., via the 'gh config set git_protocol' configuration command
SystemProtocol CloneProtocol = "system"
// SSHProtocol forces this extension to clone repositories via SSH.
// As such, the Git remote will look like: git@github.com:org/repo.git
SSHProtocol CloneProtocol = "ssh"
// HTTPSProtocol forces this extension to clone repositories via HTTPS.
// As such, the Git remote will look like: https://github.com/org/repo.git
HTTPSProtocol CloneProtocol = "https"
)
// HandleRepository determines whether a directory with the repository name does exist.
// If it does, it checks out its default branch and updates it locally.
// Otherwise, it clones it.
func HandleRepository(dryRun bool, output, organization string, repositoryInfo github.RepositoryInfo, protocol CloneProtocol) error {
repository := repositoryInfo.Name
repoPath, err := safeAbsPath(fmt.Sprintf("%s/%s", output, repository))
if err != nil {
return err
}
info, err := os.Stat(repoPath)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
if dryRun {
fmt.Printf("=> %s/%s: new clone in '%s'\n", organization, repository, repoPath)
return nil
}
log.Println("[debug] cloning repo because local folder not found:", repoPath)
if err := clone(repoPath, organization, repository, protocol); err != nil {
return err
}
return nil
}
return err
}
if !info.IsDir() {
return fmt.Errorf("expected folder for repository '%s'", repoPath)
}
if dryRun {
fmt.Printf("=> %s/%s: update in '%s'\n", organization, repository, repoPath)
return nil
}
log.Println("[debug] updating local clone for repo:", repoPath)
return updateLocalClone(repoPath, organization, repositoryInfo)
}
func clone(output, organization string, repository string, protocol CloneProtocol) error {
var repoUrl string
if protocol == SystemProtocol {
repoUrl = fmt.Sprintf("%s/%s", organization, repository)
} else if protocol == SSHProtocol {
repoUrl = fmt.Sprintf("git@github.com:%s/%s.git", organization, repository)
} else if protocol == HTTPSProtocol {
repoUrl = fmt.Sprintf("https://github.com/%s/%s.git", organization, repository)
} else {
return fmt.Errorf("unknown protocol for cloning: %s", protocol)
}
repoPath, err := safeAbsPath(output)
if err != nil {
return err
}
args := []string{"repo", "clone", repoUrl, repoPath}
_, stdErr, err := gh.Exec(args...)
if stdErrString := stdErr.String(); stdErrString != "" {
fmt.Println(stdErrString)
}
return err
}
func updateLocalClone(outputPath, organization string, repositoryInfo github.RepositoryInfo) error {
repository := repositoryInfo.Name
repoPath, err := safeAbsPath(outputPath)
if err != nil {
return err
}
err = fetchAllRemotes(repoPath)
if err != nil {
log.Println("[warn]", err)
}
if repositoryInfo.IsEmpty {
log.Printf("[warn] skipped syncing empty repo: %s. Only remotes have been fetched\n", repoPath)
return nil
}
args := []string{"repo", "sync", "--source", fmt.Sprintf("%s/%s", organization, repository)}
_, _, err = github.RunGhCliInDir(repoPath, nil, args...)
return err
}
func fetchAllRemotes(outputPath string) error {
repoPath, err := safeAbsPath(outputPath)
if err != nil {
return err
}
args := []string{"fetch", "--all", "--prune", "--tags", "--recurse-submodules"}
_, _, err = cli.RunCommandInDir("git", repoPath, nil, args...)
return err
}
func safeAbsPath(p string) (string, error) {
return filepath.Abs(filepath.FromSlash(p))
}