validateBlockScalarExpressionSizes scans the YAML lines for multi-line block scalars (literal | or folded >) and returns an error when any such block both contains a GitHub Actions template expression (${{ }}) and exceeds maxSize bytes in total.
(lines []string, maxSize int)
| 108 | // (literal | or folded >) and returns an error when any such block both contains a |
| 109 | // GitHub Actions template expression (${{ }}) and exceeds maxSize bytes in total. |
| 110 | func validateBlockScalarExpressionSizes(lines []string, maxSize int) error { |
| 111 | // Track whether we are inside a block scalar and its metadata. |
| 112 | inBlock := false |
| 113 | blockKey := "" |
| 114 | blockStartLine := 0 |
| 115 | blockIndent := -1 |
| 116 | blockSize := 0 |
| 117 | blockHasExpression := false |
| 118 | |
| 119 | checkBlock := func() error { |
| 120 | if inBlock && blockHasExpression && blockSize > maxSize { |
| 121 | actualSize := console.FormatFileSize(int64(blockSize)) |
| 122 | maxSizeFormatted := console.FormatFileSize(int64(maxSize)) |
| 123 | return fmt.Errorf("expression value for %q (%s) exceeds maximum allowed size (%s) starting at line %d. "+ |
| 124 | "GitHub Actions has a 21KB limit for YAML values that contain template expressions (${{ }}). "+ |
| 125 | "Split the step into separate run: blocks so that no single block containing ${{ }} expressions exceeds the limit", |
| 126 | blockKey, actualSize, maxSizeFormatted, blockStartLine+1) |
| 127 | } |
| 128 | return nil |
| 129 | } |
| 130 | |
| 131 | for i, line := range lines { |
| 132 | // Count leading spaces to determine indentation level. |
| 133 | trimmed := strings.TrimLeft(line, " \t") |
| 134 | indent := len(line) - len(trimmed) |
| 135 | |
| 136 | if inBlock { |
| 137 | // An empty line is part of the block (blank lines are allowed inside block scalars). |
| 138 | if strings.TrimSpace(line) == "" { |
| 139 | blockSize += len(line) + 1 // +1 for the newline |
| 140 | continue |
| 141 | } |
| 142 | // A line whose indentation is greater than the block header's indentation |
| 143 | // is still inside the block scalar. |
| 144 | if indent > blockIndent { |
| 145 | blockSize += len(line) + 1 |
| 146 | if strings.Contains(line, "${{") { |
| 147 | blockHasExpression = true |
| 148 | } |
| 149 | continue |
| 150 | } |
| 151 | // Indentation dropped back – the block has ended. |
| 152 | if err := checkBlock(); err != nil { |
| 153 | return err |
| 154 | } |
| 155 | inBlock = false |
| 156 | blockKey = "" |
| 157 | blockSize = 0 |
| 158 | blockHasExpression = false |
| 159 | blockIndent = -1 |
| 160 | } |
| 161 | |
| 162 | // Detect the start of a block scalar: a YAML key whose value is | or > |
| 163 | // e.g. " run: |" or " script: >" |
| 164 | if colonIdx := strings.Index(trimmed, ":"); colonIdx > 0 { |
| 165 | afterColon := strings.TrimSpace(trimmed[colonIdx+1:]) |
| 166 | if afterColon == "|" || afterColon == ">" || strings.HasPrefix(afterColon, "|-") || strings.HasPrefix(afterColon, ">-") { |
| 167 | inBlock = true |