generateCheckoutStepLines generates YAML step lines for a single non-default checkout. The index parameter identifies the checkout's position in the ordered list, used to reference the correct app token minting step when app authentication is configured. When keepCredentialsForPush is true (safe_out
(entry *resolvedCheckout, index int, keepCredentialsForPush bool, pushToken string, getActionPin func(string) string)
| 491 | // (persist-credentials: true) and the post-checkout cleanup step is suppressed so a later |
| 492 | // git fetch/push can authenticate. |
| 493 | func generateCheckoutStepLines(entry *resolvedCheckout, index int, keepCredentialsForPush bool, pushToken string, getActionPin func(string) string) []string { |
| 494 | checkoutManagerLog.Printf("Generating checkout step lines: index=%d, repo=%q, path=%q, ref=%q, appAuth=%v", |
| 495 | index, entry.key.repository, entry.key.path, entry.ref, entry.githubApp != nil) |
| 496 | name := "Checkout " + checkoutStepName(entry.key) |
| 497 | var sb strings.Builder |
| 498 | fmt.Fprintf(&sb, " - name: %s\n", name) |
| 499 | fmt.Fprintf(&sb, " uses: %s\n", getActionPin("actions/checkout")) |
| 500 | sb.WriteString(" with:\n") |
| 501 | |
| 502 | if keepCredentialsForPush { |
| 503 | // safe_outputs job: retain credentials so later git fetch/push can authenticate. |
| 504 | sb.WriteString(" persist-credentials: true\n") |
| 505 | } else if entry.cleanCreds { |
| 506 | sb.WriteString(" persist-credentials: true\n") |
| 507 | } else { |
| 508 | // Security: default behavior disables credential persistence |
| 509 | sb.WriteString(" persist-credentials: false\n") |
| 510 | } |
| 511 | |
| 512 | if entry.key.wiki { |
| 513 | // Wiki checkout: use "{repository}.wiki" as the effective repository. |
| 514 | fmt.Fprintf(&sb, " repository: %s\n", wikiRepository(entry.key.repository)) |
| 515 | } else if entry.key.repository != "" { |
| 516 | fmt.Fprintf(&sb, " repository: %s\n", entry.key.repository) |
| 517 | } |
| 518 | if entry.ref != "" { |
| 519 | fmt.Fprintf(&sb, " ref: %s\n", entry.ref) |
| 520 | } |
| 521 | if entry.key.path != "" { |
| 522 | fmt.Fprintf(&sb, " path: %s\n", entry.key.path) |
| 523 | } |
| 524 | // Determine effective token: github-app-minted token takes precedence |
| 525 | effectiveToken := resolveCheckoutTokenExpression(entry, index, false) |
| 526 | // safe_outputs job: when this checkout declares no token/app of its own, persist the |
| 527 | // resolved push token so the retained .git/config credential matches the token the |
| 528 | // safe-output handlers use to fetch/push. |
| 529 | if effectiveToken == "" && keepCredentialsForPush && pushToken != "" { |
| 530 | effectiveToken = pushToken |
| 531 | } |
| 532 | if effectiveToken != "" { |
| 533 | fmt.Fprintf(&sb, " token: %s\n", effectiveToken) |
| 534 | } |
| 535 | if entry.fetchDepth != nil { |
| 536 | fmt.Fprintf(&sb, " fetch-depth: %d\n", *entry.fetchDepth) |
| 537 | } |
| 538 | if len(entry.sparsePatterns) > 0 { |
| 539 | sb.WriteString(" sparse-checkout: |\n") |
| 540 | for _, pattern := range entry.sparsePatterns { |
| 541 | fmt.Fprintf(&sb, " %s\n", strings.TrimSpace(pattern)) |
| 542 | } |
| 543 | // Prevent actions/checkout from adding --filter=blob:none when sparse-checkout |
| 544 | // is specified. Blobless clones require credentials for lazy blob fetches, but |
| 545 | // agent jobs intentionally do not retain git credentials after checkout, making |
| 546 | // offline git operations fail. Using blob:limit=1073741824 (1 GiB) effectively |
| 547 | // fetches all blobs up front on GitHub-hosted repos (GitHub rejects blobs > 100 MiB), |
| 548 | // while keeping the filter non-empty so actions/checkout won't substitute blob:none. The subsequent repair step then |
| 549 | // clears partial-clone markers entirely. |
| 550 | sb.WriteString(" filter: 'blob:limit=1073741824'\n") |
no test coverage detected