mirror of
https://github.com/gabrie30/ghorg.git
synced 2025-08-06 14:27:28 +02:00
Empty repos should not fail on pull (#513)
This commit is contained in:
parent
4500043663
commit
a1395a2a6f
23
cmd/clone.go
23
cmd/clone.go
@ -852,10 +852,24 @@ func CloneAllRepos(git git.Gitter, cloneTargets []scm.Repo) {
|
||||
// Retry checkout
|
||||
errRetry := git.Checkout(repo)
|
||||
if errRetry != nil {
|
||||
e := fmt.Sprintf("Could not checkout out %s, branch may not exist or may not have any contents/commits, no changes made on: %s Error: %v", repo.CloneBranch, repo.URL, errRetry)
|
||||
hasRemoteHeads, errHasRemoteHeads := git.HasRemoteHeads(repo)
|
||||
if errHasRemoteHeads != nil {
|
||||
e := fmt.Sprintf("Could not checkout %s, branch may not exist or may not have any contents/commits, no changes made on: %s Errors: %v %v", repo.CloneBranch, repo.URL, errRetry, errHasRemoteHeads)
|
||||
cloneErrors = append(cloneErrors, e)
|
||||
return
|
||||
}
|
||||
if hasRemoteHeads {
|
||||
// weird, should not happen, return original checkout error
|
||||
e := fmt.Sprintf("Could not checkout %s, branch may not exist or may not have any contents/commits, no changes made on: %s Error: %v", repo.CloneBranch, repo.URL, errRetry)
|
||||
cloneErrors = append(cloneErrors, e)
|
||||
return
|
||||
} else {
|
||||
// this is _just_ an empty repository
|
||||
e := fmt.Sprintf("Could not checkout %s due to repository being empty, no changes made on: %s", repo.CloneBranch, repo.URL)
|
||||
cloneInfos = append(cloneInfos, e)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
count, _ := git.RepoCommitCount(repo)
|
||||
@ -1032,6 +1046,7 @@ func CloneAllRepos(git git.Gitter, cloneTargets []scm.Repo) {
|
||||
writeGhorgStats(date, allReposToCloneCount, cloneCount, pulledCount, cloneInfosCount, cloneErrorsCount, updateRemoteCount, newCommits, pruneCount, hasCollisions)
|
||||
}
|
||||
|
||||
if os.Getenv("GHORG_DONT_EXIT_UNDER_TEST") != "true" {
|
||||
if os.Getenv("GHORG_EXIT_CODE_ON_CLONE_INFOS") != "0" && cloneInfosCount > 0 {
|
||||
exitCode, err := strconv.Atoi(os.Getenv("GHORG_EXIT_CODE_ON_CLONE_INFOS"))
|
||||
if err != nil {
|
||||
@ -1041,16 +1056,20 @@ func CloneAllRepos(git git.Gitter, cloneTargets []scm.Repo) {
|
||||
|
||||
os.Exit(exitCode)
|
||||
}
|
||||
}
|
||||
|
||||
if os.Getenv("GHORG_DONT_EXIT_UNDER_TEST") != "true" {
|
||||
if cloneErrorsCount > 0 {
|
||||
exitCode, err := strconv.Atoi(os.Getenv("GHORG_EXIT_CODE_ON_CLONE_ISSUES"))
|
||||
if err != nil {
|
||||
colorlog.PrintError("Could not convert GHORG_EXIT_CODE_ON_CLONE_ISSUES from string to integer")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
os.Exit(exitCode)
|
||||
}
|
||||
} else {
|
||||
cloneErrorsCount = 0
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
"os"
|
||||
"reflect"
|
||||
@ -13,6 +14,7 @@ import (
|
||||
func TestShouldLowerRegularString(t *testing.T) {
|
||||
|
||||
upperName := "RepoName"
|
||||
defer setOutputDirName([]string{""})
|
||||
setOutputDirName([]string{upperName})
|
||||
|
||||
if outputDirName != "reponame" {
|
||||
@ -23,6 +25,7 @@ func TestShouldLowerRegularString(t *testing.T) {
|
||||
func TestShouldNotChangeLowerCasedRegularString(t *testing.T) {
|
||||
|
||||
lowerName := "repo_name"
|
||||
defer setOutputDirName([]string{""})
|
||||
setOutputDirName([]string{lowerName})
|
||||
|
||||
if outputDirName != "repo_name" {
|
||||
@ -34,6 +37,7 @@ func TestReplaceDashWithUnderscore(t *testing.T) {
|
||||
|
||||
want := "repo-name"
|
||||
lowerName := "repo-name"
|
||||
defer setOutputDirName([]string{""})
|
||||
setOutputDirName([]string{lowerName})
|
||||
|
||||
if outputDirName != want {
|
||||
@ -44,6 +48,7 @@ func TestReplaceDashWithUnderscore(t *testing.T) {
|
||||
func TestShouldNotChangeNonLettersString(t *testing.T) {
|
||||
|
||||
numberName := "1234567_8"
|
||||
defer setOutputDirName([]string{""})
|
||||
setOutputDirName([]string{numberName})
|
||||
|
||||
if outputDirName != "1234567_8" {
|
||||
@ -57,6 +62,13 @@ func NewMockGit() MockGitClient {
|
||||
return MockGitClient{}
|
||||
}
|
||||
|
||||
func (g MockGitClient) HasRemoteHeads(repo scm.Repo) (bool, error) {
|
||||
if repo.Name == "testRepoEmpty" {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (g MockGitClient) Clone(repo scm.Repo) error {
|
||||
_, err := os.MkdirTemp(os.Getenv("GHORG_ABSOLUTE_PATH_TO_CLONE_TO"), repo.Name)
|
||||
if err != nil {
|
||||
@ -74,6 +86,9 @@ func (g MockGitClient) SetOriginWithCredentials(repo scm.Repo) error {
|
||||
}
|
||||
|
||||
func (g MockGitClient) Checkout(repo scm.Repo) error {
|
||||
if repo.Name == "testRepoEmpty" {
|
||||
return errors.New("Cannot checkout any specific branch in an empty repository")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -144,6 +159,49 @@ func TestInitialClone(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCloneEmptyRepo(t *testing.T) {
|
||||
defer UnsetEnv("GHORG_")()
|
||||
dir, err := os.MkdirTemp("", "ghorg_test_empty_repo")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
os.Setenv("GHORG_ABSOLUTE_PATH_TO_CLONE_TO", dir)
|
||||
setOuputDirAbsolutePath()
|
||||
|
||||
os.Setenv("GHORG_DONT_EXIT_UNDER_TEST", "true")
|
||||
|
||||
// simulate a previous clone of empty git repository
|
||||
repoErr := os.Mkdir(outputDirAbsolutePath+"/"+"testRepoEmpty", 0o700)
|
||||
if repoErr != nil {
|
||||
log.Fatal(repoErr)
|
||||
}
|
||||
defer os.RemoveAll(outputDirAbsolutePath + "/" + "testRepoEmpty")
|
||||
|
||||
os.Setenv("GHORG_CONCURRENCY", "1")
|
||||
var testRepos = []scm.Repo{
|
||||
{
|
||||
Name: "testRepoEmpty",
|
||||
URL: "git@github.com:org/testRepoEmpty.git",
|
||||
CloneBranch: "main",
|
||||
},
|
||||
}
|
||||
|
||||
mockGit := NewMockGit()
|
||||
CloneAllRepos(mockGit, testRepos)
|
||||
gotInfos := len(cloneInfos)
|
||||
expectedInfos := 1
|
||||
if gotInfos != expectedInfos {
|
||||
t.Fatalf("Wrong number of cloneInfos, expected: %v, got: %v", expectedInfos, gotInfos)
|
||||
}
|
||||
gotInfo := cloneInfos[0]
|
||||
expected := "Could not checkout main due to repository being empty, no changes made on: git@github.com:org/testRepoEmpty.git"
|
||||
if gotInfo != expected {
|
||||
t.Errorf("Wrong cloneInfo, expected: %v, got: %v", expected, gotInfo)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMatchPrefix(t *testing.T) {
|
||||
defer UnsetEnv("GHORG_")()
|
||||
dir, err := os.MkdirTemp("", "ghorg_test_match_prefix")
|
||||
@ -154,6 +212,8 @@ func TestMatchPrefix(t *testing.T) {
|
||||
os.Setenv("GHORG_ABSOLUTE_PATH_TO_CLONE_TO", dir)
|
||||
os.Setenv("GHORG_CONCURRENCY", "1")
|
||||
os.Setenv("GHORG_MATCH_PREFIX", "test")
|
||||
os.Setenv("GHORG_DONT_EXIT_UNDER_TEST", "true")
|
||||
|
||||
var testRepos = []scm.Repo{
|
||||
{
|
||||
Name: "testRepoOne",
|
||||
@ -191,6 +251,8 @@ func TestExcludeMatchPrefix(t *testing.T) {
|
||||
os.Setenv("GHORG_ABSOLUTE_PATH_TO_CLONE_TO", dir)
|
||||
os.Setenv("GHORG_CONCURRENCY", "1")
|
||||
os.Setenv("GHORG_EXCLUDE_MATCH_PREFIX", "test")
|
||||
os.Setenv("GHORG_DONT_EXIT_UNDER_TEST", "true")
|
||||
|
||||
var testRepos = []scm.Repo{
|
||||
{
|
||||
Name: "testRepoOne",
|
||||
@ -228,6 +290,8 @@ func TestMatchRegex(t *testing.T) {
|
||||
os.Setenv("GHORG_ABSOLUTE_PATH_TO_CLONE_TO", dir)
|
||||
os.Setenv("GHORG_CONCURRENCY", "1")
|
||||
os.Setenv("GHORG_MATCH_REGEX", "^test-")
|
||||
os.Setenv("GHORG_DONT_EXIT_UNDER_TEST", "true")
|
||||
|
||||
var testRepos = []scm.Repo{
|
||||
{
|
||||
Name: "test-RepoOne",
|
||||
@ -267,6 +331,8 @@ func TestExcludeMatchRegex(t *testing.T) {
|
||||
os.Setenv("GHORG_CONCURRENCY", "1")
|
||||
os.Setenv("GHORG_OUTPUT_DIR", testDescriptor)
|
||||
os.Setenv("GHORG_EXCLUDE_MATCH_REGEX", "^test-")
|
||||
os.Setenv("GHORG_DONT_EXIT_UNDER_TEST", "true")
|
||||
|
||||
var testRepos = []scm.Repo{
|
||||
{
|
||||
Name: "test-RepoOne",
|
||||
|
31
git/git.go
31
git/git.go
@ -1,6 +1,7 @@
|
||||
package git
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
@ -26,6 +27,7 @@ type Gitter interface {
|
||||
FetchAll(scm.Repo) error
|
||||
FetchCloneBranch(scm.Repo) error
|
||||
RepoCommitCount(scm.Repo) (int, error)
|
||||
HasRemoteHeads(scm.Repo) (bool, error)
|
||||
}
|
||||
|
||||
type GitClient struct{}
|
||||
@ -51,6 +53,35 @@ func printDebugCmd(cmd *exec.Cmd, repo scm.Repo) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (g GitClient) HasRemoteHeads(repo scm.Repo) (bool, error) {
|
||||
cmd := exec.Command("git", "ls-remote", "--heads", "--quiet", "--exit-code")
|
||||
|
||||
err := cmd.Run()
|
||||
if err == nil {
|
||||
// successfully listed the remote heads
|
||||
return true, nil
|
||||
}
|
||||
|
||||
var exitError *exec.ExitError
|
||||
if !errors.As(err, &exitError) {
|
||||
// error, but no exit code, return err
|
||||
return false, err
|
||||
}
|
||||
|
||||
exitCode := exitError.ExitCode()
|
||||
if exitCode == 0 {
|
||||
// ls-remote did successfully list the remote heads
|
||||
return true, nil
|
||||
} else if exitCode == 2 {
|
||||
// repository is empty
|
||||
return false, nil
|
||||
} else {
|
||||
// another exit code, simply return err
|
||||
return false, err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (g GitClient) Clone(repo scm.Repo) error {
|
||||
args := []string{"clone", repo.CloneURL, repo.HostPath}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user