diff --git a/taskfile/node_git.go b/taskfile/node_git.go index eba1a5c646..62d429fe3d 100644 --- a/taskfile/node_git.go +++ b/taskfile/node_git.go @@ -15,6 +15,7 @@ import ( "github.com/go-task/task/v3/errors" "github.com/go-task/task/v3/internal/execext" "github.com/go-task/task/v3/internal/filepathext" + "github.com/go-task/task/v3/internal/fsext" ) // An GitNode is a node that reads a Taskfile from a remote location via Git. @@ -104,13 +105,13 @@ func (node *GitNode) buildURL() string { // Get the base URL baseURL := node.url.String() - ref := node.ref - if ref == "" { - ref = "HEAD" - } // Always use git:: prefix for git URLs (following Terraform's pattern) // This forces go-getter to use git protocol - return fmt.Sprintf("git::%s?ref=%s&depth=1", baseURL, ref) + if node.ref != "" { + return fmt.Sprintf("git::%s?ref=%s&depth=1", baseURL, node.ref) + } + // When no ref is specified, omit it entirely to let git clone the default branch + return fmt.Sprintf("git::%s?depth=1", baseURL) } // getOrCloneRepo returns the path to a cached git repository. @@ -164,11 +165,16 @@ func (node *GitNode) ReadContext(ctx context.Context) ([]byte, error) { } // Build path to Taskfile in the cached repo - taskfilePath := node.path - if taskfilePath == "" { - taskfilePath = "Taskfile.yml" + // If node.path is empty, search in repo root; otherwise search in the specified path + // fsext.SearchPath handles both files and directories (searching for DefaultTaskfiles) + searchPath := repoDir + if node.path != "" { + searchPath = filepath.Join(repoDir, node.path) + } + filePath, err := fsext.SearchPath(searchPath, DefaultTaskfiles) + if err != nil { + return nil, err } - filePath := filepath.Join(repoDir, taskfilePath) // Read file from cached repo b, err := os.ReadFile(filePath) @@ -230,7 +236,7 @@ func (node *GitNode) repoCacheKey() string { ref := node.ref if ref == "" { - ref = "HEAD" + ref = "_default_" // Placeholder for the remote's default branch } return filepath.Join(node.url.Host, repoPath, ref) diff --git a/taskfile/node_git_test.go b/taskfile/node_git_test.go index b6ae6bf271..71ea046511 100644 --- a/taskfile/node_git_test.go +++ b/taskfile/node_git_test.go @@ -127,9 +127,9 @@ func TestGitNode_buildURL(t *testing.T) { expectedURL: "git::https://github.com/foo/bar.git?ref=v1.0.0&depth=1", }, { - name: "HTTPS without ref (uses remote HEAD)", + name: "HTTPS without ref (uses remote default branch)", entrypoint: "https://github.com/foo/bar.git//Taskfile.yml", - expectedURL: "git::https://github.com/foo/bar.git?ref=HEAD&depth=1", + expectedURL: "git::https://github.com/foo/bar.git?depth=1", }, { name: "SSH with directory path", @@ -198,20 +198,20 @@ func TestRepoCacheKey_DifferentRepos(t *testing.T) { assert.NotEqual(t, key1, key2, "Different repos should generate different cache keys") } -func TestRepoCacheKey_NoRefVsHead(t *testing.T) { +func TestRepoCacheKey_NoRefVsExplicitRef(t *testing.T) { t.Parallel() - // No ref (defaults to HEAD) vs explicit HEAD should have SAME cache key + // No ref (uses default branch) vs explicit ref should have DIFFERENT cache keys node1, err := NewGitNode("https://github.com/foo/bar.git//file.yml", "", false) require.NoError(t, err) - node2, err := NewGitNode("https://github.com/foo/bar.git//file.yml?ref=HEAD", "", false) + node2, err := NewGitNode("https://github.com/foo/bar.git//file.yml?ref=main", "", false) require.NoError(t, err) key1 := node1.repoCacheKey() key2 := node2.repoCacheKey() - assert.Equal(t, key1, key2, "No ref and explicit HEAD should generate same cache key") + assert.NotEqual(t, key1, key2, "No ref and explicit ref should generate different cache keys") } func TestRepoCacheKey_SSHvsHTTPS(t *testing.T) {