getOrCloneRepo returns the path to a cached git repository. If the repository is not cached, it clones it first. This function is thread-safe: multiple goroutines cloning the same repo+ref will synchronize, and only one clone operation will occur. The cache directory is /tmp/task-git-repos/{cache_k
(ctx context.Context)
| 122 | // |
| 123 | // The cache directory is /tmp/task-git-repos/{cache_key}/ |
| 124 | func (node *GitNode) getOrCloneRepo(ctx context.Context) (string, error) { |
| 125 | cacheKey := node.repoCacheKey() |
| 126 | |
| 127 | repoMutex := globalGitRepoCache.getLockForRepo(cacheKey) |
| 128 | repoMutex.Lock() |
| 129 | defer repoMutex.Unlock() |
| 130 | |
| 131 | cacheDir := filepath.Join(os.TempDir(), "task-git-repos", cacheKey) |
| 132 | |
| 133 | // Check cache FIRST - if already cloned, no network needed, timeout irrelevant |
| 134 | gitDir := filepath.Join(cacheDir, ".git") |
| 135 | if _, err := os.Stat(gitDir); err == nil { |
| 136 | return cacheDir, nil |
| 137 | } |
| 138 | |
| 139 | // Only check context if we need to clone (requires network) |
| 140 | if err := ctx.Err(); err != nil { |
| 141 | return "", fmt.Errorf("context cancelled while waiting for repository lock: %w", err) |
| 142 | } |
| 143 | |
| 144 | getterURL := node.buildURL() |
| 145 | |
| 146 | client := &getter.Client{ |
| 147 | Ctx: ctx, |
| 148 | Src: getterURL, |
| 149 | Dst: cacheDir, |
| 150 | Mode: getter.ClientModeDir, |
| 151 | } |
| 152 | |
| 153 | if err := client.Get(); err != nil { |
| 154 | _ = os.RemoveAll(cacheDir) |
| 155 | return "", fmt.Errorf("failed to clone repository: %w", err) |
| 156 | } |
| 157 | |
| 158 | return cacheDir, nil |
| 159 | } |
| 160 | |
| 161 | func (node *GitNode) ReadContext(ctx context.Context) ([]byte, error) { |
| 162 | // Get or clone the repository into cache |
no test coverage detected