Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 52 additions & 1 deletion db_lib/GoGitClient.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,49 @@ func (t ProgressWrapper) Write(p []byte) (n int, err error) {
return len(p), nil
}

// resolveReferenceName checks if the provided branch name is actually a branch or tag
// and returns the appropriate plumbing.ReferenceName
func (c GoGitClient) resolveReferenceName(r GitRepository) (plumbing.ReferenceName, error) {
remote := git.NewRemote(memory.NewStorage(), &config.RemoteConfig{
Name: "origin",
URLs: []string{r.Repository.GitURL},
})

auth, err := c.getAuthMethod(r)
if err != nil {
return "", fmt.Errorf("failed to create auth method: %w", err)
}

listOptions := &git.ListOptions{}
if auth != nil {
listOptions.Auth = auth
}

refs, err := remote.List(listOptions)
if err != nil {
return "", fmt.Errorf("failed to list remote references: %w", err)
}

// Check if it's a branch
branchRef := plumbing.NewBranchReferenceName(r.Repository.GitBranch)
for _, ref := range refs {
if ref.Name() == branchRef {
return branchRef, nil
}
}

// Check if it's a tag
tagRef := plumbing.NewTagReferenceName(r.Repository.GitBranch)
for _, ref := range refs {
if ref.Name() == tagRef {
return tagRef, nil
}
}

// If neither found, return error
return "", fmt.Errorf("reference '%s' not found as branch or tag", r.Repository.GitBranch)
}

func (c GoGitClient) getAuthMethod(r GitRepository) (transport.AuthMethod, error) {
switch r.Repository.SSHKey.Type {
case db.AccessKeySSH:
Expand Down Expand Up @@ -102,11 +145,19 @@ func (c GoGitClient) Clone(r GitRepository) error {
return authErr
}

// Resolve reference name to support both branches and tags
refName, refErr := c.resolveReferenceName(r)
if refErr != nil {
// If we can't resolve the reference, log a warning but continue with branch assumption
r.Logger.Log("Warning: Unable to resolve reference type, assuming branch: " + refErr.Error())
refName = plumbing.NewBranchReferenceName(r.Repository.GitBranch)
}

cloneOpt := &git.CloneOptions{
URL: r.Repository.GetGitURL(true),
Progress: ProgressWrapper{r.Logger},
RecurseSubmodules: git.DefaultSubmoduleRecursionDepth,
ReferenceName: plumbing.NewBranchReferenceName(r.Repository.GitBranch),
ReferenceName: refName,
Auth: authMethod,
}

Expand Down
81 changes: 81 additions & 0 deletions db_lib/GoGitClient_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package db_lib

import (
"os/exec"
"testing"
"time"

"github.com/go-git/go-git/v5/plumbing"
"github.com/semaphoreui/semaphore/db"
"github.com/semaphoreui/semaphore/pkg/task_logger"
)

// mockLogger is a simple mock implementation of task_logger.Logger for testing
type mockLogger struct{}

func (m *mockLogger) Log(msg string) {}
func (m *mockLogger) Logf(format string, a ...any) {}
func (m *mockLogger) LogWithTime(now time.Time, msg string) {}
func (m *mockLogger) LogfWithTime(now time.Time, format string, a ...any) {}
func (m *mockLogger) LogCmd(cmd *exec.Cmd) {}
func (m *mockLogger) SetStatus(status task_logger.TaskStatus) {}
func (m *mockLogger) AddStatusListener(l task_logger.StatusListener) {}
func (m *mockLogger) AddLogListener(l task_logger.LogListener) {}
func (m *mockLogger) SetCommit(hash, message string) {}
func (m *mockLogger) WaitLog() {}

// TestResolveReferenceNameBranch tests that resolveReferenceName correctly identifies a branch
func TestResolveReferenceNameBranch(t *testing.T) {
// This is a basic structural test to ensure the function exists and has the right signature
// Actual network tests would require a real git repository which is beyond unit testing

client := GoGitClient{
keyInstaller: nil,
}

// Create a test repository struct
repo := GitRepository{
Repository: db.Repository{
GitURL: "https://github.com/semaphoreui/semaphore.git",
GitBranch: "main",
SSHKey: db.AccessKey{
Type: db.AccessKeyNone,
},
},
Logger: &mockLogger{},
}

// Test that the method can be called (will likely fail to connect, but that's ok)
_, err := client.resolveReferenceName(repo)

// We expect an error since we can't actually connect to the repo in unit tests
// The important thing is that the method exists and compiles correctly
if err == nil {
t.Log("Method successfully resolved reference (unexpected in unit test environment)")
} else {
t.Logf("Expected error in unit test environment: %v", err)
}
}

// TestResolveReferenceNameHelperLogic tests the logic of checking branch vs tag
func TestResolveReferenceNameHelperLogic(t *testing.T) {
// Test that plumbing reference names are created correctly
branchName := "main"
tagName := "v1.0.0"

branchRef := plumbing.NewBranchReferenceName(branchName)
tagRef := plumbing.NewTagReferenceName(tagName)

expectedBranchRef := plumbing.ReferenceName("refs/heads/main")
expectedTagRef := plumbing.ReferenceName("refs/tags/v1.0.0")

if branchRef != expectedBranchRef {
t.Errorf("Expected branch reference %s, got %s", expectedBranchRef, branchRef)
}

if tagRef != expectedTagRef {
t.Errorf("Expected tag reference %s, got %s", expectedTagRef, tagRef)
}

t.Log("Reference name creation logic is correct")
}
37 changes: 37 additions & 0 deletions pro/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,51 @@ go 1.24.2
require github.com/semaphoreui/semaphore v0.0.0-20250712180151-72836311c5b9

require (
dario.cat/mergo v1.0.1 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
github.com/Masterminds/squirrel v1.5.4 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/ProtonMail/go-crypto v1.1.6 // indirect
github.com/cloudflare/circl v1.6.1 // indirect
github.com/creack/pty v1.1.24 // indirect
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.6.2 // indirect
github.com/go-git/go-git/v5 v5.16.3 // indirect
github.com/go-gorp/gorp/v3 v3.1.0 // indirect
github.com/go-sql-driver/mysql v1.9.3 // indirect
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
github.com/google/go-github v17.0.0+incompatible // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/mux v1.8.1 // indirect
github.com/gorilla/securecookie v1.1.2 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
github.com/lib/pq v1.10.9 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/pjbgf/sha1cd v0.3.2 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/skeema/knownhosts v1.3.1 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
golang.org/x/crypto v0.38.0 // indirect
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect
golang.org/x/net v0.39.0 // indirect
golang.org/x/sys v0.33.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
modernc.org/libc v1.65.10 // indirect
modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.11.0 // indirect
modernc.org/sqlite v1.38.0 // indirect
)

replace github.com/semaphoreui/semaphore => ../
Loading
Loading