( ctx context.Context, token string, ref BranchRef, )
| 295 | } |
| 296 | |
| 297 | func (g *gitlabProvider) FetchBranchDiff( |
| 298 | ctx context.Context, |
| 299 | token string, |
| 300 | ref BranchRef, |
| 301 | ) (string, error) { |
| 302 | if ref.Owner == "" || ref.Repo == "" || ref.Branch == "" { |
| 303 | return "", nil |
| 304 | } |
| 305 | |
| 306 | pid := gitLabPID(ref.Owner, ref.Repo) |
| 307 | opts := reqOpts(ctx, token) |
| 308 | |
| 309 | // Get the default branch from the project. |
| 310 | project, _, err := g.client.Projects.GetProject(pid, nil, opts...) |
| 311 | if err != nil { |
| 312 | return "", g.wrapError(err, "get project") |
| 313 | } |
| 314 | defaultBranch := strings.TrimSpace(project.DefaultBranch) |
| 315 | if defaultBranch == "" { |
| 316 | return "", xerrors.New("gitlab project default branch is empty") |
| 317 | } |
| 318 | |
| 319 | // Use raw HTTP with io.LimitReader to bound memory. The library's |
| 320 | // Compare() decodes the full response before returning, which |
| 321 | // would allow a maliciously large diff to OOM the process. |
| 322 | compareURL := fmt.Sprintf("%sprojects/%s/repository/compare?from=%s&to=%s&unidiff=true", |
| 323 | g.client.BaseURL().String(), |
| 324 | url.PathEscape(pid), |
| 325 | url.QueryEscape(defaultBranch), |
| 326 | url.QueryEscape(ref.Branch), |
| 327 | ) |
| 328 | req, err := http.NewRequestWithContext(ctx, http.MethodGet, compareURL, nil) |
| 329 | if err != nil { |
| 330 | return "", g.wrapError(err, "create compare request") |
| 331 | } |
| 332 | if token != "" { |
| 333 | req.Header.Set("Authorization", "Bearer "+token) |
| 334 | } |
| 335 | |
| 336 | resp, err := g.client.HTTPClient().Do(req) |
| 337 | if err != nil { |
| 338 | return "", g.wrapError(err, "compare branches") |
| 339 | } |
| 340 | defer resp.Body.Close() |
| 341 | |
| 342 | if resp.StatusCode != http.StatusOK { |
| 343 | if rlErr := checkRateLimitError(resp, g.clock, "RateLimit-Reset"); rlErr != nil { |
| 344 | return "", rlErr |
| 345 | } |
| 346 | body, readErr := io.ReadAll(io.LimitReader(resp.Body, 8192)) |
| 347 | if readErr != nil { |
| 348 | return "", g.wrapError( |
| 349 | xerrors.Errorf("unexpected status %d", resp.StatusCode), |
| 350 | "compare branches", |
| 351 | ) |
| 352 | } |
| 353 | return "", g.wrapError( |
| 354 | xerrors.Errorf("unexpected status %d: %s", resp.StatusCode, strings.TrimSpace(string(body))), |
nothing calls this directly
no test coverage detected